| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304 |
- #! /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())
|