Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
52 changes: 51 additions & 1 deletion src/Agent.Worker/TestResults/Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ public interface IParser : IExtension
string Name { get; }

TestDataProvider ParseTestResultFiles(IExecutionContext executionContext, TestRunContext testRunContext, List<string> testResultsFiles);

TestDataProvider ParseTestResultFiles(IExecutionContext executionContext, TestRunContext testRunContext, List<string> testResultsFiles, IFeatureFlagService featureFlagService);
}

public abstract class Parser : AgentService
Expand All @@ -24,7 +26,14 @@ public abstract class Parser : AgentService

protected abstract ITestResultParser GetTestResultParser(IExecutionContext executionContext);

protected abstract ITestResultParser GetTestResultParser(IExecutionContext executionContext, IFeatureFlagService featureFlagService);

public TestDataProvider ParseTestResultFiles(IExecutionContext executionContext, TestRunContext testRunContext, List<string> testResultsFiles)
{
return ParseTestResultFiles(executionContext, testRunContext, testResultsFiles, null);
}

public TestDataProvider ParseTestResultFiles(IExecutionContext executionContext, TestRunContext testRunContext, List<string> testResultsFiles, IFeatureFlagService featureFlagService)
{
ArgUtil.NotNull(executionContext, nameof(executionContext));
if (string.IsNullOrEmpty(Name))
Expand All @@ -33,7 +42,9 @@ public TestDataProvider ParseTestResultFiles(IExecutionContext executionContext,
return null;
}
// Create test result parser object based on the test Runner provided
var testResultParser = GetTestResultParser(executionContext);
var testResultParser = featureFlagService != null
? GetTestResultParser(executionContext, featureFlagService)
: GetTestResultParser(executionContext);
if (testResultParser == null)
{
return null;
Expand Down Expand Up @@ -76,6 +87,14 @@ protected override ITestResultParser GetTestResultParser(IExecutionContext execu
bool enableCustomTestFields = featureFlagService.GetFeatureFlagState(TestResultsConstants.CustomTestFieldsInPTRInputFilesEnabled, TestResultsConstants.TCMServiceInstanceGuid);
return new JUnitResultParser(traceListener, false, enableCustomTestFields);
}

[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA2000:Dispose objects before losing scope", MessageId = "CommandTraceListener")]
protected override ITestResultParser GetTestResultParser(IExecutionContext executionContext, IFeatureFlagService featureFlagService)
{
var traceListener = new CommandTraceListener(executionContext);
bool enableCustomTestFields = featureFlagService.GetFeatureFlagState(TestResultsConstants.CustomTestFieldsInPTRInputFilesEnabled, TestResultsConstants.TCMServiceInstanceGuid);
return new JUnitResultParser(traceListener, false, enableCustomTestFields);
}
}

public class XUnitParser : Parser, IParser
Expand All @@ -91,6 +110,13 @@ protected override ITestResultParser GetTestResultParser(IExecutionContext execu
return new XUnitResultParser(traceListener, setNameAsDisplayName: false, isTestCaseParallelReportingEnabled: false, enableCustomTestFields);
}

[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA2000:Dispose objects before losing scope", MessageId = "CommandTraceListener")]
protected override ITestResultParser GetTestResultParser(IExecutionContext executionContext, IFeatureFlagService featureFlagService)
{
var traceListener = new CommandTraceListener(executionContext);
bool enableCustomTestFields = featureFlagService.GetFeatureFlagState(TestResultsConstants.CustomTestFieldsInPTRInputFilesEnabled, TestResultsConstants.TCMServiceInstanceGuid);
return new XUnitResultParser(traceListener, setNameAsDisplayName: false, isTestCaseParallelReportingEnabled: false, enableCustomTestFields);
}
}

public class TrxParser : Parser, IParser
Expand All @@ -107,6 +133,14 @@ protected override ITestResultParser GetTestResultParser(IExecutionContext execu
return new TrxResultParser(traceListener, enableXUnitHeirarchicalParsing, enableCustomTestFields);
}

[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA2000:Dispose objects before losing scope", MessageId = "CommandTraceListener")]
protected override ITestResultParser GetTestResultParser(IExecutionContext executionContext, IFeatureFlagService featureFlagService)
{
var traceListener = new CommandTraceListener(executionContext);
var enableXUnitHeirarchicalParsing = featureFlagService.GetFeatureFlagState(TestResultsConstants.EnableXUnitHeirarchicalParsing, TestResultsConstants.TFSServiceInstanceGuid);
bool enableCustomTestFields = featureFlagService.GetFeatureFlagState(TestResultsConstants.CustomTestFieldsInPTRInputFilesEnabled, TestResultsConstants.TCMServiceInstanceGuid);
return new TrxResultParser(traceListener, enableXUnitHeirarchicalParsing, enableCustomTestFields);
}
}

public class NUnitParser : Parser, IParser
Expand All @@ -122,6 +156,13 @@ protected override ITestResultParser GetTestResultParser(IExecutionContext execu
return new NUnitResultParser(traceListener, isTestCaseParallelReportingEnabled: false, enableCustomTestFields);
}

[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA2000:Dispose objects before losing scope", MessageId = "CommandTraceListener")]
protected override ITestResultParser GetTestResultParser(IExecutionContext executionContext, IFeatureFlagService featureFlagService)
{
var traceListener = new CommandTraceListener(executionContext);
bool enableCustomTestFields = featureFlagService.GetFeatureFlagState(TestResultsConstants.CustomTestFieldsInPTRInputFilesEnabled, TestResultsConstants.TCMServiceInstanceGuid);
return new NUnitResultParser(traceListener, isTestCaseParallelReportingEnabled: false, enableCustomTestFields);
}
}

public class CTestParser : Parser, IParser
Expand All @@ -135,6 +176,10 @@ protected override ITestResultParser GetTestResultParser(IExecutionContext execu
return new CTestResultParser(traceListener);
}

protected override ITestResultParser GetTestResultParser(IExecutionContext executionContext, IFeatureFlagService featureFlagService)
{
return GetTestResultParser(executionContext);
}
}

public class ContainerStructureTestParser : Parser, IParser
Expand All @@ -147,5 +192,10 @@ protected override ITestResultParser GetTestResultParser(IExecutionContext execu
var traceListener = new CommandTraceListener(executionContext);
return new ContainerStructureTestResultParser(traceListener);
}

protected override ITestResultParser GetTestResultParser(IExecutionContext executionContext, IFeatureFlagService featureFlagService)
{
return GetTestResultParser(executionContext);
}
}
}
95 changes: 62 additions & 33 deletions src/Agent.Worker/TestResults/ResultsCommandExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,32 @@ public void Execute(IExecutionContext context, Command command)
string teamProject = context.Variables.System_TeamProject;

