1import os
2from six.moves.urllib.parse import urljoin
3from abc import ABCMeta, abstractmethod, abstractproperty
4
5
6def get_source_file(source_files, tests_root, manifest, path):
7    def make_new():
8        from .sourcefile import SourceFile
9
10        return SourceFile(tests_root, path, manifest.url_base)
11
12    if source_files is None:
13        return make_new()
14
15    if path not in source_files:
16        source_files[path] = make_new()
17
18    return source_files[path]
19
20
21class ManifestItem(object):
22    __metaclass__ = ABCMeta
23
24    item_type = None
25
26    def __init__(self, source_file, manifest=None):
27        self.manifest = manifest
28        self.source_file = source_file
29
30    @abstractproperty
31    def id(self):
32        """The test's id (usually its url)"""
33        pass
34
35    @property
36    def path(self):
37        """The test path relative to the test_root"""
38        return self.source_file.rel_path
39
40    @property
41    def https(self):
42        return "https" in self.source_file.meta_flags
43
44    def key(self):
45        """A unique identifier for the test"""
46        return (self.item_type, self.id)
47
48    def meta_key(self):
49        """Extra metadata that doesn't form part of the test identity, but for
50        which changes mean regenerating the manifest (e.g. the test timeout."""
51        return ()
52
53    def __eq__(self, other):
54        if not hasattr(other, "key"):
55            return False
56        return self.key() == other.key()
57
58    def __hash__(self):
59        return hash(self.key() + self.meta_key())
60
61    def __repr__(self):
62        return "<%s.%s id=%s, path=%s>" % (self.__module__, self.__class__.__name__, self.id, self.path)
63
64    def to_json(self):
65        return [{}]
66
67    @classmethod
68    def from_json(cls, manifest, tests_root, path, obj, source_files=None):
69        source_file = get_source_file(source_files, tests_root, manifest, path)
70        return cls(source_file,
71                   manifest=manifest)
72
73
74class URLManifestItem(ManifestItem):
75    def __init__(self, source_file, url, url_base="/", manifest=None):
76        ManifestItem.__init__(self, source_file, manifest=manifest)
77        self._url = url
78        self.url_base = url_base
79
80    @property
81    def id(self):
82        return self.url
83
84    @property
85    def url(self):
86        return urljoin(self.url_base, self._url)
87
88    def to_json(self):
89        rv = [self._url, {}]
90        return rv
91
92    @classmethod
93    def from_json(cls, manifest, tests_root, path, obj, source_files=None):
94        source_file = get_source_file(source_files, tests_root, manifest, path)
95        url, extras = obj
96        return cls(source_file,
97                   url,
98                   url_base=manifest.url_base,
99                   manifest=manifest)
100
101
102class TestharnessTest(URLManifestItem):
103    item_type = "testharness"
104
105    def __init__(self, source_file, url, url_base="/", timeout=None, testdriver=False, manifest=None):
106        URLManifestItem.__init__(self, source_file, url, url_base=url_base, manifest=manifest)
107        self.timeout = timeout
108        self.testdriver = testdriver
109
110    def meta_key(self):
111        return (self.timeout, self.testdriver)
112
113    def to_json(self):
114        rv = URLManifestItem.to_json(self)
115        if self.timeout is not None:
116            rv[-1]["timeout"] = self.timeout
117        if self.testdriver:
118            rv[-1]["testdriver"] = self.testdriver
119        return rv
120
121    @classmethod
122    def from_json(cls, manifest, tests_root, path, obj, source_files=None):
123        source_file = get_source_file(source_files, tests_root, manifest, path)
124
125        url, extras = obj
126        return cls(source_file,
127                   url,
128                   url_base=manifest.url_base,
129                   timeout=extras.get("timeout"),
130                   testdriver=bool(extras.get("testdriver")),
131                   manifest=manifest)
132
133
134class RefTestNode(URLManifestItem):
135    item_type = "reftest_node"
136
137    def __init__(self, source_file, url, references, url_base="/", timeout=None,
138                 viewport_size=None, dpi=None, manifest=None):
139        URLManifestItem.__init__(self, source_file, url, url_base=url_base, manifest=manifest)
140        for _, ref_type in references:
141            if ref_type not in ["==", "!="]:
142                raise ValueError("Unrecognised ref_type %s" % ref_type)
143        self.references = tuple(references)
144        self.timeout = timeout
145        self.viewport_size = viewport_size
146        self.dpi = dpi
147
148    def meta_key(self):
149        return (self.timeout, self.viewport_size, self.dpi)
150
151    def to_json(self):
152        rv = [self.url, self.references, {}]
153        extras = rv[-1]
154        if self.timeout is not None:
155            extras["timeout"] = self.timeout
156        if self.viewport_size is not None:
157            extras["viewport_size"] = self.viewport_size
158        if self.dpi is not None:
159            extras["dpi"] = self.dpi
160        return rv
161
162    @classmethod
163    def from_json(cls, manifest, tests_root, path, obj, source_files=None):
164        source_file = get_source_file(source_files, tests_root, manifest, path)
165        url, references, extras = obj
166        return cls(source_file,
167                   url,
168                   references,
169                   url_base=manifest.url_base,
170                   timeout=extras.get("timeout"),
171                   viewport_size=extras.get("viewport_size"),
172                   dpi=extras.get("dpi"),
173                   manifest=manifest)
174
175    def to_RefTest(self):
176        if type(self) == RefTest:
177            return self
178        rv = RefTest.__new__(RefTest)
179        rv.__dict__.update(self.__dict__)
180        return rv
181
182    def to_RefTestNode(self):
183        if type(self) == RefTestNode:
184            return self
185        rv = RefTestNode.__new__(RefTestNode)
186        rv.__dict__.update(self.__dict__)
187        return rv
188
189
190class RefTest(RefTestNode):
191    item_type = "reftest"
192
193
194class ManualTest(URLManifestItem):
195    item_type = "manual"
196
197
198class ConformanceCheckerTest(URLManifestItem):
199    item_type = "conformancechecker"
200
201
202class VisualTest(URLManifestItem):
203    item_type = "visual"
204
205
206class Stub(URLManifestItem):
207    item_type = "stub"
208
209
210class WebdriverSpecTest(URLManifestItem):
211    item_type = "wdspec"
212
213    def __init__(self, source_file, url, url_base="/", timeout=None, manifest=None):
214        URLManifestItem.__init__(self, source_file, url, url_base=url_base, manifest=manifest)
215        self.timeout = timeout
216
217    def to_json(self):
218        rv = URLManifestItem.to_json(self)
219        if self.timeout is not None:
220            rv[-1]["timeout"] = self.timeout
221        return rv
222
223    @classmethod
224    def from_json(cls, manifest, tests_root, path, obj, source_files=None):
225        source_file = get_source_file(source_files, tests_root, manifest, path)
226
227        url, extras = obj
228        return cls(source_file,
229                   url,
230                   url_base=manifest.url_base,
231                   timeout=extras.get("timeout"),
232                   manifest=manifest)
233
234
235class SupportFile(ManifestItem):
236    item_type = "support"
237
238    @property
239    def id(self):
240        return self.source_file.rel_path
241