Title: [274220] trunk/Tools
Revision
274220
Author
[email protected]
Date
2021-03-10 10:59:11 -0800 (Wed, 10 Mar 2021)

Log Message

Adding Python type annotations to test_expectations.py
https://bugs.webkit.org/show_bug.cgi?id=222770

Reviewed by Jonathan Bedard.

* Scripts/webkitpy/layout_tests/models/test_expectations.py:
(ParseError.__init__):
(ParseError.__str__):
(ParseError.__repr__):
(TestExpectationWarning.__init__):
(TestExpectationParser):
(TestExpectationParser.__init__):
(TestExpectationParser.parse):
(TestExpectationParser.expectation_for_skipped_test):
(TestExpectationParser._parse_line):
(TestExpectationParser._parse_modifiers):
(TestExpectationParser._parse_expectations):
(TestExpectationParser._check_test_exists):
(TestExpectationParser._collect_matching_tests):
(TestExpectationParser._tokenize_line):
(TestExpectationLine):
(TestExpectationLine.__init__):
(TestExpectationLine.__str__):
(TestExpectationLine.is_invalid):
(TestExpectationLine.is_flaky):
(TestExpectationLine.expected_behavior):
(TestExpectationLine.create_passing_expectation):
(TestExpectationLine.to_string):
(TestExpectationLine._serialize_parsed_expectations):
(TestExpectationLine._serialize_parsed_modifiers):
(TestExpectationLine._format_line):
(TestExpectationsModel):
(TestExpectationsModel.__init__):
(TestExpectationsModel._dict_of_sets):
(TestExpectationsModel.get_test_set):
(TestExpectationsModel.get_test_set_for_keyword):
(TestExpectationsModel.get_tests_with_result_type):
(TestExpectationsModel.get_tests_with_timeline):
(TestExpectationsModel.get_modifiers):
(TestExpectationsModel.has_modifier):
(TestExpectationsModel.has_keyword):
(TestExpectationsModel.has_test):
(TestExpectationsModel.get_expectation_line):
(TestExpectationsModel.get_expectations):
(TestExpectationsModel.get_expectations_or_pass):
(TestExpectationsModel.expectations_to_string):
(TestExpectationsModel.get_expectations_string):
(TestExpectationsModel.expectation_to_string):
(TestExpectationsModel.add_expectation_line):
(TestExpectationsModel._add_test):
(TestExpectationsModel._clear_expectations_for_test):
(TestExpectationsModel._remove_from_sets):
(TestExpectationsModel._already_seen_better_match):
(TestExpectations):
(TestExpectations.expectation_from_string):
(TestExpectations.result_was_expected):
(TestExpectations.remove_pixel_failures):
(TestExpectations.remove_leak_failures):
(TestExpectations.has_pixel_failures):
(TestExpectations.suffixes_for_expectations):
(TestExpectations.__init__):
(TestExpectations.readable_filename_and_line_number):
(TestExpectations.parse_generic_expectations):
(TestExpectations.parse_default_port_expectations):
(TestExpectations.parse_override_expectations):
(TestExpectations.parse_all_expectations):
(TestExpectations.model):
(TestExpectations.get_rebaselining_failures):
(TestExpectations.filtered_expectations_for_test):
(TestExpectations.matches_an_expected_result):
(TestExpectations.is_rebaselining):
(TestExpectations._shorten_filename):
(TestExpectations._report_warnings):
(TestExpectations._process_tests_without_expectations):
(TestExpectations.has_warnings):
(TestExpectations.remove_configuration_from_test):
(TestExpectations.remove_rebaselined_tests):
(TestExpectations.remove_rebaselined_tests.without_rebaseline_modifier):
(TestExpectations._add_expectations):
(TestExpectations.add_skipped_tests):
(TestExpectations.list_to_string):
(TestExpectations.serialize):
(TestExpectations.nones_out):

Modified Paths

Diff

Modified: trunk/Tools/ChangeLog (274219 => 274220)


--- trunk/Tools/ChangeLog	2021-03-10 18:31:24 UTC (rev 274219)
+++ trunk/Tools/ChangeLog	2021-03-10 18:59:11 UTC (rev 274220)
@@ -1,5 +1,91 @@
 2021-03-10  Sam Sneddon  <[email protected]>
 
