diff --git a/src/Verify.Tests/Naming/NameForParameterTests.StringHash.verified.txt b/src/Verify.Tests/Naming/NameForParameterTests.StringHash.verified.txt new file mode 100644 index 000000000..147b3b566 --- /dev/null +++ b/src/Verify.Tests/Naming/NameForParameterTests.StringHash.verified.txt @@ -0,0 +1 @@ +a-1 \ No newline at end of file diff --git a/src/Verify.Tests/Naming/NameForParameterTests.cs b/src/Verify.Tests/Naming/NameForParameterTests.cs index d006f67f7..bce0802e7 100644 --- a/src/Verify.Tests/Naming/NameForParameterTests.cs +++ b/src/Verify.Tests/Naming/NameForParameterTests.cs @@ -12,6 +12,12 @@ public Task StringEmpty() => public Task StringInvalidPathChar() => Verify(VerifierSettings.GetNameForParameter("a/a", counter: CounterBuilder.Empty())); + // `#` is reserved for the indexed-target namespace, so a value containing one + // would make this case's files look like the targets of the case without it + [Fact] + public Task StringHash() => + Verify(VerifierSettings.GetNameForParameter("a#1", counter: CounterBuilder.Empty())); + [Fact] public void CollectionItemPathFriendlyFalseNotCleaned() { diff --git a/src/Verify.Tests/Naming/NamerTests.ParametersWithHash_value=x-1.verified.txt b/src/Verify.Tests/Naming/NamerTests.ParametersWithHash_value=x-1.verified.txt new file mode 100644 index 000000000..1829444fc --- /dev/null +++ b/src/Verify.Tests/Naming/NamerTests.ParametersWithHash_value=x-1.verified.txt @@ -0,0 +1 @@ +x#1 \ No newline at end of file diff --git a/src/Verify.Tests/Naming/NamerTests.ParametersWithHash_value=x.verified.txt b/src/Verify.Tests/Naming/NamerTests.ParametersWithHash_value=x.verified.txt new file mode 100644 index 000000000..b18d45413 --- /dev/null +++ b/src/Verify.Tests/Naming/NamerTests.ParametersWithHash_value=x.verified.txt @@ -0,0 +1 @@ +x \ No newline at end of file diff --git a/src/Verify.Tests/Naming/NamerTests.cs b/src/Verify.Tests/Naming/NamerTests.cs index cb8702c19..725fc47c5 100644 --- a/src/Verify.Tests/Naming/NamerTests.cs +++ b/src/Verify.Tests/Naming/NamerTests.cs @@ -491,6 +491,15 @@ public Task ParametersWithBadPathChars(string value) => Verify(value) .UseParameters(value); + // `{prefix}#` is the indexed-target namespace, so without cleaning the `x` case + // would claim the files of the `x#1` case + [Theory] + [InlineData("x")] + [InlineData("x#1")] + public Task ParametersWithHash(string value) => + Verify(value) + .UseParameters(value); + [Fact] public Task SingleTarget() => Verify([new Target("txt", "data")]); diff --git a/src/Verify/FileNameCleaner.cs b/src/Verify/FileNameCleaner.cs index b7f987e00..2d34a19c7 100644 --- a/src/Verify/FileNameCleaner.cs +++ b/src/Verify/FileNameCleaner.cs @@ -42,7 +42,10 @@ ':', '*', '?', - '/' + '/', + // Not invalid on disk, but reserved: `{prefix}#` is the indexed-target namespace, + // so a `#` in a value would make one case's files look like another case's targets + '#' ]; static char[] invalidPathChars = diff --git a/src/todo.md b/src/todo.md index 03784f69b..b3b8b883a 100644 --- a/src/todo.md +++ b/src/todo.md @@ -35,7 +35,7 @@ All six resolved 2026-08-16 (five fixed here; the inline item resolved as by-des - [ ] **Sub-millisecond date parameters collide.** `Verify/Serialization/DateFormatter_DateTime.cs` (and the `DateTimeOffset` twin) use `Second == 0` / `Millisecond == 0` to omit the fraction, but sub-millisecond ticks leave those properties 0. `AddTicks(1)` and `AddTicks(2)` cases format identically → spurious "prefix has already been used" (or silent sharing of one verified file). Correct check is ticks-based. -- [ ] **`#` in parameter values collides with the indexed-target namespace.** +- [x] **`#` in parameter values collides with the indexed-target namespace.** `Verify/Naming/MatchingFileFinder.cs:9,20` — `indexedPattern: "{prefix}#"` matches by prefix, and `#` is not sanitized. Cases `"x"` and `"x#1"` on one method: running `"x"` deletes `C.M_p=x#1.received.txt` and sweeps `C.M_p=x#1.verified.txt` into the stale set (deleted under AutoVerify). - [ ] **Trimmed fraction format collapses into a standard format specifier.**