From 5f01538987681bbf7d92e0b363a7ee02db04a86b Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Mon, 28 Oct 2024 12:35:15 +0000 Subject: [PATCH 1/3] Add some proper tests --- src/django_watchfiles/__init__.py | 5 --- tests/test_django_watchfiles.py | 68 +++++++++++++++++++++++++++++-- 2 files changed, 64 insertions(+), 9 deletions(-) diff --git a/src/django_watchfiles/__init__.py b/src/django_watchfiles/__init__.py index 7437f2c..a0d49cc 100644 --- a/src/django_watchfiles/__init__.py +++ b/src/django_watchfiles/__init__.py @@ -57,9 +57,7 @@ def __init__(self) -> None: def file_filter(self, change: watchfiles.Change, filename: str) -> bool: path = Path(filename) - # print(f"Path: {path} / {change}") if path in set(self.watched_files(include_globs=False)): - # print("Path in watched files") return True for directory, globs in self.directory_globs.items(): try: @@ -67,12 +65,9 @@ def file_filter(self, change: watchfiles.Change, filename: str) -> bool: except ValueError: pass else: - # print("Path is sub dir") for glob in globs: if fnmatch.fnmatch(str(relative_path), glob): - # print("Path is glob match") return True - # print("file filter", change, path) return False def watched_roots(self, watched_files: list[Path]) -> frozenset[Path]: diff --git a/tests/test_django_watchfiles.py b/tests/test_django_watchfiles.py index 1c96d72..d521476 100644 --- a/tests/test_django_watchfiles.py +++ b/tests/test_django_watchfiles.py @@ -1,9 +1,69 @@ from __future__ import annotations +import tempfile +from pathlib import Path -def test_import(): - # TODO: figure out tests +from django.test import SimpleTestCase +from django.utils import autoreload - import django_watchfiles +from django_watchfiles import MutableWatcher +from django_watchfiles import WatchfilesReloader - assert django_watchfiles # type: ignore [truthy-bool] + +class MutableWatcherTests(SimpleTestCase): + def setUp(self): + self.watcher = MutableWatcher(lambda *args: True) + self.addCleanup(self.watcher.stop) + + temp_dir = self.enterContext(tempfile.TemporaryDirectory()) + self.temp_path = Path(temp_dir) + + def test_set_roots_unchanged(self): + assert not self.watcher.change_event.is_set() + self.watcher.set_roots(set()) + assert not self.watcher.change_event.is_set() + + def test_set_roots_changed(self): + assert not self.watcher.change_event.is_set() + self.watcher.set_roots({Path("/tmp")}) + assert self.watcher.change_event.is_set() + + def test_stop(self): + assert not self.watcher.stop_event.is_set() + self.watcher.stop() + assert self.watcher.stop_event.is_set() + + def test_iter_yields_changes(self): + (self.temp_path / "test.txt").touch() + self.watcher.set_roots({self.temp_path}) + iterator = iter(self.watcher) + # flush initial events + next(iterator) + + (self.temp_path / "test.txt").touch() + changes = next(iterator) + + assert isinstance(changes, set) + assert len(changes) == 1 + _, path = changes.pop() + assert path == str(self.temp_path.resolve() / "test.txt") + + def test_iter_respects_change_event(self): + (self.temp_path / "test.txt").touch() + self.watcher.set_roots({self.temp_path}) + iterator = iter(self.watcher) + # flush initial events + next(iterator) + + self.watcher.set_roots(set()) + self.watcher.set_roots({self.temp_path}) + changes = next(iterator) + + assert isinstance(changes, set) + assert len(changes) == 0 + + +class ReplacedGetReloaderTests(SimpleTestCase): + def test_replaced_get_reloader(self): + reloader = autoreload.get_reloader() + assert isinstance(reloader, WatchfilesReloader) From e9dbdd915db11680787161cdccc76eda07741c12 Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Mon, 28 Oct 2024 15:05:54 +0000 Subject: [PATCH 2/3] Backport enterContext() --- tests/compat.py | 37 +++++++++++++++++++++++++++++++++ tests/test_django_watchfiles.py | 2 +- 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 tests/compat.py diff --git a/tests/compat.py b/tests/compat.py new file mode 100644 index 0000000..658942a --- /dev/null +++ b/tests/compat.py @@ -0,0 +1,37 @@ +from __future__ import annotations + +import sys +from contextlib import AbstractContextManager +from typing import Any +from typing import Callable +from typing import TypeVar + +from django import test + +_T = TypeVar("_T") + +if sys.version_info < (3, 11): + + def _enter_context(cm: Any, addcleanup: Callable[..., None]) -> Any: + # We look up the special methods on the type to match the with + # statement. + cls = type(cm) + try: + enter = cls.__enter__ + exit = cls.__exit__ + except AttributeError: + raise TypeError( + f"'{cls.__module__}.{cls.__qualname__}' object does " + f"not support the context manager protocol" + ) from None + result = enter(cm) + addcleanup(exit, cm, None, None, None) + return result + + +class SimpleTestCase(test.SimpleTestCase): + if sys.version_info < (3, 11): + + def enterContext(self, cm: AbstractContextManager[_T]) -> _T: + result: _T = _enter_context(cm, self.addCleanup) + return result diff --git a/tests/test_django_watchfiles.py b/tests/test_django_watchfiles.py index d521476..4a7be22 100644 --- a/tests/test_django_watchfiles.py +++ b/tests/test_django_watchfiles.py @@ -3,11 +3,11 @@ import tempfile from pathlib import Path -from django.test import SimpleTestCase from django.utils import autoreload from django_watchfiles import MutableWatcher from django_watchfiles import WatchfilesReloader +from tests.compat import SimpleTestCase class MutableWatcherTests(SimpleTestCase): From c93fdbee4f48165de983e81dc3f2f89d9cfffc5e Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Mon, 28 Oct 2024 15:10:18 +0000 Subject: [PATCH 3/3] coverage++ --- tests/compat.py | 2 +- tests/test_django_watchfiles.py | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/tests/compat.py b/tests/compat.py index 658942a..e934867 100644 --- a/tests/compat.py +++ b/tests/compat.py @@ -19,7 +19,7 @@ def _enter_context(cm: Any, addcleanup: Callable[..., None]) -> Any: try: enter = cls.__enter__ exit = cls.__exit__ - except AttributeError: + except AttributeError: # pragma: no cover raise TypeError( f"'{cls.__module__}.{cls.__qualname__}' object does " f"not support the context manager protocol" diff --git a/tests/test_django_watchfiles.py b/tests/test_django_watchfiles.py index 4a7be22..718a35f 100644 --- a/tests/test_django_watchfiles.py +++ b/tests/test_django_watchfiles.py @@ -1,6 +1,7 @@ from __future__ import annotations import tempfile +import time from pathlib import Path from django.utils import autoreload @@ -33,6 +34,18 @@ def test_stop(self): self.watcher.stop() assert self.watcher.stop_event.is_set() + def test_iter_no_changes(self): + (self.temp_path / "test.txt").touch() + self.watcher.set_roots({self.temp_path}) + iterator = iter(self.watcher) + # flush initial events + next(iterator) + time.sleep(0.1) # 100ms Rust timeout + + changes = next(iterator) + + assert changes == set() + def test_iter_yields_changes(self): (self.temp_path / "test.txt").touch() self.watcher.set_roots({self.temp_path})