86320f8ff84353637262c1a055ec84a12eb44356
[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     @property
246     def id(self):
247         return self.url
248
249
250 class ManualTest(Test):
251     test_type = "manual"
252
253     @property
254     def id(self):
255         return self.url
256
257
258 class ReftestTest(Test):
259     result_cls = ReftestResult
260     test_type = "reftest"
261
262     def __init__(self, tests_root, url, inherit_metadata, test_metadata, references,
263                  timeout=None, path=None, viewport_size=None, dpi=None, protocol="http"):
264         Test.__init__(self, tests_root, url, inherit_metadata, test_metadata, timeout,
265                       path, protocol)
266
267         for _, ref_type in references:
268             if ref_type not in ("==", "!="):
269                 raise ValueError
270
271         self.references = references
272         self.viewport_size = viewport_size
273         self.dpi = dpi
274
275     @classmethod
276     def from_manifest(cls,
277                       manifest_test,
278                       inherit_metadata,
279                       test_metadata,
280                       nodes=None,
281                       references_seen=None):
282
283         timeout = cls.long_timeout if manifest_test.timeout == "long" else cls.default_timeout
284
285         if nodes is None:
286             nodes = {}
287         if references_seen is None:
288             references_seen = set()
289
290         url = manifest_test.url
291
292         node = cls(manifest_test.source_file.tests_root,
293                    manifest_test.url,
294                    inherit_metadata,
295                    test_metadata,
296                    [],
297                    timeout=timeout,
298                    path=manifest_test.path,
299                    viewport_size=manifest_test.viewport_size,
300                    dpi=manifest_test.dpi,
301                    protocol="https" if hasattr(manifest_test, "https") and manifest_test.https else "http")
302
303         nodes[url] = node
304
305         for ref_url, ref_type in manifest_test.references:
306             comparison_key = (ref_type,) + tuple(sorted([url, ref_url]))
307             if ref_url in nodes:
308                 manifest_node = ref_url
309                 if comparison_key in references_seen:
310                     # We have reached a cycle so stop here
311                     # Note that just seeing a node for the second time is not
312                     # enough to detect a cycle because
313                     # A != B != C != A must include C != A
314                     # but A == B == A should not include the redundant B == A.
315                     continue
316
317             references_seen.add(comparison_key)
318
319             manifest_node = manifest_test.manifest.get_reference(ref_url)
320             if manifest_node:
321                 reference = ReftestTest.from_manifest(manifest_node,
322                                                       [],
323                                                       None,
324                                                       nodes,
325                                                       references_seen)
326             else:
327                 reference = ReftestTest(manifest_test.source_file.tests_root,
328                                         ref_url,
329                                         [],
330                                         None,
331                                         [])
332
333             node.references.append((reference, ref_type))
334
335         return node
336
337     def update_metadata(self, metadata):
338         if not "url_count" in metadata:
339             metadata["url_count"] = defaultdict(int)
340         for reference, _ in self.references:
341             # We assume a naive implementation in which a url with multiple
342             # possible screenshots will need to take both the lhs and rhs screenshots
343             # for each possible match
344             metadata["url_count"][(self.environment["protocol"], reference.url)] += 1
345             reference.update_metadata(metadata)
346         return metadata
347
348     @property
349     def id(self):
350         return self.url
351
352     @property
353     def keys(self):
354         return ("reftype", "refurl")
355
356
357 class WdspecTest(Test):
358
359     result_cls = WdspecResult
360     subtest_result_cls = WdspecSubtestResult
361     test_type = "wdspec"
362
363     default_timeout = 25
364     long_timeout = 120
365
366
367 manifest_test_cls = {"reftest": ReftestTest,
368                      "testharness": TestharnessTest,
369                      "manual": ManualTest,
370                      "wdspec": WdspecTest}
371
372
373 def from_manifest(manifest_test, inherit_metadata, test_metadata):
374     test_cls = manifest_test_cls[manifest_test.item_type]
375     return test_cls.from_manifest(manifest_test, inherit_metadata, test_metadata)