1#!/usr/bin/env python3 2 3# Copyright © 2024 Valve Corporation 4# SPDX-License-Identifier: MIT 5 6import argparse 7import collections 8import subprocess 9import os 10import re 11import sys 12import tempfile 13import textwrap 14from pathlib import Path 15 16class TestFileChange: 17 def __init__(self, line, result): 18 self.line = line 19 self.result = result 20 21class TestFileChanges: 22 def __init__(self, name): 23 self.name = name 24 self.changes = [] 25 26if __name__ == '__main__': 27 parser = argparse.ArgumentParser() 28 parser.add_argument('--build-dir', '-B', required=False) 29 parser.add_argument('--test-filter', '-f', required=False) 30 parser.add_argument('--update-all', '-u', action='store_true') 31 args = parser.parse_args() 32 33 bin_path = 'src/compiler/nir/nir_tests' 34 if args.build_dir: 35 bin_path = args.build_dir + '/' + bin_path 36 37 if not os.path.isfile(bin_path): 38 print(f'{bin_path} \033[91m does not exist!\033[0m') 39 exit(1) 40 41 build_args = ['meson', 'compile'] 42 if args.build_dir: 43 build_args.append(f'-C{args.build_dir}') 44 subprocess.run(build_args) 45 46 test_args = [bin_path] 47 if args.test_filter: 48 test_args.append(f'--gtest_filter={args.test_filter}') 49 50 env = os.environ.copy() 51 if args.update_all: 52 env['NIR_TEST_DUMP_SHADERS'] = 'true' 53 54 output = subprocess.run(test_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, env=env) 55 56 expected_pattern = re.compile(r'Expected \(([\d\w\W/.-_]+):(\d+)\):') 57 58 test_result = None 59 expectations = collections.defaultdict(list) 60 61 # Parse the output of the test binary and gather the changed shaders. 62 for output_line in output.stdout.split('\n'): 63 if output_line.startswith('Got:'): 64 test_result = '' 65 66 continue 67 68 if output_line.startswith('Expected ('): 69 match = expected_pattern.match(output_line) 70 file = match.group(1).removeprefix('../') 71 line = int(match.group(2)) 72 73 expectations[file].append(TestFileChange(line, test_result.strip())) 74 75 test_result = None 76 77 continue 78 79 if test_result is not None: 80 test_result += output_line + '\n' 81 82 patches = [] 83 84 # Generate patches for the changed shaders. 85 for file in expectations: 86 changes = expectations[file] 87 88 updated_test_file = '' 89 change_index = 0 90 line_index = 1 91 inside_expectation = False 92 93 with open(file) as test_file: 94 for test_line in test_file: 95 if test_line.strip().startswith(')\"'): 96 inside_expectation = False 97 98 if not inside_expectation: 99 updated_test_file += test_line 100 101 if change_index < len(changes) and line_index == changes[change_index].line: 102 inside_expectation = True 103 indentation = len(test_line) - len(test_line.lstrip()) + 3 104 updated_test_file += textwrap.indent(changes[change_index].result, " " * indentation) + '\n' 105 change_index += 1 106 107 line_index += 1 108 109 with tempfile.NamedTemporaryFile(delete_on_close=False) as tmp: 110 tmp.write(bytes(updated_test_file, encoding="utf-8")) 111 tmp.close() 112 113 diff = subprocess.run( 114 ['git', 'diff', '--no-index', file, tmp.name], 115 stdout=subprocess.PIPE, 116 stderr=subprocess.STDOUT, 117 universal_newlines=True, 118 ) 119 patch = diff.stdout.replace(tmp.name, '/' + file) 120 121 print(patch) 122 123 patches.append(patch) 124 125 if len(patches) != 0: 126 sys.stdout.write('\033[96mApply the changes listed above?\033[0m [Y/n]') 127 response = None 128 try: 129 response = input() 130 except KeyboardInterrupt: 131 print() 132 sys.exit(1) 133 134 if response in ['', 'y', 'Y']: 135 for patch in patches: 136 apply = subprocess.Popen( 137 ['git', 'apply', '--allow-empty'], 138 stdin=subprocess.PIPE, 139 ) 140 apply.communicate(input=bytes(patch, encoding="utf-8")) 141