summaryrefslogtreecommitdiff
path: root/buildscripts/iwyu/test
diff options
context:
space:
mode:
Diffstat (limited to 'buildscripts/iwyu/test')
-rw-r--r--buildscripts/iwyu/test/basic/a.h1
-rw-r--r--buildscripts/iwyu/test/basic/b.cpp5
-rw-r--r--buildscripts/iwyu/test/basic/b.h1
-rw-r--r--buildscripts/iwyu/test/basic/expected_results.py17
-rw-r--r--buildscripts/iwyu/test/basic/test_config.yml25
-rw-r--r--buildscripts/iwyu/test/no_include/a.h1
-rw-r--r--buildscripts/iwyu/test/no_include/b.cpp5
-rw-r--r--buildscripts/iwyu/test/no_include/b.h1
-rw-r--r--buildscripts/iwyu/test/no_include/expected_results.py18
-rw-r--r--buildscripts/iwyu/test/no_include/test_config.yml27
-rw-r--r--buildscripts/iwyu/test/run_tests.py97
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.")