Files
klubhaus-doorbell/libraries/FastLED/ci/util/test_exceptions.py
2026-02-12 00:45:31 -08:00

110 lines
3.3 KiB
Python

#!/usr/bin/env python3
"""Custom exceptions for test failures that need to bubble up to callers."""
import subprocess
from dataclasses import dataclass
from typing import List, Optional
@dataclass
class TestFailureInfo:
"""Information about a single test failure"""
test_name: str
command: str
return_code: int
output: str
error_type: str = "test_failure"
class FastLEDTestException(Exception):
"""Base exception for FastLED test failures"""
def __init__(self, message: str, failures: Optional[List[TestFailureInfo]] = None):
super().__init__(message)
self.failures = failures or []
self.message = message
def add_failure(self, failure: TestFailureInfo) -> None:
"""Add a test failure to this exception"""
self.failures.append(failure)
def has_failures(self) -> bool:
"""Check if this exception contains any failures"""
return len(self.failures) > 0
def get_failure_summary(self) -> str:
"""Get a summary of all failures"""
if not self.failures:
return self.message
summary = [self.message]
summary.append(f"\nFailed tests ({len(self.failures)}):")
for failure in self.failures:
summary.append(
f" - {failure.test_name}: {failure.error_type} (exit code {failure.return_code})"
)
return "\n".join(summary)
def get_detailed_failure_info(self) -> str:
"""Get detailed information about all failures"""
if not self.failures:
return self.message
details = [self.message]
details.append(f"\n{'=' * 50}")
details.append("DETAILED FAILURE INFORMATION")
details.append(f"{'=' * 50}")
for i, failure in enumerate(self.failures, 1):
cmd_str: str = (
subprocess.list2cmdline(failure.command)
if isinstance(failure.command, list)
else failure.command
)
assert isinstance(cmd_str, str)
details.append(f"\n{i}. {failure.test_name}")
details.append(f" Command: {cmd_str}")
details.append(f" Error Type: {failure.error_type}")
details.append(f" Exit Code: {failure.return_code}")
details.append(f" Output:")
# Indent the output
for line in failure.output.split("\n"):
details.append(f" {line}")
return "\n".join(details)
class CompilationFailedException(FastLEDTestException):
"""Exception for compilation failures"""
def __init__(
self,
message: str = "Compilation failed",
failures: Optional[List[TestFailureInfo]] = None,
):
super().__init__(message, failures)
class TestExecutionFailedException(FastLEDTestException):
"""Exception for test execution failures"""
def __init__(
self,
message: str = "Test execution failed",
failures: Optional[List[TestFailureInfo]] = None,
):
super().__init__(message, failures)
class TestTimeoutException(FastLEDTestException):
"""Exception for test timeouts"""
def __init__(
self,
message: str = "Test execution timed out",
failures: Optional[List[TestFailureInfo]] = None,
):
super().__init__(message, failures)