diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /testing/web-platform/tests/tools/manifest/item.py | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/tools/manifest/item.py')
-rw-r--r-- | testing/web-platform/tests/tools/manifest/item.py | 385 |
1 files changed, 385 insertions, 0 deletions
diff --git a/testing/web-platform/tests/tools/manifest/item.py b/testing/web-platform/tests/tools/manifest/item.py new file mode 100644 index 0000000000..02a72eeb29 --- /dev/null +++ b/testing/web-platform/tests/tools/manifest/item.py @@ -0,0 +1,385 @@ +import os.path +from inspect import isabstract +from urllib.parse import urljoin, urlparse, parse_qs +from abc import ABCMeta, abstractproperty + +from .utils import to_os_path + +MYPY = False +if MYPY: + # MYPY is set to True when run under Mypy. + from typing import Any, Dict, Hashable, List, Optional, Sequence, Text, Tuple, Type, Union, cast + from .manifest import Manifest + Fuzzy = Dict[Optional[Tuple[str, str, str]], List[int]] + PageRanges = Dict[str, List[int]] + +item_types = {} # type: Dict[str, Type[ManifestItem]] + + +class ManifestItemMeta(ABCMeta): + """Custom metaclass that registers all the subclasses in the + item_types dictionary according to the value of their item_type + attribute, and otherwise behaves like an ABCMeta.""" + + def __new__(cls, name, bases, attrs): + # type: (Type[ManifestItemMeta], str, Tuple[type], Dict[str, Any]) -> ManifestItemMeta + inst = super().__new__(cls, name, bases, attrs) + if isabstract(inst): + return inst + + assert issubclass(inst, ManifestItem) + if MYPY: + inst_ = cast(Type[ManifestItem], inst) + item_type = cast(str, inst_.item_type) + else: + inst_ = inst + assert isinstance(inst_.item_type, str) + item_type = inst_.item_type + + item_types[item_type] = inst_ + + return inst_ + + +class ManifestItem(metaclass=ManifestItemMeta): + __slots__ = ("_tests_root", "path") + + def __init__(self, tests_root, path): + # type: (Text, Text) -> None + self._tests_root = tests_root + self.path = path + + @abstractproperty + def id(self): + # type: () -> Text + """The test's id (usually its url)""" + pass + + @abstractproperty + def item_type(self): + # type: () -> str + """The item's type""" + pass + + @property + def path_parts(self): + # type: () -> Tuple[Text, ...] + return tuple(self.path.split(os.path.sep)) + + def key(self): + # type: () -> Hashable + """A unique identifier for the test""" + return (self.item_type, self.id) + + def __eq__(self, other): + # type: (Any) -> bool + if not hasattr(other, "key"): + return False + return bool(self.key() == other.key()) + + def __hash__(self): + # type: () -> int + return hash(self.key()) + + def __repr__(self): + # type: () -> str + return f"<{self.__module__}.{self.__class__.__name__} id={self.id!r}, path={self.path!r}>" + + def to_json(self): + # type: () -> Tuple[Any, ...] + return () + + @classmethod + def from_json(cls, + manifest, # type: Manifest + path, # type: Text + obj # type: Any + ): + # type: (...) -> ManifestItem + path = to_os_path(path) + tests_root = manifest.tests_root + assert tests_root is not None + return cls(tests_root, path) + + +class URLManifestItem(ManifestItem): + __slots__ = ("url_base", "_url", "_extras", "_flags") + + def __init__(self, + tests_root, # type: Text + path, # type: Text + url_base, # type: Text + url, # type: Optional[Text] + **extras # type: Any + ): + # type: (...) -> None + super().__init__(tests_root, path) + assert url_base[0] == "/" + self.url_base = url_base + assert url is None or url[0] != "/" + self._url = url + self._extras = extras + parsed_url = urlparse(self.url) + self._flags = (set(parsed_url.path.rsplit("/", 1)[1].split(".")[1:-1]) | + set(parse_qs(parsed_url.query).get("wpt_flags", []))) + + @property + def id(self): + # type: () -> Text + return self.url + + @property + def url(self): + # type: () -> Text + rel_url = self._url or self.path.replace(os.path.sep, "/") + # we can outperform urljoin, because we know we just have path relative URLs + if self.url_base == "/": + return "/" + rel_url + return urljoin(self.url_base, rel_url) + + @property + def https(self): + # type: () -> bool + return "https" in self._flags or "serviceworker" in self._flags or "serviceworker-module" in self._flags + + @property + def h2(self): + # type: () -> bool + return "h2" in self._flags + + @property + def subdomain(self): + # type: () -> bool + # Note: this is currently hard-coded to check for `www`, rather than + # all possible valid subdomains. It can be extended if needed. + return "www" in self._flags + + def to_json(self): + # type: () -> Tuple[Optional[Text], Dict[Any, Any]] + rel_url = None if self._url == self.path.replace(os.path.sep, "/") else self._url + rv = (rel_url, {}) # type: Tuple[Optional[Text], Dict[Any, Any]] + return rv + + @classmethod + def from_json(cls, + manifest, # type: Manifest + path, # type: Text + obj # type: Tuple[Text, Dict[Any, Any]] + ): + # type: (...) -> URLManifestItem + path = to_os_path(path) + url, extras = obj + tests_root = manifest.tests_root + assert tests_root is not None + return cls(tests_root, + path, + manifest.url_base, + url, + **extras) + + +class TestharnessTest(URLManifestItem): + __slots__ = () + + item_type = "testharness" + + @property + def timeout(self): + # type: () -> Optional[Text] + return self._extras.get("timeout") + + @property + def pac(self): + # type: () -> Optional[Text] + return self._extras.get("pac") + + @property + def testdriver(self): + # type: () -> Optional[Text] + return self._extras.get("testdriver") + + @property + def jsshell(self): + # type: () -> Optional[Text] + return self._extras.get("jsshell") + + @property + def script_metadata(self): + # type: () -> Optional[List[Tuple[Text, Text]]] + return self._extras.get("script_metadata") + + def to_json(self): + # type: () -> Tuple[Optional[Text], Dict[Text, Any]] + rv = super().to_json() + if self.timeout is not None: + rv[-1]["timeout"] = self.timeout + if self.pac is not None: + rv[-1]["pac"] = self.pac + if self.testdriver: + rv[-1]["testdriver"] = self.testdriver + if self.jsshell: + rv[-1]["jsshell"] = True + if self.script_metadata: + rv[-1]["script_metadata"] = [(k, v) for (k,v) in self.script_metadata] + return rv + + +class RefTest(URLManifestItem): + __slots__ = ("references",) + + item_type = "reftest" + + def __init__(self, + tests_root, # type: Text + path, # type: Text + url_base, # type: Text + url, # type: Optional[Text] + references=None, # type: Optional[List[Tuple[Text, Text]]] + **extras # type: Any + ): + super().__init__(tests_root, path, url_base, url, **extras) + if references is None: + self.references = [] # type: List[Tuple[Text, Text]] + else: + self.references = references + + @property + def timeout(self): + # type: () -> Optional[Text] + return self._extras.get("timeout") + + @property + def viewport_size(self): + # type: () -> Optional[Text] + return self._extras.get("viewport_size") + + @property + def dpi(self): + # type: () -> Optional[Text] + return self._extras.get("dpi") + + @property + def fuzzy(self): + # type: () -> Fuzzy + fuzzy = self._extras.get("fuzzy", {}) # type: Union[Fuzzy, List[Tuple[Optional[Sequence[Text]], List[int]]]] + if not isinstance(fuzzy, list): + return fuzzy + + rv = {} # type: Fuzzy + for k, v in fuzzy: # type: Tuple[Optional[Sequence[Text]], List[int]] + if k is None: + key = None # type: Optional[Tuple[Text, Text, Text]] + else: + # mypy types this as Tuple[Text, ...] + assert len(k) == 3 + key = tuple(k) # type: ignore + rv[key] = v + return rv + + def to_json(self): # type: ignore + # type: () -> Tuple[Optional[Text], List[Tuple[Text, Text]], Dict[Text, Any]] + rel_url = None if self._url == self.path else self._url + rv = (rel_url, self.references, {}) # type: Tuple[Optional[Text], List[Tuple[Text, Text]], Dict[Text, Any]] + extras = rv[-1] + if self.timeout is not None: + extras["timeout"] = self.timeout + if self.viewport_size is not None: + extras["viewport_size"] = self.viewport_size + if self.dpi is not None: + extras["dpi"] = self.dpi + if self.fuzzy: + extras["fuzzy"] = list(self.fuzzy.items()) + return rv + + @classmethod + def from_json(cls, # type: ignore + manifest, # type: Manifest + path, # type: Text + obj # type: Tuple[Text, List[Tuple[Text, Text]], Dict[Any, Any]] + ): + # type: (...) -> RefTest + tests_root = manifest.tests_root + assert tests_root is not None + path = to_os_path(path) + url, references, extras = obj + return cls(tests_root, + path, + manifest.url_base, + url, + references, + **extras) + + +class PrintRefTest(RefTest): + __slots__ = ("references",) + + item_type = "print-reftest" + + @property + def page_ranges(self): + # type: () -> PageRanges + return self._extras.get("page_ranges", {}) + + def to_json(self): # type: ignore + rv = super().to_json() + if self.page_ranges: + rv[-1]["page_ranges"] = self.page_ranges + return rv + + +class ManualTest(URLManifestItem): + __slots__ = () + + item_type = "manual" + + +class ConformanceCheckerTest(URLManifestItem): + __slots__ = () + + item_type = "conformancechecker" + + +class VisualTest(URLManifestItem): + __slots__ = () + + item_type = "visual" + + +class CrashTest(URLManifestItem): + __slots__ = () + + item_type = "crashtest" + + @property + def timeout(self): + # type: () -> Optional[Text] + return None + + +class WebDriverSpecTest(URLManifestItem): + __slots__ = () + + item_type = "wdspec" + + @property + def timeout(self): + # type: () -> Optional[Text] + return self._extras.get("timeout") + + def to_json(self): + # type: () -> Tuple[Optional[Text], Dict[Text, Any]] + rv = super().to_json() + if self.timeout is not None: + rv[-1]["timeout"] = self.timeout + return rv + + +class SupportFile(ManifestItem): + __slots__ = () + + item_type = "support" + + @property + def id(self): + # type: () -> Text + return self.path |