From 37c3eaaededabf620152d7fd2aaa972e94b68aa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niko=20F=C3=B6hr?= Date: Fri, 13 Sep 2024 10:23:46 +0300 Subject: [PATCH] Change how platform support is defined --- docs/source/api-reference.md | 10 +- src/wakepy/__main__.py | 1 + src/wakepy/core/__init__.py | 3 +- src/wakepy/core/constants.py | 52 ++++++++-- src/wakepy/core/method.py | 75 ++++++++------- src/wakepy/core/platform.py | 94 +++++++++++++++++-- src/wakepy/methods/_testing.py | 4 +- src/wakepy/methods/freedesktop.py | 4 +- src/wakepy/methods/gnome.py | 4 +- src/wakepy/methods/macos.py | 4 +- src/wakepy/methods/windows.py | 4 +- tests/unit/test_core/conftest.py | 20 ++-- tests/unit/test_core/test_constants.py | 23 +++-- .../test_core/test_method/test_activation.py | 78 ++++++++------- .../unit/test_core/test_method/test_method.py | 26 ++++- tests/unit/test_core/test_mode.py | 4 +- tests/unit/test_core/test_platform.py | 72 ++++++++++++-- tests/unit/test_core/test_prioritization.py | 7 +- tests/unit/test_core/testmethods.py | 4 +- tests/unit/test_main.py | 6 +- 20 files changed, 358 insertions(+), 137 deletions(-) diff --git a/docs/source/api-reference.md b/docs/source/api-reference.md index 1678e203..cb28e48f 100644 --- a/docs/source/api-reference.md +++ b/docs/source/api-reference.md @@ -51,11 +51,19 @@ Wakepy Core .. autoclass:: wakepy.ModeExit :exclude-members: args, with_traceback -.. autoclass:: wakepy.core.constants.PlatformName +.. autoclass:: wakepy.core.constants.PlatformType :members: :undoc-members: :member-order: bysource +.. autoclass:: wakepy.core.constants.IdentifiedPlatformType + :members: + :undoc-members: + :member-order: bysource + +.. autodata:: wakepy.core.platform.CURRENT_PLATFORM + :no-value: + DBus Adapter ------------- DBus adapters are an advanced concept of wakepy. They would be used in such a case where wants to use other D-Bus python library than the default (which is `jeepney `_). diff --git a/src/wakepy/__main__.py b/src/wakepy/__main__.py index df3da857..9828b8a7 100644 --- a/src/wakepy/__main__.py +++ b/src/wakepy/__main__.py @@ -69,6 +69,7 @@ def handle_activation_error(result: ActivationResult) -> None: def _get_activation_error_text(result: ActivationResult) -> str: from wakepy import __version__ + # LATER: This should be improved in https://github.com/fohrloop/wakepy/issues/378 error_text = f""" Wakepy could not activate the "{result.mode_name}" mode. This might occur because of a bug or because your current platform is not yet supported or your system is missing required software. diff --git a/src/wakepy/core/__init__.py b/src/wakepy/core/__init__.py index d8965149..2d023807 100644 --- a/src/wakepy/core/__init__.py +++ b/src/wakepy/core/__init__.py @@ -7,8 +7,9 @@ from .activationresult import ActivationResult as ActivationResult from .activationresult import MethodActivationResult as MethodActivationResult from .constants import BusType as BusType +from .constants import IdentifiedPlatformType as IdentifiedPlatformType from .constants import ModeName as ModeName -from .constants import PlatformName as PlatformName +from .constants import PlatformType as PlatformType from .dbus import DBusAdapter as DBusAdapter from .dbus import DBusAddress as DBusAddress from .dbus import DBusMethod as DBusMethod diff --git a/src/wakepy/core/constants.py b/src/wakepy/core/constants.py index 50563b89..7d2fd697 100644 --- a/src/wakepy/core/constants.py +++ b/src/wakepy/core/constants.py @@ -22,22 +22,58 @@ """ -class PlatformName(StrEnum): - """All the different platforms wakepy knows about. Any platform that is not - detected will be named ``OTHER``.""" +class IdentifiedPlatformType(StrEnum): + """The type identifier for the (:attr:`~wakepy.core.platform.CURRENT_PLATFORM`). + Any process will be categorized into exactly one + :class:`IdentifiedPlatformType` type; these are mutually exclusive options. + Any platform that is not detected will be labeled as ``UNKNOWN``. - WINDOWS = auto() + See also: :class:`PlatformType`, which you should use with Method + subclasses.""" # noqa: W505 + WINDOWS = auto() LINUX = auto() - MACOS = auto() + FREEBSD = auto() + UNKNOWN = auto() + + +class PlatformType(StrEnum): + """Enumeration for supported platform types. Each identified platform can + be categorized to at least one, but potentially many of these. In other + words, each :class:`IdentifiedPlatformType` (type of + :attr:`~wakepy.core.platform.CURRENT_PLATFORM`) maps into one or many + ``PlatformType``. + + To be used in subclasses of :class:`~wakepy.Method` in the + :meth:`~wakepy.Method.supported_platforms`. + """ + + WINDOWS = IdentifiedPlatformType.WINDOWS.value + """Any Windows version from Windows 10 onwards.""" + + LINUX = IdentifiedPlatformType.LINUX.value + """Includes any Linux distro. Excludes things like Android & ChromeOS""" + + MACOS = IdentifiedPlatformType.MACOS.value """Mac OS (Darwin)""" - OTHER = auto() - """Anything else""" + FREEBSD = IdentifiedPlatformType.FREEBSD.value + """FreeBSD. Also includes GhostBSD""" + + UNKNOWN = IdentifiedPlatformType.UNKNOWN.value + """Any non-identified platform""" + + BSD = auto() + """Any BSD system (Currently just FreeBSD / GhostBSD, but is likely to + change in the future).""" + UNIX_LIKE_FOSS = auto() + """Unix-like desktop environment, but FOSS. Includes: LINUX and BSD. + Excludes: Android (mobile), MacOS (non-FOSS), ChromeOS (non-FOSS).""" -PlatformNameValue = Literal["WINDOWS", "LINUX", "MACOS", "OTHER"] + ANY = auto() + """Means any platform.""" class ModeName(StrEnum): diff --git a/src/wakepy/core/method.py b/src/wakepy/core/method.py index 7932e1ec..32df6412 100644 --- a/src/wakepy/core/method.py +++ b/src/wakepy/core/method.py @@ -14,9 +14,9 @@ from typing import Type, cast from .activationresult import MethodActivationResult -from .constants import PlatformName, StageName +from .constants import PlatformType, StageName from .heartbeat import Heartbeat -from .platform import CURRENT_PLATFORM +from .platform import CURRENT_PLATFORM, get_platform_supported from .registry import register_method from .strenum import StrEnum, auto @@ -31,7 +31,7 @@ from wakepy.core import DBusAdapter, DBusMethodCall - from .constants import ModeName, PlatformName + from .constants import ModeName, PlatformType MethodCls = Type["Method"] @@ -69,20 +69,26 @@ class Method(ABC): defines the Mode `foo` (:class:`Mode` classes are themselves not defined or registered anywhere)""" - supported_platforms: Tuple[PlatformName, ...] = ( - PlatformName.LINUX, - PlatformName.WINDOWS, - PlatformName.MACOS, - PlatformName.OTHER, - ) - """Lists the platforms the Method supports. If a platform is not listed in - ``method.supported_platforms``, the ``method`` is not going to be used on - the platform (when used as part of a :class:`Mode`), and the Method - activation result will show a fail in the "PLATFORM" stage. + supported_platforms: Tuple[PlatformType, ...] = (PlatformType.ANY,) + """Lists the platforms the Method supports. If the current platform is not + part of any of the platform types listed in ``method.supported_platforms``, + the ``method`` is not* going to be used (when used as part of a + :class:`Mode`), and the Method activation result will show a fail in the + "PLATFORM" stage. + + When subclassing the ``Method``, defining ``supported_platforms`` reduces + some work required when writing the logic for :meth:`caniuse`. + Additionally, it aids in distinguishing the "PLATFORM" stage fail as a + separate type of failure. - When subclassing, defining ``supported_platforms`` reduces some work - required when writing the logic for :meth:`caniuse`. Additionally, it aids - in distinguishing the "PLATFORM" stage fail as a separate type of failure. + As an example, if the Method would support all Unix-like FOSS desktop + operating systems, the supported_platforms would be + ``(PlatformType.UNIX_LIKE_FOSS, )``. See the + :class:`~wakepy.core.constants.PlatformType` for all options. + + \*unless the current platform is unidentified (``UNKNOWN``). In this case, + platform check does not fail (nor succeed), and the activation process + continues normally. Default: Support all platforms. """ @@ -114,6 +120,7 @@ def __init__(self, **kwargs: object) -> None: # waits for https://github.com/fohrloop/wakepy/issues/256 # self.method_kwargs = kwargs + _check_supported_platforms(self.supported_platforms, self.__class__.__name__) def __init_subclass__(cls, **kwargs: object) -> None: register_method(cls) @@ -285,6 +292,22 @@ def is_unnamed(cls) -> bool: return cls.name == unnamed +def _check_supported_platforms( + supported_platforms: Tuple[PlatformType, ...], classname: str +) -> None: + err_supported_platforms = ( + f"The supported_platforms of {classname} must be a tuple of PlatformType!" + ) + + if not isinstance(supported_platforms, tuple): + raise ValueError(err_supported_platforms) + for p in supported_platforms: + if not isinstance(p, PlatformType): + raise ValueError( + err_supported_platforms + f' One item ({p}) is of type "{type(p)}"' + ) + + def activate_method(method: Method) -> Tuple[MethodActivationResult, Heartbeat | None]: """Activates a mode defined by a single Method. @@ -303,7 +326,7 @@ def activate_method(method: Method) -> Tuple[MethodActivationResult, Heartbeat | success=False, method_name=method.name, mode_name=method.mode_name ) - if not get_platform_supported(method, platform=CURRENT_PLATFORM): + if get_platform_supported(CURRENT_PLATFORM, method.supported_platforms) is False: result.failure_stage = StageName.PLATFORM_SUPPORT return result, None @@ -372,24 +395,6 @@ def deactivate_method(method: Method, heartbeat: Optional[Heartbeat] = None) -> method.dbus_adapter.close_connections() -def get_platform_supported(method: Method, platform: PlatformName) -> bool: - """Checks if method is supported by the platform - - Parameters - ---------- - method: Method - The method which platform support to check. - platform: - The platform to check against. - - Returns - ------- - is_supported: bool - If True, the platform is supported. Otherwise, False. - """ - return platform in method.supported_platforms - - def caniuse_fails(method: Method) -> tuple[bool, str]: """Check if the requirements of a Method are met or not. diff --git a/src/wakepy/core/platform.py b/src/wakepy/core/platform.py index 3e4759f5..aa3f852b 100644 --- a/src/wakepy/core/platform.py +++ b/src/wakepy/core/platform.py @@ -1,23 +1,103 @@ +from __future__ import annotations + import platform +import typing import warnings -from .constants import PlatformName +from .constants import IdentifiedPlatformType, PlatformType + +if typing.TYPE_CHECKING: + from typing import Callable, Tuple + + PlatformFunc = Callable[[IdentifiedPlatformType], bool] -def get_current_platform() -> PlatformName: +def get_current_platform() -> IdentifiedPlatformType: # Ref: https://docs.python.org/3/library/platform.html#platform.system system = platform.system() if system == "Windows": - return PlatformName.WINDOWS + return IdentifiedPlatformType.WINDOWS elif system == "Darwin": - return PlatformName.MACOS + return IdentifiedPlatformType.MACOS elif system == "Linux": - return PlatformName.LINUX + return IdentifiedPlatformType.LINUX + elif system == "FreeBSD": + return IdentifiedPlatformType.FREEBSD + # LATER: This should be improved in https://github.com/fohrloop/wakepy/issues/378 warnings.warn( f"Could not detect current platform! platform.system() returned {system}" ) - return PlatformName.OTHER + return IdentifiedPlatformType.UNKNOWN + + +CURRENT_PLATFORM: IdentifiedPlatformType = get_current_platform() +"""The current platform as detected. If the platform cannot be detected, +defaults to ``UNKNOWN``.""" + + +def get_platform_supported( + platform: IdentifiedPlatformType, supported_platforms: Tuple[PlatformType, ...] +) -> bool | None: + """Checks if a platform is in the supported platforms. + + Parameters + ---------- + platform: IdentifiedPlatformType + The platform to check. + supported_platforms: + The platforms to check against. + + Returns + ------- + is_supported: bool | None + If platform is supported, returns True. If the support is unknown, + returns None, and if the platform is not supported, returns False. + """ + for supported_platform in supported_platforms: + func = PLATFORM_INFO_FUNCS[supported_platform] + if func(platform) is True: + return True + if is_unknown(platform): + return None + return False + + +def is_windows(current_platform: IdentifiedPlatformType) -> bool: + return current_platform == IdentifiedPlatformType.WINDOWS + + +def is_linux(current_platform: IdentifiedPlatformType) -> bool: + return current_platform == IdentifiedPlatformType.LINUX + + +def is_freebsd(current_platform: IdentifiedPlatformType) -> bool: + return current_platform == IdentifiedPlatformType.FREEBSD + + +def is_macos(current_platform: IdentifiedPlatformType) -> bool: + return current_platform == IdentifiedPlatformType.MACOS + + +def is_bsd(current_platform: IdentifiedPlatformType) -> bool: + return is_freebsd(current_platform) + + +def is_unknown(current_platform: IdentifiedPlatformType) -> bool: + return current_platform == IdentifiedPlatformType.UNKNOWN + + +def is_unix_like_foss(current_platform: IdentifiedPlatformType) -> bool: + return is_bsd(current_platform) or is_linux(current_platform) -CURRENT_PLATFORM = get_current_platform() +PLATFORM_INFO_FUNCS: dict[PlatformType, PlatformFunc] = { + PlatformType.WINDOWS: is_windows, + PlatformType.LINUX: is_linux, + PlatformType.MACOS: is_macos, + PlatformType.FREEBSD: is_freebsd, + PlatformType.UNKNOWN: is_unknown, + PlatformType.BSD: is_bsd, + PlatformType.ANY: lambda _: True, + PlatformType.UNIX_LIKE_FOSS: is_unix_like_foss, +} diff --git a/src/wakepy/methods/_testing.py b/src/wakepy/methods/_testing.py index 2689ca5e..9d022787 100644 --- a/src/wakepy/methods/_testing.py +++ b/src/wakepy/methods/_testing.py @@ -2,7 +2,7 @@ activation success. It is controlled with the WAKEPY_FAKE_SUCCESS environment variable and meant to be used in CI pipelines / tests.""" -from wakepy.core import CURRENT_PLATFORM, Method +from wakepy.core import Method, PlatformType from wakepy.core.constants import WAKEPY_FAKE_SUCCESS @@ -16,7 +16,7 @@ class WakepyFakeSuccess(Method): name = WAKEPY_FAKE_SUCCESS mode_name = "_fake" - supported_platforms = (CURRENT_PLATFORM,) + supported_platforms = (PlatformType.ANY,) def enter_mode(self) -> None: """Does nothing ("succeeds" automatically; Will never raise an diff --git a/src/wakepy/methods/freedesktop.py b/src/wakepy/methods/freedesktop.py index a3803dab..2abbb62e 100644 --- a/src/wakepy/methods/freedesktop.py +++ b/src/wakepy/methods/freedesktop.py @@ -14,7 +14,7 @@ DBusMethodCall, Method, ModeName, - PlatformName, + PlatformType, ) if typing.TYPE_CHECKING: @@ -41,7 +41,7 @@ class FreedesktopInhibitorWithCookieMethod(Method): """Base class for freedesktop.org D-Bus based methods.""" service_dbus_address: DBusAddress - supported_platforms = (PlatformName.LINUX,) + supported_platforms = (PlatformType.LINUX,) def __init__(self, **kwargs: object) -> None: super().__init__(**kwargs) diff --git a/src/wakepy/methods/gnome.py b/src/wakepy/methods/gnome.py index 8ce3fe3a..fd7085a5 100644 --- a/src/wakepy/methods/gnome.py +++ b/src/wakepy/methods/gnome.py @@ -11,7 +11,7 @@ DBusMethodCall, Method, ModeName, - PlatformName, + PlatformType, ) if typing.TYPE_CHECKING: @@ -56,7 +56,7 @@ class _GnomeSessionManager(Method, ABC): params=("inhibit_cookie",), ).of(session_manager) - supported_platforms = (PlatformName.LINUX,) + supported_platforms = (PlatformType.LINUX,) @property @abstractmethod diff --git a/src/wakepy/methods/macos.py b/src/wakepy/methods/macos.py index 63cfbe39..31b3baa0 100644 --- a/src/wakepy/methods/macos.py +++ b/src/wakepy/methods/macos.py @@ -5,7 +5,7 @@ from abc import ABC, abstractmethod from subprocess import PIPE, Popen -from wakepy.core import Method, ModeName, PlatformName +from wakepy.core import Method, ModeName, PlatformType if typing.TYPE_CHECKING: from typing import Optional @@ -18,7 +18,7 @@ class _MacCaffeinate(Method, ABC): Also: https://web.archive.org/web/20140604153141/https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man8/caffeinate.8.html """ - supported_platforms = (PlatformName.MACOS,) + supported_platforms = (PlatformType.MACOS,) def __init__(self, **kwargs: object) -> None: super().__init__(**kwargs) diff --git a/src/wakepy/methods/windows.py b/src/wakepy/methods/windows.py index caf42bb3..844a06c6 100644 --- a/src/wakepy/methods/windows.py +++ b/src/wakepy/methods/windows.py @@ -9,7 +9,7 @@ from queue import Queue from threading import Event, Thread -from wakepy.core import Method, ModeName, PlatformName +from wakepy.core import Method, ModeName, PlatformType logger = logging.getLogger(__name__) @@ -34,7 +34,7 @@ class WindowsSetThreadExecutionState(Method, ABC): # The SetThreadExecutionState docs say that it supports Windows XP and # above (client) or Windows Server 2003 and above (server) - supported_platforms = (PlatformName.WINDOWS,) + supported_platforms = (PlatformType.WINDOWS,) _wait_timeout = 5 # seconds """timeout for calls like queue.get() and thread.join() which could otherwise block execution indefinitely.""" diff --git a/tests/unit/test_core/conftest.py b/tests/unit/test_core/conftest.py index 0b8c7907..9783d83a 100644 --- a/tests/unit/test_core/conftest.py +++ b/tests/unit/test_core/conftest.py @@ -1,7 +1,7 @@ import pytest from tests.unit.test_core.testmethods import TestMethod -from wakepy.core import DBusAddress, DBusMethod, Method, PlatformName +from wakepy.core import DBusAddress, DBusMethod, Method, PlatformType from wakepy.core.heartbeat import Heartbeat @@ -28,34 +28,34 @@ def provide_methods_different_platforms(monkeypatch, testutils): class WindowsA(TestMethod): name = "WinA" - supported_platforms = (PlatformName.WINDOWS,) + supported_platforms = (PlatformType.WINDOWS,) class WindowsB(TestMethod): name = "WinB" - supported_platforms = (PlatformName.WINDOWS,) + supported_platforms = (PlatformType.WINDOWS,) class WindowsC(TestMethod): name = "WinC" - supported_platforms = (PlatformName.WINDOWS,) + supported_platforms = (PlatformType.WINDOWS,) class LinuxA(TestMethod): name = "LinuxA" - supported_platforms = (PlatformName.LINUX,) + supported_platforms = (PlatformType.LINUX,) class LinuxB(TestMethod): name = "LinuxB" - supported_platforms = (PlatformName.LINUX,) + supported_platforms = (PlatformType.LINUX,) class LinuxC(TestMethod): name = "LinuxC" - supported_platforms = (PlatformName.LINUX,) + supported_platforms = (PlatformType.LINUX,) class MultiPlatformA(TestMethod): name = "multiA" supported_platforms = ( - PlatformName.LINUX, - PlatformName.WINDOWS, - PlatformName.MACOS, + PlatformType.LINUX, + PlatformType.WINDOWS, + PlatformType.MACOS, ) diff --git a/tests/unit/test_core/test_constants.py b/tests/unit/test_core/test_constants.py index 4c7c912d..572b0ba3 100644 --- a/tests/unit/test_core/test_constants.py +++ b/tests/unit/test_core/test_constants.py @@ -1,9 +1,10 @@ -from wakepy.core import BusType, ModeName, PlatformName -from wakepy.core.constants import BusTypeValue, ModeNameValue, PlatformNameValue - - -def test_platformname(assert_strenum_values): - assert_strenum_values(PlatformName, PlatformNameValue) +from wakepy.core import BusType, ModeName +from wakepy.core.constants import ( + BusTypeValue, + IdentifiedPlatformType, + ModeNameValue, + PlatformType, +) def test_mode_name(assert_strenum_values): @@ -12,3 +13,13 @@ def test_mode_name(assert_strenum_values): def test_bustype(assert_strenum_values): assert_strenum_values(BusType, BusTypeValue) + + +def test_platform_types_in_sync(): + """Test that each IdentifiedPlatformType is also in PlatformType: anything + that can be detected can also be selected by the Method sublasses in + supported_platforms.""" + + identified = {member.value for member in IdentifiedPlatformType} + selectable = {member.value for member in PlatformType} + assert not (identified - selectable) diff --git a/tests/unit/test_core/test_method/test_activation.py b/tests/unit/test_core/test_method/test_activation.py index 1c1f4009..518d6d93 100644 --- a/tests/unit/test_core/test_method/test_activation.py +++ b/tests/unit/test_core/test_method/test_activation.py @@ -5,6 +5,7 @@ import datetime as dt import re +from unittest.mock import patch import pytest import time_machine @@ -17,23 +18,23 @@ combinations_of_test_methods, get_test_method_class, ) -from wakepy.core import Method, MethodActivationResult, PlatformName, get_methods -from wakepy.core.constants import StageName, StageNameValue +from wakepy.core import Method, MethodActivationResult, PlatformType +from wakepy.core.constants import IdentifiedPlatformType, StageName, StageNameValue from wakepy.core.heartbeat import Heartbeat from wakepy.core.method import ( activate_method, caniuse_fails, deactivate_method, - get_platform_supported, try_enter_and_heartbeat, ) -from wakepy.core.platform import CURRENT_PLATFORM + +P = IdentifiedPlatformType class TestActivateMethod: """tests for activate_method""" - def test_activate_method_method_without_name(self): + def test_method_without_name(self): """Methods used for activation must have a name. If not, there should be a ValueError raised""" @@ -46,25 +47,45 @@ def test_activate_method_method_without_name(self): ): activate_method(method) - def test_activate_method_method_without_platform_support(self): + @patch("wakepy.core.method.CURRENT_PLATFORM", IdentifiedPlatformType.WINDOWS) + def test_method_without_platform_support(self): UnsupportedMethod = get_test_method_class( - supported_platforms=( - PlatformName.WINDOWS - if CURRENT_PLATFORM != PlatformName.WINDOWS - else PlatformName.LINUX - ), + supported_platforms=(PlatformType.LINUX,), ) unsupported_method = UnsupportedMethod() - # The current platform is set to linux, so method supporting only linux - # should fail. + # The supported_platform is LINUX and CURRENT_PLATFORM is set to + # WINDOWS so this must fail. res, heartbeat = activate_method(unsupported_method) assert res.failure_stage == StageName.PLATFORM_SUPPORT assert res.success is False assert heartbeat is None - def test_activate_method_method_caniuse_fails(self): + def test_with_unknown_platform_support_any(self): + SupportedMethod = get_test_method_class( + supported_platforms=(PlatformType.ANY,), caniuse=True, enter_mode=None + ) + unsupported_method = SupportedMethod() + + res, _ = activate_method(unsupported_method) + assert res.success is True + + @patch("wakepy.core.method.CURRENT_PLATFORM", IdentifiedPlatformType.UNKNOWN) + def test_with_unknown_platform_support_just_linux(self): + # This is otherwise supported method, so it works also on the UNKNOWN + # system. Only the platform support check should return None ("I don't + # know"), but the rest of the activation process should proceed and the + # activation should succeed. + SupportedMethod = get_test_method_class( + supported_platforms=(PlatformType.LINUX,), caniuse=True, enter_mode=None + ) + unsupported_method = SupportedMethod() + + res, _ = activate_method(unsupported_method) + assert res.success is True + + def test_method_caniuse_fails(self): # Case 1: Fail by returning False from caniuse method = get_test_method_class(caniuse=False)() res, heartbeat = activate_method(method) @@ -83,7 +104,7 @@ def test_activate_method_method_caniuse_fails(self): assert res.failure_reason == "SomeSW version <2.1.5 not supported" assert heartbeat is None - def test_activate_method_method_enter_mode_fails(self): + def test_method_enter_mode_fails(self): # Case: Fail by returning False from enter_mode method = get_test_method_class( caniuse=True, enter_mode=RuntimeError("failed") @@ -94,7 +115,7 @@ def test_activate_method_method_enter_mode_fails(self): assert "RuntimeError('failed')" in res.failure_reason assert heartbeat is None - def test_activate_method_enter_mode_success(self): + def test_enter_mode_success(self): method = get_test_method_class(caniuse=True, enter_mode=None)() res, heartbeat = activate_method(method) assert res.success is True @@ -103,7 +124,7 @@ def test_activate_method_enter_mode_success(self): # No heartbeat on success, as the used Method does not have heartbeat() assert heartbeat is None - def test_activate_method_heartbeat_success(self): + def test_heartbeat_success(self): method = get_test_method_class(heartbeat=None)() res, heartbeat = activate_method(method) assert res.success is True @@ -323,29 +344,6 @@ def test_heartbeat_returns_bad_balue(self): assert heartbeat_call_time is None -class TestPlatformSupported: - """tests for get_platform_supported""" - - @pytest.mark.usefixtures("provide_methods_different_platforms") - def test_get_platform_supported(self): - WindowsA, LinuxA, MultiPlatformA = get_methods(["WinA", "LinuxA", "multiA"]) - - # The windows method is only supported on windows - assert get_platform_supported(WindowsA(), PlatformName.WINDOWS) - assert not get_platform_supported(WindowsA(), PlatformName.LINUX) - assert not get_platform_supported(WindowsA(), PlatformName.MACOS) - - # The linux method is only supported on linux - assert get_platform_supported(LinuxA(), PlatformName.LINUX) - assert not get_platform_supported(LinuxA(), PlatformName.WINDOWS) - assert not get_platform_supported(LinuxA(), PlatformName.MACOS) - - # Case: Method that supports linux, windows and macOS - assert get_platform_supported(MultiPlatformA(), PlatformName.LINUX) - assert get_platform_supported(MultiPlatformA(), PlatformName.WINDOWS) - assert get_platform_supported(MultiPlatformA(), PlatformName.MACOS) - - class TestCanIUseFails: """test caniuse_fails""" diff --git a/tests/unit/test_core/test_method/test_method.py b/tests/unit/test_core/test_method/test_method.py index 67358e7b..b7b99a09 100644 --- a/tests/unit/test_core/test_method/test_method.py +++ b/tests/unit/test_core/test_method/test_method.py @@ -6,12 +6,12 @@ import pytest from tests.unit.test_core.testmethods import TestMethod -from wakepy.core import DBusMethodCall -from wakepy.core.constants import PlatformName +from wakepy.core import DBusMethodCall, PlatformType from wakepy.core.method import ( Method, MethodOutcome, MethodOutcomeValue, + _check_supported_platforms, has_enter, has_exit, has_heartbeat, @@ -122,7 +122,7 @@ def test_method_defaults(): assert m.exit_mode() is None # type: ignore # By default, all platforms are supported - assert set(m.supported_platforms) == set(PlatformName) + assert set(m.supported_platforms) == set((PlatformType.ANY,)) @pytest.mark.usefixtures("provide_methods_a_f") @@ -147,3 +147,23 @@ def test_process_dbus_call(dbus_method: DBusMethod): def test_methodoutcome(assert_strenum_values): assert_strenum_values(MethodOutcome, MethodOutcomeValue) + + +class TestCheckSupportedPlatforms: + def test_wrong_type_of_supported_platforms(self): + + with pytest.raises( + ValueError, + match="The supported_platforms of someclass must be a tuple of PlatformType!", # noqa: E501 + ): + _check_supported_platforms("x", "someclass") # type: ignore + + def test_wrong_type_of_a_single_platform(self): + supported_platforms = (PlatformType.UNIX_LIKE_FOSS, "foo", PlatformType.WINDOWS) + with pytest.raises( + ValueError, + match=re.escape( + "The supported_platforms of someclass must be a tuple of PlatformType! One item (foo) is of type \"\"" # noqa: E501 + ), + ): + _check_supported_platforms(supported_platforms, "someclass") # type: ignore diff --git a/tests/unit/test_core/test_mode.py b/tests/unit/test_core/test_mode.py index b771929b..10d73ec3 100644 --- a/tests/unit/test_core/test_mode.py +++ b/tests/unit/test_core/test_mode.py @@ -10,6 +10,7 @@ from tests.unit.test_core.testmethods import get_test_method_class from wakepy import ActivationError, ActivationResult, Method, Mode +from wakepy.core import PlatformType from wakepy.core.activationresult import MethodActivationResult from wakepy.core.constants import WAKEPY_FAKE_SUCCESS, StageName from wakepy.core.dbus import DBusAdapter @@ -21,7 +22,6 @@ select_methods, should_fake_success, ) -from wakepy.core.platform import CURRENT_PLATFORM from wakepy.core.registry import get_method, get_methods if typing.TYPE_CHECKING: @@ -48,7 +48,7 @@ def methods_abc(monkeypatch, testutils) -> List[Type[Method]]: testutils.empty_method_registry(monkeypatch) class TestMethod(Method): - supported_platforms = (CURRENT_PLATFORM,) + supported_platforms = (PlatformType.ANY,) class MethodA(TestMethod): name = "MethodA" diff --git a/tests/unit/test_core/test_platform.py b/tests/unit/test_core/test_platform.py index 4dd9251e..40d77c73 100644 --- a/tests/unit/test_core/test_platform.py +++ b/tests/unit/test_core/test_platform.py @@ -2,25 +2,85 @@ import pytest -from wakepy.core import PlatformName -from wakepy.core.platform import get_current_platform +from wakepy.core import PlatformType +from wakepy.core.constants import IdentifiedPlatformType +from wakepy.core.platform import get_current_platform, get_platform_supported + +P = IdentifiedPlatformType class TestGetCurrentPlatform: @patch("platform.system", lambda: "Windows") def test_windows(self): - assert get_current_platform() == PlatformName.WINDOWS + assert get_current_platform() == PlatformType.WINDOWS @patch("platform.system", lambda: "Darwin") def test_macos(self): - assert get_current_platform() == PlatformName.MACOS + assert get_current_platform() == PlatformType.MACOS @patch("platform.system", lambda: "Linux") def test_linux(self): - assert get_current_platform() == PlatformName.LINUX + assert get_current_platform() == PlatformType.LINUX + + @patch("platform.system", lambda: "FreeBSD") + def test_bsd(self): + assert get_current_platform() == PlatformType.FREEBSD @patch("platform.system", lambda: "This does not exist") def test_other(self): with pytest.warns(UserWarning, match="Could not detect current platform!"): - assert get_current_platform() == PlatformName.OTHER + assert get_current_platform() == PlatformType.UNKNOWN + + +class TestPlatformSupported: + """tests for get_platform_supported""" + + def test_windows(self): + + # On Windows, anything that supports Windows is supported. + assert get_platform_supported(P.WINDOWS, (PlatformType.WINDOWS,)) is True + assert ( + get_platform_supported( + P.WINDOWS, + (PlatformType.MACOS, PlatformType.WINDOWS, PlatformType.LINUX), + ) + is True + ) + + # If there is no windows in the supported platforms, get False + assert get_platform_supported(P.WINDOWS, (PlatformType.LINUX,)) is False + assert ( + get_platform_supported(P.WINDOWS, (PlatformType.LINUX, PlatformType.BSD)) + is False + ) + # Unless there is ANY, which means anything is supported + assert ( + get_platform_supported( + P.WINDOWS, (PlatformType.LINUX, PlatformType.BSD, PlatformType.ANY) + ) + is True + ) + + def test_unknown(self): + # Unknown platform is always "unknown"; returns None + assert get_platform_supported(P.UNKNOWN, (PlatformType.WINDOWS,)) is None + assert get_platform_supported(P.UNKNOWN, (PlatformType.LINUX,)) is None + # .. unless "ANY" is supported. + assert get_platform_supported(P.UNKNOWN, (PlatformType.ANY,)) is True + + def test_freebsd(self): + + assert get_platform_supported(P.FREEBSD, (PlatformType.WINDOWS,)) is False + assert get_platform_supported(P.FREEBSD, (PlatformType.FREEBSD,)) is True + # FreeBSD is BSD + assert get_platform_supported(P.FREEBSD, (PlatformType.BSD,)) is True + # FreeBSD is unix like + assert get_platform_supported(P.FREEBSD, (PlatformType.UNIX_LIKE_FOSS,)) is True + + def test_linux(self): + + assert get_platform_supported(P.LINUX, (PlatformType.WINDOWS,)) is False + assert get_platform_supported(P.LINUX, (PlatformType.LINUX,)) is True + # Linux is unix like + assert get_platform_supported(P.LINUX, (PlatformType.UNIX_LIKE_FOSS,)) is True diff --git a/tests/unit/test_core/test_prioritization.py b/tests/unit/test_core/test_prioritization.py index e05a4e36..6c37b208 100644 --- a/tests/unit/test_core/test_prioritization.py +++ b/tests/unit/test_core/test_prioritization.py @@ -5,7 +5,7 @@ import pytest -from wakepy.core import PlatformName +from wakepy.core import PlatformType from wakepy.core.constants import WAKEPY_FAKE_SUCCESS from wakepy.core.prioritization import ( _check_methods_priority, @@ -25,7 +25,7 @@ def set_current_platform_to_linux(monkeypatch): monkeypatch.setattr( - "wakepy.core.prioritization.CURRENT_PLATFORM", PlatformName.LINUX + "wakepy.core.prioritization.CURRENT_PLATFORM", PlatformType.LINUX ) @@ -33,7 +33,8 @@ def set_current_platform_to_linux(monkeypatch): def set_current_platform_to_windows(monkeypatch): monkeypatch.setattr( - "wakepy.core.prioritization.CURRENT_PLATFORM", PlatformName.WINDOWS + "wakepy.core.prioritization.CURRENT_PLATFORM", + PlatformType.WINDOWS, ) diff --git a/tests/unit/test_core/testmethods.py b/tests/unit/test_core/testmethods.py index 71817f8e..8f6469ec 100644 --- a/tests/unit/test_core/testmethods.py +++ b/tests/unit/test_core/testmethods.py @@ -38,7 +38,7 @@ from collections import Counter from typing import Iterable, Type -from wakepy.core import CURRENT_PLATFORM +from wakepy.core import PlatformType from wakepy.core.method import Method @@ -82,7 +82,7 @@ def get_test_method_class( enter_mode=METHOD_MISSING, heartbeat=METHOD_MISSING, exit_mode=METHOD_MISSING, - supported_platforms=(CURRENT_PLATFORM,), + supported_platforms=(PlatformType.ANY,), ) -> Type[Method]: """Get a test Method class with the .caniuse(), .enter_mode(), .heartbeat() and .exit_mode() methods defined as wanted. All methods can either be: diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index 8d844147..f56082c3 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -14,7 +14,7 @@ parse_arguments, wait_until_keyboardinterrupt, ) -from wakepy.core import CURRENT_PLATFORM +from wakepy.core import PlatformType from wakepy.core.constants import ModeName @@ -36,7 +36,7 @@ class WorkingMethod(Method): name = "method1" mode_name = mode_name_working - supported_platforms = (CURRENT_PLATFORM,) + supported_platforms = (PlatformType.ANY,) def enter_mode(self) -> None: return @@ -52,7 +52,7 @@ class BrokenMethod(Method): name = "method2_broken" mode_name = mode_name_broken - supported_platforms = (CURRENT_PLATFORM,) + supported_platforms = (PlatformType.ANY,) def enter_mode(self) -> None: raise RuntimeError("foo")