From c1b6b2c1ab4c8f181f3cb1a436afb6428009c7ab Mon Sep 17 00:00:00 2001 From: Jakub Chocholowicz <59966772+jakubch1@users.noreply.github.com> Date: Thu, 2 Jul 2020 12:07:01 +0200 Subject: [PATCH] Adding test run attachments processing (#2463) * v1 * Merging v1 * Rename to MultiTestRunsFinalization * New version * More changes * More changes * Next changes * Fix * test * More changes * Dmc chagnes * next * small changes * compiled * More changes * acceptance tests green * Review comments #1 * Resolving more comments * Tests for design mode client * Tests for events handler * revert not related changes * More changes * Compiling OK, tests OK * Unit tests for manager * More changes * More tests * tests for reqeust sender * more tests * Tests for cancelling * Acceptance tests done * Remove not used stuff * Fix comments * Fix race condition in test * Fix another race condition * Fix converting to xml * fix next test * fix test * Next changes * Review changes #1 * Fixing multi test finalization manager tests * Fixes * Fix last unit test * Fix acceptance tests * Progress feature, compiling + unit tests * acceptance tests changes * More changes * Fixing resources accesability * Fix test * Fix race conditions in acceptance tests * RFC changes merged * Log warning in case of unexpected message id * Fix spelling * Additional comment * Restore some stuff in interfaces * Big renaming * Added processingSettings * Fix naming * Move explanation to --- ...stRunAttachmentsProcessingEventsHandler.cs | 77 +++ .../DesignMode/DesignModeClient.cs | 43 +- .../RequestHelper/ITestRequestManager.cs | 12 + .../ITestRunAttachmentsProcessingManager.cs | 33 + .../Telemetry/TelemetryDataConstants.cs | 10 + .../Messages/MessageType.cs | 20 + ...RunAttachmentsProcessingCompletePayload.cs | 26 + ...RunAttachmentsProcessingProgressPayload.cs | 18 + .../Interfaces/ITestPlatformEventSource.cs | 36 + .../Tracing/TestPlatformEventSource.cs | 42 ++ .../TestPlatformInstrumentationEvents.cs | 30 + .../TestRunAttachmentsProcessingManager.cs | 180 +++++ .../Parallel/ParallelProxyExecutionManager.cs | 14 +- .../Parallel/ParallelRunDataAggregator.cs | 2 +- .../Parallel/ParallelRunEventsHandler.cs | 2 +- ...ProxyExecutionManagerWithDataCollection.cs | 7 +- .../ParallelDataCollectionEventsHandler.cs | 37 +- ...nAttachmentsProcessingCompleteEventArgs.cs | 42 ++ ...nAttachmentsProcessingProgressEventArgs.cs | 52 ++ ...stRunAttachmentsProcessingEventsHandler.cs | 32 + .../TestRunAttachmentsProcessingPayload.cs | 26 + .../IDataCollectorAttachmentProcessor.cs | 50 ++ .../IDataCollectorAttachments.cs | 1 + .../CodeCoverageDataAttachmentsHandler.cs | 68 +- .../ITranslationLayerRequestSender.cs | 5 +- .../ITranslationLayerRequestSenderAsync.cs | 34 +- .../Interfaces/IVsTestConsoleWrapper.cs | 13 +- .../Interfaces/IVsTestConsoleWrapperAsync.cs | 12 + ...form.VsTestConsole.TranslationLayer.csproj | 2 +- .../Resources/Resources.Designer.cs | 25 +- .../Resources/Resources.resx | 3 + .../Resources/xlf/Resources.cs.xlf | 5 + .../Resources/xlf/Resources.de.xlf | 5 + .../Resources/xlf/Resources.es.xlf | 5 + .../Resources/xlf/Resources.fr.xlf | 5 + .../Resources/xlf/Resources.it.xlf | 5 + .../Resources/xlf/Resources.ja.xlf | 5 + .../Resources/xlf/Resources.ko.xlf | 5 + .../Resources/xlf/Resources.pl.xlf | 5 + .../Resources/xlf/Resources.pt-BR.xlf | 5 + .../Resources/xlf/Resources.ru.xlf | 5 + .../Resources/xlf/Resources.tr.xlf | 5 + .../Resources/xlf/Resources.xlf | 5 + .../Resources/xlf/Resources.zh-Hans.xlf | 5 + .../Resources/xlf/Resources.zh-Hant.xlf | 5 + .../VsTestConsoleRequestSender.cs | 76 ++- .../VsTestConsoleWrapper.cs | 157 ++++- .../VsTestConsoleWrapperAsync.cs | 259 -------- .../TestPlatformHelpers/TestRequestManager.cs | 67 +- .../CodeCoverageTests.cs | 443 +++++++++++++ .../EventHandler/RunEventHandler.cs | 11 + ...estRunAttachmentsProcessingEventHandler.cs | 109 +++ ...AttachmentsProcessingEventsHandlerTests.cs | 67 ++ .../DesignMode/DesignModeClientTests.cs | 87 +++ ...estRunAttachmentsProcessingManagerTests.cs | 625 ++++++++++++++++++ .../ParallelProxyExecutionManagerTests.cs | 27 + ...arallelDataCollectionEventsHandlerTests.cs | 95 +++ .../Utility/ConverterTests.cs | 2 +- ...CodeCoverageDataAttachmentsHandlerTests.cs | 88 ++- .../VsTestConsoleRequestSenderTests.cs | 284 +++++++- .../VsTestConsoleWrapperAsyncTests.cs | 34 +- .../VsTestConsoleWrapperTests.cs | 21 + ...llyQualifiedTestsArgumentProcessorTests.cs | 19 +- .../ListTestsArgumentProcessorTests.cs | 17 +- .../RunSpecificTestsArgumentProcessorTests.cs | 47 +- .../RunTestsArgumentProcessorTests.cs | 17 +- .../TestRequestManagerTests.cs | 165 ++++- 67 files changed, 3250 insertions(+), 491 deletions(-) create mode 100644 src/Microsoft.TestPlatform.Client/AttachmentsProcessing/TestRunAttachmentsProcessingEventsHandler.cs create mode 100644 src/Microsoft.TestPlatform.Common/Interfaces/Engine/ITestRunAttachmentsProcessingManager.cs create mode 100644 src/Microsoft.TestPlatform.CommunicationUtilities/Messages/TestRunAttachmentsProcessingCompletePayload.cs create mode 100644 src/Microsoft.TestPlatform.CommunicationUtilities/Messages/TestRunAttachmentsProcessingProgressPayload.cs create mode 100644 src/Microsoft.TestPlatform.CrossPlatEngine/AttachmentsProcessing/TestRunAttachmentsProcessingManager.cs create mode 100644 src/Microsoft.TestPlatform.ObjectModel/Client/Events/TestRunAttachmentsProcessingCompleteEventArgs.cs create mode 100644 src/Microsoft.TestPlatform.ObjectModel/Client/Events/TestRunAttachmentsProcessingProgressEventArgs.cs create mode 100644 src/Microsoft.TestPlatform.ObjectModel/Client/Interfaces/ITestRunAttachmentsProcessingEventsHandler.cs create mode 100644 src/Microsoft.TestPlatform.ObjectModel/Client/Payloads/TestRunAttachmentsProcessingPayload.cs create mode 100644 src/Microsoft.TestPlatform.ObjectModel/DataCollector/IDataCollectorAttachmentProcessor.cs delete mode 100644 src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/VsTestConsoleWrapperAsync.cs create mode 100644 test/Microsoft.TestPlatform.AcceptanceTests/TranslationLayerTests/CodeCoverageTests.cs create mode 100644 test/Microsoft.TestPlatform.AcceptanceTests/TranslationLayerTests/EventHandler/TestRunAttachmentsProcessingEventHandler.cs create mode 100644 test/Microsoft.TestPlatform.Client.UnitTests/AttachmentsProcessing/TestRunAttachmentsProcessingEventsHandlerTests.cs create mode 100644 test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/AttachmentsProcessing/TestRunAttachmentsProcessingManagerTests.cs create mode 100644 test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/DataCollection/ParallelDataCollectionEventsHandlerTests.cs 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. + The active Test Run Attachments Processing was aborted. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.de.xlf b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.de.xlf index 361f2a9f80..ead9e18cd3 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.de.xlf +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.de.xlf @@ -61,6 +61,11 @@ Die Datei "{0}" ist nicht vorhanden. + + The active Test Run Attachments Processing was aborted. + The active Test Run Attachments Processing was aborted. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.es.xlf b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.es.xlf index 5566e1cd03..704f3fc1d1 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.es.xlf +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.es.xlf @@ -61,6 +61,11 @@ El archivo {0} no existe + + The active Test Run Attachments Processing was aborted. + The active Test Run Attachments Processing was aborted. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.fr.xlf b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.fr.xlf index 5bef4c27d2..788ee98fbb 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.fr.xlf +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.fr.xlf @@ -61,6 +61,11 @@ Le fichier {0} n'existe pas + + The active Test Run Attachments Processing was aborted. + The active Test Run Attachments Processing was aborted. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.it.xlf b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.it.xlf index b1e559d790..471c34b6ce 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.it.xlf +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.it.xlf @@ -61,6 +61,11 @@ Il file {0} non esiste + + The active Test Run Attachments Processing was aborted. + The active Test Run Attachments Processing was aborted. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.ja.xlf b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.ja.xlf index 8b58d1d21c..9bbf726d85 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.ja.xlf +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.ja.xlf @@ -61,6 +61,11 @@ ファイル {0} が存在しません + + The active Test Run Attachments Processing was aborted. + The active Test Run Attachments Processing was aborted. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.ko.xlf b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.ko.xlf index 028bc0fa22..250d2e203d 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.ko.xlf +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.ko.xlf @@ -61,6 +61,11 @@ {0} 파일이 없습니다. + + The active Test Run Attachments Processing was aborted. + The active Test Run Attachments Processing was aborted. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.pl.xlf b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.pl.xlf index 7c09aa494f..d9a8a303ef 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.pl.xlf +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.pl.xlf @@ -61,6 +61,11 @@ Plik {0} nie istnieje + + The active Test Run Attachments Processing was aborted. + The active Test Run Attachments Processing was aborted. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.pt-BR.xlf b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.pt-BR.xlf index 475cfc8773..ac67cd51d7 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.pt-BR.xlf +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.pt-BR.xlf @@ -61,6 +61,11 @@ O arquivo {0} não existe + + The active Test Run Attachments Processing was aborted. + The active Test Run Attachments Processing was aborted. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.ru.xlf b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.ru.xlf index 980e35e1fc..40d4023b26 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.ru.xlf +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.ru.xlf @@ -61,6 +61,11 @@ Файл {0} не существует. + + The active Test Run Attachments Processing was aborted. + The active Test Run Attachments Processing was aborted. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.tr.xlf b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.tr.xlf index 2b9f819abc..f1817063cc 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.tr.xlf +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.tr.xlf @@ -61,6 +61,11 @@ {0} dosyası yok + + The active Test Run Attachments Processing was aborted. + The active Test Run Attachments Processing was aborted. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.xlf b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.xlf index e63797562a..7c79b0d188 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.xlf +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.xlf @@ -23,6 +23,11 @@ File {0} does not exists + + The active Test Run Attachments Processing was aborted. + The active Test Run Attachments Processing was aborted. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.zh-Hans.xlf b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.zh-Hans.xlf index d566b9b20c..35db394ca4 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.zh-Hans.xlf +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.zh-Hans.xlf @@ -61,6 +61,11 @@ 文件 {0} 不存在 + + The active Test Run Attachments Processing was aborted. + The active Test Run Attachments Processing was aborted. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.zh-Hant.xlf b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.zh-Hant.xlf index b420856a52..77b761a7ee 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.zh-Hant.xlf +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.zh-Hant.xlf @@ -61,6 +61,11 @@ 檔案 {0} 不存在 + + The active Test Run Attachments Processing was aborted. + The active Test Run Attachments Processing was aborted. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/VsTestConsoleRequestSender.cs b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/VsTestConsoleRequestSender.cs index 838cbdcb4b..f61b47fdce 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/VsTestConsoleRequestSender.cs +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/VsTestConsoleRequestSender.cs @@ -26,7 +26,7 @@ namespace Microsoft.TestPlatform.VsTestConsole.TranslationLayer /// /// VstestConsoleRequestSender for sending requests to Vstest.console.exe /// - internal class VsTestConsoleRequestSender : ITranslationLayerRequestSender, ITranslationLayerRequestSenderAsync + internal class VsTestConsoleRequestSender : ITranslationLayerRequestSender { private readonly ICommunicationManager communicationManager; @@ -383,6 +383,12 @@ public void EndSession() this.communicationManager.SendMessage(MessageType.SessionEnd); } + /// + public Task ProcessTestRunAttachmentsAsync(IEnumerable attachments, bool collectMetrics, ITestRunAttachmentsProcessingEventsHandler testSessionEventsHandler, CancellationToken cancellationToken) + { + return this.SendMessageAndListenAndReportAttachmentsProcessingResultAsync(attachments, collectMetrics, testSessionEventsHandler, cancellationToken); + } + /// /// Closes the communication channel /// @@ -725,6 +731,74 @@ private async Task SendMessageAndListenAndReportTestResultsAsync(string messageT this.testPlatformEventSource.TranslationLayerExecutionStop(); } + private async Task SendMessageAndListenAndReportAttachmentsProcessingResultAsync(IEnumerable attachments, bool collectMetrics, ITestRunAttachmentsProcessingEventsHandler eventHandler, CancellationToken cancellationToken) + { + try + { + var payload = new TestRunAttachmentsProcessingPayload + { + Attachments = attachments, + CollectMetrics = collectMetrics + }; + + this.communicationManager.SendMessage(MessageType.TestRunAttachmentsProcessingStart, payload); + var isTestRunAttachmentsProcessingComplete = false; + + using (cancellationToken.Register(() => this.communicationManager.SendMessage(MessageType.TestRunAttachmentsProcessingCancel))) + { + // Cycle through the messages that the vstest.console sends. + // Currently each of the operations are not separate tasks since they should not each take much time. + // This is just a notification. + while (!isTestRunAttachmentsProcessingComplete) + { + var message = await this.TryReceiveMessageAsync().ConfigureAwait(false); + + if (string.Equals(MessageType.TestRunAttachmentsProcessingComplete, message.MessageType)) + { + if (EqtTrace.IsInfoEnabled) + { + EqtTrace.Info("VsTestConsoleRequestSender.SendMessageAndListenAndReportAttachments: Process complete."); + } + + var testRunAttachmentsProcessingCompletePayload = this.dataSerializer.DeserializePayload(message); + + eventHandler.HandleTestRunAttachmentsProcessingComplete(testRunAttachmentsProcessingCompletePayload.AttachmentsProcessingCompleteEventArgs, testRunAttachmentsProcessingCompletePayload.Attachments); + isTestRunAttachmentsProcessingComplete = true; + } + else if (string.Equals(MessageType.TestRunAttachmentsProcessingProgress, message.MessageType)) + { + var testRunAttachmentsProcessingProgressPayload = this.dataSerializer.DeserializePayload(message); + eventHandler.HandleTestRunAttachmentsProcessingProgress(testRunAttachmentsProcessingProgressPayload.AttachmentsProcessingProgressEventArgs); + } + else if (string.Equals(MessageType.TestMessage, message.MessageType)) + { + var testMessagePayload = this.dataSerializer.DeserializePayload(message); + eventHandler.HandleLogMessage(testMessagePayload.MessageLevel, testMessagePayload.Message); + } + else + { + EqtTrace.Warning($"VsTestConsoleRequestSender.SendMessageAndListenAndReportAttachments: Unexpected message received {message.MessageType}."); + } + } + } + } + catch (Exception exception) + { + EqtTrace.Error("Aborting Test Session End Operation: {0}", exception); + eventHandler.HandleLogMessage(TestMessageLevel.Error, TranslationLayerResources.AbortedTestRunAttachmentsProcessing); + eventHandler.HandleTestRunAttachmentsProcessingComplete(new TestRunAttachmentsProcessingCompleteEventArgs(false, exception), null); + + // Earlier we were closing the connection with vstest.console in case of exceptions + // Removing that code because vstest.console might be in a healthy state and letting the client + // know of the error, so that the TL can wait for the next instruction from the client itself. + // Also, connection termination might not kill the process which could result in files being locked by testhost. + } + finally + { + this.testPlatformEventSource.TranslationLayerTestRunAttachmentsProcessingStop(); + } + } + private Message TryReceiveMessage() { Message message = null; diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/VsTestConsoleWrapper.cs b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/VsTestConsoleWrapper.cs index 42f587d0f4..eb8780bdf6 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/VsTestConsoleWrapper.cs +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/VsTestConsoleWrapper.cs @@ -7,7 +7,8 @@ namespace Microsoft.TestPlatform.VsTestConsole.TranslationLayer using System.Diagnostics; using System.Globalization; using System.Linq; - + using System.Threading; + using System.Threading.Tasks; using Microsoft.TestPlatform.VsTestConsole.TranslationLayer.Interfaces; using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Helpers; using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Tracing; @@ -275,6 +276,148 @@ public void EndSession() #endregion + #region IVsTestConsoleWrapperAsync + + /// + public async Task StartSessionAsync() + { + EqtTrace.Info("VsTestConsoleWrapperAsync.StartSessionAsync: Starting VsTestConsoleWrapper session"); + + this.testPlatformEventSource.TranslationLayerInitializeStart(); + + var timeout = EnvironmentHelper.GetConnectionTimeout(); + // Start communication + var port = await this.requestSender.InitializeCommunicationAsync(timeout * 1000); + + if (port > 0) + { + // Fill the parameters + this.consoleParameters.ParentProcessId = Process.GetCurrentProcess().Id; + this.consoleParameters.PortNumber = port; + + // Start vstest.console.exe process + this.vstestConsoleProcessManager.StartProcess(this.consoleParameters); + } + else + { + // Close the sender as it failed to host server + this.requestSender.Close(); + throw new TransationLayerException("Error hosting communication channel and connecting to console"); + } + } + + /// + public async Task InitializeExtensionsAsync(IEnumerable pathToAdditionalExtensions) + { + await this.EnsureInitializedAsync(); + this.pathToAdditionalExtensions = pathToAdditionalExtensions.ToList(); + this.requestSender.InitializeExtensions(this.pathToAdditionalExtensions); + } + + /// + public async Task DiscoverTestsAsync(IEnumerable sources, string discoverySettings, ITestDiscoveryEventsHandler discoveryEventsHandler) + { + this.testPlatformEventSource.TranslationLayerDiscoveryStart(); + await this.EnsureInitializedAsync(); + + // Converts ITestDiscoveryEventsHandler to ITestDiscoveryEventsHandler2 + var discoveryCompleteEventsHandler2 = new DiscoveryEventsHandleConverter(discoveryEventsHandler); + await this.requestSender.DiscoverTestsAsync(sources, discoverySettings, options: null, discoveryEventsHandler: discoveryCompleteEventsHandler2); + } + + + /// + public async Task DiscoverTestsAsync(IEnumerable sources, string discoverySettings, TestPlatformOptions options, ITestDiscoveryEventsHandler2 discoveryEventsHandler) + { + this.testPlatformEventSource.TranslationLayerDiscoveryStart(); + await this.EnsureInitializedAsync(); + await this.requestSender.DiscoverTestsAsync(sources, discoverySettings, options, discoveryEventsHandler); + } + + /// + public async Task RunTestsAsync(IEnumerable sources, string runSettings, ITestRunEventsHandler testRunEventsHandler) + { + await RunTestsAsync(sources, runSettings, null, testRunEventsHandler); + } + + /// + public async Task RunTestsAsync(IEnumerable sources, string runSettings, TestPlatformOptions options, ITestRunEventsHandler testRunEventsHandler) + { + var sourceList = sources.ToList(); + this.testPlatformEventSource.TranslationLayerExecutionStart(0, sourceList.Count, 0, runSettings ?? string.Empty); + + await this.EnsureInitializedAsync(); + await this.requestSender.StartTestRunAsync(sourceList, runSettings, options, testRunEventsHandler); + } + + /// + public async Task RunTestsAsync(IEnumerable testCases, string runSettings, ITestRunEventsHandler testRunEventsHandler) + { + var testCaseList = testCases.ToList(); + this.testPlatformEventSource.TranslationLayerExecutionStart(0, 0, testCaseList.Count, runSettings ?? string.Empty); + + await this.EnsureInitializedAsync(); + await this.requestSender.StartTestRunAsync(testCaseList, runSettings, options: null, runEventsHandler: testRunEventsHandler); + } + + /// + public async Task RunTestsAsync(IEnumerable testCases, string runSettings, TestPlatformOptions options, ITestRunEventsHandler testRunEventsHandler) + { + var testCaseList = testCases.ToList(); + this.testPlatformEventSource.TranslationLayerExecutionStart(0, 0, testCaseList.Count, runSettings ?? string.Empty); + + await this.EnsureInitializedAsync(); + await this.requestSender.StartTestRunAsync(testCaseList, runSettings, options, testRunEventsHandler); + } + + /// + public async Task RunTestsWithCustomTestHostAsync(IEnumerable sources, string runSettings, ITestRunEventsHandler testRunEventsHandler, ITestHostLauncher customTestHostLauncher) + { + await RunTestsWithCustomTestHostAsync(sources, runSettings, null, testRunEventsHandler, customTestHostLauncher); + } + + /// + public async Task RunTestsWithCustomTestHostAsync(IEnumerable sources, string runSettings, TestPlatformOptions options, ITestRunEventsHandler testRunEventsHandler, ITestHostLauncher customTestHostLauncher) + { + var sourceList = sources.ToList(); + this.testPlatformEventSource.TranslationLayerExecutionStart(1, sourceList.Count, 0, runSettings ?? string.Empty); + + await this.EnsureInitializedAsync(); + await this.requestSender.StartTestRunWithCustomHostAsync(sourceList, runSettings, options, testRunEventsHandler, customTestHostLauncher); + } + + /// + public async Task RunTestsWithCustomTestHostAsync(IEnumerable testCases, string runSettings, ITestRunEventsHandler testRunEventsHandler, ITestHostLauncher customTestHostLauncher) + { + var testCaseList = testCases.ToList(); + this.testPlatformEventSource.TranslationLayerExecutionStart(1, 0, testCaseList.Count, runSettings ?? string.Empty); + + await this.EnsureInitializedAsync(); + await this.requestSender.StartTestRunWithCustomHostAsync(testCaseList, runSettings, options: null, runEventsHandler: testRunEventsHandler, customTestHostLauncher: customTestHostLauncher); + } + + /// + public async Task RunTestsWithCustomTestHostAsync(IEnumerable testCases, string runSettings, TestPlatformOptions options, ITestRunEventsHandler testRunEventsHandler, ITestHostLauncher customTestHostLauncher) + { + var testCaseList = testCases.ToList(); + this.testPlatformEventSource.TranslationLayerExecutionStart(1, 0, testCaseList.Count, runSettings ?? string.Empty); + + await this.EnsureInitializedAsync(); + await this.requestSender.StartTestRunWithCustomHostAsync(testCaseList, runSettings, options, testRunEventsHandler, customTestHostLauncher); + } + + /// + public async Task ProcessTestRunAttachmentsAsync(IEnumerable attachments, string processingSettings, bool isLastBatch, bool collectMetrics, ITestRunAttachmentsProcessingEventsHandler testSessionEventsHandler, CancellationToken cancellationToken) + { + this.testPlatformEventSource.TranslationLayerTestRunAttachmentsProcessingStart(); + + await this.EnsureInitializedAsync().ConfigureAwait(false); + await requestSender.ProcessTestRunAttachmentsAsync(attachments, collectMetrics, testSessionEventsHandler, cancellationToken).ConfigureAwait(false); + } + + #endregion + + private void EnsureInitialized() { if (!this.vstestConsoleProcessManager.IsProcessInitialized()) @@ -297,6 +440,18 @@ private void EnsureInitialized() } } + private async Task EnsureInitializedAsync() + { + if (!this.vstestConsoleProcessManager.IsProcessInitialized()) + { + EqtTrace.Info("VsTestConsoleWrapper.EnsureInitializedAsync: Process is not started."); + await this.StartSessionAsync(); + + EqtTrace.Info("VsTestConsoleWrapper.EnsureInitializedAsync: Send a request to initialize extensions."); + this.requestSender.InitializeExtensions(this.pathToAdditionalExtensions); + } + } + private bool WaitForConnection() { EqtTrace.Info("VsTestConsoleWrapper.WaitForConnection: Waiting for connection to command line runner."); diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/VsTestConsoleWrapperAsync.cs b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/VsTestConsoleWrapperAsync.cs deleted file mode 100644 index ca0a357c75..0000000000 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/VsTestConsoleWrapperAsync.cs +++ /dev/null @@ -1,259 +0,0 @@ -// 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.TestPlatform.VsTestConsole.TranslationLayer -{ - using System.Collections.Generic; - using System.Diagnostics; - using System.Linq; - using System.Threading.Tasks; - - using Microsoft.TestPlatform.VsTestConsole.TranslationLayer.Interfaces; - using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Helpers; - using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Tracing; - using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Tracing.Interfaces; - using Microsoft.VisualStudio.TestPlatform.ObjectModel; - using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; - using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces; - - /// - /// An implementation of to invoke test operations - /// via the vstest.console test runner. - /// - public class VsTestConsoleWrapperAsync : IVsTestConsoleWrapperAsync - { - #region Private Members - - private readonly IProcessManager vstestConsoleProcessManager; - - private readonly ITranslationLayerRequestSenderAsync requestSender; - - /// - /// Path to additional extensions to reinitialize vstest.console - /// - private IEnumerable pathToAdditionalExtensions; - - /// - /// Additional parameters for vstest.console.exe - /// - private readonly ConsoleParameters consoleParameters; - - private readonly ITestPlatformEventSource testPlatformEventSource; - - #endregion - - #region Constructor - - /// - /// Initializes a new instance of the class. - /// - /// - /// Path to the test runner vstest.console.exe. - /// - public VsTestConsoleWrapperAsync(string vstestConsolePath) : - this(vstestConsolePath, ConsoleParameters.Default) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Path to the test runner vstest.console.exe. - /// The parameters to be passed onto the runner process - public VsTestConsoleWrapperAsync(string vstestConsolePath, ConsoleParameters consoleParameters) : - this(new VsTestConsoleRequestSender(), new VsTestConsoleProcessManager(vstestConsolePath), consoleParameters, TestPlatformEventSource.Instance) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Sender for test messages. - /// Process manager. - /// The parameters to be passed onto the runner process - /// Performance event source - internal VsTestConsoleWrapperAsync(ITranslationLayerRequestSenderAsync requestSender, IProcessManager processManager, ConsoleParameters consoleParameters, ITestPlatformEventSource testPlatformEventSource) - { - this.requestSender = requestSender; - this.vstestConsoleProcessManager = processManager; - this.consoleParameters = consoleParameters; - this.testPlatformEventSource = testPlatformEventSource; - this.pathToAdditionalExtensions = new List(); - - this.vstestConsoleProcessManager.ProcessExited += (sender, args) => this.requestSender.OnProcessExited(); - } - - #endregion - - #region IVsTestConsoleWrapper - - /// - public async Task StartSessionAsync() - { - EqtTrace.Info("VsTestConsoleWrapperAsync.StartSessionAsync: Starting VsTestConsoleWrapper session"); - - this.testPlatformEventSource.TranslationLayerInitializeStart(); - - var timeout = EnvironmentHelper.GetConnectionTimeout(); - // Start communication - var port = await this.requestSender.InitializeCommunicationAsync(timeout * 1000); - - if (port > 0) - { - // Fill the parameters - this.consoleParameters.ParentProcessId = Process.GetCurrentProcess().Id; - this.consoleParameters.PortNumber = port; - - // Start vstest.console.exe process - this.vstestConsoleProcessManager.StartProcess(this.consoleParameters); - } - else - { - // Close the sender as it failed to host server - this.requestSender.Close(); - throw new TransationLayerException("Error hosting communication channel and connecting to console"); - } - } - - /// - public async Task InitializeExtensionsAsync(IEnumerable pathToAdditionalExtensions) - { - await this.EnsureInitializedAsync(); - this.pathToAdditionalExtensions = pathToAdditionalExtensions.ToList(); - this.requestSender.InitializeExtensions(this.pathToAdditionalExtensions); - } - - /// - public async Task DiscoverTestsAsync(IEnumerable sources, string discoverySettings, ITestDiscoveryEventsHandler discoveryEventsHandler) - { - this.testPlatformEventSource.TranslationLayerDiscoveryStart(); - await this.EnsureInitializedAsync(); - - // Converts ITestDiscoveryEventsHandler to ITestDiscoveryEventsHandler2 - var discoveryCompleteEventsHandler2 = new DiscoveryEventsHandleConverter(discoveryEventsHandler); - await this.requestSender.DiscoverTestsAsync(sources, discoverySettings, options: null, discoveryEventsHandler: discoveryCompleteEventsHandler2); - } - - - /// - public async Task DiscoverTestsAsync(IEnumerable sources, string discoverySettings, TestPlatformOptions options, ITestDiscoveryEventsHandler2 discoveryEventsHandler) - { - this.testPlatformEventSource.TranslationLayerDiscoveryStart(); - await this.EnsureInitializedAsync(); - await this.requestSender.DiscoverTestsAsync(sources, discoverySettings, options, discoveryEventsHandler); - } - - /// - public void CancelDiscovery() - { - // TODO: Cancel Discovery - // this.requestSender.CancelDiscovery(); - } - - /// - public async Task RunTestsAsync(IEnumerable sources, string runSettings, ITestRunEventsHandler testRunEventsHandler) - { - await RunTestsAsync(sources, runSettings, null, testRunEventsHandler); - } - - /// - public async Task RunTestsAsync(IEnumerable sources, string runSettings, TestPlatformOptions options, ITestRunEventsHandler testRunEventsHandler) - { - var sourceList = sources.ToList(); - this.testPlatformEventSource.TranslationLayerExecutionStart(0, sourceList.Count, 0, runSettings ?? string.Empty); - - await this.EnsureInitializedAsync(); - await this.requestSender.StartTestRunAsync(sourceList, runSettings, options, testRunEventsHandler); - } - - /// - public async Task RunTestsAsync(IEnumerable testCases, string runSettings, ITestRunEventsHandler testRunEventsHandler) - { - var testCaseList = testCases.ToList(); - this.testPlatformEventSource.TranslationLayerExecutionStart(0, 0, testCaseList.Count, runSettings ?? string.Empty); - - await this.EnsureInitializedAsync(); - await this.requestSender.StartTestRunAsync(testCaseList, runSettings, options: null, runEventsHandler: testRunEventsHandler); - } - - /// - public async Task RunTestsAsync(IEnumerable testCases, string runSettings, TestPlatformOptions options, ITestRunEventsHandler testRunEventsHandler) - { - var testCaseList = testCases.ToList(); - this.testPlatformEventSource.TranslationLayerExecutionStart(0, 0, testCaseList.Count, runSettings ?? string.Empty); - - await this.EnsureInitializedAsync(); - await this.requestSender.StartTestRunAsync(testCaseList, runSettings, options, testRunEventsHandler); - } - - /// - public async Task RunTestsWithCustomTestHostAsync(IEnumerable sources, string runSettings, ITestRunEventsHandler testRunEventsHandler, ITestHostLauncher customTestHostLauncher) - { - await RunTestsWithCustomTestHostAsync(sources, runSettings, null, testRunEventsHandler, customTestHostLauncher); - } - - /// - public async Task RunTestsWithCustomTestHostAsync(IEnumerable sources, string runSettings, TestPlatformOptions options, ITestRunEventsHandler testRunEventsHandler, ITestHostLauncher customTestHostLauncher) - { - var sourceList = sources.ToList(); - this.testPlatformEventSource.TranslationLayerExecutionStart(1, sourceList.Count, 0, runSettings ?? string.Empty); - - await this.EnsureInitializedAsync(); - await this.requestSender.StartTestRunWithCustomHostAsync(sourceList, runSettings, options, testRunEventsHandler, customTestHostLauncher); - } - - /// - public async Task RunTestsWithCustomTestHostAsync(IEnumerable testCases, string runSettings, ITestRunEventsHandler testRunEventsHandler, ITestHostLauncher customTestHostLauncher) - { - var testCaseList = testCases.ToList(); - this.testPlatformEventSource.TranslationLayerExecutionStart(1, 0, testCaseList.Count, runSettings ?? string.Empty); - - await this.EnsureInitializedAsync(); - await this.requestSender.StartTestRunWithCustomHostAsync(testCaseList, runSettings, options: null, runEventsHandler: testRunEventsHandler, customTestHostLauncher: customTestHostLauncher); - } - - /// - public async Task RunTestsWithCustomTestHostAsync(IEnumerable testCases, string runSettings, TestPlatformOptions options, ITestRunEventsHandler testRunEventsHandler, ITestHostLauncher customTestHostLauncher) - { - var testCaseList = testCases.ToList(); - this.testPlatformEventSource.TranslationLayerExecutionStart(1, 0, testCaseList.Count, runSettings ?? string.Empty); - - await this.EnsureInitializedAsync(); - await this.requestSender.StartTestRunWithCustomHostAsync(testCaseList, runSettings, options, testRunEventsHandler, customTestHostLauncher); - } - - /// - public void CancelTestRun() - { - this.requestSender.CancelTestRun(); - } - - /// - public void AbortTestRun() - { - this.requestSender.AbortTestRun(); - } - - /// - public void EndSession() - { - this.requestSender.EndSession(); - this.requestSender.Close(); - } - - #endregion - - private async Task EnsureInitializedAsync() - { - if (!this.vstestConsoleProcessManager.IsProcessInitialized()) - { - EqtTrace.Info("VsTestConsoleWrapper.EnsureInitializedAsync: Process is not started."); - await this.StartSessionAsync(); - - EqtTrace.Info("VsTestConsoleWrapper.EnsureInitializedAsync: Send a request to initialize extensions."); - this.requestSender.InitializeExtensions(this.pathToAdditionalExtensions); - } - } - - } -} diff --git a/src/vstest.console/TestPlatformHelpers/TestRequestManager.cs b/src/vstest.console/TestPlatformHelpers/TestRequestManager.cs index 778006ebf5..3fc25a44e8 100644 --- a/src/vstest.console/TestPlatformHelpers/TestRequestManager.cs +++ b/src/vstest.console/TestPlatformHelpers/TestRequestManager.cs @@ -8,10 +8,12 @@ namespace Microsoft.VisualStudio.TestPlatform.CommandLine.TestPlatformHelpers using System.IO; using System.Linq; using System.Reflection; + using System.Threading; using System.Threading.Tasks; using System.Xml; using System.Xml.XPath; using Microsoft.VisualStudio.TestPlatform.Client; + using Microsoft.VisualStudio.TestPlatform.Client.TestRunAttachmentsProcessing; using Microsoft.VisualStudio.TestPlatform.Client.RequestHelper; using Microsoft.VisualStudio.TestPlatform.CommandLine.Internal; using Microsoft.VisualStudio.TestPlatform.CommandLine.Processors.Utilities; @@ -24,9 +26,11 @@ namespace Microsoft.VisualStudio.TestPlatform.CommandLine.TestPlatformHelpers using Microsoft.VisualStudio.TestPlatform.Common.Utilities; using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Tracing; using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Tracing.Interfaces; + using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.TestRunAttachmentsProcessing; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities; using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions; using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions.Interfaces; @@ -49,6 +53,7 @@ internal class TestRequestManager : ITestRequestManager private readonly Task metricsPublisher; private bool isDisposed; private IProcessHelper processHelper; + private ITestRunAttachmentsProcessingManager attachmentsProcessingManager; /// /// Maintains the current active execution request @@ -62,6 +67,12 @@ internal class TestRequestManager : ITestRequestManager /// private IDiscoveryRequest currentDiscoveryRequest; + /// + /// Maintains the current active test run attachments processing cancellation token source + /// Assumption : There can only be one active attachments processing request. + /// + private CancellationTokenSource currentAttachmentsProcessingCancellationTokenSource; + #region Constructor public TestRequestManager() @@ -72,11 +83,12 @@ public TestRequestManager() TestPlatformEventSource.Instance, new InferHelper(AssemblyMetadataProvider.Instance), MetricsPublisherFactory.GetMetricsPublisher(IsTelemetryOptedIn(), CommandLineOptions.Instance.IsDesignMode), - new ProcessHelper()) + new ProcessHelper(), + new TestRunAttachmentsProcessingManager(TestPlatformEventSource.Instance, new CodeCoverageDataAttachmentsHandler())) { } - internal TestRequestManager(CommandLineOptions commandLineOptions, ITestPlatform testPlatform, TestRunResultAggregator testRunResultAggregator, ITestPlatformEventSource testPlatformEventSource, InferHelper inferHelper, Task metricsPublisher, IProcessHelper processHelper) + internal TestRequestManager(CommandLineOptions commandLineOptions, ITestPlatform testPlatform, TestRunResultAggregator testRunResultAggregator, ITestPlatformEventSource testPlatformEventSource, InferHelper inferHelper, Task metricsPublisher, IProcessHelper processHelper, ITestRunAttachmentsProcessingManager attachmentsProcessingManager) { this.testPlatform = testPlatform; this.commandLineOptions = commandLineOptions; @@ -85,6 +97,7 @@ internal TestRequestManager(CommandLineOptions commandLineOptions, ITestPlatform this.inferHelper = inferHelper; this.metricsPublisher = metricsPublisher; this.processHelper = processHelper; + this.attachmentsProcessingManager = attachmentsProcessingManager; } #endregion @@ -160,9 +173,7 @@ public void DiscoverTests(DiscoveryRequestPayload discoveryPayload, ITestDiscove // Collect Commands this.LogCommandsTelemetryPoints(requestData); } - - - + // create discovery request var criteria = new DiscoveryCriteria(discoveryPayload.Sources, batchSize, this.commandLineOptions.TestStatsEventTimeout, runsettings) { @@ -301,6 +312,45 @@ public void RunTests(TestRunRequestPayload testRunRequestPayload, ITestHostLaunc } } + /// + public void ProcessTestRunAttachments(TestRunAttachmentsProcessingPayload attachmentsProcessingPayload, ITestRunAttachmentsProcessingEventsHandler attachmentsProcessingEventsHandler, ProtocolConfig protocolConfig) + { + EqtTrace.Info("TestRequestManager.ProcessTestRunAttachments: Test run attachments processing started."); + + this.telemetryOptedIn = attachmentsProcessingPayload.CollectMetrics; + var requestData = this.GetRequestData(protocolConfig); + + // Make sure to run the run request inside a lock as the below section is not thread-safe + // There can be only one discovery, execution or attachments processing request at a given point in time + lock (this.syncObject) + { + try + { + EqtTrace.Info("TestRequestManager.ProcessTestRunAttachments: Synchronization context taken"); + this.testPlatformEventSource.TestRunAttachmentsProcessingRequestStart(); + + this.currentAttachmentsProcessingCancellationTokenSource = new CancellationTokenSource(); + + Task task = this.attachmentsProcessingManager.ProcessTestRunAttachmentsAsync(requestData, attachmentsProcessingPayload.Attachments, attachmentsProcessingEventsHandler, this.currentAttachmentsProcessingCancellationTokenSource.Token); + task.Wait(); + } + finally + { + if (this.currentAttachmentsProcessingCancellationTokenSource != null) + { + this.currentAttachmentsProcessingCancellationTokenSource.Dispose(); + this.currentAttachmentsProcessingCancellationTokenSource = null; + } + + EqtTrace.Info("TestRequestManager.ProcessTestRunAttachments: Test run attachments processing completed."); + this.testPlatformEventSource.TestRunAttachmentsProcessingRequestStop(); + + // Post the attachments processing complete event + this.metricsPublisher.Result.PublishMetrics(TelemetryDataConstants.TestAttachmentsProcessingCompleteEvent, requestData.MetricsCollection.Metrics); + } + } + } + private void LogTelemetryForLegacySettings(IRequestData requestData, string runsettings) { requestData.MetricsCollection.Add(TelemetryDataConstants.TestSettingsUsed, InferRunSettingsHelper.IsTestSettingsEnabled(runsettings)); @@ -342,6 +392,13 @@ public void AbortTestRun() this.currentTestRunRequest?.Abort(); } + /// + public void CancelTestRunAttachmentsProcessing() + { + EqtTrace.Info("TestRequestManager.CancelTestRunAttachmentsProcessing: Sending cancel request."); + this.currentAttachmentsProcessingCancellationTokenSource?.Cancel(); + } + #endregion public void Dispose() diff --git a/test/Microsoft.TestPlatform.AcceptanceTests/TranslationLayerTests/CodeCoverageTests.cs b/test/Microsoft.TestPlatform.AcceptanceTests/TranslationLayerTests/CodeCoverageTests.cs new file mode 100644 index 0000000000..81c1fe51ae --- /dev/null +++ b/test/Microsoft.TestPlatform.AcceptanceTests/TranslationLayerTests/CodeCoverageTests.cs @@ -0,0 +1,443 @@ +// 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.TestPlatform.AcceptanceTests.TranslationLayerTests +{ + using Castle.Core.Internal; + using Microsoft.TestPlatform.TestUtilities; + using Microsoft.TestPlatform.VsTestConsole.TranslationLayer.Interfaces; + using Microsoft.VisualStudio.TestPlatform.Common.Telemetry; + using Microsoft.VisualStudio.TestPlatform.ObjectModel; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.IO; + using System.Linq; + using System.Threading; + using System.Threading.Tasks; + using System.Xml; + + /// + /// The Test run attachments processing tests using VsTestConsoleWrapper API's + /// + [TestClass] + public class CodeCoverageTests : AcceptanceTestBase + { + /* + * Below value is just safe coverage result for which all tests are passing. + * Inspecting this value gives us confidence that there is no big drop in overall coverage. + */ + private const double ExpectedMinimalModuleCoverage = 30.0; + + private IVsTestConsoleWrapper vstestConsoleWrapper; + private RunEventHandler runEventHandler; + private TestRunAttachmentsProcessingEventHandler testRunAttachmentsProcessingEventHandler; + + private void Setup() + { + this.vstestConsoleWrapper = this.GetVsTestConsoleWrapper(); + this.runEventHandler = new RunEventHandler(); + this.testRunAttachmentsProcessingEventHandler = new TestRunAttachmentsProcessingEventHandler(); + } + + [TestCleanup] + public void Cleanup() + { + this.vstestConsoleWrapper?.EndSession(); + } + + [TestMethod] + [NetFullTargetFrameworkDataSource] + [NetCoreTargetFrameworkDataSource] + public void TestRunWithCodeCoverage(RunnerInfo runnerInfo) + { + // arrange + AcceptanceTestBase.SetTestEnvironment(this.testEnvironment, runnerInfo); + this.Setup(); + + // act + this.vstestConsoleWrapper.RunTests(this.GetTestAssemblies(), this.GetCodeCoverageRunSettings(1), this.runEventHandler); + + // assert + Assert.AreEqual(6, this.runEventHandler.TestResults.Count); + + int expectedNumberOfAttachments = testEnvironment.RunnerFramework.Equals(IntegrationTestBase.CoreRunnerFramework) && + testEnvironment.TargetFramework.Equals(IntegrationTestBase.CoreRunnerFramework) ? 2 : 1; + Assert.AreEqual(expectedNumberOfAttachments, this.runEventHandler.Attachments.Count); + + AssertCoverageResults(this.runEventHandler.Attachments); + } + + [TestMethod] + [NetFullTargetFrameworkDataSource] + [NetCoreTargetFrameworkDataSource] + public void TestRunWithCodeCoverageParallel(RunnerInfo runnerInfo) + { + // arrange + AcceptanceTestBase.SetTestEnvironment(this.testEnvironment, runnerInfo); + this.Setup(); + + // act + this.vstestConsoleWrapper.RunTests(this.GetTestAssemblies(), this.GetCodeCoverageRunSettings(4), this.runEventHandler); + + // assert + Assert.AreEqual(6, this.runEventHandler.TestResults.Count); + Assert.AreEqual(testEnvironment.RunnerFramework.Equals(IntegrationTestBase.DesktopRunnerFramework) ? 1 : 2, this.runEventHandler.Attachments.Count); + + AssertCoverageResults(this.runEventHandler.Attachments); + } + + [TestMethod] + [NetFullTargetFrameworkDataSource] + [NetCoreTargetFrameworkDataSource] + public async Task TestRunWithCodeCoverageAndAttachmentsProcessing(RunnerInfo runnerInfo) + { + // arrange + AcceptanceTestBase.SetTestEnvironment(this.testEnvironment, runnerInfo); + this.Setup(); + + this.vstestConsoleWrapper.RunTests(this.GetTestAssemblies().Take(1), this.GetCodeCoverageRunSettings(1), this.runEventHandler); + this.vstestConsoleWrapper.RunTests(this.GetTestAssemblies().Skip(1), this.GetCodeCoverageRunSettings(1), this.runEventHandler); + + Assert.AreEqual(6, this.runEventHandler.TestResults.Count); + Assert.AreEqual(2, this.runEventHandler.Attachments.Count); + + // act + await this.vstestConsoleWrapper.ProcessTestRunAttachmentsAsync(runEventHandler.Attachments, null, true, true, testRunAttachmentsProcessingEventHandler, CancellationToken.None); + + // Assert + testRunAttachmentsProcessingEventHandler.EnsureSuccess(); + Assert.AreEqual(testEnvironment.RunnerFramework.Equals(IntegrationTestBase.DesktopRunnerFramework) ? 1 : 2, this.testRunAttachmentsProcessingEventHandler.Attachments.Count); + + AssertCoverageResults(this.testRunAttachmentsProcessingEventHandler.Attachments); + + Assert.IsFalse(testRunAttachmentsProcessingEventHandler.CompleteArgs.IsCanceled); + Assert.IsNull(testRunAttachmentsProcessingEventHandler.CompleteArgs.Error); + + for (int i = 0; i < testRunAttachmentsProcessingEventHandler.ProgressArgs.Count; i++) + { + VisualStudio.TestPlatform.ObjectModel.Client.TestRunAttachmentsProcessingProgressEventArgs progressArgs = testRunAttachmentsProcessingEventHandler.ProgressArgs[i]; + Assert.AreEqual(1, progressArgs.CurrentAttachmentProcessorIndex); + Assert.AreEqual(1, progressArgs.CurrentAttachmentProcessorUris.Count); + Assert.AreEqual("datacollector://microsoft/CodeCoverage/2.0", progressArgs.CurrentAttachmentProcessorUris.First().AbsoluteUri); + Assert.AreEqual(1, progressArgs.AttachmentProcessorsCount); + if (testRunAttachmentsProcessingEventHandler.ProgressArgs.Count == 2) + { + Assert.AreEqual(i == 0 ? 50 : 100, progressArgs.CurrentAttachmentProcessorProgress); + } + } + + Assert.AreEqual("Completed", testRunAttachmentsProcessingEventHandler.CompleteArgs.Metrics[TelemetryDataConstants.AttachmentsProcessingState]); + Assert.AreEqual(2L, testRunAttachmentsProcessingEventHandler.CompleteArgs.Metrics[TelemetryDataConstants.NumberOfAttachmentsSentForProcessing]); + Assert.AreEqual(testEnvironment.RunnerFramework.Equals(IntegrationTestBase.DesktopRunnerFramework) ? 1L : 2L, testRunAttachmentsProcessingEventHandler.CompleteArgs.Metrics[TelemetryDataConstants.NumberOfAttachmentsAfterProcessing]); + Assert.IsTrue(testRunAttachmentsProcessingEventHandler.CompleteArgs.Metrics.ContainsKey(TelemetryDataConstants.TimeTakenInSecForAttachmentsProcessing)); + + Assert.IsTrue(File.Exists(runEventHandler.Attachments.First().Attachments.First().Uri.LocalPath)); + Assert.IsTrue(File.Exists(runEventHandler.Attachments.Last().Attachments.First().Uri.LocalPath) != testEnvironment.RunnerFramework.Equals(IntegrationTestBase.DesktopRunnerFramework)); + } + + [TestMethod] + [NetFullTargetFrameworkDataSource] + [NetCoreTargetFrameworkDataSource] + public async Task TestRunWithCodeCoverageAndAttachmentsProcessingNoMetrics(RunnerInfo runnerInfo) + { + // arrange + AcceptanceTestBase.SetTestEnvironment(this.testEnvironment, runnerInfo); + this.Setup(); + + this.vstestConsoleWrapper.RunTests(this.GetTestAssemblies().Take(1), this.GetCodeCoverageRunSettings(1), this.runEventHandler); + this.vstestConsoleWrapper.RunTests(this.GetTestAssemblies().Skip(1), this.GetCodeCoverageRunSettings(1), this.runEventHandler); + + Assert.AreEqual(6, this.runEventHandler.TestResults.Count); + Assert.AreEqual(2, this.runEventHandler.Attachments.Count); + + // act + await this.vstestConsoleWrapper.ProcessTestRunAttachmentsAsync(runEventHandler.Attachments, null, true, false, testRunAttachmentsProcessingEventHandler, CancellationToken.None); + + // Assert + testRunAttachmentsProcessingEventHandler.EnsureSuccess(); + Assert.AreEqual(testEnvironment.RunnerFramework.Equals(IntegrationTestBase.DesktopRunnerFramework) ? 1 : 2, this.testRunAttachmentsProcessingEventHandler.Attachments.Count); + + AssertCoverageResults(this.testRunAttachmentsProcessingEventHandler.Attachments); + + Assert.IsFalse(testRunAttachmentsProcessingEventHandler.CompleteArgs.IsCanceled); + Assert.IsNull(testRunAttachmentsProcessingEventHandler.CompleteArgs.Error); + + for (int i = 0; i < testRunAttachmentsProcessingEventHandler.ProgressArgs.Count; i++) + { + VisualStudio.TestPlatform.ObjectModel.Client.TestRunAttachmentsProcessingProgressEventArgs progressArgs = testRunAttachmentsProcessingEventHandler.ProgressArgs[i]; + Assert.AreEqual(1, progressArgs.CurrentAttachmentProcessorIndex); + Assert.AreEqual(1, progressArgs.CurrentAttachmentProcessorUris.Count); + Assert.AreEqual("datacollector://microsoft/CodeCoverage/2.0", progressArgs.CurrentAttachmentProcessorUris.First().AbsoluteUri); + Assert.AreEqual(1, progressArgs.AttachmentProcessorsCount); + if (testRunAttachmentsProcessingEventHandler.ProgressArgs.Count == 2) + { + Assert.AreEqual(i == 0 ? 50 : 100, progressArgs.CurrentAttachmentProcessorProgress); + } + } + + Assert.IsTrue(testRunAttachmentsProcessingEventHandler.CompleteArgs.Metrics.IsNullOrEmpty()); + + Assert.IsTrue(File.Exists(runEventHandler.Attachments.First().Attachments.First().Uri.LocalPath)); + Assert.IsTrue(File.Exists(runEventHandler.Attachments.Last().Attachments.First().Uri.LocalPath) != testEnvironment.RunnerFramework.Equals(IntegrationTestBase.DesktopRunnerFramework)); + } + + [TestMethod] + [NetFullTargetFrameworkDataSource] + [NetCoreTargetFrameworkDataSource] + public async Task TestRunWithCodeCoverageAndAttachmentsProcessingModuleDuplicated(RunnerInfo runnerInfo) + { + // arrange + AcceptanceTestBase.SetTestEnvironment(this.testEnvironment, runnerInfo); + this.Setup(); + + this.vstestConsoleWrapper.RunTests(this.GetTestAssemblies().Take(1), this.GetCodeCoverageRunSettings(1), this.runEventHandler); + this.vstestConsoleWrapper.RunTests(this.GetTestAssemblies().Skip(1), this.GetCodeCoverageRunSettings(1), this.runEventHandler); + this.vstestConsoleWrapper.RunTests(this.GetTestAssemblies().Skip(1), this.GetCodeCoverageRunSettings(1), this.runEventHandler); + + Assert.AreEqual(9, this.runEventHandler.TestResults.Count); + Assert.AreEqual(3, this.runEventHandler.Attachments.Count); + + // act + await this.vstestConsoleWrapper.ProcessTestRunAttachmentsAsync(runEventHandler.Attachments, null, true, true, testRunAttachmentsProcessingEventHandler, CancellationToken.None); + + // Assert + testRunAttachmentsProcessingEventHandler.EnsureSuccess(); + Assert.AreEqual(testEnvironment.RunnerFramework.Equals(IntegrationTestBase.DesktopRunnerFramework) ? 1 : 3, this.testRunAttachmentsProcessingEventHandler.Attachments.Count); + + AssertCoverageResults(this.testRunAttachmentsProcessingEventHandler.Attachments); + + Assert.IsFalse(testRunAttachmentsProcessingEventHandler.CompleteArgs.IsCanceled); + Assert.IsNull(testRunAttachmentsProcessingEventHandler.CompleteArgs.Error); + + for (int i = 0; i < testRunAttachmentsProcessingEventHandler.ProgressArgs.Count; i++) + { + VisualStudio.TestPlatform.ObjectModel.Client.TestRunAttachmentsProcessingProgressEventArgs progressArgs = testRunAttachmentsProcessingEventHandler.ProgressArgs[i]; + Assert.AreEqual(1, progressArgs.CurrentAttachmentProcessorIndex); + Assert.AreEqual("datacollector://microsoft/CodeCoverage/2.0", progressArgs.CurrentAttachmentProcessorUris.First().AbsoluteUri); + Assert.AreEqual(1, progressArgs.AttachmentProcessorsCount); + + if (testRunAttachmentsProcessingEventHandler.ProgressArgs.Count == 3) + { + Assert.AreEqual(i == 0 ? 33 : i == 1 ? 66 : 100, progressArgs.CurrentAttachmentProcessorProgress); + } + } + + Assert.AreEqual("Completed", testRunAttachmentsProcessingEventHandler.CompleteArgs.Metrics[TelemetryDataConstants.AttachmentsProcessingState]); + Assert.AreEqual(3L, testRunAttachmentsProcessingEventHandler.CompleteArgs.Metrics[TelemetryDataConstants.NumberOfAttachmentsSentForProcessing]); + Assert.AreEqual(testEnvironment.RunnerFramework.Equals(IntegrationTestBase.DesktopRunnerFramework) ? 1L : 3L, testRunAttachmentsProcessingEventHandler.CompleteArgs.Metrics[TelemetryDataConstants.NumberOfAttachmentsAfterProcessing]); + Assert.IsTrue(testRunAttachmentsProcessingEventHandler.CompleteArgs.Metrics.ContainsKey(TelemetryDataConstants.TimeTakenInSecForAttachmentsProcessing)); + + Assert.IsTrue(File.Exists(runEventHandler.Attachments.First().Attachments.First().Uri.LocalPath)); + Assert.IsTrue(File.Exists(runEventHandler.Attachments.Last().Attachments.First().Uri.LocalPath) != testEnvironment.RunnerFramework.Equals(IntegrationTestBase.DesktopRunnerFramework)); + } + + [TestMethod] + [NetFullTargetFrameworkDataSource] + [NetCoreTargetFrameworkDataSource] + public async Task TestRunWithCodeCoverageAndAttachmentsProcessingCancelled(RunnerInfo runnerInfo) + { + // arrange + AcceptanceTestBase.SetTestEnvironment(this.testEnvironment, runnerInfo); + this.Setup(); + + if (!testEnvironment.RunnerFramework.Equals(IntegrationTestBase.DesktopRunnerFramework)) return; + + this.vstestConsoleWrapper.RunTests(this.GetTestAssemblies().Take(1), this.GetCodeCoverageRunSettings(1), this.runEventHandler); + Assert.AreEqual(3, this.runEventHandler.TestResults.Count); + Assert.AreEqual(1, this.runEventHandler.Attachments.Count); + + List attachments = Enumerable.Range(0, 1000).Select(i => this.runEventHandler.Attachments.First()).ToList(); + + CancellationTokenSource cts = new CancellationTokenSource(); + + Task attachmentsProcessing = this.vstestConsoleWrapper.ProcessTestRunAttachmentsAsync(attachments, null, true, true, testRunAttachmentsProcessingEventHandler, cts.Token); + + while (true) + { + try + { + if (testRunAttachmentsProcessingEventHandler.ProgressArgs.Count >= 3) + break; + } + catch + { + // ignore + } + await Task.Delay(100); + } + + // act + cts.Cancel(); + + // Assert + await attachmentsProcessing; + testRunAttachmentsProcessingEventHandler.EnsureSuccess(); + + Assert.AreEqual(1000, this.testRunAttachmentsProcessingEventHandler.Attachments.Count); + + Assert.IsTrue(testRunAttachmentsProcessingEventHandler.CompleteArgs.IsCanceled); + Assert.IsNull(testRunAttachmentsProcessingEventHandler.CompleteArgs.Error); + + Assert.IsTrue((testEnvironment.RunnerFramework.Equals(IntegrationTestBase.DesktopRunnerFramework) ? 3 : 0) <= testRunAttachmentsProcessingEventHandler.ProgressArgs.Count); + for (int i = 0; i < testRunAttachmentsProcessingEventHandler.ProgressArgs.Count; i++) + { + VisualStudio.TestPlatform.ObjectModel.Client.TestRunAttachmentsProcessingProgressEventArgs progressArgs = testRunAttachmentsProcessingEventHandler.ProgressArgs[i]; + Assert.AreEqual(1, progressArgs.CurrentAttachmentProcessorIndex); + Assert.AreEqual("datacollector://microsoft/CodeCoverage/2.0", progressArgs.CurrentAttachmentProcessorUris.First().AbsoluteUri); + Assert.AreEqual(1, progressArgs.AttachmentProcessorsCount); + + if (i == 0) + { + Assert.AreEqual(0, progressArgs.CurrentAttachmentProcessorProgress); + } + } + + Assert.AreEqual("Canceled", testRunAttachmentsProcessingEventHandler.CompleteArgs.Metrics[TelemetryDataConstants.AttachmentsProcessingState]); + Assert.AreEqual(1000L, testRunAttachmentsProcessingEventHandler.CompleteArgs.Metrics[TelemetryDataConstants.NumberOfAttachmentsSentForProcessing]); + Assert.AreEqual(1000L, testRunAttachmentsProcessingEventHandler.CompleteArgs.Metrics[TelemetryDataConstants.NumberOfAttachmentsAfterProcessing]); + Assert.IsTrue(testRunAttachmentsProcessingEventHandler.CompleteArgs.Metrics.ContainsKey(TelemetryDataConstants.TimeTakenInSecForAttachmentsProcessing)); + + Assert.IsTrue(File.Exists(runEventHandler.Attachments.First().Attachments.First().Uri.LocalPath)); + } + + [TestMethod] + [NetFullTargetFrameworkDataSource] + [NetCoreTargetFrameworkDataSource] + public async Task EndSessionShouldEnsureVstestConsoleProcessDies(RunnerInfo runnerInfo) + { + var numOfProcesses = Process.GetProcessesByName("vstest.console").Length; + + AcceptanceTestBase.SetTestEnvironment(this.testEnvironment, runnerInfo); + this.Setup(); + + this.vstestConsoleWrapper.RunTests(this.GetTestAssemblies().Take(1), this.GetCodeCoverageRunSettings(1), this.runEventHandler); + this.vstestConsoleWrapper.RunTests(this.GetTestAssemblies().Skip(1), this.GetCodeCoverageRunSettings(1), this.runEventHandler); + + Assert.AreEqual(6, this.runEventHandler.TestResults.Count); + Assert.AreEqual(2, this.runEventHandler.Attachments.Count); + + await this.vstestConsoleWrapper.ProcessTestRunAttachmentsAsync(runEventHandler.Attachments, null, true, true, testRunAttachmentsProcessingEventHandler, CancellationToken.None); + + // act + this.vstestConsoleWrapper?.EndSession(); + + // Assert + Assert.AreEqual(numOfProcesses, Process.GetProcessesByName("vstest.console").Length); + + this.vstestConsoleWrapper = null; + } + + private IList GetTestAssemblies() + { + return GetProjects().Select(p => this.GetAssetFullPath(p)).ToList(); + } + + private IList GetProjects() + { + return new List { "SimpleTestProject.dll", "SimpleTestProject2.dll" }; + } + + /// + /// Default RunSettings + /// + /// + public string GetCodeCoverageRunSettings(int cpuCount) + { + string runSettingsXml = $@" + + + {FrameworkArgValue} + {GetCodeCoveragePath()} + {cpuCount} + + + + + + + + + .*CPPUnitTestFramework.* + + + + + True + True + True + False + + + + + + "; + return runSettingsXml; + } + + private void AssertCoverageResults(IList attachments) + { + if (attachments.Count == 1) + { + var xmlCoverage = GetXmlCoverage(attachments.First()); + + foreach (var project in GetProjects()) + { + var moduleNode = GetModuleNode(xmlCoverage.DocumentElement, project.ToLower()); + AssertCoverage(moduleNode, ExpectedMinimalModuleCoverage); + } + } + } + + private XmlDocument GetXmlCoverage(AttachmentSet attachment) + { + string output = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString() + ".xml"); + + var analyze = Process.Start(new ProcessStartInfo + { + FileName = GetCodeCoverageExePath(), + Arguments = $"analyze /include_skipped_functions /include_skipped_modules /output:\"{output}\" \"{attachment.Attachments.First().Uri.LocalPath}\"", + RedirectStandardOutput = true, + UseShellExecute = false + }); + + string analysisOutput = analyze.StandardOutput.ReadToEnd(); + + analyze.WaitForExit(); + Assert.IsTrue(0 == analyze.ExitCode, $"Code Coverage analyze failed: {analysisOutput}"); + + XmlDocument coverage = new XmlDocument(); + coverage.Load(output); + return coverage; + } + + private string GetCodeCoveragePath() + { + return Path.Combine(IntegrationTestEnvironment.TestPlatformRootDirectory, "artifacts", IntegrationTestEnvironment.BuildConfiguration, "Microsoft.CodeCoverage"); + } + + private string GetCodeCoverageExePath() + { + return Path.Combine(GetCodeCoveragePath(), "CodeCoverage", "CodeCoverage.exe"); + } + + private XmlNode GetModuleNode(XmlNode node, string name) + { + return GetNode(node, "module", name); + } + + private XmlNode GetNode(XmlNode node, string type, string name) + { + return node.SelectSingleNode($"//{type}[@name='{name}']"); + } + + private void AssertCoverage(XmlNode node, double expectedCoverage) + { + var coverage = double.Parse(node.Attributes["block_coverage"].Value); + Console.WriteLine($"Checking coverage for {node.Name} {node.Attributes["name"].Value}. Expected at least: {expectedCoverage}. Result: {coverage}"); + Assert.IsTrue(coverage > expectedCoverage, $"Coverage check failed for {node.Name} {node.Attributes["name"].Value}. Expected at least: {expectedCoverage}. Found: {coverage}"); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.TestPlatform.AcceptanceTests/TranslationLayerTests/EventHandler/RunEventHandler.cs b/test/Microsoft.TestPlatform.AcceptanceTests/TranslationLayerTests/EventHandler/RunEventHandler.cs index f3e1793d30..c4bd80e385 100644 --- a/test/Microsoft.TestPlatform.AcceptanceTests/TranslationLayerTests/EventHandler/RunEventHandler.cs +++ b/test/Microsoft.TestPlatform.AcceptanceTests/TranslationLayerTests/EventHandler/RunEventHandler.cs @@ -18,6 +18,11 @@ public class RunEventHandler : ITestRunEventsHandler2 /// public List TestResults { get; private set; } + /// + /// Gets the attachments. + /// + public List Attachments { get; private set; } + /// /// Gets the metrics. /// @@ -39,6 +44,7 @@ public RunEventHandler() { this.TestResults = new List(); this.Errors = new List(); + this.Attachments = new List(); } public void EnsureSuccess() @@ -69,6 +75,11 @@ public void HandleTestRunComplete( this.TestResults.AddRange(lastChunkArgs.NewTestResults); } + if (testRunCompleteArgs.AttachmentSets != null) + { + this.Attachments.AddRange(testRunCompleteArgs.AttachmentSets); + } + this.Metrics = testRunCompleteArgs.Metrics; } diff --git a/test/Microsoft.TestPlatform.AcceptanceTests/TranslationLayerTests/EventHandler/TestRunAttachmentsProcessingEventHandler.cs b/test/Microsoft.TestPlatform.AcceptanceTests/TranslationLayerTests/EventHandler/TestRunAttachmentsProcessingEventHandler.cs new file mode 100644 index 0000000000..8a0b6513c6 --- /dev/null +++ b/test/Microsoft.TestPlatform.AcceptanceTests/TranslationLayerTests/EventHandler/TestRunAttachmentsProcessingEventHandler.cs @@ -0,0 +1,109 @@ +// 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.TestPlatform.AcceptanceTests.TranslationLayerTests +{ + using System; + using System.Collections.Generic; + using System.Linq; + using Microsoft.VisualStudio.TestPlatform.ObjectModel; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; + + /// + public class TestRunAttachmentsProcessingEventHandler : ITestRunAttachmentsProcessingEventsHandler + { + public List Attachments { get; private set; } + + public TestRunAttachmentsProcessingCompleteEventArgs CompleteArgs { get; private set; } + + public List ProgressArgs { get; private set; } + + /// + /// Gets the log message. + /// + public string LogMessage { get; private set; } + + public List Errors { get; set; } + + /// + /// Gets the test message level. + /// + public TestMessageLevel TestMessageLevel { get; private set; } + + public TestRunAttachmentsProcessingEventHandler() + { + this.Errors = new List(); + this.Attachments = new List(); + this.ProgressArgs = new List(); + } + + public void EnsureSuccess() + { + if (this.Errors.Any()) + { + throw new InvalidOperationException($"Test run reported errors:{Environment.NewLine}{string.Join(Environment.NewLine + Environment.NewLine, this.Errors)}"); + } + } + + public void HandleLogMessage(TestMessageLevel level, string message) + { + this.LogMessage = message; + this.TestMessageLevel = level; + if (level == TestMessageLevel.Error) + { + this.Errors.Add(message); + } + } + + public void HandleRawMessage(string rawMessage) + { + // No op + } + + public int LaunchProcessWithDebuggerAttached(TestProcessStartInfo testProcessStartInfo) + { + // No op + return -1; + } + + public bool AttachDebuggerToProcess(int pid) + { + // No op + return true; + } + + public void HandleTestRunAttachmentsProcessingComplete(ICollection attachments) + { + if(attachments != null) + { + this.Attachments.AddRange(attachments); + } + } + + public void HandleTestRunAttachmentsProcessingComplete(TestRunAttachmentsProcessingCompleteEventArgs attachmentsProcessingCompleteEventArgs, IEnumerable lastChunk) + { + if (lastChunk != null) + { + this.Attachments.AddRange(lastChunk); + } + + if (attachmentsProcessingCompleteEventArgs.Error != null) + { + Errors.Add(attachmentsProcessingCompleteEventArgs.Error.Message); + } + + CompleteArgs = attachmentsProcessingCompleteEventArgs; + } + + public void HandleProcessedAttachmentsChunk(IEnumerable attachments) + { + throw new NotImplementedException(); + } + + public void HandleTestRunAttachmentsProcessingProgress(TestRunAttachmentsProcessingProgressEventArgs AttachmentsProcessingProgressEventArgs) + { + ProgressArgs.Add(AttachmentsProcessingProgressEventArgs); + } + } +} diff --git a/test/Microsoft.TestPlatform.Client.UnitTests/AttachmentsProcessing/TestRunAttachmentsProcessingEventsHandlerTests.cs b/test/Microsoft.TestPlatform.Client.UnitTests/AttachmentsProcessing/TestRunAttachmentsProcessingEventsHandlerTests.cs new file mode 100644 index 0000000000..2988e594c6 --- /dev/null +++ b/test/Microsoft.TestPlatform.Client.UnitTests/AttachmentsProcessing/TestRunAttachmentsProcessingEventsHandlerTests.cs @@ -0,0 +1,67 @@ +// 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.TestPlatform.Client.UnitTests.TestRunAttachmentsProcessing +{ + using 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 Microsoft.VisualStudio.TestTools.UnitTesting; + + using Moq; + + [TestClass] + public class TestRunAttachmentsProcessingEventsHandlerTests + { + private readonly Mock mockCommunicationManager; + private readonly ITestRunAttachmentsProcessingEventsHandler handler; + + public TestRunAttachmentsProcessingEventsHandlerTests() + { + this.mockCommunicationManager = new Mock(); + this.handler = new TestRunAttachmentsProcessingEventsHandler(mockCommunicationManager.Object); + } + + [TestMethod] + public void EventsHandlerHandleLogMessageShouldSendTestMessage() + { + string message = "error message"; + + handler.HandleLogMessage(TestMessageLevel.Error, message); + + mockCommunicationManager.Verify(cm => cm.SendMessage(MessageType.TestMessage, It.Is(p => p.MessageLevel == TestMessageLevel.Error && p.Message == message))); + } + + [TestMethod] + public void EventsHandlerHandleTestRunAttachmentsProcessingCompleteShouldSendAttachmentsProcessingCompleteMessage() + { + var attachments = new[] { new AttachmentSet(new System.Uri("http://www.bing.com/"), "code coverage") }; + var args = new TestRunAttachmentsProcessingCompleteEventArgs(false, null); + + handler.HandleTestRunAttachmentsProcessingComplete(args, attachments); + + mockCommunicationManager.Verify(cm => cm.SendMessage(MessageType.TestRunAttachmentsProcessingComplete, It.Is(p => p.Attachments == attachments && p.AttachmentsProcessingCompleteEventArgs == args))); + } + + [TestMethod] + public void EventsHandlerHandleTestRunAttachmentsProcessingProgressShouldSendAttachmentsProcessingProgressMessage() + { + var args = new TestRunAttachmentsProcessingProgressEventArgs(1, new[] { new System.Uri("http://www.bing.com/") }, 90, 2); + + handler.HandleTestRunAttachmentsProcessingProgress(args); + + mockCommunicationManager.Verify(cm => cm.SendMessage(MessageType.TestRunAttachmentsProcessingProgress, It.Is(p => p.AttachmentsProcessingProgressEventArgs == args))); + } + + [TestMethod] + public void EventsHandlerHandleRawMessageShouldDoNothing() + { + handler.HandleRawMessage("any"); + + mockCommunicationManager.Verify(cm => cm.SendMessage(It.IsAny()), Times.Never); + mockCommunicationManager.Verify(cm => cm.SendMessage(It.IsAny(), It.IsAny()), Times.Never); + } + } +} diff --git a/test/Microsoft.TestPlatform.Client.UnitTests/DesignMode/DesignModeClientTests.cs b/test/Microsoft.TestPlatform.Client.UnitTests/DesignMode/DesignModeClientTests.cs index 98befe71f8..1066cc4f3d 100644 --- a/test/Microsoft.TestPlatform.Client.UnitTests/DesignMode/DesignModeClientTests.cs +++ b/test/Microsoft.TestPlatform.Client.UnitTests/DesignMode/DesignModeClientTests.cs @@ -9,6 +9,7 @@ namespace Microsoft.VisualStudio.TestPlatform.Client.UnitTests.DesignMode using System.Threading.Tasks; using Microsoft.VisualStudio.TestPlatform.Client.DesignMode; + using Microsoft.VisualStudio.TestPlatform.Client.TestRunAttachmentsProcessing; using Microsoft.VisualStudio.TestPlatform.Client.RequestHelper; using Microsoft.VisualStudio.TestPlatform.Common.Interfaces; using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities; @@ -405,6 +406,92 @@ public void DesignModeClientConnectShouldSendTestMessageAndDiscoverCompleteOnTes this.mockCommunicationManager.Verify(cm => cm.SendMessage(MessageType.DiscoveryComplete, It.IsAny()), Times.Once()); } + [TestMethod] + public void DesignModeClientConnectShouldSendTestMessageAndAttachmentsProcessingCompleteOnExceptionInAttachmentsProcessing() + { + var payload = new TestRunAttachmentsProcessingPayload(); + var startAttachmentsProcessing = new Message { MessageType = MessageType.TestRunAttachmentsProcessingStart, Payload = JToken.FromObject(payload) }; + this.mockCommunicationManager.Setup(cm => cm.WaitForServerConnection(It.IsAny())).Returns(true); + this.mockCommunicationManager.SetupSequence(cm => cm.ReceiveMessage()).Returns(startAttachmentsProcessing); + this.mockCommunicationManager + .Setup(cm => cm.SendMessage(MessageType.TestRunAttachmentsProcessingComplete, It.IsAny())) + .Callback(() => complateEvent.Set()); + this.mockTestRequestManager.Setup( + rm => rm.ProcessTestRunAttachments( + It.IsAny(), + It.IsAny(), + It.IsAny())) + .Throws(new Exception()); + + this.designModeClient.ConnectToClientAndProcessRequests(PortNumber, this.mockTestRequestManager.Object); + + Assert.IsTrue(this.complateEvent.WaitOne(Timeout), "AttachmentsProcessing not completed."); + this.mockCommunicationManager.Verify(cm => cm.SendMessage(MessageType.TestMessage, It.IsAny()), Times.Once()); + this.mockCommunicationManager.Verify(cm => cm.SendMessage(MessageType.TestRunAttachmentsProcessingComplete, It.Is(p => p.Attachments == null)), Times.Once()); + } + + [TestMethod] + public void DesignModeClientConnectShouldSendTestMessageAndDiscoverCompleteOnTestPlatformExceptionInAttachmentsProcessing() + { + var payload = new TestRunAttachmentsProcessingPayload(); + var startAttachmentsProcessing = new Message { MessageType = MessageType.TestRunAttachmentsProcessingStart, Payload = JToken.FromObject(payload) }; + this.mockCommunicationManager.Setup(cm => cm.WaitForServerConnection(It.IsAny())).Returns(true); + this.mockCommunicationManager.SetupSequence(cm => cm.ReceiveMessage()).Returns(startAttachmentsProcessing); + this.mockCommunicationManager + .Setup(cm => cm.SendMessage(MessageType.TestRunAttachmentsProcessingComplete, It.IsAny())) + .Callback(() => complateEvent.Set()); + this.mockTestRequestManager.Setup( + rm => rm.ProcessTestRunAttachments( + It.IsAny(), + It.IsAny(), + It.IsAny())) + .Throws(new TestPlatformException("Hello world")); + + this.designModeClient.ConnectToClientAndProcessRequests(PortNumber, this.mockTestRequestManager.Object); + + Assert.IsTrue(this.complateEvent.WaitOne(Timeout), "AttachmentsProcessing not completed."); + this.mockCommunicationManager.Verify(cm => cm.SendMessage(MessageType.TestMessage, It.IsAny()), Times.Once()); + this.mockCommunicationManager.Verify(cm => cm.SendMessage(MessageType.TestRunAttachmentsProcessingComplete, It.Is(p => p.Attachments == null)), Times.Once()); + } + + [TestMethod] + public void DesignModeClientConnectShouldCallRequestManagerForAttachmentsProcessingStart() + { + var payload = new TestRunAttachmentsProcessingPayload(); + var startAttachmentsProcessing = new Message { MessageType = MessageType.TestRunAttachmentsProcessingStart, Payload = JToken.FromObject(payload) }; + this.mockCommunicationManager.Setup(cm => cm.WaitForServerConnection(It.IsAny())).Returns(true); + this.mockCommunicationManager.SetupSequence(cm => cm.ReceiveMessage()).Returns(startAttachmentsProcessing); + + this.mockTestRequestManager + .Setup( + rm => rm.ProcessTestRunAttachments( + It.IsAny(), + It.IsAny(), + It.IsAny())) + .Callback(() => complateEvent.Set()); + + this.designModeClient.ConnectToClientAndProcessRequests(PortNumber, this.mockTestRequestManager.Object); + + Assert.IsTrue(this.complateEvent.WaitOne(Timeout), "AttachmentsProcessing not completed."); + this.mockCommunicationManager.Verify(cm => cm.SendMessage(MessageType.TestMessage, It.IsAny()), Times.Never); + this.mockCommunicationManager.Verify(cm => cm.SendMessage(MessageType.TestRunAttachmentsProcessingComplete, It.IsAny()), Times.Never); + this.mockTestRequestManager.Verify(rm => rm.ProcessTestRunAttachments(It.IsAny(), It.IsAny(), It.IsAny())); + } + + [TestMethod] + public void DesignModeClientConnectShouldCallRequestManagerForAttachmentsProcessingCancel() + { + var cancelAttachmentsProcessing = new Message { MessageType = MessageType.TestRunAttachmentsProcessingCancel }; + this.mockCommunicationManager.Setup(cm => cm.WaitForServerConnection(It.IsAny())).Returns(true); + this.mockCommunicationManager.SetupSequence(cm => cm.ReceiveMessage()).Returns(cancelAttachmentsProcessing); + + this.designModeClient.ConnectToClientAndProcessRequests(PortNumber, this.mockTestRequestManager.Object); + + this.mockCommunicationManager.Verify(cm => cm.SendMessage(MessageType.TestMessage, It.IsAny()), Times.Never); + this.mockCommunicationManager.Verify(cm => cm.SendMessage(MessageType.TestRunAttachmentsProcessingComplete, It.IsAny()), Times.Never); + this.mockTestRequestManager.Verify(rm => rm.CancelTestRunAttachmentsProcessing()); + } + [TestMethod] public void DesignModeClientConnectShouldSendTestMessageAndExecutionCompleteOnExceptionInTestRun() { diff --git a/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/AttachmentsProcessing/TestRunAttachmentsProcessingManagerTests.cs b/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/AttachmentsProcessing/TestRunAttachmentsProcessingManagerTests.cs new file mode 100644 index 0000000000..a51adcc7d6 --- /dev/null +++ b/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/AttachmentsProcessing/TestRunAttachmentsProcessingManagerTests.cs @@ -0,0 +1,625 @@ +// 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.TestPlatform.CrossPlatEngine.UnitTests.TestRunAttachmentsProcessing +{ + using System; + using System.Collections.Generic; + 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.CrossPlatEngine.TestRunAttachmentsProcessing; + using Microsoft.VisualStudio.TestPlatform.ObjectModel; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + + [TestClass] + public class TestRunAttachmentsProcessingManagerTests + { + private const string uri1 = "datacollector://microsoft/some1/1.0"; + private const string uri2 = "datacollector://microsoft/some2/2.0"; + private const string uri3 = "datacollector://microsoft/some3/2.0"; + + private readonly Mock mockRequestData; + private readonly Mock mockMetricsCollection; + private readonly Mock mockEventSource; + private readonly Mock mockAttachmentHandler1; + private readonly Mock mockAttachmentHandler2; + private readonly Mock mockEventsHandler; + private readonly TestRunAttachmentsProcessingManager manager; + private readonly CancellationTokenSource cancellationTokenSource; + + public TestRunAttachmentsProcessingManagerTests() + { + mockRequestData = new Mock(); + mockMetricsCollection = new Mock(); + mockRequestData.Setup(r => r.MetricsCollection).Returns(mockMetricsCollection.Object); + + mockEventSource = new Mock(); + mockAttachmentHandler1 = new Mock(); + mockAttachmentHandler2 = new Mock(); + mockEventsHandler = new Mock(); + + mockAttachmentHandler1.Setup(h => h.GetExtensionUris()).Returns(new[] { new Uri(uri1) }); + mockAttachmentHandler2.Setup(h => h.GetExtensionUris()).Returns(new[] { new Uri(uri2) }); + + manager = new TestRunAttachmentsProcessingManager(mockEventSource.Object, mockAttachmentHandler1.Object, mockAttachmentHandler2.Object); + + cancellationTokenSource = new CancellationTokenSource(); + } + + [TestMethod] + public async Task ProcessTestRunAttachmentsAsync_ShouldReturnInitialAttachmentsThroughEventsHandler_IfNoAttachmentsOnInput() + { + // arrange + List inputAttachments = new List(); + + // act + await manager.ProcessTestRunAttachmentsAsync(mockRequestData.Object, inputAttachments, mockEventsHandler.Object, cancellationTokenSource.Token); + + // assert + VerifyCompleteEvent(false, false); + mockEventsHandler.Verify(h => h.HandleTestRunAttachmentsProcessingComplete(It.Is(a => !a.IsCanceled), It.Is>(c => c.Count == 0))); + mockEventsHandler.Verify(h => h.HandleLogMessage(It.IsAny(), It.IsAny()), Times.Never); + mockEventsHandler.Verify(h => h.HandleTestRunAttachmentsProcessingProgress(It.IsAny()), Times.Never); + mockEventSource.Verify(s => s.TestRunAttachmentsProcessingStart(0)); + mockEventSource.Verify(s => s.TestRunAttachmentsProcessingStop(0)); + mockAttachmentHandler1.Verify(h => h.GetExtensionUris(), Times.Never); + mockAttachmentHandler2.Verify(h => h.GetExtensionUris(), Times.Never); + mockAttachmentHandler1.Verify(h => h.ProcessAttachmentSetsAsync(It.IsAny>(), It.IsAny>(), It.IsAny(), It.IsAny()), Times.Never); + mockAttachmentHandler2.Verify(h => h.ProcessAttachmentSetsAsync(It.IsAny>(), It.IsAny>(), It.IsAny(), It.IsAny()), Times.Never); + + VerifyMetrics(inputCount: 0, outputCount: 0); + } + + [TestMethod] + public async Task ProcessTestRunAttachmentsAsync_ShouldReturnNoAttachments_IfNoAttachmentsOnInput() + { + // arrange + List inputAttachments = new List(); + + // act + var result = await manager.ProcessTestRunAttachmentsAsync(mockRequestData.Object, inputAttachments, cancellationTokenSource.Token); + + // assert + Assert.AreEqual(0, result.Count); + mockAttachmentHandler1.Verify(h => h.GetExtensionUris(), Times.Never); + mockAttachmentHandler2.Verify(h => h.GetExtensionUris(), Times.Never); + mockAttachmentHandler1.Verify(h => h.ProcessAttachmentSetsAsync(It.IsAny>(), It.IsAny>(), It.IsAny(), It.IsAny()), Times.Never); + mockAttachmentHandler2.Verify(h => h.ProcessAttachmentSetsAsync(It.IsAny>(), It.IsAny>(), It.IsAny(), It.IsAny()), Times.Never); + + VerifyMetrics(inputCount: 0, outputCount: 0); + } + + [TestMethod] + public async Task ProcessTestRunAttachmentsAsync_ShouldReturn1NotProcessedAttachmentThroughEventsHandler_If1NotRelatedAttachmentOnInput() + { + // arrange + List inputAttachments = new List + { + new AttachmentSet(new Uri(uri3), "uri3_input") + }; + + // act + await manager.ProcessTestRunAttachmentsAsync(mockRequestData.Object, inputAttachments, mockEventsHandler.Object, cancellationTokenSource.Token); + + // assert + VerifyCompleteEvent(false, false, inputAttachments[0]); + mockEventsHandler.Verify(h => h.HandleTestRunAttachmentsProcessingProgress(It.IsAny()), Times.Never); + mockEventsHandler.Verify(h => h.HandleLogMessage(It.IsAny(), It.IsAny()), Times.Never); + mockAttachmentHandler1.Verify(h => h.GetExtensionUris()); + mockAttachmentHandler2.Verify(h => h.GetExtensionUris()); + mockAttachmentHandler1.Verify(h => h.ProcessAttachmentSetsAsync(It.IsAny>(), It.IsAny>(), It.IsAny(), It.IsAny()), Times.Never); + mockAttachmentHandler2.Verify(h => h.ProcessAttachmentSetsAsync(It.IsAny>(), It.IsAny>(), It.IsAny(), It.IsAny()), Times.Never); + + VerifyMetrics(inputCount: 1, outputCount: 1); + } + + [TestMethod] + public async Task ProcessTestRunAttachmentsAsync_ShouldReturn1NotProcessedAttachment_If1NotRelatedAttachmentOnInput() + { + // arrange + List inputAttachments = new List + { + new AttachmentSet(new Uri(uri3), "uri3_input") + }; + + // act + var result = await manager.ProcessTestRunAttachmentsAsync(mockRequestData.Object, inputAttachments, cancellationTokenSource.Token); + + // assert + Assert.AreEqual(1, result.Count); + Assert.IsTrue(result.Contains(inputAttachments[0])); + mockAttachmentHandler1.Verify(h => h.GetExtensionUris()); + mockAttachmentHandler2.Verify(h => h.GetExtensionUris()); + mockAttachmentHandler1.Verify(h => h.ProcessAttachmentSetsAsync(It.IsAny>(), It.IsAny>(), It.IsAny(), It.IsAny()), Times.Never); + mockAttachmentHandler2.Verify(h => h.ProcessAttachmentSetsAsync(It.IsAny>(), It.IsAny>(), It.IsAny(), It.IsAny()), Times.Never); + + VerifyMetrics(inputCount: 1, outputCount: 1); + } + + [TestMethod] + public async Task ProcessTestRunAttachmentsAsync_ShouldReturn1ProcessedAttachmentThroughEventsHandler_IfRelatedAttachmentOnInput() + { + // arrange + List inputAttachments = new List + { + new AttachmentSet(new Uri(uri1), "uri1_input") + }; + + List outputAttachments = new List + { + new AttachmentSet(new Uri(uri1), "uri1_output") + }; + + mockAttachmentHandler1.Setup(h => h.ProcessAttachmentSetsAsync(It.IsAny>(), It.IsAny>(), It.IsAny(), It.IsAny())).ReturnsAsync(outputAttachments); + + // act + await manager.ProcessTestRunAttachmentsAsync(mockRequestData.Object, inputAttachments, mockEventsHandler.Object, cancellationTokenSource.Token); + + // assert + VerifyCompleteEvent(false, false, outputAttachments[0]); + mockEventsHandler.Verify(h => h.HandleTestRunAttachmentsProcessingProgress(It.IsAny()), Times.Never); + mockEventsHandler.Verify(h => h.HandleLogMessage(It.IsAny(), It.IsAny()), Times.Never); + mockAttachmentHandler1.Verify(h => h.GetExtensionUris()); + mockAttachmentHandler2.Verify(h => h.GetExtensionUris()); + mockAttachmentHandler1.Verify(h => h.ProcessAttachmentSetsAsync(It.Is>(c => c.Count == 1 && c.Contains(inputAttachments[0])), It.IsAny>(), It.IsAny(), cancellationTokenSource.Token)); + mockAttachmentHandler2.Verify(h => h.ProcessAttachmentSetsAsync(It.IsAny>(), It.IsAny>(), It.IsAny(), It.IsAny()), Times.Never); + + VerifyMetrics(inputCount: 1, outputCount: 1); + } + + [TestMethod] + public async Task ProcessTestRunAttachmentsAsync_ShouldReturn1ProcessedAttachment_IfRelatedAttachmentOnInput() + { + // arrange + List inputAttachments = new List + { + new AttachmentSet(new Uri(uri1), "uri1_input") + }; + + List outputAttachments = new List + { + new AttachmentSet(new Uri(uri1), "uri1_output") + }; + + mockAttachmentHandler1.Setup(h => h.ProcessAttachmentSetsAsync(It.IsAny>(), It.IsAny>(), It.IsAny(), It.IsAny())).ReturnsAsync(outputAttachments); + + // act + var result = await manager.ProcessTestRunAttachmentsAsync(mockRequestData.Object, inputAttachments, cancellationTokenSource.Token); + + // assert + Assert.AreEqual(1, result.Count); + Assert.IsTrue(result.Contains(outputAttachments[0])); + mockEventSource.Verify(s => s.TestRunAttachmentsProcessingStart(1)); + mockEventSource.Verify(s => s.TestRunAttachmentsProcessingStop(1)); + mockAttachmentHandler1.Verify(h => h.GetExtensionUris()); + mockAttachmentHandler2.Verify(h => h.GetExtensionUris()); + mockAttachmentHandler1.Verify(h => h.ProcessAttachmentSetsAsync(It.Is>(c => c.Count == 1 && c.Contains(inputAttachments[0])), It.IsAny>(), It.IsAny(), cancellationTokenSource.Token)); + mockAttachmentHandler2.Verify(h => h.ProcessAttachmentSetsAsync(It.IsAny>(), It.IsAny>(), It.IsAny(), It.IsAny()), Times.Never); + + VerifyMetrics(inputCount: 1, outputCount: 1); + } + + [TestMethod] + public async Task ProcessTestRunAttachmentsAsync_ShouldReturnInitialAttachmentsThroughEventsHandler_IfRelatedAttachmentOnInputButHandlerThrowsException() + { + // arrange + List inputAttachments = new List + { + new AttachmentSet(new Uri(uri1), "uri1_input") + }; + + mockAttachmentHandler1.Setup(h => h.ProcessAttachmentSetsAsync(It.IsAny>(), It.IsAny>(), It.IsAny(), It.IsAny())).Throws(new Exception("exception message")); + + // act + await manager.ProcessTestRunAttachmentsAsync(mockRequestData.Object, inputAttachments, mockEventsHandler.Object, cancellationTokenSource.Token); + + // assert + VerifyCompleteEvent(false, true, inputAttachments[0]); + mockEventsHandler.Verify(h => h.HandleTestRunAttachmentsProcessingProgress(It.IsAny()), Times.Never); + mockEventsHandler.Verify(h => h.HandleLogMessage(TestMessageLevel.Error, "exception message"), Times.Once); + mockAttachmentHandler1.Verify(h => h.GetExtensionUris()); + mockAttachmentHandler2.Verify(h => h.GetExtensionUris(), Times.Never); + mockAttachmentHandler1.Verify(h => h.ProcessAttachmentSetsAsync(It.Is>(c => c.Count == 1 && c.Contains(inputAttachments[0])), It.IsAny>(), It.IsAny(), cancellationTokenSource.Token)); + mockAttachmentHandler2.Verify(h => h.ProcessAttachmentSetsAsync(It.IsAny>(), It.IsAny>(), It.IsAny(), It.IsAny()), Times.Never); + + VerifyMetrics(inputCount: 1, outputCount: 1, status: "Failed"); + } + + [TestMethod] + public async Task ProcessTestRunAttachmentsAsync_ShouldReturnInitialAttachments_IfRelatedAttachmentOnInputButHandlerThrowsException() + { + // arrange + List inputAttachments = new List + { + new AttachmentSet(new Uri(uri1), "uri1_input") + }; + + mockAttachmentHandler1.Setup(h => h.ProcessAttachmentSetsAsync(It.IsAny>(), It.IsAny>(), It.IsAny(), It.IsAny())).Throws(new Exception("exception message")); + + // act + var result = await manager.ProcessTestRunAttachmentsAsync(mockRequestData.Object, inputAttachments, cancellationTokenSource.Token); + + // assert + Assert.AreEqual(1, result.Count); + Assert.IsTrue(result.Contains(inputAttachments[0])); + mockAttachmentHandler1.Verify(h => h.GetExtensionUris()); + mockAttachmentHandler2.Verify(h => h.GetExtensionUris(), Times.Never); + mockAttachmentHandler1.Verify(h => h.ProcessAttachmentSetsAsync(It.Is>(c => c.Count == 1 && c.Contains(inputAttachments[0])), It.IsAny>(), It.IsAny(), cancellationTokenSource.Token)); + mockAttachmentHandler2.Verify(h => h.ProcessAttachmentSetsAsync(It.IsAny>(), It.IsAny>(), It.IsAny(), It.IsAny()), Times.Never); + + VerifyMetrics(inputCount: 1, outputCount: 1, status: "Failed"); + } + + [TestMethod] + public async Task ProcessTestRunAttachmentsAsync_ShouldReturnInitialAttachmentsThroughEventsHandler_IfOperationIsCancelled() + { + // arrange + cancellationTokenSource.Cancel(); + List inputAttachments = new List + { + new AttachmentSet(new Uri(uri1), "uri1_input") + }; + + // act + await manager.ProcessTestRunAttachmentsAsync(mockRequestData.Object, inputAttachments, mockEventsHandler.Object, cancellationTokenSource.Token); + + // assert + VerifyCompleteEvent(true, false, inputAttachments[0]); + mockEventsHandler.Verify(h => h.HandleTestRunAttachmentsProcessingProgress(It.IsAny()), Times.Never); + mockAttachmentHandler1.Verify(h => h.GetExtensionUris(), Times.Never); + mockAttachmentHandler2.Verify(h => h.GetExtensionUris(), Times.Never); + mockAttachmentHandler1.Verify(h => h.ProcessAttachmentSetsAsync(It.IsAny>(), It.IsAny>(), It.IsAny(), It.IsAny()), Times.Never); + mockAttachmentHandler2.Verify(h => h.ProcessAttachmentSetsAsync(It.IsAny>(), It.IsAny>(), It.IsAny(), It.IsAny()), Times.Never); + + VerifyMetrics(inputCount: 1, outputCount: 1, status: "Canceled"); + } + + [TestMethod] + public async Task ProcessTestRunAttachmentsAsync_ShouldReturnInitialAttachments_IfOperationIsCancelled() + { + // arrange + cancellationTokenSource.Cancel(); + List inputAttachments = new List + { + new AttachmentSet(new Uri(uri1), "uri1_input") + }; + + // act + var result = await manager.ProcessTestRunAttachmentsAsync(mockRequestData.Object, inputAttachments, cancellationTokenSource.Token); + + // assert + Assert.AreEqual(1, result.Count); + Assert.IsTrue(result.Contains(inputAttachments[0])); + mockAttachmentHandler1.Verify(h => h.GetExtensionUris(), Times.Never); + mockAttachmentHandler2.Verify(h => h.GetExtensionUris(), Times.Never); + mockAttachmentHandler1.Verify(h => h.ProcessAttachmentSetsAsync(It.IsAny>(), It.IsAny>(), It.IsAny(), It.IsAny()), Times.Never); + mockAttachmentHandler2.Verify(h => h.ProcessAttachmentSetsAsync(It.IsAny>(), It.IsAny>(), It.IsAny(), It.IsAny()), Times.Never); + + VerifyMetrics(inputCount: 1, outputCount: 1, status: "Canceled"); + } + + [TestMethod] + public async Task ProcessTestRunAttachmentsAsync_ShouldReturnProcessedAttachmentsThroughEventsHandler_IfRelatedAttachmentsOnInput() + { + // arrange + List inputAttachments = new List + { + new AttachmentSet(new Uri(uri1), "uri1_input1"), + new AttachmentSet(new Uri(uri1), "uri1_input2"), + new AttachmentSet(new Uri(uri2), "uri2_input1"), + new AttachmentSet(new Uri(uri2), "uri2_input2"), + new AttachmentSet(new Uri(uri3), "uri3_input1"), + }; + + List outputAttachmentsForHandler1 = new List + { + new AttachmentSet(new Uri(uri1), "uri1_output") + }; + + List outputAttachmentsForHandler2 = new List + { + new AttachmentSet(new Uri(uri2), "uri2_output") + }; + + mockAttachmentHandler1.Setup(h => h.ProcessAttachmentSetsAsync(It.IsAny>(), It.IsAny>(), It.IsAny(), It.IsAny())).ReturnsAsync(outputAttachmentsForHandler1); + mockAttachmentHandler2.Setup(h => h.ProcessAttachmentSetsAsync(It.IsAny>(), It.IsAny>(), It.IsAny(), It.IsAny())).ReturnsAsync(outputAttachmentsForHandler2); + + // act + await manager.ProcessTestRunAttachmentsAsync(mockRequestData.Object, inputAttachments, mockEventsHandler.Object, cancellationTokenSource.Token); + + // assert + VerifyCompleteEvent(false, false, inputAttachments[4], outputAttachmentsForHandler1.First(), outputAttachmentsForHandler2.First()); + mockEventsHandler.Verify(h => h.HandleTestRunAttachmentsProcessingProgress(It.IsAny()), Times.Never); + mockEventsHandler.Verify(h => h.HandleLogMessage(It.IsAny(), It.IsAny()), Times.Never); + mockAttachmentHandler1.Verify(h => h.GetExtensionUris()); + mockAttachmentHandler2.Verify(h => h.GetExtensionUris()); + mockAttachmentHandler1.Verify(h => h.ProcessAttachmentSetsAsync(It.Is>(c => c.Count == 2 && c.Contains(inputAttachments[0]) && c.Contains(inputAttachments[1])), It.IsAny>(), It.IsAny(), cancellationTokenSource.Token)); + mockAttachmentHandler2.Verify(h => h.ProcessAttachmentSetsAsync(It.Is>(c => c.Count == 2 && c.Contains(inputAttachments[2]) && c.Contains(inputAttachments[3])), It.IsAny>(), It.IsAny(), cancellationTokenSource.Token)); + + VerifyMetrics(inputCount: 5, outputCount: 3); + } + + [TestMethod] + public async Task ProcessTestRunAttachmentsAsync_ShouldReturnProcessedAttachments_IfRelatedAttachmentsOnInput() + { + // arrange + List inputAttachments = new List + { + new AttachmentSet(new Uri(uri1), "uri1_input1"), + new AttachmentSet(new Uri(uri1), "uri1_input2"), + new AttachmentSet(new Uri(uri2), "uri2_input1"), + new AttachmentSet(new Uri(uri2), "uri2_input2"), + new AttachmentSet(new Uri(uri3), "uri3_input1"), + }; + + List outputAttachmentsForHandler1 = new List + { + new AttachmentSet(new Uri(uri1), "uri1_output") + }; + + List outputAttachmentsForHandler2 = new List + { + new AttachmentSet(new Uri(uri2), "uri2_output") + }; + + mockAttachmentHandler1.Setup(h => h.ProcessAttachmentSetsAsync(It.IsAny>(), It.IsAny>(), It.IsAny(), It.IsAny())).ReturnsAsync(outputAttachmentsForHandler1); + mockAttachmentHandler2.Setup(h => h.ProcessAttachmentSetsAsync(It.IsAny>(), It.IsAny>(), It.IsAny(), It.IsAny())).ReturnsAsync(outputAttachmentsForHandler2); + + // act + var result = await manager.ProcessTestRunAttachmentsAsync(mockRequestData.Object, inputAttachments, cancellationTokenSource.Token); + + // assert + Assert.AreEqual(3, result.Count); + Assert.IsTrue(result.Contains(inputAttachments[4])); + Assert.IsTrue(result.Contains(outputAttachmentsForHandler1[0])); + Assert.IsTrue(result.Contains(outputAttachmentsForHandler2[0])); + mockAttachmentHandler1.Verify(h => h.GetExtensionUris()); + mockAttachmentHandler2.Verify(h => h.GetExtensionUris()); + mockAttachmentHandler1.Verify(h => h.ProcessAttachmentSetsAsync(It.Is>(c => c.Count == 2 && c.Contains(inputAttachments[0]) && c.Contains(inputAttachments[1])), It.IsAny>(), It.IsAny(), cancellationTokenSource.Token)); + mockAttachmentHandler2.Verify(h => h.ProcessAttachmentSetsAsync(It.Is>(c => c.Count == 2 && c.Contains(inputAttachments[2]) && c.Contains(inputAttachments[3])), It.IsAny>(), It.IsAny(), cancellationTokenSource.Token)); + + VerifyMetrics(inputCount: 5, outputCount: 3); + } + + [TestMethod] + public async Task ProcessTestRunAttachmentsAsync_ShouldReturnInitialAttachmentsThroughEventsHandler_IfOperationCancelled() + { + // arrange + List inputAttachments = new List + { + new AttachmentSet(new Uri(uri1), "uri1_input") + }; + + ICollection outputAttachments = new List + { + new AttachmentSet(new Uri(uri1), "uri1_output") + }; + + var innerTaskCompletionSource = new TaskCompletionSource(); + + mockAttachmentHandler1.Setup(h => h.ProcessAttachmentSetsAsync(It.IsAny>(), It.IsAny>(), It.IsAny(), It.IsAny())).Returns((ICollection i1, IProgress progress, IMessageLogger logger, CancellationToken cancellation) => + { + try + { + for (int i = 0; i < 100; ++i) + { + Task.Delay(100).Wait(); + Console.WriteLine($"Iteration: {i}"); + logger.SendMessage(TestMessageLevel.Informational, $"Iteration: {i}"); + + cancellation.ThrowIfCancellationRequested(); + progress.Report(i + 1); + + if (i == 3) + { + cancellationTokenSource.Cancel(); + Task.Delay(500).Wait(); + } + } + } + finally + { + innerTaskCompletionSource.TrySetResult(null); + } + + return Task.FromResult(outputAttachments); + }); + + // act + await manager.ProcessTestRunAttachmentsAsync(mockRequestData.Object, inputAttachments, mockEventsHandler.Object, cancellationTokenSource.Token); + Console.WriteLine("Attachments processing done"); + await innerTaskCompletionSource.Task; + + // assert + VerifyCompleteEvent(true, false, inputAttachments[0]); + mockEventsHandler.Verify(h => h.HandleTestRunAttachmentsProcessingProgress(It.IsAny()), Times.Exactly(4)); + mockEventsHandler.Verify(h => h.HandleTestRunAttachmentsProcessingProgress(It.Is(a => VerifyProgressArgs(a, 1)))); + mockEventsHandler.Verify(h => h.HandleTestRunAttachmentsProcessingProgress(It.Is(a => VerifyProgressArgs(a, 2)))); + mockEventsHandler.Verify(h => h.HandleTestRunAttachmentsProcessingProgress(It.Is(a => VerifyProgressArgs(a, 3)))); + mockEventsHandler.Verify(h => h.HandleTestRunAttachmentsProcessingProgress(It.Is(a => VerifyProgressArgs(a, 4)))); + mockEventsHandler.Verify(h => h.HandleLogMessage(TestMessageLevel.Informational, "Attachments processing was cancelled.")); + mockAttachmentHandler1.Verify(h => h.GetExtensionUris()); + mockAttachmentHandler2.Verify(h => h.GetExtensionUris(), Times.Never); + mockAttachmentHandler1.Verify(h => h.ProcessAttachmentSetsAsync(It.Is>(c => c.Count == 1 && c.Contains(inputAttachments[0])), It.IsAny>(), It.IsAny(), cancellationTokenSource.Token)); + mockAttachmentHandler2.Verify(h => h.ProcessAttachmentSetsAsync(It.IsAny>(), It.IsAny>(), It.IsAny(), It.IsAny()), Times.Never); + + VerifyMetrics(inputCount: 1, outputCount: 1, status: "Canceled"); + } + + [TestMethod] + public async Task ProcessTestRunAttachmentsAsync_ShouldReturnInitialAttachments_IfOperationCancelled() + { + // arrange + List inputAttachments = new List + { + new AttachmentSet(new Uri(uri1), "uri1_input") + }; + + ICollection outputAttachments = new List + { + new AttachmentSet(new Uri(uri1), "uri1_output") + }; + + var innerTaskCompletionSource = new TaskCompletionSource(); + + mockAttachmentHandler1.Setup(h => h.ProcessAttachmentSetsAsync(It.IsAny>(), It.IsAny>(), It.IsAny(), It.IsAny())).Returns((ICollection i1, IProgress p, IMessageLogger logger, CancellationToken cancellation) => + { + try + { + for (int i = 0; i < 1000; ++i) + { + Task.Delay(100).Wait(); + Console.WriteLine($"Iteration: {i}"); + logger.SendMessage(TestMessageLevel.Informational, $"Iteration: {i}"); + + cancellation.ThrowIfCancellationRequested(); + + if (i == 3) + { + cancellationTokenSource.Cancel(); + Task.Delay(500).Wait(); + } + } + } + finally + { + innerTaskCompletionSource.TrySetResult(null); + } + + return Task.FromResult(outputAttachments); + }); + + // act + var result = await manager.ProcessTestRunAttachmentsAsync(mockRequestData.Object, inputAttachments, cancellationTokenSource.Token); + Console.WriteLine("Attachments processing done"); + await innerTaskCompletionSource.Task; + + // assert + Assert.IsNotNull(result); + Assert.AreEqual(1, result.Count); + Assert.IsTrue(result.Contains(inputAttachments[0])); + mockAttachmentHandler1.Verify(h => h.GetExtensionUris()); + mockAttachmentHandler2.Verify(h => h.GetExtensionUris(), Times.Never); + mockAttachmentHandler1.Verify(h => h.ProcessAttachmentSetsAsync(It.Is>(c => c.Count == 1 && c.Contains(inputAttachments[0])), It.IsAny>(), It.IsAny(), cancellationTokenSource.Token)); + mockAttachmentHandler2.Verify(h => h.ProcessAttachmentSetsAsync(It.IsAny>(), It.IsAny>(), It.IsAny(), It.IsAny()), Times.Never); + + VerifyMetrics(inputCount: 1, outputCount: 1, status: "Canceled"); + } + + [TestMethod] + public async Task ProcessTestRunAttachmentsAsync_ShouldReturnProperlySendProgressEvents_IfHandlersPropagesEvents() + { + // arrange + List inputAttachments = new List + { + new AttachmentSet(new Uri(uri1), "uri1_input"), + new AttachmentSet(new Uri(uri2), "uri2_input") + }; + + ICollection outputAttachments1 = new List + { + new AttachmentSet(new Uri(uri1), "uri1_output") + }; + + ICollection outputAttachments2 = new List + { + new AttachmentSet(new Uri(uri2), "uri2_output") + }; + + var innerTaskCompletionSource = new TaskCompletionSource(); + + int counter = 0; + mockEventsHandler.Setup(h => h.HandleTestRunAttachmentsProcessingProgress(It.IsAny())).Callback(() => + { + counter++; + if(counter == 6) + { + innerTaskCompletionSource.TrySetResult(null); + } + }); + + mockAttachmentHandler1.Setup(h => h.ProcessAttachmentSetsAsync(It.IsAny>(), It.IsAny>(), It.IsAny(), It.IsAny())).Returns((ICollection i1, IProgress progress, IMessageLogger logger, CancellationToken cancellation) => + { + progress.Report(25); + progress.Report(50); + progress.Report(75); + logger.SendMessage(TestMessageLevel.Error, "error"); + progress.Report(100); + return Task.FromResult(outputAttachments1); + }); + + mockAttachmentHandler2.Setup(h => h.ProcessAttachmentSetsAsync(It.IsAny>(), It.IsAny>(), It.IsAny(), It.IsAny())).Returns((ICollection i1, IProgress progress, IMessageLogger logger, CancellationToken cancellation) => + { + progress.Report(50); + logger.SendMessage(TestMessageLevel.Informational, "info"); + progress.Report(100); + return Task.FromResult(outputAttachments2); + }); + + // act + await manager.ProcessTestRunAttachmentsAsync(mockRequestData.Object, inputAttachments, mockEventsHandler.Object, CancellationToken.None); + + // assert + await innerTaskCompletionSource.Task; + VerifyCompleteEvent(false, false, outputAttachments1.First(), outputAttachments2.First()); + mockEventsHandler.Verify(h => h.HandleTestRunAttachmentsProcessingProgress(It.IsAny()), Times.Exactly(6)); + mockEventsHandler.Verify(h => h.HandleTestRunAttachmentsProcessingProgress(It.Is(a => VerifyProgressArgsForTwoHandlers(a, 1, 25, uri1)))); + mockEventsHandler.Verify(h => h.HandleTestRunAttachmentsProcessingProgress(It.Is(a => VerifyProgressArgsForTwoHandlers(a, 1, 50, uri1)))); + mockEventsHandler.Verify(h => h.HandleTestRunAttachmentsProcessingProgress(It.Is(a => VerifyProgressArgsForTwoHandlers(a, 1, 75, uri1)))); + mockEventsHandler.Verify(h => h.HandleTestRunAttachmentsProcessingProgress(It.Is(a => VerifyProgressArgsForTwoHandlers(a, 1, 100, uri1)))); + mockEventsHandler.Verify(h => h.HandleTestRunAttachmentsProcessingProgress(It.Is(a => VerifyProgressArgsForTwoHandlers(a, 2, 50, uri2)))); + mockEventsHandler.Verify(h => h.HandleTestRunAttachmentsProcessingProgress(It.Is(a => VerifyProgressArgsForTwoHandlers(a, 2, 100, uri2)))); + mockAttachmentHandler1.Verify(h => h.GetExtensionUris()); + mockAttachmentHandler2.Verify(h => h.GetExtensionUris()); + mockAttachmentHandler1.Verify(h => h.ProcessAttachmentSetsAsync(It.Is>(c => c.Count == 1 && c.Contains(inputAttachments[0])), It.IsAny>(), It.IsAny(), CancellationToken.None)); + mockAttachmentHandler2.Verify(h => h.ProcessAttachmentSetsAsync(It.Is>(c => c.Count == 1 && c.Contains(inputAttachments[1])), It.IsAny>(), It.IsAny(), CancellationToken.None)); + + mockEventsHandler.Verify(h => h.HandleLogMessage(TestMessageLevel.Informational, "info")); + mockEventsHandler.Verify(h => h.HandleLogMessage(TestMessageLevel.Error, "error")); + + VerifyMetrics(inputCount: 2, outputCount: 2); + } + + private void VerifyMetrics(int inputCount, int outputCount, string status = "Completed") + { + mockEventSource.Verify(s => s.TestRunAttachmentsProcessingStart(inputCount)); + mockEventSource.Verify(s => s.TestRunAttachmentsProcessingStop(outputCount)); + + mockMetricsCollection.Verify(m => m.Add(TelemetryDataConstants.NumberOfAttachmentsSentForProcessing, inputCount)); + mockMetricsCollection.Verify(m => m.Add(TelemetryDataConstants.NumberOfAttachmentsAfterProcessing, outputCount)); + mockMetricsCollection.Verify(m => m.Add(TelemetryDataConstants.AttachmentsProcessingState, status)); + mockMetricsCollection.Verify(m => m.Add(TelemetryDataConstants.TimeTakenInSecForAttachmentsProcessing, It.IsAny())); + } + + private void VerifyCompleteEvent(bool isCanceled, bool containsError, params AttachmentSet[] expectedSets) + { + mockEventsHandler.Verify(h => h.HandleTestRunAttachmentsProcessingComplete( + It.Is(a => a.IsCanceled == isCanceled && (a.Error != null) == containsError), + It.Is>(c => c.Count == expectedSets.Length && expectedSets.All(e => c.Contains(e))))); + } + + private bool VerifyProgressArgs(TestRunAttachmentsProcessingProgressEventArgs args, int progress) + { + Assert.AreEqual(1, args.CurrentAttachmentProcessorIndex); + Assert.AreEqual(2, args.AttachmentProcessorsCount); + Assert.AreEqual(1, args.CurrentAttachmentProcessorUris.Count); + Assert.AreEqual(uri1, args.CurrentAttachmentProcessorUris.First().AbsoluteUri); + return progress == args.CurrentAttachmentProcessorProgress; + } + + private bool VerifyProgressArgsForTwoHandlers(TestRunAttachmentsProcessingProgressEventArgs args, long handlerIndex, long progress, string uri) + { + return progress == args.CurrentAttachmentProcessorProgress && + args.CurrentAttachmentProcessorIndex == handlerIndex && + args.CurrentAttachmentProcessorUris.First().AbsoluteUri == uri && + args.AttachmentProcessorsCount == 2; + } + } +} diff --git a/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Client/Parallel/ParallelProxyExecutionManagerTests.cs b/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Client/Parallel/ParallelProxyExecutionManagerTests.cs index 9a7a439a80..78b7376bcf 100644 --- a/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Client/Parallel/ParallelProxyExecutionManagerTests.cs +++ b/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Client/Parallel/ParallelProxyExecutionManagerTests.cs @@ -15,6 +15,7 @@ namespace TestPlatform.CrossPlatEngine.UnitTests.Client using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.ObjectModel; using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Client; using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Client.Parallel; + using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.DataCollection; using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.DataCollection.Interfaces; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; @@ -171,6 +172,32 @@ public void HandlePartialRunCompleteShouldCreateNewProxyExecutionManagerIfDataCo Assert.IsTrue(this.proxyManagerFuncCalled); } + [TestMethod] + public void HandlePartialRunCompleteShouldCreateNewProxyExecutionManagerIfDataCollectionEnabledAndCreatorWithDataCollection() + { + var completeArgs = new TestRunCompleteEventArgs(null, true, true, null, null, TimeSpan.Zero); + this.mockTestHostManager = new Mock(); + this.mockRequestSender = new Mock(); + this.mockDataCollectionManager = new Mock(); + var proxyDataCollectionManager = new ProxyExecutionManagerWithDataCollection(this.mockRequestData.Object, this.mockRequestSender.Object, this.mockTestHostManager.Object, this.mockDataCollectionManager.Object); + var managers = new List>(); + this.proxyManagerFunc = () => + { + this.proxyManagerFuncCalled = true; + var manager = new Mock(this.mockRequestData.Object, this.mockRequestSender.Object, this.mockTestHostManager.Object, this.mockDataCollectionManager.Object); + managers.Add(manager); + return manager.Object; + }; + var parallelExecutionManager = this.SetupExecutionManager(this.proxyManagerFunc, 2, setupTestCases: true); + + this.proxyManagerFuncCalled = false; + parallelExecutionManager.HandlePartialRunComplete(proxyDataCollectionManager, completeArgs, null, null, null); + Assert.IsTrue(this.proxyManagerFuncCalled); + + var handler = parallelExecutionManager.GetHandlerForGivenManager(managers.Last().Object); + Assert.IsTrue(handler is ParallelDataCollectionEventsHandler); + } + [TestMethod] public void HandlePartialRunCompleteShouldCreateNewProxyExecutionManagerIfIsAbortedIsTrue() { diff --git a/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/DataCollection/ParallelDataCollectionEventsHandlerTests.cs b/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/DataCollection/ParallelDataCollectionEventsHandlerTests.cs new file mode 100644 index 0000000000..6823e8efc1 --- /dev/null +++ b/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/DataCollection/ParallelDataCollectionEventsHandlerTests.cs @@ -0,0 +1,95 @@ +// 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.TestPlatform.CrossPlatEngine.UnitTests.DataCollection +{ + using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Client.Parallel; + using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.DataCollection; + using Microsoft.VisualStudio.TestPlatform.ObjectModel; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using System; + using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Threading; + using System.Threading.Tasks; + + [TestClass] + public class ParallelDataCollectionEventsHandlerTests + { + private const string uri1 = "datacollector://microsoft/some1/1.0"; + private const string uri2 = "datacollector://microsoft/some2/2.0"; + private const string uri3 = "datacollector://microsoft/some3/2.0"; + + private readonly Mock mockRequestData; + private readonly Mock mockProxyExecutionManager; + private readonly Mock mockTestRunEventsHandler; + private readonly Mock mockParallelProxyExecutionManager; + private readonly Mock mockTestRunAttachmentsProcessingManager; + private readonly CancellationTokenSource cancellationTokenSource; + private readonly ParallelDataCollectionEventsHandler parallelDataCollectionEventsHandler; + + public ParallelDataCollectionEventsHandlerTests() + { + mockRequestData = new Mock(); + mockProxyExecutionManager = new Mock(); + mockTestRunEventsHandler = new Mock(); + mockParallelProxyExecutionManager = new Mock(); + mockTestRunAttachmentsProcessingManager = new Mock(); + cancellationTokenSource = new CancellationTokenSource(); + parallelDataCollectionEventsHandler = new ParallelDataCollectionEventsHandler(mockRequestData.Object, mockProxyExecutionManager.Object, mockTestRunEventsHandler.Object, + mockParallelProxyExecutionManager.Object, new ParallelRunDataAggregator(), mockTestRunAttachmentsProcessingManager.Object, cancellationTokenSource.Token); + + mockParallelProxyExecutionManager.Setup(m => m.HandlePartialRunComplete(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>(), It.IsAny>())).Returns(true); + } + + [TestMethod] + public void HandleTestRunComplete_ShouldCallProcessTestRunAttachmentsAsyncWithAttachmentsAndUseResults() + { + // arrange + List inputAttachments = new List + { + new AttachmentSet(new Uri(uri1), "uri1_input1"), + new AttachmentSet(new Uri(uri2), "uri2_input1"), + new AttachmentSet(new Uri(uri3), "uri3_input1") + }; + + Collection outputAttachments = new Collection + { + new AttachmentSet(new Uri(uri1), "uri1_input1") + }; + + mockTestRunAttachmentsProcessingManager.Setup(f => f.ProcessTestRunAttachmentsAsync(mockRequestData.Object, It.IsAny>(), It.IsAny())).Returns(Task.FromResult(outputAttachments)); + + // act + parallelDataCollectionEventsHandler.HandleTestRunComplete(new TestRunCompleteEventArgs(null, false, false, null, null, TimeSpan.FromSeconds(1)), null, inputAttachments, null); + + // assert + mockTestRunEventsHandler.Verify(h => h.HandleTestRunComplete(It.IsAny(), It.IsAny(), It.Is>(c => c.Count == 1 && c.Contains(outputAttachments[0])), It.IsAny>())); + mockTestRunAttachmentsProcessingManager.Verify(f => f.ProcessTestRunAttachmentsAsync(mockRequestData.Object, It.Is>(a => a.Count == 3), cancellationTokenSource.Token)); + } + + [TestMethod] + public void HandleTestRunComplete_ShouldCallProcessTestRunAttachmentsAsyncWithAttachmentsAndNotUserResults_IfManagerReturnsNull() + { + // arrange + List inputAttachments = new List + { + new AttachmentSet(new Uri(uri1), "uri1_input1"), + new AttachmentSet(new Uri(uri2), "uri2_input1"), + new AttachmentSet(new Uri(uri3), "uri3_input1") + }; + + mockTestRunAttachmentsProcessingManager.Setup(f => f.ProcessTestRunAttachmentsAsync(mockRequestData.Object, It.IsAny>(), It.IsAny())).Returns(Task.FromResult((Collection)null)); + + // act + parallelDataCollectionEventsHandler.HandleTestRunComplete(new TestRunCompleteEventArgs(null, false, false, null, null, TimeSpan.FromSeconds(1)), null, inputAttachments, null); + + // assert + mockTestRunEventsHandler.Verify(h => h.HandleTestRunComplete(It.IsAny(), It.IsAny(), It.Is>(c => c.Count == 3), It.IsAny>())); + mockTestRunAttachmentsProcessingManager.Verify(f => f.ProcessTestRunAttachmentsAsync(mockRequestData.Object, It.Is>(a => a.Count == 3), cancellationTokenSource.Token)); + } + } +} diff --git a/test/Microsoft.TestPlatform.Extensions.TrxLogger.UnitTests/Utility/ConverterTests.cs b/test/Microsoft.TestPlatform.Extensions.TrxLogger.UnitTests/Utility/ConverterTests.cs index 8d1e341c9e..1734b9c15d 100644 --- a/test/Microsoft.TestPlatform.Extensions.TrxLogger.UnitTests/Utility/ConverterTests.cs +++ b/test/Microsoft.TestPlatform.Extensions.TrxLogger.UnitTests/Utility/ConverterTests.cs @@ -145,7 +145,7 @@ public void ToTestElementShouldContainExpectedTestMethodPropertiesIfFqnDoesNotEn } [TestMethod] - public void ToResultFilesShouldAddAttachementsWithRelativeURI() + public void ToResultFilesShouldAddAttachmentsWithRelativeURI() { UriDataAttachment uriDataAttachment1 = new UriDataAttachment(new Uri($"/mnt/c/abc.txt", UriKind.Relative), "Description 1"); diff --git a/test/Microsoft.TestPlatform.Utilities.UnitTests/CodeCoverageDataAttachmentsHandlerTests.cs b/test/Microsoft.TestPlatform.Utilities.UnitTests/CodeCoverageDataAttachmentsHandlerTests.cs index 7d63ed0a18..ff89d295bd 100644 --- a/test/Microsoft.TestPlatform.Utilities.UnitTests/CodeCoverageDataAttachmentsHandlerTests.cs +++ b/test/Microsoft.TestPlatform.Utilities.UnitTests/CodeCoverageDataAttachmentsHandlerTests.cs @@ -1,50 +1,108 @@ -using System.Collections.Generic; - -namespace Microsoft.TestPlatform.Utilities.UnitTests +namespace Microsoft.TestPlatform.Utilities.UnitTests { + using Moq; + using System; + using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Linq; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.Utilities; using Microsoft.VisualStudio.TestTools.UnitTesting; - using System; - using System.Collections.ObjectModel; [TestClass] public class CodeCoverageDataAttachmentsHandlerTests { - private CodeCoverageDataAttachmentsHandler coverageDataAttachmentsHandler; + private readonly Mock> mockProgressReporter; + private readonly CodeCoverageDataAttachmentsHandler coverageDataAttachmentsHandler; public CodeCoverageDataAttachmentsHandlerTests() { + mockProgressReporter = new Mock>(); coverageDataAttachmentsHandler = new CodeCoverageDataAttachmentsHandler(); } [TestMethod] - public void HandleDataCollectionAttachmentSetsShouldReturnEmptySetWhenNoAttachmentsOrAttachmentsAreNull() + public async Task HandleDataCollectionAttachmentSetsShouldReturnEmptySetWhenNoAttachmentsOrAttachmentsAreNull() { Collection attachment = new Collection(); - ICollection resultAttachmentSets = - coverageDataAttachmentsHandler.HandleDataCollectionAttachmentSets(attachment); + ICollection resultAttachmentSets = await + coverageDataAttachmentsHandler.ProcessAttachmentSetsAsync(attachment, mockProgressReporter.Object, null, CancellationToken.None); Assert.IsNotNull(resultAttachmentSets); Assert.IsTrue(resultAttachmentSets.Count == 0); - resultAttachmentSets = coverageDataAttachmentsHandler.HandleDataCollectionAttachmentSets(null); + resultAttachmentSets = await coverageDataAttachmentsHandler.ProcessAttachmentSetsAsync(null, mockProgressReporter.Object, null, CancellationToken.None); Assert.IsNotNull(resultAttachmentSets); Assert.IsTrue(resultAttachmentSets.Count == 0); + + mockProgressReporter.Verify(p => p.Report(It.IsAny()), Times.Never); } [TestMethod] - public void HandleDataCollectionAttachmentSetsShouldReturnEmptySetWhenNoCodeCoverageAttachments() + public async Task HandleDataCollectionAttachmentSetsShouldReturnInputIfOnly1Attachment() { - Collection attachment = new Collection(); var attachmentSet = new AttachmentSet(new Uri("//badrui//"), string.Empty); + attachmentSet.Attachments.Add(new UriDataAttachment(new Uri("C:\\temp\\aa"), "coverage")); - ICollection resultAttachmentSets = - coverageDataAttachmentsHandler.HandleDataCollectionAttachmentSets(attachment); + Collection attachment = new Collection { attachmentSet }; + ICollection resultAttachmentSets = await + coverageDataAttachmentsHandler.ProcessAttachmentSetsAsync(attachment, mockProgressReporter.Object, null, CancellationToken.None); Assert.IsNotNull(resultAttachmentSets); - Assert.IsTrue(resultAttachmentSets.Count == 0); + Assert.IsTrue(resultAttachmentSets.Count == 1); + Assert.AreEqual("datacollector://microsoft/CodeCoverage/2.0", resultAttachmentSets.First().Uri.AbsoluteUri); + Assert.AreEqual("file:///C:/temp/aa", resultAttachmentSets.First().Attachments.First().Uri.AbsoluteUri); + } + + [TestMethod] + public async Task HandleDataCollectionAttachmentSetsShouldThrowIfCancellationRequested() + { + var attachmentSet = new AttachmentSet(new Uri("//badrui//"), string.Empty); + attachmentSet.Attachments.Add(new UriDataAttachment(new Uri("C:\\temp\\aa"), "coverage")); + CancellationTokenSource cts = new CancellationTokenSource(); + cts.Cancel(); + + Collection attachment = new Collection + { + attachmentSet, + attachmentSet + }; + + await Assert.ThrowsExceptionAsync(async () => await coverageDataAttachmentsHandler.ProcessAttachmentSetsAsync(attachment, mockProgressReporter.Object, null, cts.Token)); + + Assert.AreEqual(2, attachment.Count); + + mockProgressReporter.Verify(p => p.Report(It.IsAny()), Times.Never); + } + + [TestMethod] + public async Task HandleDataCollectionAttachmentSetsShouldReturnExistingAttachmentsIfFailedToLoadLibrary() + { + var attachmentSet1 = new AttachmentSet(new Uri("//badrui//"), string.Empty); + attachmentSet1.Attachments.Add(new UriDataAttachment(new Uri("C:\\temp\\aa"), "coverage")); + + var attachmentSet2 = new AttachmentSet(new Uri("//badruj//"), string.Empty); + attachmentSet2.Attachments.Add(new UriDataAttachment(new Uri("C:\\temp\\ab"), "coverage")); + + CancellationTokenSource cts = new CancellationTokenSource(); + + Collection attachment = new Collection + { + attachmentSet1, + attachmentSet2 + }; + + var result = await coverageDataAttachmentsHandler.ProcessAttachmentSetsAsync(attachment, mockProgressReporter.Object, null, cts.Token); + + Assert.AreEqual(2, result.Count); + Assert.IsTrue(result.Contains(attachmentSet1)); + Assert.IsTrue(result.Contains(attachmentSet2)); + + mockProgressReporter.Verify(p => p.Report(It.IsAny()), Times.Never); } } } diff --git a/test/TranslationLayer.UnitTests/VsTestConsoleRequestSenderTests.cs b/test/TranslationLayer.UnitTests/VsTestConsoleRequestSenderTests.cs index 457856254d..add71d422e 100644 --- a/test/TranslationLayer.UnitTests/VsTestConsoleRequestSenderTests.cs +++ b/test/TranslationLayer.UnitTests/VsTestConsoleRequestSenderTests.cs @@ -33,8 +33,6 @@ public class VsTestConsoleRequestSenderTests { private readonly ITranslationLayerRequestSender requestSender; - private readonly ITranslationLayerRequestSenderAsync requestSenderAsync; - private readonly Mock mockCommunicationManager; private readonly int WaitTimeout = 2000; @@ -49,10 +47,6 @@ public VsTestConsoleRequestSenderTests() this.mockCommunicationManager.Object, JsonDataSerializer.Instance, new Mock().Object); - this.requestSenderAsync = new VsTestConsoleRequestSender( - this.mockCommunicationManager.Object, - JsonDataSerializer.Instance, - new Mock().Object); } #region Communication Tests @@ -104,7 +98,7 @@ public async Task InitializeCommunicationAsyncShouldReturnInvalidPortNumberIfHos this.mockCommunicationManager.Setup(cm => cm.HostServer(new IPEndPoint(IPAddress.Loopback, 0))).Throws(new Exception("Fail")); this.mockCommunicationManager.Setup(cm => cm.AcceptClientAsync()).Returns(Task.FromResult(false)); - var portOutput = await this.requestSenderAsync.InitializeCommunicationAsync(this.WaitTimeout); + var portOutput = await this.requestSender.InitializeCommunicationAsync(this.WaitTimeout); Assert.IsTrue(portOutput < 0, "Negative port number must be returned if Hosting Server fails."); this.mockCommunicationManager.Verify(cm => cm.HostServer(new IPEndPoint(IPAddress.Loopback, 0)), Times.Once); @@ -143,7 +137,7 @@ public async Task InitializeCommunicationAsyncShouldFailConnectionIfMessageRecei this.mockCommunicationManager.Setup(cm => cm.AcceptClientAsync()).Returns(Task.FromResult(false)).Callback(() => { }); this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).ThrowsAsync(new Exception("Fail")); - var portOutput = await this.requestSenderAsync.InitializeCommunicationAsync(this.WaitTimeout); + var portOutput = await this.requestSender.InitializeCommunicationAsync(this.WaitTimeout); // Connection must not succeed as handshake failed Assert.AreEqual(-1, portOutput, "Connection must fail if handshake failed."); @@ -189,7 +183,7 @@ public async Task InitializeCommunicationAsyncShouldFailConnectionIfSessionConne this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(discoveryMessage)); - var portOutput = await this.requestSenderAsync.InitializeCommunicationAsync(this.WaitTimeout); + var portOutput = await this.requestSender.InitializeCommunicationAsync(this.WaitTimeout); Assert.AreEqual(-1, portOutput, "Connection must fail if version check failed."); this.mockCommunicationManager.Verify(cm => cm.HostServer(new IPEndPoint(IPAddress.Loopback, 0)), Times.Once); @@ -239,7 +233,7 @@ public async Task InitializeCommunicationAsyncShouldFailConnectionIfSendMessageF this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(sessionConnected)); this.mockCommunicationManager.Setup(cm => cm.SendMessage(MessageType.VersionCheck, this.protocolVersion)).Throws(new Exception("Fail")); - var portOutput = await this.requestSenderAsync.InitializeCommunicationAsync(this.WaitTimeout); + var portOutput = await this.requestSender.InitializeCommunicationAsync(this.WaitTimeout); Assert.AreEqual(-1, portOutput, "Connection must fail if version check failed."); this.mockCommunicationManager.Verify(cm => cm.HostServer(new IPEndPoint(IPAddress.Loopback, 0)), Times.Once); @@ -310,7 +304,7 @@ public async Task InitializeCommunicationAsyncShouldFailConnectionIfProtocolIsNo this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(sessionConnected)); this.mockCommunicationManager.Setup(cm => cm.SendMessage(MessageType.VersionCheck)).Callback(changedMessage); - var portOutput = await this.requestSenderAsync.InitializeCommunicationAsync(this.WaitTimeout); + var portOutput = await this.requestSender.InitializeCommunicationAsync(this.WaitTimeout); Assert.AreEqual(-1, portOutput, "Connection must fail if version check failed."); this.mockCommunicationManager.Verify(cm => cm.HostServer(new IPEndPoint(IPAddress.Loopback, 0)), Times.Once); @@ -361,7 +355,7 @@ public async Task DiscoverTestsAsyncShouldCompleteWithZeroTests() }; this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(discoveryComplete)); - await this.requestSenderAsync.DiscoverTestsAsync(new List() { "1.dll" }, null, null, mockHandler.Object); + await this.requestSender.DiscoverTestsAsync(new List() { "1.dll" }, null, null, mockHandler.Object); mockHandler.Verify(mh => mh.HandleDiscoveryComplete(It.IsAny(), null), Times.Once, "Discovery Complete must be called"); mockHandler.Verify(mh => mh.HandleDiscoveredTests(It.IsAny>()), Times.Never, "DiscoveredTests must not be called"); @@ -427,7 +421,7 @@ public async Task DiscoverTestsAsyncShouldCompleteWithSingleTest() mockHandler.Setup(mh => mh.HandleDiscoveredTests(It.IsAny>())).Callback( () => this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(discoveryComplete))); - await this.requestSenderAsync.DiscoverTestsAsync(new List() { "1.dll" }, null, new TestPlatformOptions(), mockHandler.Object); + await this.requestSender.DiscoverTestsAsync(new List() { "1.dll" }, null, new TestPlatformOptions(), mockHandler.Object); mockHandler.Verify(mh => mh.HandleDiscoveryComplete(It.IsAny(), null), Times.Once, "Discovery Complete must be called"); mockHandler.Verify(mh => mh.HandleDiscoveredTests(It.IsAny>()), Times.Once, "DiscoveredTests must be called"); @@ -498,7 +492,7 @@ public async Task DiscoverTestsAsyncShouldReportBackTestsWithTraitsInTestsFoundM this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult((discoveryComplete))); }); - await this.requestSenderAsync.DiscoverTestsAsync(new List() { "1.dll" }, null, new TestPlatformOptions(), mockHandler.Object); + await this.requestSender.DiscoverTestsAsync(new List() { "1.dll" }, null, new TestPlatformOptions(), mockHandler.Object); Assert.IsNotNull(receivedTestCases); Assert.AreEqual(1, receivedTestCases.Count); @@ -570,7 +564,7 @@ public async Task DiscoverTestsAsyncShouldReportBackTestsWithTraitsInDiscoveryCo receivedTestCases = tests?.ToList(); }); - await this.requestSenderAsync.DiscoverTestsAsync(new List() { "1.dll" }, null, new TestPlatformOptions(), mockHandler.Object); + await this.requestSender.DiscoverTestsAsync(new List() { "1.dll" }, null, new TestPlatformOptions(), mockHandler.Object); Assert.IsNotNull(receivedTestCases); Assert.AreEqual(1, receivedTestCases.Count); @@ -635,7 +629,7 @@ public async Task DiscoverTestsAsyncShouldCompleteWithTestMessage() mockHandler.Setup(mh => mh.HandleLogMessage(It.IsAny(), It.IsAny())).Callback( () => this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(discoveryComplete))); - await this.requestSenderAsync.DiscoverTestsAsync(new List() { "1.dll" }, null, new TestPlatformOptions(), mockHandler.Object); + await this.requestSender.DiscoverTestsAsync(new List() { "1.dll" }, null, new TestPlatformOptions(), mockHandler.Object); mockHandler.Verify(mh => mh.HandleDiscoveryComplete(It.IsAny(), null), Times.Once, "Discovery Complete must be called"); mockHandler.Verify(mh => mh.HandleDiscoveredTests(It.IsAny>()), Times.Once, "DiscoveredTests must be called"); @@ -665,7 +659,7 @@ public async Task DiscoverTestsAsyncShouldAbortOnExceptionInSendMessage() var payload = new DiscoveryRequestPayload { Sources = sources, RunSettings = null }; this.mockCommunicationManager.Setup(cm => cm.SendMessage(MessageType.StartDiscovery, payload)).Throws(new IOException()); - await this.requestSenderAsync.DiscoverTestsAsync(sources, null, new TestPlatformOptions(), mockHandler.Object); + await this.requestSender.DiscoverTestsAsync(sources, null, new TestPlatformOptions(), mockHandler.Object); mockHandler.Verify(mh => mh.HandleDiscoveryComplete(It.IsAny(), null), Times.Once, "Discovery Complete must be called"); mockHandler.Verify(mh => mh.HandleLogMessage(TestMessageLevel.Error, It.IsAny()), Times.Once, "TestMessage event must be called"); @@ -710,16 +704,16 @@ public async Task DiscoverTestsAsyncShouldLogErrorWhenProcessExited() var testCase = new TestCase("hello", new Uri("world://how"), "1.dll"); var testCaseList = new List() { testCase }; var testsFound = CreateMessage(MessageType.TestCasesFound, testCaseList); - await this.requestSenderAsync.InitializeCommunicationAsync(this.WaitTimeout); + await this.requestSender.InitializeCommunicationAsync(this.WaitTimeout); this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Callback( (CancellationToken c) => { - Task.Run(() => this.requestSenderAsync.OnProcessExited()).Wait(); + Task.Run(() => this.requestSender.OnProcessExited()).Wait(); Assert.IsTrue(c.IsCancellationRequested); }).Returns(Task.FromResult((Message)null)); - await this.requestSenderAsync.DiscoverTestsAsync(sources, null, new TestPlatformOptions(), mockHandler.Object); + await this.requestSender.DiscoverTestsAsync(sources, null, new TestPlatformOptions(), mockHandler.Object); mockHandler.Verify(mh => mh.HandleLogMessage(TestMessageLevel.Error, It.IsAny()), Times.Once); } @@ -778,7 +772,7 @@ public async Task StartTestRunAsyncShouldCompleteWithZeroTests() var runComplete = CreateMessage(MessageType.ExecutionComplete, payload); this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(runComplete)); - await this.requestSenderAsync.StartTestRunAsync(new List() { "1.dll" }, null, null, mockHandler.Object); + await this.requestSender.StartTestRunAsync(new List() { "1.dll" }, null, null, mockHandler.Object); mockHandler.Verify(mh => mh.HandleTestRunComplete(It.IsAny(), It.IsAny(), null, null), Times.Once, "Run Complete must be called"); @@ -888,7 +882,7 @@ public async Task StartTestRunAsyncShouldCompleteWithSingleTestAndMessage() this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(runComplete)); }); - await this.requestSenderAsync.StartTestRunAsync(new List() { "1.dll" }, null, null, mockHandler.Object); + await this.requestSender.StartTestRunAsync(new List() { "1.dll" }, null, null, mockHandler.Object); mockHandler.Verify(mh => mh.HandleTestRunComplete(It.IsAny(), It.IsAny(), null, null), Times.Once, "Run Complete must be called"); @@ -1060,7 +1054,7 @@ public async Task StartTestRunAsyncWithCustomHostShouldComplete() this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(runprocessInfoPayload)); - await this.requestSenderAsync.StartTestRunWithCustomHostAsync(new List() { "1.dll" }, null, null, mockHandler.Object, mockLauncher.Object); + await this.requestSender.StartTestRunWithCustomHostAsync(new List() { "1.dll" }, null, null, mockHandler.Object, mockLauncher.Object); mockHandler.Verify(mh => mh.HandleTestRunComplete(It.IsAny(), It.IsAny(), null, null), Times.Once, "Run Complete must be called"); @@ -1161,7 +1155,7 @@ public async Task StartTestRunAsyncWithCustomHostShouldNotAbortAndSendErrorToVst this.mockCommunicationManager.Setup(cm => cm.SendMessage(It.IsAny(), It.IsAny(), this.protocolVersion)). Callback(() => this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(runComplete))); - await this.requestSenderAsync.StartTestRunWithCustomHostAsync(new List() { "1.dll" }, null, null, mockHandler.Object, mockLauncher.Object); + await this.requestSender.StartTestRunWithCustomHostAsync(new List() { "1.dll" }, null, null, mockHandler.Object, mockLauncher.Object); mockHandler.Verify(mh => mh.HandleTestRunComplete(It.IsAny(), It.IsAny(), null, null), Times.Once, "Run Complete must be called"); @@ -1258,7 +1252,7 @@ public async Task StartTestRunAsyncWithSelectedTestsShouldCompleteWithZeroTests( var runComplete = CreateMessage(MessageType.ExecutionComplete, payload); this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(runComplete)); - await this.requestSenderAsync.StartTestRunAsync(new List(), null, new TestPlatformOptions(), mockHandler.Object); + await this.requestSender.StartTestRunAsync(new List(), null, new TestPlatformOptions(), mockHandler.Object); mockHandler.Verify(mh => mh.HandleTestRunComplete(It.IsAny(), It.IsAny(), null, null), Times.Once, "Run Complete must be called"); @@ -1366,7 +1360,7 @@ public async Task StartTestRunAsyncWithSelectedTestsShouldCompleteWithSingleTest this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(runComplete)); }); - await this.requestSenderAsync.StartTestRunAsync(testCaseList, null, new TestPlatformOptions(), mockHandler.Object); + await this.requestSender.StartTestRunAsync(testCaseList, null, new TestPlatformOptions(), mockHandler.Object); mockHandler.Verify(mh => mh.HandleTestRunComplete(It.IsAny(), It.IsAny(), null, null), Times.Once, "Run Complete must be called"); @@ -1474,7 +1468,7 @@ public async Task StartTestRunAsyncWithSelectedTestsHavingTraitsShouldReturnTest receivedChangeEventArgs = stats; }); - await this.requestSenderAsync.StartTestRunAsync(testCaseList, null, new TestPlatformOptions(), mockHandler.Object); + await this.requestSender.StartTestRunAsync(testCaseList, null, new TestPlatformOptions(), mockHandler.Object); Assert.IsNotNull(receivedChangeEventArgs); Assert.IsTrue(receivedChangeEventArgs.NewTestResults.Count() > 0); @@ -1588,7 +1582,7 @@ public async Task StartTestRunAsyncWithSelectedTestsHavingTraitsShouldReturnTest this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(runComplete)); }); - await this.requestSenderAsync.StartTestRunAsync(testCaseList, null, new TestPlatformOptions(), mockHandler.Object); + await this.requestSender.StartTestRunAsync(testCaseList, null, new TestPlatformOptions(), mockHandler.Object); Assert.IsNotNull(receivedChangeEventArgs); Assert.IsTrue(receivedChangeEventArgs.NewTestResults.Any()); @@ -1715,7 +1709,7 @@ public async Task StartTestRunWithCustomHostAsyncWithSelectedTestsShouldComplete this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(runprocessInfoPayload)); - await this.requestSenderAsync.StartTestRunWithCustomHostAsync(testCaseList, null, new TestPlatformOptions(), mockHandler.Object, mockLauncher.Object); + await this.requestSender.StartTestRunWithCustomHostAsync(testCaseList, null, new TestPlatformOptions(), mockHandler.Object, mockLauncher.Object); mockHandler.Verify(mh => mh.HandleTestRunComplete(It.IsAny(), It.IsAny(), null, null), Times.Once, "Run Complete must be called"); @@ -1797,8 +1791,8 @@ public async Task StartTestRunWithCustomHostAsyncInParallelShouldCallCustomHostM } }); - await this.requestSenderAsync.InitializeCommunicationAsync(this.WaitTimeout); - await this.requestSenderAsync.StartTestRunWithCustomHostAsync(sources, null, null, mockHandler.Object, mockLauncher.Object); + await this.requestSender.InitializeCommunicationAsync(this.WaitTimeout); + await this.requestSender.StartTestRunWithCustomHostAsync(sources, null, null, mockHandler.Object, mockLauncher.Object); mockLauncher.Verify(ml => ml.LaunchTestHost(It.IsAny()), Times.Exactly(2)); } @@ -1830,7 +1824,7 @@ public async Task StartTestRunAsyncShouldAbortOnExceptionInSendMessage() this.mockCommunicationManager.Setup(cm => cm.SendMessage(MessageType.TestRunAllSourcesWithDefaultHost, payload, this.protocolVersion)).Throws(exception); - await this.requestSenderAsync.StartTestRunAsync(sources, null, null, mockHandler.Object); + await this.requestSender.StartTestRunAsync(sources, null, null, mockHandler.Object); mockHandler.Verify(mh => mh.HandleTestRunComplete(It.IsAny(), null, null, null), Times.Once, "Test Run Complete must be called"); mockHandler.Verify(mh => mh.HandleLogMessage(TestMessageLevel.Error, It.IsAny()), Times.Once, "TestMessage event must be called"); @@ -1883,22 +1877,240 @@ public async Task StartTestRunAsyncShouldLogErrorOnProcessExited() RunAttachments = null, TestRunCompleteArgs = dummyCompleteArgs }; - await this.requestSenderAsync.InitializeCommunicationAsync(this.WaitTimeout); + await this.requestSender.InitializeCommunicationAsync(this.WaitTimeout); this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())) .Callback((CancellationToken c) => { - Task.Run(() => this.requestSenderAsync.OnProcessExited()).Wait(); + Task.Run(() => this.requestSender.OnProcessExited()).Wait(); Assert.IsTrue(c.IsCancellationRequested); }).Returns(Task.FromResult((Message)null)); - await this.requestSenderAsync.StartTestRunAsync(sources, null, null, mockHandler.Object); + await this.requestSender.StartTestRunAsync(sources, null, null, mockHandler.Object); mockHandler.Verify(mh => mh.HandleLogMessage(TestMessageLevel.Error, It.IsAny()), Times.Once); } #endregion + #region Attachments Processing Tests + + [TestMethod] + public async Task ProcessTestRunAttachmentsAsyncShouldCompleteWithZeroAttachments() + { + await this.InitializeCommunicationAsync(); + + var mockHandler = new Mock(); + + var payload = new TestRunAttachmentsProcessingCompletePayload() + { + AttachmentsProcessingCompleteEventArgs = new TestRunAttachmentsProcessingCompleteEventArgs(false, null), + Attachments = new AttachmentSet[0] + }; + + var attachmentsProcessingComplete = new Message() + { + MessageType = MessageType.TestRunAttachmentsProcessingComplete, + Payload = JToken.FromObject(payload) + }; + this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(attachmentsProcessingComplete)); + + await this.requestSender.ProcessTestRunAttachmentsAsync(new List { new AttachmentSet(new Uri("http://www.bing.com"), "a") }, true, mockHandler.Object, CancellationToken.None); + + mockCommunicationManager.Verify(c => c.SendMessage(MessageType.TestRunAttachmentsProcessingStart, It.IsAny())); + mockCommunicationManager.Verify(c => c.SendMessage(MessageType.TestRunAttachmentsProcessingCancel), Times.Never); + mockHandler.Verify(mh => mh.HandleTestRunAttachmentsProcessingComplete(It.Is(a => !a.IsCanceled && a.Error == null), It.Is>(a => a.Count == 0)), Times.Once, "Attachments Processing Complete must be called"); + mockHandler.Verify(mh => mh.HandleLogMessage(It.IsAny(), It.IsAny()), Times.Never, "TestMessage event must not be called"); + } + + [TestMethod] + public async Task ProcessTestRunAttachmentsAsyncShouldCompleteWithOneAttachment() + { + await this.InitializeCommunicationAsync(); + + var mockHandler = new Mock(); + + var payload = new TestRunAttachmentsProcessingCompletePayload() + { + AttachmentsProcessingCompleteEventArgs = new TestRunAttachmentsProcessingCompleteEventArgs(true, new Exception("msg")), + Attachments = new List { new AttachmentSet(new Uri("http://www.bing.com"), "out") } + }; + var attachmentsProcessingComplete = new Message() + { + MessageType = MessageType.TestRunAttachmentsProcessingComplete, + Payload = JToken.FromObject(payload) + }; + this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(attachmentsProcessingComplete)); + + await this.requestSender.ProcessTestRunAttachmentsAsync(new List { new AttachmentSet(new Uri("http://www.bing.com"), "a") }, true, mockHandler.Object, CancellationToken.None); + + mockCommunicationManager.Verify(c => c.SendMessage(MessageType.TestRunAttachmentsProcessingStart, It.IsAny())); + mockCommunicationManager.Verify(c => c.SendMessage(MessageType.TestRunAttachmentsProcessingCancel), Times.Never); + mockHandler.Verify(mh => mh.HandleTestRunAttachmentsProcessingComplete(It.Is(a => a.IsCanceled && a.Error != null), It.Is>(a => a.Count == 1)), Times.Once, "Attachments Processing Complete must be called"); + mockHandler.Verify(mh => mh.HandleLogMessage(It.IsAny(), It.IsAny()), Times.Never, "TestMessage event must not be called"); + } + + [TestMethod] + public async Task ProcessTestRunAttachmentsAsyncShouldCompleteWithOneAttachmentAndTestMessage() + { + await this.InitializeCommunicationAsync(); + + var mockHandler = new Mock(); + + var payload = new TestRunAttachmentsProcessingCompletePayload() + { + AttachmentsProcessingCompleteEventArgs = new TestRunAttachmentsProcessingCompleteEventArgs(false, null), + Attachments = new List { new AttachmentSet(new Uri("http://www.bing.com"), "out") } + }; + + var attachmentsProcessingComplete = new Message() + { + MessageType = MessageType.TestRunAttachmentsProcessingComplete, + Payload = JToken.FromObject(payload) + }; + + var mpayload = new TestMessagePayload() { MessageLevel = TestMessageLevel.Informational, Message = "Hello" }; + var message = CreateMessage(MessageType.TestMessage, mpayload); + + this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(message)); + mockHandler.Setup(mh => mh.HandleLogMessage(It.IsAny(), It.IsAny())).Callback( + () => this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(attachmentsProcessingComplete))); + + await this.requestSender.ProcessTestRunAttachmentsAsync(new List { new AttachmentSet(new Uri("http://www.bing.com"), "a") }, false, mockHandler.Object, CancellationToken.None); + + mockCommunicationManager.Verify(c => c.SendMessage(MessageType.TestRunAttachmentsProcessingStart, It.IsAny())); + mockCommunicationManager.Verify(c => c.SendMessage(MessageType.TestRunAttachmentsProcessingCancel), Times.Never); + mockHandler.Verify(mh => mh.HandleTestRunAttachmentsProcessingComplete(It.IsAny(), It.Is>(a => a.Count == 1)), Times.Once, "Attachments Processing Complete must be called"); + mockHandler.Verify(mh => mh.HandleLogMessage(TestMessageLevel.Informational, "Hello"), Times.Once, "TestMessage event must be called"); + } + + [TestMethod] + public async Task ProcessTestRunAttachmentsAsyncShouldCompleteWithOneAttachmentAndProgressMessage() + { + await this.InitializeCommunicationAsync(); + + var mockHandler = new Mock(); + + var completePayload = new TestRunAttachmentsProcessingCompletePayload() + { + AttachmentsProcessingCompleteEventArgs = new TestRunAttachmentsProcessingCompleteEventArgs(false, null), + Attachments = new List { new AttachmentSet(new Uri("http://www.bing.com"), "out") } + }; + + var attachmentsProcessingComplete = new Message() + { + MessageType = MessageType.TestRunAttachmentsProcessingComplete, + Payload = JToken.FromObject(completePayload) + }; + + var progressPayload = new TestRunAttachmentsProcessingProgressPayload() + { + AttachmentsProcessingProgressEventArgs = new TestRunAttachmentsProcessingProgressEventArgs(1, new[] { new Uri("http://www.bing.com/") }, 50, 2) + }; + + var attachmentsProcessingProgress = new Message() + { + MessageType = MessageType.TestRunAttachmentsProcessingProgress, + Payload = JToken.FromObject(progressPayload) + }; + this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(attachmentsProcessingProgress)); + + mockHandler.Setup(mh => mh.HandleTestRunAttachmentsProcessingProgress(It.IsAny())).Callback( + () => this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(attachmentsProcessingComplete))); + + await this.requestSender.ProcessTestRunAttachmentsAsync(new List { new AttachmentSet(new Uri("http://www.bing.com"), "a") }, false, mockHandler.Object, CancellationToken.None); + + mockCommunicationManager.Verify(c => c.SendMessage(MessageType.TestRunAttachmentsProcessingStart, It.IsAny())); + mockCommunicationManager.Verify(c => c.SendMessage(MessageType.TestRunAttachmentsProcessingCancel), Times.Never); + mockHandler.Verify(mh => mh.HandleTestRunAttachmentsProcessingComplete(It.IsAny(), It.Is>(a => a.Count == 1)), Times.Once, "Attachments Processing Complete must be called"); + mockHandler.Verify(mh => mh.HandleTestRunAttachmentsProcessingProgress(It.Is(a => a.CurrentAttachmentProcessorIndex == 1 && a.CurrentAttachmentProcessorUris.First() == new Uri("http://www.bing.com/") && a.CurrentAttachmentProcessorProgress == 50 && a.AttachmentProcessorsCount == 2)), Times.Once, "Attachments processing Progress must be called"); + mockHandler.Verify(mh => mh.HandleLogMessage(TestMessageLevel.Informational, "Hello"), Times.Never); + } + + [TestMethod] + public async Task ProcessTestRunAttachmentsAsyncShouldSendCancelMessageIfCancellationTokenCancelled() + { + await this.InitializeCommunicationAsync(); + + var cts = new CancellationTokenSource(); + + var mockHandler = new Mock(); + + var payload = new TestRunAttachmentsProcessingCompletePayload() + { + Attachments = new List { new AttachmentSet(new Uri("http://www.bing.com"), "out") } + }; + var attachmentsProcessingComplete = new Message() + { + MessageType = MessageType.TestRunAttachmentsProcessingComplete, + Payload = JToken.FromObject(payload) + }; + + var mpayload = new TestMessagePayload() { MessageLevel = TestMessageLevel.Informational, Message = "Hello" }; + var message = CreateMessage(MessageType.TestMessage, mpayload); + + this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(message)); + mockHandler.Setup(mh => mh.HandleLogMessage(It.IsAny(), It.IsAny())).Callback(() => + { + cts.Cancel(); + this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(attachmentsProcessingComplete)); + }); + + await this.requestSender.ProcessTestRunAttachmentsAsync(new List { new AttachmentSet(new Uri("http://www.bing.com"), "a") }, false, mockHandler.Object, cts.Token); + + mockCommunicationManager.Verify(c => c.SendMessage(MessageType.TestRunAttachmentsProcessingStart, It.IsAny())); + mockCommunicationManager.Verify(c => c.SendMessage(MessageType.TestRunAttachmentsProcessingCancel)); + mockHandler.Verify(mh => mh.HandleTestRunAttachmentsProcessingComplete(It.IsAny(), It.Is>(a => a.Count == 1)), Times.Once, "Attachments Processing Complete must be called"); + mockHandler.Verify(mh => mh.HandleLogMessage(TestMessageLevel.Informational, "Hello"), Times.Once, "TestMessage event must be called"); + } + + [TestMethod] + public async Task ProcessTestRunAttachmentsAsyncShouldSendCancelMessageIfCancellationTokenCancelledAtTheBeginning() + { + await this.InitializeCommunicationAsync(); + + var cts = new CancellationTokenSource(); + cts.Cancel(); + + var mockHandler = new Mock(); + + var payload = new TestRunAttachmentsProcessingCompletePayload() + { + Attachments = new List { new AttachmentSet(new Uri("http://www.bing.com"), "out") } + }; + var attachmentsProcessingComplete = new Message() + { + MessageType = MessageType.TestRunAttachmentsProcessingComplete, + Payload = JToken.FromObject(payload) + }; + + this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(attachmentsProcessingComplete)); + + await this.requestSender.ProcessTestRunAttachmentsAsync(new List { new AttachmentSet(new Uri("http://www.bing.com"), "a") }, true, mockHandler.Object, cts.Token); + + mockCommunicationManager.Verify(c => c.SendMessage(MessageType.TestRunAttachmentsProcessingStart, It.IsAny())); + mockCommunicationManager.Verify(c => c.SendMessage(MessageType.TestRunAttachmentsProcessingCancel)); + mockHandler.Verify(mh => mh.HandleTestRunAttachmentsProcessingComplete(It.IsAny(), It.Is>(a => a.Count == 1)), Times.Once, "Attachments Processing Complete must be called"); + mockHandler.Verify(mh => mh.HandleLogMessage(TestMessageLevel.Informational, "Hello"), Times.Never, "TestMessage event must be called"); + } + + [TestMethod] + public async Task ProcessTestRunAttachmentsAsyncShouldAbortOnExceptionInSendMessage() + { + var mockHandler = new Mock(); + this.mockCommunicationManager.Setup(cm => cm.SendMessage(MessageType.TestRunAttachmentsProcessingStart, It.IsAny())).Throws(new IOException()); + + await this.requestSender.ProcessTestRunAttachmentsAsync(new List { new AttachmentSet(new Uri("http://www.bing.com"), "out") }, false, mockHandler.Object, CancellationToken.None); + + mockHandler.Verify(mh => mh.HandleTestRunAttachmentsProcessingComplete(It.Is(a => !a.IsCanceled && a.Error is IOException), null), Times.Once, "Attachments Processing Complete must be called"); + mockHandler.Verify(mh => mh.HandleLogMessage(TestMessageLevel.Error, It.IsAny()), Times.Once, "TestMessage event must be called"); + this.mockCommunicationManager.Verify(cm => cm.StopServer(), Times.Never); + } + + #endregion + + + #region Private Methods /// @@ -1980,7 +2192,7 @@ private async Task InitializeCommunicationAsync() this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(sessionConnected)); this.mockCommunicationManager.Setup(cm => cm.SendMessage(MessageType.VersionCheck, this.protocolVersion)).Callback(changedMessage); - var portOutput = await this.requestSenderAsync.InitializeCommunicationAsync(this.WaitTimeout); + var portOutput = await this.requestSender.InitializeCommunicationAsync(this.WaitTimeout); Assert.AreEqual(dummyPortInput, portOutput, "Connection must succeed."); } diff --git a/test/TranslationLayer.UnitTests/VsTestConsoleWrapperAsyncTests.cs b/test/TranslationLayer.UnitTests/VsTestConsoleWrapperAsyncTests.cs index d272a5753b..fed30c3eb9 100644 --- a/test/TranslationLayer.UnitTests/VsTestConsoleWrapperAsyncTests.cs +++ b/test/TranslationLayer.UnitTests/VsTestConsoleWrapperAsyncTests.cs @@ -5,6 +5,8 @@ namespace Microsoft.TestPlatform.VsTestConsole.TranslationLayer.UnitTests { using System; using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Threading; using System.Threading.Tasks; using Microsoft.TestPlatform.VsTestConsole.TranslationLayer.Interfaces; @@ -12,6 +14,7 @@ namespace Microsoft.TestPlatform.VsTestConsole.TranslationLayer.UnitTests using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces; + using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions.Interfaces; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; @@ -19,11 +22,13 @@ namespace Microsoft.TestPlatform.VsTestConsole.TranslationLayer.UnitTests [TestClass] public class VsTestConsoleWrapperAsyncTests { - private IVsTestConsoleWrapperAsync consoleWrapper; + private IVsTestConsoleWrapper consoleWrapper; private Mock mockProcessManager; - private Mock mockRequestSender; + private Mock mockRequestSender; + + private Mock mockProcessHelper; private readonly List testSources = new List { "Hello", "World" }; @@ -40,13 +45,15 @@ public void TestInitialize() { this.consoleParameters = new ConsoleParameters(); - this.mockRequestSender = new Mock(); + this.mockRequestSender = new Mock(); this.mockProcessManager = new Mock(); - this.consoleWrapper = new VsTestConsoleWrapperAsync( + this.mockProcessHelper = new Mock(); + this.consoleWrapper = new VsTestConsoleWrapper( this.mockRequestSender.Object, this.mockProcessManager.Object, this.consoleParameters, - new Mock().Object); + new Mock().Object, + this.mockProcessHelper.Object); this.mockRequestSender.Setup(rs => rs.InitializeCommunicationAsync(It.IsAny())).Returns(Task.FromResult(100)); } @@ -302,6 +309,23 @@ await this.consoleWrapper.RunTestsWithCustomTestHostAsync( this.mockRequestSender.Verify(rs => rs.StartTestRunWithCustomHostAsync(this.testCases, "RunSettings", options, It.IsAny(), It.IsAny()), Times.Once); } + [TestMethod] + public async Task ProcessTestRunAttachmentsAsyncShouldSucceed() + { + var attachments = new Collection(); + var cancellationToken = new CancellationToken(); + + await this.consoleWrapper.ProcessTestRunAttachmentsAsync( + attachments, + null, + true, + true, + new Mock().Object, + cancellationToken); + + this.mockRequestSender.Verify(rs => rs.ProcessTestRunAttachmentsAsync(attachments, true, It.IsAny(), cancellationToken)); + } + [TestMethod] public void EndSessionShouldSucceed() { diff --git a/test/TranslationLayer.UnitTests/VsTestConsoleWrapperTests.cs b/test/TranslationLayer.UnitTests/VsTestConsoleWrapperTests.cs index 2691bb43c7..ee9c0f6a23 100644 --- a/test/TranslationLayer.UnitTests/VsTestConsoleWrapperTests.cs +++ b/test/TranslationLayer.UnitTests/VsTestConsoleWrapperTests.cs @@ -13,7 +13,10 @@ namespace Microsoft.TestPlatform.VsTestConsole.TranslationLayer.UnitTests using Moq; using System; using System.Collections.Generic; + using System.Collections.ObjectModel; using System.Diagnostics; + using System.Threading; + using System.Threading.Tasks; [TestClass] public class VsTestConsoleWrapperTests @@ -53,6 +56,7 @@ public void TestInitialize() this.mockRequestSender.Setup(rs => rs.WaitForRequestHandlerConnection(It.IsAny())).Returns(true); this.mockRequestSender.Setup(rs => rs.InitializeCommunication()).Returns(100); + this.mockRequestSender.Setup(rs => rs.InitializeCommunicationAsync(It.IsAny())).Returns(Task.FromResult(100)); } [TestMethod] @@ -303,6 +307,23 @@ public void RunTestsWithSelectedTestsAndOptionsUsingACustomHostShouldPassOnOptio this.mockRequestSender.Verify(rs => rs.StartTestRunWithCustomHost(this.testCases, "RunSettings", options, It.IsAny(), It.IsAny()), Times.Once); } + [TestMethod] + public async Task ProcessTestRunAttachmentsAsyncShouldSucceed() + { + var attachments = new Collection(); + var cancellationToken = new CancellationToken(); + + await this.consoleWrapper.ProcessTestRunAttachmentsAsync( + attachments, + null, + true, + true, + new Mock().Object, + cancellationToken); + + this.mockRequestSender.Verify(rs => rs.ProcessTestRunAttachmentsAsync(attachments, true, It.IsAny(), cancellationToken)); + } + [TestMethod] public void EndSessionShouldSucceed() { diff --git a/test/vstest.console.UnitTests/Processors/ListFullyQualifiedTestsArgumentProcessorTests.cs b/test/vstest.console.UnitTests/Processors/ListFullyQualifiedTestsArgumentProcessorTests.cs index ca191e2c8f..4a12456ce3 100644 --- a/test/vstest.console.UnitTests/Processors/ListFullyQualifiedTestsArgumentProcessorTests.cs +++ b/test/vstest.console.UnitTests/Processors/ListFullyQualifiedTestsArgumentProcessorTests.cs @@ -18,6 +18,7 @@ namespace Microsoft.VisualStudio.TestPlatform.CommandLine.UnitTests.Processors using Microsoft.VisualStudio.TestPlatform.CommandLine.Publisher; using Microsoft.VisualStudio.TestPlatform.CommandLineUtilities; using Microsoft.VisualStudio.TestPlatform.Common.Utilities; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine; using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions.Interfaces; using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -44,6 +45,7 @@ public class ListFullyQualifiedTestsArgumentProcessorTests private Task mockMetricsPublisherTask; private Mock mockMetricsPublisher; private Mock mockProcessHelper; + private Mock mockAttachmentsProcessingManager; private static ListFullyQualifiedTestsArgumentExecutor GetExecutor(ITestRequestManager testRequestManager, IOutput output) { @@ -78,6 +80,7 @@ public ListFullyQualifiedTestsArgumentProcessorTests() this.mockAssemblyMetadataProvider.Setup(x => x.GetFrameWork(It.IsAny())).Returns(new FrameworkName(Constants.DotNetFramework40)); this.inferHelper = new InferHelper(this.mockAssemblyMetadataProvider.Object); this.mockProcessHelper = new Mock(); + this.mockAttachmentsProcessingManager = new Mock(); } /// @@ -124,7 +127,7 @@ public void ExecutorInitializeWithValidSourceShouldAddItToTestSources() { CommandLineOptions.Instance.FileHelper = this.mockFileHelper.Object; CommandLineOptions.Instance.FilePatternParser = new FilePatternParser(new Mock().Object, this.mockFileHelper.Object); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, TestPlatformFactory.GetTestPlatform(), TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, TestPlatformFactory.GetTestPlatform(), TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object); var executor = GetExecutor(testRequestManager, null); executor.Initialize(this.dummyTestFilePath); @@ -136,7 +139,7 @@ public void ExecutorInitializeWithValidSourceShouldAddItToTestSources() public void ExecutorExecuteForNoSourcesShouldReturnFail() { CommandLineOptions.Instance.Reset(); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, TestPlatformFactory.GetTestPlatform(), TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, TestPlatformFactory.GetTestPlatform(), TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object); var executor = GetExecutor(testRequestManager, null); Assert.ThrowsException(() => executor.Execute()); @@ -153,7 +156,7 @@ public void ExecutorExecuteShouldThrowTestPlatformException() this.ResetAndAddSourceToCommandLineOptions(true); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object); var executor = GetExecutor(testRequestManager, null); @@ -170,7 +173,7 @@ public void ExecutorExecuteShouldThrowSettingsException() mockTestPlatform.Setup(tp => tp.CreateDiscoveryRequest(It.IsAny(), It.IsAny(), It.IsAny())).Returns(mockDiscoveryRequest.Object); this.ResetAndAddSourceToCommandLineOptions(true); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object); var listTestsArgumentExecutor = GetExecutor(testRequestManager, null); @@ -189,7 +192,7 @@ public void ExecutorExecuteShouldThrowInvalidOperationException() this.ResetAndAddSourceToCommandLineOptions(true); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object); var listTestsArgumentExecutor = GetExecutor(testRequestManager, null); @@ -207,7 +210,7 @@ public void ExecutorExecuteShouldThrowOtherExceptions() this.ResetAndAddSourceToCommandLineOptions(true); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object); var executor = GetExecutor(testRequestManager, null); @@ -299,7 +302,7 @@ private void RunListFullyQualifiedTestArgumentProcessorWithTraits(Mock mockMetricsPublisherTask; private Mock mockMetricsPublisher; private Mock mockProcessHelper; + private Mock mockAttachmentsProcessingManager; private static ListTestsArgumentExecutor GetExecutor(ITestRequestManager testRequestManager, IOutput output) { @@ -76,6 +78,7 @@ public ListTestsArgumentProcessorTests() this.mockAssemblyMetadataProvider.Setup(x => x.GetFrameWork(It.IsAny())).Returns(new FrameworkName(Constants.DotNetFramework40)); this.inferHelper = new InferHelper(this.mockAssemblyMetadataProvider.Object); this.mockProcessHelper = new Mock(); + this.mockAttachmentsProcessingManager = new Mock(); } /// @@ -125,7 +128,7 @@ public void ExecutorInitializeWithValidSourceShouldAddItToTestSources() { CommandLineOptions.Instance.FileHelper = this.mockFileHelper.Object; CommandLineOptions.Instance.FilePatternParser = new FilePatternParser(new Mock().Object, this.mockFileHelper.Object); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, TestPlatformFactory.GetTestPlatform(), TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, TestPlatformFactory.GetTestPlatform(), TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object); var executor = GetExecutor(testRequestManager, null); executor.Initialize(this.dummyTestFilePath); @@ -138,7 +141,7 @@ public void ExecutorExecuteForNoSourcesShouldReturnFail() { CommandLineOptions.Instance.Reset(); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, TestPlatformFactory.GetTestPlatform(), TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, TestPlatformFactory.GetTestPlatform(), TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object); var executor = GetExecutor(testRequestManager, null); Assert.ThrowsException(() => executor.Execute()); @@ -155,7 +158,7 @@ public void ExecutorExecuteShouldThrowTestPlatformException() this.ResetAndAddSourceToCommandLineOptions(); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object); var executor = GetExecutor(testRequestManager, null); Assert.ThrowsException(() => executor.Execute()); @@ -172,7 +175,7 @@ public void ExecutorExecuteShouldThrowSettingsException() this.ResetAndAddSourceToCommandLineOptions(); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object); var listTestsArgumentExecutor = GetExecutor(testRequestManager, null); Assert.ThrowsException(() => listTestsArgumentExecutor.Execute()); @@ -189,7 +192,7 @@ public void ExecutorExecuteShouldThrowInvalidOperationException() this.ResetAndAddSourceToCommandLineOptions(); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object); var listTestsArgumentExecutor = GetExecutor(testRequestManager, null); Assert.ThrowsException(() => listTestsArgumentExecutor.Execute()); @@ -206,7 +209,7 @@ public void ExecutorExecuteShouldThrowOtherExceptions() this.ResetAndAddSourceToCommandLineOptions(); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object); var executor = GetExecutor(testRequestManager, null); Assert.ThrowsException(() => executor.Execute()); @@ -263,7 +266,7 @@ private void RunListTestArgumentProcessorExecuteWithMockSetup(Mock mockMetricsPublisherTask; private Mock mockMetricsPublisher; private Mock mockProcessHelper; + private Mock mockAttachmentsProcessingManager; private RunSpecificTestsArgumentExecutor GetExecutor(ITestRequestManager testRequestManager) { @@ -67,6 +69,7 @@ public RunSpecificTestsArgumentProcessorTests() this.mockProcessHelper = new Mock(); this.mockProcessHelper.Setup(x => x.GetCurrentProcessId()).Returns(1234); this.mockProcessHelper.Setup(x => x.GetProcessName(It.IsAny())).Returns("dotnet.exe"); + this.mockAttachmentsProcessingManager = new Mock(); } [TestMethod] @@ -111,7 +114,7 @@ public void InitializeShouldThrowIfArgumentIsNull() { CommandLineOptions.Instance.Reset(); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, TestPlatformFactory.GetTestPlatform(), TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, TestPlatformFactory.GetTestPlatform(), TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object); var executor = GetExecutor(testRequestManager); Assert.ThrowsException(() => { executor.Initialize(null); }); @@ -122,7 +125,7 @@ public void InitializeShouldThrowIfArgumentIsEmpty() { CommandLineOptions.Instance.Reset(); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, TestPlatformFactory.GetTestPlatform(), TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, TestPlatformFactory.GetTestPlatform(), TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object); var executor = GetExecutor(testRequestManager); Assert.ThrowsException(() => { executor.Initialize(String.Empty); }); @@ -133,7 +136,7 @@ public void InitializeShouldThrowIfArgumentIsWhiteSpace() { CommandLineOptions.Instance.Reset(); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, TestPlatformFactory.GetTestPlatform(), TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, TestPlatformFactory.GetTestPlatform(), TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object); var executor = GetExecutor(testRequestManager); Assert.ThrowsException(() => { executor.Initialize(" "); }); @@ -144,7 +147,7 @@ public void InitializeShouldThrowIfArgumentsAreEmpty() { CommandLineOptions.Instance.Reset(); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, TestPlatformFactory.GetTestPlatform(), TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, TestPlatformFactory.GetTestPlatform(), TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object); var executor = GetExecutor(testRequestManager); Assert.ThrowsException(() => { executor.Initialize(" , "); }); @@ -155,7 +158,7 @@ public void ExecutorShouldSplitTestsSeparatedByComma() { CommandLineOptions.Instance.Reset(); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, TestPlatformFactory.GetTestPlatform(), TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, TestPlatformFactory.GetTestPlatform(), TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object); var executor = GetExecutor(testRequestManager); Assert.ThrowsException(() => executor.Execute()); @@ -166,7 +169,7 @@ public void ExecutorExecuteForNoSourcesShouldThrowCommandLineException() { CommandLineOptions.Instance.Reset(); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, TestPlatformFactory.GetTestPlatform(), TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, TestPlatformFactory.GetTestPlatform(), TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object); var executor = GetExecutor(testRequestManager); Assert.ThrowsException(() => executor.Execute()); @@ -189,7 +192,7 @@ public void ExecutorExecuteForValidSourceWithTestCaseFilterShouldRunTests() mockTestPlatform.Setup(tp => tp.CreateTestRunRequest(It.IsAny(), It.IsAny(), It.IsAny())).Returns(mockTestRunRequest.Object); mockTestPlatform.Setup(tp => tp.CreateDiscoveryRequest(It.IsAny(), It.IsAny(), It.IsAny())).Returns(mockDiscoveryRequest.Object); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object);; var executor = GetExecutor(testRequestManager); CommandLineOptions.Instance.TestCaseFilterValue = "Filter"; @@ -214,7 +217,7 @@ public void ExecutorExecuteShouldThrowTestPlatformExceptionThrownDuringDiscovery mockTestPlatform.Setup(tp => tp.CreateDiscoveryRequest(It.IsAny(), It.IsAny(), It.IsAny())).Returns(mockDiscoveryRequest.Object); this.ResetAndAddSourceToCommandLineOptions(); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object);; var executor = GetExecutor(testRequestManager); Assert.ThrowsException(() => executor.Execute()); @@ -232,7 +235,7 @@ public void ExecutorExecuteShouldThrowInvalidOperationExceptionThrownDuringDisco mockTestPlatform.Setup(tp => tp.CreateDiscoveryRequest(It.IsAny(), It.IsAny(), It.IsAny())).Returns(mockDiscoveryRequest.Object); this.ResetAndAddSourceToCommandLineOptions(); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object);; var executor = GetExecutor(testRequestManager); Assert.ThrowsException(() => executor.Execute()); @@ -250,7 +253,7 @@ public void ExecutorExecuteShouldThrowSettingsExceptionThrownDuringDiscovery() mockTestPlatform.Setup(tp => tp.CreateDiscoveryRequest(It.IsAny(), It.IsAny(), It.IsAny())).Returns(mockDiscoveryRequest.Object); this.ResetAndAddSourceToCommandLineOptions(); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object);; var executor = GetExecutor(testRequestManager); Assert.ThrowsException(() => executor.Execute()); @@ -273,7 +276,7 @@ public void ExecutorExecuteShouldThrowTestPlatformExceptionThrownDuringExecution mockTestPlatform.Setup(tp => tp.CreateDiscoveryRequest(It.IsAny(), It.IsAny(), It.IsAny())).Returns(mockDiscoveryRequest.Object); this.ResetAndAddSourceToCommandLineOptions(); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object);; var executor = GetExecutor(testRequestManager); executor.Initialize("Test1"); @@ -298,7 +301,7 @@ public void ExecutorExecuteShouldThrowSettingsExceptionThrownDuringExecution() mockTestPlatform.Setup(tp => tp.CreateDiscoveryRequest(It.IsAny(), It.IsAny(), It.IsAny())).Returns(mockDiscoveryRequest.Object); this.ResetAndAddSourceToCommandLineOptions(); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object);; var executor = GetExecutor(testRequestManager); executor.Initialize("Test1"); @@ -324,7 +327,7 @@ public void ExecutorExecuteShouldThrowInvalidOperationExceptionThrownDuringExecu this.ResetAndAddSourceToCommandLineOptions(); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object);; var executor = GetExecutor(testRequestManager); executor.Initialize("Test1"); @@ -345,7 +348,7 @@ public void ExecutorExecuteShouldForValidSourcesAndNoTestsDiscoveredShouldLogWar mockDiscoveryRequest.Setup(dr => dr.DiscoverAsync()).Raises(dr => dr.OnDiscoveredTests += null, new DiscoveredTestsEventArgs(new List())); mockTestPlatform.Setup(tp => tp.CreateDiscoveryRequest(It.IsAny(), It.IsAny(), It.IsAny())).Returns(mockDiscoveryRequest.Object); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object);; var executor = GetExecutor(testRequestManager); executor.Initialize("Test1"); @@ -366,7 +369,7 @@ public void ExecutorExecuteShouldForValidSourcesAndNoTestsDiscoveredShouldLogApp mockDiscoveryRequest.Setup(dr => dr.DiscoverAsync()).Raises(dr => dr.OnDiscoveredTests += null, new DiscoveredTestsEventArgs(new List())); mockTestPlatform.Setup(tp => tp.CreateDiscoveryRequest(It.IsAny(), It.IsAny(), It.IsAny())).Returns(mockDiscoveryRequest.Object); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object);; var executor = GetExecutor(testRequestManager); executor.Initialize("Test1"); @@ -393,7 +396,7 @@ public void ExecutorExecuteShouldForValidSourcesAndValidSelectedTestsRunsTestsAn mockTestPlatform.Setup(tp => tp.CreateTestRunRequest(It.IsAny(), It.IsAny(), It.IsAny())).Returns(mockTestRunRequest.Object); mockTestPlatform.Setup(tp => tp.CreateDiscoveryRequest(It.IsAny(), It.IsAny(), It.IsAny())).Returns(mockDiscoveryRequest.Object); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object);; var executor = GetExecutor(testRequestManager); executor.Initialize("Test1"); @@ -419,7 +422,7 @@ public void ExecutorShouldRunTestsWhenTestsAreCommaSeparated() mockTestPlatform.Setup(tp => tp.CreateTestRunRequest(It.IsAny(), It.IsAny(), It.IsAny())).Returns(mockTestRunRequest.Object); mockTestPlatform.Setup(tp => tp.CreateDiscoveryRequest(It.IsAny(), It.IsAny(), It.IsAny())).Returns(mockDiscoveryRequest.Object); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object);; var executor = GetExecutor(testRequestManager); executor.Initialize("Test1, Test2"); @@ -446,7 +449,7 @@ public void ExecutorShouldRunTestsWhenTestsAreFiltered() mockTestPlatform.Setup(tp => tp.CreateTestRunRequest(It.IsAny(), It.IsAny(), It.IsAny())).Returns(mockTestRunRequest.Object); mockTestPlatform.Setup(tp => tp.CreateDiscoveryRequest(It.IsAny(), It.IsAny(), It.IsAny())).Returns(mockDiscoveryRequest.Object); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object);; var executor = GetExecutor(testRequestManager); executor.Initialize("Test1"); @@ -472,7 +475,7 @@ public void ExecutorShouldWarnWhenTestsAreNotAvailable() mockTestPlatform.Setup(tp => tp.CreateTestRunRequest(It.IsAny(), It.IsAny(), It.IsAny())).Returns(mockTestRunRequest.Object); mockTestPlatform.Setup(tp => tp.CreateDiscoveryRequest(It.IsAny(), It.IsAny(), It.IsAny())).Returns(mockDiscoveryRequest.Object); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object);; var executor = GetExecutor(testRequestManager); executor.Initialize("Test1, Test2"); @@ -499,7 +502,7 @@ public void ExecutorShouldRunTestsWhenTestsAreCommaSeparatedWithEscape() mockTestPlatform.Setup(tp => tp.CreateTestRunRequest(It.IsAny(), It.IsAny(), It.IsAny())).Returns(mockTestRunRequest.Object); mockTestPlatform.Setup(tp => tp.CreateDiscoveryRequest(It.IsAny(), It.IsAny(), It.IsAny())).Returns(mockDiscoveryRequest.Object); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object);; var executor = GetExecutor(testRequestManager); executor.Initialize("Test1(a\\,b), Test2(c\\,d)"); @@ -529,7 +532,7 @@ public void ExecutorShouldDisplayWarningIfNoTestsAreExecuted() mockTestPlatform.Setup(tp => tp.CreateDiscoveryRequest(It.IsAny(), It.IsAny(), It.IsAny())).Returns(mockDiscoveryRequest.Object); this.ResetAndAddSourceToCommandLineOptions(); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object);; var executor = GetExecutor(testRequestManager); executor.Initialize("Test1"); @@ -558,7 +561,7 @@ public void ExecutorShouldNotDisplayWarningIfTestsAreExecuted() mockTestPlatform.Setup(tp => tp.CreateDiscoveryRequest(It.IsAny(), It.IsAny(), It.IsAny())).Returns(mockDiscoveryRequest.Object); this.ResetAndAddSourceToCommandLineOptions(); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object);; var executor = GetExecutor(testRequestManager); executor.Initialize("Test1"); diff --git a/test/vstest.console.UnitTests/Processors/RunTestsArgumentProcessorTests.cs b/test/vstest.console.UnitTests/Processors/RunTestsArgumentProcessorTests.cs index 3143356d41..d3b3814117 100644 --- a/test/vstest.console.UnitTests/Processors/RunTestsArgumentProcessorTests.cs +++ b/test/vstest.console.UnitTests/Processors/RunTestsArgumentProcessorTests.cs @@ -23,6 +23,7 @@ namespace Microsoft.VisualStudio.TestPlatform.CommandLine.UnitTests.Processors using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Tracing.Interfaces; 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.PlatformAbstractions.Interfaces; using Microsoft.VisualStudio.TestPlatform.Utilities; @@ -47,6 +48,7 @@ public class RunTestsArgumentProcessorTests private Task mockMetricsPublisherTask; private Mock mockMetricsPublisher; private Mock mockProcessHelper; + private Mock mockAttachmentsProcessingManager; public RunTestsArgumentProcessorTests() { @@ -64,6 +66,7 @@ public RunTestsArgumentProcessorTests() .Returns(Architecture.X86); this.mockAssemblyMetadataProvider.Setup(x => x.GetFrameWork(It.IsAny())).Returns(new FrameworkName(Constants.DotNetFramework40)); this.mockProcessHelper = new Mock(); + this.mockAttachmentsProcessingManager = new Mock(); } [TestMethod] @@ -109,7 +112,7 @@ public void ExecutorExecuteShouldReturnSuccessWithoutExecutionInDesignMode() CommandLineOptions.Instance.Reset(); CommandLineOptions.Instance.IsDesignMode = true; - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, TestPlatformFactory.GetTestPlatform(), TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, TestPlatformFactory.GetTestPlatform(), TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object); var executor = new RunTestsArgumentExecutor(CommandLineOptions.Instance, runSettingsProvider, testRequestManager, this.mockOutput.Object); Assert.AreEqual(ArgumentProcessorResult.Success, executor.Execute()); @@ -119,7 +122,7 @@ public void ExecutorExecuteShouldReturnSuccessWithoutExecutionInDesignMode() public void ExecutorExecuteForNoSourcesShouldThrowCommandLineException() { CommandLineOptions.Instance.Reset(); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, TestPlatformFactory.GetTestPlatform(), TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, TestPlatformFactory.GetTestPlatform(), TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object); var executor = GetExecutor(testRequestManager); Assert.ThrowsException(() => executor.Execute()); @@ -148,7 +151,7 @@ public void ExecutorExecuteShouldThrowTestPlatformException() mockTestPlatform.Setup(tp => tp.CreateTestRunRequest(It.IsAny(), It.IsAny(), It.IsAny())).Returns(mockTestRunRequest.Object); this.ResetAndAddSourceToCommandLineOptions(); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object); var executor = GetExecutor(testRequestManager); Assert.ThrowsException(() => executor.Execute()); @@ -164,7 +167,7 @@ public void ExecutorExecuteShouldThrowSettingsException() mockTestPlatform.Setup(tp => tp.CreateTestRunRequest(It.IsAny(), It.IsAny(), It.IsAny())).Returns(mockTestRunRequest.Object); this.ResetAndAddSourceToCommandLineOptions(); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object); var executor = GetExecutor(testRequestManager); Assert.ThrowsException(() => executor.Execute()); @@ -180,7 +183,7 @@ public void ExecutorExecuteShouldThrowInvalidOperationException() mockTestPlatform.Setup(tp => tp.CreateTestRunRequest(It.IsAny(), It.IsAny(), It.IsAny())).Returns(mockTestRunRequest.Object); this.ResetAndAddSourceToCommandLineOptions(); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object); var executor = GetExecutor(testRequestManager); Assert.ThrowsException(() => executor.Execute()); @@ -196,7 +199,7 @@ public void ExecutorExecuteShouldThrowOtherExceptions() mockTestPlatform.Setup(tp => tp.CreateTestRunRequest(It.IsAny(), It.IsAny(), It.IsAny())).Returns(mockTestRunRequest.Object); this.ResetAndAddSourceToCommandLineOptions(); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object); var executor = GetExecutor(testRequestManager); Assert.ThrowsException(() => executor.Execute()); @@ -251,7 +254,7 @@ private ArgumentProcessorResult RunRunArgumentProcessorExecuteWithMockSetup(ITes this.ResetAndAddSourceToCommandLineOptions(); - var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object); + var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, this.mockProcessHelper.Object, this.mockAttachmentsProcessingManager.Object); var executor = GetExecutor(testRequestManager); return executor.Execute(); diff --git a/test/vstest.console.UnitTests/TestPlatformHelpers/TestRequestManagerTests.cs b/test/vstest.console.UnitTests/TestPlatformHelpers/TestRequestManagerTests.cs index 6f5bdca8cc..e8f7ba2a30 100644 --- a/test/vstest.console.UnitTests/TestPlatformHelpers/TestRequestManagerTests.cs +++ b/test/vstest.console.UnitTests/TestPlatformHelpers/TestRequestManagerTests.cs @@ -1,9 +1,6 @@ // 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.Utilities; - namespace vstest.console.UnitTests.TestPlatformHelpers { using System; @@ -25,6 +22,7 @@ namespace vstest.console.UnitTests.TestPlatformHelpers using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Runtime.Versioning; @@ -35,8 +33,9 @@ namespace vstest.console.UnitTests.TestPlatformHelpers using vstest.console.UnitTests.TestDoubles; using Microsoft.VisualStudio.TestPlatform.Utilities; using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions.Interfaces; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine; - [TestClass] + [TestClass] public class TestRequestManagerTests { private DummyLoggerEvents mockLoggerEvents; @@ -49,12 +48,11 @@ public class TestRequestManagerTests private InferHelper inferHelper; private ITestRequestManager testRequestManager; private Mock mockTestPlatformEventSource; - private Mock mockRequestData; - private Mock mockMetricsCollection; private ProtocolConfig protocolConfig; private Task mockMetricsPublisherTask; private Mock mockMetricsPublisher; private Mock mockProcessHelper; + private Mock mockAttachmentsProcessingManager; private const string DefaultRunsettings = @" @@ -79,6 +77,7 @@ public TestRequestManagerTests() this.mockMetricsPublisher = new Mock(); this.mockMetricsPublisherTask = Task.FromResult(this.mockMetricsPublisher.Object); + this.mockAttachmentsProcessingManager = new Mock(); this.testRequestManager = new TestRequestManager( this.commandLineOptions, this.mockTestPlatform.Object, @@ -86,10 +85,8 @@ public TestRequestManagerTests() this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, - this.mockProcessHelper.Object); - this.mockMetricsCollection = new Mock(); - this.mockRequestData = new Mock(); - this.mockRequestData.Setup(rd => rd.MetricsCollection).Returns(this.mockMetricsCollection.Object); + this.mockProcessHelper.Object, + this.mockAttachmentsProcessingManager.Object); this.mockTestPlatform.Setup(tp => tp.CreateDiscoveryRequest(It.IsAny(), It.IsAny(), It.IsAny())) .Returns(this.mockDiscoveryRequest.Object); this.mockTestPlatform.Setup(tp => tp.CreateTestRunRequest(It.IsAny(), It.IsAny(), It.IsAny())) @@ -99,7 +96,7 @@ public TestRequestManagerTests() this.mockAssemblyMetadataProvider.Setup(a => a.GetFrameWork(It.IsAny())) .Returns(new FrameworkName(Constants.DotNetFramework40)); this.mockProcessHelper.Setup(x => x.GetCurrentProcessId()).Returns(1234); - this.mockProcessHelper.Setup(x => x.GetProcessName(It.IsAny())).Returns("dotnet.exe"); + this.mockProcessHelper.Setup(x => x.GetProcessName(It.IsAny())).Returns("dotnet.exe"); } [TestCleanup] @@ -122,7 +119,8 @@ public void TestRequestManagerShouldNotInitializeConsoleLoggerIfDesignModeIsSet( new Mock().Object, this.inferHelper, this.mockMetricsPublisherTask, - this.mockProcessHelper.Object); + this.mockProcessHelper.Object, + this.mockAttachmentsProcessingManager.Object); Assert.IsFalse(this.mockLoggerEvents.EventsSubscribed()); } @@ -203,8 +201,9 @@ public void DiscoverTestsShouldCallTestPlatformAndSucceed() TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, - this.mockMetricsPublisherTask, - this.mockProcessHelper.Object); + this.mockMetricsPublisherTask, + this.mockProcessHelper.Object, + this.mockAttachmentsProcessingManager.Object); this.testRequestManager.DiscoverTests(payload, mockDiscoveryRegistrar.Object, this.protocolConfig); @@ -253,7 +252,8 @@ public void DiscoverTestsShouldPassSameProtocolConfigInRequestData() this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, - this.mockProcessHelper.Object); + this.mockProcessHelper.Object, + this.mockAttachmentsProcessingManager.Object); // Act this.testRequestManager.DiscoverTests(payload, mockDiscoveryRegistrar.Object, mockProtocolConfig); @@ -300,7 +300,8 @@ public void DiscoverTestsShouldCollectMetrics() this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, - this.mockProcessHelper.Object); + this.mockProcessHelper.Object, + this.mockAttachmentsProcessingManager.Object); // Act @@ -349,7 +350,8 @@ public void DiscoverTestsShouldCollectTargetDeviceLocalMachineIfTargetDeviceStri this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, - this.mockProcessHelper.Object); + this.mockProcessHelper.Object, + this.mockAttachmentsProcessingManager.Object); // Act @@ -392,7 +394,8 @@ public void DiscoverTestsShouldCollectTargetDeviceIfTargetDeviceIsDevice() this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, - this.mockProcessHelper.Object); + this.mockProcessHelper.Object, + this.mockAttachmentsProcessingManager.Object); // Act @@ -435,7 +438,8 @@ public void DiscoverTestsShouldCollectTargetDeviceIfTargetDeviceIsEmulator() this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, - this.mockProcessHelper.Object); + this.mockProcessHelper.Object, + this.mockAttachmentsProcessingManager.Object); // Act @@ -478,7 +482,8 @@ public void DiscoverTestsShouldCollectCommands() this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, - this.mockProcessHelper.Object); + this.mockProcessHelper.Object, + this.mockAttachmentsProcessingManager.Object); CommandLineOptions.Instance.Parallel = true; CommandLineOptions.Instance.EnableCodeCoverage = true; @@ -533,7 +538,8 @@ public void DiscoverTestsShouldCollectTestSettings() this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, - this.mockProcessHelper.Object); + this.mockProcessHelper.Object, + this.mockAttachmentsProcessingManager.Object); CommandLineOptions.Instance.SettingsFile = @"c://temp/.testsettings"; @@ -580,7 +586,8 @@ public void DiscoverTestsShouldCollectVsmdiFile() this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, - this.mockProcessHelper.Object); + this.mockProcessHelper.Object, + this.mockAttachmentsProcessingManager.Object); CommandLineOptions.Instance.SettingsFile = @"c://temp/.vsmdi"; @@ -627,7 +634,8 @@ public void DiscoverTestsShouldCollectTestRunConfigFile() this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, - this.mockProcessHelper.Object); + this.mockProcessHelper.Object, + this.mockAttachmentsProcessingManager.Object); CommandLineOptions.Instance.SettingsFile = @"c://temp/.testrunConfig"; @@ -949,7 +957,8 @@ public void RunTestsShouldCollectCommands() this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, - this.mockProcessHelper.Object); + this.mockProcessHelper.Object, + this.mockAttachmentsProcessingManager.Object); CommandLineOptions.Instance.Parallel = true; CommandLineOptions.Instance.EnableCodeCoverage = true; @@ -1016,7 +1025,8 @@ public void RunTestsShouldCollectTelemetryForLegacySettings() this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, - this.mockProcessHelper.Object); + this.mockProcessHelper.Object, + this.mockAttachmentsProcessingManager.Object); // Act. this.testRequestManager.RunTests(payload, new Mock().Object, new Mock().Object, mockProtocolConfig); @@ -1065,7 +1075,8 @@ public void RunTestsShouldCollectTelemetryForTestSettingsEmbeddedInsideRunSettin this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, - this.mockProcessHelper.Object); + this.mockProcessHelper.Object, + this.mockAttachmentsProcessingManager.Object); // Act. this.testRequestManager.RunTests(payload, new Mock().Object, new Mock().Object, mockProtocolConfig); @@ -1112,7 +1123,8 @@ public void RunTestsShouldCollectMetrics() this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, - this.mockProcessHelper.Object); + this.mockProcessHelper.Object, + this.mockAttachmentsProcessingManager.Object); // Act. this.testRequestManager.RunTests(payload, new Mock().Object, new Mock().Object, mockProtocolConfig); @@ -1159,7 +1171,8 @@ public void RunTestsWithSourcesShouldCallTestPlatformAndSucceed() this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask, - this.mockProcessHelper.Object); + this.mockProcessHelper.Object, + this.mockAttachmentsProcessingManager.Object); this.testRequestManager.RunTests(payload, mockCustomlauncher.Object, mockRunEventsRegistrar.Object, this.protocolConfig); @@ -2164,6 +2177,102 @@ public void DiscoverTestsShouldOverrideOnlyAssemblyNameIfConsoleLoggerAlreadyPre Assert.IsNotNull(loggerSettingsList[1].CodeBase); } + [TestMethod] + public void ProcessTestRunAttachmentsShouldSucceedWithTelemetryEnabled() + { + var mockEventsHandler = new Mock(); + mockAttachmentsProcessingManager + .Setup(m => m.ProcessTestRunAttachmentsAsync(It.IsAny(), It.IsAny>(), It.IsAny(), It.IsAny())) + .Returns((IRequestData r, ICollection a, ITestRunAttachmentsProcessingEventsHandler h, CancellationToken token) => Task.Run(() => + { + r.MetricsCollection.Add(TelemetryDataConstants.NumberOfAttachmentsSentForProcessing, 5); + r.MetricsCollection.Add(TelemetryDataConstants.NumberOfAttachmentsAfterProcessing, 1); + })); + + var payload = new TestRunAttachmentsProcessingPayload() + { + Attachments = new List { new AttachmentSet(new Uri("http://www.bing.com"), "out") }, + CollectMetrics = true + }; + + testRequestManager.ProcessTestRunAttachments(payload, mockEventsHandler.Object, this.protocolConfig); + + mockAttachmentsProcessingManager.Verify(m => m.ProcessTestRunAttachmentsAsync(It.Is(r => r.IsTelemetryOptedIn), payload.Attachments, mockEventsHandler.Object, It.IsAny())); + mockTestPlatformEventSource.Verify(es => es.TestRunAttachmentsProcessingRequestStart()); + mockTestPlatformEventSource.Verify(es => es.TestRunAttachmentsProcessingRequestStop()); + + mockMetricsPublisher.Verify(p => p.PublishMetrics(TelemetryDataConstants.TestAttachmentsProcessingCompleteEvent, + It.Is>(m => m.Count == 2 && + m.ContainsKey(TelemetryDataConstants.NumberOfAttachmentsSentForProcessing) && (int)m[TelemetryDataConstants.NumberOfAttachmentsSentForProcessing] == 5 && + m.ContainsKey(TelemetryDataConstants.NumberOfAttachmentsAfterProcessing) && (int)m[TelemetryDataConstants.NumberOfAttachmentsAfterProcessing] == 1))); + } + + [TestMethod] + public void ProcessTestRunAttachmentsShouldSucceedWithTelemetryDisabled() + { + var mockEventsHandler = new Mock(); + mockAttachmentsProcessingManager + .Setup(m => m.ProcessTestRunAttachmentsAsync(It.IsAny(), It.IsAny>(), It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(true)); + + var payload = new TestRunAttachmentsProcessingPayload() + { + Attachments = new List { new AttachmentSet(new Uri("http://www.bing.com"), "out") }, + CollectMetrics = false + }; + + testRequestManager.ProcessTestRunAttachments(payload, mockEventsHandler.Object, this.protocolConfig); + + mockAttachmentsProcessingManager.Verify(m => m.ProcessTestRunAttachmentsAsync(It.Is(r => !r.IsTelemetryOptedIn), payload.Attachments, mockEventsHandler.Object, It.IsAny())); + mockTestPlatformEventSource.Verify(es => es.TestRunAttachmentsProcessingRequestStart()); + mockTestPlatformEventSource.Verify(es => es.TestRunAttachmentsProcessingRequestStop()); + } + + [TestMethod] + public async Task CancelTestRunAttachmentsProcessingShouldSucceedIfRequestInProgress() + { + var mockEventsHandler = new Mock(); + mockAttachmentsProcessingManager + .Setup(m => m.ProcessTestRunAttachmentsAsync(It.IsAny(), It.IsAny>(), It.IsAny(), It.IsAny())) + .Returns((IRequestData r, ICollection a, ITestRunAttachmentsProcessingEventsHandler h, CancellationToken token) => Task.Run(() => + { + int i = 0; + while (!token.IsCancellationRequested) + { + i++; + Console.WriteLine($"Iteration {i}"); + Task.Delay(5).Wait(); + } + + r.MetricsCollection.Add(TelemetryDataConstants.AttachmentsProcessingState, "Canceled"); + })); + + var payload = new TestRunAttachmentsProcessingPayload() + { + Attachments = new List { new AttachmentSet(new Uri("http://www.bing.com"), "out") }, + CollectMetrics = true + }; + + Task task = Task.Run(() => testRequestManager.ProcessTestRunAttachments(payload, mockEventsHandler.Object, this.protocolConfig)); + await Task.Delay(50); + testRequestManager.CancelTestRunAttachmentsProcessing(); + + await task; + + mockAttachmentsProcessingManager.Verify(m => m.ProcessTestRunAttachmentsAsync(It.IsAny(), payload.Attachments, mockEventsHandler.Object, It.IsAny())); + mockTestPlatformEventSource.Verify(es => es.TestRunAttachmentsProcessingRequestStart()); + mockTestPlatformEventSource.Verify(es => es.TestRunAttachmentsProcessingRequestStop()); + + mockMetricsPublisher.Verify(p => p.PublishMetrics(TelemetryDataConstants.TestAttachmentsProcessingCompleteEvent, + It.Is>(m => m.Count == 1 && m.ContainsKey(TelemetryDataConstants.AttachmentsProcessingState) && (string)m[TelemetryDataConstants.AttachmentsProcessingState] == "Canceled"))); + } + + [TestMethod] + public void CancelTestRunAttachmentsProcessingShouldSucceedIfNoRequest() + { + testRequestManager.CancelTestRunAttachmentsProcessing(); + } + private static DiscoveryRequestPayload CreateDiscoveryPayload(string runsettings) { var discoveryPayload = new DiscoveryRequestPayload