addtest.py (11491B)
1 import os 2 3 import manifestparser 4 5 6 class Creator: 7 def __init__(self, topsrcdir, test, suite, doc, **kwargs): 8 self.topsrcdir = topsrcdir 9 self.test = test 10 self.suite = suite 11 self.doc = doc 12 self.kwargs = kwargs 13 14 def check_args(self): 15 """Perform any validation required for suite-specific arguments""" 16 return True 17 18 def __iter__(self): 19 """Iterate over a list of (path, data) tuples corresponding to the files 20 to be created""" 21 yield (self.test, self._get_template_contents()) 22 23 def _get_template_contents(self, **kwargs): 24 raise NotImplementedError 25 26 def update_manifest(self): 27 """Perform any manifest updates required to register the added tests""" 28 raise NotImplementedError 29 30 31 class XpcshellCreator(Creator): 32 template_body = """/* Any copyright is dedicated to the Public Domain. 33 https://creativecommons.org/publicdomain/zero/1.0/ */ 34 35 "use strict"; 36 37 add_task(async function test_TODO() { 38 ok(true, "TODO: implement the test"); 39 }); 40 """ 41 42 def _get_template_contents(self): 43 return self.template_body 44 45 def update_manifest(self): 46 update_toml_or_ini("xpcshell", self.test) 47 48 49 class MochitestCreator(Creator): 50 templates = { 51 "mochitest-browser-chrome": "browser.template.txt", 52 "mochitest-plain": "plain%(doc)s.template.txt", 53 "mochitest-chrome": "chrome%(doc)s.template.txt", 54 } 55 56 def _get_template_contents(self): 57 mochitest_templates = os.path.abspath( 58 os.path.join(os.path.dirname(__file__), "mochitest", "static") 59 ) 60 template_file_name = None 61 62 template_file_name = self.templates.get(self.suite) 63 64 if template_file_name is None: 65 print(f"Sorry, `addtest` doesn't currently know how to add {self.suite}") 66 return None 67 68 template_file_name = template_file_name % {"doc": self.doc} 69 70 template_file = os.path.join(mochitest_templates, template_file_name) 71 if not os.path.isfile(template_file): 72 print( 73 f"Sorry, `addtest` doesn't currently know how to add {self.suite} with document type {self.doc}" 74 ) 75 return None 76 77 with open(template_file) as f: 78 return f.read() 79 80 def update_manifest(self): 81 # attempt to insert into the appropriate manifest 82 guessed_prefix = { 83 "mochitest-plain": "mochitest", 84 "mochitest-chrome": "chrome", 85 "mochitest-browser-chrome": "browser", 86 }[self.suite] 87 88 update_toml_or_ini(guessed_prefix, self.test) 89 90 91 class WebPlatformTestsCreator(Creator): 92 template_prefix = """<!doctype html> 93 %(documentElement)s<meta charset=utf-8> 94 """ 95 template_long_timeout = "<meta name=timeout content=long>\n" 96 97 template_body_th = """<title></title> 98 <script src=/resources/testharness.js></script> 99 <script src=/resources/testharnessreport.js></script> 100 <script> 101 102 </script> 103 """ 104 105 template_body_reftest = """<title></title> 106 <link rel=%(match)s href=%(ref)s> 107 """ 108 109 template_body_reftest_wait = """<script src="/common/reftest-wait.js"></script> 110 """ 111 112 template_js = "" 113 template_js_long_timeout = "//META: timeout=long\n" 114 115 upstream_path = os.path.join("testing", "web-platform", "tests") 116 local_path = os.path.join("testing", "web-platform", "mozilla", "tests") 117 118 def __init__(self, *args, **kwargs): 119 super().__init__(*args, **kwargs) 120 self.reftest = self.suite == "web-platform-tests-reftest" 121 122 @classmethod 123 def get_parser(cls, parser): 124 parser.add_argument( 125 "--long-timeout", 126 action="store_true", 127 help="Test should be given a long timeout " 128 "(typically 60s rather than 10s, but varies depending on environment)", 129 ) 130 parser.add_argument( 131 "-m", "--reference", dest="ref", help="Path to the reference file" 132 ) 133 parser.add_argument( 134 "--mismatch", action="store_true", help="Create a mismatch reftest" 135 ) 136 parser.add_argument( 137 "--wait", 138 action="store_true", 139 help="Create a reftest that waits until takeScreenshot() is called", 140 ) 141 142 def check_args(self): 143 if self.wpt_type(self.test) is None: 144 print( 145 """Test path %s is not in wpt directories: 146 testing/web-platform/tests for tests that may be shared 147 testing/web-platform/mozilla/tests for Gecko-only tests""" 148 % self.test 149 ) 150 return False 151 152 if not self.reftest: 153 if self.kwargs["ref"]: 154 print("--ref only makes sense for a reftest") 155 return False 156 157 if self.kwargs["mismatch"]: 158 print("--mismatch only makes sense for a reftest") 159 return False 160 161 if self.kwargs["wait"]: 162 print("--wait only makes sense for a reftest") 163 return False 164 # Set the ref to a url relative to the test 165 elif self.kwargs["ref"]: 166 if self.ref_path(self.kwargs["ref"]) is None: 167 print("--ref doesn't refer to a path inside web-platform-tests") 168 return False 169 170 def __iter__(self): 171 yield (self.test, self._get_template_contents()) 172 173 if self.reftest and self.kwargs["ref"]: 174 ref_path = self.ref_path(self.kwargs["ref"]) 175 yield (ref_path, self._get_template_contents(reference=True)) 176 177 def _get_template_contents(self, reference=False): 178 args = { 179 "documentElement": ( 180 "<html class=reftest-wait>\n" if self.kwargs["wait"] else "" 181 ) 182 } 183 184 if self.test.rsplit(".", 1)[1] == "js": 185 template = self.template_js 186 if self.kwargs["long_timeout"]: 187 template += self.template_js_long_timeout 188 else: 189 template = self.template_prefix % args 190 if self.kwargs["long_timeout"]: 191 template += self.template_long_timeout 192 193 if self.reftest: 194 if not reference: 195 args = { 196 "match": "match" if not self.kwargs["mismatch"] else "mismatch", 197 "ref": ( 198 self.ref_url(self.kwargs["ref"]) 199 if self.kwargs["ref"] 200 else '""' 201 ), 202 } 203 template += self.template_body_reftest % args 204 if self.kwargs["wait"]: 205 template += self.template_body_reftest_wait 206 else: 207 template += "<title></title>" 208 else: 209 template += self.template_body_th 210 211 return template 212 213 def update_manifest(self): 214 pass 215 216 def src_rel_path(self, path): 217 if path is None: 218 return 219 220 abs_path = os.path.normpath(os.path.abspath(path)) 221 return os.path.relpath(abs_path, self.topsrcdir) 222 223 def wpt_type(self, path): 224 path = self.src_rel_path(path) 225 if path.startswith(self.upstream_path): 226 return "upstream" 227 elif path.startswith(self.local_path): 228 return "local" 229 return None 230 231 def ref_path(self, path): 232 # The ref parameter can be one of several things 233 # 1. An absolute path to a reference file 234 # 2. A path to a file relative to the topsrcdir 235 # 3. A path relative to the test file 236 # These are not unambiguous, so it's somewhat best effort 237 238 if os.path.isabs(path): 239 path = os.path.normpath(path) 240 if not path.startswith(self.topsrcdir): 241 # Path is an absolute URL relative to the tests root 242 if path.startswith("/_mozilla/"): 243 base = self.local_path 244 path = path[len("/_mozilla/") :] 245 else: 246 base = self.upstream_path 247 path = path[1:] 248 path = path.replace("/", os.sep) 249 return os.path.join(base, path) 250 else: 251 return self.src_rel_path(path) 252 elif self.wpt_type(path) is not None: 253 return path 254 else: 255 test_rel_path = self.src_rel_path( 256 os.path.join(os.path.dirname(self.test), path) 257 ) 258 if self.wpt_type(test_rel_path) is not None: 259 return test_rel_path 260 # Returning None indicates that the path wasn't valid 261 262 def ref_url(self, path): 263 ref_path = self.ref_path(path) 264 if not ref_path: 265 return 266 267 if path[0] == "/" and len(path) < len(ref_path): 268 # This is an absolute url 269 return path 270 271 # Othewise it's a file path 272 wpt_type_ref = self.wpt_type(ref_path) 273 wpt_type_test = self.wpt_type(self.test) 274 if wpt_type_ref == wpt_type_test: 275 return os.path.relpath(ref_path, os.path.dirname(self.test)) 276 277 # If we have a local test referencing an upstream ref, 278 # or vice-versa use absolute paths 279 if wpt_type_ref == "upstream": 280 rel_path = os.path.relpath(ref_path, self.upstream_path) 281 url_base = "/" 282 elif wpt_type_ref == "local": 283 rel_path = os.path.relpath(ref_path, self.local_path) 284 url_base = "/_mozilla/" 285 else: 286 return None 287 return url_base + rel_path.replace(os.path.sep, "/") 288 289 290 # Insert a new test in the right place 291 def update_toml_or_ini(manifest_prefix, testpath): 292 basedir = os.path.dirname(testpath) 293 manifest_file = os.path.join(basedir, manifest_prefix + ".toml") 294 if not os.path.isfile(manifest_file): 295 manifest_file = os.path.join(basedir, manifest_prefix + ".ini") 296 if not os.path.isfile(manifest_file): 297 print(f"Could not open manifest file {manifest_file}") 298 return 299 filename = os.path.basename(testpath) 300 write_to_manifest_file(manifest_file, filename) 301 302 303 # Insert a new test in the right place within a given manifest file 304 def write_to_manifest_file(manifest_file, filename): 305 use_toml = manifest_file.endswith(".toml") 306 manifest = manifestparser.TestManifest(manifests=[manifest_file], use_toml=use_toml) 307 insert_before = None 308 309 if any(t["name"] == filename for t in manifest.tests): 310 print(f"{filename} is already in the manifest.") 311 return 312 313 for test in manifest.tests: 314 if test.get("name") > filename: 315 insert_before = test.get("name") 316 break 317 318 with open(manifest_file) as f: 319 contents = f.readlines() 320 321 entry_line = '["{}"]\n' if use_toml else "[{}]" 322 filename = (entry_line + "\n").format(filename) 323 324 if not insert_before: 325 contents.append(filename) 326 else: 327 insert_before = entry_line.format(insert_before) 328 for i in range(len(contents)): 329 if contents[i].startswith(insert_before): 330 contents.insert(i, filename) 331 break 332 333 with open(manifest_file, "w", newline="\n") as f: 334 f.write("".join(contents)) 335 336 337 TEST_CREATORS = { 338 "mochitest": MochitestCreator, 339 "web-platform-tests": WebPlatformTestsCreator, 340 "xpcshell": XpcshellCreator, 341 } 342 343 344 def creator_for_suite(suite): 345 for key, creator in TEST_CREATORS.items(): 346 if suite.startswith(key): 347 return creator 348 return None