Skip to content

Commit

Permalink
Fix forced refresh for future timestamps and add tests.
Browse files Browse the repository at this point in the history
In principle, we should never have to compare a timestamp representing a
future date when we check whether a cached file should be refreshed.
However, files with bogus mtime values and/or computers configured with
a bogus system time are certainly not uncommon, so encountering a
timestamp higher than the current time can (and will) definitely happen.

Under an "always refresh" policy, a refresh must be triggered even if
the cached file appears to "newer than now", so we explicitly implement
that behaviour here.

We also add a complete test fixture for the CachePolicy class.
  • Loading branch information
gouttegd committed Aug 18, 2024
1 parent ac3c29d commit b1db7b9
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/oaklib/utilities/caching.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ def refresh(self, then):
:return: True if the data should be refreshed, otherwise False
"""

if self._max_age <= 0:
# Forceful refresh/reset, even if "then" is somehow in the future
return True
return time.time() - then > self._max_age

def refresh_file(self, pathname):
Expand Down
95 changes: 95 additions & 0 deletions tests/test_utilities/test_caching.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import os
import time
import unittest

from oaklib.utilities.caching import CachePolicy


class TestCachePolicy(unittest.TestCase):

def test_refresh_policy(self):
policy = CachePolicy.from_string("refresh")

self.assertTrue(policy.always_refresh)
self.assertFalse(policy.never_refresh)
self.assertFalse(policy.reset)

self.assertEqual(CachePolicy.REFRESH, policy)

now = time.time()
self.assertTrue(policy.refresh(now))
self.assertTrue(policy.refresh(now + 86400)) # 1 day in the future
self.assertTrue(policy.refresh(now - 86400)) # 1 day in the past

def test_never_refresh_policy(self):
policy = CachePolicy.from_string("no-refresh")

self.assertTrue(policy.never_refresh)
self.assertFalse(policy.always_refresh)
self.assertFalse(policy.reset)

self.assertEqual(CachePolicy.NO_REFRESH, policy)

now = time.time()
self.assertFalse(policy.refresh(now))
self.assertFalse(policy.refresh(now + 86400))
self.assertFalse(policy.refresh(now - 86400))

# inexistent file is always refreshed even under "no-refresh"
self.assertTrue(policy.refresh_file("inexistent-file"))

def test_reset_policy(self):
policy = CachePolicy.from_string("reset")
self.assertEqual(policy, CachePolicy.from_string("clear"))

self.assertTrue(policy.reset)
self.assertFalse(policy.always_refresh)
self.assertFalse(policy.never_refresh)

self.assertEqual(CachePolicy.RESET, policy)

now = time.time()
self.assertTrue(policy.refresh(now))
self.assertTrue(policy.refresh(now + 86400))
self.assertTrue(policy.refresh(now - 86400))

def test_refresh_after_1day_policy(self):
policy = CachePolicy.from_string('1d')

self.assertFalse(policy.always_refresh)
self.assertFalse(policy.never_refresh)
self.assertFalse(policy.reset)

now = time.time()
self.assertTrue(policy.refresh(now - 90000)) # 25 hours in the past
self.assertFalse(policy.refresh(now - 82800)) # 23 hours in the past

def test_refresh_file(self):
now = time.time()

# Create dummy file with known mtime 3 days in the past
path = "tests/output/dummy-cache"
with open(path, "w"):
pass
os.utime(path, (now - 259200, now - 259200))

self.assertTrue(CachePolicy.REFRESH.refresh_file(path))
self.assertTrue(CachePolicy.RESET.refresh_file(path))
self.assertFalse(CachePolicy.NO_REFRESH.refresh_file(path))
self.assertTrue(CachePolicy.from_string('2d').refresh_file(path))
self.assertFalse(CachePolicy.from_string('4d').refresh_file(path))

os.unlink(path)

# Inexistent file gets refreshed even under no-refresh
self.assertTrue(CachePolicy.NO_REFRESH.refresh_file(path))

def test_parsing_durations(self):
self.assertEqual(CachePolicy.from_string("1")._max_age, 86400)
self.assertEqual(CachePolicy.from_string("1d")._max_age, 86400)
self.assertEqual(CachePolicy.from_string("86400s")._max_age, 86400)
self.assertEqual(CachePolicy.from_string("1w")._max_age, 86400 * 7)
self.assertEqual(CachePolicy.from_string("1m")._max_age, 86400 * 30)
self.assertEqual(CachePolicy.from_string("1y")._max_age, 86400 * 365)

self.assertIsNone(CachePolicy.from_string("bogus"))

0 comments on commit b1db7b9

Please sign in to comment.