Skip to content

test: cover SelectionsLock's sentinel cleanup and exhaustion - #71

Merged
StuartMeeks merged 1 commit into
mainfrom
fix/57-selectionslock-tests
Aug 25, 2026
Merged

test: cover SelectionsLock's sentinel cleanup and exhaustion#71
StuartMeeks merged 1 commit into
mainfrom
fix/57-selectionslock-tests

Conversation

@StuartMeeks

Copy link
Copy Markdown
Owner

Fixes #57.

The gaps

Three, all in behaviour the type's own remarks lean on:

  1. DeleteOnClose was never asserted. It is what makes stale-lock recovery unnecessary — "there is no stale-lock recovery code to maintain". Acquire_AfterRelease_Succeeds claimed it in a comment and then only checked a second acquire worked, which it would even with a leftover file that the second acquirer simply reopened.
  2. The retry-exhaustion path was never reached. The ~5.1 s backoff ladder ending in a thrown IOException — its message, and whether it preserves the blocking exception — was unverified.
  3. The cross-process guarantee was only inferred, from one same-process contention test.

What was added

  • The sentinel is absent after dispose (asserted in two places).
  • A permanently held lock throws IOException naming the path, saying another process may be holding it, and preserving the inner exception.
  • Four concurrent contenders never overlap inside the critical section — mutual exclusion asserted directly, which is the property the lock exists for.

Red-proof

Replacing FileOptions.DeleteOnClose with FileOptions.None:

failed ... SelectionsLockTests.Acquire_AfterRelease_Succeeds
failed ... SelectionsLockTests.Acquire_SerialisesConcurrentHolders

Both passed against that same change before this PR — which is precisely the regression the old comment implied was covered.

Verification

Build clean, 408 tests (354 passed, 54 skipped), up from 404.

Note the exhaustion test costs ~5 s of wall clock by design, since shortening the ladder would mean testing something other than the shipped behaviour.

🤖 Generated with Claude Code

Three gaps, all in behaviour the type's own remarks rely on.

DeleteOnClose is what makes stale-lock recovery unnecessary -- "there is no
stale-lock recovery code to maintain" -- but no test asserted the sentinel is
actually gone after dispose. Acquire_AfterRelease_Succeeds said so in a comment
and then only checked that a second acquire worked, which it would even with a
leftover file that the second acquirer simply reopened.

The ~5.1s backoff ladder ending in a thrown IOException was never reached, so
neither its message nor its inner exception was verified.

And the lock's actual job -- serialising selections read-modify-write across
processes -- was only inferred from a single same-process contention test.

Now asserted: the sentinel is absent after dispose; a permanently held lock
throws IOException naming the path, saying another process may hold it, and
preserving the inner exception; and four concurrent contenders never overlap
inside the critical section.

Verified to bite: replacing DeleteOnClose with FileOptions.None fails
Acquire_AfterRelease_Succeeds and Acquire_SerialisesConcurrentHolders. Both
passed against that same change before.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@StuartMeeks
StuartMeeks merged commit f2ee317 into main Aug 25, 2026
9 checks passed
@StuartMeeks
StuartMeeks deleted the fix/57-selectionslock-tests branch August 25, 2026 03:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SelectionsLock tests never assert sentinel cleanup or the exhaustion path

1 participant