nir/tests: Do not rely on __LINE__

__LINE__ can be inconsistent when using different compilers. This patch
changes the test runner to do a simple string find/replace of the test
source file instead of looking for the line where the reference string
starts.

Acked-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/33980>
This commit is contained in:
Konstantin
2025-03-05 16:07:29 +01:00
committed by Marge Bot
parent c496774c35
commit e7a44de184
2 changed files with 82 additions and 41 deletions

View File

@@ -13,15 +13,44 @@ import tempfile
import textwrap
from pathlib import Path
class TestFileChange:
def __init__(self, line, result):
self.line = line
self.result = result
def trim_blank_lines(string, trailing):
lines = string.split('\n')
string = ''
empty_line = True
for i in range(len(lines)):
line_index = len(lines) - 1 - i if trailing else i
if empty_line and lines[line_index].strip() == '':
continue
class TestFileChanges:
def __init__(self, name):
self.name = name
self.changes = []
write_newline = not empty_line if trailing else line_index < len(lines) - 1
newline = '\n' if write_newline else ''
if trailing:
string = lines[line_index] + newline + string
else:
string += lines[line_index] + newline
empty_line = False
return string
class TestFileChange:
def __init__(self, expected, result):
self.expected = expected
# Apply the indentation of the expectation to the result
indentation = 1000
for expected_line in expected.split('\n'):
if match := re.match(r'^(\s*)\S', expected_line):
line_indentation = len(match.group(1))
if indentation > line_indentation:
indentation = line_indentation
self.result = ''
result = result.split('\n')
for i in range(len(result)):
result_line = result[i]
indentation_str = '' if result_line.strip() == '' else ' '*indentation
self.result += indentation_str + result_line + ('\n' if i < len(result) - 1 else '')
if __name__ == '__main__':
parser = argparse.ArgumentParser()
@@ -53,31 +82,52 @@ if __name__ == '__main__':
output = subprocess.run(test_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, env=env)
expected_pattern = re.compile(r'Expected \(([\d\w\W/.-_]+):(\d+)\):')
expected_pattern = re.compile(r'BEGIN EXPECTED\(([\d\w\W/.-_]+)\)')
test_result = None
expectations = collections.defaultdict(list)
current_file = None
current_result = None
current_expected = None
inside_result = False
inside_expected = False
# Parse the output of the test binary and gather the changed shaders.
for output_line in output.stdout.split('\n'):
if output_line.startswith('Got:'):
test_result = ''
if output_line.startswith('BEGIN RESULT'):
inside_result = True
current_result = ''
continue
if output_line.startswith('Expected ('):
if output_line.startswith('BEGIN EXPECTED'):
match = expected_pattern.match(output_line)
file = match.group(1).removeprefix('../')
line = int(match.group(2))
expectations[file].append(TestFileChange(line, test_result.strip()))
test_result = None
current_file = match.group(1).removeprefix('../')
inside_expected = True
current_expected = ''
continue
if test_result is not None:
test_result += output_line + '\n'
if output_line.startswith('END'):
if current_result is not None and current_expected is not None:
# remove trailing and leading blank lines
current_result = trim_blank_lines(current_result, True)
current_result = trim_blank_lines(current_result, False)
current_expected = trim_blank_lines(current_expected, True)
current_expected = trim_blank_lines(current_expected, False)
expectations[current_file].append(TestFileChange(current_expected, current_result))
current_result = None
current_expected = None
inside_result = False
inside_expected = False
continue
if inside_result:
current_result += output_line + '\n'
if inside_expected:
current_expected += output_line + '\n'
patches = []
@@ -86,25 +136,17 @@ if __name__ == '__main__':
changes = expectations[file]
updated_test_file = ''
change_index = 0
line_index = 1
inside_expectation = False
with open(file) as test_file:
for test_line in test_file:
if test_line.strip().startswith(')\"'):
inside_expectation = False
updated_test_file = str(test_file.read())
if not inside_expectation:
updated_test_file += test_line
for change in changes:
updated_test_file = updated_test_file.replace(change.expected, change.result)
if change_index < len(changes) and line_index == changes[change_index].line:
inside_expectation = True
indentation = len(test_line) - len(test_line.lstrip()) + 3
updated_test_file += textwrap.indent(changes[change_index].result, " " * indentation) + '\n'
change_index += 1
line_index += 1
# change.expected == change.result can be the case when using NIR_TEST_DUMP_SHADERS.
if change.expected in updated_test_file and change.expected != change.result:
print(f'Duplicate test case in {file}!')
exit(1)
with tempfile.NamedTemporaryFile(delete_on_close=False) as tmp:
tmp.write(bytes(updated_test_file, encoding="utf-8"))

View File

@@ -16,7 +16,6 @@
struct nir_reference_shader {
const char *string;
const char *file;
uint32_t line;
};
class nir_test : public ::testing::Test {
@@ -121,7 +120,7 @@ class nir_test : public ::testing::Test {
}
if (failed || debug_get_bool_option("NIR_TEST_DUMP_SHADERS", false))
printf("Got:\n%s\nExpected (%s:%u):\n%s\n", result, reference.file, reference.line, expected);
printf("BEGIN RESULT\n%s\nEND\nBEGIN EXPECTED(%s)\n%s\nEND\n", result, reference.file, expected);
free(result);
}
@@ -131,6 +130,6 @@ class nir_test : public ::testing::Test {
nir_builder *b;
};
#define NIR_REFERENCE_SHADER(expected) nir_reference_shader{.string = expected, .file = __FILE__, .line = __LINE__}
#define NIR_REFERENCE_SHADER(expected) nir_reference_shader{.string = expected, .file = __FILE__}
#endif