| # Copyright (c) 2018 Google LLC |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| """A number of placeholders and their rules for expansion when used in tests. |
| |
| These placeholders, when used in spirv_args or expected_* variables of |
| SpirvTest, have special meanings. In spirv_args, they will be substituted by |
| the result of instantiate_for_spirv_args(), while in expected_*, by |
| instantiate_for_expectation(). A TestCase instance will be passed in as |
| argument to the instantiate_*() methods. |
| """ |
| |
| import os |
| import subprocess |
| import tempfile |
| from string import Template |
| |
| |
| class PlaceHolderException(Exception): |
| """Exception class for PlaceHolder.""" |
| pass |
| |
| |
| class PlaceHolder(object): |
| """Base class for placeholders.""" |
| |
| def instantiate_for_spirv_args(self, testcase): |
| """Instantiation rules for spirv_args. |
| |
| This method will be called when the current placeholder appears in |
| spirv_args. |
| |
| Returns: |
| A string to replace the current placeholder in spirv_args. |
| """ |
| raise PlaceHolderException('Subclass should implement this function.') |
| |
| def instantiate_for_expectation(self, testcase): |
| """Instantiation rules for expected_*. |
| |
| This method will be called when the current placeholder appears in |
| expected_*. |
| |
| Returns: |
| A string to replace the current placeholder in expected_*. |
| """ |
| raise PlaceHolderException('Subclass should implement this function.') |
| |
| |
| class FileShader(PlaceHolder): |
| """Stands for a shader whose source code is in a file.""" |
| |
| def __init__(self, source, suffix, assembly_substr=None): |
| assert isinstance(source, str) |
| assert isinstance(suffix, str) |
| self.source = source |
| self.suffix = suffix |
| self.filename = None |
| # If provided, this is a substring which is expected to be in |
| # the disassembly of the module generated from this input file. |
| self.assembly_substr = assembly_substr |
| |
| def instantiate_for_spirv_args(self, testcase): |
| """Creates a temporary file and writes the source into it. |
| |
| Returns: |
| The name of the temporary file. |
| """ |
| shader, self.filename = tempfile.mkstemp( |
| dir=testcase.directory, suffix=self.suffix) |
| shader_object = os.fdopen(shader, 'w') |
| shader_object.write(self.source) |
| shader_object.close() |
| return self.filename |
| |
| def instantiate_for_expectation(self, testcase): |
| assert self.filename is not None |
| return self.filename |
| |
| |
| class ConfigFlagsFile(PlaceHolder): |
| """Stands for a configuration file for spirv-opt generated out of a string.""" |
| |
| def __init__(self, content, suffix): |
| assert isinstance(content, str) |
| assert isinstance(suffix, str) |
| self.content = content |
| self.suffix = suffix |
| self.filename = None |
| |
| def instantiate_for_spirv_args(self, testcase): |
| """Creates a temporary file and writes content into it. |
| |
| Returns: |
| The name of the temporary file. |
| """ |
| temp_fd, self.filename = tempfile.mkstemp( |
| dir=testcase.directory, suffix=self.suffix) |
| fd = os.fdopen(temp_fd, 'w') |
| fd.write(self.content) |
| fd.close() |
| return '-Oconfig=%s' % self.filename |
| |
| def instantiate_for_expectation(self, testcase): |
| assert self.filename is not None |
| return self.filename |
| |
| |
| class FileSPIRVShader(PlaceHolder): |
| """Stands for a source shader file which must be converted to SPIR-V.""" |
| |
| def __init__(self, source, suffix, assembly_substr=None): |
| assert isinstance(source, str) |
| assert isinstance(suffix, str) |
| self.source = source |
| self.suffix = suffix |
| self.filename = None |
| # If provided, this is a substring which is expected to be in |
| # the disassembly of the module generated from this input file. |
| self.assembly_substr = assembly_substr |
| |
| def instantiate_for_spirv_args(self, testcase): |
| """Creates a temporary file, writes the source into it and assembles it. |
| |
| Returns: |
| The name of the assembled temporary file. |
| """ |
| shader, asm_filename = tempfile.mkstemp( |
| dir=testcase.directory, suffix=self.suffix) |
| shader_object = os.fdopen(shader, 'w') |
| shader_object.write(self.source) |
| shader_object.close() |
| self.filename = '%s.spv' % asm_filename |
| cmd = [ |
| testcase.test_manager.assembler_path, asm_filename, '-o', self.filename |
| ] |
| process = subprocess.Popen( |
| args=cmd, |
| stdin=subprocess.PIPE, |
| stdout=subprocess.PIPE, |
| stderr=subprocess.PIPE, |
| cwd=testcase.directory) |
| output = process.communicate() |
| assert process.returncode == 0 and not output[0] and not output[1] |
| return self.filename |
| |
| def instantiate_for_expectation(self, testcase): |
| assert self.filename is not None |
| return self.filename |
| |
| |
| class StdinShader(PlaceHolder): |
| """Stands for a shader whose source code is from stdin.""" |
| |
| def __init__(self, source): |
| assert isinstance(source, str) |
| self.source = source |
| self.filename = None |
| |
| def instantiate_for_spirv_args(self, testcase): |
| """Writes the source code back to the TestCase instance.""" |
| testcase.stdin_shader = self.source |
| self.filename = '-' |
| return self.filename |
| |
| def instantiate_for_expectation(self, testcase): |
| assert self.filename is not None |
| return self.filename |
| |
| |
| class TempFileName(PlaceHolder): |
| """Stands for a temporary file's name.""" |
| |
| def __init__(self, filename): |
| assert isinstance(filename, str) |
| assert filename != '' |
| self.filename = filename |
| |
| def instantiate_for_spirv_args(self, testcase): |
| return os.path.join(testcase.directory, self.filename) |
| |
| def instantiate_for_expectation(self, testcase): |
| return os.path.join(testcase.directory, self.filename) |
| |
| |
| class SpecializedString(PlaceHolder): |
| """Returns a string that has been specialized based on TestCase. |
| |
| The string is specialized by expanding it as a string.Template |
| with all of the specialization being done with each $param replaced |
| by the associated member on TestCase. |
| """ |
| |
| def __init__(self, filename): |
| assert isinstance(filename, str) |
| assert filename != '' |
| self.filename = filename |
| |
| def instantiate_for_spirv_args(self, testcase): |
| return Template(self.filename).substitute(vars(testcase)) |
| |
| def instantiate_for_expectation(self, testcase): |
| return Template(self.filename).substitute(vars(testcase)) |