/sdk/tests/conformance/ogles/process-ogles2-tests.py
Python | 587 lines | 559 code | 14 blank | 14 comment | 15 complexity | 8c7b6f94c1399c7d7cf88703e62608ab MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1
- #!/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:]))