File: conftest.py

package info (click to toggle)
python-xknx 3.6.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 4,012 kB
  • sloc: python: 39,710; javascript: 8,556; makefile: 27; sh: 12
file content (70 lines) | stat: -rw-r--r-- 2,223 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
"""Conftest for XKNX."""

import asyncio
from unittest.mock import AsyncMock, Mock, patch

import pytest

from xknx import XKNX


class EventLoopClockAdvancer:
    """Allow advancing of loop time."""

    # thanks to @dermotduffy for his asyncio.sleep mock
    # https://github.com/dermotduffy/hyperion-py/blob/main/tests/client_test.py#L273

    __slots__ = ("_base_time", "loop", "offset")

    def __init__(self, loop: asyncio.AbstractEventLoop) -> None:
        """Initialize."""
        self.offset = 0.0
        self._base_time = loop.time
        self.loop = loop

        # incorporate offset timing into the event loop
        self.loop.time = self.time  # type: ignore[assignment]

    def time(self) -> float:
        """Return loop time adjusted by offset."""
        return self._base_time() + self.offset

    async def _exhaust_callbacks(self) -> None:
        """Run the loop until all ready callbacks are executed."""
        while self.loop._ready:  # noqa: ASYNC110  # type: ignore[attr-defined]
            await asyncio.sleep(0)

    async def __call__(self, seconds: float) -> None:
        """Advance time by a given offset in seconds."""
        # Exhaust all callbacks.
        await self._exhaust_callbacks()

        if seconds > 0:
            # advance the clock by the given offset
            self.offset += seconds
            # Once the clock is adjusted, new tasks may have just been
            # scheduled for running in the next pass through the event loop
            await asyncio.sleep(0)
            await self._exhaust_callbacks()


@pytest.fixture
def time_travel(event_loop: asyncio.AbstractEventLoop) -> EventLoopClockAdvancer:
    """Advance loop time and run callbacks."""
    return EventLoopClockAdvancer(event_loop)


@pytest.fixture
def xknx_no_interface() -> XKNX:
    """Return XKNX instance without KNX/IP interface."""

    def knx_ip_interface_mock() -> Mock:
        """Create a xknx knx ip interface mock."""
        mock = Mock()
        mock.start = AsyncMock()
        mock.stop = AsyncMock()
        mock.send_cemi = AsyncMock()
        return mock

    with patch("xknx.xknx.knx_interface_factory", return_value=knx_ip_interface_mock()):
        return XKNX()