Skip to content
Merged
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
Expand Up @@ -108,12 +108,47 @@ internal async Task<TestResult[]> RunTestMethodAsync()
DebugEx.Assert(_test != null, "Test should not be null.");
DebugEx.Assert(_testMethodInfo.MethodInfo != null, "Test method should not be null.");

List<TestResult> results = [];
if (_testMethodInfo.Executor == null)
{
throw ApplicationStateGuard.Unreachable();
}

// Fast path for non-data-driven tests (the common case).
// A single attribute scan replaces the two scans done by TryExecuteDataSourceBasedTestsAsync and
// TryExecuteFoldedDataDrivenTestsAsync, and avoids allocating a List<TestResult> that would
// immediately be spread back into a TestResult[].
if (_test.DataType != DynamicDataType.ITestDataSource && !IsDataDrivenTest())
Comment thread
Evangelink marked this conversation as resolved.
Dismissed
{
_testContext.SetDisplayName(_test.DisplayName);
TestResult[] testResults = await ExecuteTestAsync(_testContext, _testMethodInfo).ConfigureAwait(false);

foreach (TestResult testResult in testResults)
{
if (StringEx.IsNullOrWhiteSpace(testResult.DisplayName))
{
testResult.DisplayName = _test.DisplayName;
}
}
Comment thread
Evangelink marked this conversation as resolved.
Dismissed

UnitTestOutcome fastPathOutcome = GetAggregateOutcome(testResults);
_testContext.SetOutcome(fastPathOutcome);

// Set a result in case no result is present, preserving the safeguard from the slow path
// (ExecuteAsync dereferences result[0] in its finally block).
return testResults.Length == 0
?
[
new TestResult
{
Outcome = fastPathOutcome,
TestFailureException = new TestFailedException(UnitTestOutcome.Error, Resource.UTA_NoTestResult),
},
]
: testResults;
}

// Slow path for data-driven tests.
List<TestResult> results = [];
bool isDataDriven = false;
if (_test.DataType == DynamicDataType.ITestDataSource)
{
Expand All @@ -137,6 +172,9 @@ internal async Task<TestResult[]> RunTestMethodAsync()
}
else
{
// IsDataDrivenTest() returned true but neither Try...Async method handled the test
// (e.g., multiple DataSourceAttributes → TryExecuteDataSourceBasedTestsAsync returns false).
// Execute as a normal non-data-driven test, preserving existing behavior.
_testContext.SetDisplayName(_test.DisplayName);
TestResult[] testResults = await ExecuteTestAsync(_testContext, _testMethodInfo).ConfigureAwait(false);

Expand Down Expand Up @@ -176,6 +214,26 @@ internal async Task<TestResult[]> RunTestMethodAsync()
return [.. results];
}

/// <summary>
/// Returns <see langword="true"/> if this test method has a <see cref="DataSourceAttribute"/> or
/// implements <see cref="UTF.ITestDataSource"/>, indicating it requires the data-driven execution path.
/// A single attribute-cache pass checks for both, replacing the two separate scans that would otherwise
/// be performed by <see cref="TryExecuteDataSourceBasedTestsAsync"/> and
/// <see cref="TryExecuteFoldedDataDrivenTestsAsync"/>.
/// </summary>
private bool IsDataDrivenTest()
{
foreach (Attribute attribute in PlatformServiceProvider.Instance.ReflectionOperations.GetCustomAttributesCached(_testMethodInfo.MethodInfo))
{
if (attribute is DataSourceAttribute or UTF.ITestDataSource)
{
return true;
}
}
Comment thread
Evangelink marked this conversation as resolved.
Dismissed

return false;
}

private async Task<bool> TryExecuteDataSourceBasedTestsAsync(List<TestResult> results)
{
// Iterate cached attributes directly to preserve the previous semantics:
Expand Down Expand Up @@ -476,7 +534,7 @@ private async Task<TestResult[]> ExecuteTestAsync(ITestContext executionContext,
/// </summary>
/// <param name="results">Results.</param>
/// <returns>Aggregate outcome.</returns>
private static UnitTestOutcome GetAggregateOutcome(List<TestResult> results)
private static UnitTestOutcome GetAggregateOutcome(IReadOnlyList<TestResult> results)
{
// In case results are not present, set outcome as unknown.
if (results.Count == 0)
Expand Down