[Piglit] [RFC 06/12] gen_constant_array_size_tests.py: Convert to mako.
Dylan Baker
baker.dylan.c at gmail.com
Mon Dec 8 17:11:27 PST 2014
This patch converts from string concatenation to using a mako template
to render the tests.
This allows us to throw 100 lines of code away, and makes the template
much easier to understand.
Signed-off-by: Dylan Baker <dylanx.c.baker at intel.com>
---
generated_tests/gen_constant_array_size_tests.py | 378 +++++++++--------------
1 file changed, 150 insertions(+), 228 deletions(-)
diff --git a/generated_tests/gen_constant_array_size_tests.py b/generated_tests/gen_constant_array_size_tests.py
index d165fee..0195ebf 100644
--- a/generated_tests/gen_constant_array_size_tests.py
+++ b/generated_tests/gen_constant_array_size_tests.py
@@ -21,262 +21,184 @@
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
-# Generate a pair of glsl parser tests for every overloaded version of
-# every built-in function, which test that the built-in functions are
-# handled properly when applied to constant arguments inside an array
-# size declaration.
-#
-# In each pair of generated tests, one test exercises the built-in
-# function in vertex shaders, and the other exercises it in fragment
-# shaders.
-#
-# This program outputs, to stdout, the name of each file it generates.
-# With the optional argument --names-only, it only outputs the names
-# of the files; it doesn't generate them.
-
-import abc
-import optparse
-import os
-import os.path
-
-import builtin_function
-from builtins import glsl_types, generators
+"""Generate tests for overloaded built-in functions.
+Generate a pair of glsl parser tests for every overloaded version of
+every built-in function, which test that the built-in functions are
+handled properly when applied to constant arguments inside an array
+size declaration.
-class ParserTest(object):
- """Class used to build a test of a single built-in. This is an
- abstract base class--derived types should override test_suffix(),
- output_var(), and other functions if necessary.
- """
- __metaclass__ = abc.ABCMeta
-
- def __init__(self, signature, test_vectors):
- """Prepare to build a test for a single built-in. signature
- is the signature of the built-in (a key from the
- builtin_function.test_suite dict), and test_vectors is the
- list of test vectors for testing the given builtin (the
- corresponding value from the builtin_function.test_suite
- dict).
- """
- self.__signature = signature
- self.__test_vectors = test_vectors
+In each pair of generated tests, one test exercises the built-in
+function in vertex shaders, and the other exercises it in fragment
+shaders.
- def glsl_version(self):
- if self.__signature.version_introduced < 120:
- # Before version 1.20, built-in function invocations
- # weren't allowed in constant expressions. So even if
- # this built-in was introduced prior to 1.20, test it in
- # version 1.20.
- return 120
- else:
- return self.__signature.version_introduced
+This program outputs, to stdout, the name of each file it generates.
+With the optional argument --names-only, it only outputs the names
+of the files; it doesn't generate them.
- def version_directive(self):
- return '#version {0}\n'.format(self.glsl_version())
+"""
- def additional_declarations(self):
- """Return a string containing any additional declarations that
- should be placed after the version directive. Returns the
- empty string by default.
- """
- return ''
+from __future__ import print_function, division
+import os
- def additional_extensions(self):
- """Return a list (or other iterable) containing any additional
- extension requirements that the test has. Returns the empty
- list by default.
- """
- return []
+from mako.template import Template
- @abc.abstractmethod
- def test_suffix(self):
- """Return the suffix that should be used in the test file name
- to identify the type of shader, e.g. "vert" for a vertex
- shader test.
- """
+import builtin_function
+from builtins import glsl_types, generators
- @abc.abstractmethod
- def output_var(self):
- """Return the output variable for the test."""
- def make_condition(self, test_vector):
- """Generate a GLSL constant expression that should evaluate to
- true if the GLSL compiler's constant evaluation produces the
- correct result for the given test vector, and false if not.
- """
- invocation = self.__signature.template.format(
- *[generators.glsl_constant(x) for x in test_vector.arguments])
- if self.__signature.rettype.base_type == glsl_types.GLSL_FLOAT:
- # Test floating-point values within tolerance
- if self.__signature.name == 'distance':
- # Don't use the distance() function to test itself.
- return '{0} <= {1} && {1} <= {2}'.format(
- test_vector.result - test_vector.tolerance,
- invocation,
- test_vector.result + test_vector.tolerance)
- elif self.__signature.rettype.is_matrix:
- # We can't apply distance() to matrices. So apply it
- # to each column and root-sum-square the results. It
- # is safe to use pow() here because its behavior is
- # verified in the pow() tests.
- terms = []
- for col in xrange(self.__signature.rettype.num_cols):
- terms.append('pow(distance({0}[{1}], {2}), 2)'.format(
- invocation, col,
- generators.glsl_constant(test_vector.result[:, col])))
- rss_distance = ' + '.join(terms)
- sq_tolerance = test_vector.tolerance * test_vector.tolerance
- return '{0} <= {1}'.format(
- rss_distance, generators.glsl_constant(sq_tolerance))
- else:
- return 'distance({0}, {1}) <= {2}'.format(
- invocation,
- generators.glsl_constant(test_vector.result),
- generators.glsl_constant(test_vector.tolerance))
+TEMPLATE = Template("""\
+/* [config]
+ * expect_result: pass
+ * glsl_version: ${glsl_version}
+ * [end config]
+ *
+ * Check that the following test vectors are constantfolded correctly:
+% for template, result in comments:
+ * ${template} => ${result}
+% endfor
+ */
+#version ${int(float(glsl_version) * 100)}
+% if extension:
+#extension GL_${extension} : require
+% endif
+
+void main()
+{
+% for i, vector in enumerate(vectors):
+ float[${vector} ? 1 : -1] array${i};
+% else:
+ ## This is a clever (but maybe not good) use of python's for/else syntax.
+ ## vectors is a generator, which means that we cannot get a value again
+ ## after we've iterated passed it. This trick allows us to bypass that
+ ## limitation and output an additional value on the last iteration without
+ ## storing the value of i because the else is still part of the for loop,
+ ## and all of the variables from the last iteration of the for loop are
+ ## still in scope.
+ ${output_var} = vec4(${' + '.join('array{}.length()'.format(x) for x in xrange(i + 1))});
+% endfor
+}
+""")
+
+
+def make_glsl_version(signature, stage):
+ """Get the minimum glsl_version from the signature."""
+ if stage == 'geom':
+ version = max(150, signature.version_introduced)
+ else:
+ version = max(120, signature.version_introduced)
+ return '{0:1.2f}'.format(float(version) / 100)
+
+
+def make_condition(signature, test_vector):
+ """Generate a GLSL constant expression that should evaluate to true if the
+ GLSL compiler's constant evaluation produces the correct result for the
+ given test vector, and false if not.
+ """
+ invocation = signature.template.format(
+ *[generators.glsl_constant(x) for x in test_vector.arguments])
+ if signature.rettype.base_type == glsl_types.GLSL_FLOAT:
+ # Test floating-point values within tolerance
+ if signature.name == 'distance':
+ # Don't use the distance() function to test itself.
+ return '{0} <= {1} && {1} <= {2}'.format(
+ test_vector.result - test_vector.tolerance,
+ invocation,
+ test_vector.result + test_vector.tolerance)
+ elif signature.rettype.is_matrix:
+ # We can't apply distance() to matrices. So apply it
+ # to each column and root-sum-square the results. It
+ # is safe to use pow() here because its behavior is
+ # verified in the pow() tests.
+ terms = []
+ for col in xrange(signature.rettype.num_cols):
+ terms.append('pow(distance({0}[{1}], {2}), 2)'.format(
+ invocation, col,
+ generators.glsl_constant(test_vector.result[:, col])))
+ rss_distance = ' + '.join(terms)
+ sq_tolerance = test_vector.tolerance * test_vector.tolerance
+ return '{0} <= {1}'.format(
+ rss_distance, generators.glsl_constant(sq_tolerance))
else:
- # Test non-floating point values exactly
- assert not self.__signature.rettype.is_matrix
- if self.__signature.name == 'equal':
- # Don't use the equal() function to test itself.
- assert self.__signature.rettype.is_vector
- terms = []
- for row in xrange(self.__signature.rettype.num_rows):
- terms.append('{0}[{1}] == {2}'.format(
- invocation, row,
- generators.glsl_constant(test_vector.result[row])))
- return ' && '.join(terms)
- elif self.__signature.rettype.is_vector:
- return 'all(equal({0}, {1}))'.format(
- invocation,
- generators.glsl_constant(test_vector.result))
- else:
- return '{0} == {1}'.format(
- invocation,
- generators.glsl_constant(test_vector.result))
-
- def make_shader(self):
- """Generate the shader code necessary to test the built-in."""
- shader = self.version_directive()
- if self.__signature.extension:
- shader += '#extension GL_{0} : require\n'.format(self.__signature.extension)
- shader += self.additional_declarations()
- shader += '\n'
- shader += 'void main()\n'
- shader += '{\n'
- lengths = []
- for i, test_vector in enumerate(self.__test_vectors):
- shader += ' float[{0} ? 1 : -1] array{1};\n'.format(
- self.make_condition(test_vector), i)
- lengths.append('array{0}.length()'.format(i))
- shader += ' {0} = vec4({1});\n'.format(
- self.output_var(), ' + '.join(lengths))
- shader += '}\n'
- return shader
-
- def filename(self):
- argtype_names = '-'.join(
- str(argtype) for argtype in self.__signature.argtypes)
- if self.__signature.extension:
- subdir = self.__signature.extension.lower()
+ return 'distance({0}, {1}) <= {2}'.format(
+ invocation,
+ generators.glsl_constant(test_vector.result),
+ generators.glsl_constant(test_vector.tolerance))
+ else:
+ # Test non-floating point values exactly
+ assert not signature.rettype.is_matrix
+ if signature.name == 'equal':
+ # Don't use the equal() function to test itself.
+ assert signature.rettype.is_vector
+ terms = []
+ for row in xrange(signature.rettype.num_rows):
+ terms.append('{0}[{1}] == {2}'.format(
+ invocation, row,
+ generators.glsl_constant(test_vector.result[row])))
+ return ' && '.join(terms)
+ elif signature.rettype.is_vector:
+ return 'all(equal({0}, {1}))'.format(
+ invocation,
+ generators.glsl_constant(test_vector.result))
else:
- subdir = 'glsl-{0:1.2f}'.format(float(self.glsl_version()) / 100)
- return os.path.join(
- 'spec', subdir, 'compiler', 'built-in-functions',
- '{0}-{1}.{2}'.format(
- self.__signature.name, argtype_names, self.test_suffix()))
-
- def generate_parser_test(self):
- """Generate the test and write it to the output file."""
- parser_test = '/* [config]\n'
- parser_test += ' * expect_result: pass\n'
- parser_test += ' * glsl_version: {0:1.2f}\n'.format(
- float(self.glsl_version()) / 100)
- req_extensions = list(self.additional_extensions())
- if req_extensions:
- parser_test += ' * require_extensions: {0}\n'.format(
- ' '.join(req_extensions))
- parser_test += ' * [end config]\n'
- parser_test += ' *\n'
- parser_test += ' * Check that the following test vectors are constant'\
- 'folded correctly:\n'
- for test_vector in self.__test_vectors:
- parser_test += ' * {0} => {1}\n'.format(
- self.__signature.template.format(
- *[generators.glsl_constant(arg) for
- arg in test_vector.arguments]),
+ return '{0} == {1}'.format(
+ invocation,
generators.glsl_constant(test_vector.result))
- parser_test += ' */\n'
- parser_test += self.make_shader()
- filename = self.filename()
- dirname = os.path.dirname(filename)
- if not os.path.exists(dirname):
- os.makedirs(dirname)
- with open(filename, 'w') as f:
- f.write(parser_test)
-class VertexParserTest(ParserTest):
- """Derived class for tests that exercise the built-in in a vertex
- shader.
- """
- def test_suffix(self):
- return 'vert'
-
- def output_var(self):
- return 'gl_Position'
+def get_filename(signature, stage):
+ """Construct a filename from the given args."""
+ argtype_names = '-'.join(str(argtype) for argtype in signature.argtypes)
+ if signature.extension:
+ subdir = signature.extension.lower()
+ else:
+ subdir = 'glsl-{0}'.format(make_glsl_version(signature, stage))
+ return os.path.join('spec', subdir, 'compiler', 'built-in-functions',
+ '{0}-{1}.{2}'.format(signature.name, argtype_names,
+ stage))
-class GeometryParserTest(ParserTest):
- """Derived class for tests that exercise the built-in in a geometry
- shader.
- """
- def glsl_version(self):
- return max(150, ParserTest.glsl_version(self))
-
- def test_suffix(self):
- return 'geom'
-
- def output_var(self):
- return 'gl_Position'
+def make_output_var(stage):
+ """Set the output variable by stage."""
+ if stage == "frag":
+ return 'gl_FragColor'
+ return 'gl_Position'
-class FragmentParserTest(ParserTest):
- """Derived class for tests that exercise the built-in in a fagment
- shader.
- """
- def test_suffix(self):
- return 'frag'
- def output_var(self):
- return 'gl_FragColor'
+def comment_generator(signature, vectors):
+ """Generate comment arguments."""
+ for vector in vectors:
+ template = signature.template.format(
+ *[generators.glsl_constant(a) for a in vector.arguments])
+ result = generators.glsl_constant(vector.result)
+ yield template, result
def all_tests():
- for signature, test_vectors in sorted(builtin_function.test_suite.items()):
+ for signature, vectors in sorted(builtin_function.test_suite.iteritems()):
# Assignment operators other than = cannot be used in the constant
# array size tests
if not signature.name.startswith('op-assign'):
- yield VertexParserTest(signature, test_vectors)
- yield GeometryParserTest(signature, test_vectors)
- yield FragmentParserTest(signature, test_vectors)
+ yield signature, vectors
def main():
- desc = 'Generate shader tests that test built-in functions using constant'\
- 'array sizes'
- usage = 'usage: %prog [-h] [--names-only]'
- parser = optparse.OptionParser(description=desc, usage=usage)
- parser.add_option('--names-only',
- dest='names_only',
- action='store_true',
- help="Don't output files, just generate a list of"
- "filenames to stdout")
- options, args = parser.parse_args()
-
- for test in all_tests():
- if not options.names_only:
- test.generate_parser_test()
- print test.filename()
+ for signature, vectors in all_tests():
+ for stage in ['frag', 'vert', 'geom']:
+ filename = get_filename(signature, stage)
+
+ if not os.path.exists(os.path.dirname(filename)):
+ os.makedirs(os.path.dirname(filename))
+
+ with open(filename, 'w') as f:
+ f.write(TEMPLATE.render(
+ glsl_version=make_glsl_version(signature, stage),
+ vectors=(make_condition(signature, v) for v in vectors),
+ comments=comment_generator(signature, vectors),
+ output_var=make_output_var(stage),
+ extension=signature.extension))
+ print(filename)
if __name__ == '__main__':
--
2.2.0
More information about the Piglit
mailing list