Bug 1613556 [wpt PR 21619] - Revert "Major new manifest version (v8): path trie editi...
[gecko.git] / testing / web-platform / tests / tools / manifest / item.py
blob80b8a66871c82074cf7246729efbf62b5ad079b2
1 from inspect import isabstract
2 from six import iteritems, with_metaclass
3 from six.moves.urllib.parse import urljoin, urlparse
4 from abc import ABCMeta, abstractproperty
6 from .utils import to_os_path
8 MYPY = False
9 if MYPY:
10 # MYPY is set to True when run under Mypy.
11 from typing import Optional
12 from typing import Text
13 from typing import Dict
14 from typing import Tuple
15 from typing import List
16 from typing import Union
17 from typing import Type
18 from typing import Any
19 from typing import Sequence
20 from typing import Hashable
21 from .manifest import Manifest
22 Fuzzy = Dict[Optional[Tuple[Text, Text, Text]], List[int]]
24 item_types = {} # type: Dict[str, Type[ManifestItem]]
27 class ManifestItemMeta(ABCMeta):
28 """Custom metaclass that registers all the subclasses in the
29 item_types dictionary according to the value of their item_type
30 attribute, and otherwise behaves like an ABCMeta."""
32 def __new__(cls, name, bases, attrs):
33 # type: (Type[ManifestItemMeta], str, Tuple[ManifestItemMeta, ...], Dict[str, Any]) -> ManifestItemMeta
34 rv = super(ManifestItemMeta, cls).__new__(cls, name, bases, attrs)
35 if not isabstract(rv):
36 assert issubclass(rv, ManifestItem)
37 assert isinstance(rv.item_type, str)
38 item_types[rv.item_type] = rv
40 return rv # type: ignore
43 class ManifestItem(with_metaclass(ManifestItemMeta)):
44 __slots__ = ("_tests_root", "path")
46 def __init__(self, tests_root, path):
47 # type: (Text, Text) -> None
48 self._tests_root = tests_root
49 self.path = path
51 @abstractproperty
52 def id(self):
53 # type: () -> Text
54 """The test's id (usually its url)"""
55 pass
57 @abstractproperty
58 def item_type(self):
59 # type: () -> str
60 """The item's type"""
61 pass
63 def key(self):
64 # type: () -> Hashable
65 """A unique identifier for the test"""
66 return (self.item_type, self.id)
68 def __eq__(self, other):
69 # type: (Any) -> bool
70 if not hasattr(other, "key"):
71 return False
72 return bool(self.key() == other.key())
74 def __hash__(self):
75 # type: () -> int
76 return hash(self.key())
78 def __repr__(self):
79 # type: () -> str
80 return "<%s.%s id=%r, path=%r>" % (self.__module__, self.__class__.__name__, self.id, self.path)
82 def to_json(self):
83 # type: () -> Tuple[Any, ...]
84 return ()
86 @classmethod
87 def from_json(cls,
88 manifest, # type: Manifest
89 path, # type: Text
90 obj # type: Any
92 # type: (...) -> ManifestItem
93 path = to_os_path(path)
94 tests_root = manifest.tests_root
95 assert tests_root is not None
96 return cls(tests_root, path)
99 class URLManifestItem(ManifestItem):
100 __slots__ = ("url_base", "_url", "_extras")
102 def __init__(self,
103 tests_root, # type: Text
104 path, # type: Text
105 url_base, # type: Text
106 url, # type: Text
107 **extras # type: Any
109 # type: (...) -> None
110 super(URLManifestItem, self).__init__(tests_root, path)
111 assert url_base[0] == "/"
112 self.url_base = url_base
113 assert url[0] != "/"
114 self._url = url
115 self._extras = extras
117 @property
118 def id(self):
119 # type: () -> Text
120 return self.url
122 @property
123 def url(self):
124 # type: () -> Text
125 # we can outperform urljoin, because we know we just have path relative URLs
126 if self.url_base == "/":
127 return "/" + self._url
128 return urljoin(self.url_base, self._url)
130 @property
131 def https(self):
132 # type: () -> bool
133 flags = set(urlparse(self.url).path.rsplit("/", 1)[1].split(".")[1:-1])
134 return "https" in flags or "serviceworker" in flags
136 @property
137 def h2(self):
138 # type: () -> bool
139 flags = set(urlparse(self.url).path.rsplit("/", 1)[1].split(".")[1:-1])
140 return "h2" in flags
142 def to_json(self):
143 # type: () -> Tuple[Text, Dict[Any, Any]]
144 rv = (self._url, {}) # type: Tuple[Text, Dict[Any, Any]]
145 return rv
147 @classmethod
148 def from_json(cls,
149 manifest, # type: Manifest
150 path, # type: Text
151 obj # type: Tuple[Text, Dict[Any, Any]]
153 # type: (...) -> URLManifestItem
154 path = to_os_path(path)
155 url, extras = obj
156 tests_root = manifest.tests_root
157 assert tests_root is not None
158 return cls(tests_root,
159 path,
160 manifest.url_base,
161 url,
162 **extras)
165 class TestharnessTest(URLManifestItem):
166 __slots__ = ()
168 item_type = "testharness"
170 @property
171 def timeout(self):
172 # type: () -> Optional[Text]
173 return self._extras.get("timeout")
175 @property
176 def testdriver(self):
177 # type: () -> Optional[Text]
178 return self._extras.get("testdriver")
180 @property
181 def jsshell(self):
182 # type: () -> Optional[Text]
183 return self._extras.get("jsshell")
185 @property
186 def script_metadata(self):
187 # type: () -> Optional[Text]
188 return self._extras.get("script_metadata")
190 def to_json(self):
191 # type: () -> Tuple[Text, Dict[Text, Any]]
192 rv = super(TestharnessTest, self).to_json()
193 if self.timeout is not None:
194 rv[-1]["timeout"] = self.timeout
195 if self.testdriver:
196 rv[-1]["testdriver"] = self.testdriver
197 if self.jsshell:
198 rv[-1]["jsshell"] = True
199 if self.script_metadata:
200 rv[-1]["script_metadata"] = self.script_metadata
201 return rv
204 class RefTest(URLManifestItem):
205 __slots__ = ("references",)
207 item_type = "reftest"
209 def __init__(self,
210 tests_root, # type: Text
211 path, # type: Text
212 url_base, # type: Text
213 url, # type: Text
214 references=None, # type: Optional[List[Tuple[Text, Text]]]
215 **extras # type: Any
217 super(RefTest, self).__init__(tests_root, path, url_base, url, **extras)
218 if references is None:
219 self.references = [] # type: List[Tuple[Text, Text]]
220 else:
221 self.references = references
223 @property
224 def timeout(self):
225 # type: () -> Optional[Text]
226 return self._extras.get("timeout")
228 @property
229 def viewport_size(self):
230 # type: () -> Optional[Text]
231 return self._extras.get("viewport_size")
233 @property
234 def dpi(self):
235 # type: () -> Optional[Text]
236 return self._extras.get("dpi")
238 @property
239 def fuzzy(self):
240 # type: () -> Fuzzy
241 fuzzy = self._extras.get("fuzzy", {}) # type: Union[Fuzzy, List[Tuple[Optional[Sequence[Text]], List[int]]]]
242 if not isinstance(fuzzy, list):
243 return fuzzy
245 rv = {} # type: Fuzzy
246 for k, v in fuzzy: # type: Tuple[Optional[Sequence[Text]], List[int]]
247 if k is None:
248 key = None # type: Optional[Tuple[Text, Text, Text]]
249 else:
250 # mypy types this as Tuple[Text, ...]
251 assert len(k) == 3
252 key = tuple(k) # type: ignore
253 rv[key] = v
254 return rv
256 def to_json(self): # type: ignore
257 # type: () -> Tuple[Text, List[Tuple[Text, Text]], Dict[Text, Any]]
258 rv = (self._url, self.references, {}) # type: Tuple[Text, List[Tuple[Text, Text]], Dict[Text, Any]]
259 extras = rv[-1]
260 if self.timeout is not None:
261 extras["timeout"] = self.timeout
262 if self.viewport_size is not None:
263 extras["viewport_size"] = self.viewport_size
264 if self.dpi is not None:
265 extras["dpi"] = self.dpi
266 if self.fuzzy:
267 extras["fuzzy"] = list(iteritems(self.fuzzy))
268 return rv
270 @classmethod
271 def from_json(cls, # type: ignore
272 manifest, # type: Manifest
273 path, # type: Text
274 obj # type: Tuple[Text, List[Tuple[Text, Text]], Dict[Any, Any]]
276 # type: (...) -> RefTest
277 tests_root = manifest.tests_root
278 assert tests_root is not None
279 path = to_os_path(path)
280 url, references, extras = obj
281 return cls(tests_root,
282 path,
283 manifest.url_base,
284 url,
285 references,
286 **extras)
289 class ManualTest(URLManifestItem):
290 __slots__ = ()
292 item_type = "manual"
295 class ConformanceCheckerTest(URLManifestItem):
296 __slots__ = ()
298 item_type = "conformancechecker"
301 class VisualTest(URLManifestItem):
302 __slots__ = ()
304 item_type = "visual"
307 class CrashTest(URLManifestItem):
308 __slots__ = ()
310 item_type = "crashtest"
312 @property
313 def timeout(self):
314 # type: () -> Optional[Text]
315 return None
318 class WebDriverSpecTest(URLManifestItem):
319 __slots__ = ()
321 item_type = "wdspec"
323 @property
324 def timeout(self):
325 # type: () -> Optional[Text]
326 return self._extras.get("timeout")
328 def to_json(self):
329 # type: () -> Tuple[Text, Dict[Text, Any]]
330 rv = super(WebDriverSpecTest, self).to_json()
331 if self.timeout is not None:
332 rv[-1]["timeout"] = self.timeout
333 return rv
336 class SupportFile(ManifestItem):
337 __slots__ = ()
339 item_type = "support"
341 @property
342 def id(self):
343 # type: () -> Text
344 return self.path