mirror of
https://github.com/servo/servo.git
synced 2025-08-01 19:50:30 +01:00
364 lines
10 KiB
Python
364 lines
10 KiB
Python
from copy import copy
|
|
from inspect import isabstract
|
|
from six import iteritems, with_metaclass
|
|
from six.moves.urllib.parse import urljoin, urlparse
|
|
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 Optional
|
|
from typing import Text
|
|
from typing import Dict
|
|
from typing import Tuple
|
|
from typing import List
|
|
from typing import Union
|
|
from typing import Type
|
|
from typing import Any
|
|
from typing import Sequence
|
|
from typing import Hashable
|
|
from .manifest import Manifest
|
|
Fuzzy = Dict[Optional[Tuple[Text, Text, Text]], 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[ManifestItemMeta, ...], Dict[str, Any]) -> type
|
|
rv = ABCMeta.__new__(cls, name, bases, attrs)
|
|
if not isabstract(rv):
|
|
assert issubclass(rv, ManifestItem)
|
|
assert isinstance(rv.item_type, str)
|
|
item_types[rv.item_type] = rv
|
|
|
|
return rv
|
|
|
|
|
|
class ManifestItem(with_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: () -> Hashable
|
|
"""The test's id (usually its url)"""
|
|
pass
|
|
|
|
@abstractproperty
|
|
def item_type(self):
|
|
# type: () -> str
|
|
"""The item's type"""
|
|
pass
|
|
|
|
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 "<%s.%s id=%s, path=%s>" % (self.__module__, self.__class__.__name__, self.id, self.path)
|
|
|
|
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")
|
|
|
|
def __init__(self,
|
|
tests_root, # type: Text
|
|
path, # type: Text
|
|
url_base, # type: Text
|
|
url, # type: Text
|
|
**extras # type: Any
|
|
):
|
|
# type: (...) -> None
|
|
super(URLManifestItem, self).__init__(tests_root, path)
|
|
assert url_base[0] == "/"
|
|
self.url_base = url_base
|
|
assert url[0] != "/"
|
|
self._url = url
|
|
self._extras = extras
|
|
|
|
@property
|
|
def id(self):
|
|
# type: () -> Text
|
|
return self.url
|
|
|
|
@property
|
|
def url(self):
|
|
# type: () -> Text
|
|
# we can outperform urljoin, because we know we just have path relative URLs
|
|
if self.url_base == "/":
|
|
return "/" + self._url
|
|
return urljoin(self.url_base, self._url)
|
|
|
|
@property
|
|
def https(self):
|
|
# type: () -> bool
|
|
flags = set(urlparse(self.url).path.rsplit("/", 1)[1].split(".")[1:-1])
|
|
return ("https" in flags or "serviceworker" in flags)
|
|
|
|
def to_json(self):
|
|
# type: () -> Tuple[Text, Dict[Any, Any]]
|
|
rv = (self._url, {}) # type: Tuple[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 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[Text]
|
|
return self._extras.get("script_metadata")
|
|
|
|
def to_json(self):
|
|
# type: () -> Tuple[Text, Dict[Text, Any]]
|
|
rv = super(TestharnessTest, self).to_json()
|
|
if self.timeout is not None:
|
|
rv[-1]["timeout"] = self.timeout
|
|
if self.testdriver:
|
|
rv[-1]["testdriver"] = self.testdriver
|
|
if self.jsshell:
|
|
rv[-1]["jsshell"] = True
|
|
if self.script_metadata:
|
|
rv[-1]["script_metadata"] = self.script_metadata
|
|
return rv
|
|
|
|
|
|
class RefTestBase(URLManifestItem):
|
|
__slots__ = ("references",)
|
|
|
|
def __init__(self,
|
|
tests_root, # type: Text
|
|
path, # type: Text
|
|
url_base, # type: Text
|
|
url, # type: Text
|
|
references=None, # type: Optional[List[Tuple[Text, Text]]]
|
|
**extras # type: Any
|
|
):
|
|
super(RefTestBase, self).__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[Text, List[Tuple[Text, Text]], Dict[Text, Any]]
|
|
rv = (self._url, self.references, {}) # type: Tuple[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(iteritems(self.fuzzy))
|
|
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: (...) -> RefTestBase
|
|
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)
|
|
|
|
def to_RefTest(self):
|
|
# type: () -> RefTest
|
|
if type(self) == RefTest:
|
|
assert isinstance(self, RefTest)
|
|
return self
|
|
rv = copy(self)
|
|
rv.__class__ = RefTest
|
|
assert isinstance(rv, RefTest)
|
|
return rv
|
|
|
|
def to_RefTestNode(self):
|
|
# type: () -> RefTestNode
|
|
if type(self) == RefTestNode:
|
|
assert isinstance(self, RefTestNode)
|
|
return self
|
|
rv = copy(self)
|
|
rv.__class__ = RefTestNode
|
|
assert isinstance(rv, RefTestNode)
|
|
return rv
|
|
|
|
|
|
class RefTestNode(RefTestBase):
|
|
__slots__ = ()
|
|
|
|
item_type = "reftest_node"
|
|
|
|
|
|
class RefTest(RefTestBase):
|
|
__slots__ = ()
|
|
|
|
item_type = "reftest"
|
|
|
|
|
|
class ManualTest(URLManifestItem):
|
|
__slots__ = ()
|
|
|
|
item_type = "manual"
|
|
|
|
|
|
class ConformanceCheckerTest(URLManifestItem):
|
|
__slots__ = ()
|
|
|
|
item_type = "conformancechecker"
|
|
|
|
|
|
class VisualTest(URLManifestItem):
|
|
__slots__ = ()
|
|
|
|
item_type = "visual"
|
|
|
|
|
|
class Stub(URLManifestItem):
|
|
__slots__ = ()
|
|
|
|
item_type = "stub"
|
|
|
|
|
|
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[Text, Dict[Text, Any]]
|
|
rv = super(WebDriverSpecTest, self).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
|