summaryrefslogtreecommitdiffstats
path: root/dom/canvas/test/webgl-conf/generate-wrappers-and-manifest.py
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-xdom/canvas/test/webgl-conf/generate-wrappers-and-manifest.py577
1 files changed, 577 insertions, 0 deletions
diff --git a/dom/canvas/test/webgl-conf/generate-wrappers-and-manifest.py b/dom/canvas/test/webgl-conf/generate-wrappers-and-manifest.py
new file mode 100755
index 0000000000..b8f4264994
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/generate-wrappers-and-manifest.py
@@ -0,0 +1,577 @@
+#!/usr/bin/env python3
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# Write a Mochitest manifest for WebGL conformance test files.
+
+import os
+import re
+import shutil
+from pathlib import Path
+
+# All paths in this file are based where this file is run.
+WRAPPER_TEMPLATE_FILE = "mochi-wrapper.html.template"
+MANIFEST_TEMPLATE_FILE = "mochitest.ini.template"
+ERRATA_FILE = "mochitest-errata.ini"
+DEST_MANIFEST_PATHSTR = "generated-mochitest.ini"
+
+BASE_TEST_LIST_PATHSTR = "checkout/00_test_list.txt"
+GENERATED_PATHSTR = "generated"
+WEBGL2_TEST_MANGLE = "2_"
+PATH_SEP_MANGLING = "__"
+
+SUPPORT_DIRS = [
+ "checkout",
+]
+
+EXTRA_SUPPORT_FILES = [
+ "always-fail.html",
+ "iframe-passthrough.css",
+ "mochi-single.html",
+]
+
+ACCEPTABLE_ERRATA_KEYS = set(
+ [
+ "fail-if",
+ "skip-if",
+ "prefs",
+ ]
+)
+
+
+def ChooseSubsuite(name):
+ # name: generated/test_2_conformance2__vertex_arrays__vertex-array-object.html
+ assert " " not in name, name
+
+ split = name.split("__")
+
+ version = "1"
+ if "/test_2_" in split[0]:
+ version = "2"
+
+ category = "core"
+
+ split[0] = split[0].split("/")[1]
+ if "deqp" in split[0]:
+ if version == "1":
+ # There's few enough that we'll just merge them with webgl1-ext.
+ category = "ext"
+ else:
+ category = "deqp"
+ elif "conformance" in split[0]:
+ if split[1] in ("glsl", "glsl3", "ogles"):
+ category = "ext"
+ elif split[1] == "textures" and split[2] != "misc":
+ category = "ext"
+
+ return "webgl{}-{}".format(version, category)
+
+
+########################################################################
+# GetTestList
+
+
+def GetTestList():
+ split = BASE_TEST_LIST_PATHSTR.rsplit("/", 1)
+ basePath = "."
+ testListFile = split[-1]
+ if len(split) == 2:
+ basePath = split[0]
+
+ allowWebGL1 = True
+ allowWebGL2 = True
+ alwaysFailEntry = TestEntry("always-fail.html", True, False)
+ testList = [alwaysFailEntry]
+ AccumTests(basePath, testListFile, allowWebGL1, allowWebGL2, testList)
+
+ for x in testList:
+ x.path = os.path.relpath(x.path, basePath).replace(os.sep, "/")
+ continue
+
+ return testList
+
+
+##############################
+# Internals
+
+
+def IsVersionLess(a, b):
+ aSplit = [int(x) for x in a.split(".")]
+ bSplit = [int(x) for x in b.split(".")]
+
+ while len(aSplit) < len(bSplit):
+ aSplit.append(0)
+
+ while len(aSplit) > len(bSplit):
+ bSplit.append(0)
+
+ for i in range(len(aSplit)):
+ aVal = aSplit[i]
+ bVal = bSplit[i]
+
+ if aVal == bVal:
+ continue
+
+ return aVal < bVal
+
+ return False
+
+
+class TestEntry:
+ def __init__(self, path, webgl1, webgl2):
+ self.path = path
+ self.webgl1 = webgl1
+ self.webgl2 = webgl2
+ return
+
+
+def AccumTests(pathStr, listFile, allowWebGL1, allowWebGL2, out_testList):
+ listPathStr = pathStr + "/" + listFile
+
+ listPath = listPathStr.replace("/", os.sep)
+ assert os.path.exists(listPath), "Bad `listPath`: " + listPath
+
+ with open(listPath, "r") as fIn:
+ lineNum = 0
+ for line in fIn:
+ lineNum += 1
+
+ curLine = line.strip()
+ if not curLine:
+ continue
+ if curLine.startswith("//"):
+ continue
+ if curLine.startswith("#"):
+ continue
+
+ webgl1 = allowWebGL1
+ webgl2 = allowWebGL2
+ parts = curLine.split()
+ while parts[0].startswith("--"): # '--min-version 1.0.2 foo.html'
+ flag = parts.pop(0)
+ if flag == "--min-version":
+ minVersion = parts.pop(0)
+ if not IsVersionLess(minVersion, "2.0.0"): # >= 2.0.0
+ webgl1 = False
+ break
+ elif flag == "--max-version":
+ maxVersion = parts.pop(0)
+ if IsVersionLess(maxVersion, "2.0.0"):
+ webgl2 = False
+ break
+ elif flag == "--slow":
+ continue # TODO
+ else:
+ text = "Unknown flag '{}': {}:{}: {}".format(
+ flag, listPath, lineNum, line
+ )
+ assert False, text
+ continue
+
+ assert webgl1 or webgl2
+ assert len(parts) == 1, parts
+ testOrManifest = parts[0]
+
+ split = testOrManifest.rsplit(".", 1)
+ assert len(split) == 2, "Bad split for `line`: " + line
+ (name, ext) = split
+
+ if ext == "html":
+ newTestFilePathStr = pathStr + "/" + testOrManifest
+ entry = TestEntry(newTestFilePathStr, webgl1, webgl2)
+ out_testList.append(entry)
+ continue
+
+ assert ext == "txt", "Bad `ext` on `line`: " + line
+
+ split = testOrManifest.rsplit("/", 1)
+ nextListFile = split[-1]
+ nextPathStr = ""
+ if len(split) != 1:
+ nextPathStr = split[0]
+
+ nextPathStr = pathStr + "/" + nextPathStr
+ AccumTests(nextPathStr, nextListFile, webgl1, webgl2, out_testList)
+ continue
+
+ return
+
+
+########################################################################
+# Templates
+
+
+def FillTemplate(inFilePath, templateDict, outFilePath):
+ templateShell = ImportTemplate(inFilePath)
+ OutputFilledTemplate(templateShell, templateDict, outFilePath)
+ return
+
+
+def ImportTemplate(inFilePath):
+ with open(inFilePath, "r") as f:
+ return TemplateShell(f)
+
+
+def OutputFilledTemplate(templateShell, templateDict, outFilePath):
+ spanStrList = templateShell.Fill(templateDict)
+
+ with open(outFilePath, "w", newline="\n") as f:
+ f.writelines(spanStrList)
+ return
+
+
+##############################
+# Internals
+
+
+def WrapWithIndent(lines, indentLen):
+ split = lines.split("\n")
+ if len(split) == 1:
+ return lines
+
+ ret = [split[0]]
+ indentSpaces = " " * indentLen
+ for line in split[1:]:
+ ret.append(indentSpaces + line)
+
+ return "\n".join(ret)
+
+
+templateRE = re.compile("(%%.*?%%)")
+assert templateRE.split(" foo = %%BAR%%;") == [" foo = ", "%%BAR%%", ";"]
+
+
+class TemplateShellSpan:
+ def __init__(self, span):
+ self.span = span
+
+ self.isLiteralSpan = True
+ if self.span.startswith("%%") and self.span.endswith("%%"):
+ self.isLiteralSpan = False
+ self.span = self.span[2:-2]
+
+ return
+
+ def Fill(self, templateDict, indentLen):
+ if self.isLiteralSpan:
+ return self.span
+
+ assert self.span in templateDict, "'" + self.span + "' not in dict!"
+
+ filling = templateDict[self.span]
+
+ return WrapWithIndent(filling, indentLen)
+
+
+class TemplateShell:
+ def __init__(self, iterableLines):
+ spanList = []
+ curLiteralSpan = []
+ for line in iterableLines:
+ split = templateRE.split(line)
+
+ for cur in split:
+ isTemplateSpan = cur.startswith("%%") and cur.endswith("%%")
+ if not isTemplateSpan:
+ curLiteralSpan.append(cur)
+ continue
+
+ if curLiteralSpan:
+ span = "".join(curLiteralSpan)
+ span = TemplateShellSpan(span)
+ spanList.append(span)
+ curLiteralSpan = []
+
+ assert len(cur) >= 4
+
+ span = TemplateShellSpan(cur)
+ spanList.append(span)
+ continue
+ continue
+
+ if curLiteralSpan:
+ span = "".join(curLiteralSpan)
+ span = TemplateShellSpan(span)
+ spanList.append(span)
+
+ self.spanList = spanList
+ return
+
+ # Returns spanStrList.
+
+ def Fill(self, templateDict):
+ indentLen = 0
+ ret = []
+ for span in self.spanList:
+ span = span.Fill(templateDict, indentLen)
+ ret.append(span)
+
+ # Get next `indentLen`.
+ try:
+ lineStartPos = span.rindex("\n") + 1
+
+ # let span = 'foo\nbar'
+ # len(span) is 7
+ # lineStartPos is 4
+ indentLen = len(span) - lineStartPos
+ except ValueError:
+ indentLen += len(span)
+ continue
+
+ return ret
+
+
+########################################################################
+# Output
+
+
+def IsWrapperWebGL2(wrapperPath):
+ return wrapperPath.startswith(GENERATED_PATHSTR + "/test_" + WEBGL2_TEST_MANGLE)
+
+
+def WriteWrapper(entryPath, webgl2, templateShell, wrapperPathAccum):
+ mangledPath = entryPath.replace("/", PATH_SEP_MANGLING)
+ maybeWebGL2Mangle = ""
+ if webgl2:
+ maybeWebGL2Mangle = WEBGL2_TEST_MANGLE
+
+ # Mochitests must start with 'test_' or similar, or the test
+ # runner will ignore our tests.
+ # The error text is "is not a valid test".
+ wrapperFileName = "test_" + maybeWebGL2Mangle + mangledPath
+
+ wrapperPath = GENERATED_PATHSTR + "/" + wrapperFileName
+ print("Adding wrapper: " + wrapperPath)
+
+ args = ""
+ if webgl2:
+ args = "?webglVersion=2"
+
+ templateDict = {
+ "TEST_PATH": entryPath,
+ "ARGS": args,
+ }
+
+ OutputFilledTemplate(templateShell, templateDict, wrapperPath)
+
+ if webgl2:
+ assert IsWrapperWebGL2(wrapperPath)
+
+ wrapperPathAccum.append(wrapperPath)
+ return
+
+
+def WriteWrappers(testEntryList):
+ templateShell = ImportTemplate(WRAPPER_TEMPLATE_FILE)
+
+ generatedDirPath = GENERATED_PATHSTR.replace("/", os.sep)
+ if not os.path.exists(generatedDirPath):
+ os.mkdir(generatedDirPath)
+ assert os.path.isdir(generatedDirPath)
+
+ wrapperPathList = []
+ for entry in testEntryList:
+ if entry.webgl1:
+ WriteWrapper(entry.path, False, templateShell, wrapperPathList)
+ if entry.webgl2:
+ WriteWrapper(entry.path, True, templateShell, wrapperPathList)
+ continue
+
+ print("{} wrappers written.\n".format(len(wrapperPathList)))
+ return wrapperPathList
+
+
+kManifestRelPathStr = os.path.relpath(".", os.path.dirname(DEST_MANIFEST_PATHSTR))
+kManifestRelPathStr = kManifestRelPathStr.replace(os.sep, "/")
+
+
+def ManifestPathStr(pathStr):
+ pathStr = kManifestRelPathStr + "/" + pathStr
+ return os.path.normpath(pathStr).replace(os.sep, "/")
+
+
+def WriteManifest(wrapperPathStrList, supportPathStrList):
+ destPathStr = DEST_MANIFEST_PATHSTR
+ print("Generating manifest: " + destPathStr)
+
+ errataMap = LoadErrata()
+
+ # DEFAULT_ERRATA
+ defaultSectionName = "DEFAULT"
+
+ defaultSectionLines = []
+ if defaultSectionName in errataMap:
+ defaultSectionLines = errataMap[defaultSectionName]
+ del errataMap[defaultSectionName]
+
+ defaultSectionStr = "\n".join(defaultSectionLines)
+
+ # SUPPORT_FILES
+ supportPathStrList = [ManifestPathStr(x) for x in supportPathStrList]
+ supportPathStrList = sorted(supportPathStrList)
+ supportFilesStr = "\n".join(supportPathStrList)
+
+ # MANIFEST_TESTS
+ manifestTestLineList = []
+ wrapperPathStrList = sorted(wrapperPathStrList)
+ for wrapperPathStr in wrapperPathStrList:
+ wrapperManifestPathStr = ManifestPathStr(wrapperPathStr)
+ sectionName = "[" + wrapperManifestPathStr + "]"
+ manifestTestLineList.append(sectionName)
+
+ errataLines = []
+
+ subsuite = ChooseSubsuite(wrapperPathStr)
+ errataLines.append("subsuite = " + subsuite)
+
+ if wrapperPathStr in errataMap:
+ assert subsuite
+ errataLines += errataMap[wrapperPathStr]
+ del errataMap[wrapperPathStr]
+
+ manifestTestLineList += errataLines
+ continue
+
+ if errataMap:
+ print("Errata left in map:")
+ for x in errataMap.keys():
+ print(" " * 4 + x)
+ assert False
+
+ manifestTestsStr = "\n".join(manifestTestLineList)
+
+ # Fill the template.
+ templateDict = {
+ "DEFAULT_ERRATA": defaultSectionStr,
+ "SUPPORT_FILES": supportFilesStr,
+ "MANIFEST_TESTS": manifestTestsStr,
+ }
+
+ destPath = destPathStr.replace("/", os.sep)
+ FillTemplate(MANIFEST_TEMPLATE_FILE, templateDict, destPath)
+ return
+
+
+##############################
+# Internals
+
+
+kManifestHeaderRegex = re.compile(r"\[([^]]*)\]")
+
+
+def LoadINI(path):
+ curSectionName = None
+ curSectionMap = {}
+
+ lineNum = 0
+
+ ret = {}
+ ret[curSectionName] = (lineNum, curSectionMap)
+
+ with open(path, "r") as f:
+ for line in f:
+ lineNum += 1
+
+ line = line.strip()
+ if not line:
+ continue
+
+ if line[0] in [";", "#"]:
+ continue
+
+ if line[0] == "[":
+ assert line[-1] == "]", "{}:{}".format(path, lineNum)
+
+ curSectionName = line[1:-1]
+ assert (
+ curSectionName not in ret
+ ), "Line {}: Duplicate section: {}".format(lineNum, line)
+
+ curSectionMap = {}
+ ret[curSectionName] = (lineNum, curSectionMap)
+ continue
+
+ split = line.split("=", 1)
+ key = split[0].strip()
+ val = ""
+ if len(split) == 2:
+ val = split[1].strip()
+
+ curSectionMap[key] = (lineNum, val)
+ continue
+
+ return ret
+
+
+def LoadErrata():
+ iniMap = LoadINI(ERRATA_FILE)
+
+ ret = {}
+
+ for (sectionName, (sectionLineNum, sectionMap)) in iniMap.items():
+ curLines = []
+
+ if sectionName is None:
+ continue
+ elif sectionName != "DEFAULT":
+ path = sectionName.replace("/", os.sep)
+ assert os.path.exists(path), "Errata line {}: Invalid file: {}".format(
+ sectionLineNum, sectionName
+ )
+
+ for (key, (lineNum, val)) in sectionMap.items():
+ assert key in ACCEPTABLE_ERRATA_KEYS, "Line {}: {}".format(lineNum, key)
+
+ curLine = "{} = {}".format(key, val)
+ curLines.append(curLine)
+ continue
+
+ ret[sectionName] = curLines
+ continue
+
+ return ret
+
+
+########################################################################
+
+
+def GetSupportFileList():
+ ret = EXTRA_SUPPORT_FILES[:]
+
+ for pathStr in SUPPORT_DIRS:
+ ret += GetFilePathListForDir(pathStr)
+ continue
+
+ for pathStr in ret:
+ path = pathStr.replace("/", os.sep)
+ assert os.path.exists(path), path + "\n\n\n" + "pathStr: " + str(pathStr)
+ continue
+
+ return ret
+
+
+def GetFilePathListForDir(baseDir):
+ ret = []
+ for root, folders, files in os.walk(baseDir):
+ for f in files:
+ filePath = os.path.join(root, f)
+ filePath = filePath.replace(os.sep, "/")
+ ret.append(filePath)
+
+ return ret
+
+
+if __name__ == "__main__":
+ file_dir = Path(__file__).parent
+ os.chdir(str(file_dir))
+ shutil.rmtree(file_dir / "generated", True)
+
+ testEntryList = GetTestList()
+ wrapperPathStrList = WriteWrappers(testEntryList)
+
+ supportPathStrList = GetSupportFileList()
+ WriteManifest(wrapperPathStrList, supportPathStrList)
+
+ print("Done!")