TestRunContext runContext = CreateTestRunContext();

// Capture mutable instance fields as locals before the async boundary.
// Execute() can be called again (overwriting fields) while PublishTestRunDataAsync is still running.
var executionContext = _executionContext;
var testResultFiles = _testResultFiles;
var testRunner = _testRunner;
var mergeResults = _mergeResults;
var runTitle = _runTitle;
var publishRunLevelAttachments = _publishRunLevelAttachments;
var testCaseResults = _testCaseResults;
var testPlanId = _testPlanId;
var publishTestResultsLibFeatureState = _publishTestResultsLibFeatureState;
var triggerCoverageMergeJobFeatureState = _triggerCoverageMergeJobFeatureState;
var failTaskOnFailedTests = _failTaskOnFailedTests;
var isDetectTestRunRetry = _isDetectTestRunRetry;
var telemetryProperties = _telemetryProperties;

var commandContext = context.GetHostContext().CreateService<IAsyncCommandContext>();
commandContext.InitializeCommandContext(context, StringUtil.Loc("PublishTestResults"));
commandContext.Task = PublishTestRunDataAsync(teamProject, runContext);
_executionContext.AsyncCommands.Add(commandContext);
commandContext.Task = PublishTestRunDataAsync(
executionContext, teamProject, runContext,
testResultFiles, testRunner, mergeResults, runTitle,
publishRunLevelAttachments, testCaseResults, testPlanId,
publishTestResultsLibFeatureState, triggerCoverageMergeJobFeatureState,
failTaskOnFailedTests, isDetectTestRunRetry, telemetryProperties);
executionContext.AsyncCommands.Add(commandContext);
}

