diff --git a/src/Microsoft.TestPlatform.Client/AttachmentsProcessing/TestRunAttachmentsProcessingEventsHandler.cs b/src/Microsoft.TestPlatform.Client/AttachmentsProcessing/TestRunAttachmentsProcessingEventsHandler.cs
new file mode 100644
index 0000000000..6bceff7f24
--- /dev/null
+++ b/src/Microsoft.TestPlatform.Client/AttachmentsProcessing/TestRunAttachmentsProcessingEventsHandler.cs
@@ -0,0 +1,77 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace Microsoft.VisualStudio.TestPlatform.Client.TestRunAttachmentsProcessing
+{
+ using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Interfaces;
+ using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.ObjectModel;
+ using Microsoft.VisualStudio.TestPlatform.ObjectModel;
+ using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
+ using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
+ using System.Collections.Generic;
+
+ ///
+ /// The test run attachments processing events handler.
+ ///
+ ///
+ public class TestRunAttachmentsProcessingEventsHandler : ITestRunAttachmentsProcessingEventsHandler
+ {
+ private readonly ICommunicationManager communicationManager;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The communication manager.
+ public TestRunAttachmentsProcessingEventsHandler(ICommunicationManager communicationManager)
+ {
+ this.communicationManager = communicationManager;
+ }
+
+ ///
+ public void HandleTestRunAttachmentsProcessingComplete(TestRunAttachmentsProcessingCompleteEventArgs attachmentsProcessingCompleteEventArgs, IEnumerable lastChunk)
+ {
+ if (EqtTrace.IsInfoEnabled)
+ {
+ EqtTrace.Info("Test run attachments processing completed.");
+ }
+
+ var payload = new TestRunAttachmentsProcessingCompletePayload()
+ {
+ AttachmentsProcessingCompleteEventArgs = attachmentsProcessingCompleteEventArgs,
+ Attachments = lastChunk
+ };
+
+ this.communicationManager.SendMessage(MessageType.TestRunAttachmentsProcessingComplete, payload);
+ }
+
+ ///
+ public void HandleTestRunAttachmentsProcessingProgress(TestRunAttachmentsProcessingProgressEventArgs attachmentsProcessingProgressEventArgs)
+ {
+ var payload = new TestRunAttachmentsProcessingProgressPayload()
+ {
+ AttachmentsProcessingProgressEventArgs = attachmentsProcessingProgressEventArgs,
+ };
+
+ this.communicationManager.SendMessage(MessageType.TestRunAttachmentsProcessingProgress, payload);
+ }
+
+ ///
+ public void HandleProcessedAttachmentsChunk(IEnumerable attachments)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ ///
+ public void HandleLogMessage(TestMessageLevel level, string message)
+ {
+ var testMessagePayload = new TestMessagePayload { MessageLevel = level, Message = message };
+ this.communicationManager.SendMessage(MessageType.TestMessage, testMessagePayload);
+ }
+
+ ///
+ public void HandleRawMessage(string rawMessage)
+ {
+ // No-Op
+ }
+ }
+}
diff --git a/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeClient.cs b/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeClient.cs
index e4366b4c8b..6b16b27a55 100644
--- a/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeClient.cs
+++ b/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeClient.cs
@@ -9,7 +9,7 @@ namespace Microsoft.VisualStudio.TestPlatform.Client.DesignMode
using System.Net;
using System.Threading;
using System.Threading.Tasks;
-
+ using Microsoft.VisualStudio.TestPlatform.Client.TestRunAttachmentsProcessing;
using Microsoft.VisualStudio.TestPlatform.Client.RequestHelper;
using Microsoft.VisualStudio.TestPlatform.Common.Logging;
using Microsoft.VisualStudio.TestPlatform.Common.Utilities;
@@ -199,6 +199,14 @@ private void ProcessRequests(ITestRequestManager testRequestManager)
break;
}
+ case MessageType.TestRunAttachmentsProcessingStart:
+ {
+ var testRunAttachmentsProcessingPayload =
+ this.communicationManager.DeserializePayload(message);
+ this.StartTestRunAttachmentsProcessing(testRunAttachmentsProcessingPayload, testRequestManager);
+ break;
+ }
+
case MessageType.CancelDiscovery:
{
testRequestManager.CancelDiscovery();
@@ -217,6 +225,12 @@ private void ProcessRequests(ITestRequestManager testRequestManager)
break;
}
+ case MessageType.TestRunAttachmentsProcessingCancel:
+ {
+ testRequestManager.CancelTestRunAttachmentsProcessing();
+ break;
+ }
+
case MessageType.CustomTestHostLaunchCallback:
{
this.onCustomTestHostLaunchAckReceived?.Invoke(message);
@@ -458,6 +472,33 @@ private void StartDiscovery(DiscoveryRequestPayload discoveryRequestPayload, ITe
});
}
+ private void StartTestRunAttachmentsProcessing(TestRunAttachmentsProcessingPayload attachmentsProcessingPayload, ITestRequestManager testRequestManager)
+ {
+ Task.Run(
+ delegate
+ {
+ try
+ {
+ testRequestManager.ProcessTestRunAttachments(attachmentsProcessingPayload, new TestRunAttachmentsProcessingEventsHandler(this.communicationManager), this.protocolConfig);
+ }
+ catch (Exception ex)
+ {
+ EqtTrace.Error("DesignModeClient: Exception in StartTestRunAttachmentsProcessing: " + ex);
+
+ var testMessagePayload = new TestMessagePayload { MessageLevel = TestMessageLevel.Error, Message = ex.ToString() };
+ this.communicationManager.SendMessage(MessageType.TestMessage, testMessagePayload);
+
+ var payload = new TestRunAttachmentsProcessingCompletePayload()
+ {
+ Attachments = null
+ };
+
+ // Send run complete to translation layer
+ this.communicationManager.SendMessage(MessageType.TestRunAttachmentsProcessingComplete, payload);
+ }
+ });
+ }
+
#region IDisposable Support
private bool disposedValue = false; // To detect redundant calls
diff --git a/src/Microsoft.TestPlatform.Client/RequestHelper/ITestRequestManager.cs b/src/Microsoft.TestPlatform.Client/RequestHelper/ITestRequestManager.cs
index 45e6a655f6..0a0e68e301 100644
--- a/src/Microsoft.TestPlatform.Client/RequestHelper/ITestRequestManager.cs
+++ b/src/Microsoft.TestPlatform.Client/RequestHelper/ITestRequestManager.cs
@@ -44,6 +44,13 @@ public interface ITestRequestManager : IDisposable
/// Protocol related information
void RunTests(TestRunRequestPayload testRunRequestPayLoad, ITestHostLauncher customTestHostLauncher, ITestRunEventsRegistrar testRunEventsRegistrar, ProtocolConfig protocolConfig);
+ ///
+ /// Processes test run attachments
+ ///
+ /// Test run attachments processing payload
+ /// Test run attachments processing events handler
+ void ProcessTestRunAttachments(TestRunAttachmentsProcessingPayload testRunAttachmentsProcessingPayload, ITestRunAttachmentsProcessingEventsHandler testRunAttachmentsProcessingEventsHandler, ProtocolConfig protocolConfig);
+
///
/// Cancel the current TestRun request
///
@@ -58,5 +65,10 @@ public interface ITestRequestManager : IDisposable
/// Cancels the current discovery request
///
void CancelDiscovery();
+
+ ///
+ /// Cancels the current test run attachments processing request
+ ///
+ void CancelTestRunAttachmentsProcessing();
}
}
diff --git a/src/Microsoft.TestPlatform.Common/Interfaces/Engine/ITestRunAttachmentsProcessingManager.cs b/src/Microsoft.TestPlatform.Common/Interfaces/Engine/ITestRunAttachmentsProcessingManager.cs
new file mode 100644
index 0000000000..d6b88d89c8
--- /dev/null
+++ b/src/Microsoft.TestPlatform.Common/Interfaces/Engine/ITestRunAttachmentsProcessingManager.cs
@@ -0,0 +1,33 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine
+{
+ ///
+ /// Orchestrates test run attachments processing operations.
+ ///
+ internal interface ITestRunAttachmentsProcessingManager
+ {
+ ///
+ /// Processes attachments and provides results through handler
+ ///
+ /// Collection of attachments
+ /// EventHandler for handling test run attachments processing event
+ /// Cancellation token
+ Task ProcessTestRunAttachmentsAsync(IRequestData requestData, IEnumerable attachments, ITestRunAttachmentsProcessingEventsHandler eventHandler, CancellationToken cancellationToken);
+
+ ///
+ /// Processes attachments
+ ///
+ /// Collection of attachments
+ /// Cancellation token
+ /// Collection of attachments.
+ Task> ProcessTestRunAttachmentsAsync(IRequestData requestData, IEnumerable attachments, CancellationToken cancellationToken);
+ }
+}
diff --git a/src/Microsoft.TestPlatform.Common/Telemetry/TelemetryDataConstants.cs b/src/Microsoft.TestPlatform.Common/Telemetry/TelemetryDataConstants.cs
index b7a277e1e1..be35c610c2 100644
--- a/src/Microsoft.TestPlatform.Common/Telemetry/TelemetryDataConstants.cs
+++ b/src/Microsoft.TestPlatform.Common/Telemetry/TelemetryDataConstants.cs
@@ -90,9 +90,19 @@ public static class TelemetryDataConstants
public static string NumberOfAdapterUsedToDiscoverTests = "VS.TestDiscovery.AdaptersUsedCount";
+ // *********************Attachments Processing****************************
+ public static string NumberOfAttachmentsSentForProcessing = "VS.AttachmentsProcessing.InitialAttachmentsCount";
+
+ public static string NumberOfAttachmentsAfterProcessing = "VS.AttachmentsProcessing.FinalAttachmentsCount";
+
+ public static string TimeTakenInSecForAttachmentsProcessing = "VS.AttachmentsProcessing.TotalTimeTakenInSec";
+ public static string AttachmentsProcessingState = "VS.AttachmentsProcessing.State";
+
// **************Events Name **********************************
public static string TestDiscoveryCompleteEvent = "vs/testplatform/testdiscoverysession";
public static string TestExecutionCompleteEvent = "vs/testplatform/testrunsession";
+
+ public static string TestAttachmentsProcessingCompleteEvent = "vs/testplatform/testattachmentsprocessingsession";
}
}
diff --git a/src/Microsoft.TestPlatform.CommunicationUtilities/Messages/MessageType.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/Messages/MessageType.cs
index 52a74e7f0b..76cfeec873 100644
--- a/src/Microsoft.TestPlatform.CommunicationUtilities/Messages/MessageType.cs
+++ b/src/Microsoft.TestPlatform.CommunicationUtilities/Messages/MessageType.cs
@@ -123,6 +123,26 @@ public static class MessageType
///
public const string CustomTestHostLaunchCallback = "TestExecution.CustomTestHostLaunchCallback";
+ ///
+ /// Test run attachments processing
+ ///
+ public const string TestRunAttachmentsProcessingStart = "TestRunAttachmentsProcessing.Start";
+
+ ///
+ /// Test run attachments processing callback
+ ///
+ public const string TestRunAttachmentsProcessingComplete = "TestRunAttachmentsProcessing.Complete";
+
+ ///
+ /// Test run attachments processing progress
+ ///
+ public const string TestRunAttachmentsProcessingProgress = "TestRunAttachmentsProcessing.Progress";
+
+ ///
+ /// Cancel test run attachments processing
+ ///
+ public const string TestRunAttachmentsProcessingCancel = "TestRunAttachmentsProcessing.Cancel";
+
///
/// Extensions Initialization
///
diff --git a/src/Microsoft.TestPlatform.CommunicationUtilities/Messages/TestRunAttachmentsProcessingCompletePayload.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/Messages/TestRunAttachmentsProcessingCompletePayload.cs
new file mode 100644
index 0000000000..40ac9c4c8b
--- /dev/null
+++ b/src/Microsoft.TestPlatform.CommunicationUtilities/Messages/TestRunAttachmentsProcessingCompletePayload.cs
@@ -0,0 +1,26 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.ObjectModel
+{
+ using System.Collections.Generic;
+
+ using Microsoft.VisualStudio.TestPlatform.ObjectModel;
+ using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
+
+ ///
+ /// Test run attachments processing complete payload.
+ ///
+ public class TestRunAttachmentsProcessingCompletePayload
+ {
+ ///
+ /// Gets or sets the test run attachments processing complete args.
+ ///
+ public TestRunAttachmentsProcessingCompleteEventArgs AttachmentsProcessingCompleteEventArgs { get; set; }
+
+ ///
+ /// Gets or sets the attachments.
+ ///
+ public IEnumerable Attachments { get; set; }
+ }
+}
diff --git a/src/Microsoft.TestPlatform.CommunicationUtilities/Messages/TestRunAttachmentsProcessingProgressPayload.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/Messages/TestRunAttachmentsProcessingProgressPayload.cs
new file mode 100644
index 0000000000..80082ae30d
--- /dev/null
+++ b/src/Microsoft.TestPlatform.CommunicationUtilities/Messages/TestRunAttachmentsProcessingProgressPayload.cs
@@ -0,0 +1,18 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.ObjectModel
+{
+ using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
+
+ ///
+ /// Test run attachments processing complete payload.
+ ///
+ public class TestRunAttachmentsProcessingProgressPayload
+ {
+ ///
+ /// Gets or sets the test run attachments processing complete args.
+ ///
+ public TestRunAttachmentsProcessingProgressEventArgs AttachmentsProcessingProgressEventArgs { get; set; }
+ }
+}
diff --git a/src/Microsoft.TestPlatform.CoreUtilities/Tracing/Interfaces/ITestPlatformEventSource.cs b/src/Microsoft.TestPlatform.CoreUtilities/Tracing/Interfaces/ITestPlatformEventSource.cs
index 137924573b..7f88ccbf83 100644
--- a/src/Microsoft.TestPlatform.CoreUtilities/Tracing/Interfaces/ITestPlatformEventSource.cs
+++ b/src/Microsoft.TestPlatform.CoreUtilities/Tracing/Interfaces/ITestPlatformEventSource.cs
@@ -182,5 +182,41 @@ public interface ITestPlatformEventSource
/// Mark the completion of Metrics Dispose.
///
void MetricsDisposeStop();
+
+ ///
+ /// The test run attachments processing request start.
+ ///
+ void TestRunAttachmentsProcessingRequestStart();
+
+ ///
+ /// The test run attachments processing request stop.
+ ///
+ void TestRunAttachmentsProcessingRequestStop();
+
+ ///
+ /// The test run attachments processing start.
+ ///
+ ///
+ /// The number of attachments.
+ ///
+ void TestRunAttachmentsProcessingStart(long numberOfAttachments);
+
+ ///
+ /// The test run attachments processing stop.
+ ///
+ ///
+ /// The number of attachments.
+ ///
+ void TestRunAttachmentsProcessingStop(long numberOfAttachments);
+
+ ///
+ /// Mark the start of translation layer test run attachments processing request.
+ ///
+ void TranslationLayerTestRunAttachmentsProcessingStart();
+
+ ///
+ /// Mark the completion of translation layer test run attachments processing request.
+ ///
+ void TranslationLayerTestRunAttachmentsProcessingStop();
}
}
diff --git a/src/Microsoft.TestPlatform.CoreUtilities/Tracing/TestPlatformEventSource.cs b/src/Microsoft.TestPlatform.CoreUtilities/Tracing/TestPlatformEventSource.cs
index 10e71e7a91..74adb6a2af 100644
--- a/src/Microsoft.TestPlatform.CoreUtilities/Tracing/TestPlatformEventSource.cs
+++ b/src/Microsoft.TestPlatform.CoreUtilities/Tracing/TestPlatformEventSource.cs
@@ -237,5 +237,47 @@ public void MetricsDisposeStop()
{
this.WriteEvent(TestPlatformInstrumentationEvents.MetricsDisposeStopEventId);
}
+
+ ///
+ [Event(TestPlatformInstrumentationEvents.TestRunAttachmentsProcessingRequestStartEventId)]
+ public void TestRunAttachmentsProcessingRequestStart()
+ {
+ this.WriteEvent(TestPlatformInstrumentationEvents.TestRunAttachmentsProcessingRequestStartEventId);
+ }
+
+ ///
+ [Event(TestPlatformInstrumentationEvents.TestRunAttachmentsProcessingRequestStopEventId)]
+ public void TestRunAttachmentsProcessingRequestStop()
+ {
+ this.WriteEvent(TestPlatformInstrumentationEvents.TestRunAttachmentsProcessingRequestStopEventId);
+ }
+
+ ///
+ [Event(TestPlatformInstrumentationEvents.TestRunAttachmentsProcessingStartEventId)]
+ public void TestRunAttachmentsProcessingStart(long numberOfAttachments)
+ {
+ this.WriteEvent(TestPlatformInstrumentationEvents.TestRunAttachmentsProcessingStartEventId, numberOfAttachments);
+ }
+
+ ///
+ [Event(TestPlatformInstrumentationEvents.TestRunAttachmentsProcessingStopEventId)]
+ public void TestRunAttachmentsProcessingStop(long numberOfAttachments)
+ {
+ this.WriteEvent(TestPlatformInstrumentationEvents.TestRunAttachmentsProcessingStopEventId, numberOfAttachments);
+ }
+
+ ///
+ [Event(TestPlatformInstrumentationEvents.TranslationLayerTestRunAttachmentsProcessingStartEventId)]
+ public void TranslationLayerTestRunAttachmentsProcessingStart()
+ {
+ this.WriteEvent(TestPlatformInstrumentationEvents.TranslationLayerTestRunAttachmentsProcessingStartEventId);
+ }
+
+ ///
+ [Event(TestPlatformInstrumentationEvents.TranslationLayerTestRunAttachmentsProcessingStopEventId)]
+ public void TranslationLayerTestRunAttachmentsProcessingStop()
+ {
+ this.WriteEvent(TestPlatformInstrumentationEvents.TranslationLayerTestRunAttachmentsProcessingStopEventId);
+ }
}
}
diff --git a/src/Microsoft.TestPlatform.CoreUtilities/Tracing/TestPlatformInstrumentationEvents.cs b/src/Microsoft.TestPlatform.CoreUtilities/Tracing/TestPlatformInstrumentationEvents.cs
index 629d8cd45c..1aa2311048 100644
--- a/src/Microsoft.TestPlatform.CoreUtilities/Tracing/TestPlatformInstrumentationEvents.cs
+++ b/src/Microsoft.TestPlatform.CoreUtilities/Tracing/TestPlatformInstrumentationEvents.cs
@@ -157,5 +157,35 @@ internal class TestPlatformInstrumentationEvents
/// Event fired on Metrics Dispose completes.
///
public const int MetricsDisposeStopEventId = 0x39;
+
+ ///
+ /// The session attachments processing start event id.
+ ///
+ public const int TestRunAttachmentsProcessingStartEventId = 0x40;
+
+ ///
+ /// The session attachments processing stop event id.
+ ///
+ public const int TestRunAttachmentsProcessingStopEventId = 0x41;
+
+ ///
+ /// The session attachments processing request start event id.
+ ///
+ public const int TestRunAttachmentsProcessingRequestStartEventId = 0x42;
+
+ ///
+ /// The session attachments processing request stop event id.
+ ///
+ public const int TestRunAttachmentsProcessingRequestStopEventId = 0x43;
+
+ ///
+ /// Events fired on session attachments processing start of translation layer.
+ ///
+ public const int TranslationLayerTestRunAttachmentsProcessingStartEventId = 0x44;
+
+ ///
+ /// Events fired on session attachments processing complete in translation layer.
+ ///
+ public const int TranslationLayerTestRunAttachmentsProcessingStopEventId = 0x45;
}
}
\ No newline at end of file
diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/AttachmentsProcessing/TestRunAttachmentsProcessingManager.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/AttachmentsProcessing/TestRunAttachmentsProcessingManager.cs
new file mode 100644
index 0000000000..6eff75838a
--- /dev/null
+++ b/src/Microsoft.TestPlatform.CrossPlatEngine/AttachmentsProcessing/TestRunAttachmentsProcessingManager.cs
@@ -0,0 +1,180 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.VisualStudio.TestPlatform.Common.Telemetry;
+using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Tracing.Interfaces;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
+
+namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.TestRunAttachmentsProcessing
+{
+ ///
+ /// Orchestrates test run attachments processing operations.
+ ///
+ public class TestRunAttachmentsProcessingManager : ITestRunAttachmentsProcessingManager
+ {
+ private static string AttachmentsProcessingCompleted = "Completed";
+ private static string AttachmentsProcessingCanceled = "Canceled";
+ private static string AttachmentsProcessingFailed = "Failed";
+
+ private readonly ITestPlatformEventSource testPlatformEventSource;
+ private readonly IDataCollectorAttachmentProcessor[] dataCollectorAttachmentsProcessors;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public TestRunAttachmentsProcessingManager(ITestPlatformEventSource testPlatformEventSource, params IDataCollectorAttachmentProcessor[] dataCollectorAttachmentsProcessors)
+ {
+ this.testPlatformEventSource = testPlatformEventSource ?? throw new ArgumentNullException(nameof(testPlatformEventSource));
+ this.dataCollectorAttachmentsProcessors = dataCollectorAttachmentsProcessors ?? throw new ArgumentNullException(nameof(dataCollectorAttachmentsProcessors));
+ }
+
+ ///
+ public async Task ProcessTestRunAttachmentsAsync(IRequestData requestData, IEnumerable attachments, ITestRunAttachmentsProcessingEventsHandler eventHandler, CancellationToken cancellationToken)
+ {
+ await InternalProcessTestRunAttachmentsAsync(requestData, new Collection(attachments.ToList()), eventHandler, cancellationToken).ConfigureAwait(false);
+ }
+ ///
+ public Task> ProcessTestRunAttachmentsAsync(IRequestData requestData, IEnumerable attachments, CancellationToken cancellationToken)
+ {
+ return InternalProcessTestRunAttachmentsAsync(requestData, new Collection(attachments.ToList()), null, cancellationToken);
+ }
+
+ private async Task> InternalProcessTestRunAttachmentsAsync(IRequestData requestData, Collection attachments, ITestRunAttachmentsProcessingEventsHandler eventHandler, CancellationToken cancellationToken)
+ {
+ Stopwatch stopwatch = Stopwatch.StartNew();
+
+ try
+ {
+ testPlatformEventSource.TestRunAttachmentsProcessingStart(attachments?.Count ?? 0);
+ requestData.MetricsCollection.Add(TelemetryDataConstants.NumberOfAttachmentsSentForProcessing, attachments?.Count ?? 0);
+
+ cancellationToken.ThrowIfCancellationRequested();
+
+ var taskCompletionSource = new TaskCompletionSource>();
+ using (cancellationToken.Register(() => taskCompletionSource.TrySetCanceled()))
+ {
+ Task> task = Task.Run(async () => await ProcessAttachmentsAsync(new Collection(attachments.ToList()), eventHandler, cancellationToken));
+
+ var completedTask = await Task.WhenAny(task, taskCompletionSource.Task).ConfigureAwait(false);
+
+ if (completedTask == task)
+ {
+ return FinalizeOperation(requestData, new TestRunAttachmentsProcessingCompleteEventArgs(false, null), await task, stopwatch, eventHandler);
+ }
+ else
+ {
+ eventHandler?.HandleLogMessage(TestMessageLevel.Informational, "Attachments processing was cancelled.");
+ return FinalizeOperation(requestData, new TestRunAttachmentsProcessingCompleteEventArgs(true, null), attachments, stopwatch, eventHandler);
+ }
+ }
+ }
+ catch (OperationCanceledException)
+ {
+ if (EqtTrace.IsWarningEnabled)
+ {
+ EqtTrace.Warning("TestRunAttachmentsProcessingManager: operation was cancelled.");
+ }
+ return FinalizeOperation(requestData, new TestRunAttachmentsProcessingCompleteEventArgs(true, null), attachments, stopwatch, eventHandler);
+ }
+ catch (Exception e)
+ {
+ EqtTrace.Error("TestRunAttachmentsProcessingManager: Exception in ProcessTestRunAttachmentsAsync: " + e);
+
+ eventHandler?.HandleLogMessage(TestMessageLevel.Error, e.Message);
+ return FinalizeOperation(requestData, new TestRunAttachmentsProcessingCompleteEventArgs(false, e), attachments, stopwatch, eventHandler);
+ }
+ }
+
+ private async Task> ProcessAttachmentsAsync(Collection attachments, ITestRunAttachmentsProcessingEventsHandler eventsHandler, CancellationToken cancellationToken)
+ {
+ if (attachments == null || !attachments.Any()) return attachments;
+
+ var logger = CreateMessageLogger(eventsHandler);
+
+ for (int i = 0; i < dataCollectorAttachmentsProcessors.Length; i++)
+ {
+ var dataCollectorAttachmentsProcessor = dataCollectorAttachmentsProcessors[i];
+ int attachmentsHandlerIndex = i + 1;
+
+ ICollection attachmentProcessorUris = dataCollectorAttachmentsProcessor.GetExtensionUris()?.ToList();
+ if (attachmentProcessorUris != null && attachmentProcessorUris.Any())
+ {
+ var attachmentsToBeProcessed = attachments.Where(dataCollectionAttachment => attachmentProcessorUris.Any(uri => uri.Equals(dataCollectionAttachment.Uri))).ToArray();
+ if (attachmentsToBeProcessed.Any())
+ {
+ foreach (var attachment in attachmentsToBeProcessed)
+ {
+ attachments.Remove(attachment);
+ }
+
+ IProgress progressReporter = new Progress((int progress) =>
+ eventsHandler?.HandleTestRunAttachmentsProcessingProgress(
+ new TestRunAttachmentsProcessingProgressEventArgs(attachmentsHandlerIndex, attachmentProcessorUris, progress, dataCollectorAttachmentsProcessors.Length)));
+
+ ICollection processedAttachments = await dataCollectorAttachmentsProcessor.ProcessAttachmentSetsAsync(new Collection(attachmentsToBeProcessed), progressReporter, logger, cancellationToken).ConfigureAwait(false);
+
+ foreach (var attachment in processedAttachments)
+ {
+ attachments.Add(attachment);
+ }
+ }
+ }
+ }
+
+ return attachments;
+ }
+
+ private Collection FinalizeOperation(IRequestData requestData, TestRunAttachmentsProcessingCompleteEventArgs completeArgs, Collection attachments, Stopwatch stopwatch, ITestRunAttachmentsProcessingEventsHandler eventHandler)
+ {
+ testPlatformEventSource.TestRunAttachmentsProcessingStop(attachments.Count);
+ requestData.MetricsCollection.Add(TelemetryDataConstants.NumberOfAttachmentsAfterProcessing, attachments.Count);
+ requestData.MetricsCollection.Add(TelemetryDataConstants.AttachmentsProcessingState, completeArgs.Error != null ? AttachmentsProcessingFailed : completeArgs.IsCanceled ? AttachmentsProcessingCanceled : AttachmentsProcessingCompleted);
+
+ stopwatch.Stop();
+ requestData.MetricsCollection.Add(TelemetryDataConstants.TimeTakenInSecForAttachmentsProcessing, stopwatch.Elapsed.TotalSeconds);
+
+ completeArgs.Metrics = requestData.MetricsCollection.Metrics;
+ eventHandler?.HandleTestRunAttachmentsProcessingComplete(completeArgs, attachments);
+
+ return attachments;
+ }
+
+ private IMessageLogger CreateMessageLogger(ITestRunAttachmentsProcessingEventsHandler eventsHandler)
+ {
+ return eventsHandler != null ? (IMessageLogger)new AttachmentsProcessingMessageLogger(eventsHandler) : new NullMessageLogger();
+ }
+
+ private class AttachmentsProcessingMessageLogger : IMessageLogger
+ {
+ private readonly ITestRunAttachmentsProcessingEventsHandler eventsHandler;
+
+ public AttachmentsProcessingMessageLogger(ITestRunAttachmentsProcessingEventsHandler eventsHandler)
+ {
+ this.eventsHandler = eventsHandler ?? throw new ArgumentNullException(nameof(eventsHandler));
+ }
+
+ public void SendMessage(TestMessageLevel testMessageLevel, string message)
+ {
+ eventsHandler.HandleLogMessage(testMessageLevel, message);
+ }
+ }
+
+ private class NullMessageLogger : IMessageLogger
+ {
+ public void SendMessage(TestMessageLevel testMessageLevel, string message)
+ {
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Client/Parallel/ParallelProxyExecutionManager.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/Client/Parallel/ParallelProxyExecutionManager.cs
index 3e0e0da772..d5cc0cecb4 100644
--- a/src/Microsoft.TestPlatform.CrossPlatEngine/Client/Parallel/ParallelProxyExecutionManager.cs
+++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Client/Parallel/ParallelProxyExecutionManager.cs
@@ -14,11 +14,14 @@ namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Client.Parallel
using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities;
using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Interfaces;
using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.ObjectModel;
+ using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Tracing;
using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.DataCollection;
+ using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.TestRunAttachmentsProcessing;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
+ using Microsoft.VisualStudio.TestPlatform.Utilities;
///
/// ParallelProxyExecutionManager that manages parallel execution
@@ -256,12 +259,19 @@ private ParallelRunEventsHandler GetEventsHandler(IProxyExecutionManager concurr
{
if (concurrentManager is ProxyExecutionManagerWithDataCollection)
{
+ var concurrentManagerWithDataCollection = concurrentManager as ProxyExecutionManagerWithDataCollection;
+
+ // TODO : use TestPluginCache to iterate over all IDataCollectorAttachments
+ var attachmentsProcessingManager = new TestRunAttachmentsProcessingManager(TestPlatformEventSource.Instance, new CodeCoverageDataAttachmentsHandler());
+
return new ParallelDataCollectionEventsHandler(
this.requestData,
- concurrentManager,
+ concurrentManagerWithDataCollection,
this.currentRunEventsHandler,
this,
- this.currentRunDataAggregator);
+ this.currentRunDataAggregator,
+ attachmentsProcessingManager,
+ concurrentManagerWithDataCollection.CancellationToken);
}
return new ParallelRunEventsHandler(
diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Client/Parallel/ParallelRunDataAggregator.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/Client/Parallel/ParallelRunDataAggregator.cs
index 2cc3d3c26c..b2d7f238b8 100644
--- a/src/Microsoft.TestPlatform.CrossPlatEngine/Client/Parallel/ParallelRunDataAggregator.cs
+++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Client/Parallel/ParallelRunDataAggregator.cs
@@ -49,7 +49,7 @@ public ParallelRunDataAggregator()
public TimeSpan ElapsedTime { get; set; }
- public Collection RunContextAttachments { get; }
+ public Collection RunContextAttachments { get; set; }
public List RunCompleteArgsAttachments { get; }
diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Client/Parallel/ParallelRunEventsHandler.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/Client/Parallel/ParallelRunEventsHandler.cs
index bacf1da410..5a66d9528e 100644
--- a/src/Microsoft.TestPlatform.CrossPlatEngine/Client/Parallel/ParallelRunEventsHandler.cs
+++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Client/Parallel/ParallelRunEventsHandler.cs
@@ -31,7 +31,7 @@ internal class ParallelRunEventsHandler : ITestRunEventsHandler2
private IDataSerializer dataSerializer;
- private IRequestData requestData;
+ protected IRequestData requestData;
public ParallelRunEventsHandler(IRequestData requestData,
IProxyExecutionManager proxyExecutionManager,
diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyExecutionManagerWithDataCollection.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyExecutionManagerWithDataCollection.cs
index 6aa6684e2d..179a6df370 100644
--- a/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyExecutionManagerWithDataCollection.cs
+++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyExecutionManagerWithDataCollection.cs
@@ -5,7 +5,7 @@ namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Client
{
using System;
using System.Collections.Generic;
-
+ using System.Threading;
using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Interfaces;
using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.DataCollection;
using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.DataCollection.Interfaces;
@@ -70,6 +70,11 @@ internal IProxyDataCollectionManager ProxyDataCollectionManager
get; private set;
}
+ ///
+ /// Gets the cancellation token for execution.
+ ///
+ internal CancellationToken CancellationToken => CancellationTokenSource.Token;
+
///
/// Ensure that the Execution component of engine is ready for execution usually by loading extensions.
/// Skip default adapters flag.
diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/DataCollection/ParallelDataCollectionEventsHandler.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/DataCollection/ParallelDataCollectionEventsHandler.cs
index 0f5432cd78..a50d5fe6ff 100644
--- a/src/Microsoft.TestPlatform.CrossPlatEngine/DataCollection/ParallelDataCollectionEventsHandler.cs
+++ b/src/Microsoft.TestPlatform.CrossPlatEngine/DataCollection/ParallelDataCollectionEventsHandler.cs
@@ -3,30 +3,33 @@
namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.DataCollection
{
- using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
- using System.Linq;
-
+ using System.Threading;
using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities;
using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Interfaces;
using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Client.Parallel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine;
- using Microsoft.VisualStudio.TestPlatform.Utilities;
internal class ParallelDataCollectionEventsHandler : ParallelRunEventsHandler
{
private readonly ParallelRunDataAggregator runDataAggregator;
+ private readonly ITestRunAttachmentsProcessingManager attachmentsProcessingManager;
+ private readonly CancellationToken cancellationToken;
public ParallelDataCollectionEventsHandler(IRequestData requestData,
IProxyExecutionManager proxyExecutionManager,
ITestRunEventsHandler actualRunEventsHandler,
IParallelProxyExecutionManager parallelProxyExecutionManager,
- ParallelRunDataAggregator runDataAggregator) :
+ ParallelRunDataAggregator runDataAggregator,
+ ITestRunAttachmentsProcessingManager attachmentsProcessingManager,
+ CancellationToken cancellationToken) :
this(requestData, proxyExecutionManager, actualRunEventsHandler, parallelProxyExecutionManager, runDataAggregator, JsonDataSerializer.Instance)
{
+ this.attachmentsProcessingManager = attachmentsProcessingManager;
+ this.cancellationToken = cancellationToken;
}
internal ParallelDataCollectionEventsHandler(IRequestData requestData,
@@ -53,33 +56,13 @@ public override void HandleTestRunComplete(
if (parallelRunComplete)
{
- // TODO : use TestPluginCache to iterate over all IDataCollectorAttachments
- {
- var coverageHandler = new CodeCoverageDataAttachmentsHandler();
- Uri attachementUri = coverageHandler.GetExtensionUri();
- if (attachementUri != null)
- {
- var coverageAttachments = runDataAggregator.RunContextAttachments
- .Where(dataCollectionAttachment => attachementUri.Equals(dataCollectionAttachment.Uri)).ToArray();
-
- foreach (var coverageAttachment in coverageAttachments)
- {
- runDataAggregator.RunContextAttachments.Remove(coverageAttachment);
- }
-
- ICollection attachments = coverageHandler.HandleDataCollectionAttachmentSets(new Collection(coverageAttachments));
- foreach (var attachment in attachments)
- {
- runDataAggregator.RunContextAttachments.Add(attachment);
- }
- }
- }
+ runDataAggregator.RunContextAttachments = attachmentsProcessingManager.ProcessTestRunAttachmentsAsync(requestData, runDataAggregator.RunContextAttachments, cancellationToken).Result ?? runDataAggregator.RunContextAttachments;
var completedArgs = new TestRunCompleteEventArgs(this.runDataAggregator.GetAggregatedRunStats(),
this.runDataAggregator.IsCanceled,
this.runDataAggregator.IsAborted,
this.runDataAggregator.GetAggregatedException(),
- this.runDataAggregator.RunContextAttachments,
+ runDataAggregator.RunContextAttachments,
this.runDataAggregator.ElapsedTime);
// Add Metrics from Test Host
diff --git a/src/Microsoft.TestPlatform.ObjectModel/Client/Events/TestRunAttachmentsProcessingCompleteEventArgs.cs b/src/Microsoft.TestPlatform.ObjectModel/Client/Events/TestRunAttachmentsProcessingCompleteEventArgs.cs
new file mode 100644
index 0000000000..b5e446ff68
--- /dev/null
+++ b/src/Microsoft.TestPlatform.ObjectModel/Client/Events/TestRunAttachmentsProcessingCompleteEventArgs.cs
@@ -0,0 +1,42 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace Microsoft.VisualStudio.TestPlatform.ObjectModel.Client
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Runtime.Serialization;
+
+ [DataContract]
+ public class TestRunAttachmentsProcessingCompleteEventArgs : EventArgs
+ {
+ ///
+ /// Default constructor.
+ ///
+ /// Specifies whether the attachments processing is canceled.
+ /// Specifies the error encountered during the execution of the attachments processing.
+ public TestRunAttachmentsProcessingCompleteEventArgs(bool isCanceled, Exception error)
+ {
+ this.IsCanceled = isCanceled;
+ this.Error = error;
+ }
+
+ ///
+ /// Gets a value indicating whether the attachments processing is canceled or not.
+ ///
+ [DataMember]
+ public bool IsCanceled { get; private set; }
+
+ ///
+ /// Gets the error encountered during the attachments processing of the test runs. Null if there is no error.
+ ///
+ [DataMember]
+ public Exception Error { get; private set; }
+
+ ///
+ /// Get or Sets the Metrics (used for telemetry)
+ ///
+ [DataMember]
+ public IDictionary Metrics { get; set; }
+ }
+}
diff --git a/src/Microsoft.TestPlatform.ObjectModel/Client/Events/TestRunAttachmentsProcessingProgressEventArgs.cs b/src/Microsoft.TestPlatform.ObjectModel/Client/Events/TestRunAttachmentsProcessingProgressEventArgs.cs
new file mode 100644
index 0000000000..8218336e5b
--- /dev/null
+++ b/src/Microsoft.TestPlatform.ObjectModel/Client/Events/TestRunAttachmentsProcessingProgressEventArgs.cs
@@ -0,0 +1,52 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace Microsoft.VisualStudio.TestPlatform.ObjectModel.Client
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Runtime.Serialization;
+
+ [DataContract]
+ public class TestRunAttachmentsProcessingProgressEventArgs : EventArgs
+ {
+ ///
+ /// Default constructor.
+ ///
+ /// Specifies current attachment processor index.
+ /// Specifies current processor Uris.
+ /// Specifies current processor progress.
+ /// Specifies the overall number of processors.
+ public TestRunAttachmentsProcessingProgressEventArgs(long currentAttachmentProcessorIndex, ICollection currentAttachmentProcessorUris, long currentAttachmentProcessorProgress, long attachmentProcessorsCount)
+ {
+ CurrentAttachmentProcessorIndex = currentAttachmentProcessorIndex;
+ CurrentAttachmentProcessorUris = currentAttachmentProcessorUris;
+ CurrentAttachmentProcessorProgress = currentAttachmentProcessorProgress;
+ AttachmentProcessorsCount = attachmentProcessorsCount;
+ }
+
+ ///
+ /// Gets a current attachment processor index.
+ ///
+ [DataMember]
+ public long CurrentAttachmentProcessorIndex { get; private set; }
+
+ ///
+ /// Gets a current attachment processor URI.
+ ///
+ [DataMember]
+ public ICollection CurrentAttachmentProcessorUris { get; private set; }
+
+ ///
+ /// Gets a current attachment processor progress.
+ ///
+ [DataMember]
+ public long CurrentAttachmentProcessorProgress { get; private set; }
+
+ ///
+ /// Gets the overall number of attachment processors.
+ ///
+ [DataMember]
+ public long AttachmentProcessorsCount { get; private set; }
+ }
+}
diff --git a/src/Microsoft.TestPlatform.ObjectModel/Client/Interfaces/ITestRunAttachmentsProcessingEventsHandler.cs b/src/Microsoft.TestPlatform.ObjectModel/Client/Interfaces/ITestRunAttachmentsProcessingEventsHandler.cs
new file mode 100644
index 0000000000..f6235a92e9
--- /dev/null
+++ b/src/Microsoft.TestPlatform.ObjectModel/Client/Interfaces/ITestRunAttachmentsProcessingEventsHandler.cs
@@ -0,0 +1,32 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Collections.Generic;
+
+namespace Microsoft.VisualStudio.TestPlatform.ObjectModel.Client
+{
+ ///
+ /// Interface contract for handling test run attachments processing events
+ ///
+ public interface ITestRunAttachmentsProcessingEventsHandler : ITestMessageEventHandler
+ {
+ ///
+ /// Dispatch TestRunAttachmentsProcessingComplete event to listeners.
+ ///
+ /// AttachmentsProcessing Complete event args.
+ /// Last set of processed attachment sets.
+ void HandleTestRunAttachmentsProcessingComplete(TestRunAttachmentsProcessingCompleteEventArgs attachmentsProcessingCompleteEventArgs, IEnumerable lastChunk);
+
+ ///
+ /// Dispatch ProcessedAttachmentsChunk event to listeners.
+ ///
+ /// Processed attachment sets.
+ void HandleProcessedAttachmentsChunk(IEnumerable attachments);
+
+ ///
+ /// Dispatch TestRunAttachmentsProcessingProgress event to listeners.
+ ///
+ /// AttachmentsProcessing Progress event args.
+ void HandleTestRunAttachmentsProcessingProgress(TestRunAttachmentsProcessingProgressEventArgs AttachmentsProcessingProgressEventArgs);
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.TestPlatform.ObjectModel/Client/Payloads/TestRunAttachmentsProcessingPayload.cs b/src/Microsoft.TestPlatform.ObjectModel/Client/Payloads/TestRunAttachmentsProcessingPayload.cs
new file mode 100644
index 0000000000..6abdb68082
--- /dev/null
+++ b/src/Microsoft.TestPlatform.ObjectModel/Client/Payloads/TestRunAttachmentsProcessingPayload.cs
@@ -0,0 +1,26 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace Microsoft.VisualStudio.TestPlatform.ObjectModel.Client
+{
+ using System.Collections.Generic;
+ using System.Runtime.Serialization;
+
+ ///
+ /// Class used to define the TestRunAttachmentsProcessingPayload sent by the Vstest.console translation layers into design mode
+ ///
+ public class TestRunAttachmentsProcessingPayload
+ {
+ ///
+ /// Collection of attachments.
+ ///
+ [DataMember]
+ public IEnumerable Attachments { get; set; }
+
+ ///
+ /// Gets or sets whether Metrics should be collected or not.
+ ///
+ [DataMember]
+ public bool CollectMetrics { get; set; }
+ }
+}
diff --git a/src/Microsoft.TestPlatform.ObjectModel/DataCollector/IDataCollectorAttachmentProcessor.cs b/src/Microsoft.TestPlatform.ObjectModel/DataCollector/IDataCollectorAttachmentProcessor.cs
new file mode 100644
index 0000000000..dfdc77c9fe
--- /dev/null
+++ b/src/Microsoft.TestPlatform.ObjectModel/DataCollector/IDataCollectorAttachmentProcessor.cs
@@ -0,0 +1,50 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Threading;
+ using System.Threading.Tasks;
+
+ using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
+
+ ///
+ /// Interface for data collectors add-ins that choose to reprocess generated attachments
+ ///
+ public interface IDataCollectorAttachmentProcessor
+ {
+ ///
+ /// Gets the attachments Uris, which are handled by attachment processor
+ ///
+ IEnumerable GetExtensionUris();
+
+ ///
+ /// Indicates whether attachment processor is supporting incremental processing of attachments
+ ///
+ ///
+ /// `SupportsIncrementalProcessing` should indicate if attachment processor is supporting incremental processing of attachments. It means that `ProcessAttachmentSetsAsync` should be [associative](https://en.wikipedia.org/wiki/Associative_property).
+ /// By default `SupportsIncrementalProcessing` should be `False`, unless processing can take longer time and it's beneficial to start the process as soon as possible.
+ ///
+ /// If `SupportsIncrementalProcessing` is `True` Test Platform may try to speed up whole process by reprocessing data collector attachments as soon as possible when any two test executions are done.For example let's assume we have 5 test executions which are generating 5 data collector attachments: `a1`, `a2`, `a3`, `a4` and `a5`. Test platform could perform invocations:
+ /// * `var result1 = await ProcessAttachmentSetsAsync([a1, a2, a3], ...);` when first 3 executions are done
+ /// * `var result2 = await ProcessAttachmentSetsAsync(result1.Concat([a4]), ...);` when 4th execution is done
+ /// * `var finalResult = await ProcessAttachmentSetsAsync(result2.Concat([a5]), ...);` when last test execution is done
+ ///
+ /// If `SupportsIncrementalProcessing` is `False` then Test Platform will wait for all test executions to finish and call `ProcessAttachmentSetsAsync` only once:
+ /// * `var finalResult = await ProcessAttachmentSetsAsync([a1, a2, a3, a4, a5], ...);`
+ ///
+ bool SupportsIncrementalProcessing { get; }
+
+ ///
+ /// Reprocess attachments generated by independent test executions
+ ///
+ /// Attachments to be processed
+ /// Progress reporter. Accepts integers from 0 to 100
+ /// Message logger
+ /// Cancellation token
+ /// Attachments after reprocessing
+ Task> ProcessAttachmentSetsAsync(ICollection attachments, IProgress progressReporter, IMessageLogger logger, CancellationToken cancellationToken);
+ }
+}
diff --git a/src/Microsoft.TestPlatform.ObjectModel/DataCollector/IDataCollectorAttachments.cs b/src/Microsoft.TestPlatform.ObjectModel/DataCollector/IDataCollectorAttachments.cs
index e0cd5e97c9..a342d74ea6 100644
--- a/src/Microsoft.TestPlatform.ObjectModel/DataCollector/IDataCollectorAttachments.cs
+++ b/src/Microsoft.TestPlatform.ObjectModel/DataCollector/IDataCollectorAttachments.cs
@@ -9,6 +9,7 @@ namespace Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection
///
/// Interface for data collectors add-ins that choose to handle attachment(s) generated
///
+ [Obsolete("Interface is deprecated. Please use IDataCollectorAttachmentProcessor instead")]
public interface IDataCollectorAttachments
{
///
diff --git a/src/Microsoft.TestPlatform.Utilities/CodeCoverageDataAttachmentsHandler.cs b/src/Microsoft.TestPlatform.Utilities/CodeCoverageDataAttachmentsHandler.cs
index d75d81ce88..2b911f6926 100644
--- a/src/Microsoft.TestPlatform.Utilities/CodeCoverageDataAttachmentsHandler.cs
+++ b/src/Microsoft.TestPlatform.Utilities/CodeCoverageDataAttachmentsHandler.cs
@@ -9,11 +9,14 @@ namespace Microsoft.VisualStudio.TestPlatform.Utilities
using System.IO;
using System.Linq;
using System.Reflection;
+ using System.Threading;
+ using System.Threading.Tasks;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection;
+ using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions;
- public class CodeCoverageDataAttachmentsHandler : IDataCollectorAttachments
+ public class CodeCoverageDataAttachmentsHandler : IDataCollectorAttachmentProcessor
{
private const string CoverageUri = "datacollector://microsoft/CodeCoverage/2.0";
private const string CoverageFileExtension = ".coverage";
@@ -25,42 +28,50 @@ public class CodeCoverageDataAttachmentsHandler : IDataCollectorAttachments
private static readonly Uri CodeCoverageDataCollectorUri = new Uri(CoverageUri);
- public Uri GetExtensionUri()
+ public bool SupportsIncrementalProcessing => true;
+
+ public IEnumerable GetExtensionUris()
{
- return CodeCoverageDataCollectorUri;
- }
+ yield return CodeCoverageDataCollectorUri;
+ }
- public ICollection HandleDataCollectionAttachmentSets(ICollection dataCollectionAttachments)
+ public Task> ProcessAttachmentSetsAsync(ICollection attachments, IProgress progressReporter, IMessageLogger logger, CancellationToken cancellationToken)
{
- if (dataCollectionAttachments != null && dataCollectionAttachments.Any())
+ if (attachments != null && attachments.Any())
{
- var codeCoverageFiles = dataCollectionAttachments.Select(coverageAttachment => coverageAttachment.Attachments[0].Uri.LocalPath).ToArray();
- var outputFile = MergeCodeCoverageFiles(codeCoverageFiles);
+ var codeCoverageFiles = attachments.Select(coverageAttachment => coverageAttachment.Attachments[0].Uri.LocalPath).ToArray();
+ var outputFile = MergeCodeCoverageFiles(codeCoverageFiles, progressReporter, cancellationToken);
var attachmentSet = new AttachmentSet(CodeCoverageDataCollectorUri, CoverageFriendlyName);
if (!string.IsNullOrEmpty(outputFile))
{
attachmentSet.Attachments.Add(new UriDataAttachment(new Uri(outputFile), CoverageFriendlyName));
- return new Collection { attachmentSet };
+ return Task.FromResult((ICollection)new Collection { attachmentSet });
}
// In case merging fails(esp in dotnet core we cannot merge), so return filtered list of Code Coverage Attachments
- return dataCollectionAttachments;
+ return Task.FromResult(attachments);
}
- return new Collection();
+ return Task.FromResult((ICollection)new Collection());
}
- private string MergeCodeCoverageFiles(IList files)
+ private string MergeCodeCoverageFiles(IList files, IProgress progressReporter, CancellationToken cancellationToken)
{
- string fileName = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + CoverageFileExtension);
+ if(files.Count == 1)
+ {
+ return files[0];
+ }
+
+ string tempFileName = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + CoverageFileExtension);
string outputfileName = files[0];
- File.Create(fileName).Dispose();
+ File.Create(tempFileName).Dispose();
var assemblyPath = Path.Combine(Path.GetDirectoryName(typeof(CodeCoverageDataAttachmentsHandler).GetTypeInfo().Assembly.GetAssemblyLocation()), CodeCoverageAnalysisAssemblyName + ".dll");
try
{
+ cancellationToken.ThrowIfCancellationRequested();
Assembly assembly = new PlatformAssemblyLoadContext().LoadAssemblyFromPath(assemblyPath);
var type = assembly.GetType(CodeCoverageAnalysisAssemblyName + "." + CoverageInfoTypeName);
@@ -68,19 +79,40 @@ private string MergeCodeCoverageFiles(IList files)
if (methodInfo != null)
{
+ IList filesToDelete = new List(files.Count) { tempFileName };
+
for (int i = 1; i < files.Count; i++)
{
- methodInfo.Invoke(null, new object[] { files[i], outputfileName, fileName, true });
- File.Copy(fileName, outputfileName, true);
+ cancellationToken.ThrowIfCancellationRequested();
+ progressReporter?.Report(100 * i / files.Count);
+
+ cancellationToken.ThrowIfCancellationRequested();
+ methodInfo.Invoke(null, new object[] { files[i], outputfileName, tempFileName, true });
- File.Delete(files[i]);
+ cancellationToken.ThrowIfCancellationRequested();
+ File.Copy(tempFileName, outputfileName, true);
+
+ filesToDelete.Add(files[i]);
}
- File.Delete(fileName);
+ cancellationToken.ThrowIfCancellationRequested();
+ foreach (string fileName in filesToDelete)
+ {
+ File.Delete(fileName);
+ }
}
+ progressReporter?.Report(100);
return outputfileName;
}
+ catch (OperationCanceledException)
+ {
+ if (EqtTrace.IsWarningEnabled)
+ {
+ EqtTrace.Warning("CodeCoverageDataCollectorAttachmentsHandler: operation was cancelled.");
+ }
+ throw;
+ }
catch (Exception ex)
{
if (EqtTrace.IsErrorEnabled)
diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/ITranslationLayerRequestSender.cs b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/ITranslationLayerRequestSender.cs
index aabf810570..35e50a9b10 100644
--- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/ITranslationLayerRequestSender.cs
+++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/ITranslationLayerRequestSender.cs
@@ -5,7 +5,8 @@ namespace Microsoft.TestPlatform.VsTestConsole.TranslationLayer.Interfaces
{
using System;
using System.Collections.Generic;
-
+ using System.Threading;
+ using System.Threading.Tasks;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces;
@@ -13,7 +14,7 @@ namespace Microsoft.TestPlatform.VsTestConsole.TranslationLayer.Interfaces
///
/// Defines contract to send test platform requests to test host
///
- internal interface ITranslationLayerRequestSender : IDisposable
+ internal interface ITranslationLayerRequestSender : IDisposable, ITranslationLayerRequestSenderAsync
{
///
/// Initializes the communication for sending requests
diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/ITranslationLayerRequestSenderAsync.cs b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/ITranslationLayerRequestSenderAsync.cs
index a49ce93fab..23166126d7 100644
--- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/ITranslationLayerRequestSenderAsync.cs
+++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/ITranslationLayerRequestSenderAsync.cs
@@ -5,6 +5,7 @@ namespace Microsoft.TestPlatform.VsTestConsole.TranslationLayer.Interfaces
{
using System;
using System.Collections.Generic;
+ using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
@@ -22,16 +23,6 @@ internal interface ITranslationLayerRequestSenderAsync : IDisposable
///
Task InitializeCommunicationAsync(int clientConnectionTimeout);
- ///
- /// See
- ///
- void Close();
-
- ///
- /// See
- ///
- void InitializeExtensions(IEnumerable pathToAdditionalExtensions);
-
///
/// Asynchronous equivalent of ITranslationLayerRequestSender.DiscoverTests/>.
///
@@ -58,23 +49,12 @@ internal interface ITranslationLayerRequestSenderAsync : IDisposable
Task StartTestRunWithCustomHostAsync(IEnumerable testCases, string runSettings, TestPlatformOptions options, ITestRunEventsHandler runEventsHandler, ITestHostLauncher customTestHostLauncher);
///
- /// See .
- ///
- void EndSession();
-
- ///
- /// See .
- ///
- void CancelTestRun();
-
- ///
- /// See .
- ///
- void AbortTestRun();
-
- ///
- /// See .
+ /// Provides back all attachments to TestPlatform for additional processing (for example merging)
///
- void OnProcessExited();
+ /// Collection of attachments
+ /// Enables metrics collection
+ /// Events handler
+ /// Cancellation token
+ Task ProcessTestRunAttachmentsAsync(IEnumerable attachments, bool collectMetrics, ITestRunAttachmentsProcessingEventsHandler testRunAttachmentsProcessingCompleteEventsHandler, CancellationToken cancellationToken);
}
}
diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/IVsTestConsoleWrapper.cs b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/IVsTestConsoleWrapper.cs
index b1469d3f8d..79e09b98dd 100644
--- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/IVsTestConsoleWrapper.cs
+++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/IVsTestConsoleWrapper.cs
@@ -4,7 +4,6 @@
namespace Microsoft.TestPlatform.VsTestConsole.TranslationLayer.Interfaces
{
using System.Collections.Generic;
-
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces;
@@ -12,7 +11,7 @@ namespace Microsoft.TestPlatform.VsTestConsole.TranslationLayer.Interfaces
///
/// Controller for various test operations on the test runner.
///
- public interface IVsTestConsoleWrapper
+ public interface IVsTestConsoleWrapper : IVsTestConsoleWrapperAsync
{
///
/// Starts the test runner process and readies for requests.
@@ -40,12 +39,12 @@ public interface IVsTestConsoleWrapper
/// Settings XML for test discovery
/// Options to be passed into the platform.
/// EventHandler to receive discovery events
- void DiscoverTests(IEnumerable sources, string discoverySettings, TestPlatformOptions options, ITestDiscoveryEventsHandler2 discoveryEventsHandler);
+ void DiscoverTests(IEnumerable sources, string discoverySettings, TestPlatformOptions options, ITestDiscoveryEventsHandler2 discoveryEventsHandler);
///
/// Cancels the last discovery request.
///
- void CancelDiscovery();
+ new void CancelDiscovery();
///
/// Starts a test run given a list of sources.
@@ -122,16 +121,16 @@ public interface IVsTestConsoleWrapper
///
/// Cancel the last test run.
///
- void CancelTestRun();
+ new void CancelTestRun();
///
/// Abort the last test run.
///
- void AbortTestRun();
+ new void AbortTestRun();
///
/// Ends the test session and stops processing requests.
///
- void EndSession();
+ new void EndSession();
}
}
diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/IVsTestConsoleWrapperAsync.cs b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/IVsTestConsoleWrapperAsync.cs
index a303a3504f..452de38cb6 100644
--- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/IVsTestConsoleWrapperAsync.cs
+++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/IVsTestConsoleWrapperAsync.cs
@@ -4,6 +4,7 @@
namespace Microsoft.TestPlatform.VsTestConsole.TranslationLayer.Interfaces
{
using System.Collections.Generic;
+ using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
@@ -91,6 +92,17 @@ public interface IVsTestConsoleWrapperAsync
///
void AbortTestRun();
+ ///
+ /// Provides back all attachments to TestPlatform for additional processing (for example merging)
+ ///
+ /// Collection of attachments
+ /// XML processing settings
+ /// Indicates that all test executions are done and all data is provided
+ /// Enables metrics collection (used for telemetry)
+ /// EventHandler to receive session complete event
+ /// Cancellation token
+ Task ProcessTestRunAttachmentsAsync(IEnumerable attachments, string processingSettings, bool isLastBatch, bool collectMetrics, ITestRunAttachmentsProcessingEventsHandler eventsHandler, CancellationToken cancellationToken);
+
///
/// See .
///
diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Microsoft.TestPlatform.VsTestConsole.TranslationLayer.csproj b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Microsoft.TestPlatform.VsTestConsole.TranslationLayer.csproj
index f625e66f36..e2952d93f7 100644
--- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Microsoft.TestPlatform.VsTestConsole.TranslationLayer.csproj
+++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Microsoft.TestPlatform.VsTestConsole.TranslationLayer.csproj
@@ -37,7 +37,7 @@
- ResXFileCodeGenerator
+ PublicResXFileCodeGenerator
Resources.Designer.cs
diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/Resources.Designer.cs b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/Resources.Designer.cs
index b0401e605b..a218efe7f5 100644
--- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/Resources.Designer.cs
+++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/Resources.Designer.cs
@@ -10,7 +10,6 @@
namespace Microsoft.VisualStudio.TestPlatform.VsTestConsole.TranslationLayer.Resources {
using System;
- using System.Reflection;
///
@@ -20,7 +19,7 @@ namespace Microsoft.VisualStudio.TestPlatform.VsTestConsole.TranslationLayer.Res
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class Resources {
@@ -40,7 +39,8 @@ internal Resources() {
public static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.VisualStudio.TestPlatform.VsTestConsole.TranslationLayer.Resources.Resources", typeof(Resources).GetTypeInfo().Assembly);
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.VisualStudio.TestPlatform.VsTestConsole.TranslationLayer.Resources.Reso" +
+ "urces", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
@@ -60,7 +60,16 @@ internal Resources() {
resourceCulture = value;
}
}
-
+
+ ///
+ /// Looks up a localized string similar to The active Test Run Attachments Processing was aborted..
+ ///
+ public static string AbortedTestRunAttachmentsProcessing {
+ get {
+ return ResourceManager.GetString("AbortedTestRunAttachmentsProcessing", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to The active Tests Discovery was aborted..
///
@@ -69,7 +78,7 @@ public static string AbortedTestsDiscovery {
return ResourceManager.GetString("AbortedTestsDiscovery", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to The active Tests Run was aborted..
///
@@ -78,7 +87,7 @@ public static string AbortedTestsRun {
return ResourceManager.GetString("AbortedTestsRun", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to Failed to receive message from vstest.console process.
///
@@ -87,7 +96,7 @@ public static string FailedToReceiveMessage {
return ResourceManager.GetString("FailedToReceiveMessage", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to File {0} does not exists.
///
@@ -96,7 +105,7 @@ public static string InvalidFilePath {
return ResourceManager.GetString("InvalidFilePath", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to vstest.console process exited abnormally.
///
diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/Resources.resx b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/Resources.resx
index a52549c171..eb053d0a74 100644
--- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/Resources.resx
+++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/Resources.resx
@@ -117,6 +117,9 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+ The active Test Run Attachments Processing was aborted.
+
The active Tests Discovery was aborted.
diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.cs.xlf b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.cs.xlf
index ab7c034843..378546e5aa 100644
--- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.cs.xlf
+++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.cs.xlf
@@ -61,6 +61,11 @@
Soubor {0} neexistuje.
+
+
+ The active Test Run Attachments Processing was aborted.
+
+