Index: conformance/ogles/process-ogles2-tests.py |
=================================================================== |
--- conformance/ogles/process-ogles2-tests.py (revision 0) |
+++ conformance/ogles/process-ogles2-tests.py (working copy) |
@@ -0,0 +1,587 @@ |
+#!/usr/bin/python |
+ |
+"""generates tests from OpenGL ES 2.0 .run/.test files.""" |
+ |
+import os |
+import os.path |
+import sys |
+import re |
+import json |
+import shutil |
+from optparse import OptionParser |
+from xml.dom.minidom import parse |
+ |
+if sys.version < '2.6': |
+ print 'Wrong Python Version !!!: Need >= 2.6' |
+ sys.exit(1) |
+ |
+# each shader test generates up to 3 512x512 images. |
+# a 512x512 image takes 1meg of memory so set this |
+# number apporpriate for the platform with |
+# the smallest memory issue. At 8 that means |
+# at least 24 meg is needed to run the test. |
+MAX_TESTS_PER_SET = 8 |
+ |
+VERBOSE = False |
+ |
+FILTERS = [ |
+ re.compile("GL/"), |
+] |
+ |
+LICENSE = """ |
+/* |
+** Copyright (c) 2012 The Khronos Group Inc. |
+** |
+** Permission is hereby granted, free of charge, to any person obtaining a |
+** copy of this software and/or associated documentation files (the |
+** "Materials"), to deal in the Materials without restriction, including |
+** without limitation the rights to use, copy, modify, merge, publish, |
+** distribute, sublicense, and/or sell copies of the Materials, and to |
+** permit persons to whom the Materials are furnished to do so, subject to |
+** the following conditions: |
+** |
+** The above copyright notice and this permission notice shall be included |
+** in all copies or substantial portions of the Materials. |
+** |
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
+** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. |
+*/ |
+""" |
+ |
+COMMENT_RE = re.compile("/\*\n\*\*\s+Copyright.*?\*/", |
+ re.IGNORECASE | re.DOTALL) |
+REMOVE_COPYRIGHT_RE = re.compile("\/\/\s+Copyright.*?\n", |
+ re.IGNORECASE | re.DOTALL) |
+MATRIX_RE = re.compile("Matrix(\\d)") |
+ |
+VALID_UNIFORM_TYPES = [ |
+ "uniform1f", |
+ "uniform1fv", |
+ "uniform1fv", |
+ "uniform1i", |
+ "uniform1iv", |
+ "uniform1iv", |
+ "uniform2f", |
+ "uniform2fv", |
+ "uniform2fv", |
+ "uniform2i", |
+ "uniform2iv", |
+ "uniform2iv", |
+ "uniform3f", |
+ "uniform3fv", |
+ "uniform3fv", |
+ "uniform3i", |
+ "uniform3iv", |
+ "uniform3iv", |
+ "uniform4f", |
+ "uniform4fv", |
+ "uniform4fv", |
+ "uniform4i", |
+ "uniform4iv", |
+ "uniform4ivy", |
+ "uniformMatrix2fv", |
+ "uniformMatrix2fv", |
+ "uniformMatrix3fv", |
+ "uniformMatrix3fv", |
+ "uniformMatrix4fv", |
+ "uniformMatrix4fv", |
+] |
+ |
+SUBSTITUTIONS = [ |
+ ("uniformmat3fv", "uniformMatrix3fv"), |
+ ("uniformmat4fv", "uniformMatrix4fv"), |
+] |
+ |
+ |
+def Log(msg): |
+ global VERBOSE |
+ if VERBOSE: |
+ print msg |
+ |
+ |
+def TransposeMatrix(values, dim): |
+ size = dim * dim |
+ count = len(values) / size |
+ for m in range(0, count): |
+ offset = m * size |
+ for i in range(0, dim): |
+ for j in range(i + 1, dim): |
+ t = values[offset + i * dim + j] |
+ values[offset + i * dim + j] = values[offset + j * dim + i] |
+ values[offset + j * dim + i] = t |
+ |
+ |
+def GetValidTypeName(type_name): |
+ global VALID_UNIFORM_TYPES |
+ global SUBSTITUTIONS |
+ for subst in SUBSTITUTIONS: |
+ type_name = type_name.replace(subst[0], subst[1]) |
+ if not type_name in VALID_UNIFORM_TYPES: |
+ print "unknown type name: ", type_name |
+ raise SyntaxError |
+ return type_name |
+ |
+ |
+def WriteOpen(filename): |
+ dirname = os.path.dirname(filename) |
+ if len(dirname) > 0 and not os.path.exists(dirname): |
+ os.makedirs(dirname) |
+ return open(filename, "wb") |
+ |
+ |
+class TxtWriter(): |
+ def __init__(self, filename): |
+ self.filename = filename |
+ self.lines = [] |
+ |
+ def Write(self, line): |
+ self.lines.append(line) |
+ |
+ def Close(self): |
+ if len(self.lines) > 0: |
+ Log("Writing: %s" % self.filename) |
+ f = WriteOpen(self.filename) |
+ f.write("# this file is auto-generated. DO NOT EDIT.\n") |
+ f.write("".join(self.lines)) |
+ f.close() |
+ |
+ |
+def ReadFileAsLines(filename): |
+ f = open(filename, "r") |
+ lines = f.readlines() |
+ f.close() |
+ return [line.strip() for line in lines] |
+ |
+ |
+def ReadFile(filename): |
+ f = open(filename, "r") |
+ content = f.read() |
+ f.close() |
+ return content.replace("\r\n", "\n") |
+ |
+ |
+def Chunkify(list, chunk_size): |
+ """divides an array into chunks of chunk_size""" |
+ return [list[i:i + chunk_size] for i in range(0, len(list), chunk_size)] |
+ |
+ |
+def GetText(nodelist): |
+ """Gets the text of from a list of nodes""" |
+ rc = [] |
+ for node in nodelist: |
+ if node.nodeType == node.TEXT_NODE: |
+ rc.append(node.data) |
+ return ''.join(rc) |
+ |
+ |
+def GetElementText(node, name): |
+ """Gets the text of an element""" |
+ elements = node.getElementsByTagName(name) |
+ if len(elements) > 0: |
+ return GetText(elements[0].childNodes) |
+ else: |
+ return None |
+ |
+ |
+def GetBoolElement(node, name): |
+ text = GetElementText(node, name) |
+ return text.lower() == "true" |
+ |
+ |
+def GetModel(node): |
+ """Gets the model""" |
+ model = GetElementText(node, "model") |
+ if model and len(model.strip()) == 0: |
+ elements = node.getElementsByTagName("model") |
+ if len(elements) > 0: |
+ model = GetElementText(elements[0], "filename") |
+ return model |
+ |
+ |
+def RelativizePaths(base, paths, template): |
+ """converts paths to relative paths""" |
+ rels = [] |
+ for p in paths: |
+ #print "---" |
+ #print "base: ", os.path.abspath(base) |
+ #print "path: ", os.path.abspath(p) |
+ relpath = os.path.relpath(os.path.abspath(p), os.path.dirname(os.path.abspath(base))).replace("\\", "/") |
+ #print "rel : ", relpath |
+ rels.append(template % relpath) |
+ return "\n".join(rels) |
+ |
+ |
+def CopyFile(filename, src, dst): |
+ s = os.path.abspath(os.path.join(os.path.dirname(src), filename)) |
+ d = os.path.abspath(os.path.join(os.path.dirname(dst), filename)) |
+ dst_dir = os.path.dirname(d) |
+ if not os.path.exists(dst_dir): |
+ os.makedirs(dst_dir) |
+ shutil.copyfile(s, d) |
+ |
+ |
+def CopyShader(filename, src, dst): |
+ s = os.path.abspath(os.path.join(os.path.dirname(src), filename)) |
+ d = os.path.abspath(os.path.join(os.path.dirname(dst), filename)) |
+ text = ReadFile(s) |
+ # By agreement with the Khronos OpenGL working group we are allowed |
+ # to open source only the .vert and .frag files from the OpenGL ES 2.0 |
+ # conformance tests. All other files from the OpenGL ES 2.0 conformance |
+ # tests are not included. |
+ marker = "insert-copyright-here" |
+ new_text = COMMENT_RE.sub(marker, text) |
+ if new_text == text: |
+ print "no matching license found:", s |
+ raise RuntimeError |
+ new_text = REMOVE_COPYRIGHT_RE.sub("", new_text) |
+ new_text = new_text.replace(marker, LICENSE) |
+ f = WriteOpen(d) |
+ f.write(new_text) |
+ f.close() |
+ |
+ |
+def IsOneOf(string, regexs): |
+ for regex in regexs: |
+ if re.match(regex, string): |
+ return True |
+ return False |
+ |
+ |
+def CheckForUnknownTags(valid_tags, node, depth=1): |
+ """do a hacky check to make sure we're not missing something.""" |
+ for child in node.childNodes: |
+ if child.localName and not IsOneOf(child.localName, valid_tags[0]): |
+ print "unsupported tag:", child.localName |
+ print "depth:", depth |
+ raise SyntaxError |
+ else: |
+ if len(valid_tags) > 1: |
+ CheckForUnknownTags(valid_tags[1:], child, depth + 1) |
+ |
+ |
+def IsFileWeWant(filename): |
+ for f in FILTERS: |
+ if f.search(filename): |
+ return True |
+ return False |
+ |
+ |
+class TestReader(): |
+ """class to read and parse tests""" |
+ |
+ def __init__(self, basepath): |
+ self.tests = [] |
+ self.modes = {} |
+ self.patterns = {} |
+ self.basepath = basepath |
+ |
+ def Print(self, msg): |
+ if self.verbose: |
+ print msg |
+ |
+ def MakeOutPath(self, filename): |
+ relpath = os.path.relpath(os.path.abspath(filename), os.path.dirname(os.path.abspath(self.basepath))) |
+ return relpath |
+ |
+ def ReadTests(self, filename): |
+ """reads a .run file and parses.""" |
+ Log("reading %s" % filename) |
+ outname = self.MakeOutPath(filename + ".txt") |
+ f = TxtWriter(outname) |
+ dirname = os.path.dirname(filename) |
+ lines = ReadFileAsLines(filename) |
+ count = 0 |
+ tests_data = [] |
+ for line in lines: |
+ if len(line) > 0 and not line.startswith("#"): |
+ fname = os.path.join(dirname, line) |
+ if line.endswith(".run"): |
+ if self.ReadTests(fname): |
+ f.Write(line + ".txt\n") |
+ count += 1 |
+ elif line.endswith(".test"): |
+ tests_data.extend(self.ReadTest(fname)) |
+ else: |
+ print "Error in %s:%d:%s" % (filename, count, line) |
+ raise SyntaxError() |
+ if len(tests_data): |
+ global MAX_TESTS_PER_SET |
+ sets = Chunkify(tests_data, MAX_TESTS_PER_SET) |
+ id = 1 |
+ for set in sets: |
+ suffix = "_%03d_to_%03d" % (id, id + len(set) - 1) |
+ test_outname = self.MakeOutPath(filename + suffix + ".html") |
+ if os.path.basename(test_outname).startswith("input.run"): |
+ dname = os.path.dirname(test_outname) |
+ folder_name = os.path.basename(dname) |
+ test_outname = os.path.join(dname, folder_name + suffix + ".html") |
+ self.WriteTests(filename, test_outname, {"tests":set}) |
+ f.Write(os.path.basename(test_outname) + "\n") |
+ id += len(set) |
+ count += 1 |
+ f.Close() |
+ return count |
+ |
+ def ReadTest(self, filename): |
+ """reads a .test file and parses.""" |
+ Log("reading %s" % filename) |
+ dom = parse(filename) |
+ tests = dom.getElementsByTagName("test") |
+ tests_data = [] |
+ outname = self.MakeOutPath(filename + ".html") |
+ for test in tests: |
+ if not IsFileWeWant(filename): |
+ self.CopyShaders(test, filename, outname) |
+ else: |
+ test_data = self.ProcessTest(test, filename, outname, len(tests_data)) |
+ if test_data: |
+ tests_data.append(test_data) |
+ return tests_data |
+ |
+ def ProcessTest(self, test, filename, outname, id): |
+ """Process a test""" |
+ mode = test.getAttribute("mode") |
+ pattern = test.getAttribute("pattern") |
+ self.modes[mode] = 1 |
+ self.patterns[pattern] = 1 |
+ Log ("%d: mode: %s pattern: %s" % (id, mode, pattern)) |
+ method = getattr(self, 'Process_' + pattern) |
+ test_data = method(test, filename, outname) |
+ if test_data: |
+ test_data["pattern"] = pattern |
+ return test_data |
+ |
+ def WriteTests(self, filename, outname, tests_data): |
+ Log("Writing %s" % outname) |
+ template = """<!DOCTYPE html> |
+<!-- this file is auto-generated. DO NOT EDIT. |
+%(license)s |
+--> |
+<html> |
+<head> |
+<meta charset="utf-8"> |
+<title>WebGL GLSL conformance test: %(title)s</title> |
+%(css)s |
+%(scripts)s |
+</head> |
+<body> |
+<canvas id="example" width="500" height="500" style="width: 16px; height: 16px;"></canvas> |
+<div id="description"></div> |
+<div id="console"></div> |
+</body> |
+<script> |
+"use strict"; |
+OpenGLESTestRunner.run(%(tests_data)s); |
+var successfullyParsed = true; |
+</script> |
+</html> |
+""" |
+ css = [ |
+ "../../resources/js-test-style.css", |
+ "../resources/ogles-tests.css", |
+ ] |
+ scripts = [ |
+ "../../resources/js-test-pre.js", |
+ "../resources/webgl-test.js", |
+ "../resources/webgl-test-utils.js", |
+ "ogles-utils.js", |
+ ] |
+ css_html = RelativizePaths(outname, css, '<link rel="stylesheet" href="%s" />') |
+ scripts_html = RelativizePaths(outname, scripts, '<script src="%s"></script>') |
+ |
+ f = WriteOpen(outname) |
+ f.write(template % { |
+ "license": LICENSE, |
+ "css": css_html, |
+ "scripts": scripts_html, |
+ "title": os.path.basename(outname), |
+ "tests_data": json.dumps(tests_data, indent=2) |
+ }) |
+ f.close() |
+ |
+ |
+ def CopyShaders(self, test, filename, outname): |
+ """For tests we don't actually support yet, at least copy the shaders""" |
+ shaders = test.getElementsByTagName("shader") |
+ for shader in shaders: |
+ for name in ["vertshader", "fragshader"]: |
+ s = GetElementText(shader, name) |
+ if s and s != "empty": |
+ CopyShader(s, filename, outname) |
+ |
+ # |
+ # pattern handlers. |
+ # |
+ |
+ def Process_compare(self, test, filename, outname): |
+ global MATRIX_RE |
+ |
+ valid_tags = [ |
+ ["shader", "model", "glstate"], |
+ ["uniform", "vertshader", "fragshader", "filename", "depthrange"], |
+ ["name", "count", "transpose", "uniform*", "near", "far"], |
+ ] |
+ CheckForUnknownTags(valid_tags, test) |
+ |
+ # parse the test |
+ shaders = test.getElementsByTagName("shader") |
+ shaderInfos = [] |
+ for shader in shaders: |
+ v = GetElementText(shader, "vertshader") |
+ f = GetElementText(shader, "fragshader") |
+ CopyShader(v, filename, outname) |
+ CopyShader(f, filename, outname) |
+ info = { |
+ "vertexShader": v, |
+ "fragmentShader": f, |
+ } |
+ shaderInfos.append(info) |
+ uniformElems = shader.getElementsByTagName("uniform") |
+ if len(uniformElems) > 0: |
+ uniforms = {} |
+ info["uniforms"] = uniforms |
+ for uniformElem in uniformElems: |
+ uniform = {"count": 1} |
+ for child in uniformElem.childNodes: |
+ if child.localName == None: |
+ pass |
+ elif child.localName == "name": |
+ uniforms[GetText(child.childNodes)] = uniform |
+ elif child.localName == "count": |
+ uniform["count"] = int(GetText(child.childNodes)) |
+ elif child.localName == "transpose": |
+ uniform["transpose"] = (GetText(child.childNodes) == "true") |
+ else: |
+ if "type" in uniform: |
+ print "utype was:", uniform["type"], " found ", child.localName |
+ raise SyntaxError |
+ type_name = GetValidTypeName(child.localName) |
+ uniform["type"] = type_name |
+ valueText = GetText(child.childNodes).replace(",", " ") |
+ uniform["value"] = [float(t) for t in valueText.split()] |
+ m = MATRIX_RE.search(type_name) |
+ if m: |
+ # Why are these backward from the API?!?!? |
+ TransposeMatrix(uniform["value"], int(m.group(1))) |
+ data = { |
+ "name": os.path.basename(outname), |
+ "model": GetModel(test), |
+ "referenceProgram": shaderInfos[1], |
+ "testProgram": shaderInfos[0], |
+ } |
+ gl_states = test.getElementsByTagName("glstate") |
+ if len(gl_states) > 0: |
+ state = {} |
+ data["state"] = state |
+ for gl_state in gl_states: |
+ for state_name in gl_state.childNodes: |
+ if state_name.localName: |
+ values = {} |
+ for field in state_name.childNodes: |
+ if field.localName: |
+ values[field.localName] = GetText(field.childNodes) |
+ state[state_name.localName] = values |
+ return data |
+ |
+ def Process_shaderload(self, test, filename, outname): |
+ """no need for shaderload tests""" |
+ self.CopyShaders(test, filename, outname) |
+ |
+ def Process_extension(self, test, filename, outname): |
+ """no need for extension tests""" |
+ self.CopyShaders(test, filename, outname) |
+ |
+ def Process_createtests(self, test, filename, outname): |
+ Log("createtests Not implemented: %s" % filename) |
+ self.CopyShaders(test, filename, outname) |
+ |
+ def Process_GL2Test(self, test, filename, outname): |
+ Log("GL2Test Not implemented: %s" % filename) |
+ self.CopyShaders(test, filename, outname) |
+ |
+ def Process_uniformquery(self, test, filename, outname): |
+ Log("uniformquery Not implemented: %s" % filename) |
+ self.CopyShaders(test, filename, outname) |
+ |
+ def Process_egl_image_external(self, test, filename, outname): |
+ """no need for egl_image_external tests""" |
+ self.CopyShaders(test, filename, outname) |
+ |
+ def Process_dismount(self, test, filename, outname): |
+ Log("dismount Not implemented: %s" % filename) |
+ self.CopyShaders(test, filename, outname) |
+ |
+ def Process_build(self, test, filename, outname): |
+ """don't need build tests""" |
+ valid_tags = [ |
+ ["shader", "compstat", "linkstat"], |
+ ["vertshader", "fragshader"], |
+ ] |
+ CheckForUnknownTags(valid_tags, test) |
+ |
+ shader = test.getElementsByTagName("shader") |
+ if not shader: |
+ return None |
+ vs = GetElementText(shader[0], "vertshader") |
+ fs = GetElementText(shader[0], "fragshader") |
+ if vs and vs != "empty": |
+ CopyShader(vs, filename, outname) |
+ if fs and fs != "empty": |
+ CopyShader(fs, filename, outname) |
+ data = { |
+ "name": os.path.basename(outname), |
+ "compstat": bool(GetBoolElement(test, "compstat")), |
+ "linkstat": bool(GetBoolElement(test, "linkstat")), |
+ "testProgram": { |
+ "vertexShader": vs, |
+ "fragmentShader": fs, |
+ }, |
+ } |
+ attach = test.getElementsByTagName("attach") |
+ if len(attach) > 0: |
+ data["attachError"] = GetElementText(attach[0], "attacherror") |
+ return data |
+ |
+ def Process_coverage(self, test, filename, outname): |
+ Log("coverage Not implemented: %s" % filename) |
+ self.CopyShaders(test, filename, outname) |
+ |
+ def Process_attributes(self, test, filename, outname): |
+ Log("attributes Not implemented: %s" % filename) |
+ self.CopyShaders(test, filename, outname) |
+ |
+ def Process_fixed(self, test, filename, outname): |
+ """no need for fixed function tests""" |
+ self.CopyShaders(test, filename, outname) |
+ |
+ |
+def main(argv): |
+ """This is the main function.""" |
+ global VERBOSE |
+ |
+ parser = OptionParser() |
+ parser.add_option( |
+ "-v", "--verbose", action="store_true", |
+ help="prints more output.") |
+ |
+ (options, args) = parser.parse_args(args=argv) |
+ |
+ if len(args) < 1: |
+ pass # fix me |
+ |
+ os.chdir(os.path.dirname(__file__) or '.') |
+ |
+ VERBOSE = options.verbose |
+ |
+ filename = args[0] |
+ test_reader = TestReader(filename) |
+ test_reader.ReadTests(filename) |
+ |
+ |
+if __name__ == '__main__': |
+ sys.exit(main(sys.argv[1:])) |
Property changes on: conformance/ogles/process-ogles2-tests.py |
___________________________________________________________________ |
Added: svn:eol-style |
## -0,0 +1 ## |
+LF |
\ No newline at end of property |