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
4 changes: 2 additions & 2 deletions docs/combinations.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,8 +216,8 @@ public class CombinationResultsConverter :
var keyValues = new string[items.Count, keysLength];

// Keys repeat across rows (a column only has as many distinct values as
// its input list), so cache the computed name per distinct key value.
var nameCache = new Dictionary<object, string>();
// its input list), so cache the computed name per key.
var nameCache = new Dictionary<object, string>(ReferenceComparer.Instance);
string? nullName = null;

for (var itemIndex = 0; itemIndex < items.Count; itemIndex++)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
2000-01-01Utc , 2000-01-01+0 : Utc 00:00:00,
2000-01-01Utc , 2000-01-01T01-00+1: Utc 01:00:00,
2000-01-01Local, 2000-01-01+0 : Local 00:00:00,
2000-01-01Local, 2000-01-01T01-00+1: Local 01:00:00,
2000-01-01 , 2000-01-01+0 : Unspecified 00:00:00,
2000-01-01 , 2000-01-01T01-00+1: Unspecified 01:00:00
}
20 changes: 20 additions & 0 deletions src/Verify.Tests/CombinationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -174,4 +174,24 @@ public Task RecordingPausedTest()
params1,
params2);
}

// DateTime.Equals ignores Kind and DateTimeOffset.Equals compares only the instant,
// while the rendered names include both, so keys that differ only in those must not
// share a cached name
[Fact]
public Task DateKeysThatCompareEqual() =>
Combination()
.Verify(
(dateTime, dateTimeOffset) => $"{dateTime.Kind} {dateTimeOffset.Offset}",
new List<DateTime>
{
new(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc),
new(2000, 1, 1, 0, 0, 0, DateTimeKind.Local),
new(2000, 1, 1, 0, 0, 0, DateTimeKind.Unspecified)
},
new List<DateTimeOffset>
{
new(2000, 1, 1, 0, 0, 0, TimeSpan.Zero),
new(2000, 1, 1, 1, 0, 0, TimeSpan.FromHours(1))
});
}
4 changes: 2 additions & 2 deletions src/Verify/Combinations/CombinationResultsConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ public override void Write(VerifyJsonWriter writer, CombinationResults results)
var keyValues = new string[items.Count, keysLength];

// Keys repeat across rows (a column only has as many distinct values as
// its input list), so cache the computed name per distinct key value.
var nameCache = new Dictionary<object, string>();
// its input list), so cache the computed name per key.
var nameCache = new Dictionary<object, string>(ReferenceComparer.Instance);
string? nullName = null;

for (var itemIndex = 0; itemIndex < items.Count; itemIndex++)
Expand Down
17 changes: 17 additions & 0 deletions src/Verify/Combinations/ReferenceComparer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System.Runtime.CompilerServices;

// Used for the combinations name cache. Each input list is materialized once, so a key
// recurs as the same boxed instance and reference equality is what makes the cache hit.
// Value equality would be wrong there: DateTime.Equals ignores Kind and
// DateTimeOffset.Equals compares only the instant, while both render into the name.
sealed class ReferenceComparer :
IEqualityComparer<object>
{
public static ReferenceComparer Instance = new();

public new bool Equals(object? x, object? y) =>
ReferenceEquals(x, y);

public int GetHashCode(object value) =>
RuntimeHelpers.GetHashCode(value);
}
2 changes: 1 addition & 1 deletion src/todo.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ All six resolved 2026-08-16 (five fixed here; the inline item resolved as by-des
- [ ] **Registering an `IgnoreInstance` predicate disables empty-collection ignoring for that type.**
`Verify/Serialization/SerializationSettings_ShouldIgnore.cs:26-40` — when predicates exist but none match, the early `return false` skips the `ignoreEmptyCollections` check at 42-47. An empty `List<string>` starts appearing as `[]` merely because an unrelated predicate was registered.

- [ ] **Combinations name cache collapses distinct keys.**
- [x] **Combinations name cache collapses distinct keys.**
`Verify/Combinations/CombinationResultsConverter.cs:32-54` — `Dictionary<object, string>` keyed on the boxed value: `DateTime.Equals` ignores `Kind`, `DateTimeOffset.Equals` compares only the instant, while the rendered names include Kind/offset. Inputs `2000-01-01 Utc` and `2000-01-01 Local` both get labeled `2000-01-01Utc`.

- [ ] **MSTest overloaded test methods resolve to the wrong `MethodInfo`.**
Expand Down
Loading