diff --git a/src/Verify.Tests/Naming/PrefixUniqueTests.cs b/src/Verify.Tests/Naming/PrefixUniqueTests.cs new file mode 100644 index 000000000..64e8b64ce --- /dev/null +++ b/src/Verify.Tests/Naming/PrefixUniqueTests.cs @@ -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( + () => 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"); + } +} diff --git a/src/Verify/Naming/PrefixUnique.cs b/src/Verify/Naming/PrefixUnique.cs index 2cb0188dd..cadbc093a 100644 --- a/src/Verify/Naming/PrefixUnique.cs +++ b/src/Verify/Naming/PrefixUnique.cs @@ -1,6 +1,9 @@ static class PrefixUnique { - static ConcurrentDictionary 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 prefixSet = new(StringComparer.OrdinalIgnoreCase); public static void CheckPrefixIsUnique(string prefix) { diff --git a/src/todo.md b/src/todo.md index 03784f69b..64655a49e 100644 --- a/src/todo.md +++ b/src/todo.md @@ -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.**