private void LoadPublishTestResultsInputs(IExecutionContext context, Dictionary<string, string> eventProperties, string data)
Expand Down Expand Up @@ -298,59 +320,66 @@ private TestRunContext CreateTestRunContext()

}

private PublishOptions GetPublishOptions()
private PublishOptions GetPublishOptions(bool mergeResults, bool publishRunLevelAttachments, bool isDetectTestRunRetry)
{
var publishOptions = new PublishOptions()
{
IsMergeTestResultsToSingleRun = _mergeResults,
IsAddTestRunAttachments = _publishRunLevelAttachments,
IsDetectTestRunRetry = _isDetectTestRunRetry
IsMergeTestResultsToSingleRun = mergeResults,
IsAddTestRunAttachments = publishRunLevelAttachments,
IsDetectTestRunRetry = isDetectTestRunRetry
};

return publishOptions;
}

private async Task PublishTestRunDataAsync(string teamProject, TestRunContext testRunContext)
private async Task PublishTestRunDataAsync(
IExecutionContext executionContext, string teamProject, TestRunContext testRunContext,
List<string> testResultFiles, string testRunner, bool mergeResults, string runTitle,
bool publishRunLevelAttachments, TestCaseResult[] testCaseResults, string testPlanId,
bool publishTestResultsLibFeatureState, bool triggerCoverageMergeJobFeatureState,
bool failTaskOnFailedTests, bool isDetectTestRunRetry,
Dictionary<string, object> telemetryProperties)
{
bool isTestRunOutcomeFailed = false;

_telemetryProperties.Add("UsePublishTestResultsLib", _publishTestResultsLibFeatureState);
using (var connection = WorkerUtilities.GetVssConnection(_executionContext))
telemetryProperties.Add("UsePublishTestResultsLib", publishTestResultsLibFeatureState);
using (var connection = WorkerUtilities.GetVssConnection(executionContext))
{

//This check is to determine to use "Microsoft.TeamFoundation.PublishTestResults" Library or the agent code to parse and publish the test results.
if (_publishTestResultsLibFeatureState)
if (publishTestResultsLibFeatureState)
{
var publisher = _executionContext.GetHostContext().GetService<ITestDataPublisher>();
publisher.InitializePublisher(_executionContext, teamProject, connection, _testRunner);
var publisher = executionContext.GetHostContext().CreateService<ITestDataPublisher>();
publisher.InitializePublisher(executionContext, teamProject, connection, testRunner);

if (!_testCaseResults.IsNullOrEmpty() && !_testPlanId.IsNullOrEmpty())
var publishOptions = GetPublishOptions(mergeResults, publishRunLevelAttachments, isDetectTestRunRetry);
if (!testCaseResults.IsNullOrEmpty() && !testPlanId.IsNullOrEmpty())
{
isTestRunOutcomeFailed = await publisher.PublishAsync(testRunContext, _testResultFiles, _testCaseResults, GetPublishOptions(), _executionContext.CancellationToken);
isTestRunOutcomeFailed = await publisher.PublishAsync(testRunContext, testResultFiles, testCaseResults, publishOptions, executionContext.CancellationToken);
}
else
{
isTestRunOutcomeFailed = await publisher.PublishAsync(testRunContext, _testResultFiles, GetPublishOptions(), _executionContext.CancellationToken);
isTestRunOutcomeFailed = await publisher.PublishAsync(testRunContext, testResultFiles, publishOptions, executionContext.CancellationToken);
}
}
else
{
var publisher = _executionContext.GetHostContext().GetService<ILegacyTestRunDataPublisher>();
publisher.InitializePublisher(_executionContext, teamProject, connection, _testRunner, _publishRunLevelAttachments);
var publisher = executionContext.GetHostContext().CreateService<ILegacyTestRunDataPublisher>();
publisher.InitializePublisher(executionContext, teamProject, connection, testRunner, publishRunLevelAttachments);

isTestRunOutcomeFailed = await publisher.PublishAsync(testRunContext, _testResultFiles, _runTitle, _executionContext.Variables.Build_BuildId, _mergeResults);
isTestRunOutcomeFailed = await publisher.PublishAsync(testRunContext, testResultFiles, runTitle, executionContext.Variables.Build_BuildId, mergeResults);
}

if (isTestRunOutcomeFailed && _failTaskOnFailedTests)
if (isTestRunOutcomeFailed && failTaskOnFailedTests)
{
_executionContext.Result = TaskResult.Failed;
_executionContext.Error(StringUtil.Loc("FailedTestsInResults"));
executionContext.Result = TaskResult.Failed;
executionContext.Error(StringUtil.Loc("FailedTestsInResults"));
}

await PublishEventsAsync(connection);
if (_triggerCoverageMergeJobFeatureState)
await PublishEventsAsync(connection, executionContext, telemetryProperties);
if (triggerCoverageMergeJobFeatureState)
{
TriggerCoverageMergeJob(_testResultFiles, _executionContext);
TriggerCoverageMergeJob(testResultFiles, executionContext);
}
}
}
Expand All @@ -361,7 +390,7 @@ private void TriggerCoverageMergeJob(List<string> resultFilesInput, IExecutionCo
try
{
ITestResultsServer _testResultsServer = context.GetHostContext().GetService<ITestResultsServer>();
using (var connection = WorkerUtilities.GetVssConnection(_executionContext))
using (var connection = WorkerUtilities.GetVssConnection(context))
{
foreach (var resultFile in resultFilesInput)
{
Expand All @@ -381,14 +410,14 @@ private void TriggerCoverageMergeJob(List<string> resultFilesInput, IExecutionCo
Path.GetExtension(file).Equals(".coverage", StringComparison.OrdinalIgnoreCase)
)
{
_testResultsServer.InitializeServer(connection, _executionContext);
_testResultsServer.InitializeServer(connection, context);
try
{
var codeCoverageResults = _testResultsServer.UpdateCodeCoverageSummaryAsync(connection, _executionContext.Variables.System_TeamProjectId.ToString(), _executionContext.Variables.Build_BuildId.GetValueOrDefault());
var codeCoverageResults = _testResultsServer.UpdateCodeCoverageSummaryAsync(connection, context.Variables.System_TeamProjectId.ToString(), context.Variables.Build_BuildId.GetValueOrDefault());
}
catch (Exception e)
{
_executionContext.Section($"Could not queue code coverage merge:{e}");
context.Section($"Could not queue code coverage merge:{e}");
}
}
}
Expand All @@ -398,28 +427,28 @@ private void TriggerCoverageMergeJob(List<string> resultFilesInput, IExecutionCo
}
catch (Exception e)
{
_executionContext.Debug($"Exception in Method:{e.Message}");
context.Debug($"Exception in Method:{e.Message}");
}
}

private async Task PublishEventsAsync(VssConnection connection)
private async Task PublishEventsAsync(VssConnection connection, IExecutionContext executionContext, Dictionary<string, object> telemetryProperties)
{
try
{
CustomerIntelligenceEvent ciEvent = new CustomerIntelligenceEvent()
{
Area = _telemetryArea,
Feature = _telemetryFeature,
Properties = _telemetryProperties
Properties = telemetryProperties
};

var ciService = _executionContext.GetHostContext().GetService<ICustomerIntelligenceServer>();
var ciService = executionContext.GetHostContext().GetService<ICustomerIntelligenceServer>();
ciService.Initialize(connection);
await ciService.PublishEventsAsync(new CustomerIntelligenceEvent[] { ciEvent });
}
catch (Exception ex)
{
_executionContext.Debug(StringUtil.Loc("TelemetryCommandFailed", ex.Message));
executionContext.Debug(StringUtil.Loc("TelemetryCommandFailed", ex.Message));
}
}

Expand Down
Loading
Loading