From bc5d6b60de3c3b80882a7682b53f83e51448a1eb Mon Sep 17 00:00:00 2001 From: Sassan Haradji Date: Thu, 25 Jul 2024 14:56:49 +0400 Subject: [PATCH] refactor(core): use python-fake for faking --- CHANGELOG.md | 1 + poetry.lock | 84 +++++----- pyproject.toml | 3 +- .../store-desktop-000.jsonc | 4 +- .../all_services_register/store-rpi-000.jsonc | 8 +- tests/monkeypatch.py | 63 ++++---- tests/setup.sh | 30 ++-- ubo_app/display.py | 3 +- ubo_app/menu_app/menu_central.py | 3 +- ubo_app/services/040-camera/setup.py | 6 - ubo_app/services/050-lightdm/setup.py | 4 +- ubo_app/services/050-ssh/setup.py | 4 +- ubo_app/services/080-docker/reducer.py | 4 +- ubo_app/setup.py | 56 ++++--- ubo_app/store/main.py | 2 +- ubo_app/system/system_manager/led.py | 2 +- ubo_app/utils/fake.py | 143 ------------------ 17 files changed, 151 insertions(+), 269 deletions(-) delete mode 100644 ubo_app/utils/fake.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 031f8fef..bb945a20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - feat(core): long-pressing the reset button for 3 seconds or more reboots the device - closes #116 - fix(keypad): keypad becoming unresponsive if a key was pressed while the app was loading - closes #118 - fix(camera): closing the camera viewfinder will close the picamera instance so that it can be used again +- refactor(core): use python-fake for faking ## Version 0.15.4 diff --git a/poetry.lock b/poetry.lock index 6ddf31bd..61a7bbe4 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2,13 +2,13 @@ [[package]] name = "adafruit-blinka" -version = "8.46.0" +version = "8.46.1" description = "CircuitPython APIs for non-CircuitPython versions of Python such as CPython on Linux and MicroPython." optional = false python-versions = ">=3.7.0" files = [ - {file = "Adafruit_Blinka-8.46.0-py3-none-any.whl", hash = "sha256:cfd750a6d2df137fa3c35c0f53c23e7af8cbe8f614d387f908c180cfba954b8e"}, - {file = "adafruit_blinka-8.46.0.tar.gz", hash = "sha256:f2d4d10aa17e13e5e6bd2212b62c00d0e76c767a0f80f3c858d77bd6ab1d4251"}, + {file = "Adafruit_Blinka-8.46.1-py3-none-any.whl", hash = "sha256:15fc177a2cacea551bb0c342ae55bf2fd041bcf43bb7ced46dadabf5259dbf54"}, + {file = "adafruit_blinka-8.46.1.tar.gz", hash = "sha256:2236b46cb2a26833ba371f80f539ba655900dfd92983fc57c85a62b5f4ef8f47"}, ] [package.dependencies] @@ -16,7 +16,6 @@ adafruit-circuitpython-typing = "*" Adafruit-PlatformDetect = ">=3.70.1" Adafruit-PureIO = ">=1.1.7" binho-host-adapter = ">=0.1.6" -numpy = ">=1.21.5" pyftdi = ">=0.40.0" sysv-ipc = {version = ">=1.1.0", markers = "sys_platform == \"linux\" and platform_machine != \"mips\""} @@ -1327,13 +1326,13 @@ files = [ [[package]] name = "pure-eval" -version = "0.2.2" +version = "0.2.3" description = "Safely evaluate AST nodes without side effects" optional = false python-versions = "*" files = [ - {file = "pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"}, - {file = "pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"}, + {file = "pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0"}, + {file = "pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42"}, ] [package.extras] @@ -1444,13 +1443,13 @@ files = [ [[package]] name = "pyright" -version = "1.1.372" +version = "1.1.373" description = "Command line wrapper for pyright" optional = false python-versions = ">=3.7" files = [ - {file = "pyright-1.1.372-py3-none-any.whl", hash = "sha256:25b15fb8967740f0949fd35b963777187f0a0404c0bd753cc966ec139f3eaa0b"}, - {file = "pyright-1.1.372.tar.gz", hash = "sha256:a9f5e0daa955daaa17e3d1ef76d3623e75f8afd5e37b437d3ff84d5b38c15420"}, + {file = "pyright-1.1.373-py3-none-any.whl", hash = "sha256:b805413227f2c209f27b14b55da27fe5e9fb84129c9f1eb27708a5d12f6f000e"}, + {file = "pyright-1.1.373.tar.gz", hash = "sha256:f41bcfc8b9d1802b09921a394d6ae1ce19694957b628bc657629688daf8a83ff"}, ] [package.dependencies] @@ -1476,20 +1475,20 @@ cp2110 = ["hidapi"] [[package]] name = "pytest" -version = "8.2.2" +version = "8.3.2" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.2.2-py3-none-any.whl", hash = "sha256:c434598117762e2bd304e526244f67bf66bbd7b5d6cf22138be51ff661980343"}, - {file = "pytest-8.2.2.tar.gz", hash = "sha256:de4bb8104e201939ccdc688b27a89a7be2079b22e2bd2b07f806b6ba71117977"}, + {file = "pytest-8.3.2-py3-none-any.whl", hash = "sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5"}, + {file = "pytest-8.3.2.tar.gz", hash = "sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce"}, ] [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} iniconfig = "*" packaging = "*" -pluggy = ">=1.5,<2.0" +pluggy = ">=1.5,<2" [package.extras] dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] @@ -1609,6 +1608,17 @@ files = [ [package.extras] cli = ["click (>=5.0)"] +[[package]] +name = "python-fake" +version = "0.1.0" +description = "A general purpose mock library for Python" +optional = false +python-versions = "<4.0,>=3.9" +files = [ + {file = "python_fake-0.1.0-py3-none-any.whl", hash = "sha256:841a918668ee455def2dfc9c7b3a644c56e5a681918a49dc2cccb7c0d5e12e2c"}, + {file = "python_fake-0.1.0.tar.gz", hash = "sha256:fe1f9badf87762987130195bce25dfffcd3ec8389251c1355fb6fbaeff770c44"}, +] + [[package]] name = "python-immutable" version = "1.1.1" @@ -1772,29 +1782,29 @@ files = [ [[package]] name = "ruff" -version = "0.5.3" +version = "0.5.4" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.5.3-py3-none-linux_armv6l.whl", hash = "sha256:b12424d9db7347fa63c5ed9af010003338c63c629fb9c9c6adb2aa4f5699729b"}, - {file = "ruff-0.5.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b8d72c5684bbd4ed304a9a955ee2e67f57b35f6193222ade910cca8a805490e3"}, - {file = "ruff-0.5.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d2fc2cdb85ccac1e816cc9d5d8cedefd93661bd957756d902543af32a6b04a71"}, - {file = "ruff-0.5.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf4bc751240b2fab5d19254571bcacb315c7b0b00bf3c912d52226a82bbec073"}, - {file = "ruff-0.5.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bc697ec874fdd7c7ba0a85ec76ab38f8595224868d67f097c5ffc21136e72fcd"}, - {file = "ruff-0.5.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e791d34d3557a3819b3704bc1f087293c821083fa206812842fa363f6018a192"}, - {file = "ruff-0.5.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:76bb5a87fd397520b91a83eae8a2f7985236d42dd9459f09eef58e7f5c1d8316"}, - {file = "ruff-0.5.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a8cfc7a26422c78e94f1ec78ec02501bbad2df5834907e75afe474cc6b83a8c1"}, - {file = "ruff-0.5.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96066c4328a49fce2dd40e80f7117987369feec30ab771516cf95f1cc2db923c"}, - {file = "ruff-0.5.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03bfe9ab5bdc0b08470c3b261643ad54ea86edc32b64d1e080892d7953add3ad"}, - {file = "ruff-0.5.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:7704582a026fa02cca83efd76671a98ee6eb412c4230209efe5e2a006c06db62"}, - {file = "ruff-0.5.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:08058d077e21b856d32ebf483443390e29dc44d927608dc8f092ff6776519da9"}, - {file = "ruff-0.5.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:77d49484429ed7c7e6e2e75a753f153b7b58f875bdb4158ad85af166a1ec1822"}, - {file = "ruff-0.5.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:642cbff6cbfa38d2566d8db086508d6f472edb136cbfcc4ea65997745368c29e"}, - {file = "ruff-0.5.3-py3-none-win32.whl", hash = "sha256:eafc45dd8bdc37a00b28e68cc038daf3ca8c233d73fea276dcd09defb1352841"}, - {file = "ruff-0.5.3-py3-none-win_amd64.whl", hash = "sha256:cbaec2ddf4f78e5e9ecf5456ea0f496991358a1d883862ed0b9e947e2b6aea93"}, - {file = "ruff-0.5.3-py3-none-win_arm64.whl", hash = "sha256:05fbd2cb404775d6cd7f2ff49504e2d20e13ef95fa203bd1ab22413af70d420b"}, - {file = "ruff-0.5.3.tar.gz", hash = "sha256:2a3eb4f1841771fa5b67a56be9c2d16fd3cc88e378bd86aaeaec2f7e6bcdd0a2"}, + {file = "ruff-0.5.4-py3-none-linux_armv6l.whl", hash = "sha256:82acef724fc639699b4d3177ed5cc14c2a5aacd92edd578a9e846d5b5ec18ddf"}, + {file = "ruff-0.5.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:da62e87637c8838b325e65beee485f71eb36202ce8e3cdbc24b9fcb8b99a37be"}, + {file = "ruff-0.5.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e98ad088edfe2f3b85a925ee96da652028f093d6b9b56b76fc242d8abb8e2059"}, + {file = "ruff-0.5.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c55efbecc3152d614cfe6c2247a3054cfe358cefbf794f8c79c8575456efe19"}, + {file = "ruff-0.5.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f9b85eaa1f653abd0a70603b8b7008d9e00c9fa1bbd0bf40dad3f0c0bdd06793"}, + {file = "ruff-0.5.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0cf497a47751be8c883059c4613ba2f50dd06ec672692de2811f039432875278"}, + {file = "ruff-0.5.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:09c14ed6a72af9ccc8d2e313d7acf7037f0faff43cde4b507e66f14e812e37f7"}, + {file = "ruff-0.5.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:628f6b8f97b8bad2490240aa84f3e68f390e13fabc9af5c0d3b96b485921cd60"}, + {file = "ruff-0.5.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3520a00c0563d7a7a7c324ad7e2cde2355733dafa9592c671fb2e9e3cd8194c1"}, + {file = "ruff-0.5.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93789f14ca2244fb91ed481456f6d0bb8af1f75a330e133b67d08f06ad85b516"}, + {file = "ruff-0.5.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:029454e2824eafa25b9df46882f7f7844d36fd8ce51c1b7f6d97e2615a57bbcc"}, + {file = "ruff-0.5.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:9492320eed573a13a0bc09a2957f17aa733fff9ce5bf00e66e6d4a88ec33813f"}, + {file = "ruff-0.5.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a6e1f62a92c645e2919b65c02e79d1f61e78a58eddaebca6c23659e7c7cb4ac7"}, + {file = "ruff-0.5.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:768fa9208df2bec4b2ce61dbc7c2ddd6b1be9fb48f1f8d3b78b3332c7d71c1ff"}, + {file = "ruff-0.5.4-py3-none-win32.whl", hash = "sha256:e1e7393e9c56128e870b233c82ceb42164966f25b30f68acbb24ed69ce9c3a4e"}, + {file = "ruff-0.5.4-py3-none-win_amd64.whl", hash = "sha256:58b54459221fd3f661a7329f177f091eb35cf7a603f01d9eb3eb11cc348d38c4"}, + {file = "ruff-0.5.4-py3-none-win_arm64.whl", hash = "sha256:bd53da65f1085fb5b307c38fd3c0829e76acf7b2a912d8d79cadcdb4875c1eb7"}, + {file = "ruff-0.5.4.tar.gz", hash = "sha256:2795726d5f71c4f4e70653273d1c23a8182f07dd8e48c12de5d867bfb7557eed"}, ] [[package]] @@ -1989,13 +1999,13 @@ files = [ [[package]] name = "ubo-gui" -version = "0.12.2" +version = "0.12.3a1" description = "GUI sdk for Ubo Pod" optional = false python-versions = "<4.0,>=3.11" files = [ - {file = "ubo_gui-0.12.2-py3-none-any.whl", hash = "sha256:2f9085ba79ab5c9b57822ea5ce681cd1c8ec8b42f7e050fd7678839b331c7493"}, - {file = "ubo_gui-0.12.2.tar.gz", hash = "sha256:1777fb070708c0134b69fc80f287c0a2f7f63744f87fb8b432d1f13b40ab0eff"}, + {file = "ubo_gui-0.12.3a1-py3-none-any.whl", hash = "sha256:7c114bfdc26ee12da9fd2c5ab9630636b7098b19bd77a4877139d312f35f2f2c"}, + {file = "ubo_gui-0.12.3a1.tar.gz", hash = "sha256:a8e3d576476ba2775662c7e4b94d391e492784af7ab9a65adac7c5246d44f468"}, ] [package.dependencies] @@ -2141,4 +2151,4 @@ dev = ["headless-kivy", "headless-kivy"] [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "81229fe3a1f7b096ff2c518c9274ca76c0e0b397fca046e73c501c038999c37e" +content-hash = "73d8d6ab57518e053125ba9fdb3240a62c6640589acd1f66f53540b4187de94c" diff --git a/pyproject.toml b/pyproject.toml index 5dd75332..ea1d572a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,7 +17,7 @@ priority = "primary" [tool.poetry.dependencies] python = "^3.11" psutil = "^6.0.0" -ubo-gui = "^0.12.2" +ubo-gui = "^0.12.3a1" headless-kivy = [ { version = "^0.9.4", markers = "extra=='default'", extras = [ "default", @@ -49,6 +49,7 @@ adafruit-circuitpython-pct2075 = "^1.1.21" adafruit-circuitpython-veml7700 = "^1.1.22" rpi-lgpio = { version = "^0.6", markers = "platform_machine=='aarch64'" } +python-fake = "^0.1.0" [tool.poetry.group.dev] optional = true diff --git a/tests/integration/results/test_services/all_services_register/store-desktop-000.jsonc b/tests/integration/results/test_services/all_services_register/store-desktop-000.jsonc index 18985ae4..71d5f91a 100644 --- a/tests/integration/results/test_services/all_services_register/store-desktop-000.jsonc +++ b/tests/integration/results/test_services/all_services_register/store-desktop-000.jsonc @@ -75,7 +75,7 @@ }, "lightdm": { "is_active": true, - "is_enabled": false + "is_enabled": true }, "main": { "menu": { @@ -599,7 +599,7 @@ }, "ssh": { "is_active": true, - "is_enabled": false + "is_enabled": true }, "status_icons": { "icons": [ diff --git a/tests/integration/results/test_services/all_services_register/store-rpi-000.jsonc b/tests/integration/results/test_services/all_services_register/store-rpi-000.jsonc index d40d91ec..eaed82b5 100644 --- a/tests/integration/results/test_services/all_services_register/store-rpi-000.jsonc +++ b/tests/integration/results/test_services/all_services_register/store-rpi-000.jsonc @@ -74,7 +74,7 @@ "is_connected": true }, "lightdm": { - "is_active": false, + "is_active": true, "is_enabled": true }, "main": { @@ -350,7 +350,7 @@ 1, 1 ], - "icon": "[color=#ffff00]󰝦[/color]", + "icon": "[color=#008000]󰪥[/color]", "is_short": false, "label": "LightDM", "opacity": null, @@ -385,7 +385,7 @@ 1, 1 ], - "icon": "[color=#ffff00]󰝦[/color]", + "icon": "[color=#008000]󰪥[/color]", "is_short": false, "label": "SSH", "opacity": null, @@ -598,7 +598,7 @@ "playback_volume": 0.5 }, "ssh": { - "is_active": false, + "is_active": true, "is_enabled": true }, "status_icons": { diff --git a/tests/monkeypatch.py b/tests/monkeypatch.py index c976e883..72b56286 100644 --- a/tests/monkeypatch.py +++ b/tests/monkeypatch.py @@ -13,7 +13,7 @@ def _monkeypatch_socket(monkeypatch: pytest.MonkeyPatch) -> None: import socket - from ubo_app.utils.fake import Fake + from fake import Fake monkeypatch.setattr(socket, 'gethostname', lambda: 'test-hostname') @@ -63,7 +63,7 @@ def _monkeypatch_psutil(monkeypatch: pytest.MonkeyPatch) -> None: def _monkeypatch_docker(monkeypatch: pytest.MonkeyPatch) -> None: - from ubo_app.utils.fake import Fake + from fake import Fake monkeypatch.setattr( 'docker.from_env', @@ -84,7 +84,7 @@ def now(cls: type[DateTime], tz: datetime.tzinfo | None = None) -> DateTime: def _monkeypatch_uuid(monkeypatch: pytest.MonkeyPatch) -> None: - from ubo_app.utils.fake import Fake + from fake import Fake counter = 0 @@ -121,7 +121,7 @@ def debug_uuid4() -> Fake: def _monkeypatch_rpi_modules() -> None: - from ubo_app.utils.fake import Fake + from fake import Fake class FakeSensor(Fake): lux = 0.0 @@ -137,7 +137,7 @@ class FakeSensorModule(Fake): def _monkeypatch_aiohttp() -> None: - from ubo_app.utils.fake import Fake + from fake import Fake class FakeUpdateResponse(Fake): async def json(self: FakeUpdateResponse) -> dict[str, object]: @@ -165,7 +165,7 @@ def _monkeypatch_subprocess(monkeypatch: pytest.MonkeyPatch) -> None: import subprocess from pathlib import Path - from ubo_app.utils.fake import Fake + from fake import Fake original_subprocess_run = subprocess.run @@ -202,7 +202,7 @@ def fake_subprocess_run( def _monkeypatch_asyncio_subprocess(monkeypatch: pytest.MonkeyPatch) -> None: import asyncio - from ubo_app.utils.fake import Fake + from fake import Fake class FakeAsyncProcess(Fake): def __init__(self: FakeAsyncProcess, output: bytes = b'') -> None: @@ -212,41 +212,37 @@ async def communicate(self: FakeAsyncProcess) -> tuple[bytes, bytes]: return cast(bytes, self.output), b'' async def fake_create_subprocess_exec( - command: str, - *args: list[str], - **kwargs: object, - ) -> FakeAsyncProcess: + *_args: str, + **kwargs: Any, # noqa: ANN401 + ) -> object: _ = kwargs - if command == '/usr/bin/env' and args == ('systemctl', 'is-enabled', 'ssh'): - return FakeAsyncProcess(output=b'enabled') - if command == '/usr/bin/env' and args == ('systemctl', 'is-active', 'ssh'): - return FakeAsyncProcess(output=b'active') - if command == '/usr/bin/env' and args == ('systemctl', 'is-enabled', 'lightdm'): - return FakeAsyncProcess(output=b'enabled') - if command == '/usr/bin/env' and args == ('systemctl', 'is-active', 'lightdm'): - return FakeAsyncProcess(output=b'active') - if command == '/usr/bin/env' and args == ('which', 'docker'): + command = _args[0] + args = _args[1:] + + if command == '/usr/bin/env': + command = args[0] + args = args[1:] + + if command == 'systemctl': + if args[0] == 'is-enabled': + return FakeAsyncProcess(output=b'enabled') + if args[0] == 'is-active': + return FakeAsyncProcess(output=b'active') + if command == 'which' and args[0] == 'docker': return FakeAsyncProcess(output=b'/bin/docker') - if command == '/usr/bin/env' and args[0] == 'pulseaudio': + if command == 'pulseaudio': return FakeAsyncProcess() - msg = ( - 'Unexpected `asyncio.create_subprocess_exec` command in test ' - f'environment: {command} - {args}' - ) - raise ValueError(msg) + return await original_asyncio_create_subprocess_exec(*_args, **kwargs) + + original_asyncio_create_subprocess_exec = asyncio.create_subprocess_exec monkeypatch.setattr(asyncio, 'create_subprocess_exec', fake_create_subprocess_exec) - monkeypatch.setattr( - asyncio, - 'open_unix_connection', - Fake(_Fake__return_value=Fake(_Fake__await_value=(Fake(), Fake()))), - ) def _monkeypatch_asyncio_socket(monkeypatch: pytest.MonkeyPatch) -> None: import asyncio - from ubo_app.utils.fake import Fake + from fake import Fake monkeypatch.setattr(asyncio, 'open_connection', Fake()) @@ -260,9 +256,10 @@ def _monkeypatch(monkeypatch: pytest.MonkeyPatch) -> None: import atexit import importlib.metadata + from fake import Fake + import ubo_app.constants import ubo_app.utils.serializer - from ubo_app.utils.fake import Fake tracemalloc.start() diff --git a/tests/setup.sh b/tests/setup.sh index c3f92c8e..9a3c4d37 100644 --- a/tests/setup.sh +++ b/tests/setup.sh @@ -1,13 +1,25 @@ #!/bin/bash -# Disconnect all active connections -nmcli connection show --active | grep wifi | awk '{print $1}' | while read conn; do - nmcli connection down "$conn" -done +IS_RPI=false -# Delete all Wi-Fi connections -nmcli connection show | grep wifi | awk '{print $1}' | while read conn; do - nmcli connection delete "$conn" -done +# Check if the script is running on a Raspberry Pi +if [ -e /etc/os-release ]; then + source /etc/os-release + if [[ $ID == "raspbian" ]]; then + IS_RPI=true + fi +fi -echo "All Wi-Fi connections have been disconnected and deleted." +if [ "$IS_RPI" = true ]; then + # Disconnect all active connections + nmcli connection show --active | grep wifi | awk '{print $1}' | while read conn; do + nmcli connection down "$conn" + done + + # Delete all Wi-Fi connections + nmcli connection show | grep wifi | awk '{print $1}' | while read conn; do + nmcli connection delete "$conn" + done + + echo "All Wi-Fi connections have been disconnected and deleted." +fi diff --git a/ubo_app/display.py b/ubo_app/display.py index 0865a0b7..f41eadf9 100644 --- a/ubo_app/display.py +++ b/ubo_app/display.py @@ -14,9 +14,10 @@ from numpy._typing import NDArray +from fake import Fake + from ubo_app.constants import BYTES_PER_PIXEL from ubo_app.utils import IS_RPI -from ubo_app.utils.fake import Fake if IS_RPI: import board diff --git a/ubo_app/menu_app/menu_central.py b/ubo_app/menu_app/menu_central.py index e5eb87d5..7b27799c 100644 --- a/ubo_app/menu_app/menu_central.py +++ b/ubo_app/menu_app/menu_central.py @@ -68,11 +68,12 @@ def __init__(self: MenuAppCentral, **kwargs: object) -> None: @autorun(lambda state: state.main.menu) @debounce(0.1, DebounceOptions(leading=True, trailing=True, time_window=0.1)) + @mainthread def _(menu: Menu | None) -> None: self = _self() if not self or not menu: return - mainthread(self.menu_widget.set_root_menu)(menu) + self.menu_widget.set_root_menu(menu) def build(self: UboApp) -> Widget | None: root = super().build() diff --git a/ubo_app/services/040-camera/setup.py b/ubo_app/services/040-camera/setup.py index fd20f279..ec200e49 100644 --- a/ubo_app/services/040-camera/setup.py +++ b/ubo_app/services/040-camera/setup.py @@ -26,16 +26,10 @@ ) from ubo_app.utils import IS_RPI from ubo_app.utils.async_ import create_task -from ubo_app.utils.fake import Fake if TYPE_CHECKING: from numpy._typing import NDArray -if not IS_RPI: - import sys - - sys.modules['picamera2.picamera2'] = Fake() - from picamera2.picamera2 import Picamera2 THROTTL_TIME = 0.5 diff --git a/ubo_app/services/050-lightdm/setup.py b/ubo_app/services/050-lightdm/setup.py index becf5bb1..5bb3cfe8 100644 --- a/ubo_app/services/050-lightdm/setup.py +++ b/ubo_app/services/050-lightdm/setup.py @@ -108,9 +108,9 @@ def lightdm_title(_: LightDMState) -> str: async def check_is_lightdm_active() -> None: """Check if the LightDM service is active.""" if await is_unit_active('lightdm'): - dispatch(LightDMUpdateStateAction(is_enabled=True)) + dispatch(LightDMUpdateStateAction(is_active=True)) else: - dispatch(LightDMUpdateStateAction(is_enabled=False)) + dispatch(LightDMUpdateStateAction(is_active=False)) async def check_is_lightdm_enabled() -> None: diff --git a/ubo_app/services/050-ssh/setup.py b/ubo_app/services/050-ssh/setup.py index 6d547361..fbd4ee19 100644 --- a/ubo_app/services/050-ssh/setup.py +++ b/ubo_app/services/050-ssh/setup.py @@ -239,9 +239,9 @@ def ssh_title(_: SSHState) -> str: async def check_is_ssh_active() -> None: """Check if the SSH service is active.""" if await is_unit_active('ssh'): - dispatch(SSHUpdateStateAction(is_enabled=True)) + dispatch(SSHUpdateStateAction(is_active=True)) else: - dispatch(SSHUpdateStateAction(is_enabled=False)) + dispatch(SSHUpdateStateAction(is_active=False)) async def check_is_ssh_enabled() -> None: diff --git a/ubo_app/services/080-docker/reducer.py b/ubo_app/services/080-docker/reducer.py index 49866f6e..73c92d94 100644 --- a/ubo_app/services/080-docker/reducer.py +++ b/ubo_app/services/080-docker/reducer.py @@ -81,7 +81,9 @@ class ImageEntry(Immutable): path: str registry: str dependencies: list[str] | None = None - ports: dict[str, int | None] = field(default_factory=dict) + ports: dict[str, int | list[int] | tuple[str, int] | None] = field( + default_factory=dict, + ) hosts: dict[str, str] = field(default_factory=dict) note: str | None = None environment_vairables: ( diff --git a/ubo_app/setup.py b/ubo_app/setup.py index 126d2ffc..53886c68 100644 --- a/ubo_app/setup.py +++ b/ubo_app/setup.py @@ -1,10 +1,21 @@ """Compatibility layer for different environments.""" +from __future__ import annotations + from pathlib import Path -from typing import Any +from typing import Any, cast import dotenv import numpy as np +from fake import Fake + + +class _FakeAsyncProcess(Fake): + def __init__(self: _FakeAsyncProcess, output: bytes = b'') -> None: + super().__init__(_Fake__props={'output': output}) + + async def communicate(self: _FakeAsyncProcess) -> tuple[bytes, bytes]: + return cast(bytes, self.output), b'' def setup() -> None: @@ -14,8 +25,6 @@ def setup() -> None: # it should be changed to `Fake()` and moved inside the `if not IS_RPI` when the # new sdbus is released {- - from ubo_app.utils.fake import Fake - sys.modules['sdbus.utils.inspect'] = Fake( _Fake__props={ 'inspect_dbus_path': lambda obj: obj._dbus.object_path, # noqa: SLF001 @@ -39,16 +48,10 @@ def setup() -> None: sys.modules['sdbus_async'] = Fake() sys.modules['sdbus_async.networkmanager'] = Fake() sys.modules['sdbus_async.networkmanager.enums'] = Fake() - sys.modules['picamera2'] = Fake( + sys.modules['picamera2.picamera2'] = Fake( _Fake__props={ - 'Picamera2': Fake( - _Fake__return_value=Fake( - _Fake__props={ - 'capture_array': Fake( - _Fake__return_value=np.zeros((1, 1, 3), dtype=np.uint8), - ), - }, - ), + 'capture_array': Fake( + _Fake__return_value=np.zeros((1, 1, 3), dtype=np.uint8), ), }, ) @@ -65,28 +68,31 @@ def fake_subprocess_run( subprocess.run = fake_subprocess_run - original_asyncio_create_subprocess_exec = asyncio.create_subprocess_exec + async def fake_create_subprocess_exec( + *_args: str, + **kwargs: Any, # noqa: ANN401 + ) -> object: + command = _args[0] + args = _args[1:] - def fake_create_subprocess_exec(*args: str, **kwargs: Any) -> object: # noqa: ANN401 - command = args[0] if command == '/usr/bin/env': - command = args[1] + command = args[0] + args = args[1:] if isinstance(command, Path): command = command.as_posix() if any(i in command for i in ('reboot', 'poweroff')): return Fake() if command in {'curl', 'tar'} or command.endswith('/code'): return original_asyncio_create_subprocess_exec(*args, **kwargs) - return Fake( - _Fake__await_value=Fake( - _Fake__props={ - 'communicate': Fake( - _Fake__return_value=Fake(_Fake__await_value=['', '']), - ), - }, - ), - ) + + return await original_asyncio_create_subprocess_exec(*_args, **kwargs) + + original_asyncio_create_subprocess_exec = asyncio.create_subprocess_exec asyncio.create_subprocess_exec = fake_create_subprocess_exec + asyncio.open_unix_connection = ( + Fake(_Fake__return_value=Fake(_Fake__await_value=(Fake(), Fake()))), + ) + import ubo_app.display as _ # noqa: F401 diff --git a/ubo_app/store/main.py b/ubo_app/store/main.py index 4767d7bd..51f7e89e 100644 --- a/ubo_app/store/main.py +++ b/ubo_app/store/main.py @@ -12,6 +12,7 @@ from typing import TYPE_CHECKING, Any, TypeVar, cast, get_origin, overload import dill +from fake import Fake from immutable import Immutable from redux import ( BaseCombineReducerState, @@ -49,7 +50,6 @@ from ubo_app.store.status_icons.reducer import reducer as status_icons_reducer from ubo_app.store.update_manager import UpdateManagerAction, UpdateManagerState from ubo_app.store.update_manager.reducer import reducer as update_manager_reducer -from ubo_app.utils.fake import Fake from ubo_app.utils.serializer import add_type_field if TYPE_CHECKING: diff --git a/ubo_app/system/system_manager/led.py b/ubo_app/system/system_manager/led.py index 34d2f4f0..9ac01ae5 100644 --- a/ubo_app/system/system_manager/led.py +++ b/ubo_app/system/system_manager/led.py @@ -9,10 +9,10 @@ import board from adafruit_blinka.microcontroller.generic_micropython import Pin +from fake import Fake from ubo_gui.menu import warnings from ubo_app.logging import add_file_handler, add_stdout_handler, get_logger -from ubo_app.utils.fake import Fake if Path('/proc/device-tree/model').read_text().startswith('Raspberry Pi 5'): import sys diff --git a/ubo_app/utils/fake.py b/ubo_app/utils/fake.py deleted file mode 100644 index 7bde8de3..00000000 --- a/ubo_app/utils/fake.py +++ /dev/null @@ -1,143 +0,0 @@ -# ruff: noqa: D100, D101, D102, D103, D104, D105, D107 -from __future__ import annotations - -from types import ModuleType -from typing import TYPE_CHECKING, Any, Self, cast - -from ubo_app.logging import logger - -if TYPE_CHECKING: - from collections.abc import Generator, Iterator - - -class Fake(ModuleType): - def __init__( - self: Fake, - *args: object, - __return_value: object | None = None, - __await_value: object | None = None, - __props: dict[str, object] | None = None, - **kwargs: object, - ) -> None: - logger.verbose( - 'Initializing `Fake`', - extra={ - 'args_': args, - 'kwargs': kwargs, - '__return_value': __return_value, - '__await_value': __await_value, - '__props': __props, - }, - ) - if __props is not None: - for key, value in __props.items(): - super().__setattr__(key, value) - self.__return_value = __return_value - self.__await_value = __await_value - self.iterated = False - super().__init__('') - - def __init_subclass__(cls: type[Fake], **kwargs: dict[str, Any]) -> None: - logger.verbose('Subclassing `Fake`', extra={'cls': cls, 'kwargs': kwargs}) - - def __getattr__(self: Fake, attr: str) -> Fake: - logger.verbose( - 'Accessing fake attribute of a `Fake` insta', - extra={'attr': attr}, - ) - if attr == '__file__': - return cast(Fake, 'fake') - return self - - def __setattr__(self: Fake, attr: str, value: object) -> None: - logger.verbose( - 'Accessing fake attribute of a `Fake` insta', - extra={'attr': attr}, - ) - super().__setattr__(attr, value) - - def __getitem__(self: Fake, key: object) -> Fake: - logger.verbose( - 'Accessing fake item of a `Fake` instance', - extra={'key': key}, - ) - return self - - def __setitem__(self: Fake, key: object, value: object) -> None: - logger.verbose( - 'Setting fake item of a `Fake` instance', - extra={'key': key, 'value': value}, - ) - - def __call__(self: Fake, *args: object, **kwargs: dict[str, Any]) -> object: - logger.verbose( - 'Calling a `Fake` instance', - extra={ - 'args_': args, - 'kwargs': kwargs, - '__return_value': self.__return_value, - }, - ) - return self.__return_value if self.__return_value is not None else self - - def __await__(self: Fake) -> Generator[Any, Any, object]: - yield - return self.__await_value or Fake() - - def __next__(self: Fake) -> Fake: - if self.iterated: - raise StopIteration - self.iterated = True - return self - - def __anext__(self: Fake) -> Fake: - if self.iterated: - raise StopAsyncIteration - self.iterated = True - return self - - def __iter__(self: Fake) -> Iterator[Fake]: - return Fake() - - def __aiter__(self: Fake) -> Iterator[Fake]: - return Fake() - - def __enter__(self: Fake) -> Fake: # noqa: PYI034 - return self - - def __exit__(self: Fake, *_: object) -> None: - pass - - async def __aenter__(self: Self) -> Self: - return self - - async def __aexit__(self: Fake, *_: object) -> None: - pass - - def __mro_entries__(self: Fake, bases: tuple[type[Fake]]) -> tuple[type[Fake]]: - logger.verbose( - 'Getting MRO entries of a `Fake` instance', - extra={'bases': bases}, - ) - return (cast(type, self),) - - def __len__(self: Fake) -> int: - return 1 - - def __index__(self: Fake) -> int: - return 1 - - def __contains__(self: Fake, _: object) -> bool: - return True - - def __eq__(self: Fake, _: object) -> bool: - return True - - def __ne__(self: Fake, _: object) -> bool: - return False - - def __str__(self: Fake) -> str: - return 'Fake' - - def __repr__(self: Fake) -> str: - return 'Fake'