|
| 1 | +# SPDX-License-Identifier: GPL-2.0 |
| 2 | + |
| 3 | +""" Test Makefile, .gitignore and config format """ |
| 4 | + |
| 5 | +import os |
| 6 | +import subprocess |
| 7 | +from typing import Tuple |
| 8 | + |
| 9 | + |
| 10 | +LOCAL_DIR = os.path.dirname(__file__) |
| 11 | + |
| 12 | + |
| 13 | +def ret_merge(ret, nret): |
| 14 | + """ merge results """ |
| 15 | + if ret[0] == 0 or nret[0] == 0: |
| 16 | + val = 0 |
| 17 | + else: |
| 18 | + val = min(ret[0], nret[0]) |
| 19 | + |
| 20 | + desc = "" |
| 21 | + if ret[1] and nret[1]: |
| 22 | + desc = ret[1] + "; " + nret[1] |
| 23 | + else: |
| 24 | + desc = ret[1] + nret[1] |
| 25 | + return (val, desc) |
| 26 | + |
| 27 | + |
| 28 | +def check_new_files_makefile(tree, new_files, log): |
| 29 | + """ Make sure new files are listed in a Makefile, somewhere """ |
| 30 | + |
| 31 | + ret = (0, "") |
| 32 | + cnt = 0 |
| 33 | + |
| 34 | + for path in new_files: |
| 35 | + if path.endswith(('.sh', '.py')): |
| 36 | + needle = path |
| 37 | + elif path.endswith(('.c')): |
| 38 | + needle = path.split('.')[0] |
| 39 | + else: |
| 40 | + log.append("makefile inclusion check ignoring " + path) |
| 41 | + continue |
| 42 | + |
| 43 | + makefile = os.path.dirname(path) + "/Makefile" |
| 44 | + |
| 45 | + cmd = ["git", "grep", "--exit-code", needle, "---", makefile] |
| 46 | + result = subprocess.run(cmd, cwd=tree.path, check=False) |
| 47 | + log.append(" ".join(cmd) + f":: {result.returncode}") |
| 48 | + if result.returncode: |
| 49 | + ret_merge(ret, (1, path + " not found in Makefile")) |
| 50 | + cnt += 1 |
| 51 | + |
| 52 | + if not ret[0] and cnt: |
| 53 | + ret = (0, f"New files in Makefile checked ({cnt})") |
| 54 | + |
| 55 | + return ret |
| 56 | + |
| 57 | + |
| 58 | +def check_new_files_gitignore(tree, new_files, log): |
| 59 | + """ Make sure new binaries are listed in .gitignore """ |
| 60 | + |
| 61 | + ret = (0, "") |
| 62 | + cnt = 0 |
| 63 | + |
| 64 | + for path in new_files: |
| 65 | + if path.endswith(('.c')): |
| 66 | + needle = path.split('.')[0] |
| 67 | + else: |
| 68 | + log.append("gitignore check ignoring " + path) |
| 69 | + continue |
| 70 | + |
| 71 | + target = os.path.dirname(path) + "/.gitignore" |
| 72 | + |
| 73 | + cmd = ["git", "grep", "--exit-code", needle, "---", target] |
| 74 | + result = subprocess.run(cmd, cwd=tree.path, check=False) |
| 75 | + log.append(" ".join(cmd) + f":: {result.returncode}") |
| 76 | + if result.returncode: |
| 77 | + ret_merge(ret, (1, needle + " not found in .gitignore")) |
| 78 | + cnt += 1 |
| 79 | + |
| 80 | + if not ret[0] and cnt: |
| 81 | + ret = (0, f"New files in gitignore checked ({cnt})") |
| 82 | + |
| 83 | + return ret |
| 84 | + |
| 85 | + |
| 86 | +def _check_file_fmt(tree, path, script, result_dir, ident): |
| 87 | + cmd = [script, os.path.join(tree.path, path)] |
| 88 | + |
| 89 | + result = subprocess.run(cmd, cwd=LOCAL_DIR, capture_output=True, |
| 90 | + text=True, check=False) |
| 91 | + with open(os.path.join(result_dir, ident), "w", encoding="utf-8") as fp: |
| 92 | + fp.write(result.stdout) |
| 93 | + return result.returncode |
| 94 | + |
| 95 | + |
| 96 | +def check_file_formats(tree, file_list, log, result_dir): |
| 97 | + """ Validate sort order of all touched files """ |
| 98 | + |
| 99 | + ret = (0, "") |
| 100 | + i = 0 |
| 101 | + for path in file_list: |
| 102 | + if path.endswith("/config"): |
| 103 | + script = "validate_config_format.py" |
| 104 | + fmt = f"fmt-config-{i}" |
| 105 | + elif path.endswith("/.gitignore"): |
| 106 | + script = "validate_config_format.py" |
| 107 | + fmt = f"fmt-gitignore-{i}" |
| 108 | + elif path.endswith("/Makefile"): |
| 109 | + script = "validate_Makefile_format.py" |
| 110 | + fmt = f"fmt-makefile-{i}" |
| 111 | + else: |
| 112 | + log.append("format check ignoring " + path) |
| 113 | + continue |
| 114 | + |
| 115 | + if _check_file_fmt(tree, path, script, result_dir, fmt): |
| 116 | + ret = ret_merge(ret, (1, "Bad format: " + path)) |
| 117 | + |
| 118 | + if not ret[0] and i: |
| 119 | + ret = (0, f"Good format ({i})") |
| 120 | + |
| 121 | + return ret |
| 122 | + |
| 123 | + |
| 124 | +def extract_files(patch): |
| 125 | + """Extract paths of new files being added by the series.""" |
| 126 | + |
| 127 | + new_files = set() |
| 128 | + mod_files = set() |
| 129 | + lines = patch.raw_patch.split("\n") |
| 130 | + |
| 131 | + # Walk lines, skip last since it doesn't have next |
| 132 | + for i, line in enumerate(lines[:-1]): |
| 133 | + next_line = lines[i + 1] |
| 134 | + |
| 135 | + if not next_line.startswith("+++ b/"): |
| 136 | + continue |
| 137 | + if 'tools/testing/selftests/' not in next_line: |
| 138 | + continue |
| 139 | + |
| 140 | + file_path = next_line[6:] |
| 141 | + |
| 142 | + if line == "--- /dev/null": |
| 143 | + new_files.add(file_path) |
| 144 | + else: |
| 145 | + mod_files.add(file_path) |
| 146 | + |
| 147 | + # We're testing a series, same file may appear multiple times |
| 148 | + mod_files -= new_files |
| 149 | + return list(new_files), list(mod_files) |
| 150 | + |
| 151 | + |
| 152 | +def check_selftest(tree, patch, result_dir) -> Tuple[int, str, str]: |
| 153 | + """ Main function / entry point """ |
| 154 | + |
| 155 | + # Check for new files in the series |
| 156 | + new_files, mod_files = extract_files(patch) |
| 157 | + |
| 158 | + ret = (0, "") |
| 159 | + log = ["New files:"] + new_files + ["", "Modified files:"] + mod_files + [""] |
| 160 | + |
| 161 | + if not new_files and not mod_files: |
| 162 | + ret = (0, "No changes to selftests") |
| 163 | + else: |
| 164 | + nret = check_file_formats(tree, new_files + mod_files, log, result_dir) |
| 165 | + ret = ret_merge(ret, nret) |
| 166 | + |
| 167 | + if new_files: |
| 168 | + nret = check_new_files_makefile(tree, new_files, log) |
| 169 | + ret = ret_merge(ret, nret) |
| 170 | + |
| 171 | + nret = check_new_files_gitignore(tree, new_files, log) |
| 172 | + ret = ret_merge(ret, nret) |
| 173 | + |
| 174 | + if not ret[0] and not ret[1]: |
| 175 | + ret = (0, f"New files {len(new_files)}, modified {len(mod_files)}, no checks") |
| 176 | + |
| 177 | + return ret[0], ret[1], "\n".join(log) |
0 commit comments