@@ -1169,6 +1169,40 @@ def test_filestream_cache_prune_only_if_stat_unchanged(tmp_path):
11691169 assert not path .exists ()
11701170
11711171
1172+ def test_filestream_cache_touch_atime_only_if_stat_unchanged (tmp_path ):
1173+ """The atime-touch is also stat-guarded so a racing rewriter's freshly
1174+ replaced file does NOT get its mtime rolled back to the previous
1175+ entry's value. Without the guard, the eviction stat-check (which keys
1176+ on (ino, size, mtime_ns)) would mistake the replacement for the old
1177+ entry and delete a just-committed file."""
1178+ from cuda .core .utils import FileStreamProgramCache
1179+ from cuda .core .utils ._program_cache import _touch_atime
1180+
1181+ same_size_bytes = b"v" * 64
1182+ with FileStreamProgramCache (tmp_path / "fc" ) as cache :
1183+ cache [b"k" ] = same_size_bytes
1184+ path = cache ._path_for_key (b"k" )
1185+ stale_stat = path .stat ()
1186+ # Concurrent writer replaces with same-size payload (same st_size,
1187+ # different ino/mtime) -- this is the dangerous case: ino and
1188+ # mtime differ, only the stat-guard saves us.
1189+ time .sleep (0.02 )
1190+ cache [b"k" ] = same_size_bytes
1191+ new_mtime_ns = path .stat ().st_mtime_ns
1192+
1193+ _touch_atime (path , stale_stat )
1194+ # The new file's mtime must be untouched.
1195+ assert path .stat ().st_mtime_ns == new_mtime_ns
1196+
1197+ # With a stat that matches the current file, atime is updated and
1198+ # mtime is preserved.
1199+ fresh_stat = path .stat ()
1200+ _touch_atime (path , fresh_stat )
1201+ after = path .stat ()
1202+ assert after .st_mtime_ns == fresh_stat .st_mtime_ns
1203+ assert after .st_atime_ns >= fresh_stat .st_atime_ns
1204+
1205+
11721206def test_filestream_cache_returns_bytes_verbatim (tmp_path ):
11731207 """The cache stores raw binary and does no payload validation: whatever
11741208 bytes were written are returned exactly. External tools (cuobjdump,
@@ -1242,13 +1276,22 @@ def test_filestream_cache_rejects_negative_size_cap(tmp_path):
12421276def test_default_cache_dir_lives_under_user_cache_root ():
12431277 """We delegate per-platform path resolution to ``platformdirs``, so we
12441278 only verify the cuda-python-owned suffix; the OS-specific bits are
1245- platformdirs' responsibility."""
1279+ platformdirs' responsibility.
1280+
1281+ ``opinion=False`` matters: with the default ``opinion=True`` on
1282+ Windows, platformdirs inserts an extra ``Cache`` component that
1283+ would diverge from the documented ``<root>/cuda-python/program-cache``
1284+ layout. Asserting against the same flag pins the layout invariant.
1285+ """
12461286 import platformdirs
12471287
12481288 from cuda .core .utils ._program_cache import _default_cache_dir
12491289
1250- expected = platformdirs .user_cache_path ("cuda-python" , appauthor = False ) / "program-cache"
1290+ expected = platformdirs .user_cache_path ("cuda-python" , appauthor = False , opinion = False ) / "program-cache"
12511291 assert _default_cache_dir () == expected
1292+ # Path must end with cuda-python/program-cache regardless of platform;
1293+ # no extra "Cache" component sneaks in.
1294+ assert _default_cache_dir ().parts [- 2 :] == ("cuda-python" , "program-cache" )
12521295
12531296
12541297def test_filestream_cache_uses_default_dir_when_path_omitted (tmp_path , monkeypatch ):
0 commit comments