+        Adding Python type annotations to test_expectations.py
+        https://bugs.webkit.org/show_bug.cgi?id=222770
+
+        Reviewed by Jonathan Bedard.
+
+        * Scripts/webkitpy/layout_tests/models/test_expectations.py:
+        (ParseError.__init__):
+        (ParseError.__str__):
+        (ParseError.__repr__):
+        (TestExpectationWarning.__init__):
+        (TestExpectationParser):
+        (TestExpectationParser.__init__):
+        (TestExpectationParser.parse):
+        (TestExpectationParser.expectation_for_skipped_test):
+        (TestExpectationParser._parse_line):
+        (TestExpectationParser._parse_modifiers):
+        (TestExpectationParser._parse_expectations):
+        (TestExpectationParser._check_test_exists):
+        (TestExpectationParser._collect_matching_tests):
+        (TestExpectationParser._tokenize_line):
+        (TestExpectationLine):
+        (TestExpectationLine.__init__):
+        (TestExpectationLine.__str__):
+        (TestExpectationLine.is_invalid):
+        (TestExpectationLine.is_flaky):
+        (TestExpectationLine.expected_behavior):
+        (TestExpectationLine.create_passing_expectation):
+        (TestExpectationLine.to_string):
+        (TestExpectationLine._serialize_parsed_expectations):
+        (TestExpectationLine._serialize_parsed_modifiers):
+        (TestExpectationLine._format_line):
+        (TestExpectationsModel):
+        (TestExpectationsModel.__init__):
+        (TestExpectationsModel._dict_of_sets):
+        (TestExpectationsModel.get_test_set):
+        (TestExpectationsModel.get_test_set_for_keyword):
+        (TestExpectationsModel.get_tests_with_result_type):
+        (TestExpectationsModel.get_tests_with_timeline):
+        (TestExpectationsModel.get_modifiers):
+        (TestExpectationsModel.has_modifier):
+        (TestExpectationsModel.has_keyword):
+        (TestExpectationsModel.has_test):
+        (TestExpectationsModel.get_expectation_line):
+        (TestExpectationsModel.get_expectations):
+        (TestExpectationsModel.get_expectations_or_pass):
+        (TestExpectationsModel.expectations_to_string):
+        (TestExpectationsModel.get_expectations_string):
+        (TestExpectationsModel.expectation_to_string):
+        (TestExpectationsModel.add_expectation_line):
+        (TestExpectationsModel._add_test):
+        (TestExpectationsModel._clear_expectations_for_test):
+        (TestExpectationsModel._remove_from_sets):
+        (TestExpectationsModel._already_seen_better_match):
+        (TestExpectations):
+        (TestExpectations.expectation_from_string):
+        (TestExpectations.result_was_expected):
+        (TestExpectations.remove_pixel_failures):
+        (TestExpectations.remove_leak_failures):
+        (TestExpectations.has_pixel_failures):
+        (TestExpectations.suffixes_for_expectations):
+        (TestExpectations.__init__):
+        (TestExpectations.readable_filename_and_line_number):
+        (TestExpectations.parse_generic_expectations):
+        (TestExpectations.parse_default_port_expectations):
+        (TestExpectations.parse_override_expectations):
+        (TestExpectations.parse_all_expectations):
+        (TestExpectations.model):
+        (TestExpectations.get_rebaselining_failures):
+        (TestExpectations.filtered_expectations_for_test):
+        (TestExpectations.matches_an_expected_result):
+        (TestExpectations.is_rebaselining):
+        (TestExpectations._shorten_filename):
+        (TestExpectations._report_warnings):
+        (TestExpectations._process_tests_without_expectations):
+        (TestExpectations.has_warnings):
+        (TestExpectations.remove_configuration_from_test):
+        (TestExpectations.remove_rebaselined_tests):
+        (TestExpectations.remove_rebaselined_tests.without_rebaseline_modifier):
+        (TestExpectations._add_expectations):
+        (TestExpectations.add_skipped_tests):
+        (TestExpectations.list_to_string):
+        (TestExpectations.serialize):
+        (TestExpectations.nones_out):
+
+2021-03-10  Sam Sneddon  <[email protected]>
+
         Mark myself as a committer.
 
         Unreviewed.

Modified: trunk/Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py (274219 => 274220)


--- trunk/Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py	2021-03-10 18:31:24 UTC (rev 274219)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py	2021-03-10 18:59:11 UTC (rev 274220)
@@ -35,8 +35,28 @@
 import sys
 
 from webkitpy.common.iteration_compatibility import iteritems, itervalues
-from webkitpy.layout_tests.models.test_configuration import TestConfigurationConverter
+from webkitpy.layout_tests.models.test_configuration import (
+    TestConfiguration,
+    TestConfigurationConverter,
+)
+from webkitpy.port.base import Port
 
+MYPY = False
+if MYPY:
+    # MYPY is True when running Mypy; typing is stdlib only in Py3
+    from typing import (
+        Any,
+        Callable,
+        Container,
+        Dict,
+        Iterable,
+        List,
+        Optional,
+        Set,
+        Tuple,
+        Union,
+    )
+
 _log = logging.getLogger(__name__)
 
 
@@ -53,18 +73,22 @@
 
 class ParseError(Exception):
     def __init__(self, warnings):
+        # type: (List[TestExpectationWarning]) -> None
         super(ParseError, self).__init__()
         self.warnings = warnings
 
     def __str__(self):
+        # type: () -> str
         return '\n'.join(map(str, self.warnings))
 
     def __repr__(self):
+        # type: () -> str
         return 'ParseError(warnings=%s)' % self.warnings
 
 
 class TestExpectationWarning(object):
     def __init__(self, filename, line_number, line, error, test=None):
+        # type: (str, int, str, str, Optional[str]) -> None
         self.filename = filename
         self.line_number = line_number
         self.line = line
