Skip to content

Commit

Permalink
feat(tests): add pyfakefs to mock filesystem in tests
Browse files Browse the repository at this point in the history
  • Loading branch information
sassanh committed May 16, 2024
1 parent a358c6e commit dec8e22
Show file tree
Hide file tree
Showing 20 changed files with 108 additions and 48 deletions.
1 change: 0 additions & 1 deletion .github/workflows/integration_delivery.yml
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,6 @@ jobs:

- name: Run Tests
run: |
mkdir -p $HOME/.kivy/mods
POETRY_VIRTUALENVS_OPTIONS_SYSTEM_SITE_PACKAGES=true poetry run poe test --make-screenshots --cov-report=xml --cov-report=html -n auto --log-level=DEBUG
- name: Collect Window Screenshots
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# ChangeLog

## Version 0.14.3

- feat(tests): add `pyfakefs` to mock filesystem in tests

## Version 0.14.2

- fix(vscode): show a success notification when the login process is completed instead
Expand Down
43 changes: 27 additions & 16 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "ubo-app"
version = "0.14.2"
version = "0.14.3"
description = "Ubo main app, running on device initialization. A platform for running other apps."
authors = ["Sassan Haradji <[email protected]>"]
license = "Apache-2.0"
Expand Down Expand Up @@ -60,6 +60,7 @@ toml = "^0.10.2"
pytest-mock = "^3.14.0"
pyaudio = { version = "^0.2.14", markers = "platform_machine!='aarch64'" }
ipython = "^8.23.0"
pyfakefs = "^5.5.0"

[tool.poetry.extras]
default = ["ubo-gui"]
Expand Down
1 change: 1 addition & 0 deletions scripts/Dockerfile.test
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
FROM ubo-app-dev

RUN mkdir -p /root/.kivy/mods
RUN touch /root/.kivy/icon

ENTRYPOINT ["/bin/bash", "-c", "reset; poetry install --with dev --extras=dev --verbose && poetry run poe test $@"]
60 changes: 45 additions & 15 deletions tests/fixtures/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
import logging
import sys
import weakref
from pathlib import Path
from typing import TYPE_CHECKING

import platformdirs
import pytest

from ubo_app.setup import setup
Expand All @@ -17,6 +19,7 @@
from collections.abc import AsyncGenerator

from _pytest.fixtures import SubRequest
from pyfakefs.fake_filesystem import FakeFilesystem

from ubo_app.menu_app.menu import MenuApp

Expand All @@ -26,9 +29,10 @@
class AppContext:
"""Context object for tests running a menu application."""

def __init__(self: AppContext, request: SubRequest) -> None:
def __init__(self: AppContext, request: SubRequest, *, fs: FakeFilesystem) -> None:
"""Initialize the context."""
self.request = request
self.fs = fs

def set_app(self: AppContext, app: MenuApp) -> None:
"""Set the application."""
Expand Down Expand Up @@ -81,35 +85,61 @@ async def clean_up(self: AppContext) -> None:

Window.close()

for module in set(sys.modules) - modules_snapshot:
if module != 'objc' and 'numpy' not in module and 'cache' not in module:
del sys.modules[module]
gc.collect()


@pytest.fixture()
async def app_context(
request: SubRequest,
) -> AsyncGenerator[AppContext, None]:
async def app_context(request: SubRequest) -> AsyncGenerator[AppContext, None]:
"""Create the application."""
import os

from pyfakefs.fake_filesystem_unittest import Patcher

os.environ['KIVY_NO_FILELOG'] = '1'
os.environ['KIVY_NO_CONSOLELOG'] = '1'
os.environ['KIVY_METRICS_DENSITY'] = '1'

setup()

import headless_kivy_pi.config

headless_kivy_pi.config.setup_headless_kivy(
{'automatic_fps': True, 'flip_vertical': True},
)

context = AppContext(request)
current_path = Path()
with Patcher(
additional_skip_names=[
'redux_pytest.fixtures',
'tests.fixtures.snapshot',
'pathlib',
],
) as patcher:
assert patcher.fs is not None

patcher.fs.add_real_paths(
[
(current_path / 'tests').absolute().as_posix(),
(current_path / 'ubo_app').absolute().as_posix(),
platformdirs.user_cache_dir('pypoetry'),
],
)

setup()

yield context
context = AppContext(request, fs=patcher.fs)

await context.clean_up()
yield context

assert not hasattr(context, 'app'), 'App not cleaned up'
await context.clean_up()

assert not hasattr(context, 'app'), 'App not cleaned up'

del context
del patcher

for module in set(sys.modules) - modules_snapshot:
if (
module != 'objc'
and 'numpy' not in module
and 'kivy.cache' not in module
):
del sys.modules[module]

gc.collect()
6 changes: 3 additions & 3 deletions tests/fixtures/snapshot.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@

import os
from collections import defaultdict
from pathlib import Path
from typing import TYPE_CHECKING, Any, cast

import pytest

