Skip to content

Commit 68eb200

Browse files
committed
Add FileWatcher unit tests
The existing tests are really integration tests, as they are not only testing `FileWatcher`, but also `Select` and `PeriodicTimer`, which is confusing when there is a bug in any of the other classes as we might get test failures here. This commit adds a a proper unit tests mocking `awatch`. Signed-off-by: Leandro Lucarella <[email protected]>
1 parent 5126a17 commit 68eb200

File tree

1 file changed

+64
-0
lines changed

1 file changed

+64
-0
lines changed

tests/utils/test_file_watcher.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,77 @@
33

44
"""Tests for `channel.FileWatcher`."""
55

6+
from __future__ import annotations
7+
68
import os
79
import pathlib
10+
from collections.abc import AsyncGenerator, Iterator, Sequence
811
from datetime import timedelta
12+
from typing import Any
13+
from unittest import mock
14+
15+
import pytest
16+
from watchfiles import Change
17+
from watchfiles.main import FileChange
918

1019
from frequenz.channels.util import FileWatcher, Select, Timer
1120

1221

22+
class _FakeAwatch:
23+
"""Fake awatch class to mock the awatch function."""
24+
25+
def __init__(self, changes: Sequence[FileChange] = ()) -> None:
26+
"""Create a `_FakeAwatch` instance.
27+
28+
Args:
29+
changes: A sequence of file changes to be returned by the fake awatch
30+
function.
31+
"""
32+
self.changes: Sequence[FileChange] = changes
33+
34+
async def fake_awatch(
35+
self, *paths: str, **kwargs: Any # pylint: disable=unused-argument
36+
) -> AsyncGenerator[set[FileChange], None]:
37+
"""Fake awatch function.
38+
39+
Args:
40+
paths: Paths to watch.
41+
kwargs: Keyword arguments to pass to the awatch function.
42+
"""
43+
for change in self.changes:
44+
yield {change}
45+
46+
47+
@pytest.fixture
48+
def fake_awatch() -> Iterator[_FakeAwatch]:
49+
"""Fixture to mock the awatch function."""
50+
fake = _FakeAwatch()
51+
with mock.patch(
52+
"frequenz.channels.util._file_watcher.awatch",
53+
autospec=True,
54+
side_effect=fake.fake_awatch,
55+
):
56+
yield fake
57+
58+
59+
async def test_file_watcher_receive_updates(
60+
fake_awatch: _FakeAwatch, # pylint: disable=redefined-outer-name
61+
) -> None:
62+
"""Test the file watcher receive the expected events."""
63+
filename = "test-file"
64+
changes = (
65+
(Change.added, filename),
66+
(Change.deleted, filename),
67+
(Change.modified, filename),
68+
)
69+
fake_awatch.changes = changes
70+
file_watcher = FileWatcher(paths=[filename])
71+
72+
for change in changes:
73+
recv_changes = await file_watcher.receive()
74+
assert recv_changes == pathlib.Path(change[1])
75+
76+
1377
async def test_file_watcher(tmp_path: pathlib.Path) -> None:
1478
"""Ensure file watcher is returning paths on file events.
1579

0 commit comments

Comments
 (0)