Home | History | Annotate | Download | only in tools
      1 # Copyright (c) 2018 Google LLC
      2 #
      3 # Licensed under the Apache License, Version 2.0 (the "License");
      4 # you may not use this file except in compliance with the License.
      5 # You may obtain a copy of the License at
      6 #
      7 #     http://www.apache.org/licenses/LICENSE-2.0
      8 #
      9 # Unless required by applicable law or agreed to in writing, software
     10 # distributed under the License is distributed on an "AS IS" BASIS,
     11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 # See the License for the specific language governing permissions and
     13 # limitations under the License.
     14 """A number of placeholders and their rules for expansion when used in tests.
     15 
     16 These placeholders, when used in spirv_args or expected_* variables of
     17 SpirvTest, have special meanings. In spirv_args, they will be substituted by
     18 the result of instantiate_for_spirv_args(), while in expected_*, by
     19 instantiate_for_expectation(). A TestCase instance will be passed in as
     20 argument to the instantiate_*() methods.
     21 """
     22 
     23 import os
     24 import subprocess
     25 import tempfile
     26 from string import Template
     27 
     28 
     29 class PlaceHolderException(Exception):
     30   """Exception class for PlaceHolder."""
     31   pass
     32 
     33 
     34 class PlaceHolder(object):
     35   """Base class for placeholders."""
     36 
     37   def instantiate_for_spirv_args(self, testcase):
     38     """Instantiation rules for spirv_args.
     39 
     40         This method will be called when the current placeholder appears in
     41         spirv_args.
     42 
     43         Returns:
     44             A string to replace the current placeholder in spirv_args.
     45         """
     46     raise PlaceHolderException('Subclass should implement this function.')
     47 
     48   def instantiate_for_expectation(self, testcase):
     49     """Instantiation rules for expected_*.
     50 
     51         This method will be called when the current placeholder appears in
     52         expected_*.
     53 
     54         Returns:
     55             A string to replace the current placeholder in expected_*.
     56         """
     57     raise PlaceHolderException('Subclass should implement this function.')
     58 
     59 
     60 class FileShader(PlaceHolder):
     61   """Stands for a shader whose source code is in a file."""
     62 
     63   def __init__(self, source, suffix, assembly_substr=None):
     64     assert isinstance(source, str)
     65     assert isinstance(suffix, str)
     66     self.source = source
     67     self.suffix = suffix
     68     self.filename = None
     69     # If provided, this is a substring which is expected to be in
     70     # the disassembly of the module generated from this input file.
     71     self.assembly_substr = assembly_substr
     72 
     73   def instantiate_for_spirv_args(self, testcase):
     74     """Creates a temporary file and writes the source into it.
     75 
     76         Returns:
     77             The name of the temporary file.
     78         """
     79     shader, self.filename = tempfile.mkstemp(
     80         dir=testcase.directory, suffix=self.suffix)
     81     shader_object = os.fdopen(shader, 'w')
     82     shader_object.write(self.source)
     83     shader_object.close()
     84     return self.filename
     85 
     86   def instantiate_for_expectation(self, testcase):
     87     assert self.filename is not None
     88     return self.filename
     89 
     90 
     91 class ConfigFlagsFile(PlaceHolder):
     92   """Stands for a configuration file for spirv-opt generated out of a string."""
     93 
     94   def __init__(self, content, suffix):
     95     assert isinstance(content, str)
     96     assert isinstance(suffix, str)
     97     self.content = content
     98     self.suffix = suffix
     99     self.filename = None
    100 
    101   def instantiate_for_spirv_args(self, testcase):
    102     """Creates a temporary file and writes content into it.
    103 
    104         Returns:
    105             The name of the temporary file.
    106     """
    107     temp_fd, self.filename = tempfile.mkstemp(
    108         dir=testcase.directory, suffix=self.suffix)
    109     fd = os.fdopen(temp_fd, 'w')
    110     fd.write(self.content)
    111     fd.close()
    112     return '-Oconfig=%s' % self.filename
    113 
    114   def instantiate_for_expectation(self, testcase):
    115     assert self.filename is not None
    116     return self.filename
    117 
    118 
    119 class FileSPIRVShader(PlaceHolder):
    120   """Stands for a source shader file which must be converted to SPIR-V."""
    121 
    122   def __init__(self, source, suffix, assembly_substr=None):
    123     assert isinstance(source, str)
    124     assert isinstance(suffix, str)
    125     self.source = source
    126     self.suffix = suffix
    127     self.filename = None
    128     # If provided, this is a substring which is expected to be in
    129     # the disassembly of the module generated from this input file.
    130     self.assembly_substr = assembly_substr
    131 
    132   def instantiate_for_spirv_args(self, testcase):
    133     """Creates a temporary file, writes the source into it and assembles it.
    134 
    135         Returns:
    136             The name of the assembled temporary file.
    137         """
    138     shader, asm_filename = tempfile.mkstemp(
    139         dir=testcase.directory, suffix=self.suffix)
    140     shader_object = os.fdopen(shader, 'w')
    141     shader_object.write(self.source)
    142     shader_object.close()
    143     self.filename = '%s.spv' % asm_filename
    144     cmd = [
    145         testcase.test_manager.assembler_path, asm_filename, '-o', self.filename
    146     ]
    147     process = subprocess.Popen(
    148         args=cmd,
    149         stdin=subprocess.PIPE,
    150         stdout=subprocess.PIPE,
    151         stderr=subprocess.PIPE,
    152         cwd=testcase.directory)
    153     output = process.communicate()
    154     assert process.returncode == 0 and not output[0] and not output[1]
    155     return self.filename
    156 
    157   def instantiate_for_expectation(self, testcase):
    158     assert self.filename is not None
    159     return self.filename
    160 
    161 
    162 class StdinShader(PlaceHolder):
    163   """Stands for a shader whose source code is from stdin."""
    164 
    165   def __init__(self, source):
    166     assert isinstance(source, str)
    167     self.source = source
    168     self.filename = None
    169 
    170   def instantiate_for_spirv_args(self, testcase):
    171     """Writes the source code back to the TestCase instance."""
    172     testcase.stdin_shader = self.source
    173     self.filename = '-'
    174     return self.filename
    175 
    176   def instantiate_for_expectation(self, testcase):
    177     assert self.filename is not None
    178     return self.filename
    179 
    180 
    181 class TempFileName(PlaceHolder):
    182   """Stands for a temporary file's name."""
    183 
    184   def __init__(self, filename):
    185     assert isinstance(filename, str)
    186     assert filename != ''
    187     self.filename = filename
    188 
    189   def instantiate_for_spirv_args(self, testcase):
    190     return os.path.join(testcase.directory, self.filename)
    191 
    192   def instantiate_for_expectation(self, testcase):
    193     return os.path.join(testcase.directory, self.filename)
    194 
    195 
    196 class SpecializedString(PlaceHolder):
    197   """Returns a string that has been specialized based on TestCase.
    198 
    199     The string is specialized by expanding it as a string.Template
    200     with all of the specialization being done with each $param replaced
    201     by the associated member on TestCase.
    202     """
    203 
    204   def __init__(self, filename):
    205     assert isinstance(filename, str)
    206     assert filename != ''
    207     self.filename = filename
    208 
    209   def instantiate_for_spirv_args(self, testcase):
    210     return Template(self.filename).substitute(vars(testcase))
    211 
    212   def instantiate_for_expectation(self, testcase):
    213     return Template(self.filename).substitute(vars(testcase))
    214