if TYPE_CHECKING:
from collections.abc import Generator
from pathlib import Path

from _pytest.fixtures import SubRequest
from _pytest.nodes import Node
Expand Down Expand Up @@ -49,11 +49,11 @@ def __init__(
self.make_screenshots = make_screenshots
self.test_counter: dict[str | None, int] = defaultdict(int)
file = test_node.path.with_suffix('').name
self.results_dir = (
self.results_dir = Path(
test_node.path.parent
/ 'results'
/ file
/ test_node.nodeid.split('::')[-1][5:]
/ test_node.nodeid.split('::')[-1][5:],
)
if self.results_dir.exists():
for file in self.results_dir.glob(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
"sub_menu": {
"items": [
{
"application": "ubo_app/services/030-wifi/pages/create_wireless_connection.py:CreateWirelessConnectionPage",
"application": "services/030-wifi/pages/create_wireless_connection.py:CreateWirelessConnectionPage",
"background_color": "#68B7FF",
"color": [
1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
"sub_menu": {
"items": [
{
"application": "ubo_app/services/030-wifi/pages/create_wireless_connection.py:CreateWirelessConnectionPage",
"application": "services/030-wifi/pages/create_wireless_connection.py:CreateWirelessConnectionPage",
"background_color": "#68B7FF",
"color": [
1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
"sub_menu": {
"items": [
{
"application": "ubo_app/services/030-wifi/pages/create_wireless_connection.py:CreateWirelessConnectionPage",
"application": "services/030-wifi/pages/create_wireless_connection.py:CreateWirelessConnectionPage",
"background_color": "#68B7FF",
"color": [
1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
"sub_menu": {
"items": [
{
"application": "ubo_app/services/030-wifi/pages/create_wireless_connection.py:CreateWirelessConnectionPage",
"application": "services/030-wifi/pages/create_wireless_connection.py:CreateWirelessConnectionPage",
"background_color": "#68B7FF",
"color": [
1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
"sub_menu": {
"items": [
{
"application": "ubo_app/services/030-wifi/pages/create_wireless_connection.py:CreateWirelessConnectionPage",
"application": "services/030-wifi/pages/create_wireless_connection.py:CreateWirelessConnectionPage",
"background_color": "#68B7FF",
"color": [
1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
"sub_menu": {
"items": [
{
"application": "ubo_app/services/030-wifi/pages/create_wireless_connection.py:CreateWirelessConnectionPage",
"application": "services/030-wifi/pages/create_wireless_connection.py:CreateWirelessConnectionPage",
"background_color": "#68B7FF",
"color": [
1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
"sub_menu": {
"items": [
{
"application": "ubo_app/services/030-wifi/pages/create_wireless_connection.py:CreateWirelessConnectionPage",
"application": "services/030-wifi/pages/create_wireless_connection.py:CreateWirelessConnectionPage",
"background_color": "#68B7FF",
"color": [
1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
"sub_menu": {
"items": [
{
"application": "ubo_app/services/030-wifi/pages/create_wireless_connection.py:CreateWirelessConnectionPage",
"application": "services/030-wifi/pages/create_wireless_connection.py:CreateWirelessConnectionPage",
"background_color": "#68B7FF",
"color": [
1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
"sub_menu": {
"items": [
{
"application": "ubo_app/services/030-wifi/pages/create_wireless_connection.py:CreateWirelessConnectionPage",
"application": "services/030-wifi/pages/create_wireless_connection.py:CreateWirelessConnectionPage",
"background_color": "#68B7FF",
"color": [
1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@
"sub_menu": {
"items": [
{
"application": "ubo_app/services/030-wifi/pages/create_wireless_connection.py:CreateWirelessConnectionPage",
"application": "services/030-wifi/pages/create_wireless_connection.py:CreateWirelessConnectionPage",
"background_color": "#68B7FF",
"color": [
1,
Expand Down
1 change: 1 addition & 0 deletions tests/monkeypatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ def _monkeypatch(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr('importlib.metadata.version', lambda _: '0.0.0')
monkeypatch.setattr('ubo_app.constants.STORE_GRACE_TIME', 0.1)
monkeypatch.setattr('ubo_app.utils.serializer.add_type_field', lambda _, y: y)
monkeypatch.setattr('pyzbar.pyzbar.decode', Fake())

def fake_read_from_persistent_store(
key: str,
Expand Down
8 changes: 8 additions & 0 deletions ubo_app/load_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,14 @@ def initiate(self: UboServiceThread) -> None:
def run(self: UboServiceThread) -> None:
self.loop = asyncio.new_event_loop()
self.loop.set_exception_handler(loop_exception_handler)
logger.debug(
'Starting service thread',
extra={
'thread_native_id': self.native_id,
'service_label': self.label,
'service_id': self.service_id,
},
)
asyncio.set_event_loop(self.loop)
if DEBUG_MODE:
self.loop.set_debug(enabled=True)
Expand Down
Loading

0 comments on commit dec8e22

Please sign in to comment.