Checker that flags: 1. Direct #include "doctest.h" (should use "test.h") 2. Bare doctest macros (should use FL_ prefixed versions)
| 161 | |
| 162 | |
| 163 | class UnitTestChecker(FileContentChecker): |
| 164 | """Checker that flags: |
| 165 | 1. Direct #include "doctest.h" (should use "test.h") |
| 166 | 2. Bare doctest macros (should use FL_ prefixed versions) |
| 167 | """ |
| 168 | |
| 169 | def __init__(self) -> None: |
| 170 | self.violations: dict[str, list[tuple[int, str]]] = {} |
| 171 | |
| 172 | def should_process_file(self, file_path: str) -> bool: |
| 173 | if not file_path.startswith(str(TESTS_ROOT)): |
| 174 | return False |
| 175 | if not file_path.endswith((".cpp", ".h", ".hpp")): |
| 176 | return False |
| 177 | from pathlib import Path |
| 178 | |
| 179 | if Path(file_path).name in EXEMPT_FILES: |
| 180 | return False |
| 181 | return True |
| 182 | |
| 183 | def check_file_content(self, file_content: FileContent) -> list[str]: |
| 184 | violations: list[tuple[int, str]] = [] |
| 185 | |
| 186 | for line_number, line in enumerate(file_content.lines, 1): |
| 187 | stripped = line.strip() |
| 188 | |
| 189 | # Skip line comments |
| 190 | if stripped.startswith("//"): |
| 191 | continue |
| 192 | |
| 193 | # Check 1: direct doctest.h include |
| 194 | if '#include "doctest.h"' in stripped or "#include <doctest.h>" in stripped: |
| 195 | violations.append( |
| 196 | ( |
| 197 | line_number, |
| 198 | 'Use #include "test.h" instead of #include "doctest.h"', |
| 199 | ) |
| 200 | ) |
| 201 | |
| 202 | # Check 2: bare doctest macros |
| 203 | # Two-phase approach for speed: |
| 204 | # Phase 1: fast substring check with small prefix set |
| 205 | if not any(pfx in line for pfx in _FAST_PREFIXES): |
| 206 | continue |
| 207 | |
| 208 | # Phase 2: single combined regex finds which macro matched |
| 209 | for match in _COMBINED_BANNED_RE.finditer(line): |
| 210 | macro_name = match.group(1) |
| 211 | fl_ver = BANNED_MACROS.get(macro_name) |
| 212 | if fl_ver is not None: |
| 213 | violations.append( |
| 214 | (line_number, f"Use {fl_ver}() instead of bare {macro_name}()") |
| 215 | ) |
| 216 | |
| 217 | if violations: |
| 218 | self.violations[file_content.path] = violations |
| 219 | return [] |
| 220 |
no outgoing calls