test(images): reproduce the newest-order race and fix a Windows clock flake (#348, PR 2)

- The race test now has the newer build step from the values the cached
  order was built from, which is the interleaving that inserted a file
  twice; it fails without the parts check in _newest_step.
- test_a_failed_walk_records_its_timing_and_clears_dirty compared two
  monotonic readings that Windows' ~15.6 ms clock can make equal; the
  previous start is moved back instead.
- TESTING.md: os.access ignores ACLs on Windows.
This commit is contained in:
Vito Sansevero
2026-10-26 15:40:06 -07:00
parent aeffe87dc1
commit fc972d36fe
2 changed files with 15 additions and 6 deletions
+2 -1
View File
@@ -350,7 +350,8 @@ hold a walk open with a gated scan instead of sleeping.
(`_patch_listing`): `_settled_entry` applies the walk's filters
(not a symlink or folder by `lstat`, `_name_suffix` in `MEDIA_EXTENSIONS`,
no `thumbnails` folder, at most `MAX_SCAN_DEPTH` folders, every folder on
the way listable by `os.access`). Where names fold case the stored
the way listable by `os.access`, which on Windows ignores ACLs: a folder
whose ACL denies listing is added there and corrected by the next walk). Where names fold case the stored
spelling must match first, as the filters compare it: Windows through
`nt._getfinalpathname` (its `realpath` keeps the given case when it cannot
open the file), macOS through `F_GETPATH` on a non-blocking open (a FIFO
+13 -5
View File
@@ -3229,6 +3229,10 @@ class TestSharedListing(ImageAPITestCase):
self.api.mark_root_dirty(self._output("b.png"))
self.assertTrue(record.dirty)
self.api._scan_gallery_files_sync = _raise
# Moved back, so the failed walk's start is later even on a coarse
# monotonic clock (Windows ticks every ~15.6 ms).
record.last_started_at -= 1000
record.last_duration = None
started = record.last_started_at
with self.assertLogs(self.api.logger, "WARNING"):
self._fire(record)
@@ -4097,7 +4101,8 @@ class TestSharedListing(ImageAPITestCase):
f1 = gallery_listing.insert_entry(f0, (root / "n1.png", 5000.0), 100)
f2 = gallery_listing.insert_entry(f1, (root / "n2.png", 6000.0), 100)
self.api._workspace_orders([(0, root, f0)])
self.api._workspace_listed(0, root, f1) # the warm build of add 1
key = (0, str(root))
built_from_f0 = self.api._workspace_listed_cache[key]
real = self.api._workspace_listed
armed = [True]
@@ -4105,15 +4110,18 @@ class TestSharedListing(ImageAPITestCase):
values = real(index, part_root, files)
if armed[0] and files is f1:
armed[0] = False
# Another executor thread reads f2 in full meanwhile.
self.api._workspace_orders([(0, root, f2)])
# Another executor thread read the f0 values before this one
# stored f1's, and stores its f2 step (from f0) only now.
self.api._workspace_listed_cache[key] = built_from_f0
real(index, part_root, f2)
return values
with patch.object(self.api, "_workspace_listed", side_effect=listed):
stale = self.api._workspace_orders([(0, root, f1)])["newest"]
self.assertEqual([entry.path for entry in stale], [p for p, _m in f1])
newest = self.api._workspace_orders([(0, root, f2)])["newest"]
self.assertEqual([entry.path for entry in newest], [p for p, _m in f2])
for files in (f1, f2, f1, f2):
newest = self.api._workspace_orders([(0, root, files)])["newest"]
self.assertEqual([entry.path for entry in newest], [p for p, _m in files])
def test_a_roots_change_during_an_order_build_sorts_in_full(self):
root = self.output_dir