@@ -71,9 +95,10 @@
         self.error = error
         self.test = test
 
-        self.related_files = {}
+        self.related_files = {}  # type: Dict[str, Optional[List[int]]]
 
     def __str__(self):
+        # type: () -> str
         return '{}:{} {} {}'.format(self.filename, self.line_number, self.error, self.test if self.test else self.line)
 
 
@@ -94,10 +119,11 @@
     MISSING_BUG_WARNING = 'Test lacks BUG modifier.'
 
     def __init__(self, port, full_test_list, allow_rebaseline_modifier, shorten_filename=lambda x: x):
+        # type: (Port, Optional[List[str]], bool, Callable[[str], str]) -> None
         self._port = port
         self._test_configuration_converter = TestConfigurationConverter(set(port.all_test_configurations()), port.configuration_specifier_macros())
         if full_test_list is None:
-            self._full_test_list = None
+            self._full_test_list = None  # type: Optional[Set[str]]
         else:
             self._full_test_list = set(full_test_list)
         self._allow_rebaseline_modifier = allow_rebaseline_modifier
@@ -104,6 +130,7 @@
         self._shorten_filename = shorten_filename
 
     def parse(self, filename, expectations_string):
+        # type: (str, str) -> List[TestExpectationLine]
         expectation_lines = []
         line_number = 0
         for line in expectations_string.split("\n"):
@@ -114,6 +141,7 @@
         return expectation_lines
 
     def expectation_for_skipped_test(self, test_name):
+        # type: (str) -> TestExpectationLine
         if not self._port.test_exists(test_name):
             _log.warning('The following test %s from the Skipped list doesn\'t exist' % test_name)
         expectation_line = TestExpectationLine()
@@ -133,6 +161,7 @@
         return expectation_line
 
     def _parse_line(self, expectation_line):
+        # type: (TestExpectationLine) -> None
         if not expectation_line.name:
             return
 
@@ -151,6 +180,7 @@
         self._parse_expectations(expectation_line)
 
     def _parse_modifiers(self, expectation_line):
+        # type: (TestExpectationLine) -> None
         has_wontfix = False
         has_bugid = False
         parsed_specifiers = set()
@@ -184,6 +214,7 @@
         expectation_line.matching_configurations = self._test_configuration_converter.to_config_set(parsed_specifiers, expectation_line.warnings)
 
     def _parse_expectations(self, expectation_line):
+        # type: (TestExpectationLine) -> None
         result = set()
         for part in expectation_line.expectations:
             expectation = TestExpectations.expectation_from_string(part)
@@ -194,6 +225,8 @@
         expectation_line.parsed_expectations = result
 
     def _check_test_exists(self, expectation_line):
+        # type: (TestExpectationLine) -> bool
+        assert expectation_line.name is not None
         # WebKit's way of skipping tests is to add a -disabled suffix.
         # So we should consider the path existing if the path or the
         # -disabled version exists.
@@ -208,8 +241,10 @@
         return True
 
     def _collect_matching_tests(self, expectation_line):
+        # type: (TestExpectationLine) -> None
         """Convert the test specification to an absolute, normalized
         path and make sure directories end with the OS path separator."""
+        assert expectation_line.path is not None
 
         if not self._full_test_list:
             expectation_line.matching_tests = [expectation_line.path]
@@ -262,6 +297,7 @@
     # FIXME: Seems like these should be classmethods on TestExpectationLine instead of TestExpectationParser.
     @classmethod
     def _tokenize_line(cls, filename, expectation_string, line_number):
