summaryrefslogtreecommitdiff
path: root/buildscripts/iwyu/test/run_tests.py
blob: d0e32f00a8dce3194a10f5643faed7a2606a0f06 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
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.")