sTodo-m5paper-client/libraries/FastLED/ci/tests/test_missing_pragma_once.py
2025-06-30 20:47:33 +02:00

119 lines
4.3 KiB
Python

import os
import unittest
from concurrent.futures import ThreadPoolExecutor
from ci.paths import PROJECT_ROOT
SRC_ROOT = PROJECT_ROOT / "src"
NUM_WORKERS = (os.cpu_count() or 1) * 4
# Files that are allowed to not have #pragma once
EXCLUDED_FILES = [
# Add any exceptions here
]
EXCLUDED_DIRS = [
"third_party",
"platforms",
]
class TestMissingPragmaOnce(unittest.TestCase):
def check_file(self, file_path: str) -> list[str]:
"""Check if a header file has #pragma once directive or if a cpp file incorrectly has it."""
failings: list[str] = []
with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
content = f.read()
if file_path.endswith(".h"):
# For header files, check if #pragma once is missing
if "#pragma once" not in content:
failings.append(f"Missing #pragma once in {file_path}")
elif file_path.endswith(".cpp"):
# For cpp files, check if #pragma once is incorrectly present
if "#pragma once" in content:
failings.append(f"Incorrect #pragma once in cpp file: {file_path}")
return failings
def test_pragma_once_usage(self) -> None:
"""
Searches through files to:
1. Check for missing #pragma once in header files
2. Check for incorrect #pragma once in cpp files
"""
files_to_check = []
current_dir = None
# Collect files to check
for root, dirs, files in os.walk(SRC_ROOT):
# Log when we enter a new directory
rel_path = os.path.relpath(root, SRC_ROOT)
if current_dir != rel_path:
current_dir = rel_path
print(f"Traversing directory: {rel_path}")
if rel_path in EXCLUDED_DIRS:
print(f" Skipping excluded directory: {rel_path}")
dirs[:] = [] # Skip this directory and its subdirectories
continue
# Check if this directory should be excluded
# if any(os.path.normpath(root).startswith(os.path.normpath(excluded_dir))
# for excluded_dir in EXCLUDED_DIRS):
# print(f" Skipping excluded directory: {rel_path}")
# continue
for excluded_dir in EXCLUDED_DIRS:
npath = os.path.normpath(root)
npath_excluded = os.path.normpath(excluded_dir)
print(f"Checking {npath} against excluded {npath_excluded}")
if npath.startswith(npath_excluded):
print(f" Skipping excluded directory: {rel_path}")
break
for file in files:
if file.endswith((".h", ".cpp")): # Check both header and cpp files
file_path = os.path.join(root, file)
# Check if file is excluded
# if any(file_path.endswith(excluded) for excluded in EXCLUDED_FILES):
# print(f" Skipping excluded file: {file}")
# continue
for excluded in EXCLUDED_FILES:
# print(f"Checking {file_path} against excluded {excluded}")
if file_path.endswith(excluded):
print(f" Skipping excluded file: {file}")
break
files_to_check.append(file_path)
print(f"Found {len(files_to_check)} files to check")
# Process files in parallel
all_failings = []
with ThreadPoolExecutor(max_workers=NUM_WORKERS) as executor:
futures = [
executor.submit(self.check_file, file_path)
for file_path in files_to_check
]
for future in futures:
all_failings.extend(future.result())
# Report results
if all_failings:
msg = f"Found {len(all_failings)} pragma once issues: \n" + "\n".join(
all_failings
)
for failing in all_failings:
print(failing)
self.fail(msg)
else:
print("All files have proper pragma once usage.")
print(f"Pragma once check completed. Processed {len(files_to_check)} files.")
if __name__ == "__main__":
unittest.main()