+        # type: (str, str, int) -> TestExpectationLine
         """Tokenizes a line from TestExpectations and returns an unparsed TestExpectationLine instance using the old format.
 
         The new format for a test expectation line is:
@@ -380,6 +416,7 @@
 
     @classmethod
     def _split_space_separated(cls, space_separated_string):
+        # type: (str) -> List[str]
         """Splits a space-separated string into an array."""
         return [part.strip() for part in space_separated_string.strip().split(' ')]
 
@@ -388,35 +425,45 @@
     """Represents a line in test expectations file."""
 
     def __init__(self):
+        # type: () -> None
         """Initializes a blank-line equivalent of an expectation."""
-        self.original_string = None
-        self.filename = None  # this is the path to the expectations file for this line
-        self.line_number = None
-        self.name = None  # this is the path in the line itself
-        self.path = None  # this is the normpath of self.name
-        self.modifiers = []
-        self.parsed_modifiers = []
-        self.parsed_bug_modifiers = []
-        self.matching_configurations = set()
-        self.expectations = []
-        self.parsed_expectations = set()
-        self.comment = None
-        self.matching_tests = []
-        self.warnings = []
-        self.related_files = {}  # Dictionary of files to lines number in that file which may have caused the list of warnings.
+        self.original_string = None  # type: Optional[str]
+        self.filename = None  # type: Optional[str]  # this is the path to the expectations file for this line;
+        self.line_number = None  # type: Optional[int]
+        self.name = None  # type: Optional[str]  # this is the path in the line itself
+        self.path = None  # type: Optional[str]  # this is the normpath of self.name
+        self.modifiers = []  # type: List[str]
+        self.parsed_modifiers = []  # type: List[str]
+        self.parsed_bug_modifiers = []  # type: List[str]
+        self.matching_configurations = set()  # type: Set[TestConfiguration]
+        self.expectations = []  # type: List[str]
+        self.parsed_expectations = set()  # type: Set[int]
+        self.comment = None  # type: Optional[str]
+        self.matching_tests = []  # type: List[str]
+        self.warnings = []  # type: List[str]
+        self.related_files = {}  # type: Dict[str, Optional[List[int]]]  # Dictionary of files to lines number in that file which may have caused the list of warnings.
         self.not_applicable_to_current_platform = False
+        self.is_file = False
 
     def __str__(self):
-        return self.to_string(None)
+        # type: () -> str
+        s = self.to_string(None)
+        if s is None:
+            return ''
+        else:
+            return s
 
     def is_invalid(self):
-        return self.warnings and self.warnings != [TestExpectationParser.MISSING_BUG_WARNING]
+        # type: () -> bool
+        return bool(self.warnings and self.warnings != [TestExpectationParser.MISSING_BUG_WARNING])
 
     def is_flaky(self):
+        # type: () -> bool
         return len(self.parsed_expectations) > 1
 
     @property
     def expected_behavior(self):
+        # type: () -> List[str]
         expectations = self.expectations[:]
         if "SLOW" in self.modifiers:
             expectations += ["SLOW"]
@@ -432,6 +479,7 @@
 
     @staticmethod
     def create_passing_expectation(test):
+        # type: (str) -> TestExpectationLine
         expectation_line = TestExpectationLine()
         expectation_line.name = test
         expectation_line.path = test
@@ -441,7 +489,8 @@
         return expectation_line
 
     def to_string(self, test_configuration_converter, include_modifiers=True, include_expectations=True, include_comment=True):
-        parsed_expectation_to_string = dict([[parsed_expectation, expectation_string] for expectation_string, parsed_expectation in TestExpectations.EXPECTATIONS.items()])
+        # type: (Optional[TestConfigurationConverter], bool, bool, bool) -> Optional[str]
+        parsed_expectation_to_string = dict((parsed_expectation, expectation_string) for expectation_string, parsed_expectation in TestExpectations.EXPECTATIONS.items())  # type: Dict[int, str]
 
         if self.is_invalid():
             return self.original_string or ''
@@ -450,7 +499,7 @@
             return '' if self.comment is None else "#%s" % self.comment
 
         if test_configuration_converter and self.parsed_bug_modifiers:
-            specifiers_list = test_configuration_converter.to_specifiers_list(self.matching_configurations)
+            specifiers_list = test_configuration_converter.to_specifiers_list(self.matching_configurations)  # type: List[Set[Tuple[str, str]]]
             result = []
             for specifiers in specifiers_list:
                 # FIXME: this is silly that we join the modifiers and then immediately split them.
@@ -463,6 +512,7 @@
             include_modifiers, include_expectations, include_comment)
 
     def _serialize_parsed_expectations(self, parsed_expectation_to_string):
+        # type: (Dict[int, str]) -> str
         result = []
         for index in TestExpectations.EXPECTATION_ORDER:
             if index in self.parsed_expectations:
@@ -470,6 +520,7 @@
         return ' '.join(result)
 
     def _serialize_parsed_modifiers(self, test_configuration_converter, specifiers):
+        # type: (TestConfigurationConverter, Set[Tuple[str, str]]) -> str
         result = []
         if self.parsed_bug_modifiers:
             result.extend(sorted(self.parsed_bug_modifiers))
@@ -478,10 +529,19 @@
         return ' '.join(result)
 
     @staticmethod
-    def _format_line(modifiers, name, expectations, comment, include_modifiers=True, include_expectations=True, include_comment=True):
-        bugs = []
-        new_modifiers = []
-        new_expectations = []
+    def _format_line(
+        modifiers,  # type: List[str]
+        name,  # type: str
+        expectations,  # type: List[str]
+        comment,  # type: Optional[str]
+        include_modifiers=True,  # type: bool
+        include_expectations=True,  # type: bool
+        include_comment=True,  # type: bool
+    ):
+        # type: (...) -> str
+        bugs = []  # type: List[str]
+        new_modifiers = []  # type: List[str]
+        new_expectations = []  # type: List[str]
         for modifier in modifiers:
             modifier = modifier.upper()
             if modifier.startswith('BUGWK'):
@@ -490,7 +550,7 @@
                 # FIXME: we should preserve case once we can drop the old syntax.
                 bugs.append('Bug(' + modifier[3:].lower() + ')')
             elif modifier in ('SLOW', 'SKIP', 'REBASELINE', 'WONTFIX', 'DUMPJSCONSOLELOGINSTDERR'):
-                new_expectations.append(TestExpectationParser._inverted_expectation_tokens.get(modifier))
+                new_expectations.append(TestExpectationParser._inverted_expectation_tokens[modifier])
             else:
                 new_modifiers.append(TestExpectationParser._inverted_configuration_tokens.get(modifier, modifier))
 
@@ -517,31 +577,34 @@
     """Represents relational store of all expectations and provides CRUD semantics to manage it."""
 
     def __init__(self, shorten_filename=lambda x: x):
+        # type: (Callable[[str], str]) -> None
         # Maps a test to its list of expectations.
-        self._test_to_expectations = {}
+        self._test_to_expectations = {}  # type: Dict[str, Set[int]]
 
         # Maps a test to list of its modifiers (string values)
-        self._test_to_modifiers = {}
+        self._test_to_modifiers = {}  # type: Dict[str, List[str]]
 
         # Maps a test to a TestExpectationLine instance.
-        self._test_to_expectation_line = {}
+        self._test_to_expectation_line = {}  # type: Dict[str, TestExpectationLine]
 
-        self._modifier_to_tests = self._dict_of_sets(TestExpectations.MODIFIERS)
-        self._expectation_to_tests = self._dict_of_sets(TestExpectations.EXPECTATIONS)
-        self._timeline_to_tests = self._dict_of_sets(TestExpectations.TIMELINES)
-        self._result_type_to_tests = self._dict_of_sets(TestExpectations.RESULT_TYPES)
+        self._modifier_to_tests = self._dict_of_sets(TestExpectations.MODIFIERS)  # type: Dict[int, Set[str]]
+        self._expectation_to_tests = self._dict_of_sets(TestExpectations.EXPECTATIONS)  # type: Dict[int, Set[str]]
+        self._timeline_to_tests = self._dict_of_sets(TestExpectations.TIMELINES)  # type: Dict[int, Set[str]]
+        self._result_type_to_tests = self._dict_of_sets(TestExpectations.RESULT_TYPES)  # type: Dict[int, Set[str]]
 
         self._shorten_filename = shorten_filename
 
     def _dict_of_sets(self, strings_to_constants):
+        # type: (Dict[str, int]) -> Dict[int, Set[Any]]
         """Takes a dict of strings->constants and returns a dict mapping
         each constant to an empty set."""
-        d = {}
+        d = {}  # type: Dict[int, Set[Any]]
         for c in strings_to_constants.values():
             d[c] = set()
         return d
 
     def get_test_set(self, modifier, expectation=None, include_skips=True):
+        # type: (int, None, bool) -> Set[str]
         if expectation is None:
             tests = self._modifier_to_tests[modifier]
         else:
@@ -554,6 +617,7 @@
         return tests
 
     def get_test_set_for_keyword(self, keyword):
+        # type: (str) -> Set[str]
         # FIXME: get_test_set() is an awkward public interface because it requires
         # callers to know the difference between modifiers and expectations. We
         # should replace that with this where possible.
@@ -572,32 +636,41 @@
         return matching_tests
 
     def get_tests_with_result_type(self, result_type):
+        # type: (int) -> Set[str]
         return self._result_type_to_tests[result_type]
 
     def get_tests_with_timeline(self, timeline):
+        # type: (int) -> Set[str]
         return self._timeline_to_tests[timeline]
 
     def get_modifiers(self, test):
+        # type: (str) -> List[str]
         """This returns modifiers for the given test (the modifiers plus the BUGXXXX identifier). This is used by the LTTF dashboard."""
         return self._test_to_modifiers[test]
 
     def has_modifier(self, test, modifier):
+        # type: (str, int) -> bool
         return test in self._modifier_to_tests[modifier]
 
     def has_keyword(self, test, keyword):
+        # type: (str, str) -> bool
         return (keyword.upper() in self.get_expectations_string(test) or
                 keyword.lower() in self.get_modifiers(test))
 
     def has_test(self, test):
+        # type: (str) -> bool
         return test in self._test_to_expectation_line
 
     def get_expectation_line(self, test):
+        # type: (str) -> Optional[TestExpectationLine]
         return self._test_to_expectation_line.get(test)
 
     def get_expectations(self, test):
+        # type: (str) -> Set[int]
         return self._test_to_expectations[test]
 
     def get_expectations_or_pass(self, test):
+        # type: (str) -> Set[int]
         try:
             return self.get_expectations(test)
         except:
@@ -604,6 +677,7 @@
             return set([PASS])
 
     def expectations_to_string(self, expectations):
+        # type: (Iterable[int]) -> str
         retval = []
 
         for expectation in expectations:
@@ -612,6 +686,7 @@
         return " ".join(retval)
 
     def get_expectations_string(self, test):
+        # type: (str) -> str
         """Returns the expectations for the given test as an uppercase string.
         If there are no expectations for the test, then "PASS" is returned."""
         try:
@@ -626,6 +701,7 @@
         return " ".join(retval)
 
     def expectation_to_string(self, expectation):
+        # type: (int) -> str
         """Return the uppercased string equivalent of a given expectation."""
         for item in TestExpectations.EXPECTATIONS.items():
             if item[1] == expectation:
@@ -633,6 +709,7 @@
         raise ValueError(expectation)
 
     def add_expectation_line(self, expectation_line, in_skipped=False):
+        # type: (TestExpectationLine, bool) -> None
         """Returns a list of warnings encountered while matching modifiers."""
 
         if expectation_line.is_invalid():
@@ -647,6 +724,7 @@
             self._add_test(test, expectation_line)
 
     def _add_test(self, test, expectation_line):
+        # type: (str, TestExpectationLine) -> None
         """Sets the expected state for a given test.
 
         This routine assumes the test has not been added before. If it has,
