(patterns: list[str])
| 36 | |
| 37 | |
| 38 | def main(patterns: list[str]): |
| 39 | exit_status = 0 |
| 40 | for file in map(pathlib.Path, iter_files(patterns)): |
| 41 | if file.is_dir(): |
| 42 | continue |
| 43 | |
| 44 | try: |
| 45 | contents = file.read_text(encoding="utf-8") |
| 46 | except UnicodeDecodeError: |
| 47 | continue |
| 48 | |
| 49 | try: |
| 50 | tree = ast.parse(contents) |
| 51 | except SyntaxError: |
| 52 | continue |
| 53 | |
| 54 | cls_name = None |
| 55 | for node in ast.walk(tree): |
| 56 | if isinstance(node, ast.ClassDef): |
| 57 | cls_name = node.name |
| 58 | |
| 59 | if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): |
| 60 | continue |
| 61 | |
| 62 | name = node.name |
| 63 | if not name.startswith("test"): |
| 64 | continue |
| 65 | |
| 66 | if node.decorator_list: |
| 67 | continue |
| 68 | |
| 69 | func_code = ast.unparse(node.body) |
| 70 | if func_code in ( |
| 71 | f"await super().{name}()", |
| 72 | f"return await super().{name}()", |
| 73 | f"return super().{name}()", |
| 74 | f"super().{name}()", |
| 75 | ): |
| 76 | exit_status += 1 |
| 77 | |
| 78 | lineno = node.lineno |
| 79 | msg = f"{file}:{lineno}:{cls_name}.{name} is a test patch that can be safely removed" |
| 80 | if IS_GH_CI: |
| 81 | end_lineno = node.end_lineno |
| 82 | msg = f"::error file={file},line={lineno},endLine={end_lineno},title=Redundant Test Patch::{msg}" |
| 83 | |
| 84 | print(msg, file=sys.stderr) |
| 85 | |
| 86 | return exit_status |
| 87 | |
| 88 | |
| 89 | if __name__ == "__main__": |
no test coverage detected