layout/reftests/w3c-css/import-tests.py

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rwxr-xr-x

Integrate suggestion from review to improve consistency with existing code.

michael@0 1 #!/usr/bin/python
michael@0 2 # This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 # License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
michael@0 5 import os
michael@0 6 from optparse import OptionParser
michael@0 7 from subprocess import Popen, PIPE
michael@0 8 import xml.dom.minidom
michael@0 9 import html5lib
michael@0 10 import shutil
michael@0 11 import sys
michael@0 12 import re
michael@0 13
michael@0 14 # FIXME:
michael@0 15 # * Import more tests rather than just the very limited set currently
michael@0 16 # chosen.
michael@0 17 # * Read in a (checked-in) input file with a list of test assertions
michael@0 18 # expected to fail.
michael@0 19 # * Read in a (checked-in) input file with a list of reference choices
michael@0 20 # for tests with multiple rel="match" references. (But still go
michael@0 21 # though all those references in case they, in turn, have references.)
michael@0 22
michael@0 23 # Eventually we should import all the tests that have references. (At
michael@0 24 # least for a subset of secs. And we probably want to organize the
michael@0 25 # directory structure by spec to avoid constant file moves when files
michael@0 26 # move in the W3C repository. And we probably also want to import each
michael@0 27 # test only once, even if it covers more than one spec.)
michael@0 28
michael@0 29 # But for now, let's just import a few sets of tests.
michael@0 30
michael@0 31 gSubtrees = [
michael@0 32 os.path.join("approved", "css3-namespace", "src"),
michael@0 33 #os.path.join("approved", "css3-multicol", "src"),
michael@0 34 os.path.join("contributors", "opera", "submitted", "css3-conditional"),
michael@0 35 #os.path.join("contributors", "opera", "submitted", "multicol")
michael@0 36 ]
michael@0 37
michael@0 38 gPrefixedProperties = [
michael@0 39 "column-count",
michael@0 40 "column-fill",
michael@0 41 "column-gap",
michael@0 42 "column-rule",
michael@0 43 "column-rule-color",
michael@0 44 "column-rule-style",
michael@0 45 "column-rule-width",
michael@0 46 "columns",
michael@0 47 "column-span",
michael@0 48 "column-width"
michael@0 49 ]
michael@0 50
michael@0 51 gDefaultPreferences = {
michael@0 52 "css3-conditional": "pref(layout.css.supports-rule.enabled,true)"
michael@0 53 }
michael@0 54
michael@0 55 gLog = None
michael@0 56 gFailList = {}
michael@0 57 gDestPath = None
michael@0 58 gSrcPath = None
michael@0 59 support_dirs_mapped = set()
michael@0 60 filemap = {}
michael@0 61 speclinkmap = {}
michael@0 62 propsaddedfor = []
michael@0 63 tests = []
michael@0 64 gOptions = None
michael@0 65 gArgs = None
michael@0 66 gTestfiles = []
michael@0 67 gTestFlags = {}
michael@0 68
michael@0 69 def log_output_of(subprocess):
michael@0 70 global gLog
michael@0 71 subprocess.wait()
michael@0 72 if (subprocess.returncode != 0):
michael@0 73 raise StandardError("error while running subprocess")
michael@0 74 gLog.write(subprocess.stdout.readline().rstrip())
michael@0 75
michael@0 76 def write_log_header():
michael@0 77 global gLog, gSrcPath
michael@0 78 gLog.write("Importing revision: ")
michael@0 79 log_output_of(Popen(["hg", "parent", "--template={node}"],
michael@0 80 stdout=PIPE, cwd=gSrcPath))
michael@0 81 gLog.write("\nfrom repository: ")
michael@0 82 log_output_of(Popen(["hg", "paths", "default"],
michael@0 83 stdout=PIPE, cwd=gSrcPath))
michael@0 84 gLog.write("\n")
michael@0 85
michael@0 86 def remove_existing_dirs():
michael@0 87 global gDestPath
michael@0 88 # Remove existing directories that we're going to regenerate. This
michael@0 89 # is necessary so that we can give errors in cases where our import
michael@0 90 # might copy two files to the same location, which we do by giving
michael@0 91 # errors if a copy would overwrite a file.
michael@0 92 for dirname in os.listdir(gDestPath):
michael@0 93 fulldir = os.path.join(gDestPath, dirname)
michael@0 94 if not os.path.isdir(fulldir):
michael@0 95 continue
michael@0 96 shutil.rmtree(fulldir)
michael@0 97
michael@0 98 def populate_test_files():
michael@0 99 global gSubtrees, gTestfiles
michael@0 100 for subtree in gSubtrees:
michael@0 101 for dirpath, dirnames, filenames in os.walk(subtree, topdown=True):
michael@0 102 if "support" in dirnames:
michael@0 103 dirnames.remove("support")
michael@0 104 if "reftest" in dirnames:
michael@0 105 dirnames.remove("reftest")
michael@0 106 for f in filenames:
michael@0 107 if f == "README" or \
michael@0 108 f.find("-ref.") != -1:
michael@0 109 continue
michael@0 110 gTestfiles.append(os.path.join(dirpath, f))
michael@0 111
michael@0 112 gTestfiles.sort()
michael@0 113
michael@0 114 def copy_file(test, srcfile, destname, isSupportFile=False):
michael@0 115 global gDestPath, gLog, gSrcPath
michael@0 116 if not srcfile.startswith(gSrcPath):
michael@0 117 raise StandardError("Filename " + srcfile + " does not start with " + gSrcPath)
michael@0 118 logname = srcfile[len(gSrcPath):]
michael@0 119 gLog.write("Importing " + logname + " to " + destname + "\n")
michael@0 120 destfile = os.path.join(gDestPath, destname)
michael@0 121 destdir = os.path.dirname(destfile)
michael@0 122 if not os.path.exists(destdir):
michael@0 123 os.makedirs(destdir)
michael@0 124 if os.path.exists(destfile):
michael@0 125 raise StandardError("file " + destfile + " already exists")
michael@0 126 copy_and_prefix(test, srcfile, destfile, gPrefixedProperties, isSupportFile)
michael@0 127
michael@0 128 def copy_support_files(test, dirname, spec):
michael@0 129 if dirname in support_dirs_mapped:
michael@0 130 return
michael@0 131 support_dirs_mapped.add(dirname)
michael@0 132 support_dir = os.path.join(dirname, "support")
michael@0 133 if not os.path.exists(support_dir):
michael@0 134 return
michael@0 135 for dirpath, dirnames, filenames in os.walk(support_dir):
michael@0 136 for fn in filenames:
michael@0 137 if fn == "LOCK":
michael@0 138 continue
michael@0 139 full_fn = os.path.join(dirpath, fn)
michael@0 140 copy_file(test, full_fn, os.path.join(spec, "support", full_fn[len(support_dir)+1:]), True)
michael@0 141
michael@0 142 def map_file(fn, spec):
michael@0 143 if fn in filemap:
michael@0 144 return filemap[fn]
michael@0 145 destname = os.path.join(spec, os.path.basename(fn))
michael@0 146 filemap[fn] = destname
michael@0 147 load_flags_for(fn, spec)
michael@0 148 copy_file(destname, fn, destname, False)
michael@0 149 copy_support_files(destname, os.path.dirname(fn), spec)
michael@0 150 return destname
michael@0 151
michael@0 152 def load_flags_for(fn, spec):
michael@0 153 global gTestFlags
michael@0 154 document = get_document_for(fn, spec)
michael@0 155 destname = os.path.join(spec, os.path.basename(fn))
michael@0 156 gTestFlags[destname] = []
michael@0 157
michael@0 158 for meta in document.getElementsByTagName("meta"):
michael@0 159 name = meta.getAttribute("name")
michael@0 160 if name == "flags":
michael@0 161 gTestFlags[destname] = meta.getAttribute("content").split()
michael@0 162
michael@0 163 def get_document_for(fn, spec):
michael@0 164 document = None # an xml.dom.minidom document
michael@0 165 if fn.endswith(".htm") or fn.endswith(".html"):
michael@0 166 # An HTML file
michael@0 167 f = open(fn, "r")
michael@0 168 parser = html5lib.HTMLParser(tree=html5lib.treebuilders.getTreeBuilder("dom"))
michael@0 169 document = parser.parse(f)
michael@0 170 f.close()
michael@0 171 else:
michael@0 172 # An XML file
michael@0 173 document = xml.dom.minidom.parse(fn)
michael@0 174 return document
michael@0 175
michael@0 176 def add_test_items(fn, spec):
michael@0 177 document = get_document_for(fn, spec)
michael@0 178 refs = []
michael@0 179 notrefs = []
michael@0 180 for link in document.getElementsByTagName("link"):
michael@0 181 rel = link.getAttribute("rel")
michael@0 182 if rel == "help" and spec == None:
michael@0 183 specurl = link.getAttribute("href")
michael@0 184 startidx = specurl.find("/TR/")
michael@0 185 if startidx != -1:
michael@0 186 startidx = startidx + 4
michael@0 187 endidx = specurl.find("/", startidx)
michael@0 188 if endidx != -1:
michael@0 189 spec = str(specurl[startidx:endidx])
michael@0 190 if rel == "match":
michael@0 191 arr = refs
michael@0 192 elif rel == "mismatch":
michael@0 193 arr = notrefs
michael@0 194 else:
michael@0 195 continue
michael@0 196 arr.append(os.path.join(os.path.dirname(fn), str(link.getAttribute("href"))))
michael@0 197 if len(refs) > 1:
michael@0 198 raise StandardError("Need to add code to specify which reference we want to match.")
michael@0 199 if spec is None:
michael@0 200 raise StandardError("Could not associate test with specification")
michael@0 201 for ref in refs:
michael@0 202 tests.append(["==", map_file(fn, spec), map_file(ref, spec)])
michael@0 203 for notref in notrefs:
michael@0 204 tests.append(["!=", map_file(fn, spec), map_file(notref, spec)])
michael@0 205 # Add chained references too
michael@0 206 for ref in refs:
michael@0 207 add_test_items(ref, spec=spec)
michael@0 208 for notref in notrefs:
michael@0 209 add_test_items(notref, spec=spec)
michael@0 210
michael@0 211 def copy_and_prefix(test, aSourceFileName, aDestFileName, aProps, isSupportFile=False):
michael@0 212 global gTestFlags
michael@0 213 newFile = open(aDestFileName, 'w')
michael@0 214 unPrefixedFile = open(aSourceFileName)
michael@0 215 testName = aDestFileName[len(gDestPath)+1:]
michael@0 216 ahemFontAdded = False
michael@0 217 for line in unPrefixedFile:
michael@0 218 replacementLine = line
michael@0 219 searchRegex = "\s*<style\s*"
michael@0 220
michael@0 221 if not isSupportFile and not ahemFontAdded and 'ahem' in gTestFlags[test] and re.search(searchRegex, line):
michael@0 222 # First put our ahem font declation before the first <style>
michael@0 223 # element
michael@0 224 ahemFontDecl = "<style type=\"text/css\"><![CDATA[\n@font-face "\
michael@0 225 "{\n font-family: Ahem;\n src: url("\
michael@0 226 "\"../../../fonts/Ahem.ttf\");\n}\n]]></style>\n"
michael@0 227 newFile.write(ahemFontDecl)
michael@0 228 ahemFontAdded = True
michael@0 229
michael@0 230 for rule in aProps:
michael@0 231 replacementLine = replacementLine.replace(rule, "-moz-" + rule)
michael@0 232 newFile.write(replacementLine)
michael@0 233
michael@0 234 newFile.close()
michael@0 235 unPrefixedFile.close()
michael@0 236
michael@0 237 def read_options():
michael@0 238 global gArgs, gOptions
michael@0 239 op = OptionParser()
michael@0 240 op.usage = \
michael@0 241 '''%prog <clone of hg repository>
michael@0 242 Import reftests from a W3C hg repository clone. The location of
michael@0 243 the local clone of the hg repository must be given on the command
michael@0 244 line.'''
michael@0 245 (gOptions, gArgs) = op.parse_args()
michael@0 246 if len(gArgs) != 1:
michael@0 247 op.error("Too few arguments specified.")
michael@0 248
michael@0 249 def setup_paths():
michael@0 250 global gSubtrees, gDestPath, gSrcPath
michael@0 251 # FIXME: generate gSrcPath with consistent trailing / regardless of input.
michael@0 252 # (We currently expect the argument to have a trailing slash.)
michael@0 253 gSrcPath = gArgs[0]
michael@0 254 if not os.path.isdir(gSrcPath) or \
michael@0 255 not os.path.isdir(os.path.join(gSrcPath, ".hg")):
michael@0 256 raise StandardError("source path does not appear to be a mercurial clone")
michael@0 257
michael@0 258 gDestPath = os.path.join(os.path.dirname(os.path.realpath(__file__)), "received")
michael@0 259 newSubtrees = []
michael@0 260 for relPath in gSubtrees:
michael@0 261 newSubtrees[len(gSubtrees):] = [os.path.join(gSrcPath, relPath)]
michael@0 262 gSubtrees = newSubtrees
michael@0 263
michael@0 264 def setup_log():
michael@0 265 global gLog
michael@0 266 # Since we're going to commit the tests, we should also commit
michael@0 267 # information about where they came from.
michael@0 268 gLog = open(os.path.join(gDestPath, "import.log"), "w")
michael@0 269
michael@0 270 def read_fail_list():
michael@0 271 global gFailList
michael@0 272 dirname = os.path.realpath(__file__).split(os.path.sep)
michael@0 273 dirname = os.path.sep.join(dirname[:len(dirname)-1])
michael@0 274 failListFile = open(os.path.join(dirname, "failures.list"), "r")
michael@0 275 gFailList = [x for x in [x.lstrip().rstrip() for x in failListFile] if bool(x)
michael@0 276 and not x.startswith("#")]
michael@0 277 failListFile.close()
michael@0 278
michael@0 279 def main():
michael@0 280 global gDestPath, gLog, gTestfiles, gTestFlags, gFailList
michael@0 281 read_options()
michael@0 282 setup_paths()
michael@0 283 read_fail_list()
michael@0 284 setup_log()
michael@0 285 write_log_header()
michael@0 286 remove_existing_dirs()
michael@0 287 populate_test_files()
michael@0 288
michael@0 289 for t in gTestfiles:
michael@0 290 add_test_items(t, spec=None)
michael@0 291
michael@0 292 listfile = open(os.path.join(gDestPath, "reftest.list"), "w")
michael@0 293 listfile.write("# THIS FILE IS AUTOGENERATED BY {0}\n# DO NOT EDIT!\n".format(os.path.basename(__file__)))
michael@0 294 lastDefaultPreferences = None
michael@0 295 for test in tests:
michael@0 296 defaultPreferences = gDefaultPreferences.get(test[1].split("/")[0], None)
michael@0 297 if defaultPreferences != lastDefaultPreferences:
michael@0 298 if defaultPreferences is None:
michael@0 299 listfile.write("\ndefault-preferences\n\n")
michael@0 300 else:
michael@0 301 listfile.write("\ndefault-preferences {0}\n\n".format(defaultPreferences))
michael@0 302 lastDefaultPreferences = defaultPreferences
michael@0 303 key = 0
michael@0 304 while not test[key] in gTestFlags.keys() and key < len(test):
michael@0 305 key = key + 1
michael@0 306 testKey = test[key]
michael@0 307 if 'ahem' in gTestFlags[testKey]:
michael@0 308 test = ["HTTP(../../..)"] + test
michael@0 309 if testKey in gFailList:
michael@0 310 test = ["fails"] + test
michael@0 311 listfile.write(" ".join(test) + "\n")
michael@0 312 listfile.close()
michael@0 313
michael@0 314 gLog.close()
michael@0 315
michael@0 316 if __name__ == '__main__':
michael@0 317 main()

mercurial