@@ -677,6 +755,7 @@
             self._result_type_to_tests[FAIL].add(test)
 
     def _clear_expectations_for_test(self, test):
+        # type: (str) -> None
         """Remove prexisting expectations for this test.
         This happens if we are seeing a more precise path
         than a previous listing.
@@ -689,6 +768,7 @@
             self._remove_from_sets(test, self._result_type_to_tests)
 
     def _remove_from_sets(self, test, dict_of_sets_of_tests):
+        # type: (str, Dict[int, Set[str]]) -> None
         """Removes the given test from the sets in the dictionary.
 
         Args:
@@ -699,6 +779,7 @@
                 set_of_tests.remove(test)
 
     def _already_seen_better_match(self, test, expectation_line):
+        # type: (str, TestExpectationLine) -> bool
         """Returns whether we've seen a better match already in the file.
 
         Returns True if we've already seen a expectation_line.name that matches more of the test
@@ -715,6 +796,9 @@
             # We've moved on to a new expectation file, which overrides older ones.
             return False
 
+        assert prev_expectation_line.path is not None
+        assert expectation_line.path is not None
+
         if len(prev_expectation_line.path) > len(expectation_line.path):
             # The previous path matched more of the test.
             return True
@@ -732,9 +816,14 @@
         #
         # To use the "more modifiers wins" policy, change the errors for overrides
         # to be warnings and return False".
+        assert prev_expectation_line.filename is not None
+        assert expectation_line.filename is not None
         shortened_expectation_filename = self._shorten_filename(expectation_line.filename)
         shortened_previous_expectation_filename = self._shorten_filename(prev_expectation_line.filename)
 
+        assert prev_expectation_line.line_number is not None
+        assert expectation_line.line_number is not None
+
         if prev_expectation_line.matching_configurations == expectation_line.matching_configurations:
             expectation_line.warnings.append('Duplicate or ambiguous entry lines %s:%d and %s:%d.' % (
                 shortened_previous_expectation_filename, prev_expectation_line.line_number,
@@ -761,14 +850,20 @@
             return False
 
         # Missing files will be 'None'. It should be impossible to have a missing file which also has a line associated with it.
-        assert shortened_previous_expectation_filename not in expectation_line.related_files or expectation_line.related_files[shortened_previous_expectation_filename] is not None
-        expectation_line.related_files[shortened_previous_expectation_filename] = expectation_line.related_files.get(shortened_previous_expectation_filename, [])
-        expectation_line.related_files[shortened_previous_expectation_filename].append(prev_expectation_line.line_number)
+        if shortened_previous_expectation_filename not in expectation_line.related_files:
+            expectation_line.related_files[shortened_previous_expectation_filename] = []
 
-        assert shortened_expectation_filename not in prev_expectation_line.related_files or prev_expectation_line.related_files[shortened_expectation_filename] is not None
-        prev_expectation_line.related_files[shortened_expectation_filename] = prev_expectation_line.related_files.get(shortened_expectation_filename, [])
-        prev_expectation_line.related_files[shortened_expectation_filename].append(expectation_line.line_number)
+        expectation_line_related = expectation_line.related_files[shortened_previous_expectation_filename]
+        assert expectation_line_related is not None
+        expectation_line_related.append(prev_expectation_line.line_number)
 
+        if shortened_expectation_filename not in prev_expectation_line.related_files:
+            prev_expectation_line.related_files[shortened_expectation_filename] = []
+
+        prev_expectation_line_related = prev_expectation_line.related_files[shortened_expectation_filename]
+        assert prev_expectation_line_related is not None
+        prev_expectation_line_related.append(expectation_line.line_number)
+
         return True
 
 
@@ -861,11 +956,13 @@
 
     @classmethod
     def expectation_from_string(cls, string):
+        # type: (str) -> Optional[int]
         assert(' ' not in string)  # This only handles one expectation at a time.
         return cls.EXPECTATIONS.get(string.lower())
 
     @staticmethod
     def result_was_expected(result, expected_results, test_needs_rebaselining, test_is_skipped):
+        # type: (int, Set[int], bool, bool) -> bool
         """Returns whether we got a result we were expecting.
         Args:
             result: actual result of a test execution
