Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions src/Verify.Tests/Naming/PrefixUniqueTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
public class PrefixUniqueTests
{
// The prefix maps to file names, and two prefixes differing only in case share
// one set of files on NTFS and APFS
[Fact]
public void CaseOnlyDifferenceIsNotUnique()
{
PrefixUnique.CheckPrefixIsUnique("PrefixUniqueTests.TheCase");

var exception = Assert.Throws<Exception>(
() => PrefixUnique.CheckPrefixIsUnique("prefixuniquetests.thecase"));

Assert.Contains("The prefix has already been used", exception.Message);
}

[Fact]
public void DistinctPrefixesAreUnique()
{
PrefixUnique.CheckPrefixIsUnique("PrefixUniqueTests.Distinct1");
PrefixUnique.CheckPrefixIsUnique("PrefixUniqueTests.Distinct2");
}
}
5 changes: 4 additions & 1 deletion src/Verify/Naming/PrefixUnique.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
static class PrefixUnique
{
static ConcurrentDictionary<string, byte> prefixSet = [];
// Ignoring case, since the prefix maps to file names and NTFS and APFS are both
// case insensitive. Two prefixes differing only in case would silently share one
// set of files there, and a snapshot repository has to work on every platform.
static ConcurrentDictionary<string, byte> prefixSet = new(StringComparer.OrdinalIgnoreCase);

public static void CheckPrefixIsUnique(string prefix)
{
Expand Down
2 changes: 1 addition & 1 deletion src/todo.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ All six resolved 2026-08-16 (five fixed here; the inline item resolved as by-des
- [ ] **Mismatch crash for handle-based `FileStream` received streams.**
`Verify/Compare/FileComparer.cs:50` — NotEqual fast path copies by `fileStream.Name` with no fallback; handle-based streams have `Name == "[Unknown]"`. First (New) run succeeds via the guarded `IoHelpers.WriteStream` path; later mismatches throw the generic "Failed to compare files".

- [ ] **`PrefixUnique` set is case-sensitive on case-insensitive filesystems.**
- [x] **`PrefixUnique` set is case-sensitive on case-insensitive filesystems.**
`Verify/Naming/PrefixUnique.cs:3` — methods `Foo` and `foo` map to the same files on NTFS/APFS but pass the uniqueness check and silently clobber each other.

- [ ] **`Counter` caches mix `Interlocked` counters with unsynchronized `Dictionary` writes.**
Expand Down
Loading