1fd4e5da5Sopenharmony_ci# Copyright (c) 2018 Google LLC 2fd4e5da5Sopenharmony_ci# 3fd4e5da5Sopenharmony_ci# Licensed under the Apache License, Version 2.0 (the "License"); 4fd4e5da5Sopenharmony_ci# you may not use this file except in compliance with the License. 5fd4e5da5Sopenharmony_ci# You may obtain a copy of the License at 6fd4e5da5Sopenharmony_ci# 7fd4e5da5Sopenharmony_ci# http://www.apache.org/licenses/LICENSE-2.0 8fd4e5da5Sopenharmony_ci# 9fd4e5da5Sopenharmony_ci# Unless required by applicable law or agreed to in writing, software 10fd4e5da5Sopenharmony_ci# distributed under the License is distributed on an "AS IS" BASIS, 11fd4e5da5Sopenharmony_ci# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12fd4e5da5Sopenharmony_ci# See the License for the specific language governing permissions and 13fd4e5da5Sopenharmony_ci# limitations under the License. 14fd4e5da5Sopenharmony_ci"""Manages and runs tests from the current working directory. 15fd4e5da5Sopenharmony_ci 16fd4e5da5Sopenharmony_ciThis will traverse the current working directory and look for python files that 17fd4e5da5Sopenharmony_cicontain subclasses of SpirvTest. 18fd4e5da5Sopenharmony_ci 19fd4e5da5Sopenharmony_ciIf a class has an @inside_spirv_testsuite decorator, an instance of that 20fd4e5da5Sopenharmony_ciclass will be created and serve as a test case in that testsuite. The test 21fd4e5da5Sopenharmony_cicase is then run by the following steps: 22fd4e5da5Sopenharmony_ci 23fd4e5da5Sopenharmony_ci 1. A temporary directory will be created. 24fd4e5da5Sopenharmony_ci 2. The spirv_args member variable will be inspected and all placeholders in it 25fd4e5da5Sopenharmony_ci will be expanded by calling instantiate_for_spirv_args() on placeholders. 26fd4e5da5Sopenharmony_ci The transformed list elements are then supplied as arguments to the spirv-* 27fd4e5da5Sopenharmony_ci tool under test. 28fd4e5da5Sopenharmony_ci 3. If the environment member variable exists, its write() method will be 29fd4e5da5Sopenharmony_ci invoked. 30fd4e5da5Sopenharmony_ci 4. All expected_* member variables will be inspected and all placeholders in 31fd4e5da5Sopenharmony_ci them will be expanded by calling instantiate_for_expectation() on those 32fd4e5da5Sopenharmony_ci placeholders. After placeholder expansion, if the expected_* variable is 33fd4e5da5Sopenharmony_ci a list, its element will be joined together with '' to form a single 34fd4e5da5Sopenharmony_ci string. These expected_* variables are to be used by the check_*() methods. 35fd4e5da5Sopenharmony_ci 5. The spirv-* tool will be run with the arguments supplied in spirv_args. 36fd4e5da5Sopenharmony_ci 6. All check_*() member methods will be called by supplying a TestStatus as 37fd4e5da5Sopenharmony_ci argument. Each check_*() method is expected to return a (Success, Message) 38fd4e5da5Sopenharmony_ci pair where Success is a boolean indicating success and Message is an error 39fd4e5da5Sopenharmony_ci message. 40fd4e5da5Sopenharmony_ci 7. If any check_*() method fails, the error message is output and the 41fd4e5da5Sopenharmony_ci current test case fails. 42fd4e5da5Sopenharmony_ci 43fd4e5da5Sopenharmony_ciIf --leave-output was not specified, all temporary files and directories will 44fd4e5da5Sopenharmony_cibe deleted. 45fd4e5da5Sopenharmony_ci""" 46fd4e5da5Sopenharmony_ci 47fd4e5da5Sopenharmony_ciimport argparse 48fd4e5da5Sopenharmony_ciimport fnmatch 49fd4e5da5Sopenharmony_ciimport inspect 50fd4e5da5Sopenharmony_ciimport os 51fd4e5da5Sopenharmony_ciimport shutil 52fd4e5da5Sopenharmony_ciimport subprocess 53fd4e5da5Sopenharmony_ciimport sys 54fd4e5da5Sopenharmony_ciimport tempfile 55fd4e5da5Sopenharmony_cifrom collections import defaultdict 56fd4e5da5Sopenharmony_cifrom placeholder import PlaceHolder 57fd4e5da5Sopenharmony_ci 58fd4e5da5Sopenharmony_ciEXPECTED_BEHAVIOR_PREFIX = 'expected_' 59fd4e5da5Sopenharmony_ciVALIDATE_METHOD_PREFIX = 'check_' 60fd4e5da5Sopenharmony_ci 61fd4e5da5Sopenharmony_ci 62fd4e5da5Sopenharmony_cidef get_all_variables(instance): 63fd4e5da5Sopenharmony_ci """Returns the names of all the variables in instance.""" 64fd4e5da5Sopenharmony_ci return [v for v in dir(instance) if not callable(getattr(instance, v))] 65fd4e5da5Sopenharmony_ci 66fd4e5da5Sopenharmony_ci 67fd4e5da5Sopenharmony_cidef get_all_methods(instance): 68fd4e5da5Sopenharmony_ci """Returns the names of all methods in instance.""" 69fd4e5da5Sopenharmony_ci return [m for m in dir(instance) if callable(getattr(instance, m))] 70fd4e5da5Sopenharmony_ci 71fd4e5da5Sopenharmony_ci 72fd4e5da5Sopenharmony_cidef get_all_superclasses(cls): 73fd4e5da5Sopenharmony_ci """Returns all superclasses of a given class. Omits root 'object' superclass. 74fd4e5da5Sopenharmony_ci 75fd4e5da5Sopenharmony_ci Returns: 76fd4e5da5Sopenharmony_ci A list of superclasses of the given class. The order guarantees that 77fd4e5da5Sopenharmony_ci * A Base class precedes its derived classes, e.g., for "class B(A)", it 78fd4e5da5Sopenharmony_ci will be [..., A, B, ...]. 79fd4e5da5Sopenharmony_ci * When there are multiple base classes, base classes declared first 80fd4e5da5Sopenharmony_ci precede those declared later, e.g., for "class C(A, B), it will be 81fd4e5da5Sopenharmony_ci [..., A, B, C, ...] 82fd4e5da5Sopenharmony_ci """ 83fd4e5da5Sopenharmony_ci classes = [] 84fd4e5da5Sopenharmony_ci for superclass in cls.__bases__: 85fd4e5da5Sopenharmony_ci for c in get_all_superclasses(superclass): 86fd4e5da5Sopenharmony_ci if c is not object and c not in classes: 87fd4e5da5Sopenharmony_ci classes.append(c) 88fd4e5da5Sopenharmony_ci for superclass in cls.__bases__: 89fd4e5da5Sopenharmony_ci if superclass is not object and superclass not in classes: 90fd4e5da5Sopenharmony_ci classes.append(superclass) 91fd4e5da5Sopenharmony_ci 92fd4e5da5Sopenharmony_ci return classes 93fd4e5da5Sopenharmony_ci 94fd4e5da5Sopenharmony_ci 95fd4e5da5Sopenharmony_cidef get_all_test_methods(test_class): 96fd4e5da5Sopenharmony_ci """Gets all validation methods. 97fd4e5da5Sopenharmony_ci 98fd4e5da5Sopenharmony_ci Returns: 99fd4e5da5Sopenharmony_ci A list of validation methods. The order guarantees that 100fd4e5da5Sopenharmony_ci * A method defined in superclass precedes one defined in subclass, 101fd4e5da5Sopenharmony_ci e.g., for "class A(B)", methods defined in B precedes those defined 102fd4e5da5Sopenharmony_ci in A. 103fd4e5da5Sopenharmony_ci * If a subclass has more than one superclass, e.g., "class C(A, B)", 104fd4e5da5Sopenharmony_ci then methods defined in A precedes those defined in B. 105fd4e5da5Sopenharmony_ci """ 106fd4e5da5Sopenharmony_ci classes = get_all_superclasses(test_class) 107fd4e5da5Sopenharmony_ci classes.append(test_class) 108fd4e5da5Sopenharmony_ci all_tests = [ 109fd4e5da5Sopenharmony_ci m for c in classes for m in get_all_methods(c) 110fd4e5da5Sopenharmony_ci if m.startswith(VALIDATE_METHOD_PREFIX) 111fd4e5da5Sopenharmony_ci ] 112fd4e5da5Sopenharmony_ci unique_tests = [] 113fd4e5da5Sopenharmony_ci for t in all_tests: 114fd4e5da5Sopenharmony_ci if t not in unique_tests: 115fd4e5da5Sopenharmony_ci unique_tests.append(t) 116fd4e5da5Sopenharmony_ci return unique_tests 117fd4e5da5Sopenharmony_ci 118fd4e5da5Sopenharmony_ci 119fd4e5da5Sopenharmony_ciclass SpirvTest: 120fd4e5da5Sopenharmony_ci """Base class for spirv test cases. 121fd4e5da5Sopenharmony_ci 122fd4e5da5Sopenharmony_ci Subclasses define test cases' facts (shader source code, spirv command, 123fd4e5da5Sopenharmony_ci result validation), which will be used by the TestCase class for running 124fd4e5da5Sopenharmony_ci tests. Subclasses should define spirv_args (specifying spirv_tool command 125fd4e5da5Sopenharmony_ci arguments), and at least one check_*() method (for result validation) for 126fd4e5da5Sopenharmony_ci a full-fledged test case. All check_*() methods should take a TestStatus 127fd4e5da5Sopenharmony_ci parameter and return a (Success, Message) pair, in which Success is a 128fd4e5da5Sopenharmony_ci boolean indicating success and Message is an error message. The test passes 129fd4e5da5Sopenharmony_ci iff all check_*() methods returns true. 130fd4e5da5Sopenharmony_ci 131fd4e5da5Sopenharmony_ci Often, a test case class will delegate the check_* behaviors by inheriting 132fd4e5da5Sopenharmony_ci from other classes. 133fd4e5da5Sopenharmony_ci """ 134fd4e5da5Sopenharmony_ci 135fd4e5da5Sopenharmony_ci def name(self): 136fd4e5da5Sopenharmony_ci return self.__class__.__name__ 137fd4e5da5Sopenharmony_ci 138fd4e5da5Sopenharmony_ci 139fd4e5da5Sopenharmony_ciclass TestStatus: 140fd4e5da5Sopenharmony_ci """A struct for holding run status of a test case.""" 141fd4e5da5Sopenharmony_ci 142fd4e5da5Sopenharmony_ci def __init__(self, test_manager, returncode, stdout, stderr, directory, 143fd4e5da5Sopenharmony_ci inputs, input_filenames): 144fd4e5da5Sopenharmony_ci self.test_manager = test_manager 145fd4e5da5Sopenharmony_ci self.returncode = returncode 146fd4e5da5Sopenharmony_ci # Some of our MacOS bots still run Python 2, so need to be backwards 147fd4e5da5Sopenharmony_ci # compatible here. 148fd4e5da5Sopenharmony_ci if type(stdout) is not str: 149fd4e5da5Sopenharmony_ci if sys.version_info[0] == 2: 150fd4e5da5Sopenharmony_ci self.stdout = stdout.decode('utf-8') 151fd4e5da5Sopenharmony_ci elif sys.version_info[0] == 3: 152fd4e5da5Sopenharmony_ci self.stdout = str(stdout, encoding='utf-8') if stdout is not None else stdout 153fd4e5da5Sopenharmony_ci else: 154fd4e5da5Sopenharmony_ci raise Exception('Unable to determine if running Python 2 or 3 from {}'.format(sys.version_info)) 155fd4e5da5Sopenharmony_ci else: 156fd4e5da5Sopenharmony_ci self.stdout = stdout 157fd4e5da5Sopenharmony_ci 158fd4e5da5Sopenharmony_ci if type(stderr) is not str: 159fd4e5da5Sopenharmony_ci if sys.version_info[0] == 2: 160fd4e5da5Sopenharmony_ci self.stderr = stderr.decode('utf-8') 161fd4e5da5Sopenharmony_ci elif sys.version_info[0] == 3: 162fd4e5da5Sopenharmony_ci self.stderr = str(stderr, encoding='utf-8') if stderr is not None else stderr 163fd4e5da5Sopenharmony_ci else: 164fd4e5da5Sopenharmony_ci raise Exception('Unable to determine if running Python 2 or 3 from {}'.format(sys.version_info)) 165fd4e5da5Sopenharmony_ci else: 166fd4e5da5Sopenharmony_ci self.stderr = stderr 167fd4e5da5Sopenharmony_ci 168fd4e5da5Sopenharmony_ci # temporary directory where the test runs 169fd4e5da5Sopenharmony_ci self.directory = directory 170fd4e5da5Sopenharmony_ci # List of inputs, as PlaceHolder objects. 171fd4e5da5Sopenharmony_ci self.inputs = inputs 172fd4e5da5Sopenharmony_ci # the names of input shader files (potentially including paths) 173fd4e5da5Sopenharmony_ci self.input_filenames = input_filenames 174fd4e5da5Sopenharmony_ci 175fd4e5da5Sopenharmony_ci 176fd4e5da5Sopenharmony_ciclass SpirvTestException(Exception): 177fd4e5da5Sopenharmony_ci """SpirvTest exception class.""" 178fd4e5da5Sopenharmony_ci pass 179fd4e5da5Sopenharmony_ci 180fd4e5da5Sopenharmony_ci 181fd4e5da5Sopenharmony_cidef inside_spirv_testsuite(testsuite_name): 182fd4e5da5Sopenharmony_ci """Decorator for subclasses of SpirvTest. 183fd4e5da5Sopenharmony_ci 184fd4e5da5Sopenharmony_ci This decorator checks that a class meets the requirements (see below) 185fd4e5da5Sopenharmony_ci for a test case class, and then puts the class in a certain testsuite. 186fd4e5da5Sopenharmony_ci * The class needs to be a subclass of SpirvTest. 187fd4e5da5Sopenharmony_ci * The class needs to have spirv_args defined as a list. 188fd4e5da5Sopenharmony_ci * The class needs to define at least one check_*() methods. 189fd4e5da5Sopenharmony_ci * All expected_* variables required by check_*() methods can only be 190fd4e5da5Sopenharmony_ci of bool, str, or list type. 191fd4e5da5Sopenharmony_ci * Python runtime will throw an exception if the expected_* member 192fd4e5da5Sopenharmony_ci attributes required by check_*() methods are missing. 193fd4e5da5Sopenharmony_ci """ 194fd4e5da5Sopenharmony_ci 195fd4e5da5Sopenharmony_ci def actual_decorator(cls): 196fd4e5da5Sopenharmony_ci if not inspect.isclass(cls): 197fd4e5da5Sopenharmony_ci raise SpirvTestException('Test case should be a class') 198fd4e5da5Sopenharmony_ci if not issubclass(cls, SpirvTest): 199fd4e5da5Sopenharmony_ci raise SpirvTestException( 200fd4e5da5Sopenharmony_ci 'All test cases should be subclasses of SpirvTest') 201fd4e5da5Sopenharmony_ci if 'spirv_args' not in get_all_variables(cls): 202fd4e5da5Sopenharmony_ci raise SpirvTestException('No spirv_args found in the test case') 203fd4e5da5Sopenharmony_ci if not isinstance(cls.spirv_args, list): 204fd4e5da5Sopenharmony_ci raise SpirvTestException('spirv_args needs to be a list') 205fd4e5da5Sopenharmony_ci if not any( 206fd4e5da5Sopenharmony_ci [m.startswith(VALIDATE_METHOD_PREFIX) for m in get_all_methods(cls)]): 207fd4e5da5Sopenharmony_ci raise SpirvTestException('No check_*() methods found in the test case') 208fd4e5da5Sopenharmony_ci if not all( 209fd4e5da5Sopenharmony_ci [isinstance(v, (bool, str, list)) for v in get_all_variables(cls)]): 210fd4e5da5Sopenharmony_ci raise SpirvTestException( 211fd4e5da5Sopenharmony_ci 'expected_* variables are only allowed to be bool, str, or ' 212fd4e5da5Sopenharmony_ci 'list type.') 213fd4e5da5Sopenharmony_ci cls.parent_testsuite = testsuite_name 214fd4e5da5Sopenharmony_ci return cls 215fd4e5da5Sopenharmony_ci 216fd4e5da5Sopenharmony_ci return actual_decorator 217fd4e5da5Sopenharmony_ci 218fd4e5da5Sopenharmony_ci 219fd4e5da5Sopenharmony_ciclass TestManager: 220fd4e5da5Sopenharmony_ci """Manages and runs a set of tests.""" 221fd4e5da5Sopenharmony_ci 222fd4e5da5Sopenharmony_ci def __init__(self, executable_path, assembler_path, disassembler_path): 223fd4e5da5Sopenharmony_ci self.executable_path = executable_path 224fd4e5da5Sopenharmony_ci self.assembler_path = assembler_path 225fd4e5da5Sopenharmony_ci self.disassembler_path = disassembler_path 226fd4e5da5Sopenharmony_ci self.num_successes = 0 227fd4e5da5Sopenharmony_ci self.num_failures = 0 228fd4e5da5Sopenharmony_ci self.num_tests = 0 229fd4e5da5Sopenharmony_ci self.leave_output = False 230fd4e5da5Sopenharmony_ci self.tests = defaultdict(list) 231fd4e5da5Sopenharmony_ci 232fd4e5da5Sopenharmony_ci def notify_result(self, test_case, success, message): 233fd4e5da5Sopenharmony_ci """Call this to notify the manager of the results of a test run.""" 234fd4e5da5Sopenharmony_ci self.num_successes += 1 if success else 0 235fd4e5da5Sopenharmony_ci self.num_failures += 0 if success else 1 236fd4e5da5Sopenharmony_ci counter_string = str(self.num_successes + self.num_failures) + '/' + str( 237fd4e5da5Sopenharmony_ci self.num_tests) 238fd4e5da5Sopenharmony_ci print('%-10s %-40s ' % (counter_string, test_case.test.name()) + 239fd4e5da5Sopenharmony_ci ('Passed' if success else '-Failed-')) 240fd4e5da5Sopenharmony_ci if not success: 241fd4e5da5Sopenharmony_ci print(' '.join(test_case.command)) 242fd4e5da5Sopenharmony_ci print(message) 243fd4e5da5Sopenharmony_ci 244fd4e5da5Sopenharmony_ci def add_test(self, testsuite, test): 245fd4e5da5Sopenharmony_ci """Add this to the current list of test cases.""" 246fd4e5da5Sopenharmony_ci self.tests[testsuite].append(TestCase(test, self)) 247fd4e5da5Sopenharmony_ci self.num_tests += 1 248fd4e5da5Sopenharmony_ci 249fd4e5da5Sopenharmony_ci def run_tests(self): 250fd4e5da5Sopenharmony_ci for suite in self.tests: 251fd4e5da5Sopenharmony_ci print('SPIRV tool test suite: "{suite}"'.format(suite=suite)) 252fd4e5da5Sopenharmony_ci for x in self.tests[suite]: 253fd4e5da5Sopenharmony_ci x.runTest() 254fd4e5da5Sopenharmony_ci 255fd4e5da5Sopenharmony_ci 256fd4e5da5Sopenharmony_ciclass TestCase: 257fd4e5da5Sopenharmony_ci """A single test case that runs in its own directory.""" 258fd4e5da5Sopenharmony_ci 259fd4e5da5Sopenharmony_ci def __init__(self, test, test_manager): 260fd4e5da5Sopenharmony_ci self.test = test 261fd4e5da5Sopenharmony_ci self.test_manager = test_manager 262fd4e5da5Sopenharmony_ci self.inputs = [] # inputs, as PlaceHolder objects. 263fd4e5da5Sopenharmony_ci self.file_shaders = [] # filenames of shader files. 264fd4e5da5Sopenharmony_ci self.stdin_shader = None # text to be passed to spirv_tool as stdin 265fd4e5da5Sopenharmony_ci 266fd4e5da5Sopenharmony_ci def setUp(self): 267fd4e5da5Sopenharmony_ci """Creates environment and instantiates placeholders for the test case.""" 268fd4e5da5Sopenharmony_ci 269fd4e5da5Sopenharmony_ci self.directory = tempfile.mkdtemp(dir=os.getcwd()) 270fd4e5da5Sopenharmony_ci spirv_args = self.test.spirv_args 271fd4e5da5Sopenharmony_ci # Instantiate placeholders in spirv_args 272fd4e5da5Sopenharmony_ci self.test.spirv_args = [ 273fd4e5da5Sopenharmony_ci arg.instantiate_for_spirv_args(self) 274fd4e5da5Sopenharmony_ci if isinstance(arg, PlaceHolder) else arg for arg in self.test.spirv_args 275fd4e5da5Sopenharmony_ci ] 276fd4e5da5Sopenharmony_ci # Get all shader files' names 277fd4e5da5Sopenharmony_ci self.inputs = [arg for arg in spirv_args if isinstance(arg, PlaceHolder)] 278fd4e5da5Sopenharmony_ci self.file_shaders = [arg.filename for arg in self.inputs] 279fd4e5da5Sopenharmony_ci 280fd4e5da5Sopenharmony_ci if 'environment' in get_all_variables(self.test): 281fd4e5da5Sopenharmony_ci self.test.environment.write(self.directory) 282fd4e5da5Sopenharmony_ci 283fd4e5da5Sopenharmony_ci expectations = [ 284fd4e5da5Sopenharmony_ci v for v in get_all_variables(self.test) 285fd4e5da5Sopenharmony_ci if v.startswith(EXPECTED_BEHAVIOR_PREFIX) 286fd4e5da5Sopenharmony_ci ] 287fd4e5da5Sopenharmony_ci # Instantiate placeholders in expectations 288fd4e5da5Sopenharmony_ci for expectation_name in expectations: 289fd4e5da5Sopenharmony_ci expectation = getattr(self.test, expectation_name) 290fd4e5da5Sopenharmony_ci if isinstance(expectation, list): 291fd4e5da5Sopenharmony_ci expanded_expections = [ 292fd4e5da5Sopenharmony_ci element.instantiate_for_expectation(self) 293fd4e5da5Sopenharmony_ci if isinstance(element, PlaceHolder) else element 294fd4e5da5Sopenharmony_ci for element in expectation 295fd4e5da5Sopenharmony_ci ] 296fd4e5da5Sopenharmony_ci setattr(self.test, expectation_name, expanded_expections) 297fd4e5da5Sopenharmony_ci elif isinstance(expectation, PlaceHolder): 298fd4e5da5Sopenharmony_ci setattr(self.test, expectation_name, 299fd4e5da5Sopenharmony_ci expectation.instantiate_for_expectation(self)) 300fd4e5da5Sopenharmony_ci 301fd4e5da5Sopenharmony_ci def tearDown(self): 302fd4e5da5Sopenharmony_ci """Removes the directory if we were not instructed to do otherwise.""" 303fd4e5da5Sopenharmony_ci if not self.test_manager.leave_output: 304fd4e5da5Sopenharmony_ci shutil.rmtree(self.directory) 305fd4e5da5Sopenharmony_ci 306fd4e5da5Sopenharmony_ci def runTest(self): 307fd4e5da5Sopenharmony_ci """Sets up and runs a test, reports any failures and then cleans up.""" 308fd4e5da5Sopenharmony_ci self.setUp() 309fd4e5da5Sopenharmony_ci success = False 310fd4e5da5Sopenharmony_ci message = '' 311fd4e5da5Sopenharmony_ci try: 312fd4e5da5Sopenharmony_ci self.command = [self.test_manager.executable_path] 313fd4e5da5Sopenharmony_ci self.command.extend(self.test.spirv_args) 314fd4e5da5Sopenharmony_ci 315fd4e5da5Sopenharmony_ci process = subprocess.Popen( 316fd4e5da5Sopenharmony_ci args=self.command, 317fd4e5da5Sopenharmony_ci stdin=subprocess.PIPE, 318fd4e5da5Sopenharmony_ci stdout=subprocess.PIPE, 319fd4e5da5Sopenharmony_ci stderr=subprocess.PIPE, 320fd4e5da5Sopenharmony_ci cwd=self.directory) 321fd4e5da5Sopenharmony_ci output = process.communicate(self.stdin_shader) 322fd4e5da5Sopenharmony_ci test_status = TestStatus(self.test_manager, process.returncode, output[0], 323fd4e5da5Sopenharmony_ci output[1], self.directory, self.inputs, 324fd4e5da5Sopenharmony_ci self.file_shaders) 325fd4e5da5Sopenharmony_ci run_results = [ 326fd4e5da5Sopenharmony_ci getattr(self.test, test_method)(test_status) 327fd4e5da5Sopenharmony_ci for test_method in get_all_test_methods(self.test.__class__) 328fd4e5da5Sopenharmony_ci ] 329fd4e5da5Sopenharmony_ci success, message = zip(*run_results) 330fd4e5da5Sopenharmony_ci success = all(success) 331fd4e5da5Sopenharmony_ci message = '\n'.join(message) 332fd4e5da5Sopenharmony_ci except Exception as e: 333fd4e5da5Sopenharmony_ci success = False 334fd4e5da5Sopenharmony_ci message = str(e) 335fd4e5da5Sopenharmony_ci self.test_manager.notify_result( 336fd4e5da5Sopenharmony_ci self, success, 337fd4e5da5Sopenharmony_ci message + '\nSTDOUT:\n%s\nSTDERR:\n%s' % (output[0], output[1])) 338fd4e5da5Sopenharmony_ci self.tearDown() 339fd4e5da5Sopenharmony_ci 340fd4e5da5Sopenharmony_ci 341fd4e5da5Sopenharmony_cidef main(): 342fd4e5da5Sopenharmony_ci parser = argparse.ArgumentParser() 343fd4e5da5Sopenharmony_ci parser.add_argument( 344fd4e5da5Sopenharmony_ci 'spirv_tool', 345fd4e5da5Sopenharmony_ci metavar='path/to/spirv_tool', 346fd4e5da5Sopenharmony_ci type=str, 347fd4e5da5Sopenharmony_ci nargs=1, 348fd4e5da5Sopenharmony_ci help='Path to the spirv-* tool under test') 349fd4e5da5Sopenharmony_ci parser.add_argument( 350fd4e5da5Sopenharmony_ci 'spirv_as', 351fd4e5da5Sopenharmony_ci metavar='path/to/spirv-as', 352fd4e5da5Sopenharmony_ci type=str, 353fd4e5da5Sopenharmony_ci nargs=1, 354fd4e5da5Sopenharmony_ci help='Path to spirv-as') 355fd4e5da5Sopenharmony_ci parser.add_argument( 356fd4e5da5Sopenharmony_ci 'spirv_dis', 357fd4e5da5Sopenharmony_ci metavar='path/to/spirv-dis', 358fd4e5da5Sopenharmony_ci type=str, 359fd4e5da5Sopenharmony_ci nargs=1, 360fd4e5da5Sopenharmony_ci help='Path to spirv-dis') 361fd4e5da5Sopenharmony_ci parser.add_argument( 362fd4e5da5Sopenharmony_ci '--leave-output', 363fd4e5da5Sopenharmony_ci action='store_const', 364fd4e5da5Sopenharmony_ci const=1, 365fd4e5da5Sopenharmony_ci help='Do not clean up temporary directories') 366fd4e5da5Sopenharmony_ci parser.add_argument( 367fd4e5da5Sopenharmony_ci '--test-dir', nargs=1, help='Directory to gather the tests from') 368fd4e5da5Sopenharmony_ci args = parser.parse_args() 369fd4e5da5Sopenharmony_ci default_path = sys.path 370fd4e5da5Sopenharmony_ci root_dir = os.getcwd() 371fd4e5da5Sopenharmony_ci if args.test_dir: 372fd4e5da5Sopenharmony_ci root_dir = args.test_dir[0] 373fd4e5da5Sopenharmony_ci manager = TestManager(args.spirv_tool[0], args.spirv_as[0], args.spirv_dis[0]) 374fd4e5da5Sopenharmony_ci if args.leave_output: 375fd4e5da5Sopenharmony_ci manager.leave_output = True 376fd4e5da5Sopenharmony_ci for root, _, filenames in os.walk(root_dir): 377fd4e5da5Sopenharmony_ci for filename in fnmatch.filter(filenames, '*.py'): 378fd4e5da5Sopenharmony_ci if filename.endswith('nosetest.py'): 379fd4e5da5Sopenharmony_ci # Skip nose tests, which are for testing functions of 380fd4e5da5Sopenharmony_ci # the test framework. 381fd4e5da5Sopenharmony_ci continue 382fd4e5da5Sopenharmony_ci sys.path = default_path 383fd4e5da5Sopenharmony_ci sys.path.append(root) 384fd4e5da5Sopenharmony_ci mod = __import__(os.path.splitext(filename)[0]) 385fd4e5da5Sopenharmony_ci for _, obj, in inspect.getmembers(mod): 386fd4e5da5Sopenharmony_ci if inspect.isclass(obj) and hasattr(obj, 'parent_testsuite'): 387fd4e5da5Sopenharmony_ci manager.add_test(obj.parent_testsuite, obj()) 388fd4e5da5Sopenharmony_ci manager.run_tests() 389fd4e5da5Sopenharmony_ci if manager.num_failures > 0: 390fd4e5da5Sopenharmony_ci sys.exit(-1) 391fd4e5da5Sopenharmony_ci 392fd4e5da5Sopenharmony_ci 393fd4e5da5Sopenharmony_ciif __name__ == '__main__': 394fd4e5da5Sopenharmony_ci main() 395