Check a single file for aggregation violations. If the file is: - An aggregator .cpp: verify it includes all .hpp children - An .hpp in an excluded dir: verify some aggregator includes it
(file_path: Path)
| 177 | |
| 178 | |
| 179 | def check_single_file(file_path: Path) -> tuple[bool, list[str]]: |
| 180 | """Check a single file for aggregation violations. |
| 181 | |
| 182 | If the file is: |
| 183 | - An aggregator .cpp: verify it includes all .hpp children |
| 184 | - An .hpp in an excluded dir: verify some aggregator includes it |
| 185 | """ |
| 186 | violations: list[str] = [] |
| 187 | excluded_dirs = _get_aggregated_test_dirs() |
| 188 | resolved = file_path.resolve() |
| 189 | |
| 190 | # Case 1: File is an aggregator .cpp for an excluded dir |
| 191 | for excluded_dir in excluded_dirs: |
| 192 | aggregator = _find_aggregator(excluded_dir) |
| 193 | if aggregator and aggregator.resolve() == resolved: |
| 194 | _, included_files = _collect_included_files(excluded_dir) |
| 195 | for hpp_file in sorted(excluded_dir.rglob("*.hpp")): |
| 196 | if hpp_file.resolve() not in included_files: |
| 197 | rel_agg = aggregator.relative_to(PROJECT_ROOT) |
| 198 | rel_file = hpp_file.relative_to(PROJECT_ROOT) |
| 199 | violations.append( |
| 200 | f"{rel_agg.as_posix()}: orphaned {rel_file.as_posix()}" |
| 201 | ) |
| 202 | for inc_path in sorted(_parse_includes(aggregator)): |
| 203 | inc_resolved = (aggregator.parent / inc_path).resolve() |
| 204 | if not inc_resolved.exists(): |
| 205 | inc_resolved = (PROJECT_ROOT / inc_path).resolve() |
| 206 | if inc_resolved.is_relative_to(excluded_dir) and inc_path.endswith( |
| 207 | ".cpp" |
| 208 | ): |
| 209 | rel_agg = aggregator.relative_to(PROJECT_ROOT) |
| 210 | violations.append( |
| 211 | f'{rel_agg.as_posix()}: #include "{inc_path}" should use .hpp' |
| 212 | ) |
| 213 | return len(violations) == 0, violations |
| 214 | |
| 215 | # Case 2: File is an .hpp inside an excluded directory |
| 216 | for excluded_dir in excluded_dirs: |
| 217 | if not resolved.is_relative_to(excluded_dir): |
| 218 | continue |
| 219 | aggregator, included_files = _collect_included_files(excluded_dir) |
| 220 | if not aggregator: |
| 221 | rel = excluded_dir.relative_to(PROJECT_ROOT) |
| 222 | violations.append(f"Missing aggregator: {rel.as_posix()}.cpp") |
| 223 | return False, violations |
| 224 | if resolved not in included_files: |
| 225 | rel_agg = aggregator.relative_to(PROJECT_ROOT) |
| 226 | rel_file = file_path.relative_to(PROJECT_ROOT) |
| 227 | violations.append(f"{rel_agg.as_posix()}: orphaned {rel_file.as_posix()}") |
| 228 | return len(violations) == 0, violations |
| 229 | |
| 230 | # Not relevant to aggregation |
| 231 | return True, [] |
| 232 | |
| 233 | |
| 234 | class TestAggregationChecker(FileContentChecker): |
no test coverage detected