| # 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)) |