diff --git a/src/Microsoft.TestPlatform.Common/Interfaces/Engine/IMultiTestRunFinalizationManager.cs b/src/Microsoft.TestPlatform.Common/Interfaces/Engine/IMultiTestRunFinalizationManager.cs index 7a82cd3ccd..b09c6f9339 100644 --- a/src/Microsoft.TestPlatform.Common/Interfaces/Engine/IMultiTestRunFinalizationManager.cs +++ b/src/Microsoft.TestPlatform.Common/Interfaces/Engine/IMultiTestRunFinalizationManager.cs @@ -3,6 +3,7 @@ using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Threading; using System.Threading.Tasks; @@ -14,11 +15,19 @@ namespace Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine internal interface IMultiTestRunFinalizationManager { /// - /// Finalizes multi test run + /// Finalizes multi test run and provides results through handler /// /// Attachments /// EventHandler for handling multi test run finalization events from Engine /// Cancellation token Task FinalizeMultiTestRunAsync(ICollection attachments, IMultiTestRunFinalizationEventsHandler eventHandler, CancellationToken cancellationToken); + + /// + /// Finalizes multi test + /// + /// Attachments + /// EventHandler for handling multi test run finalization events from Engine + /// Cancellation token + Task> FinalizeMultiTestRunAsync(ICollection attachments, CancellationToken cancellationToken); } } diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/DataCollection/ParallelDataCollectionEventsHandler.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/DataCollection/ParallelDataCollectionEventsHandler.cs index aa3e56de62..ed9d614743 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/DataCollection/ParallelDataCollectionEventsHandler.cs +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/DataCollection/ParallelDataCollectionEventsHandler.cs @@ -4,6 +4,7 @@ namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.DataCollection { using System.Collections.Generic; + using System.Collections.ObjectModel; using System.Threading; using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities; using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Interfaces; @@ -58,13 +59,13 @@ public override void HandleTestRunComplete( if (parallelRunComplete) { - finalizationManager.FinalizeMultiTestRunAsync(runDataAggregator.RunContextAttachments, null, cancellationToken).Wait(); + Collection attachments = finalizationManager.FinalizeMultiTestRunAsync(runDataAggregator.RunContextAttachments, cancellationToken).Result; var completedArgs = new TestRunCompleteEventArgs(this.runDataAggregator.GetAggregatedRunStats(), this.runDataAggregator.IsCanceled, this.runDataAggregator.IsAborted, this.runDataAggregator.GetAggregatedException(), - this.runDataAggregator.RunContextAttachments, + attachments, this.runDataAggregator.ElapsedTime); // Add Metrics from Test Host diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/MultiTestRunFinalization/MultiTestRunFinalizationManager.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/MultiTestRunFinalization/MultiTestRunFinalizationManager.cs index 3099fc2ccb..2eb388eeeb 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/MultiTestRunFinalization/MultiTestRunFinalizationManager.cs +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/MultiTestRunFinalization/MultiTestRunFinalizationManager.cs @@ -32,19 +32,25 @@ public MultiTestRunFinalizationManager(ITestPlatformEventSource testPlatformEven this.dataCollectorAttachmentsHandlers = dataCollectorAttachmentsHandlers ?? throw new ArgumentNullException(nameof(dataCollectorAttachmentsHandlers)); } - /// - /// Finalizes multi test run - /// - /// Attachments - /// EventHandler for handling multi test run finalization events from Engine - /// Cancellation token + /// public async Task FinalizeMultiTestRunAsync(ICollection attachments, IMultiTestRunFinalizationEventsHandler eventHandler, CancellationToken cancellationToken) + { + await InternalFinalizeMultiTestRunAsync(new Collection(attachments.ToList()), eventHandler, cancellationToken); + } + + /// + public Task> FinalizeMultiTestRunAsync(ICollection attachments, CancellationToken cancellationToken) + { + return InternalFinalizeMultiTestRunAsync(new Collection(attachments.ToList()), null, cancellationToken); + } + + private async Task> InternalFinalizeMultiTestRunAsync(Collection attachments, IMultiTestRunFinalizationEventsHandler eventHandler, CancellationToken cancellationToken) { try { - cancellationToken.ThrowIfCancellationRequested(); + testPlatformEventSource.MultiTestRunFinalizationStart(attachments?.Count ?? 0); - testPlatformEventSource.MultiTestRunFinalizationStart(attachments.Count); + cancellationToken.ThrowIfCancellationRequested(); var taskCompletionSource = new TaskCompletionSource(); cancellationToken.Register(() => @@ -54,21 +60,26 @@ public async Task FinalizeMultiTestRunAsync(ICollection attachmen Task task = Task.Run(() => { - HandleAttachements(attachments, cancellationToken); + HandleAttachements(attachments, cancellationToken); }); var completedTask = await Task.WhenAny(task, taskCompletionSource.Task); if (completedTask == task) { + await task; eventHandler?.HandleMultiTestRunFinalizationComplete(attachments); testPlatformEventSource.MultiTestRunFinalizationStop(attachments.Count); + return attachments; } else { + eventHandler?.HandleLogMessage(ObjectModel.Logging.TestMessageLevel.Informational, "Finalization was cancelled."); eventHandler?.HandleMultiTestRunFinalizationComplete(null); - testPlatformEventSource.MultiTestRunFinalizationStop(0); + testPlatformEventSource.MultiTestRunFinalizationStop(0); } + + return null; } catch (Exception e) { @@ -77,6 +88,7 @@ public async Task FinalizeMultiTestRunAsync(ICollection attachmen eventHandler?.HandleLogMessage(ObjectModel.Logging.TestMessageLevel.Error, e.Message); eventHandler?.HandleMultiTestRunFinalizationComplete(null); testPlatformEventSource.MultiTestRunFinalizationStop(0); + return null; } } diff --git a/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/MultiTestRunFinalization/MultiTestRunFinalizationManagerTests.cs b/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/MultiTestRunFinalization/MultiTestRunFinalizationManagerTests.cs new file mode 100644 index 0000000000..5a681f38c4 --- /dev/null +++ b/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/MultiTestRunFinalization/MultiTestRunFinalizationManagerTests.cs @@ -0,0 +1,478 @@ +// 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.MultiTestRunFinalization +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading; + using System.Threading.Tasks; + + using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Tracing.Interfaces; + using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.MultiTestRunFinalization; + 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 MultiTestRunFinalizationManagerTests + { + 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 mockEventSource; + private readonly Mock mockAttachmentHandler1; + private readonly Mock mockAttachmentHandler2; + private readonly Mock mockEventsHandler; + private readonly MultiTestRunFinalizationManager manager; + private readonly CancellationTokenSource cancellationTokenSource; + + public MultiTestRunFinalizationManagerTests() + { + mockEventSource = new Mock(); + mockAttachmentHandler1 = new Mock(); + mockAttachmentHandler2 = new Mock(); + mockEventsHandler = new Mock(); + + mockAttachmentHandler1.Setup(h => h.GetExtensionUri()).Returns(new Uri(uri1)); + mockAttachmentHandler2.Setup(h => h.GetExtensionUri()).Returns(new Uri(uri2)); + + manager = new MultiTestRunFinalizationManager(mockEventSource.Object, mockAttachmentHandler1.Object, mockAttachmentHandler2.Object); + + cancellationTokenSource = new CancellationTokenSource(); + } + + [TestMethod] + public async Task FinalizeMultiTestRunAsync_ShouldReturnNoAttachmentsThroughEventsHandler_IfNoAttachmentsOnInput() + { + // arrange + List inputAttachments = new List(); + + // act + await manager.FinalizeMultiTestRunAsync(inputAttachments, mockEventsHandler.Object, cancellationTokenSource.Token); + + // assert + mockEventsHandler.Verify(h => h.HandleMultiTestRunFinalizationComplete(It.Is>(c => c.Count == 0))); + mockEventsHandler.Verify(h => h.HandleLogMessage(It.IsAny(), It.IsAny()), Times.Never); + mockEventSource.Verify(s => s.MultiTestRunFinalizationStart(0)); + mockEventSource.Verify(s => s.MultiTestRunFinalizationStop(0)); + mockAttachmentHandler1.Verify(h => h.GetExtensionUri()); + mockAttachmentHandler2.Verify(h => h.GetExtensionUri()); + mockAttachmentHandler1.Verify(h => h.HandleDataCollectionAttachmentSets(It.IsAny>(), It.IsAny()), Times.Never); + mockAttachmentHandler2.Verify(h => h.HandleDataCollectionAttachmentSets(It.IsAny>(), It.IsAny()), Times.Never); + } + + [TestMethod] + public async Task FinalizeMultiTestRunAsync_ShouldReturnNoAttachments_IfNoAttachmentsOnInput() + { + // arrange + List inputAttachments = new List(); + + // act + var result = await manager.FinalizeMultiTestRunAsync(inputAttachments, cancellationTokenSource.Token); + + // assert + Assert.AreEqual(0, result.Count); + mockEventSource.Verify(s => s.MultiTestRunFinalizationStart(0)); + mockEventSource.Verify(s => s.MultiTestRunFinalizationStop(0)); + mockAttachmentHandler1.Verify(h => h.GetExtensionUri()); + mockAttachmentHandler2.Verify(h => h.GetExtensionUri()); + mockAttachmentHandler1.Verify(h => h.HandleDataCollectionAttachmentSets(It.IsAny>(), It.IsAny()), Times.Never); + mockAttachmentHandler2.Verify(h => h.HandleDataCollectionAttachmentSets(It.IsAny>(), It.IsAny()), Times.Never); + } + + [TestMethod] + public async Task FinalizeMultiTestRunAsync_ShouldReturn1NotProcessedAttachmentThroughEventsHandler_If1NotRelatedAttachmentOnInput() + { + // arrange + List inputAttachments = new List + { + new AttachmentSet(new Uri(uri3), "uri3_input") + }; + + // act + await manager.FinalizeMultiTestRunAsync(inputAttachments, mockEventsHandler.Object, cancellationTokenSource.Token); + + // assert + mockEventsHandler.Verify(h => h.HandleMultiTestRunFinalizationComplete(It.Is>(c => c.Count == 1 && c.Contains(inputAttachments[0])))); + mockEventsHandler.Verify(h => h.HandleLogMessage(It.IsAny(), It.IsAny()), Times.Never); + mockEventSource.Verify(s => s.MultiTestRunFinalizationStart(1)); + mockEventSource.Verify(s => s.MultiTestRunFinalizationStop(1)); + mockAttachmentHandler1.Verify(h => h.GetExtensionUri()); + mockAttachmentHandler2.Verify(h => h.GetExtensionUri()); + mockAttachmentHandler1.Verify(h => h.HandleDataCollectionAttachmentSets(It.IsAny>(), It.IsAny()), Times.Never); + mockAttachmentHandler2.Verify(h => h.HandleDataCollectionAttachmentSets(It.IsAny>(), It.IsAny()), Times.Never); + } + + [TestMethod] + public async Task FinalizeMultiTestRunAsync_ShouldReturn1NotProcessedAttachment_If1NotRelatedAttachmentOnInput() + { + // arrange + List inputAttachments = new List + { + new AttachmentSet(new Uri(uri3), "uri3_input") + }; + + // act + var result = await manager.FinalizeMultiTestRunAsync(inputAttachments, cancellationTokenSource.Token); + + // assert + Assert.AreEqual(1, result.Count); + Assert.IsTrue(result.Contains(inputAttachments[0])); + mockEventSource.Verify(s => s.MultiTestRunFinalizationStart(1)); + mockEventSource.Verify(s => s.MultiTestRunFinalizationStop(1)); + mockAttachmentHandler1.Verify(h => h.GetExtensionUri()); + mockAttachmentHandler2.Verify(h => h.GetExtensionUri()); + mockAttachmentHandler1.Verify(h => h.HandleDataCollectionAttachmentSets(It.IsAny>(), It.IsAny()), Times.Never); + mockAttachmentHandler2.Verify(h => h.HandleDataCollectionAttachmentSets(It.IsAny>(), It.IsAny()), Times.Never); + } + + [TestMethod] + public async Task FinalizeMultiTestRunAsync_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.HandleDataCollectionAttachmentSets(It.IsAny>(), It.IsAny())).Returns(outputAttachments); + + // act + await manager.FinalizeMultiTestRunAsync(inputAttachments, mockEventsHandler.Object, cancellationTokenSource.Token); + + // assert + mockEventsHandler.Verify(h => h.HandleMultiTestRunFinalizationComplete(It.Is>(c => c.Count == 1 && c.Contains(outputAttachments[0])))); + mockEventsHandler.Verify(h => h.HandleLogMessage(It.IsAny(), It.IsAny()), Times.Never); + mockEventSource.Verify(s => s.MultiTestRunFinalizationStart(1)); + mockEventSource.Verify(s => s.MultiTestRunFinalizationStop(1)); + mockAttachmentHandler1.Verify(h => h.GetExtensionUri()); + mockAttachmentHandler2.Verify(h => h.GetExtensionUri()); + mockAttachmentHandler1.Verify(h => h.HandleDataCollectionAttachmentSets(It.Is>(c => c.Count == 1 && c.Contains(inputAttachments[0])), cancellationTokenSource.Token)); + mockAttachmentHandler2.Verify(h => h.HandleDataCollectionAttachmentSets(It.IsAny>(), It.IsAny()), Times.Never); + } + + [TestMethod] + public async Task FinalizeMultiTestRunAsync_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.HandleDataCollectionAttachmentSets(It.IsAny>(), It.IsAny())).Returns(outputAttachments); + + // act + var result = await manager.FinalizeMultiTestRunAsync(inputAttachments, cancellationTokenSource.Token); + + // assert + Assert.AreEqual(1, result.Count); + Assert.IsTrue(result.Contains(outputAttachments[0])); + mockEventSource.Verify(s => s.MultiTestRunFinalizationStart(1)); + mockEventSource.Verify(s => s.MultiTestRunFinalizationStop(1)); + mockAttachmentHandler1.Verify(h => h.GetExtensionUri()); + mockAttachmentHandler2.Verify(h => h.GetExtensionUri()); + mockAttachmentHandler1.Verify(h => h.HandleDataCollectionAttachmentSets(It.Is>(c => c.Count == 1 && c.Contains(inputAttachments[0])), cancellationTokenSource.Token)); + mockAttachmentHandler2.Verify(h => h.HandleDataCollectionAttachmentSets(It.IsAny>(), It.IsAny()), Times.Never); + } + + [TestMethod] + public async Task FinalizeMultiTestRunAsync_ShouldReturnNullThroughEventsHandler_IfRelatedAttachmentOnInputButHandlerThrowsException() + { + // arrange + List inputAttachments = new List + { + new AttachmentSet(new Uri(uri1), "uri1_input") + }; + + mockAttachmentHandler1.Setup(h => h.HandleDataCollectionAttachmentSets(It.IsAny>(), It.IsAny())).Throws(new Exception("exception message")); + + // act + await manager.FinalizeMultiTestRunAsync(inputAttachments, mockEventsHandler.Object, cancellationTokenSource.Token); + + // assert + mockEventsHandler.Verify(h => h.HandleMultiTestRunFinalizationComplete(null)); + mockEventsHandler.Verify(h => h.HandleLogMessage(TestMessageLevel.Error, "exception message"), Times.Once); + mockEventSource.Verify(s => s.MultiTestRunFinalizationStart(1)); + mockEventSource.Verify(s => s.MultiTestRunFinalizationStop(0)); + mockAttachmentHandler1.Verify(h => h.GetExtensionUri()); + mockAttachmentHandler2.Verify(h => h.GetExtensionUri(), Times.Never); + mockAttachmentHandler1.Verify(h => h.HandleDataCollectionAttachmentSets(It.Is>(c => c.Count == 1 && c.Contains(inputAttachments[0])), cancellationTokenSource.Token)); + mockAttachmentHandler2.Verify(h => h.HandleDataCollectionAttachmentSets(It.IsAny>(), It.IsAny()), Times.Never); + } + + [TestMethod] + public async Task FinalizeMultiTestRunAsync_ShouldReturnNull_IfRelatedAttachmentOnInputButHandlerThrowsException() + { + // arrange + List inputAttachments = new List + { + new AttachmentSet(new Uri(uri1), "uri1_input") + }; + + mockAttachmentHandler1.Setup(h => h.HandleDataCollectionAttachmentSets(It.IsAny>(), It.IsAny())).Throws(new Exception("exception message")); + + // act + var result = await manager.FinalizeMultiTestRunAsync(inputAttachments, cancellationTokenSource.Token); + + // assert + Assert.IsNull(result); + mockEventSource.Verify(s => s.MultiTestRunFinalizationStart(1)); + mockEventSource.Verify(s => s.MultiTestRunFinalizationStop(0)); + mockAttachmentHandler1.Verify(h => h.GetExtensionUri()); + mockAttachmentHandler2.Verify(h => h.GetExtensionUri(), Times.Never); + mockAttachmentHandler1.Verify(h => h.HandleDataCollectionAttachmentSets(It.Is>(c => c.Count == 1 && c.Contains(inputAttachments[0])), cancellationTokenSource.Token)); + mockAttachmentHandler2.Verify(h => h.HandleDataCollectionAttachmentSets(It.IsAny>(), It.IsAny()), Times.Never); + } + + [TestMethod] + public async Task FinalizeMultiTestRunAsync_ShouldReturnNullThroughEventsHandler_IfOperationIsCancelled() + { + // arrange + cancellationTokenSource.Cancel(); + List inputAttachments = new List + { + new AttachmentSet(new Uri(uri1), "uri1_input") + }; + + // act + await manager.FinalizeMultiTestRunAsync(inputAttachments, mockEventsHandler.Object, cancellationTokenSource.Token); + + // assert + mockEventsHandler.Verify(h => h.HandleMultiTestRunFinalizationComplete(null)); + mockEventsHandler.Verify(h => h.HandleLogMessage(TestMessageLevel.Error, "The operation was canceled."), Times.Once); + mockEventSource.Verify(s => s.MultiTestRunFinalizationStart(1)); + mockEventSource.Verify(s => s.MultiTestRunFinalizationStop(0)); + mockAttachmentHandler1.Verify(h => h.GetExtensionUri(), Times.Never); + mockAttachmentHandler2.Verify(h => h.GetExtensionUri(), Times.Never); + mockAttachmentHandler1.Verify(h => h.HandleDataCollectionAttachmentSets(It.IsAny>(), It.IsAny()), Times.Never); + mockAttachmentHandler2.Verify(h => h.HandleDataCollectionAttachmentSets(It.IsAny>(), It.IsAny()), Times.Never); + } + + [TestMethod] + public async Task FinalizeMultiTestRunAsync_ShouldReturnNull_IfOperationIsCancelled() + { + // arrange + cancellationTokenSource.Cancel(); + List inputAttachments = new List + { + new AttachmentSet(new Uri(uri1), "uri1_input") + }; + + // act + var result = await manager.FinalizeMultiTestRunAsync(inputAttachments, cancellationTokenSource.Token); + + // assert + Assert.IsNull(result); + mockEventSource.Verify(s => s.MultiTestRunFinalizationStart(1)); + mockEventSource.Verify(s => s.MultiTestRunFinalizationStop(0)); + mockAttachmentHandler1.Verify(h => h.GetExtensionUri(), Times.Never); + mockAttachmentHandler2.Verify(h => h.GetExtensionUri(), Times.Never); + mockAttachmentHandler1.Verify(h => h.HandleDataCollectionAttachmentSets(It.IsAny>(), It.IsAny()), Times.Never); + mockAttachmentHandler2.Verify(h => h.HandleDataCollectionAttachmentSets(It.IsAny>(), It.IsAny()), Times.Never); + } + + [TestMethod] + public async Task FinalizeMultiTestRunAsync_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.HandleDataCollectionAttachmentSets(It.IsAny>(), It.IsAny())).Returns(outputAttachmentsForHandler1); + mockAttachmentHandler2.Setup(h => h.HandleDataCollectionAttachmentSets(It.IsAny>(), It.IsAny())).Returns(outputAttachmentsForHandler2); + + // act + await manager.FinalizeMultiTestRunAsync(inputAttachments, mockEventsHandler.Object, cancellationTokenSource.Token); + + // assert + mockEventsHandler.Verify(h => h.HandleMultiTestRunFinalizationComplete(It.Is>( + c => c.Count == 3 && + c.Contains(inputAttachments[4]) && + c.Contains(outputAttachmentsForHandler1.First()) && + c.Contains(outputAttachmentsForHandler2.First())))); + mockEventsHandler.Verify(h => h.HandleLogMessage(It.IsAny(), It.IsAny()), Times.Never); + mockEventSource.Verify(s => s.MultiTestRunFinalizationStart(5)); + mockEventSource.Verify(s => s.MultiTestRunFinalizationStop(3)); + mockAttachmentHandler1.Verify(h => h.GetExtensionUri()); + mockAttachmentHandler2.Verify(h => h.GetExtensionUri()); + mockAttachmentHandler1.Verify(h => h.HandleDataCollectionAttachmentSets(It.Is>(c => c.Count == 2 && c.Contains(inputAttachments[0]) && c.Contains(inputAttachments[1])), cancellationTokenSource.Token)); + mockAttachmentHandler2.Verify(h => h.HandleDataCollectionAttachmentSets(It.Is>(c => c.Count == 2 && c.Contains(inputAttachments[2]) && c.Contains(inputAttachments[3])), cancellationTokenSource.Token)); + } + + [TestMethod] + public async Task FinalizeMultiTestRunAsync_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.HandleDataCollectionAttachmentSets(It.IsAny>(), It.IsAny())).Returns(outputAttachmentsForHandler1); + mockAttachmentHandler2.Setup(h => h.HandleDataCollectionAttachmentSets(It.IsAny>(), It.IsAny())).Returns(outputAttachmentsForHandler2); + + // act + var result = await manager.FinalizeMultiTestRunAsync(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])); + mockEventSource.Verify(s => s.MultiTestRunFinalizationStart(5)); + mockEventSource.Verify(s => s.MultiTestRunFinalizationStop(3)); + mockAttachmentHandler1.Verify(h => h.GetExtensionUri()); + mockAttachmentHandler2.Verify(h => h.GetExtensionUri()); + mockAttachmentHandler1.Verify(h => h.HandleDataCollectionAttachmentSets(It.Is>(c => c.Count == 2 && c.Contains(inputAttachments[0]) && c.Contains(inputAttachments[1])), cancellationTokenSource.Token)); + mockAttachmentHandler2.Verify(h => h.HandleDataCollectionAttachmentSets(It.Is>(c => c.Count == 2 && c.Contains(inputAttachments[2]) && c.Contains(inputAttachments[3])), cancellationTokenSource.Token)); + } + + [TestMethod] + public async Task FinalizeMultiTestRunAsync_ShouldReturnNullThroughEventsHandler_IfOperationCancelled() + { + // arrange + List inputAttachments = new List + { + new AttachmentSet(new Uri(uri1), "uri1_input") + }; + + List outputAttachments = new List + { + new AttachmentSet(new Uri(uri1), "uri1_output") + }; + + var innerTaskCompletionSource = new TaskCompletionSource(); + + mockAttachmentHandler1.Setup(h => h.HandleDataCollectionAttachmentSets(It.IsAny>(), It.IsAny())).Returns((ICollection i1, CancellationToken cancellation) => + { + for (int i = 0; i < 1000; ++i) + { + Task.Delay(100); + Console.WriteLine($"Iteration: {i}"); + + if (cancellation.IsCancellationRequested) break; + + if (i == 3) + { + cancellationTokenSource.Cancel(); + } + } + + innerTaskCompletionSource.TrySetResult(null); + return outputAttachments; + }); + + // act + await manager.FinalizeMultiTestRunAsync(inputAttachments, mockEventsHandler.Object, cancellationTokenSource.Token); + Console.WriteLine("Finalization done"); + await innerTaskCompletionSource.Task; + + // assert + mockEventsHandler.Verify(h => h.HandleMultiTestRunFinalizationComplete(null)); + mockEventsHandler.Verify(h => h.HandleLogMessage(TestMessageLevel.Informational, "Finalization was cancelled.")); + mockEventSource.Verify(s => s.MultiTestRunFinalizationStart(1)); + mockEventSource.Verify(s => s.MultiTestRunFinalizationStop(0)); + mockAttachmentHandler1.Verify(h => h.GetExtensionUri()); + mockAttachmentHandler2.Verify(h => h.GetExtensionUri(), Times.Never); + mockAttachmentHandler1.Verify(h => h.HandleDataCollectionAttachmentSets(It.Is>(c => c.Count == 1 && c.Contains(inputAttachments[0])), cancellationTokenSource.Token)); + mockAttachmentHandler2.Verify(h => h.HandleDataCollectionAttachmentSets(It.IsAny>(), It.IsAny()), Times.Never); + } + + [TestMethod] + public async Task FinalizeMultiTestRunAsync_ShouldReturnNull_IfOperationCancelled() + { + // arrange + List inputAttachments = new List + { + new AttachmentSet(new Uri(uri1), "uri1_input") + }; + + List outputAttachments = new List + { + new AttachmentSet(new Uri(uri1), "uri1_output") + }; + + var innerTaskCompletionSource = new TaskCompletionSource(); + + mockAttachmentHandler1.Setup(h => h.HandleDataCollectionAttachmentSets(It.IsAny>(), It.IsAny())).Returns((ICollection i1, CancellationToken cancellation) => + { + for (int i = 0; i < 1000; ++i) + { + Task.Delay(100); + Console.WriteLine($"Iteration: {i}"); + + if (cancellation.IsCancellationRequested) break; + + if (i == 3) + { + cancellationTokenSource.Cancel(); + } + } + + innerTaskCompletionSource.TrySetResult(null); + return outputAttachments; + }); + + // act + var result = await manager.FinalizeMultiTestRunAsync(inputAttachments, cancellationTokenSource.Token); + Console.WriteLine("Finalization done"); + await innerTaskCompletionSource.Task; + + // assert + Assert.IsNull(result); + mockEventSource.Verify(s => s.MultiTestRunFinalizationStart(1)); + mockEventSource.Verify(s => s.MultiTestRunFinalizationStop(0)); + mockAttachmentHandler1.Verify(h => h.GetExtensionUri()); + mockAttachmentHandler2.Verify(h => h.GetExtensionUri(), Times.Never); + mockAttachmentHandler1.Verify(h => h.HandleDataCollectionAttachmentSets(It.Is>(c => c.Count == 1 && c.Contains(inputAttachments[0])), cancellationTokenSource.Token)); + mockAttachmentHandler2.Verify(h => h.HandleDataCollectionAttachmentSets(It.IsAny>(), It.IsAny()), Times.Never); + } + } +}