summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/tools/manifest/item.py
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /testing/web-platform/tests/tools/manifest/item.py
parentInitial commit. (diff)
downloadfirefox-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.py385
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