diff --git a/doc/source/configuration_file.inc b/doc/source/configuration_file.inc index d91b0fd3..571eeaea 100644 --- a/doc/source/configuration_file.inc +++ b/doc/source/configuration_file.inc @@ -22,6 +22,7 @@ A simple configuration file could look like: [check.RemoteUsers] class = Users enabled = true + error_behavior = active name = .* terminal = .* host = [0-9].* @@ -111,6 +112,18 @@ For each check, these generic options can be specified: Needs to be ``true`` for a check to actually execute. ``false`` is assumed if not specified. +.. option:: error_behavior + + Controls how temporary check errors are handled. + A temporary error is one that may resolve on its own, such as a network timeout or a service being briefly unavailable. + + ``active`` + The error is treated as activity, preventing the system from suspending until the check succeeds again. + This is the default, and preserves safe behavior when a check cannot determine system activity. + + ``ignore`` + The error is logged and the check is treated as inactive, i.e. it does not prevent suspension. + Furthermore, each check might have custom options. Wake up check configuration diff --git a/setup.cfg b/setup.cfg index 1aa78718..96d6b07f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -9,6 +9,7 @@ check_untyped_defs = True no_implicit_optional = True warn_unused_configs = True warn_unused_ignores = True +enable_error_code = exhaustive-match [tool:pytest] pythonpath = doc diff --git a/src/autosuspend/__init__.py b/src/autosuspend/__init__.py index f04ab8a8..bb24898c 100755 --- a/src/autosuspend/__init__.py +++ b/src/autosuspend/__init__.py @@ -19,7 +19,15 @@ from dbus.mainloop.glib import DBusGMainLoop from gi.repository import GLib -from .checks import Activity, CheckType, ConfigurationError, TemporaryCheckError, Wakeup +from .checks import ( + Activity, + CheckType, + ConfigurationError, + ConfiguredCheck, + ErrorBehavior, + TemporaryCheckError, + Wakeup, +) from .config import GENERAL_PARAMETERS, ConfigSchema from .util import logger_by_class_instance from .util.systemd import LogindDBusException, has_inhibit_lock @@ -117,16 +125,31 @@ def schedule_wakeup(command_template: str, wakeup_at: datetime) -> None: ) -def _safe_execute_activity(check: Activity, logger: logging.Logger) -> str | None: +def _safe_execute_activity( + check: ConfiguredCheck[Activity], logger: logging.Logger +) -> str | None: try: - return check.check() + return check.check.check() except TemporaryCheckError: - logger.warning("Check %s failed. Ignoring...", check, exc_info=True) - return f"Check {check.name} failed temporarily" + match check.error_behavior: + case ErrorBehavior.ACTIVE: + logger.warning( + "Check %s failed. Assuming activity as requested.", + check.name, + exc_info=True, + ) + return f"Check {check.name} failed temporarily." + case ErrorBehavior.IGNORE: + logger.warning( + "Check %s failed. Ignoring as requested.", check.name, exc_info=True + ) + return None def execute_checks( - checks: Iterable[Activity], all_checks: bool, logger: logging.Logger + checks: Iterable[ConfiguredCheck[Activity]], + all_checks: bool, + logger: logging.Logger, ) -> bool: """Execute the provided checks sequentially. @@ -156,21 +179,25 @@ def execute_checks( def _safe_execute_wakeup( - check: Wakeup, timestamp: datetime, logger: logging.Logger + check: ConfiguredCheck[Wakeup], + timestamp: datetime, + logger: logging.Logger, ) -> datetime | None: try: - return check.check(timestamp) + return check.check.check(timestamp) except TemporaryCheckError: - logger.warning("Wakeup %s failed. Ignoring...", check, exc_info=True) + logger.warning("Wakeup %s failed. Ignoring...", check.name, exc_info=True) return None def execute_wakeups( - wakeups: Iterable[Wakeup], timestamp: datetime, logger: logging.Logger + wakeups: Iterable[ConfiguredCheck[Wakeup]], + timestamp: datetime, + logger: logging.Logger, ) -> datetime | None: wakeup_at = None - for wakeup in wakeups: - this_at = _safe_execute_wakeup(wakeup, timestamp, logger) + for check in wakeups: + this_at = _safe_execute_wakeup(check, timestamp, logger) # sanity checks if this_at is None: @@ -180,7 +207,7 @@ def execute_wakeups( "Wakeup %s returned a scheduled wakeup at %s, " "which is earlier than the current time %s. " "Ignoring.", - wakeup, + check.name, this_at, timestamp, ) @@ -220,8 +247,8 @@ class Processor: def __init__( self, - activities: Iterable[Activity], - wakeups: Iterable[Wakeup], + activities: Iterable[ConfiguredCheck[Activity]], + wakeups: Iterable[ConfiguredCheck[Wakeup]], idle_time: float, min_sleep_time: float, wakeup_delta: float, @@ -456,11 +483,21 @@ def _set_up_single_check( prefix: str, internal_module: str, target_class: type[CheckType], -) -> CheckType: +) -> ConfiguredCheck[CheckType]: name = section.name[len(f"{prefix}.") :] class_name = _determine_check_class_name(name, section) + # parse error behavior + error_behavior_raw = section.get("error_behavior", ErrorBehavior.ACTIVE.value) + try: + error_behavior = ErrorBehavior(error_behavior_raw) + except ValueError as error: + raise ConfigurationError( + f"Invalid error_behavior value '{error_behavior_raw}' for check {name}. " + f"Valid values are: {[e.value for e in ErrorBehavior]}" + ) from error + # try to find the required class import_module, import_class = _determine_check_class_and_module( class_name, internal_module @@ -481,14 +518,14 @@ def _set_up_single_check( f"Cannot create built-in check named {class_name}: Class does not exist" ) from error - check = klass.create(name, section) + check = klass.create(section) if not isinstance(check, target_class): raise ConfigurationError( "Check %s is not a correct %s instance", check, target_class.__name__ ) _logger.debug("Created check instance %s with options %s", check, check.options()) - return check + return ConfiguredCheck(name=name, check=check, error_behavior=error_behavior) def discover_available_checks( @@ -529,7 +566,7 @@ def set_up_checks( internal_module: str, target_class: type[CheckType], error_none: bool = False, -) -> list[CheckType]: +) -> list[ConfiguredCheck[CheckType]]: """Set up :py.class:`Check` instances from a given configuration. Args: @@ -739,8 +776,8 @@ def get_wakeup_delta(config: configparser.ConfigParser) -> float: def configure_processor( args: argparse.Namespace, config: configparser.ConfigParser, - checks: Iterable[Activity], - wakeups: Iterable[Wakeup], + checks: Iterable[ConfiguredCheck[Activity]], + wakeups: Iterable[ConfiguredCheck[Wakeup]], ) -> Processor: return Processor( checks, diff --git a/src/autosuspend/checks/__init__.py b/src/autosuspend/checks/__init__.py index deab3110..7fc7e2eb 100644 --- a/src/autosuspend/checks/__init__.py +++ b/src/autosuspend/checks/__init__.py @@ -2,9 +2,11 @@ import abc import configparser +import enum from collections.abc import Mapping +from dataclasses import dataclass from datetime import datetime -from typing import Any, Self, TypeVar +from typing import Any, Generic, Self, TypeVar from autosuspend.config import ParameterSchemaAware from autosuspend.util import logger_by_class_instance @@ -32,24 +34,36 @@ class SevereCheckError(RuntimeError): CheckType = TypeVar("CheckType", bound="Check") +class ErrorBehavior(enum.Enum): + """Describes how errors from a check are handled.""" + + ACTIVE = "active" + """Treat the check as active (preventing suspension) on error.""" + IGNORE = "ignore" + """Ignore errors from this check.""" + + +@dataclass +class ConfiguredCheck(Generic[CheckType]): + """Wraps a check with its configured name and error behavior.""" + + name: str + check: CheckType + error_behavior: ErrorBehavior = ErrorBehavior.ACTIVE + + class Check(abc.ABC, ParameterSchemaAware): """Base class for all kinds of checks. Subclasses must call this class' ``__init__`` method. - - Args: - name (str): - Configured name of the check """ @classmethod @abc.abstractmethod - def create(cls: type[Self], name: str, config: configparser.SectionProxy) -> Self: + def create(cls: type[Self], config: configparser.SectionProxy) -> Self: """Create a new check instance from the provided configuration. Args: - name: - user-defined name for the check config: config parser section with the configuration for this check @@ -59,12 +73,8 @@ def create(cls: type[Self], name: str, config: configparser.SectionProxy) -> Sel """ - def __init__(self, name: str | None = None) -> None: - if name: - self.name = name - else: - self.name = self.__class__.__name__ - self.logger = logger_by_class_instance(self, name) + def __init__(self) -> None: + self.logger = logger_by_class_instance(self) def options(self) -> Mapping[str, Any]: """Return the configured options as a mapping. @@ -75,9 +85,6 @@ def options(self) -> Mapping[str, Any]: k: v for k, v in self.__dict__.items() if not callable(v) and k != "logger" } - def __str__(self) -> str: - return f"{self.name}[class={self.__class__.__name__}]" - class Activity(Check): """Base class for activity checks. @@ -99,9 +106,6 @@ def check(self) -> str | None: Check executions fails severely """ - def __str__(self) -> str: - return f"{self.name}[class={self.__class__.__name__}]" - class Wakeup(Check): """Represents a check for potential wake up points.""" diff --git a/src/autosuspend/checks/command.py b/src/autosuspend/checks/command.py index b5963e2b..12e19fd9 100644 --- a/src/autosuspend/checks/command.py +++ b/src/autosuspend/checks/command.py @@ -30,9 +30,9 @@ class CommandMixin(Check): """Mixin for configuring checks based on external commands.""" @classmethod - def create(cls: type[Self], name: str, config: configparser.SectionProxy) -> Self: + def create(cls: type[Self], config: configparser.SectionProxy) -> Self: try: - return cls(name, config["command"].strip()) # type: ignore + return cls(config["command"].strip()) except KeyError as error: raise ConfigurationError("Missing command specification") from error @@ -54,9 +54,9 @@ class CommandActivity(CommandMixin, Activity): * :ref:`external-command-activity-scripts` for a collection of user-provided scripts for some common use cases. """ - def __init__(self, name: str, command: str) -> None: + def __init__(self, command: str) -> None: CommandMixin.__init__(self, command) - Activity.__init__(self, name) + Activity.__init__(self) def check(self) -> str | None: try: @@ -80,9 +80,9 @@ class CommandWakeup(CommandMixin, Wakeup): Beware of malicious commands in obtained configuration files. """ - def __init__(self, name: str, command: str) -> None: + def __init__(self, command: str) -> None: CommandMixin.__init__(self, command) - Wakeup.__init__(self, name) + Wakeup.__init__(self) def check(self, timestamp: datetime) -> datetime | None: # noqa: ARG002 try: diff --git a/src/autosuspend/checks/ical.py b/src/autosuspend/checks/ical.py index 376316fa..dacc6c85 100644 --- a/src/autosuspend/checks/ical.py +++ b/src/autosuspend/checks/ical.py @@ -315,16 +315,14 @@ class ActiveCalendarEvent(NetworkMixin, Activity): """ @classmethod - def create( - cls, name: str, config: configparser.SectionProxy - ) -> "ActiveCalendarEvent": + def create(cls, config: configparser.SectionProxy) -> "ActiveCalendarEvent": kwargs = NetworkMixin.collect_init_args(config) kwargs["match"] = config.get("match", fallback=".*") - return cls(name, **kwargs) + return cls(**kwargs) - def __init__(self, name: str, match: str = ".*", **kwargs: Any) -> None: + def __init__(self, match: str = ".*", **kwargs: Any) -> None: NetworkMixin.__init__(self, **kwargs) - Activity.__init__(self, name) + Activity.__init__(self) self._match = match def check(self) -> str | None: @@ -370,14 +368,14 @@ class Calendar(NetworkMixin, Wakeup): """ @classmethod - def create(cls, name: str, config: configparser.SectionProxy) -> "Calendar": + def create(cls, config: configparser.SectionProxy) -> "Calendar": kwargs = NetworkMixin.collect_init_args(config) kwargs["match"] = config.get("match", fallback=".*") - return cls(name, **kwargs) + return cls(**kwargs) - def __init__(self, name: str, match: str = ".*", **kwargs: Any) -> None: + def __init__(self, match: str = ".*", **kwargs: Any) -> None: NetworkMixin.__init__(self, **kwargs) - Wakeup.__init__(self, name) + Wakeup.__init__(self) self._match = match def check(self, timestamp: datetime) -> datetime | None: diff --git a/src/autosuspend/checks/json.py b/src/autosuspend/checks/json.py index 18817436..ba29212a 100644 --- a/src/autosuspend/checks/json.py +++ b/src/autosuspend/checks/json.py @@ -43,8 +43,8 @@ def collect_init_args(cls, config: configparser.SectionProxy) -> dict[str, Any]: except Exception as error: raise ConfigurationError(f"JSONPath error {error}") from error - def __init__(self, name: str, jsonpath: JSONPath, **kwargs: Any) -> None: - Activity.__init__(self, name) + def __init__(self, jsonpath: JSONPath, **kwargs: Any) -> None: + Activity.__init__(self) NetworkMixin.__init__(self, accept="application/json", **kwargs) self._jsonpath = jsonpath diff --git a/src/autosuspend/checks/kodi.py b/src/autosuspend/checks/kodi.py index 6c06dd95..9016ea57 100644 --- a/src/autosuspend/checks/kodi.py +++ b/src/autosuspend/checks/kodi.py @@ -47,11 +47,11 @@ def collect_init_args(cls, config: configparser.SectionProxy) -> dict[str, Any]: raise ConfigurationError(f"Configuration error {error}") from error @classmethod - def create(cls, name: str, config: configparser.SectionProxy) -> Self: - return cls(name, **cls.collect_init_args(config)) + def create(cls, config: configparser.SectionProxy) -> Self: + return cls(**cls.collect_init_args(config)) def __init__( - self, name: str, url: str, suspend_while_paused: bool = False, **kwargs: Any + self, url: str, suspend_while_paused: bool = False, **kwargs: Any ) -> None: self._suspend_while_paused = suspend_while_paused if self._suspend_while_paused: @@ -66,7 +66,7 @@ def __init__( '"method": "Player.GetActivePlayers"}' ) NetworkMixin.__init__(self, url=request, **kwargs) - Activity.__init__(self, name) + Activity.__init__(self) def _safe_request_result(self) -> dict: try: @@ -120,17 +120,17 @@ def collect_init_args(cls, config: configparser.SectionProxy) -> dict[str, Any]: raise ConfigurationError("Configuration error " + str(error)) from error @classmethod - def create(cls, name: str, config: configparser.SectionProxy) -> Self: - return cls(name, **cls.collect_init_args(config)) + def create(cls, config: configparser.SectionProxy) -> Self: + return cls(**cls.collect_init_args(config)) - def __init__(self, name: str, url: str, idle_time: int, **kwargs: Any) -> None: + def __init__(self, url: str, idle_time: int, **kwargs: Any) -> None: request = url + ( '?request={"jsonrpc": "2.0", "id": 1, ' '"method": "XBMC.GetInfoBooleans",' f'"params": {{"booleans": ["System.IdleTime({idle_time})"]}}}}' ) NetworkMixin.__init__(self, url=request, **kwargs) - Activity.__init__(self, name) + Activity.__init__(self) self._idle_time = idle_time def check(self) -> str | None: diff --git a/src/autosuspend/checks/linux.py b/src/autosuspend/checks/linux.py index e27ad634..bd17d9cc 100644 --- a/src/autosuspend/checks/linux.py +++ b/src/autosuspend/checks/linux.py @@ -47,20 +47,19 @@ class ActiveConnection(Activity): @classmethod def create( cls: type[Self], - name: str, config: configparser.SectionProxy, ) -> Self: try: split_ports = config["ports"].split(",") ports = {int(p.strip()) for p in split_ports} - return cls(name, ports) + return cls(ports) except KeyError as error: raise ConfigurationError("Missing option ports") from error except ValueError as error: raise ConfigurationError("Ports must be integers") from error - def __init__(self, name: str, ports: Iterable[int]) -> None: - Activity.__init__(self, name) + def __init__(self, ports: Iterable[int]) -> None: + Activity.__init__(self) self._ports = ports def normalize_address( @@ -113,16 +112,16 @@ class Load(Activity): """ @classmethod - def create(cls: type[Self], name: str, config: configparser.SectionProxy) -> Self: + def create(cls: type[Self], config: configparser.SectionProxy) -> Self: try: - return cls(name, config.getfloat("threshold", fallback=2.5)) + return cls(config.getfloat("threshold", fallback=2.5)) except ValueError as error: raise ConfigurationError( f"Unable to parse threshold as float: {error}" ) from error - def __init__(self, name: str, threshold: float) -> None: - Activity.__init__(self, name) + def __init__(self, threshold: float) -> None: + Activity.__init__(self) self._threshold = threshold def check(self) -> str | None: @@ -187,14 +186,13 @@ def _extract_interfaces(cls, config: configparser.SectionProxy) -> list[str]: @classmethod def create( cls: type[Self], - name: str, config: configparser.SectionProxy, ) -> Self: try: interfaces = cls._extract_interfaces(config) threshold_send = config.getfloat("threshold_send", fallback=100) threshold_receive = config.getfloat("threshold_receive", fallback=100) - return cls(name, interfaces, threshold_send, threshold_receive) + return cls(interfaces, threshold_send, threshold_receive) except KeyError as error: raise ConfigurationError(f"Missing configuration key: {error}") from error except ValueError as error: @@ -202,12 +200,11 @@ def create( def __init__( self, - name: str, interfaces: Iterable[str], threshold_send: float, threshold_receive: float, ) -> None: - Activity.__init__(self, name) + Activity.__init__(self) self._interfaces = interfaces self._threshold_send = threshold_send self._threshold_receive = threshold_receive @@ -290,18 +287,18 @@ class Ping(Activity): """ @classmethod - def create(cls: type[Self], name: str, config: configparser.SectionProxy) -> Self: + def create(cls: type[Self], config: configparser.SectionProxy) -> Self: try: hosts = config["hosts"].split(",") hosts = [h.strip() for h in hosts] - return cls(name, hosts) + return cls(hosts) except KeyError as error: raise ConfigurationError( f"Unable to determine hosts to ping: {error}" ) from error - def __init__(self, name: str, hosts: Iterable[str]) -> None: - Activity.__init__(self, name) + def __init__(self, hosts: Iterable[str]) -> None: + Activity.__init__(self) self._hosts = hosts def check(self) -> str | None: @@ -337,16 +334,16 @@ class Processes(Activity): """ @classmethod - def create(cls: type[Self], name: str, config: configparser.SectionProxy) -> Self: + def create(cls: type[Self], config: configparser.SectionProxy) -> Self: try: processes = config["processes"].split(",") processes = [p.strip() for p in processes] - return cls(name, processes) + return cls(processes) except KeyError as error: raise ConfigurationError("No processes to check specified") from error - def __init__(self, name: str, processes: Iterable[str]) -> None: - Activity.__init__(self, name) + def __init__(self, processes: Iterable[str]) -> None: + Activity.__init__(self) self._processes = processes def check(self) -> str | None: @@ -394,14 +391,14 @@ class Users(Activity): """ @classmethod - def create(cls: type[Self], name: str, config: configparser.SectionProxy) -> Self: + def create(cls: type[Self], config: configparser.SectionProxy) -> Self: with warnings.catch_warnings(): warnings.simplefilter("ignore", FutureWarning) try: user_regex = re.compile(config.get("name", fallback=r".*")) terminal_regex = re.compile(config.get("terminal", fallback=r".*")) host_regex = re.compile(config.get("host", fallback=r".*")) - return cls(name, user_regex, terminal_regex, host_regex) + return cls(user_regex, terminal_regex, host_regex) except re.error as error: raise ConfigurationError( f"Regular expression is invalid: {error}", @@ -409,12 +406,11 @@ def create(cls: type[Self], name: str, config: configparser.SectionProxy) -> Sel def __init__( self, - name: str, user_regex: Pattern, terminal_regex: Pattern, host_regex: Pattern, ) -> None: - Activity.__init__(self, name) + Activity.__init__(self) self._user_regex = user_regex self._terminal_regex = terminal_regex self._host_regex = host_regex @@ -453,15 +449,15 @@ class File(Wakeup): """ @classmethod - def create(cls: type[Self], name: str, config: configparser.SectionProxy) -> Self: + def create(cls: type[Self], config: configparser.SectionProxy) -> Self: try: path = Path(config["path"]) - return cls(name, path) + return cls(path) except KeyError as error: raise ConfigurationError("Missing option path") from error - def __init__(self, name: str, path: Path) -> None: - Wakeup.__init__(self, name) + def __init__(self, path: Path) -> None: + Wakeup.__init__(self) self._path = path def check(self, timestamp: datetime) -> datetime | None: # noqa: ARG002 diff --git a/src/autosuspend/checks/logs.py b/src/autosuspend/checks/logs.py index b40bdfa5..b64d2a3d 100644 --- a/src/autosuspend/checks/logs.py +++ b/src/autosuspend/checks/logs.py @@ -63,10 +63,9 @@ class LastLogActivity(Activity): """ @classmethod - def create(cls: type[Self], name: str, config: configparser.SectionProxy) -> Self: + def create(cls: type[Self], config: configparser.SectionProxy) -> Self: try: return cls( - name, Path(config["log_file"]), re.compile(config["pattern"]), timedelta(minutes=config.getint("minutes", fallback=10)), @@ -88,7 +87,6 @@ def create(cls: type[Self], name: str, config: configparser.SectionProxy) -> Sel def __init__( self, - name: str, log_file: Path, pattern: Pattern, delta: timedelta, @@ -99,7 +97,7 @@ def __init__( raise ValueError("Given delta must be positive") if pattern.groups != 1: raise ValueError("Given pattern must have exactly one capture group") - super().__init__(name=name) + super().__init__() self.log_file = log_file self.pattern = pattern self.delta = delta diff --git a/src/autosuspend/checks/mpd.py b/src/autosuspend/checks/mpd.py index d174be76..5373f1b2 100644 --- a/src/autosuspend/checks/mpd.py +++ b/src/autosuspend/checks/mpd.py @@ -37,19 +37,19 @@ class Mpd(Activity): """ @classmethod - def create(cls: type[Self], name: str, config: configparser.SectionProxy) -> Self: + def create(cls: type[Self], config: configparser.SectionProxy) -> Self: try: host = config.get("host", fallback="localhost") port = config.getint("port", fallback=6600) timeout = config.getint("timeout", fallback=5) - return cls(name, host, port, timeout) + return cls(host, port, timeout) except ValueError as error: raise ConfigurationError( f"Host port or timeout configuration wrong: {error}" ) from error - def __init__(self, name: str, host: str, port: int, timeout: float) -> None: - Check.__init__(self, name) + def __init__(self, host: str, port: int, timeout: float) -> None: + Check.__init__(self) self._host = host self._port = port self._timeout = timeout diff --git a/src/autosuspend/checks/smb.py b/src/autosuspend/checks/smb.py index 3ab67bea..1f95a65a 100644 --- a/src/autosuspend/checks/smb.py +++ b/src/autosuspend/checks/smb.py @@ -18,10 +18,9 @@ class Smb(Activity): @classmethod def create( cls: type[Self], - name: str, config: configparser.SectionProxy | None, # noqa: ARG003 ) -> Self: - return cls(name) + return cls() def _safe_get_status(self) -> str: try: diff --git a/src/autosuspend/checks/stub.py b/src/autosuspend/checks/stub.py index b36265e9..3776e57d 100644 --- a/src/autosuspend/checks/stub.py +++ b/src/autosuspend/checks/stub.py @@ -35,15 +35,15 @@ class Periodic(Wakeup): """ @classmethod - def create(cls: type[Self], name: str, config: configparser.SectionProxy) -> Self: + def create(cls: type[Self], config: configparser.SectionProxy) -> Self: try: kwargs = {config["unit"]: float(config["value"])} - return cls(name, timedelta(**kwargs)) + return cls(timedelta(**kwargs)) except (ValueError, KeyError, TypeError) as error: raise ConfigurationError(str(error)) from error - def __init__(self, name: str, delta: timedelta) -> None: - Wakeup.__init__(self, name) + def __init__(self, delta: timedelta) -> None: + Wakeup.__init__(self) self._delta = delta def check(self, timestamp: datetime) -> datetime | None: diff --git a/src/autosuspend/checks/systemd.py b/src/autosuspend/checks/systemd.py index 421ca265..f60ae42b 100644 --- a/src/autosuspend/checks/systemd.py +++ b/src/autosuspend/checks/systemd.py @@ -65,14 +65,14 @@ class SystemdTimer(Wakeup): """ @classmethod - def create(cls: type[Self], name: str, config: configparser.SectionProxy) -> Self: + def create(cls: type[Self], config: configparser.SectionProxy) -> Self: try: - return cls(name, re.compile(config["match"])) + return cls(re.compile(config["match"])) except (re.error, ValueError, KeyError, TypeError) as error: raise ConfigurationError(str(error)) from error - def __init__(self, name: str, match: Pattern) -> None: - Wakeup.__init__(self, name) + def __init__(self, match: Pattern) -> None: + Wakeup.__init__(self) self._match = match def check(self, timestamp: datetime) -> datetime | None: # noqa: ARG002 @@ -115,7 +115,6 @@ class LogindSessionsIdle(Activity): @classmethod def create( cls: type[Self], - name: str, config: configparser.SectionProxy, ) -> Self: types = config.get("types", fallback="tty,x11,wayland").split(",") @@ -124,16 +123,15 @@ def create( states = [t.strip() for t in states] classes = config.get("classes", fallback="user").split(",") classes = [t.strip() for t in classes] - return cls(name, types, states, classes) + return cls(types, states, classes) def __init__( self, - name: str, types: Iterable[str], states: Iterable[str], classes: Iterable[str] = ("user"), ) -> None: - Activity.__init__(self, name) + Activity.__init__(self) self._types = types self._states = states self._classes = classes diff --git a/src/autosuspend/checks/util.py b/src/autosuspend/checks/util.py index e9986d27..1b7639f8 100644 --- a/src/autosuspend/checks/util.py +++ b/src/autosuspend/checks/util.py @@ -57,8 +57,8 @@ def collect_init_args( raise ConfigurationError("Lacks " + str(error) + " config entry") from error @classmethod - def create(cls: type[Self], name: str, config: configparser.SectionProxy) -> Self: - return cls(name, **cls.collect_init_args(config)) + def create(cls: type[Self], config: configparser.SectionProxy) -> Self: + return cls(**cls.collect_init_args(config)) def __init__( self, diff --git a/src/autosuspend/checks/xorg.py b/src/autosuspend/checks/xorg.py index d7f615a1..8455ae5f 100644 --- a/src/autosuspend/checks/xorg.py +++ b/src/autosuspend/checks/xorg.py @@ -149,12 +149,11 @@ class XIdleTime(Activity): """ @classmethod - def create(cls: type[Self], name: str, config: configparser.SectionProxy) -> Self: + def create(cls: type[Self], config: configparser.SectionProxy) -> Self: with warnings.catch_warnings(): warnings.simplefilter("ignore", FutureWarning) try: return cls( - name, config.getint("timeout", fallback=600), config.get("method", fallback="sockets"), re.compile(config.get("ignore_if_process", fallback=r"a^")), @@ -180,13 +179,12 @@ def _get_session_method(method: str) -> Callable[[], list[XorgSession]]: def __init__( self, - name: str, timeout: float, method: str, ignore_process_re: Pattern, ignore_users_re: Pattern, ) -> None: - Activity.__init__(self, name) + Activity.__init__(self) self._timeout = timeout self._provide_sessions: Callable[[], list[XorgSession]] self._provide_sessions = self._get_session_method(method) diff --git a/src/autosuspend/checks/xpath.py b/src/autosuspend/checks/xpath.py index f9525e30..03cfeae7 100644 --- a/src/autosuspend/checks/xpath.py +++ b/src/autosuspend/checks/xpath.py @@ -37,8 +37,8 @@ def collect_init_args(cls, config: configparser.SectionProxy) -> dict[str, Any]: raise ConfigurationError("Lacks " + str(error) + " config entry") from error @classmethod - def create(cls: type[Self], name: str, config: configparser.SectionProxy) -> Self: - return cls(name, **cls.collect_init_args(config)) + def create(cls: type[Self], config: configparser.SectionProxy) -> Self: + return cls(**cls.collect_init_args(config)) def __init__(self, xpath: str, **kwargs: Any) -> None: NetworkMixin.__init__(self, **kwargs) @@ -93,8 +93,8 @@ class XPathActivity(XPathMixin, Activity): * `lxml`_ """ - def __init__(self, name: str, **kwargs: Any) -> None: - Activity.__init__(self, name) + def __init__(self, **kwargs: Any) -> None: + Activity.__init__(self) XPathMixin.__init__(self, **kwargs) def check(self) -> str | None: @@ -124,8 +124,8 @@ class XPathWakeup(XPathMixin, Wakeup): * `lxml`_ """ - def __init__(self, name: str, **kwargs: Any) -> None: - Wakeup.__init__(self, name) + def __init__(self, **kwargs: Any) -> None: + Wakeup.__init__(self) XPathMixin.__init__(self, **kwargs) def convert_result( @@ -193,18 +193,18 @@ class XPathDeltaWakeup(XPathWakeup): ) @classmethod - def create(cls: type[Self], name: str, config: configparser.SectionProxy) -> Self: + def create(cls: type[Self], config: configparser.SectionProxy) -> Self: try: args = XPathWakeup.collect_init_args(config) args["unit"] = config.get("unit", fallback="minutes") - return cls(name, **args) + return cls(**args) except ValueError as error: raise ConfigurationError(str(error)) from error - def __init__(self, name: str, unit: str, **kwargs: Any) -> None: + def __init__(self, unit: str, **kwargs: Any) -> None: if unit not in self.UNITS: raise ValueError("Unsupported unit") - XPathWakeup.__init__(self, name, **kwargs) + XPathWakeup.__init__(self, **kwargs) self._unit = unit def convert_result(self, result: str, timestamp: datetime) -> datetime: diff --git a/tests/__init__.py b/tests/__init__.py index 0dbe5321..bae4cbf7 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -6,9 +6,5 @@ class CheckTest(abc.ABC): @abc.abstractmethod - def create_instance(self: Self, name: str) -> Check: + def create_instance(self: Self) -> Check: pass - - def test_the_configured_name_is_used(self) -> None: - name = "checktestname" - assert self.create_instance(name).name == name diff --git a/tests/test_autosuspend.py b/tests/test_autosuspend.py index 673558ea..34f074ec 100644 --- a/tests/test_autosuspend.py +++ b/tests/test_autosuspend.py @@ -12,6 +12,7 @@ from pytest_mock import MockerFixture import autosuspend +from autosuspend.checks import ConfiguredCheck, ErrorBehavior from autosuspend.util.systemd import LogindDBusException @@ -112,7 +113,7 @@ class = Mpd parser, "check", "activity", autosuspend.Activity # type: ignore ) - mock_class.create.assert_called_once_with("Foo", parser["check.Foo"]) + mock_class.create.assert_called_once_with(parser["check.Foo"]) def test_external_class(self, mocker: MockerFixture) -> None: mock_class = mocker.patch("os.path.TestCheck", create=True) @@ -130,7 +131,7 @@ class = os.path.TestCheck parser, "check", "activity", autosuspend.Activity # type: ignore ) - mock_class.create.assert_called_once_with("Foo", parser["check.Foo"]) + mock_class.create.assert_called_once_with(parser["check.Foo"]) def test_not_enabled(self, mocker: MockerFixture) -> None: mock_class = mocker.patch("autosuspend.checks.activity.Mpd") @@ -215,7 +216,7 @@ class = Mpd parser, "check", "activity", autosuspend.Activity # type: ignore ) - mock_class.create.assert_called_once_with("Foo", parser["check.Foo"]) + mock_class.create.assert_called_once_with(parser["check.Foo"]) def test_passwords_redacted(self, mocker: MockerFixture, caplog: Any) -> None: mock_class = mocker.patch("autosuspend.checks.activity.Mpd") @@ -238,6 +239,59 @@ class = Mpd assert "THEPASS" not in caplog.text + def test_error_behavior_defaults_to_active(self, mocker: MockerFixture) -> None: + mock_class = mocker.patch("autosuspend.checks.activity.Mpd") + mock_class.create.return_value = mocker.MagicMock( + spec=autosuspend.checks.Activity + ) + + parser = configparser.ConfigParser() + parser.read_string(""" + [check.Foo] + class = Mpd + enabled = True + """) + + checks = autosuspend.set_up_checks( + parser, "check", "activity", autosuspend.Activity # type: ignore + ) + + assert checks[0].error_behavior is ErrorBehavior.ACTIVE + + def test_error_behavior_explicit_ignore(self, mocker: MockerFixture) -> None: + mock_class = mocker.patch("autosuspend.checks.activity.Mpd") + mock_class.create.return_value = mocker.MagicMock( + spec=autosuspend.checks.Activity + ) + + parser = configparser.ConfigParser() + parser.read_string(""" + [check.Foo] + class = Mpd + enabled = True + error_behavior = ignore + """) + + checks = autosuspend.set_up_checks( + parser, "check", "activity", autosuspend.Activity # type: ignore + ) + + assert checks[0].error_behavior is ErrorBehavior.IGNORE + + def test_error_behavior_invalid_value_raises(self) -> None: + parser = configparser.ConfigParser() + parser.read_string(""" + [check.Foo] + class = Mpd + enabled = True + error_behavior = narf + """) + + with pytest.raises(autosuspend.ConfigurationError): + autosuspend.set_up_checks( + parser, "check", "activity", autosuspend.Activity # type: ignore + ) + class TestExecuteChecks: def test_no_checks(self, mocker: MockerFixture) -> None: @@ -245,25 +299,27 @@ def test_no_checks(self, mocker: MockerFixture) -> None: def test_matches(self, mocker: MockerFixture) -> None: matching_check = mocker.MagicMock(spec=autosuspend.Activity) - matching_check.name = "foo" matching_check.check.return_value = "matches" + configured = ConfiguredCheck("test", matching_check) assert ( - autosuspend.execute_checks([matching_check], False, mocker.MagicMock()) - is True + autosuspend.execute_checks([configured], False, mocker.MagicMock()) is True ) matching_check.check.assert_called_once_with() def test_only_first_called(self, mocker: MockerFixture) -> None: matching_check = mocker.MagicMock(spec=autosuspend.Activity) - matching_check.name = "foo" matching_check.check.return_value = "matches" second_check = mocker.MagicMock() - second_check.name = "bar" second_check.check.return_value = "matches" assert ( autosuspend.execute_checks( - [matching_check, second_check], False, mocker.MagicMock() + [ + ConfiguredCheck("first", matching_check), + ConfiguredCheck("second", second_check), + ], + False, + mocker.MagicMock(), ) is True ) @@ -272,15 +328,18 @@ def test_only_first_called(self, mocker: MockerFixture) -> None: def test_all_called(self, mocker: MockerFixture) -> None: matching_check = mocker.MagicMock(spec=autosuspend.Activity) - matching_check.name = "foo" matching_check.check.return_value = "matches" second_check = mocker.MagicMock() - second_check.name = "bar" second_check.check.return_value = "matches" assert ( autosuspend.execute_checks( - [matching_check, second_check], True, mocker.MagicMock() + [ + ConfiguredCheck("first", matching_check), + ConfiguredCheck("second", second_check), + ], + True, + mocker.MagicMock(), ) is True ) @@ -289,12 +348,21 @@ def test_all_called(self, mocker: MockerFixture) -> None: def test_treat_temporary_errors_as_activity(self, mocker: MockerFixture) -> None: matching_check = mocker.MagicMock(spec=autosuspend.Activity) - matching_check.name = "foo" matching_check.check.side_effect = autosuspend.TemporaryCheckError() + configured = ConfiguredCheck("test", matching_check, ErrorBehavior.ACTIVE) assert ( - autosuspend.execute_checks([matching_check], False, mocker.MagicMock()) - is True + autosuspend.execute_checks([configured], False, mocker.MagicMock()) is True + ) + matching_check.check.assert_called_once_with() + + def test_ignore_temporary_errors(self, mocker: MockerFixture) -> None: + matching_check = mocker.MagicMock(spec=autosuspend.Activity) + matching_check.check.side_effect = autosuspend.TemporaryCheckError() + configured = ConfiguredCheck("test", matching_check, ErrorBehavior.IGNORE) + + assert ( + autosuspend.execute_checks([configured], False, mocker.MagicMock()) is False ) matching_check.check.assert_called_once_with() @@ -310,7 +378,9 @@ def test_all_none(self, mocker: MockerFixture) -> None: wakeup = mocker.MagicMock(spec=autosuspend.Wakeup) wakeup.check.return_value = None assert ( - autosuspend.execute_wakeups([wakeup], datetime.now(UTC), mocker.MagicMock()) + autosuspend.execute_wakeups( + [ConfiguredCheck("w", wakeup)], datetime.now(UTC), mocker.MagicMock() + ) is None ) @@ -329,7 +399,10 @@ def test_skips_none_outdated_and_continues( wakeup_real.check.return_value = wake_up_at assert ( autosuspend.execute_wakeups( - [wakeup_none, wakeup_real], + [ + ConfiguredCheck("none", wakeup_none), + ConfiguredCheck("real", wakeup_real), + ], now, mocker.MagicMock(), ) @@ -343,7 +416,9 @@ def test_basic_return(self, mocker: MockerFixture) -> None: wakeup_time = now + timedelta(seconds=10) wakeup.check.return_value = wakeup_time assert ( - autosuspend.execute_wakeups([wakeup], now, mocker.MagicMock()) + autosuspend.execute_wakeups( + [ConfiguredCheck("w", wakeup)], now, mocker.MagicMock() + ) == wakeup_time ) @@ -359,7 +434,13 @@ def test_soonest_taken(self, mocker: MockerFixture) -> None: wakeup_later.check.return_value = in_between assert ( autosuspend.execute_wakeups( - [wakeup, wakeup_earlier, wakeup_later], reference, mocker.MagicMock() + [ + ConfiguredCheck("w", wakeup), + ConfiguredCheck("e", wakeup_earlier), + ConfiguredCheck("l", wakeup_later), + ], + reference, + mocker.MagicMock(), ) == earlier ) @@ -374,17 +455,26 @@ def test_ignore_temporary_errors(self, mocker: MockerFixture) -> None: wakeup_earlier = mocker.MagicMock(spec=autosuspend.Wakeup) wakeup_earlier.check.return_value = now + timedelta(seconds=10) assert autosuspend.execute_wakeups( - [wakeup, wakeup_error, wakeup_earlier], now, mocker.MagicMock() + [ + ConfiguredCheck("w", wakeup), + ConfiguredCheck("e", wakeup_error), + ConfiguredCheck("ea", wakeup_earlier), + ], + now, + mocker.MagicMock(), ) == now + timedelta(seconds=10) def test_ignore_too_early(self, mocker: MockerFixture) -> None: now = datetime.now(UTC) wakeup = mocker.MagicMock(spec=autosuspend.Wakeup) wakeup.check.return_value = now - assert autosuspend.execute_wakeups([wakeup], now, mocker.MagicMock()) is None + configured = ConfiguredCheck("w", wakeup) + assert ( + autosuspend.execute_wakeups([configured], now, mocker.MagicMock()) is None + ) assert ( autosuspend.execute_wakeups( - [wakeup], now + timedelta(seconds=1), mocker.MagicMock() + [configured], now + timedelta(seconds=1), mocker.MagicMock() ) is None ) @@ -463,17 +553,21 @@ def test_notify_and_suspend(mocker: MockerFixture) -> None: class _StubCheck(autosuspend.Activity): @classmethod - def create(cls, name: str, config: configparser.SectionProxy) -> "_StubCheck": + def create(cls, config: configparser.SectionProxy) -> "_StubCheck": raise NotImplementedError() - def __init__(self, name: str, match: str | None) -> None: - autosuspend.Activity.__init__(self, name) + def __init__(self, match: str | None) -> None: + autosuspend.Activity.__init__(self) self.match = match def check(self) -> str | None: return self.match +def _configured_stub(match: str | None) -> ConfiguredCheck[autosuspend.Activity]: + return ConfiguredCheck("stub", _StubCheck(match)) + + class SleepFn: def __init__(self) -> None: self.called = False @@ -512,7 +606,7 @@ def wakeup_fn() -> WakeupFn: class TestProcessor: def test_smoke(self, sleep_fn: SleepFn, wakeup_fn: WakeupFn) -> None: processor = autosuspend.Processor( - [_StubCheck("stub", None)], [], 2, 0, 0, sleep_fn, wakeup_fn, False + [_configured_stub(None)], [], 2, 0, 0, sleep_fn, wakeup_fn, False ) # should init the timestamp initially start = datetime.now(UTC) @@ -544,7 +638,7 @@ def test_just_woke_up_handling( self, sleep_fn: SleepFn, wakeup_fn: WakeupFn ) -> None: processor = autosuspend.Processor( - [_StubCheck("stub", None)], [], 2, 0, 0, sleep_fn, wakeup_fn, False + [_configured_stub(None)], [], 2, 0, 0, sleep_fn, wakeup_fn, False ) # should init the timestamp initially @@ -573,7 +667,14 @@ def test_wakeup_blocks_sleep( wakeup = mocker.MagicMock(spec=autosuspend.Wakeup) wakeup.check.return_value = start + timedelta(seconds=6) processor = autosuspend.Processor( - [_StubCheck("stub", None)], [wakeup], 2, 3.1, 0, sleep_fn, wakeup_fn, False + [_configured_stub(None)], + [ConfiguredCheck("wakeup", wakeup)], + 2, + 3.1, + 0, + sleep_fn, + wakeup_fn, + False, ) # init iteration @@ -593,7 +694,14 @@ def test_wakeup_exact_hit_does_not_block( wakeup = mocker.MagicMock(spec=autosuspend.Wakeup) wakeup.check.return_value = start + timedelta(seconds=6) processor = autosuspend.Processor( - [_StubCheck("stub", None)], [wakeup], 2, 3, 0, sleep_fn, wakeup_fn, False + [_configured_stub(None)], + [ConfiguredCheck("wakeup", wakeup)], + 2, + 3, + 0, + sleep_fn, + wakeup_fn, + False, ) # init iteration @@ -613,7 +721,14 @@ def test_wakeup_scheduled( wakeup = mocker.MagicMock(spec=autosuspend.Wakeup) wakeup.check.return_value = start + timedelta(seconds=25) processor = autosuspend.Processor( - [_StubCheck("stub", None)], [wakeup], 2, 10, 0, sleep_fn, wakeup_fn, False + [_configured_stub(None)], + [ConfiguredCheck("wakeup", wakeup)], + 2, + 10, + 0, + sleep_fn, + wakeup_fn, + False, ) # init iteration @@ -642,7 +757,14 @@ def test_wakeup_delta_blocks( wakeup = mocker.MagicMock(spec=autosuspend.Wakeup) wakeup.check.return_value = start + timedelta(seconds=25) processor = autosuspend.Processor( - [_StubCheck("stub", None)], [wakeup], 2, 10, 22, sleep_fn, wakeup_fn, False + [_configured_stub(None)], + [ConfiguredCheck("wakeup", wakeup)], + 2, + 10, + 22, + sleep_fn, + wakeup_fn, + False, ) # init iteration @@ -658,7 +780,14 @@ def test_wakeup_delta_applied( wakeup = mocker.MagicMock(spec=autosuspend.Wakeup) wakeup.check.return_value = start + timedelta(seconds=25) processor = autosuspend.Processor( - [_StubCheck("stub", None)], [wakeup], 2, 10, 4, sleep_fn, wakeup_fn, False + [_configured_stub(None)], + [ConfiguredCheck("wakeup", wakeup)], + 2, + 10, + 4, + sleep_fn, + wakeup_fn, + False, ) # init iteration @@ -679,7 +808,7 @@ def test_inhibit_lock_blocks_suspend( ) -> None: start = datetime.now(UTC) processor = autosuspend.Processor( - [_StubCheck("stub", None)], [], 2, 0, 0, sleep_fn, wakeup_fn, False + [_configured_stub(None)], [], 2, 0, 0, sleep_fn, wakeup_fn, False ) # init iteration @@ -703,7 +832,7 @@ def test_suspend_immediately_after_inhibit_lock_released( ) -> None: start = datetime.now(UTC) processor = autosuspend.Processor( - [_StubCheck("stub", None)], [], 2, 0, 0, sleep_fn, wakeup_fn, False + [_configured_stub(None)], [], 2, 0, 0, sleep_fn, wakeup_fn, False ) # init iteration @@ -732,7 +861,7 @@ def test_inhibit_lock_dbus_error_does_not_prevent_suspend( ) -> None: start = datetime.now(UTC) processor = autosuspend.Processor( - [_StubCheck("stub", None)], [], 2, 0, 0, sleep_fn, wakeup_fn, False + [_configured_stub(None)], [], 2, 0, 0, sleep_fn, wakeup_fn, False ) # Mock has_inhibit_lock to raise an exception diff --git a/tests/test_checks.py b/tests/test_checks.py index daad9947..ca5588ff 100644 --- a/tests/test_checks.py +++ b/tests/test_checks.py @@ -5,7 +5,7 @@ class DummyCheck(Check): @classmethod - def create(cls, name: str, config: configparser.SectionProxy) -> "DummyCheck": + def create(cls, config: configparser.SectionProxy) -> "DummyCheck": raise NotImplementedError() def check(self) -> str | None: @@ -13,13 +13,5 @@ def check(self) -> str | None: class TestCheck: - class TestName: - def test_returns_the_provided_name(self) -> None: - name = "test" - assert DummyCheck(name).name == name - - def test_has_a_sensible_default(self) -> None: - assert DummyCheck().name is not None - def test_has_a_string_representation(self) -> None: - assert isinstance(str(DummyCheck("test")), str) + assert isinstance(str(DummyCheck()), str) diff --git a/tests/test_checks_command.py b/tests/test_checks_command.py index 4ecfd6b3..c58fccd7 100644 --- a/tests/test_checks_command.py +++ b/tests/test_checks_command.py @@ -18,8 +18,8 @@ class _CommandMixinSub(CommandMixin, Activity): - def __init__(self, name: str, command: str) -> None: - Activity.__init__(self, name) + def __init__(self, command: str) -> None: + Activity.__init__(self) CommandMixin.__init__(self, command) def check(self) -> str | None: @@ -31,28 +31,25 @@ class TestCreate: def test_it_works(self) -> None: section = config_section({"command": "narf bla"}) check: _CommandMixinSub = _CommandMixinSub.create( - "name", section, ) assert check._command == "narf bla" def test_throws_if_no_command_is_configured(self) -> None: with pytest.raises(ConfigurationError): - _CommandMixinSub.create("name", config_section()) + _CommandMixinSub.create(config_section()) class TestCommandActivity(CheckTest): - def create_instance(self, name: str) -> Check: - return CommandActivity(name, "asdfasdf") + def create_instance(self) -> Check: + return CommandActivity("asdfasdf") def test_reports_activity_if_the_command_succeeds( self, mocker: MockerFixture ) -> None: mock = mocker.patch("subprocess.check_call") assert ( - CommandActivity.create( - "name", config_section({"command": "foo bar"}) - ).check() + CommandActivity.create(config_section({"command": "foo bar"})).check() is not None ) mock.assert_called_once_with("foo bar", shell=True) @@ -63,9 +60,7 @@ def test_reports_no_activity_if_the_command_fails( mock = mocker.patch("subprocess.check_call") mock.side_effect = subprocess.CalledProcessError(2, "foo bar") assert ( - CommandActivity.create( - "name", config_section({"command": "foo bar"}) - ).check() + CommandActivity.create(config_section({"command": "foo bar"})).check() is None ) mock.assert_called_once_with("foo bar", shell=True) @@ -73,31 +68,31 @@ def test_reports_no_activity_if_the_command_fails( def test_reports_missing_commands(self) -> None: with pytest.raises(SevereCheckError): CommandActivity.create( - "name", config_section({"command": "thisreallydoesnotexist"}) + config_section({"command": "thisreallydoesnotexist"}) ).check() class TestCommandWakeup(CheckTest): - def create_instance(self, name: str) -> Check: - return CommandWakeup(name, "asdf") + def create_instance(self) -> Check: + return CommandWakeup("asdf") def test_reports_the_wakup_time_received_from_the_command(self) -> None: - check = CommandWakeup("test", "echo 1234") + check = CommandWakeup("echo 1234") assert check.check(datetime.now(UTC)) == datetime.fromtimestamp(1234, UTC) def test_reports_no_wakeup_without_command_output(self) -> None: - check = CommandWakeup("test", "echo") + check = CommandWakeup("echo") assert check.check(datetime.now(UTC)) is None def test_raises_an_error_if_the_command_output_cannot_be_parsed(self) -> None: - check = CommandWakeup("test", "echo asdfasdf") + check = CommandWakeup("echo asdfasdf") with pytest.raises(TemporaryCheckError): check.check(datetime.now(UTC)) def test_uses_only_the_first_output_line(self, mocker: MockerFixture) -> None: mock = mocker.patch("subprocess.check_output") mock.return_value = "1234\nignore\n" - check = CommandWakeup("test", "echo bla") + check = CommandWakeup("echo bla") assert check.check(datetime.now(UTC)) == datetime.fromtimestamp(1234, UTC) def test_uses_only_the_first_line_even_if_empty( @@ -105,17 +100,17 @@ def test_uses_only_the_first_line_even_if_empty( ) -> None: mock = mocker.patch("subprocess.check_output") mock.return_value = " \nignore\n" - check = CommandWakeup("test", "echo bla") + check = CommandWakeup("echo bla") assert check.check(datetime.now(UTC)) is None def test_raises_if_the_called_command_fails(self, mocker: MockerFixture) -> None: mock = mocker.patch("subprocess.check_output") mock.side_effect = subprocess.CalledProcessError(2, "foo bar") - check = CommandWakeup("test", "echo bla") + check = CommandWakeup("echo bla") with pytest.raises(TemporaryCheckError): check.check(datetime.now(UTC)) def test_reports_missing_executables(self) -> None: - check = CommandWakeup("test", "reallydoesntexist bla") + check = CommandWakeup("reallydoesntexist bla") with pytest.raises(SevereCheckError): check.check(datetime.now(UTC)) diff --git a/tests/test_checks_ical.py b/tests/test_checks_ical.py index cdcfb749..5721fc3a 100644 --- a/tests/test_checks_ical.py +++ b/tests/test_checks_ical.py @@ -433,12 +433,11 @@ def test_regex_is_search_not_fullmatch(self, datadir: Path) -> None: class TestActiveCalendarEvent(CheckTest): - def create_instance(self, name: str) -> Check: - return ActiveCalendarEvent(name, url="asdfasdf", timeout=5) + def create_instance(self) -> Check: + return ActiveCalendarEvent(url="asdfasdf", timeout=5) def test_smoke(self, datadir: Path, serve_file: Callable[[Path], str]) -> None: result = ActiveCalendarEvent( - "test", url=serve_file(datadir / "long-event.ics"), timeout=3, ).check() @@ -450,7 +449,6 @@ def test_exact_range( ) -> None: with freeze_time("2016-06-05 13:00:00", tz_offset=-2): result = ActiveCalendarEvent( - "test", url=serve_file(datadir / "long-event.ics"), timeout=3, ).check() @@ -462,7 +460,6 @@ def test_before_exact_range( ) -> None: with freeze_time("2016-06-05 12:58:00", tz_offset=-2): result = ActiveCalendarEvent( - "test", url=serve_file(datadir / "long-event.ics"), timeout=3, ).check() @@ -471,7 +468,6 @@ def test_before_exact_range( def test_no_event(self, datadir: Path, serve_file: Callable[[Path], str]) -> None: assert ( ActiveCalendarEvent( - "test", url=serve_file(datadir / "old-event.ics"), timeout=3, ).check() @@ -480,7 +476,6 @@ def test_no_event(self, datadir: Path, serve_file: Callable[[Path], str]) -> Non def test_create(self) -> None: check: ActiveCalendarEvent = ActiveCalendarEvent.create( - "name", config_section( { "url": "foobar", @@ -498,7 +493,6 @@ def test_create(self) -> None: def test_create_with_match(self) -> None: check: ActiveCalendarEvent = ActiveCalendarEvent.create( - "name", config_section({"url": "foobar", "match": "important"}), ) assert check._match == "important" @@ -507,7 +501,6 @@ def test_match_filters_events( self, datadir: Path, serve_file: Callable[[Path], str] ) -> None: result = ActiveCalendarEvent( - "test", url=serve_file(datadir / "long-event.ics"), timeout=3, match="no-such-event", @@ -516,8 +509,8 @@ def test_match_filters_events( class TestCalendar(CheckTest): - def create_instance(self, name: str) -> Calendar: - return Calendar(name, url="file:///asdf", timeout=3) + def create_instance(self) -> Calendar: + return Calendar(url="file:///asdf", timeout=3) def test_create(self) -> None: section = config_section( @@ -529,7 +522,6 @@ def test_create(self) -> None: } ) check: Calendar = Calendar.create( - "name", section, ) assert check._url == "url" @@ -540,7 +532,6 @@ def test_create(self) -> None: def test_create_with_match(self) -> None: check: Calendar = Calendar.create( - "name", config_section({"url": "url", "match": "important"}), ) assert check._match == "important" @@ -551,7 +542,6 @@ def test_match_filters_events( timestamp = parser.parse("20040605T090000Z") assert ( Calendar( - "test", url=serve_file(datadir / "old-event.ics"), timeout=3, match="no-such-event", @@ -563,7 +553,6 @@ def test_empty(self, datadir: Path, serve_file: Callable[[Path], str]) -> None: timestamp = parser.parse("20050605T130000Z") assert ( Calendar( - "test", url=serve_file(datadir / "old-event.ics"), timeout=3, ).check(timestamp) @@ -576,7 +565,6 @@ def test_smoke(self, datadir: Path, serve_file: Callable[[Path], str]) -> None: assert ( Calendar( - "test", url=serve_file(datadir / "old-event.ics"), timeout=3, ).check(timestamp) @@ -591,7 +579,6 @@ def test_select_earliest( assert ( Calendar( - "test", url=serve_file(datadir / "multiple.ics"), timeout=3, ).check(timestamp) @@ -604,9 +591,9 @@ def test_ignore_running( url = serve_file(datadir / "old-event.ics") timestamp = parser.parse("20040605T110000Z") # events are taken if start hits exactly the current time - assert Calendar("test", url=url, timeout=3).check(timestamp) is not None + assert Calendar(url=url, timeout=3).check(timestamp) is not None timestamp = timestamp + timedelta(seconds=1) - assert Calendar("test", url=url, timeout=3).check(timestamp) is None + assert Calendar(url=url, timeout=3).check(timestamp) is None def test_limited_horizon( self, datadir: Path, serve_file: Callable[[Path], str] @@ -615,7 +602,6 @@ def test_limited_horizon( assert ( Calendar( - "test", url=serve_file(datadir / "after-horizon.ics"), timeout=3, ).check(timestamp) @@ -624,7 +610,6 @@ def test_limited_horizon( assert ( Calendar( - "test", url=serve_file(datadir / "before-horizon.ics"), timeout=3, ).check(timestamp) diff --git a/tests/test_checks_json.py b/tests/test_checks_json.py index 8447c93d..9802bd6e 100644 --- a/tests/test_checks_json.py +++ b/tests/test_checks_json.py @@ -14,9 +14,8 @@ class TestJsonPath(CheckTest): - def create_instance(self, name: str) -> JsonPath: + def create_instance(self) -> JsonPath: return JsonPath( - name=name, url="url", timeout=5, username="userx", @@ -33,10 +32,7 @@ def json_get_mock(mocker: MockerFixture) -> Any: def test_matching(self, json_get_mock: Any) -> None: url = "nourl" - assert ( - JsonPath("foo", jsonpath=parse("a.b"), url=url, timeout=5).check() - is not None - ) + assert JsonPath(jsonpath=parse("a.b"), url=url, timeout=5).check() is not None json_get_mock.assert_called_once_with( url, timeout=5, headers={"Accept": "application/json"} @@ -46,9 +42,7 @@ def test_matching(self, json_get_mock: Any) -> None: def test_filter_expressions_work(self, json_get_mock: Any) -> None: url = "nourl" assert ( - JsonPath( - "foo", jsonpath=parse("$[?(@.c=='ignore')]"), url=url, timeout=5 - ).check() + JsonPath(jsonpath=parse("$[?(@.c=='ignore')]"), url=url, timeout=5).check() is not None ) @@ -59,10 +53,7 @@ def test_filter_expressions_work(self, json_get_mock: Any) -> None: def test_not_matching(self, json_get_mock: Any) -> None: url = "nourl" - assert ( - JsonPath("foo", jsonpath=parse("not.there"), url=url, timeout=5).check() - is None - ) + assert JsonPath(jsonpath=parse("not.there"), url=url, timeout=5).check() is None json_get_mock.assert_called_once_with( url, timeout=5, headers={"Accept": "application/json"} @@ -74,7 +65,6 @@ def test_network_errors_are_passed( ) -> None: with pytest.raises(TemporaryCheckError): JsonPath( - name="name", url=serve_protected(datadir / "data.txt")[0], timeout=5, username="wrong", @@ -85,7 +75,6 @@ def test_network_errors_are_passed( def test_not_json(self, datadir: Path, serve_file: Callable[[Path], str]) -> None: with pytest.raises(TemporaryCheckError): JsonPath( - name="name", url=serve_file(datadir / "invalid.json"), timeout=5, jsonpath=parse("b"), @@ -94,7 +83,6 @@ def test_not_json(self, datadir: Path, serve_file: Callable[[Path], str]) -> Non class TestCreate: def test_it_works(self) -> None: check: JsonPath = JsonPath.create( - "name", config_section( { "url": "url", @@ -114,7 +102,6 @@ def test_it_works(self) -> None: def test_raises_on_missing_json_path(self) -> None: with pytest.raises(ConfigurationError): JsonPath.create( - "name", config_section( { "url": "url", @@ -128,7 +115,6 @@ def test_raises_on_missing_json_path(self) -> None: def test_raises_on_invalid_json_path(self) -> None: with pytest.raises(ConfigurationError): JsonPath.create( - "name", config_section( { "url": "url", diff --git a/tests/test_checks_kodi.py b/tests/test_checks_kodi.py index a8b2cd60..e5d42865 100644 --- a/tests/test_checks_kodi.py +++ b/tests/test_checks_kodi.py @@ -12,8 +12,8 @@ class TestKodi(CheckTest): - def create_instance(self, name: str) -> Check: - return Kodi(name, url="url", timeout=10) + def create_instance(self) -> Check: + return Kodi(url="url", timeout=10) def test_playing(self, mocker: MockerFixture) -> None: mock_reply = mocker.MagicMock() @@ -24,7 +24,7 @@ def test_playing(self, mocker: MockerFixture) -> None: } mocker.patch("requests.Session.get", return_value=mock_reply) - assert Kodi("foo", url="url", timeout=10).check() is not None + assert Kodi(url="url", timeout=10).check() is not None mock_reply.json.assert_called_once_with() @@ -33,7 +33,7 @@ def test_not_playing(self, mocker: MockerFixture) -> None: mock_reply.json.return_value = {"id": 1, "jsonrpc": "2.0", "result": []} mocker.patch("requests.Session.get", return_value=mock_reply) - assert Kodi("foo", url="url", timeout=10).check() is None + assert Kodi(url="url", timeout=10).check() is None mock_reply.json.assert_called_once_with() @@ -47,8 +47,7 @@ def test_playing_suspend_while_paused(self, mocker: MockerFixture) -> None: mocker.patch("requests.Session.get", return_value=mock_reply) assert ( - Kodi("foo", url="url", timeout=10, suspend_while_paused=True).check() - is not None + Kodi(url="url", timeout=10, suspend_while_paused=True).check() is not None ) mock_reply.json.assert_called_once_with() @@ -62,10 +61,7 @@ def test_not_playing_suspend_while_paused(self, mocker: MockerFixture) -> None: } mocker.patch("requests.Session.get", return_value=mock_reply) - assert ( - Kodi("foo", url="url", timeout=10, suspend_while_paused=True).check() - is None - ) + assert Kodi(url="url", timeout=10, suspend_while_paused=True).check() is None mock_reply.json.assert_called_once_with() @@ -75,7 +71,7 @@ def test_assertion_no_result(self, mocker: MockerFixture) -> None: mocker.patch("requests.Session.get", return_value=mock_reply) with pytest.raises(TemporaryCheckError): - Kodi("foo", url="url", timeout=10).check() + Kodi(url="url", timeout=10).check() def test_request_error(self, mocker: MockerFixture) -> None: mocker.patch( @@ -83,7 +79,7 @@ def test_request_error(self, mocker: MockerFixture) -> None: ) with pytest.raises(TemporaryCheckError): - Kodi("foo", url="url", timeout=10).check() + Kodi(url="url", timeout=10).check() def test_json_error(self, mocker: MockerFixture) -> None: mock_reply = mocker.MagicMock() @@ -91,11 +87,10 @@ def test_json_error(self, mocker: MockerFixture) -> None: mocker.patch("requests.Session.get", return_value=mock_reply) with pytest.raises(TemporaryCheckError): - Kodi("foo", url="url", timeout=10).check() + Kodi(url="url", timeout=10).check() def test_create(self) -> None: check = Kodi.create( - "name", config_section( { "url": "anurl", @@ -109,17 +104,17 @@ def test_create(self) -> None: assert not check._suspend_while_paused def test_create_default_url(self) -> None: - check = Kodi.create("name", config_section()) + check = Kodi.create(config_section()) assert check._url.split("?")[0] == "http://localhost:8080/jsonrpc" def test_create_timeout_no_number(self) -> None: with pytest.raises(ConfigurationError): - Kodi.create("name", config_section({"url": "anurl", "timeout": "string"})) + Kodi.create(config_section({"url": "anurl", "timeout": "string"})) def test_create_suspend_while_paused(self) -> None: check = Kodi.create( - "name", config_section({"url": "anurl", "suspend_while_paused": "True"}) + config_section({"url": "anurl", "suspend_while_paused": "True"}) ) assert check._url.startswith("anurl") @@ -127,12 +122,12 @@ def test_create_suspend_while_paused(self) -> None: class TestKodiIdleTime(CheckTest): - def create_instance(self, name: str) -> Check: - return KodiIdleTime(name, url="url", timeout=10, idle_time=10) + def create_instance(self) -> Check: + return KodiIdleTime(url="url", timeout=10, idle_time=10) def test_create(self) -> None: check = KodiIdleTime.create( - "name", config_section({"url": "anurl", "timeout": "12", "idle_time": "42"}) + config_section({"url": "anurl", "timeout": "12", "idle_time": "42"}) ) assert check._url.startswith("anurl") @@ -140,21 +135,17 @@ def test_create(self) -> None: assert check._idle_time == 42 def test_create_default_url(self) -> None: - check = KodiIdleTime.create("name", config_section()) + check = KodiIdleTime.create(config_section()) assert check._url.split("?")[0] == "http://localhost:8080/jsonrpc" def test_create_timeout_no_number(self) -> None: with pytest.raises(ConfigurationError): - KodiIdleTime.create( - "name", config_section({"url": "anurl", "timeout": "string"}) - ) + KodiIdleTime.create(config_section({"url": "anurl", "timeout": "string"})) def test_create_idle_time_no_number(self) -> None: with pytest.raises(ConfigurationError): - KodiIdleTime.create( - "name", config_section({"url": "anurl", "idle_time": "string"}) - ) + KodiIdleTime.create(config_section({"url": "anurl", "idle_time": "string"})) def test_no_result(self, mocker: MockerFixture) -> None: mock_reply = mocker.MagicMock() @@ -162,7 +153,7 @@ def test_no_result(self, mocker: MockerFixture) -> None: mocker.patch("requests.Session.get", return_value=mock_reply) with pytest.raises(TemporaryCheckError): - KodiIdleTime("foo", url="url", timeout=10, idle_time=42).check() + KodiIdleTime(url="url", timeout=10, idle_time=42).check() def test_result_is_list(self, mocker: MockerFixture) -> None: mock_reply = mocker.MagicMock() @@ -170,7 +161,7 @@ def test_result_is_list(self, mocker: MockerFixture) -> None: mocker.patch("requests.Session.get", return_value=mock_reply) with pytest.raises(TemporaryCheckError): - KodiIdleTime("foo", url="url", timeout=10, idle_time=42).check() + KodiIdleTime(url="url", timeout=10, idle_time=42).check() def test_result_no_entry(self, mocker: MockerFixture) -> None: mock_reply = mocker.MagicMock() @@ -178,7 +169,7 @@ def test_result_no_entry(self, mocker: MockerFixture) -> None: mocker.patch("requests.Session.get", return_value=mock_reply) with pytest.raises(TemporaryCheckError): - KodiIdleTime("foo", url="url", timeout=10, idle_time=42).check() + KodiIdleTime(url="url", timeout=10, idle_time=42).check() def test_result_wrong_entry(self, mocker: MockerFixture) -> None: mock_reply = mocker.MagicMock() @@ -190,7 +181,7 @@ def test_result_wrong_entry(self, mocker: MockerFixture) -> None: mocker.patch("requests.Session.get", return_value=mock_reply) with pytest.raises(TemporaryCheckError): - KodiIdleTime("foo", url="url", timeout=10, idle_time=42).check() + KodiIdleTime(url="url", timeout=10, idle_time=42).check() def test_active(self, mocker: MockerFixture) -> None: mock_reply = mocker.MagicMock() @@ -201,9 +192,7 @@ def test_active(self, mocker: MockerFixture) -> None: } mocker.patch("requests.Session.get", return_value=mock_reply) - assert ( - KodiIdleTime("foo", url="url", timeout=10, idle_time=42).check() is not None - ) + assert KodiIdleTime(url="url", timeout=10, idle_time=42).check() is not None def test_inactive(self, mocker: MockerFixture) -> None: mock_reply = mocker.MagicMock() @@ -214,7 +203,7 @@ def test_inactive(self, mocker: MockerFixture) -> None: } mocker.patch("requests.Session.get", return_value=mock_reply) - assert KodiIdleTime("foo", url="url", timeout=10, idle_time=42).check() is None + assert KodiIdleTime(url="url", timeout=10, idle_time=42).check() is None def test_request_error(self, mocker: MockerFixture) -> None: mocker.patch( @@ -222,4 +211,4 @@ def test_request_error(self, mocker: MockerFixture) -> None: ) with pytest.raises(TemporaryCheckError): - KodiIdleTime("foo", url="url", timeout=10, idle_time=42).check() + KodiIdleTime(url="url", timeout=10, idle_time=42).check() diff --git a/tests/test_checks_linux.py b/tests/test_checks_linux.py index fbfbf0e7..73a04611 100644 --- a/tests/test_checks_linux.py +++ b/tests/test_checks_linux.py @@ -40,8 +40,8 @@ class TestUsers(CheckTest): - def create_instance(self, name: str) -> Check: - return Users(name, re.compile(".*"), re.compile(".*"), re.compile(".*")) + def create_instance(self) -> Check: + return Users(re.compile(".*"), re.compile(".*"), re.compile(".*")) @staticmethod def create_suser( @@ -53,8 +53,7 @@ def test_reports_no_activity_without_users(self, mocker: MockerFixture) -> None: mocker.patch("psutil.users").return_value = [] assert ( - Users("users", re.compile(".*"), re.compile(".*"), re.compile(".*")).check() - is None + Users(re.compile(".*"), re.compile(".*"), re.compile(".*")).check() is None ) def test_matching_users(self, mocker: MockerFixture) -> None: @@ -63,7 +62,7 @@ def test_matching_users(self, mocker: MockerFixture) -> None: ] assert ( - Users("users", re.compile(".*"), re.compile(".*"), re.compile(".*")).check() + Users(re.compile(".*"), re.compile(".*"), re.compile(".*")).check() is not None ) @@ -75,16 +74,13 @@ def test_detect_no_activity_if_no_matching_user_exists( ] assert ( - Users( - "users", re.compile("narf"), re.compile(".*"), re.compile(".*") - ).check() + Users(re.compile("narf"), re.compile(".*"), re.compile(".*")).check() is None ) class TestCreate: def test_it_works_with_a_valid_config(self) -> None: check = Users.create( - "name", config_section( { "name": "name.*name", @@ -101,7 +97,6 @@ def test_it_works_with_a_valid_config(self) -> None: def test_raises_with_invalid_expression(self) -> None: with pytest.raises(ConfigurationError): Users.create( - "name", config_section( { "name": "name.*name", @@ -113,8 +108,8 @@ def test_raises_with_invalid_expression(self) -> None: class TestProcesses(CheckTest): - def create_instance(self, name: str) -> Check: - return Processes(name, ["foo"]) + def create_instance(self) -> Check: + return Processes(["foo"]) class StubProcess: def __init__(self, name: str) -> None: @@ -135,12 +130,12 @@ def test_detects_activity_with_matching_process( self.StubProcess("nonmatching"), ] - assert Processes("foo", ["dummy", "blubb", "other"]).check() is not None + assert Processes(["dummy", "blubb", "other"]).check() is not None def test_ignores_no_such_process_errors(self, mocker: MockerFixture) -> None: mocker.patch("psutil.process_iter").return_value = [self.RaisingProcess()] - Processes("foo", ["dummy"]).check() + Processes(["dummy"]).check() def test_detect_no_activity_for_non_matching_processes( self, mocker: MockerFixture @@ -150,12 +145,12 @@ def test_detect_no_activity_for_non_matching_processes( self.StubProcess("nonmatching"), ] - assert Processes("foo", ["dummy", "blubb", "other"]).check() is None + assert Processes(["dummy", "blubb", "other"]).check() is None class TestCreate: def test_it_works_with_a_valid_config(self) -> None: assert Processes.create( - "name", config_section({"processes": "foo, bar, narf"}) + config_section({"processes": "foo, bar, narf"}) )._processes == [ "foo", "bar", @@ -164,7 +159,7 @@ def test_it_works_with_a_valid_config(self) -> None: def test_raises_if_no_processes_are_configured(self) -> None: with pytest.raises(ConfigurationError): - Processes.create("name", config_section()) + Processes.create(config_section()) snic = namedtuple("snic", ["family", "address", "netmask", "broadcast", "ptp"]) @@ -178,8 +173,8 @@ class TestActiveConnection(CheckTest): # https://superuser.com/a/99753/227177 MY_ADDRESS_IPV6_SCOPED = "fe80::5193:518c:5c69:cccc%eth0" - def create_instance(self, name: str) -> Check: - return ActiveConnection(name, [10]) + def create_instance(self) -> Check: + return ActiveConnection([10]) @pytest.mark.parametrize( "connection", @@ -251,7 +246,7 @@ def test_detect_activity_if_port_is_connected( } mocker.patch("psutil.net_connections").return_value = [connection] - assert ActiveConnection("foo", [10, self.MY_PORT, 30]).check() is not None + assert ActiveConnection([10, self.MY_PORT, 30]).check() is not None @pytest.mark.parametrize( "connection", @@ -308,54 +303,51 @@ def test_detects_no_activity_if_port_is_not_connected( } mocker.patch("psutil.net_connections").return_value = [connection] - assert ActiveConnection("foo", [10, self.MY_PORT, 30]).check() is None + assert ActiveConnection([10, self.MY_PORT, 30]).check() is None class TestCreate: def test_it_works_with_a_valid_config(self) -> None: assert ActiveConnection.create( - "name", config_section({"ports": "10,20,30"}) + config_section({"ports": "10,20,30"}) )._ports == {10, 20, 30} def test_raises_if_no_ports_are_configured(self) -> None: with pytest.raises(ConfigurationError): - ActiveConnection.create("name", config_section()) + ActiveConnection.create(config_section()) def test_raises_if_ports_are_not_numeric(self) -> None: with pytest.raises(ConfigurationError): - ActiveConnection.create("name", config_section({"ports": "10,20xx,30"})) + ActiveConnection.create(config_section({"ports": "10,20xx,30"})) class TestLoad(CheckTest): - def create_instance(self, name: str) -> Check: - return Load(name, 0.4) + def create_instance(self) -> Check: + return Load(0.4) def test_detects_no_activity_below_threshold(self, mocker: Any) -> None: threshold = 1.34 mocker.patch("os.getloadavg").return_value = [0, threshold - 0.2, 0] - assert Load("foo", threshold).check() is None + assert Load(threshold).check() is None def test_detects_activity_above_threshold(self, mocker: MockerFixture) -> None: threshold = 1.34 mocker.patch("os.getloadavg").return_value = [0, threshold + 0.2, 0] - assert Load("foo", threshold).check() is not None + assert Load(threshold).check() is not None class TestCreate: def test_it_works_with_a_valid_config(self) -> None: - assert ( - Load.create("name", config_section({"threshold": "3.2"}))._threshold - == 3.2 - ) + assert Load.create(config_section({"threshold": "3.2"}))._threshold == 3.2 def test_raises_if_threshold_is_not_numeric(self) -> None: with pytest.raises(ConfigurationError): - Load.create("name", config_section({"threshold": "narf"})) + Load.create(config_section({"threshold": "narf"})) class TestNetworkBandwidth(CheckTest): - def create_instance(self, name: str) -> Check: - return NetworkBandwidth(name, psutil.net_if_addrs().keys(), 0, 0) + def create_instance(self) -> Check: + return NetworkBandwidth(psutil.net_if_addrs().keys(), 0, 0) @staticmethod @pytest.fixture @@ -364,7 +356,7 @@ def serve_data_url(httpserver: HTTPServer) -> str: return httpserver.url_for("") def test_detects_non_mocked_activity(self, serve_data_url: str) -> None: - check = NetworkBandwidth("name", psutil.net_if_addrs().keys(), 0, 0) + check = NetworkBandwidth(psutil.net_if_addrs().keys(), 0, 0) # make some traffic requests.get(serve_data_url, timeout=5) assert check.check() is not None @@ -378,7 +370,6 @@ class TestCreate: @pytest.mark.usefixtures("_mock_interfaces") def test_it_works_with_a_valid_config(self) -> None: check = NetworkBandwidth.create( - "name", config_section( { "interfaces": "foo, baz", @@ -393,9 +384,7 @@ def test_it_works_with_a_valid_config(self) -> None: @pytest.mark.usefixtures("_mock_interfaces") def test_default_values_work(self) -> None: - check = NetworkBandwidth.create( - "name", config_section({"interfaces": "foo, baz"}) - ) + check = NetworkBandwidth.create(config_section({"interfaces": "foo, baz"})) assert set(check._interfaces) == {"foo", "baz"} assert check._threshold_send == 100 assert check._threshold_receive == 100 @@ -447,7 +436,7 @@ def test_raises_with_an_invalid_config( self, config: Mapping[str, str], error_match: str ) -> None: with pytest.raises(ConfigurationError, match=error_match): - NetworkBandwidth.create("name", config_section(config)) + NetworkBandwidth.create(config_section(config)) @pytest.mark.parametrize( ("send_threshold", "receive_threshold", "match"), @@ -461,7 +450,7 @@ def test_detects_activity_in_direction( serve_data_url: str, ) -> None: check = NetworkBandwidth( - "name", psutil.net_if_addrs().keys(), send_threshold, receive_threshold + psutil.net_if_addrs().keys(), send_threshold, receive_threshold ) # make some traffic requests.get(serve_data_url, timeout=5) @@ -471,7 +460,7 @@ def test_detects_activity_in_direction( def test_reports_no_activity_below_threshold(self, serve_data_url: str) -> None: check = NetworkBandwidth( - "name", psutil.net_if_addrs().keys(), sys.float_info.max, sys.float_info.max + psutil.net_if_addrs().keys(), sys.float_info.max, sys.float_info.max ) # make some traffic requests.get(serve_data_url, timeout=5) @@ -479,7 +468,7 @@ def test_reports_no_activity_below_threshold(self, serve_data_url: str) -> None: def test_internal_state_updating_works(self, serve_data_url: str) -> None: check = NetworkBandwidth( - "name", psutil.net_if_addrs().keys(), sys.float_info.max, sys.float_info.max + psutil.net_if_addrs().keys(), sys.float_info.max, sys.float_info.max ) check.check() old_state = check._previous_values @@ -496,7 +485,7 @@ def test_delta_calculation_send_work(self, mocker: MockerFixture) -> None: } with freeze_time("2019-10-01 10:00:00"): - check = NetworkBandwidth("name", ["eth0"], 0, sys.float_info.max) + check = NetworkBandwidth(["eth0"], 0, sys.float_info.max) second = mocker.MagicMock() type(second).bytes_sent = mocker.PropertyMock(return_value=1222) @@ -519,7 +508,7 @@ def test_delta_calculation_receive_work(self, mocker: MockerFixture) -> None: } with freeze_time("2019-10-01 10:00:00"): - check = NetworkBandwidth("name", ["eth0"], sys.float_info.max, 0) + check = NetworkBandwidth(["eth0"], sys.float_info.max, 0) second = mocker.MagicMock() type(second).bytes_sent = mocker.PropertyMock(return_value=1222) @@ -535,8 +524,8 @@ def test_delta_calculation_receive_work(self, mocker: MockerFixture) -> None: class TestPing(CheckTest): - def create_instance(self, name: str) -> Check: - return Ping(name, "8.8.8.8") + def create_instance(self) -> Check: + return Ping("8.8.8.8") def test_calls_ping_correctly(self, mocker: MockerFixture) -> None: mock = mocker.patch("subprocess.call") @@ -544,7 +533,7 @@ def test_calls_ping_correctly(self, mocker: MockerFixture) -> None: hosts = ["abc", "129.123.145.42"] - assert Ping("name", hosts).check() is None + assert Ping(hosts).check() is None assert mock.call_count == len(hosts) for (args, _), host in zip(mock.call_args_list, hosts): @@ -555,62 +544,62 @@ def test_raises_if_the_ping_binary_is_missing(self, mocker: MockerFixture) -> No mock.side_effect = FileNotFoundError() with pytest.raises(SevereCheckError): - Ping("name", ["test"]).check() + Ping(["test"]).check() def test_detect_activity_if_ping_succeeds(self, mocker: MockerFixture) -> None: mock = mocker.patch("subprocess.call") mock.return_value = 0 - assert Ping("name", ["foo"]).check() is not None + assert Ping(["foo"]).check() is not None class TestCreate: def test_raises_if_hosts_are_missing(self) -> None: with pytest.raises(ConfigurationError): - Ping.create("name", config_section()) + Ping.create(config_section()) def test_comma_separated_hosts_are_split(self) -> None: - ping = Ping.create("name", config_section({"hosts": "a,b,c"})) + ping = Ping.create(config_section({"hosts": "a,b,c"})) assert ping._hosts == ["a", "b", "c"] class TestFile(CheckTest): - def create_instance(self, name: str) -> Check: - return File(name, Path("asdf")) + def create_instance(self) -> Check: + return File(Path("asdf")) class TestCreate: def test_it_works_with_a_valid_config(self) -> None: - check = File.create("name", config_section({"path": "/tmp/test"})) + check = File.create(config_section({"path": "/tmp/test"})) assert check._path == Path("/tmp/test") def test_raises_without_path(self) -> None: with pytest.raises(ConfigurationError): - File.create("name", config_section()) + File.create(config_section()) def test_extracts_data_from_file(self, tmp_path: Path) -> None: test_file = tmp_path / "file" test_file.write_text("42\n\n") - assert File("name", test_file).check( - datetime.now(UTC) - ) == datetime.fromtimestamp(42, UTC) + assert File(test_file).check(datetime.now(UTC)) == datetime.fromtimestamp( + 42, UTC + ) def test_reports_no_wakeup_if_file_does_not_exist(self, tmp_path: Path) -> None: - assert File("name", tmp_path / "narf").check(datetime.now(UTC)) is None + assert File(tmp_path / "narf").check(datetime.now(UTC)) is None def test_raises_on_permissions_errors(self, tmp_path: Path) -> None: file_path = tmp_path / "test" file_path.write_bytes(b"2314898") file_path.chmod(0) with pytest.raises(TemporaryCheckError): - File("name", file_path).check(datetime.now(UTC)) + File(file_path).check(datetime.now(UTC)) def test_raises_on_io_errors(self, tmp_path: Path, mocker: MockerFixture) -> None: file_path = tmp_path / "test" file_path.write_bytes(b"2314898") mocker.patch("pathlib.Path.read_text").side_effect = IOError with pytest.raises(TemporaryCheckError): - File("name", file_path).check(datetime.now(UTC)) + File(file_path).check(datetime.now(UTC)) def test_raises_if_file_contents_are_not_a_timestamp(self, tmp_path: Path) -> None: test_file = tmp_path / "filexxx" test_file.write_text("nonumber\n\n") with pytest.raises(TemporaryCheckError): - File("name", test_file).check(datetime.now(UTC)) + File(test_file).check(datetime.now(UTC)) diff --git a/tests/test_checks_logs.py b/tests/test_checks_logs.py index acf33477..876a93c8 100644 --- a/tests/test_checks_logs.py +++ b/tests/test_checks_logs.py @@ -14,9 +14,8 @@ class TestLastLogActivity(CheckTest): - def create_instance(self, name: str) -> LastLogActivity: + def create_instance(self) -> LastLogActivity: return LastLogActivity( - name=name, log_file=Path("some_file"), pattern=re.compile("^(.*)$"), delta=timedelta(minutes=10), @@ -31,7 +30,6 @@ def test_is_active(self, tmpdir: Path) -> None: with freeze_time("2020-02-02 12:15:00"): assert ( LastLogActivity( - "test", file_path, re.compile(r"^(.*)$"), timedelta(minutes=10), @@ -48,7 +46,6 @@ def test_is_not_active(self, tmpdir: Path) -> None: with freeze_time("2020-02-02 12:35:00"): assert ( LastLogActivity( - "test", file_path, re.compile(r"^(.*)$"), timedelta(minutes=10), @@ -68,7 +65,6 @@ def test_uses_last_line(self, tmpdir: Path) -> None: with freeze_time("2020-02-02 12:15:00"): assert ( LastLogActivity( - "test", file_path, re.compile(r"^(.*)$"), timedelta(minutes=10), @@ -84,7 +80,6 @@ def test_ignores_lines_that_do_not_match(self, tmpdir: Path) -> None: assert ( LastLogActivity( - "test", file_path, re.compile(r"^foo(.*)$"), timedelta(minutes=10), @@ -101,7 +96,6 @@ def test_uses_pattern(self, tmpdir: Path) -> None: with freeze_time("2020-02-02 12:15:00"): assert ( LastLogActivity( - "test", file_path, re.compile(r"^foo(.*)bar$"), timedelta(minutes=10), @@ -119,7 +113,6 @@ def test_uses_given_timezone(self, tmpdir: Path) -> None: with freeze_time("2020-02-02 12:15:00"): assert ( LastLogActivity( - "test", file_path, re.compile(r"^(.*)$"), timedelta(minutes=10), @@ -137,7 +130,6 @@ def test_prefers_parsed_timezone(self, tmpdir: Path) -> None: with freeze_time("2020-02-02 13:15:00"): assert ( LastLogActivity( - "test", file_path, re.compile(r"^(.*)$"), timedelta(minutes=10), @@ -154,7 +146,6 @@ def test_fails_if_dates_cannot_be_parsed(self, tmpdir: Path) -> None: with pytest.raises(TemporaryCheckError): LastLogActivity( - "test", file_path, re.compile(r"^(.*)$"), timedelta(minutes=10), @@ -169,7 +160,6 @@ def test_fails_if_dates_are_in_the_future(self, tmpdir: Path) -> None: with freeze_time("2020-02-02 12:15:00"), pytest.raises(TemporaryCheckError): LastLogActivity( - "test", file_path, re.compile(r"^(.*)$"), timedelta(minutes=10), @@ -182,7 +172,6 @@ def test_fails_if_file_cannot_be_read(self, tmpdir: Path) -> None: with pytest.raises(TemporaryCheckError): LastLogActivity( - "test", file_path, re.compile(r"^(.*)$"), timedelta(minutes=10), @@ -192,7 +181,6 @@ def test_fails_if_file_cannot_be_read(self, tmpdir: Path) -> None: def test_create(self) -> None: created = LastLogActivity.create( - "thename", config_section( { "name": "somename", @@ -214,7 +202,6 @@ def test_create(self) -> None: def test_create_handles_pattern_errors(self) -> None: with pytest.raises(ConfigurationError): LastLogActivity.create( - "thename", config_section( { "name": "somename", @@ -227,7 +214,6 @@ def test_create_handles_pattern_errors(self) -> None: def test_create_handles_delta_errors(self) -> None: with pytest.raises(ConfigurationError): LastLogActivity.create( - "thename", config_section( { "name": "somename", @@ -241,7 +227,6 @@ def test_create_handles_delta_errors(self) -> None: def test_create_handles_negative_deltas(self) -> None: with pytest.raises(ConfigurationError): LastLogActivity.create( - "thename", config_section( { "name": "somename", @@ -255,7 +240,6 @@ def test_create_handles_negative_deltas(self) -> None: def test_create_handles_missing_pattern_groups(self) -> None: with pytest.raises(ConfigurationError): LastLogActivity.create( - "thename", config_section( { "name": "somename", @@ -268,7 +252,6 @@ def test_create_handles_missing_pattern_groups(self) -> None: def test_create_handles_missing_keys(self) -> None: with pytest.raises(ConfigurationError): LastLogActivity.create( - "thename", config_section( { "name": "somename", diff --git a/tests/test_checks_mpd.py b/tests/test_checks_mpd.py index eb5d94f1..375bb653 100644 --- a/tests/test_checks_mpd.py +++ b/tests/test_checks_mpd.py @@ -12,12 +12,12 @@ class TestMpd(CheckTest): - def create_instance(self, name: str) -> Check: + def create_instance(self) -> Check: # concrete values are never used in the tests - return Mpd(name, None, None, None) # type: ignore + return Mpd(None, None, None) # type: ignore def test_playing(self, monkeypatch: Any) -> None: - check = Mpd("test", None, None, None) # type: ignore + check = Mpd(None, None, None) # type: ignore def get_state() -> dict: return {"state": "play"} @@ -27,7 +27,7 @@ def get_state() -> dict: assert check.check() is not None def test_not_playing(self, monkeypatch: Any) -> None: - check = Mpd("test", None, None, None) # type: ignore + check = Mpd(None, None, None) # type: ignore def get_state() -> dict: return {"state": "pause"} @@ -48,7 +48,7 @@ def test_correct_mpd_interaction(self, mocker: MockerFixture) -> None: port = 42 timeout = 17 - assert Mpd("name", host, port, timeout).check() is not None + assert Mpd(host, port, timeout).check() is not None timeout_property.assert_called_once_with(timeout) mock_instance.connect.assert_called_once_with(host, port) @@ -58,7 +58,7 @@ def test_correct_mpd_interaction(self, mocker: MockerFixture) -> None: @pytest.mark.parametrize("exception_type", [ConnectionError, mpd.ConnectionError]) def test_handle_connection_errors(self, exception_type: type) -> None: - check = Mpd("test", None, None, None) # type: ignore + check = Mpd(None, None, None) # type: ignore def _get_state() -> dict: raise exception_type() @@ -71,7 +71,6 @@ def _get_state() -> dict: def test_create(self) -> None: check = Mpd.create( - "name", config_section( { "host": "host", @@ -88,7 +87,6 @@ def test_create(self) -> None: def test_create_port_no_number(self) -> None: with pytest.raises(ConfigurationError): Mpd.create( - "name", config_section( { "host": "host", @@ -101,7 +99,6 @@ def test_create_port_no_number(self) -> None: def test_create_timeout_no_number(self) -> None: with pytest.raises(ConfigurationError): Mpd.create( - "name", config_section( { "host": "host", diff --git a/tests/test_checks_smb.py b/tests/test_checks_smb.py index b3ebec50..f3bb3a51 100644 --- a/tests/test_checks_smb.py +++ b/tests/test_checks_smb.py @@ -11,22 +11,22 @@ class TestSmb(CheckTest): - def create_instance(self, name: str) -> Check: - return Smb(name) + def create_instance(self) -> Check: + return Smb() def test_no_connections(self, datadir: Path, mocker: MockerFixture) -> None: mocker.patch("subprocess.check_output").return_value = ( datadir / "smbstatus_no_connections" ).read_bytes() - assert Smb("foo").check() is None + assert Smb().check() is None def test_with_connections(self, datadir: Path, mocker: MockerFixture) -> None: mocker.patch("subprocess.check_output").return_value = ( datadir / "smbstatus_with_connections" ).read_bytes() - res = Smb("foo").check() + res = Smb().check() assert res is not None assert len(res.splitlines()) == 3 @@ -37,13 +37,13 @@ def test_call_error(self, mocker: MockerFixture) -> None: ) with pytest.raises(TemporaryCheckError): - Smb("foo").check() + Smb().check() def test_missing_executable(self, mocker: MockerFixture) -> None: mocker.patch("subprocess.check_output", side_effect=FileNotFoundError) with pytest.raises(SevereCheckError): - Smb("foo").check() + Smb().check() def test_create(self) -> None: - assert isinstance(Smb.create("name", None), Smb) + assert isinstance(Smb.create(None), Smb) diff --git a/tests/test_checks_stub.py b/tests/test_checks_stub.py index 506f389f..b192d4cd 100644 --- a/tests/test_checks_stub.py +++ b/tests/test_checks_stub.py @@ -10,37 +10,31 @@ class TestPeriodic(CheckTest): - def create_instance(self, name: str) -> Check: + def create_instance(self) -> Check: delta = timedelta(seconds=10, minutes=42) - return Periodic(name, delta) + return Periodic(delta) def test_create(self) -> None: - check = Periodic.create( - "name", config_section({"unit": "seconds", "value": "13"}) - ) + check = Periodic.create(config_section({"unit": "seconds", "value": "13"})) assert check._delta == timedelta(seconds=13) def test_create_wrong_unit(self) -> None: with pytest.raises(ConfigurationError): - Periodic.create("name", config_section({"unit": "asdfasdf", "value": "13"})) + Periodic.create(config_section({"unit": "asdfasdf", "value": "13"})) def test_create_not_numeric(self) -> None: with pytest.raises(ConfigurationError): - Periodic.create( - "name", config_section({"unit": "seconds", "value": "asdfasd"}) - ) + Periodic.create(config_section({"unit": "seconds", "value": "asdfasd"})) def test_create_no_unit(self) -> None: with pytest.raises(ConfigurationError): - Periodic.create("name", config_section({"value": "13"})) + Periodic.create(config_section({"value": "13"})) def test_create_float(self) -> None: - Periodic.create( - "name", config_section({"unit": "seconds", "value": "21312.12"}) - ) + Periodic.create(config_section({"unit": "seconds", "value": "21312.12"})) def test_check(self) -> None: delta = timedelta(seconds=10, minutes=42) - check = Periodic("test", delta) + check = Periodic(delta) now = datetime.now(UTC) assert check.check(now) == now + delta diff --git a/tests/test_checks_systemd.py b/tests/test_checks_systemd.py index 7cceb0cd..6b40c8f5 100644 --- a/tests/test_checks_systemd.py +++ b/tests/test_checks_systemd.py @@ -28,35 +28,35 @@ class TestSystemdTimer(CheckTest): def next_timer_executions(mocker: MockerFixture) -> Mock: return mocker.patch("autosuspend.checks.systemd.next_timer_executions") - def create_instance(self, name: str) -> Check: - return SystemdTimer(name, re.compile(".*")) + def create_instance(self) -> Check: + return SystemdTimer(re.compile(".*")) def test_create_handles_incorrect_expressions(self) -> None: with pytest.raises(ConfigurationError): - SystemdTimer.create("somename", config_section({"match": "(.*"})) + SystemdTimer.create(config_section({"match": "(.*"})) def test_create_raises_if_match_is_missing(self) -> None: with pytest.raises(ConfigurationError): - SystemdTimer.create("somename", config_section()) + SystemdTimer.create(config_section()) def test_works_without_timers(self, next_timer_executions: Mock) -> None: next_timer_executions.return_value = {} now = datetime.now(UTC) - assert SystemdTimer("foo", re.compile(".*")).check(now) is None + assert SystemdTimer(re.compile(".*")).check(now) is None def test_ignores_non_matching_timers(self, next_timer_executions: Mock) -> None: now = datetime.now(UTC) next_timer_executions.return_value = {"ignored": now} - assert SystemdTimer("foo", re.compile("needle")).check(now) is None + assert SystemdTimer(re.compile("needle")).check(now) is None def test_finds_matching_timers(self, next_timer_executions: Mock) -> None: pattern = "foo" now = datetime.now(UTC) next_timer_executions.return_value = {pattern: now} - assert SystemdTimer("foo", re.compile(pattern)).check(now) is now + assert SystemdTimer(re.compile(pattern)).check(now) is now def test_selects_the_closest_execution_if_multiple_match( self, next_timer_executions: Mock @@ -67,66 +67,58 @@ def test_selects_the_closest_execution_if_multiple_match( "matching": now, } - assert SystemdTimer("foo", re.compile(".*")).check(now) is now + assert SystemdTimer(re.compile(".*")).check(now) is now class TestLogindSessionsIdle(CheckTest): - def create_instance(self, name: str) -> Check: - return LogindSessionsIdle(name, ["tty", "x11", "wayland"], ["active", "online"]) + def create_instance(self) -> Check: + return LogindSessionsIdle(["tty", "x11", "wayland"], ["active", "online"]) def test_active(self, logind: ProxyObject) -> None: logind.AddSession("c1", "seat0", 1042, "auser", True) - check = LogindSessionsIdle("test", ["test"], ["active", "online"]) + check = LogindSessionsIdle(["test"], ["active", "online"]) assert check.check() is not None @pytest.mark.skip(reason="No known way to set idle hint in dbus mock right now") def test_inactive(self, logind: ProxyObject) -> None: logind.AddSession("c1", "seat0", 1042, "auser", False) - check = LogindSessionsIdle("test", ["test"], ["active", "online"]) + check = LogindSessionsIdle(["test"], ["active", "online"]) assert check.check() is None def test_ignore_unknow_type(self, logind: ProxyObject) -> None: logind.AddSession("c1", "seat0", 1042, "auser", True) - check = LogindSessionsIdle("test", ["not_test"], ["active", "online"]) + check = LogindSessionsIdle(["not_test"], ["active", "online"]) assert check.check() is None def test_ignore_unknown_class(self, logind: ProxyObject) -> None: logind.AddSession("c1", "seat0", 1042, "user", True) - check = LogindSessionsIdle( - "test", ["test"], ["active", "online"], ["nosuchclass"] - ) + check = LogindSessionsIdle(["test"], ["active", "online"], ["nosuchclass"]) assert check.check() is None def test_configure_defaults(self) -> None: - check = LogindSessionsIdle.create("name", config_section()) + check = LogindSessionsIdle.create(config_section()) assert check._types == ["tty", "x11", "wayland"] assert check._states == ["active", "online"] def test_configure_types(self) -> None: - check = LogindSessionsIdle.create( - "name", config_section({"types": "test, bla,foo"}) - ) + check = LogindSessionsIdle.create(config_section({"types": "test, bla,foo"})) assert check._types == ["test", "bla", "foo"] def test_configure_states(self) -> None: - check = LogindSessionsIdle.create( - "name", config_section({"states": "test, bla,foo"}) - ) + check = LogindSessionsIdle.create(config_section({"states": "test, bla,foo"})) assert check._states == ["test", "bla", "foo"] def test_configure_classes(self) -> None: - check = LogindSessionsIdle.create( - "name", config_section({"classes": "test, bla,foo"}) - ) + check = LogindSessionsIdle.create(config_section({"classes": "test, bla,foo"})) assert check._classes == ["test", "bla", "foo"] @pytest.mark.usefixtures("_logind_dbus_error") def test_dbus_error(self) -> None: - check = LogindSessionsIdle("test", ["test"], ["active", "online"]) + check = LogindSessionsIdle(["test"], ["active", "online"]) with pytest.raises(TemporaryCheckError): check.check() diff --git a/tests/test_checks_xorg.py b/tests/test_checks_xorg.py index 142c8a37..9564b8d5 100644 --- a/tests/test_checks_xorg.py +++ b/tests/test_checks_xorg.py @@ -118,12 +118,12 @@ def test_ignores_and_warns_on_invalid_display_numbers( class TestXIdleTime(CheckTest): - def create_instance(self, name: str) -> Check: + def create_instance(self) -> Check: # concrete values are never used in the test - return XIdleTime(name, 10, "sockets", None, None) # type: ignore + return XIdleTime(10, "sockets", None, None) # type: ignore def test_smoke(self, mocker: MockerFixture) -> None: - check = XIdleTime("name", 100, "logind", re.compile(r"a^"), re.compile(r"a^")) + check = XIdleTime(100, "logind", re.compile(r"a^"), re.compile(r"a^")) mocker.patch.object(check, "_provide_sessions").return_value = [ XorgSession(42, getuser()), ] @@ -141,7 +141,7 @@ def test_smoke(self, mocker: MockerFixture) -> None: assert getuser() in kwargs["env"]["XAUTHORITY"] def test_no_activity(self, mocker: MockerFixture) -> None: - check = XIdleTime("name", 100, "logind", re.compile(r"a^"), re.compile(r"a^")) + check = XIdleTime(100, "logind", re.compile(r"a^"), re.compile(r"a^")) mocker.patch.object(check, "_provide_sessions").return_value = [ XorgSession(42, getuser()), ] @@ -151,7 +151,7 @@ def test_no_activity(self, mocker: MockerFixture) -> None: assert check.check() is None def test_multiple_sessions(self, mocker: MockerFixture) -> None: - check = XIdleTime("name", 100, "logind", re.compile(r"a^"), re.compile(r"a^")) + check = XIdleTime(100, "logind", re.compile(r"a^"), re.compile(r"a^")) mocker.patch.object(check, "_provide_sessions").return_value = [ XorgSession(42, getuser()), XorgSession(17, "root"), @@ -175,7 +175,7 @@ def test_multiple_sessions(self, mocker: MockerFixture) -> None: assert "root" in kwargs["env"]["XAUTHORITY"] def test_handle_call_error(self, mocker: MockerFixture) -> None: - check = XIdleTime("name", 100, "logind", re.compile(r"a^"), re.compile(r"a^")) + check = XIdleTime(100, "logind", re.compile(r"a^"), re.compile(r"a^")) mocker.patch.object(check, "_provide_sessions").return_value = [ XorgSession(42, getuser()), ] @@ -188,7 +188,7 @@ def test_handle_call_error(self, mocker: MockerFixture) -> None: check.check() def test_create_default(self) -> None: - check = XIdleTime.create("name", config_section()) + check = XIdleTime.create(config_section()) assert check._timeout == 600 assert check._ignore_process_re == re.compile(r"a^") assert check._ignore_users_re == re.compile(r"a^") @@ -196,7 +196,6 @@ def test_create_default(self) -> None: def test_create(self) -> None: check = XIdleTime.create( - "name", config_section( { "timeout": "42", @@ -213,22 +212,22 @@ def test_create(self) -> None: def test_create_no_int(self) -> None: with pytest.raises(ConfigurationError): - XIdleTime.create("name", config_section({"timeout": "string"})) + XIdleTime.create(config_section({"timeout": "string"})) def test_create_broken_process_re(self) -> None: with pytest.raises(ConfigurationError): - XIdleTime.create("name", config_section({"ignore_if_process": "[[a-9]"})) + XIdleTime.create(config_section({"ignore_if_process": "[[a-9]"})) def test_create_broken_users_re(self) -> None: with pytest.raises(ConfigurationError): - XIdleTime.create("name", config_section({"ignore_users": "[[a-9]"})) + XIdleTime.create(config_section({"ignore_users": "[[a-9]"})) def test_create_unknown_method(self) -> None: with pytest.raises(ConfigurationError): - XIdleTime.create("name", config_section({"method": "asdfasdf"})) + XIdleTime.create(config_section({"method": "asdfasdf"})) def test_list_sessions_logind_dbus_error(self, mocker: MockerFixture) -> None: - check = XIdleTime.create("name", config_section()) + check = XIdleTime.create(config_section()) mocker.patch.object(check, "_provide_sessions").side_effect = ( LogindDBusException() ) @@ -237,7 +236,7 @@ def test_list_sessions_logind_dbus_error(self, mocker: MockerFixture) -> None: check._safe_provide_sessions() def test_sudo_not_found(self, mocker: MockerFixture) -> None: - check = XIdleTime("name", 100, "logind", re.compile(r"a^"), re.compile(r"a^")) + check = XIdleTime(100, "logind", re.compile(r"a^"), re.compile(r"a^")) mocker.patch.object(check, "_provide_sessions").return_value = [ XorgSession(42, getuser()), ] diff --git a/tests/test_checks_xpath.py b/tests/test_checks_xpath.py index 4444184e..b0935854 100644 --- a/tests/test_checks_xpath.py +++ b/tests/test_checks_xpath.py @@ -19,8 +19,8 @@ class _XPathMixinSub(XPathMixin, Activity): - def __init__(self, name: str, **kwargs: Any) -> None: - Activity.__init__(self, name) + def __init__(self, **kwargs: Any) -> None: + Activity.__init__(self) XPathMixin.__init__(self, **kwargs) def check(self) -> str | None: @@ -30,7 +30,6 @@ def check(self) -> str | None: class TestXPathMixin: def test_smoke(self, datadir: Path, serve_file: Callable[[Path], str]) -> None: result = _XPathMixinSub( - "foo", xpath="/b", url=serve_file(datadir / "xml_with_encoding.xml"), timeout=5, @@ -46,7 +45,7 @@ def test_broken_xml(self, mocker: MockerFixture) -> None: mocker.patch("requests.Session.get", return_value=mock_reply) with pytest.raises(TemporaryCheckError): - _XPathMixinSub("foo", xpath="/b", url="nourl", timeout=5).evaluate() + _XPathMixinSub(xpath="/b", url="nourl", timeout=5).evaluate() def test_xml_with_encoding(self, mocker: MockerFixture) -> None: mock_reply = mocker.MagicMock() @@ -57,12 +56,12 @@ def test_xml_with_encoding(self, mocker: MockerFixture) -> None: ) mocker.patch("requests.Session.get", return_value=mock_reply) - _XPathMixinSub("foo", xpath="/b", url="nourl", timeout=5).evaluate() + _XPathMixinSub(xpath="/b", url="nourl", timeout=5).evaluate() def test_xpath_prevalidation(self) -> None: with pytest.raises(ConfigurationError, match=r"^Invalid xpath.*"): _XPathMixinSub.create( - "name", config_section({"xpath": "|34/ad", "url": "required"}) + config_section({"xpath": "|34/ad", "url": "required"}) ) @pytest.mark.parametrize("entry", ["xpath", "url"]) @@ -70,12 +69,11 @@ def test_missing_config_entry(self, entry: str) -> None: section = config_section({"xpath": "/valid", "url": "required"}) del section[entry] with pytest.raises(ConfigurationError, match=r"^Lacks '" + entry + "'.*"): - _XPathMixinSub.create("name", section) + _XPathMixinSub.create(section) def test_invalid_config_entry(self) -> None: with pytest.raises(ConfigurationError, match=r"^Configuration error .*"): _XPathMixinSub.create( - "name", config_section( {"xpath": "/valid", "url": "required", "timeout": "xxx"} ), @@ -83,9 +81,8 @@ def test_invalid_config_entry(self) -> None: class TestXPathActivity(CheckTest): - def create_instance(self, name: str) -> Check: + def create_instance(self) -> Check: return XPathActivity( - name=name, url="url", timeout=5, username="userx", @@ -101,7 +98,7 @@ def test_matching(self, mocker: MockerFixture) -> None: mock_method = mocker.patch("requests.Session.get", return_value=mock_reply) url = "nourl" - assert XPathActivity("foo", xpath="/a", url=url, timeout=5).check() is not None + assert XPathActivity(xpath="/a", url=url, timeout=5).check() is not None mock_method.assert_called_once_with(url, timeout=5, headers=None) content_property.assert_called_once_with() @@ -113,11 +110,10 @@ def test_not_matching(self, mocker: MockerFixture) -> None: content_property.return_value = "" mocker.patch("requests.Session.get", return_value=mock_reply) - assert XPathActivity("foo", xpath="/b", url="nourl", timeout=5).check() is None + assert XPathActivity(xpath="/b", url="nourl", timeout=5).check() is None def test_create(self) -> None: check: XPathActivity = XPathActivity.create( - "name", config_section( { "url": "url", @@ -139,7 +135,6 @@ def test_network_errors_are_passed( ) -> None: with pytest.raises(TemporaryCheckError): XPathActivity( - name="name", url=serve_protected(datadir / "data.txt")[0], timeout=5, username="wrong", @@ -149,8 +144,8 @@ def test_network_errors_are_passed( class TestXPathWakeup(CheckTest): - def create_instance(self, name: str) -> Check: - return XPathWakeup(name, xpath="/a", url="nourl", timeout=5) + def create_instance(self) -> Check: + return XPathWakeup(xpath="/a", url="nourl", timeout=5) def test_matching(self, mocker: MockerFixture) -> None: mock_reply = mocker.MagicMock() @@ -160,7 +155,7 @@ def test_matching(self, mocker: MockerFixture) -> None: mock_method = mocker.patch("requests.Session.get", return_value=mock_reply) url = "nourl" - assert XPathWakeup("foo", xpath="/a/@value", url=url, timeout=5).check( + assert XPathWakeup(xpath="/a/@value", url=url, timeout=5).check( datetime.now(UTC) ) == datetime.fromtimestamp(42.3, UTC) @@ -175,9 +170,7 @@ def test_not_matching(self, mocker: MockerFixture) -> None: mocker.patch("requests.Session.get", return_value=mock_reply) assert ( - XPathWakeup("foo", xpath="/b", url="nourl", timeout=5).check( - datetime.now(UTC) - ) + XPathWakeup(xpath="/b", url="nourl", timeout=5).check(datetime.now(UTC)) is None ) @@ -189,9 +182,7 @@ def test_not_a_string(self, mocker: MockerFixture) -> None: mocker.patch("requests.Session.get", return_value=mock_reply) with pytest.raises(TemporaryCheckError): - XPathWakeup("foo", xpath="/a", url="nourl", timeout=5).check( - datetime.now(UTC) - ) + XPathWakeup(xpath="/a", url="nourl", timeout=5).check(datetime.now(UTC)) def test_not_a_number(self, mocker: MockerFixture) -> None: mock_reply = mocker.MagicMock() @@ -201,7 +192,7 @@ def test_not_a_number(self, mocker: MockerFixture) -> None: mocker.patch("requests.Session.get", return_value=mock_reply) with pytest.raises(TemporaryCheckError): - XPathWakeup("foo", xpath="/a/@value", url="nourl", timeout=5).check( + XPathWakeup(xpath="/a/@value", url="nourl", timeout=5).check( datetime.now(UTC) ) @@ -218,13 +209,12 @@ def test_multiple_min(self, mocker: MockerFixture) -> None: """ mocker.patch("requests.Session.get", return_value=mock_reply) - assert XPathWakeup("foo", xpath="//a/@value", url="nourl", timeout=5).check( + assert XPathWakeup(xpath="//a/@value", url="nourl", timeout=5).check( datetime.now(UTC) ) == datetime.fromtimestamp(10, UTC) def test_create(self) -> None: check: XPathWakeup = XPathWakeup.create( - "name", config_section( { "xpath": "/valid", @@ -237,8 +227,8 @@ def test_create(self) -> None: class TestXPathDeltaWakeup(CheckTest): - def create_instance(self, name: str) -> Check: - return XPathDeltaWakeup(name, xpath="/a", url="nourl", timeout=5, unit="days") + def create_instance(self) -> Check: + return XPathDeltaWakeup(xpath="/a", url="nourl", timeout=5, unit="days") @pytest.mark.parametrize( ("unit", "factor"), @@ -262,13 +252,12 @@ def test_smoke(self, mocker: MockerFixture, unit: str, factor: float) -> None: url = "nourl" now = datetime.now(UTC) result = XPathDeltaWakeup( - "foo", xpath="/a/@value", url=url, timeout=5, unit=unit + xpath="/a/@value", url=url, timeout=5, unit=unit ).check(now) assert result == now + timedelta(seconds=42) * factor def test_create(self) -> None: check = XPathDeltaWakeup.create( - "name", config_section( { "xpath": "/valid", @@ -283,7 +272,6 @@ def test_create(self) -> None: def test_create_wrong_unit(self) -> None: with pytest.raises(ConfigurationError): XPathDeltaWakeup.create( - "name", config_section( { "xpath": "/valid", @@ -296,6 +284,4 @@ def test_create_wrong_unit(self) -> None: def test_init_wrong_unit(self) -> None: with pytest.raises(ValueError, match=r".*unit.*"): - XPathDeltaWakeup( - "name", url="url", xpath="/a", timeout=5, unit="unknownunit" - ) + XPathDeltaWakeup(url="url", xpath="/a", timeout=5, unit="unknownunit")