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
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1 2
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Value
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Value
26 changes: 26 additions & 0 deletions src/Verify.MSTest.Tests/OverloadTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Overloads share a name, so the data the test was invoked with is all there is
// to tell the MethodInfo of one from the other
[TestClass]
public partial class OverloadTests
{
[TestMethod]
[DataRow("Value")]
public Task Overload(string value) =>
Verify(value);

[TestMethod]
[DataRow(1, 2)]
public Task Overload(int first, int second) =>
Verify($"{first} {second}");

// same parameter count, so only the types of the data tell these apart
[TestMethod]
[DataRow("Value")]
public Task SameArityOverload(string text) =>
Verify(text);

[TestMethod]
[DataRow(1)]
public Task SameArityOverload(int number) =>
Verify(number);
}
114 changes: 111 additions & 3 deletions src/Verify.MSTest/TestExecutionContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,122 @@ static MethodInfo FindMethod(Type type, TestContext context)

var span = testName.AsSpan();

MethodInfo? first = null;
List<MethodInfo>? overloads = null;
foreach (var method in type.GetMethods(BindingFlags.Instance | BindingFlags.Public))
{
if (span.SequenceEqual(method.Name))
if (!span.SequenceEqual(method.Name))
{
return method;
continue;
}

if (first is null)
{
first = method;
continue;
}

overloads ??= [first];
overloads.Add(method);
}

if (first is null)
{
throw new($"Could not find method `{type.Name}.{testName}`.");
}

if (overloads is null)
{
return first;
}

// Overloads share a name, so the data the test was invoked with is all there is
// to tell them apart. Falls back to the first when it does not narrow to one.
return FindOverload(overloads, context.TestData) ?? first;
}

static MethodInfo? FindOverload(List<MethodInfo> overloads, object?[]? data)
{
data ??= [];

List<MethodInfo> byCount = [];
foreach (var overload in overloads)
{
if (MatchesCount(overload, data.Length))
{
byCount.Add(overload);
}
}

if (byCount.Count <= 1)
{
return byCount.FirstOrDefault();
}

MethodInfo? byType = null;
foreach (var overload in byCount)
{
if (!MatchesTypes(overload, data))
{
continue;
}

if (byType is not null)
{
return null;
}

byType = overload;
}

return byType;
}

static bool MatchesCount(MethodInfo method, int dataLength)
{
var parameters = method.GetParameters();
if (parameters.Length == dataLength)
{
return true;
}

// A params array DataRow exposes raw pre-binding data, so its length only has
// to cover the parameters that precede the array
return parameters.Length > 0 &&
parameters[^1].IsDefined(typeof(ParamArrayAttribute)) &&
dataLength >= parameters.Length - 1;
}

static bool MatchesTypes(MethodInfo method, object?[] data)
{
var parameters = method.GetParameters();
if (parameters.Length != data.Length)
{
return false;
}

for (var index = 0; index < parameters.Length; index++)
{
var type = parameters[index].ParameterType;
var value = data[index];

if (value is null)
{
if (type.IsValueType &&
Nullable.GetUnderlyingType(type) is null)
{
return false;
}

continue;
}

if (!type.IsInstanceOfType(value))
{
return false;
}
}

throw new($"Could not find method `{type.Name}.{testName}`.");
return true;
}
}
2 changes: 1 addition & 1 deletion src/todo.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ All six resolved 2026-08-16 (five fixed here; the inline item resolved as by-des
- [ ] **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`.**
- [x] **MSTest overloaded test methods resolve to the wrong `MethodInfo`.**
`Verify.MSTest/TestExecutionContext.cs:24-30` — `FindMethod` returns the first name match, ignoring parameters. With two `[DataRow]` overloads of one name, the parameter-count guard in `Verifier.BuildVerifier` mismatches for one of them → `SetParameters` silently skipped → both overloads collide on one snapshot prefix.

- [ ] **`Delete:` section drops subdirectories, breaking the parse round-trip.**
Expand Down
Loading