"""Core interfaces for TrigDroid following SOLID principles.
This module defines the core abstractions that enable dependency inversion
and make the system more maintainable and testable.
"""
from abc import ABC, abstractmethod
from typing import Protocol, TypeVar, Generic, Any, Dict, List, Optional, Union
from enum import Enum
# Type definitions
T = TypeVar('T')
ConfigValue = Union[str, int, bool, List[str], None]
[docs]
class LogLevel(Enum):
"""Logging levels for the application."""
DEBUG = "DEBUG"
INFO = "INFO"
WARNING = "WARNING"
ERROR = "ERROR"
CRITICAL = "CRITICAL"
[docs]
class TestResult(Enum):
"""Result of test execution."""
SUCCESS = "SUCCESS"
FAILURE = "FAILURE"
SKIPPED = "SKIPPED"
[docs]
class DeviceConnectionState(Enum):
"""Android device connection states."""
CONNECTED = "CONNECTED"
DISCONNECTED = "DISCONNECTED"
UNAUTHORIZED = "UNAUTHORIZED"
# Core Interfaces
[docs]
class ILogger(Protocol):
"""Logger interface for dependency inversion."""
[docs]
def debug(self, message: str, *args: Any) -> None: ...
[docs]
def info(self, message: str, *args: Any) -> None: ...
[docs]
def warning(self, message: str, *args: Any) -> None: ...
[docs]
def error(self, message: str, *args: Any) -> None: ...
[docs]
def critical(self, message: str, *args: Any) -> None: ...
[docs]
class IConfigurationProvider(Protocol):
"""Configuration provider interface."""
[docs]
def get_value(self, key: str) -> ConfigValue: ...
[docs]
def set_value(self, key: str, value: ConfigValue) -> None: ...
[docs]
def has_key(self, key: str) -> bool: ...
[docs]
def validate(self) -> bool: ...
[docs]
class IConfigurationValidator(Protocol):
"""Configuration validation interface."""
[docs]
def validate_config(self, config: Dict[str, ConfigValue]) -> List[str]: ...
[docs]
def is_valid(self, key: str, value: ConfigValue) -> bool: ...
[docs]
class IAndroidDevice(Protocol):
"""Android device interface."""
[docs]
def execute_command(self, command: str) -> 'ICommandResult': ...
[docs]
def install_app(self, apk_path: str) -> bool: ...
[docs]
def uninstall_app(self, package_name: str) -> bool: ...
[docs]
def start_app(self, package_name: str) -> bool: ...
[docs]
def stop_app(self, package_name: str) -> bool: ...
[docs]
def is_app_installed(self, package_name: str) -> bool: ...
[docs]
def get_device_info(self) -> Dict[str, str]: ...
[docs]
class ICommandResult(Protocol):
"""Command execution result interface."""
@property
def return_code(self) -> int: ...
@property
def stdout(self) -> bytes: ...
@property
def stderr(self) -> bytes: ...
@property
def success(self) -> bool: ...
[docs]
class ITestRunner(Protocol):
"""Test runner interface for different test types."""
[docs]
def can_run(self, test_type: str) -> bool: ...
[docs]
def execute(self, context: 'ITestContext') -> TestResult: ...
[docs]
def setup(self) -> bool: ...
[docs]
def teardown(self) -> bool: ...
[docs]
class ITestContext(Protocol):
"""Test execution context interface."""
@property
def device(self) -> IAndroidDevice: ...
@property
def config(self) -> IConfigurationProvider: ...
@property
def logger(self) -> ILogger: ...
@property
def package_name(self) -> str: ...
[docs]
class IFridaHookProvider(Protocol):
"""Frida hook provider interface."""
[docs]
def get_hook_script(self) -> str: ...
[docs]
def get_hook_config(self) -> Dict[str, Any]: ...
[docs]
def supports_hook(self, hook_name: str) -> bool: ...
[docs]
class IChangelogWriter(Protocol):
"""Changelog writer interface."""
[docs]
def write_entry(self, property_name: str, old_value: str, new_value: str, description: str = "") -> None: ...
[docs]
def flush(self) -> None: ...
[docs]
class IApplicationOrchestrator(Protocol):
"""Main application orchestrator interface."""
[docs]
def setup(self) -> bool: ...
[docs]
def execute_tests(self) -> bool: ...
[docs]
def teardown(self) -> bool: ...
# Abstract Base Classes
[docs]
class TestRunnerBase(ABC):
"""Base class for test runners implementing common functionality."""
[docs]
def __init__(self, logger: ILogger):
self._logger = logger
self._is_setup = False
[docs]
@abstractmethod
def can_run(self, test_type: str) -> bool:
"""Check if this runner can handle the given test type."""
pass
@abstractmethod
def _execute_internal(self, context: ITestContext) -> TestResult:
"""Internal execution logic to be implemented by subclasses."""
pass
[docs]
def execute(self, context: ITestContext) -> TestResult:
"""Execute the test with proper error handling."""
try:
if not self._is_setup:
if not self.setup():
return TestResult.FAILURE
return self._execute_internal(context)
except Exception as e:
self._logger.error(f"Test execution failed: {e}")
return TestResult.FAILURE
[docs]
def setup(self) -> bool:
"""Setup the test runner."""
self._is_setup = True
return True
[docs]
def teardown(self) -> bool:
"""Cleanup after test execution."""
self._is_setup = False
return True
[docs]
class ConfigurationProviderBase(ABC):
"""Base class for configuration providers."""
[docs]
def __init__(self, logger: ILogger):
self._logger = logger
self._config: Dict[str, ConfigValue] = {}
@abstractmethod
def _load_configuration(self) -> Dict[str, ConfigValue]:
"""Load configuration from the specific source."""
pass
[docs]
def get_value(self, key: str) -> ConfigValue:
"""Get configuration value by key."""
if not self._config:
self._config = self._load_configuration()
return self._config.get(key)
[docs]
def set_value(self, key: str, value: ConfigValue) -> None:
"""Set configuration value."""
self._config[key] = value
[docs]
def has_key(self, key: str) -> bool:
"""Check if configuration key exists."""
if not self._config:
self._config = self._load_configuration()
return key in self._config