@@ -884,6 +981,7 @@
 
     @staticmethod
     def remove_pixel_failures(expected_results):
+        # type: (Set[int]) -> Set[int]
         """Returns a copy of the expected results for a test, except that we
         drop any pixel failures and return the remaining expectations. For example,
         if we're not running pixel tests, then tests expected to fail as IMAGE
@@ -896,6 +994,7 @@
 
     @staticmethod
     def remove_leak_failures(expected_results):
+        # type: (Set[int]) -> Set[int]
         """Returns a copy of the expected results for a test, except that we
         drop any leak failures and return the remaining expectations. For example,
         if we're not running with --world-leaks, then tests expected to fail as LEAK
@@ -909,10 +1008,12 @@
 
     @staticmethod
     def has_pixel_failures(actual_results):
+        # type: (Container[int]) -> bool
         return IMAGE in actual_results or FAIL in actual_results
 
     @staticmethod
     def suffixes_for_expectations(expectations):
+        # type: (Set[int]) -> Set[str]
         suffixes = set()
         if IMAGE in expectations:
             suffixes.add('png')
@@ -922,15 +1023,25 @@
             suffixes.add('wav')
         return set(suffixes)
 
-    def __init__(self, port, tests=None, include_generic=True, include_overrides=True, expectations_to_lint=None, force_expectations_pass=False, device_type=None):
+    def __init__(
+        self,
+        port,  # type: Port
+        tests=None,  # type: Optional[List[str]]
+        include_generic=True,  # type: bool
+        include_overrides=True,  # type: bool
+        expectations_to_lint=None,  # type: Optional[Dict[str, str]]
+        force_expectations_pass=False,  # type: bool
+        device_type=None,  # type: Optional[str]
+    ):
+        # type: (...) -> None
         self._full_test_list = tests
