Unreviewed. Update W3C WebDriver imported tests.
[WebKit-https.git] / WebDriverTests / imported / w3c / tools / wptrunner / wptrunner / wpttest.py
1 import os
2 from collections import defaultdict
3
4 from wptmanifest.parser import atoms
5
6 atom_reset = atoms["Reset"]
7 enabled_tests = set(["testharness", "reftest", "wdspec"])
8
9
10 class Result(object):
11     def __init__(self, status, message, expected=None, extra=None):
12         if status not in self.statuses:
13             raise ValueError("Unrecognised status %s" % status)
14         self.status = status
15         self.message = message
16         self.expected = expected
17         self.extra = extra
18
19     def __repr__(self):
20         return "<%s.%s %s>" % (self.__module__, self.__class__.__name__, self.status)
21
22
23 class SubtestResult(object):
24     def __init__(self, name, status, message, stack=None, expected=None):
25         self.name = name
26         if status not in self.statuses:
27             raise ValueError("Unrecognised status %s" % status)
28         self.status = status
29         self.message = message
30         self.stack = stack
31         self.expected = expected
32
33     def __repr__(self):
34         return "<%s.%s %s %s>" % (self.__module__, self.__class__.__name__, self.name, self.status)
35
36
37 class TestharnessResult(Result):
38     default_expected = "OK"
39     statuses = set(["OK", "ERROR", "TIMEOUT", "EXTERNAL-TIMEOUT", "CRASH"])
40
41
42 class TestharnessSubtestResult(SubtestResult):
43     default_expected = "PASS"
44     statuses = set(["PASS", "FAIL", "TIMEOUT", "NOTRUN"])
45
46
47 class ReftestResult(Result):
48     default_expected = "PASS"
49     statuses = set(["PASS", "FAIL", "ERROR", "TIMEOUT", "EXTERNAL-TIMEOUT", "CRASH"])
50
51
52 class WdspecResult(Result):
53     default_expected = "OK"
54     statuses = set(["OK", "ERROR", "TIMEOUT", "EXTERNAL-TIMEOUT", "CRASH"])
55
56
57 class WdspecSubtestResult(SubtestResult):
58     default_expected = "PASS"
59     statuses = set(["PASS", "FAIL", "ERROR"])
60
61
62 def get_run_info(metadata_root, product, **kwargs):
63     return RunInfo(metadata_root, product, **kwargs)
64
65
66 class RunInfo(dict):
67     def __init__(self, metadata_root, product, debug, extras=None):
68         import mozinfo
69
70         self._update_mozinfo(metadata_root)
71         self.update(mozinfo.info)
72         self["product"] = product
73         if debug is not None:
74             self["debug"] = debug
75         elif "debug" not in self:
76             # Default to release
77             self["debug"] = False
78         if product == "firefox" and "stylo" not in self:
79             self["stylo"] = False
80         if "STYLO_FORCE_ENABLED" in os.environ:
81             self["stylo"] = True
82         if "STYLO_FORCE_DISABLED" in os.environ:
83             self["stylo"] = False
84         if extras is not None:
85             self.update(extras)
86
87     def _update_mozinfo(self, metadata_root):
88         """Add extra build information from a mozinfo.json file in a parent
89         directory"""
90         import mozinfo
91
92         path = metadata_root
93         dirs = set()
94         while path != os.path.expanduser('~'):
95             if path in dirs:
96                 break
97             dirs.add(str(path))
98             path = os.path.split(path)[0]
99
100         mozinfo.find_and_update_from_json(*dirs)
101
102
103 class Test(object):
104
105     result_cls = None
106     subtest_result_cls = None
107     test_type = None
108
109     default_timeout = 10  # seconds
110     long_timeout = 60  # seconds
111
112     def __init__(self, tests_root, url, inherit_metadata, test_metadata,
113                  timeout=None, path=None, protocol="http"):
114         self.tests_root = tests_root
115         self.url = url
116         self._inherit_metadata = inherit_metadata
117         self._test_metadata = test_metadata
118         self.timeout = timeout if timeout is not None else self.default_timeout
119         self.path = path
120         self.environment = {"protocol": protocol, "prefs": self.prefs}
121
122     def __eq__(self, other):
123         return self.id == other.id
124
125     def update_metadata(self, metadata=None):
126         if metadata is None:
127             metadata = {}
128         return metadata
129
130     @classmethod
131     def from_manifest(cls, manifest_item, inherit_metadata, test_metadata):
132         timeout = cls.long_timeout if manifest_item.timeout == "long" else cls.default_timeout
133         protocol = "https" if hasattr(manifest_item, "https") and manifest_item.https else "http"
134         return cls(manifest_item.source_file.tests_root,
135                    manifest_item.url,
136                    inherit_metadata,
137                    test_metadata,
138                    timeout=timeout,
139                    path=manifest_item.source_file.path,
140                    protocol=protocol)
141
142     @property
143     def id(self):
144         return self.url
145
146     @property
147     def keys(self):
148         return tuple()
149
150     @property
151     def abs_path(self):
152         return os.path.join(self.tests_root, self.path)
153
154     def _get_metadata(self, subtest=None):
155         if self._test_metadata is not None and subtest is not None:
156             return self._test_metadata.get_subtest(subtest)
157         else:
158             return self._test_metadata
159
160     def itermeta(self, subtest=None):
161         for metadata in self._inherit_metadata:
162             yield metadata
163
164         if self._test_metadata is not None:
165             yield self._get_metadata()
166             if subtest is not None:
167                 subtest_meta = self._get_metadata(subtest)
168                 if subtest_meta is not None:
169                     yield subtest_meta
170
171     def disabled(self, subtest=None):
172         for meta in self.itermeta(subtest):
173             disabled = meta.disabled
174             if disabled is not None:
175                 return disabled
176         return None
177
178     @property
179     def restart_after(self):
180         for meta in self.itermeta(None):
181             restart_after = meta.restart_after
182             if restart_after is not None:
183                 return True
184         return False
185
186     @property
187     def leaks(self):
188         for meta in self.itermeta(None):
189             leaks = meta.leaks
190             if leaks is not None:
191                 return leaks
192         return False
193
194     @property
195     def tags(self):
196         tags = set()
197         for meta in self.itermeta():
198             meta_tags = meta.tags
199             if atom_reset in meta_tags:
200                 tags = meta_tags.copy()
201                 tags.remove(atom_reset)
202             else:
203                 tags |= meta_tags
204
205         tags.add("dir:%s" % self.id.lstrip("/").split("/")[0])
206
207         return tags
208
209     @property
210     def prefs(self):
211         prefs = {}
212         for meta in self.itermeta():
213             meta_prefs = meta.prefs
214             if atom_reset in prefs:
215                 prefs = meta_prefs.copy()
216                 del prefs[atom_reset]
217             else:
218                 prefs.update(meta_prefs)
219         return prefs
220
221     def expected(self, subtest=None):
222         if subtest is None:
223             default = self.result_cls.default_expected
224         else:
225             default = self.subtest_result_cls.default_expected
226
227         metadata = self._get_metadata(subtest)
228         if metadata is None:
229             return default
230
231         try:
232             return metadata.get("expected")
233         except KeyError:
234             return default
235
236     def __repr__(self):
237         return "<%s.%s %s>" % (self.__module__, self.__class__.__name__, self.id)
238
239
240 class TestharnessTest(Test):
241     result_cls = TestharnessResult
242     subtest_result_cls = TestharnessSubtestResult
243     test_type = "testharness"
244
245     def __init__(self, tests_root, url, inherit_metadata, test_metadata,
246                  timeout=None, path=None, protocol="http", testdriver=False):
247         Test.__init__(self, tests_root, url, inherit_metadata, test_metadata, timeout,
248                       path, protocol)
249
250         self.testdriver = testdriver
251
252     @classmethod
253     def from_manifest(cls, manifest_item, inherit_metadata, test_metadata):
254         timeout = cls.long_timeout if manifest_item.timeout == "long" else cls.default_timeout
255         protocol = "https" if hasattr(manifest_item, "https") and manifest_item.https else "http"
256         testdriver = manifest_item.testdriver if hasattr(manifest_item, "testdriver") else False
257         return cls(manifest_item.source_file.tests_root,
258                    manifest_item.url,
259                    inherit_metadata,
260                    test_metadata,
261                    timeout=timeout,
262                    path=manifest_item.source_file.path,
263                    protocol=protocol,
264                    testdriver=testdriver)
265
266     @property
267     def id(self):
268         return self.url
269
270
271 class ManualTest(Test):
272     test_type = "manual"
273
274     @property
275     def id(self):
276         return self.url
277
278
279 class ReftestTest(Test):
280     result_cls = ReftestResult
281     test_type = "reftest"
282
283     def __init__(self, tests_root, url, inherit_metadata, test_metadata, references,
284                  timeout=None, path=None, viewport_size=None, dpi=None, protocol="http"):
285         Test.__init__(self, tests_root, url, inherit_metadata, test_metadata, timeout,
286                       path, protocol)
287
288         for _, ref_type in references:
289             if ref_type not in ("==", "!="):
290                 raise ValueError
291
292         self.references = references
293         self.viewport_size = viewport_size
294         self.dpi = dpi
295
296     @classmethod
297     def from_manifest(cls,
298                       manifest_test,
299                       inherit_metadata,
300                       test_metadata,
301                       nodes=None,
302                       references_seen=None):
303
304         timeout = cls.long_timeout if manifest_test.timeout == "long" else cls.default_timeout
305
306         if nodes is None:
307             nodes = {}
308         if references_seen is None:
309             references_seen = set()
310
311         url = manifest_test.url
312
313         node = cls(manifest_test.source_file.tests_root,
314                    manifest_test.url,
315                    inherit_metadata,
316                    test_metadata,
317                    [],
318                    timeout=timeout,
319                    path=manifest_test.path,
320                    viewport_size=manifest_test.viewport_size,
321                    dpi=manifest_test.dpi,
322                    protocol="https" if hasattr(manifest_test, "https") and manifest_test.https else "http")
323
324         nodes[url] = node
325
326         for ref_url, ref_type in manifest_test.references:
327             comparison_key = (ref_type,) + tuple(sorted([url, ref_url]))
328             if ref_url in nodes:
329                 manifest_node = ref_url
330                 if comparison_key in references_seen:
331                     # We have reached a cycle so stop here
332                     # Note that just seeing a node for the second time is not
333                     # enough to detect a cycle because
334                     # A != B != C != A must include C != A
335                     # but A == B == A should not include the redundant B == A.
336                     continue
337
338             references_seen.add(comparison_key)
339
340             manifest_node = manifest_test.manifest.get_reference(ref_url)
341             if manifest_node:
342                 reference = ReftestTest.from_manifest(manifest_node,
343                                                       [],
344                                                       None,
345                                                       nodes,
346                                                       references_seen)
347             else:
348                 reference = ReftestTest(manifest_test.source_file.tests_root,
349                                         ref_url,
350                                         [],
351                                         None,
352                                         [])
353
354             node.references.append((reference, ref_type))
355
356         return node
357
358     def update_metadata(self, metadata):
359         if not "url_count" in metadata:
360             metadata["url_count"] = defaultdict(int)
361         for reference, _ in self.references:
362             # We assume a naive implementation in which a url with multiple
363             # possible screenshots will need to take both the lhs and rhs screenshots
364             # for each possible match
365             metadata["url_count"][(self.environment["protocol"], reference.url)] += 1
366             reference.update_metadata(metadata)
367         return metadata
368
369     @property
370     def id(self):
371         return self.url
372
373     @property
374     def keys(self):
375         return ("reftype", "refurl")
376
377
378 class WdspecTest(Test):
379
380     result_cls = WdspecResult
381     subtest_result_cls = WdspecSubtestResult
382     test_type = "wdspec"
383
384     default_timeout = 25
385     long_timeout = 120
386
387
388 manifest_test_cls = {"reftest": ReftestTest,
389                      "testharness": TestharnessTest,
390                      "manual": ManualTest,
391                      "wdspec": WdspecTest}
392
393
394 def from_manifest(manifest_test, inherit_metadata, test_metadata):
395     test_cls = manifest_test_cls[manifest_test.item_type]
396     return test_cls.from_manifest(manifest_test, inherit_metadata, test_metadata)