diff options
Diffstat (limited to 'buildscripts/iwyu/test')
-rw-r--r-- | buildscripts/iwyu/test/basic/a.h | 1 | ||||
-rw-r--r-- | buildscripts/iwyu/test/basic/b.cpp | 5 | ||||
-rw-r--r-- | buildscripts/iwyu/test/basic/b.h | 1 | ||||
-rw-r--r-- | buildscripts/iwyu/test/basic/expected_results.py | 17 | ||||
-rw-r--r-- | buildscripts/iwyu/test/basic/test_config.yml | 25 | ||||
-rw-r--r-- | buildscripts/iwyu/test/no_include/a.h | 1 | ||||
-rw-r--r-- | buildscripts/iwyu/test/no_include/b.cpp | 5 | ||||
-rw-r--r-- | buildscripts/iwyu/test/no_include/b.h | 1 | ||||
-rw-r--r-- | buildscripts/iwyu/test/no_include/expected_results.py | 18 | ||||
-rw-r--r-- | buildscripts/iwyu/test/no_include/test_config.yml | 27 | ||||
-rw-r--r-- | buildscripts/iwyu/test/run_tests.py | 97 |
11 files changed, 198 insertions, 0 deletions
diff --git a/buildscripts/iwyu/test/basic/a.h b/buildscripts/iwyu/test/basic/a.h new file mode 100644 index 00000000000..ad792ace34b --- /dev/null +++ b/buildscripts/iwyu/test/basic/a.h @@ -0,0 +1 @@ +#include "b.h" diff --git a/buildscripts/iwyu/test/basic/b.cpp b/buildscripts/iwyu/test/basic/b.cpp new file mode 100644 index 00000000000..dcbc8627764 --- /dev/null +++ b/buildscripts/iwyu/test/basic/b.cpp @@ -0,0 +1,5 @@ +#include "a.h" + +type_b return_b_function() { + return type_b(); +} diff --git a/buildscripts/iwyu/test/basic/b.h b/buildscripts/iwyu/test/basic/b.h new file mode 100644 index 00000000000..422d7626e90 --- /dev/null +++ b/buildscripts/iwyu/test/basic/b.h @@ -0,0 +1 @@ +class type_b {}; diff --git a/buildscripts/iwyu/test/basic/expected_results.py b/buildscripts/iwyu/test/basic/expected_results.py new file mode 100644 index 00000000000..98ed60ea4fb --- /dev/null +++ b/buildscripts/iwyu/test/basic/expected_results.py @@ -0,0 +1,17 @@ +import os +import sys + +EXPECTED_B_CPP = """ +#include "b.h" + +type_b return_b_function() { + return type_b(); +} +""" + +with open('b.cpp') as f: + content = f.read() + if content != EXPECTED_B_CPP: + print(f'Actual:\n"""{content}"""') + print(f'Expected:\n"""{EXPECTED_B_CPP}"""') + sys.exit(1) diff --git a/buildscripts/iwyu/test/basic/test_config.yml b/buildscripts/iwyu/test/basic/test_config.yml new file mode 100644 index 00000000000..a5b906f5558 --- /dev/null +++ b/buildscripts/iwyu/test/basic/test_config.yml @@ -0,0 +1,25 @@ +# options passed to IWYU +iwyu_options: + - '--max_line_length=100' + - '--no_fwd_decls' + - '--prefix_header_includes=add' + - '--transitive_includes_only' + +# options passed to the fix script +fix_options: + - '--blank_lines' + - '--nocomments' + - '--noreorder' + - '--safe_headers' + +# filename regex to swap no_include in place +# quotes and brackets not included quotes are always assumed +# since this is targeting IWYU added headers +no_includes: + +# prefixes (non regex) to skip +skip_files: + +# regex file paths to add keep pragma +# include quotes are angle brackets +keep_includes: diff --git a/buildscripts/iwyu/test/no_include/a.h b/buildscripts/iwyu/test/no_include/a.h new file mode 100644 index 00000000000..ad792ace34b --- /dev/null +++ b/buildscripts/iwyu/test/no_include/a.h @@ -0,0 +1 @@ +#include "b.h" diff --git a/buildscripts/iwyu/test/no_include/b.cpp b/buildscripts/iwyu/test/no_include/b.cpp new file mode 100644 index 00000000000..dcbc8627764 --- /dev/null +++ b/buildscripts/iwyu/test/no_include/b.cpp @@ -0,0 +1,5 @@ +#include "a.h" + +type_b return_b_function() { + return type_b(); +} diff --git a/buildscripts/iwyu/test/no_include/b.h b/buildscripts/iwyu/test/no_include/b.h new file mode 100644 index 00000000000..422d7626e90 --- /dev/null +++ b/buildscripts/iwyu/test/no_include/b.h @@ -0,0 +1 @@ +class type_b {}; diff --git a/buildscripts/iwyu/test/no_include/expected_results.py b/buildscripts/iwyu/test/no_include/expected_results.py new file mode 100644 index 00000000000..90bda7e15a4 --- /dev/null +++ b/buildscripts/iwyu/test/no_include/expected_results.py @@ -0,0 +1,18 @@ +import os +import sys + +EXPECTED_B_CPP = """// IWYU pragma: no_include "b.h" + +#include "a.h" // IWYU pragma: keep + +type_b return_b_function() { + return type_b(); +} +""" + +with open('b.cpp') as f: + content = f.read() + if content != EXPECTED_B_CPP: + print(f'Actual:\n"""{content}"""') + print(f'Expected:\n"""{EXPECTED_B_CPP}"""') + sys.exit(1) diff --git a/buildscripts/iwyu/test/no_include/test_config.yml b/buildscripts/iwyu/test/no_include/test_config.yml new file mode 100644 index 00000000000..e441f5bac35 --- /dev/null +++ b/buildscripts/iwyu/test/no_include/test_config.yml @@ -0,0 +1,27 @@ +# options passed to IWYU +iwyu_options: + - '--max_line_length=100' + - '--no_fwd_decls' + - '--prefix_header_includes=add' + - '--transitive_includes_only' + +# options passed to the fix script +fix_options: + - '--blank_lines' + - '--nocomments' + - '--noreorder' + - '--safe_headers' + +# filename regex to swap no_include in place +# quotes and brackets not included quotes are always assumed +# since this is targeting IWYU added headers +no_includes: + - 'b.h' + +# prefixes (non regex) to skip +skip_files: + +# regex file paths to add keep pragma +# include quotes are angle brackets +keep_includes: +- '"a.h"' diff --git a/buildscripts/iwyu/test/run_tests.py b/buildscripts/iwyu/test/run_tests.py new file mode 100644 index 00000000000..d0e32f00a8d --- /dev/null +++ b/buildscripts/iwyu/test/run_tests.py @@ -0,0 +1,97 @@ +import pathlib +import yaml +import json +import shutil +import os +import glob +import subprocess +import sys +import argparse +import concurrent.futures + +parser = argparse.ArgumentParser(description='Run tests for the IWYU analysis script.') + +parser.add_argument('--mongo-toolchain-bin-dir', type=str, + help='Which toolchain bin directory to use for this analysis.', + default='/opt/mongodbtoolchain/v4/bin') + +args = parser.parse_args() + +if os.getcwd() != pathlib.Path(__file__).parent: + print( + f"iwyu test script must run in the tests directory, changing dirs to {pathlib.Path(__file__).parent.resolve()}" + ) + os.chdir(pathlib.Path(__file__).parent.resolve()) + +analysis_script = pathlib.Path(__file__).parent.parent / 'run_iwyu_analysis.py' + + +def run_test(entry): + print(f"Running test {pathlib.Path(entry)}...") + test_dir = pathlib.Path(entry) / 'test_run' + if os.path.exists(test_dir): + shutil.rmtree(test_dir) + + shutil.copytree(pathlib.Path(entry), test_dir) + + source_files = glob.glob('**/*.cpp', root_dir=test_dir, recursive=True) + compile_commands = [] + + for source_file in source_files: + output = os.path.splitext(source_file)[0] + '.o' + compile_commands.append({ + 'file': source_file, + 'command': f"{args.mongo_toolchain_bin_dir}/clang++ -o {output} -c {source_file}", + "directory": os.path.abspath(test_dir), + "output": output, + }) + + with open(test_dir / 'compile_commands.json', 'w') as compdb: + json.dump(compile_commands, compdb) + + os.makedirs(test_dir / 'etc', exist_ok=True) + with open(test_dir / 'etc' / 'iwyu_mapping.imp', 'w') as mapping: + mapping.write( + '[{include: ["\\"placeholder.h\\"", "private", "\\"placeholder2.h\\"", "public"]}]') + + iwyu_run = subprocess.run( + [sys.executable, analysis_script, '--verbose', '--config-file=test_config.yml'], text=True, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=test_dir) + + results_run = subprocess.run( + [sys.executable, pathlib.Path(entry) / 'expected_results.py'], stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, text=True, cwd=test_dir) + + msg = '\n'.join([iwyu_run.stdout, results_run.stdout, f"FAILED!: {pathlib.Path(entry)}"]) + msg = '\n'.join([f"[{pathlib.Path(entry).name}] {line}" for line in msg.split('\n')]) + + if results_run.returncode != 0: + return results_run.returncode, msg, pathlib.Path(entry).name + else: + return results_run.returncode, f"[{pathlib.Path(entry).name}] PASSED!: {pathlib.Path(entry)}", pathlib.Path( + entry).name + + +failed_tests = [] +with concurrent.futures.ThreadPoolExecutor( + max_workers=len(os.sched_getaffinity(0)) + 4) as executor: + + # create and run the IWYU jobs + future_cmd = { + executor.submit(run_test, entry): entry + for entry in pathlib.Path(__file__).parent.glob('*') if os.path.isdir(entry) + } + + # process the results + for future in concurrent.futures.as_completed(future_cmd): + result, message, test_name = future.result() + if result != 0: + failed_tests += [test_name] + print(message) + +print("\n***Tests complete.***") +if failed_tests: + print("The following tests failed:") + for test in failed_tests: + print(' - ' + test) + print("Please review the logs above for more information.") |