From ee27bcccc130c40266b60df371a6be8ee2c4801e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Wed, 9 Oct 2024 13:40:25 +0200 Subject: [PATCH] tests: Use freezegun for time mocking to fix pypy3 compatibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use freezegun for time mocking instead of manually patching the datetime module, as it provides a more streamlined solution that works both on CPython and on PyPy. Unfortunately, due to differences between the C datetime extension used by CPython, and the pure Python version of datetime (used by PyPy, and as a fallback on CPython), there does not seem to be a trivial way to mock time that would work with both versions. Fixes #2708 Signed-off-by: Michał Górny --- requirements/lint.txt | 3 +++ requirements/test.txt | 1 + tests/test_updater_top_level_update.py | 34 ++++++++++++-------------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/requirements/lint.txt b/requirements/lint.txt index 55640d1f..b69387da 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -8,3 +8,6 @@ # are pinned to prevent unexpected linting failures when tools update) ruff==0.6.7 mypy==1.11.2 + +# Required for type stubs +freezegun==1.5.1 diff --git a/requirements/test.txt b/requirements/test.txt index d5f61d17..f77d2abb 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -5,3 +5,4 @@ # coverage measurement coverage==7.6.1 +freezegun==1.5.1 diff --git a/tests/test_updater_top_level_update.py b/tests/test_updater_top_level_update.py index 78c8d776..cd82b5ba 100644 --- a/tests/test_updater_top_level_update.py +++ b/tests/test_updater_top_level_update.py @@ -11,7 +11,9 @@ import unittest from datetime import timezone from typing import Iterable, Optional -from unittest.mock import MagicMock, Mock, call, patch +from unittest.mock import MagicMock, call, patch + +import freezegun from tests import utils from tests.repository_simulator import RepositorySimulator @@ -306,8 +308,7 @@ def test_new_timestamp_unsigned(self) -> None: self._assert_files_exist([Root.type]) - @patch.object(datetime, "datetime", wraps=datetime.datetime) - def test_expired_timestamp_version_rollback(self, mock_time: Mock) -> None: + def test_expired_timestamp_version_rollback(self) -> None: """Verifies that local timestamp is used in rollback checks even if it is expired. The timestamp updates and rollback checks are performed @@ -331,10 +332,9 @@ def test_expired_timestamp_version_rollback(self, mock_time: Mock) -> None: self.sim.timestamp.version = 1 - mock_time.now.return_value = datetime.datetime.now( - timezone.utc - ) + datetime.timedelta(days=18) - patcher = patch("datetime.datetime", mock_time) + patcher = freezegun.freeze_time( + datetime.datetime.now(timezone.utc) + datetime.timedelta(days=18) + ) # Check that a rollback protection is performed even if # local timestamp has expired with patcher, self.assertRaises(BadVersionNumberError): @@ -342,8 +342,7 @@ def test_expired_timestamp_version_rollback(self, mock_time: Mock) -> None: self._assert_version_equals(Timestamp.type, 2) - @patch.object(datetime, "datetime", wraps=datetime.datetime) - def test_expired_timestamp_snapshot_rollback(self, mock_time: Mock) -> None: + def test_expired_timestamp_snapshot_rollback(self) -> None: """Verifies that rollback protection is done even if local timestamp has expired. The snapshot updates and rollback protection checks are performed @@ -370,10 +369,9 @@ def test_expired_timestamp_snapshot_rollback(self, mock_time: Mock) -> None: self.sim.update_snapshot() self.sim.timestamp.expires = now + datetime.timedelta(days=21) - mock_time.now.return_value = datetime.datetime.now( - timezone.utc - ) + datetime.timedelta(days=18) - patcher = patch("datetime.datetime", mock_time) + patcher = freezegun.freeze_time( + datetime.datetime.now(timezone.utc) + datetime.timedelta(days=18) + ) # Assert that rollback protection is done even if # local timestamp has expired with patcher, self.assertRaises(BadVersionNumberError): @@ -736,8 +734,7 @@ def test_load_metadata_from_cache(self, wrapped_open: MagicMock) -> None: expected_calls = [("root", 2), ("timestamp", None)] self.assertListEqual(self.sim.fetch_tracker.metadata, expected_calls) - @patch.object(datetime, "datetime", wraps=datetime.datetime) - def test_expired_metadata(self, mock_time: Mock) -> None: + def test_expired_metadata(self) -> None: """Verifies that expired local timestamp/snapshot can be used for updating from remote. @@ -761,10 +758,9 @@ def test_expired_metadata(self, mock_time: Mock) -> None: # Mocking time so that local timestam has expired # but the new timestamp has not - mock_time.now.return_value = datetime.datetime.now( - timezone.utc - ) + datetime.timedelta(days=18) - with patch("datetime.datetime", mock_time): + with freezegun.freeze_time( + datetime.datetime.now(timezone.utc) + datetime.timedelta(days=18) + ): self._run_refresh() # Assert that the final version of timestamp/snapshot is version 2