-        self._test_config = port.test_configuration()
+        self._test_config = port.test_configuration()  # type: TestConfiguration
         self._is_lint_mode = expectations_to_lint is not None
         self._model = TestExpectationsModel(self._shorten_filename)
         self._parser = TestExpectationParser(port, tests, self._is_lint_mode, self._shorten_filename)
         self._port = port
-        self._skipped_tests_warnings = []
-        self._expectations = []
+        self._skipped_tests_warnings = []  # type: List[TestExpectationWarning]
+        self._expectations = []  # type: List[TestExpectationLine]
         self._force_expectations_pass = force_expectations_pass
         self._device_type = device_type
         self._include_generic = include_generic
@@ -938,6 +1049,7 @@
         self._expectations_to_lint = expectations_to_lint
 
     def readable_filename_and_line_number(self, line):
+        # type: (TestExpectationLine) -> str
         if line.not_applicable_to_current_platform:
             return "(skipped for this platform)"
         if not line.filename:
@@ -947,6 +1059,7 @@
         return '{}:{}'.format(line.filename, line.line_number)
 
     def parse_generic_expectations(self):
+        # type: () -> None
         if self._port.path_to_generic_test_expectations_file() in self._expectations_dict:
             if self._include_generic:
                 expectations = self._parser.parse(list(self._expectations_dict.keys())[self._expectations_dict_index], list(self._expectations_dict.values())[self._expectations_dict_index])
@@ -955,6 +1068,7 @@
             self._expectations_dict_index += 1
 
     def parse_default_port_expectations(self):
+        # type: () -> None
         if len(self._expectations_dict) > self._expectations_dict_index:
             expectations = self._parser.parse(list(self._expectations_dict.keys())[self._expectations_dict_index], list(self._expectations_dict.values())[self._expectations_dict_index])
             self._add_expectations(expectations)
@@ -962,6 +1076,7 @@
             self._expectations_dict_index += 1
 
     def parse_override_expectations(self):
+        # type: () -> None
         while len(self._expectations_dict) > self._expectations_dict_index and self._include_overrides:
             expectations = self._parser.parse(list(self._expectations_dict.keys())[self._expectations_dict_index], list(self._expectations_dict.values())[self._expectations_dict_index])
             self._add_expectations(expectations)
@@ -969,7 +1084,8 @@
             self._expectations_dict_index += 1
 
     def parse_all_expectations(self):
-        self._expectations_dict = self._expectations_to_lint or self._port.expectations_dict(device_type=self._device_type)
+        # type: () -> None
+        self._expectations_dict = self._expectations_to_lint or self._port.expectations_dict(device_type=self._device_type)  # type: Dict[str, str]
         self._expectations_dict_index = 0
 
         self._has_warnings = False
@@ -987,12 +1103,15 @@
     # TODO(ojan): Allow for removing skipped tests when getting the list of
     # tests to run, but not when getting metrics.
     def model(self):
+        # type: () -> TestExpectationsModel
         return self._model
 
     def get_rebaselining_failures(self):
+        # type: () -> Set[str]
         return self._model.get_test_set(REBASELINE)
 
     def filtered_expectations_for_test(self, test, pixel_tests_are_enabled, world_leaks_are_enabled):
+        # type: (str, bool, bool) -> Set[int]
         expected_results = self._model.get_expectations_or_pass(test)
         if not pixel_tests_are_enabled:
             expected_results = self.remove_pixel_failures(expected_results)
@@ -1002,6 +1121,7 @@
         return expected_results
 
     def matches_an_expected_result(self, test, result, expected_results):
