305 lines
9.7 KiB
Python
Executable File
305 lines
9.7 KiB
Python
Executable File
#! /usr/bin/python3
|
|
#
|
|
# Copyright (c) 2022 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.
|
|
|
|
import glob
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
|
|
# A handful of relevant tests are hand-picked to generate extra unit tests with
|
|
# specific options of spirv-diff.
|
|
IGNORE_SET_BINDING_TESTS = ['different_decorations_vertex']
|
|
IGNORE_LOCATION_TESTS = ['different_decorations_fragment']
|
|
IGNORE_DECORATIONS_TESTS = ['different_decorations_vertex', 'different_decorations_fragment']
|
|
DUMP_IDS_TESTS = ['basic', 'int_vs_uint_constants', 'multiple_same_entry_points', 'small_functions_small_diffs']
|
|
|
|
LICENSE = u"""Copyright (c) 2022 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.
|
|
"""
|
|
|
|
TEMPLATE_TEST_FILE = u"""// GENERATED FILE - DO NOT EDIT.
|
|
// Generated by {script_name}
|
|
//
|
|
{license}
|
|
|
|
#include "../diff_test_utils.h"
|
|
|
|
#include "gtest/gtest.h"
|
|
|
|
namespace spvtools {{
|
|
namespace diff {{
|
|
namespace {{
|
|
|
|
{test_comment}
|
|
constexpr char kSrc[] = R"({src_spirv})";
|
|
constexpr char kDst[] = R"({dst_spirv})";
|
|
|
|
TEST(DiffTest, {test_name}) {{
|
|
constexpr char kDiff[] = R"({diff_spirv})";
|
|
Options options;
|
|
DoStringDiffTest(kSrc, kDst, kDiff, options);
|
|
}}
|
|
|
|
TEST(DiffTest, {test_name}NoDebug) {{
|
|
constexpr char kSrcNoDebug[] = R"({src_spirv_no_debug})";
|
|
constexpr char kDstNoDebug[] = R"({dst_spirv_no_debug})";
|
|
constexpr char kDiff[] = R"({diff_spirv_no_debug})";
|
|
Options options;
|
|
DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
|
|
}}
|
|
{extra_tests}
|
|
}} // namespace
|
|
}} // namespace diff
|
|
}} // namespace spvtools
|
|
"""
|
|
|
|
TEMPLATE_TEST_FUNC = u"""
|
|
TEST(DiffTest, {test_name}{test_tag}) {{
|
|
constexpr char kDiff[] = R"({diff_spirv})";
|
|
Options options;
|
|
{test_options}
|
|
DoStringDiffTest(kSrc, kDst, kDiff, options);
|
|
}}
|
|
"""
|
|
|
|
TEMPLATE_TEST_FILES_CMAKE = u"""# GENERATED FILE - DO NOT EDIT.
|
|
# Generated by {script_name}
|
|
#
|
|
{license}
|
|
|
|
list(APPEND DIFF_TEST_FILES
|
|
{test_files}
|
|
)
|
|
"""
|
|
|
|
VARIANT_NONE = 0
|
|
VARIANT_IGNORE_SET_BINDING = 1
|
|
VARIANT_IGNORE_LOCATION = 2
|
|
VARIANT_IGNORE_DECORATIONS = 3
|
|
VARIANT_DUMP_IDS = 4
|
|
|
|
def print_usage():
|
|
print("Usage: {} <path-to-spirv-diff>".format(sys.argv[0]))
|
|
|
|
def remove_debug_info(in_path):
|
|
tmp_dir = '.no_dbg'
|
|
|
|
if not os.path.exists(tmp_dir):
|
|
os.makedirs(tmp_dir)
|
|
|
|
(in_basename, in_ext) = os.path.splitext(in_path)
|
|
out_name = in_basename + '_no_dbg' + in_ext
|
|
out_path = os.path.join(tmp_dir, out_name)
|
|
|
|
with open(in_path, 'r') as fin:
|
|
with open(out_path, 'w') as fout:
|
|
for line in fin:
|
|
ops = line.strip().split()
|
|
op = ops[0] if len(ops) > 0 else ''
|
|
if (op != ';;' and op != 'OpName' and op != 'OpMemberName' and op != 'OpString' and
|
|
op != 'OpLine' and op != 'OpNoLine' and op != 'OpModuleProcessed'):
|
|
fout.write(line)
|
|
|
|
return out_path
|
|
|
|
def make_src_file(test_name):
|
|
return '{}_src.spvasm'.format(test_name)
|
|
|
|
def make_dst_file(test_name):
|
|
return '{}_dst.spvasm'.format(test_name)
|
|
|
|
def make_cpp_file(test_name):
|
|
return '{}_autogen.cpp'.format(test_name)
|
|
|
|
def make_camel_case(test_name):
|
|
return test_name.replace('_', ' ').title().replace(' ', '')
|
|
|
|
def make_comment(text, comment_prefix):
|
|
return '\n'.join([comment_prefix + (' ' if line.strip() else '') + line for line in text.splitlines()])
|
|
|
|
def read_file(file_name):
|
|
with open(file_name, 'r') as f:
|
|
content = f.read()
|
|
|
|
# Use unix line endings.
|
|
content = content.replace('\r\n', '\n')
|
|
|
|
return content
|
|
|
|
def parse_test_comment(src_spirv_file_name, src_spirv):
|
|
src_spirv_lines = src_spirv.splitlines()
|
|
comment_line_count = 0
|
|
while comment_line_count < len(src_spirv_lines):
|
|
if not src_spirv_lines[comment_line_count].strip().startswith(';;'):
|
|
break
|
|
comment_line_count += 1
|
|
|
|
if comment_line_count == 0:
|
|
print("Expected comment on test file '{}'. See README.md next to this file.".format(src_spirv_file_name))
|
|
sys.exit(1)
|
|
|
|
comment_block = src_spirv_lines[:comment_line_count]
|
|
spirv_block = src_spirv_lines[comment_line_count:]
|
|
|
|
comment_block = ['// ' + line.replace(';;', '').strip() for line in comment_block]
|
|
|
|
return '\n'.join(spirv_block), '\n'.join(comment_block)
|
|
|
|
def run_diff_tool(diff_tool, src_file, dst_file, variant):
|
|
args = [diff_tool]
|
|
|
|
if variant == VARIANT_IGNORE_SET_BINDING or variant == VARIANT_IGNORE_DECORATIONS:
|
|
args.append('--ignore-set-binding')
|
|
|
|
if variant == VARIANT_IGNORE_LOCATION or variant == VARIANT_IGNORE_DECORATIONS:
|
|
args.append('--ignore-location')
|
|
|
|
if variant == VARIANT_DUMP_IDS:
|
|
args.append('--with-id-map')
|
|
|
|
args.append('--no-color')
|
|
args.append('--no-indent')
|
|
|
|
args.append(src_file)
|
|
args.append(dst_file)
|
|
|
|
success = True
|
|
print(' '.join(args))
|
|
process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
|
out, err = process.communicate()
|
|
|
|
if process.returncode != 0:
|
|
print(err)
|
|
sys.exit(process.returncode)
|
|
|
|
# Use unix line endings.
|
|
out = out.replace('\r\n', '\n')
|
|
|
|
return out
|
|
|
|
def generate_extra_test(diff_tool, src_file, dst_file, variant, test_name_camel_case, test_tag, test_options):
|
|
diff = run_diff_tool(diff_tool, src_file, dst_file, variant)
|
|
return TEMPLATE_TEST_FUNC.format(
|
|
test_name = test_name_camel_case,
|
|
test_tag = test_tag,
|
|
test_options = test_options,
|
|
diff_spirv = diff)
|
|
|
|
def generate_test(diff_tool, test_name):
|
|
src_file = make_src_file(test_name)
|
|
dst_file = make_dst_file(test_name)
|
|
src_file_no_debug = remove_debug_info(src_file)
|
|
dst_file_no_debug = remove_debug_info(dst_file)
|
|
|
|
src_spirv = read_file(src_file)
|
|
dst_spirv = read_file(dst_file)
|
|
src_spirv_no_debug = read_file(src_file_no_debug)
|
|
dst_spirv_no_debug = read_file(dst_file_no_debug)
|
|
|
|
test_name_camel_case = make_camel_case(test_name)
|
|
|
|
diff_spirv = run_diff_tool(diff_tool, src_file, dst_file, VARIANT_NONE)
|
|
diff_spirv_no_debug = run_diff_tool(diff_tool, src_file_no_debug, dst_file_no_debug, VARIANT_NONE)
|
|
|
|
extra_tests = []
|
|
|
|
if test_name in IGNORE_SET_BINDING_TESTS:
|
|
extra_tests.append(generate_extra_test(diff_tool, src_file, dst_file, VARIANT_IGNORE_SET_BINDING,
|
|
test_name_camel_case, 'IgnoreSetBinding', 'options.ignore_set_binding = true;'))
|
|
|
|
if test_name in IGNORE_LOCATION_TESTS:
|
|
extra_tests.append(generate_extra_test(diff_tool, src_file, dst_file, VARIANT_IGNORE_LOCATION,
|
|
test_name_camel_case, 'IgnoreLocation', 'options.ignore_location = true;'))
|
|
|
|
if test_name in IGNORE_DECORATIONS_TESTS:
|
|
extra_tests.append(generate_extra_test(diff_tool, src_file, dst_file, VARIANT_IGNORE_DECORATIONS,
|
|
test_name_camel_case, 'IgnoreSetBindingLocation',
|
|
'\n '.join(['options.ignore_set_binding = true;', 'options.ignore_location = true;'])))
|
|
|
|
if test_name in DUMP_IDS_TESTS:
|
|
extra_tests.append(generate_extra_test(diff_tool, src_file, dst_file, VARIANT_DUMP_IDS,
|
|
test_name_camel_case, 'DumpIds', 'options.dump_id_map = true;'))
|
|
|
|
src_spirv, test_comment = parse_test_comment(src_file, src_spirv)
|
|
|
|
test_file = TEMPLATE_TEST_FILE.format(
|
|
script_name = os.path.basename(__file__),
|
|
license = make_comment(LICENSE, '//'),
|
|
test_comment = test_comment,
|
|
test_name = test_name_camel_case,
|
|
src_spirv = src_spirv,
|
|
dst_spirv = dst_spirv,
|
|
diff_spirv = diff_spirv,
|
|
src_spirv_no_debug = src_spirv_no_debug,
|
|
dst_spirv_no_debug = dst_spirv_no_debug,
|
|
diff_spirv_no_debug = diff_spirv_no_debug,
|
|
extra_tests = ''.join(extra_tests))
|
|
|
|
test_file_name = make_cpp_file(test_name)
|
|
with open(test_file_name, 'wb') as fout:
|
|
fout.write(str.encode(test_file))
|
|
|
|
return test_file_name
|
|
|
|
def generate_tests(diff_tool, test_names):
|
|
return [generate_test(diff_tool, test_name) for test_name in test_names]
|
|
|
|
def generate_cmake(test_files):
|
|
cmake = TEMPLATE_TEST_FILES_CMAKE.format(
|
|
script_name = os.path.basename(__file__),
|
|
license = make_comment(LICENSE, '#'),
|
|
test_files = '\n'.join(['"diff_files/{}"'.format(f) for f in test_files]))
|
|
|
|
with open('diff_test_files_autogen.cmake', 'wb') as fout:
|
|
fout.write(str.encode(cmake))
|
|
|
|
def main():
|
|
|
|
if len(sys.argv) != 2:
|
|
print_usage()
|
|
return 1
|
|
|
|
diff_tool = sys.argv[1]
|
|
if not os.path.exists(diff_tool):
|
|
print("No such file: {}".format(diff_tool))
|
|
print_usage()
|
|
return 1
|
|
|
|
diff_tool = os.path.realpath(diff_tool)
|
|
os.chdir(os.path.dirname(__file__))
|
|
|
|
test_names = sorted([f[:-11] for f in glob.glob("*_src.spvasm")])
|
|
|
|
test_files = generate_tests(diff_tool, test_names)
|
|
|
|
generate_cmake(test_files)
|
|
|
|
return 0
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|