+        # type: (str, int, Set[int]) -> bool
         return self.result_was_expected(result,
                                    expected_results,
                                    self.is_rebaselining(test),
@@ -1008,22 +1128,32 @@
                                    self._model.has_modifier(test, SKIP))
 
     def is_rebaselining(self, test):
+        # type: (str) -> bool
         return self._model.has_modifier(test, REBASELINE)
 
     def _shorten_filename(self, filename):
+        # type: (str) -> str
         if filename.startswith(self._port.path_from_webkit_base()):
-            return self._port.host.filesystem.relpath(filename, self._port.path_from_webkit_base())
+            trimmed = self._port.host.filesystem.relpath(filename, self._port.path_from_webkit_base())
+            if MYPY:
+                # this doesn't actually hold on Py2 (it's actually unicode)
+                assert isinstance(trimmed, str)
+            return trimmed
         return filename
 
     def _report_warnings(self):
+        # type: () -> None
         warnings = []
         for expectation in self._expectations:
+            assert expectation.filename is not None
+            assert expectation.line_number is not None
+            assert expectation.original_string is not None
             for warning in expectation.warnings:
-                warning = TestExpectationWarning(
+                warning_obj = TestExpectationWarning(
                     self._shorten_filename(expectation.filename), expectation.line_number,
                     expectation.original_string, warning, expectation.name if expectation.expectations else None)
-                warning.related_files = expectation.related_files
-                warnings.append(warning)
+                warning_obj.related_files = expectation.related_files
+                warnings.append(warning_obj)
 
         if warnings:
             self._has_warnings = True
@@ -1030,11 +1160,12 @@
             if self._is_lint_mode:
                 raise ParseError(warnings)
             _log.warning('--lint-test-files warnings:')
-            for warning in warnings:
-                _log.warning(warning)
+            for warning_obj in warnings:
+                _log.warning(warning_obj)
             _log.warning('')
 
     def _process_tests_without_expectations(self):
+        # type: () -> None
         if self._full_test_list:
             for test in self._full_test_list:
                 if not self._model.has_test(test):
@@ -1041,9 +1172,11 @@
                     self._model.add_expectation_line(TestExpectationLine.create_passing_expectation(test))
 
     def has_warnings(self):
+        # type: () -> bool
         return self._has_warnings
 
     def remove_configuration_from_test(self, test, test_configuration):
+        # type: (str, TestConfiguration) -> str
         expectations_to_remove = []
         modified_expectations = []
 
@@ -1067,8 +1200,10 @@
         return self.list_to_string(self._expectations, self._parser._test_configuration_converter, modified_expectations)
 
     def remove_rebaselined_tests(self, except_these_tests, filename):
+        # type: (List[str], str) -> str
         """Returns a copy of the expectations in the file with the tests removed."""
         def without_rebaseline_modifier(expectation):
+            # type: (TestExpectationLine) -> bool
             return (expectation.filename == filename and
                     not (not expectation.is_invalid() and
                          expectation.name in except_these_tests and
@@ -1077,6 +1212,7 @@
         return self.list_to_string(filter(without_rebaseline_modifier, self._expectations), reconstitute_only_these=[])
 
     def _add_expectations(self, expectation_list):
+        # type: (List[TestExpectationLine]) -> None
         for expectation_line in expectation_list:
             if self._force_expectations_pass:
                 expectation_line.expectations = ['PASS']
@@ -1089,10 +1225,13 @@
                 self._model.add_expectation_line(expectation_line)
 
     def add_skipped_tests(self, tests_to_skip):
+        # type: (Set[str]) -> None
         if not tests_to_skip:
             return
         for test in self._expectations:
             if test.name and test.name in tests_to_skip:
+                assert test.filename is not None
+                assert test.line_number is not None
                 test.warnings.append('%s:%d %s is also in a Skipped file.' % (test.filename, test.line_number, test.name))
 
         for test_name in tests_to_skip:
@@ -1100,8 +1239,14 @@
             self._model.add_expectation_line(expectation_line, in_skipped=True)
 
     @staticmethod
-    def list_to_string(expectation_lines, test_configuration_converter=None, reconstitute_only_these=None):
+    def list_to_string(
+        expectation_lines,  # type: Iterable[TestExpectationLine]
+        test_configuration_converter=None,  # type: Optional[TestConfigurationConverter]
+        reconstitute_only_these=None,  # type: Optional[List[TestExpectationLine]]
+    ):
+        # type: (...) -> str
         def serialize(expectation_line):
+            # type: (TestExpectationLine) -> Optional[str]
             # If reconstitute_only_these is an empty list, we want to return original_string.
             # So we need to compare reconstitute_only_these to None, not just check if it's falsey.
             if reconstitute_only_these is None or expectation_line in reconstitute_only_these:
@@ -1108,7 +1253,4 @@
                 return expectation_line.to_string(test_configuration_converter)
             return expectation_line.original_string
 
-        def nones_out(expectation_line):
-            return expectation_line is not None
-
-        return "\n".join(filter(nones_out, map(serialize, expectation_lines)))
+        return "\n".join(line for line in map(serialize, expectation_lines) if line is not None)
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to