diff --git a/src/Microsoft.TestPlatform.Common/DataCollection/DataCollectionManager.cs b/src/Microsoft.TestPlatform.Common/DataCollection/DataCollectionManager.cs index 2abe4eaf27..8ad89f443a 100644 --- a/src/Microsoft.TestPlatform.Common/DataCollection/DataCollectionManager.cs +++ b/src/Microsoft.TestPlatform.Common/DataCollection/DataCollectionManager.cs @@ -340,12 +340,32 @@ protected virtual void Dispose(bool disposing) { if (disposing) { + CleanupPlugins(); } this.disposed = true; } } + private void CleanupPlugins() + { + EqtTrace.Info("DataCollectionManager.CleanupPlugins: CleanupPlugins called"); + + if (!this.isDataCollectionEnabled) + { + return; + } + + if (EqtTrace.IsVerboseEnabled) + { + EqtTrace.Verbose("DataCollectionManager.CleanupPlugins: Cleaning up {0} plugins", this.RunDataCollectors.Count); + } + + RemoveDataCollectors(new List(this.RunDataCollectors.Values)); + + EqtTrace.Info("DataCollectionManager.CleanupPlugins: CleanupPlugins finished"); + } + #region Load and Initialize DataCollectors /// diff --git a/src/Microsoft.TestPlatform.CommunicationUtilities/DataCollectionRequestHandler.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/DataCollectionRequestHandler.cs index f62de06abb..ba4aa8c356 100644 --- a/src/Microsoft.TestPlatform.CommunicationUtilities/DataCollectionRequestHandler.cs +++ b/src/Microsoft.TestPlatform.CommunicationUtilities/DataCollectionRequestHandler.cs @@ -168,140 +168,29 @@ public void ProcessRequests() { var message = this.communicationManager.ReceiveMessage(); - if (EqtTrace.IsVerboseEnabled) + if (EqtTrace.IsInfoEnabled) { - EqtTrace.Verbose("DataCollectionRequestHandler.ProcessRequests : Datacollector received message: {0}", message); + EqtTrace.Info("DataCollectionRequestHandler.ProcessRequests : Datacollector received message: {0}", message); } switch (message.MessageType) { case MessageType.BeforeTestRunStart: - if (EqtTrace.IsInfoEnabled) - { - EqtTrace.Info("DataCollectionRequestHandler.ProcessRequests : DataCollection starting."); - } - - // Initialize datacollectors and get enviornment variables. - var settingXml = this.dataSerializer.DeserializePayload(message); - this.AddExtensionAssemblies(settingXml); - - var envVariables = this.dataCollectionManager.InitializeDataCollectors(settingXml); - var areTestCaseLevelEventsRequired = this.dataCollectionManager.SessionStarted(); - - // Open a socket communication port for test level events. - var testCaseEventsPort = 0; - if (areTestCaseLevelEventsRequired) - { - testCaseEventsPort = this.dataCollectionTestCaseEventHandler.InitializeCommunication(); - - this.testCaseEventMonitorTask = Task.Factory.StartNew( - () => - { - try - { - if ( - this.dataCollectionTestCaseEventHandler.WaitForRequestHandlerConnection( - DataCollectionCommTimeOut)) - { - this.dataCollectionTestCaseEventHandler.ProcessRequests(); - } - else - { - if (EqtTrace.IsInfoEnabled) - { - EqtTrace.Info( - "DataCollectionRequestHandler.ProcessRequests: TestCaseEventHandler timed out while connecting to the Sender."); - } - - this.dataCollectionTestCaseEventHandler.Close(); - throw new TimeoutException(); - } - } - catch (Exception e) - { - if (EqtTrace.IsErrorEnabled) - { - EqtTrace.Error( - "DataCollectionRequestHandler.ProcessRequests : Error occured during initialization of TestHost : {0}", - e); - } - } - }, - this.cancellationTokenSource.Token); - } - - this.communicationManager.SendMessage( - MessageType.BeforeTestRunStartResult, - new BeforeTestRunStartResult(envVariables, testCaseEventsPort)); - if (EqtTrace.IsInfoEnabled) - { - EqtTrace.Info("DataCollectionRequestHandler.ProcessRequests : DataCollection started."); - } - + this.HandleBeforeTestRunStart(message); break; case MessageType.AfterTestRunEnd: - if (EqtTrace.IsInfoEnabled) - { - EqtTrace.Info("DataCollection completing."); - } - - var isCancelled = this.dataSerializer.DeserializePayload(message); - - if (isCancelled) - { - this.cancellationTokenSource.Cancel(); - } - - try - { - this.testCaseEventMonitorTask?.Wait(this.cancellationTokenSource.Token); - this.dataCollectionTestCaseEventHandler.Close(); - } - catch (Exception ex) - { - if (EqtTrace.IsErrorEnabled) - { - EqtTrace.Error("DataCollectionRequestHandler.ProcessRequests : {0}", ex.ToString()); - } - } - - var attachmentsets = this.dataCollectionManager.SessionEnded(isCancelled); - this.communicationManager.SendMessage(MessageType.AfterTestRunEndResult, attachmentsets); - if (EqtTrace.IsInfoEnabled) - { - EqtTrace.Info( - "DataCollectionRequestHandler.ProcessRequests : Session End message received from server. Closing the connection."); - } - + this.HandleAfterTestRunEnd(message); isSessionEnded = true; - this.Close(); - - if (EqtTrace.IsInfoEnabled) - { - EqtTrace.Info("DataCollectionRequestHandler.ProcessRequests : DataCollection completed"); - } - break; case MessageType.TestHostLaunched: - if (EqtTrace.IsInfoEnabled) - { - EqtTrace.Info("DataCollectionRequestHandler.ProcessRequests : Test host initialized."); - } - var testHostLaunchedPayload = this.dataSerializer.DeserializePayload(message); - this.dataCollectionManager.TestHostLaunched(testHostLaunchedPayload.ProcessId); - break; default: - if (EqtTrace.IsInfoEnabled) - { - EqtTrace.Info("DataCollectionRequestHandler.ProcessRequests : Invalid Message types"); - } - + EqtTrace.Error("DataCollectionRequestHandler.ProcessRequests : Invalid Message types: {0}", message.MessageType); break; } } @@ -325,7 +214,6 @@ public void SendDataCollectionMessage(DataCollectionMessageEventArgs args) public void Dispose() { this.communicationManager?.StopClient(); - this.dataCollectionManager?.Dispose(); } /// @@ -385,5 +273,85 @@ private void AddExtensionAssemblies(string runSettings) } } } + + private void HandleBeforeTestRunStart(Message message) + { + // Initialize datacollectors and get enviornment variables. + var settingXml = this.dataSerializer.DeserializePayload(message); + this.AddExtensionAssemblies(settingXml); + + var envVariables = this.dataCollectionManager.InitializeDataCollectors(settingXml); + var areTestCaseLevelEventsRequired = this.dataCollectionManager.SessionStarted(); + + // Open a socket communication port for test level events. + var testCaseEventsPort = 0; + if (areTestCaseLevelEventsRequired) + { + testCaseEventsPort = this.dataCollectionTestCaseEventHandler.InitializeCommunication(); + + this.testCaseEventMonitorTask = Task.Factory.StartNew( + () => + { + try + { + if (this.dataCollectionTestCaseEventHandler.WaitForRequestHandlerConnection( + DataCollectionCommTimeOut)) + { + this.dataCollectionTestCaseEventHandler.ProcessRequests(); + } + else + { + EqtTrace.Error("DataCollectionRequestHandler.ProcessRequests: TestCaseEventHandler timed out while connecting to the Sender."); + this.dataCollectionTestCaseEventHandler.Close(); + throw new TimeoutException(); + } + } + catch (Exception e) + { + EqtTrace.Error("DataCollectionRequestHandler.ProcessRequests : Error occured during initialization of TestHost : {0}", e); + } + }, + this.cancellationTokenSource.Token); + } + + this.communicationManager.SendMessage( + MessageType.BeforeTestRunStartResult, + new BeforeTestRunStartResult(envVariables, testCaseEventsPort)); + + EqtTrace.Info("DataCollectionRequestHandler.ProcessRequests : DataCollection started."); + } + + private void HandleAfterTestRunEnd(Message message) + { + var isCancelled = this.dataSerializer.DeserializePayload(message); + + if (isCancelled) + { + this.cancellationTokenSource.Cancel(); + } + + try + { + this.testCaseEventMonitorTask?.Wait(this.cancellationTokenSource.Token); + this.dataCollectionTestCaseEventHandler.Close(); + } + catch (Exception ex) + { + EqtTrace.Error("DataCollectionRequestHandler.ProcessRequests : {0}", ex.ToString()); + } + + var attachmentsets = this.dataCollectionManager.SessionEnded(isCancelled); + + // Dispose all datacollectors before sending attachements to vstest.console process. + // As datacollector process exits itself on parent process(vstest.console) exits. + this.dataCollectionManager?.Dispose(); + + this.communicationManager.SendMessage(MessageType.AfterTestRunEndResult, attachmentsets); + EqtTrace.Info("DataCollectionRequestHandler.ProcessRequests : Session End message received from server. Closing the connection."); + + this.Close(); + + EqtTrace.Info("DataCollectionRequestHandler.ProcessRequests : DataCollection completed"); + } } } \ No newline at end of file diff --git a/test/Microsoft.TestPlatform.AcceptanceTests/DataCollectionTests.cs b/test/Microsoft.TestPlatform.AcceptanceTests/DataCollectionTests.cs index ac9443689f..61a6984616 100644 --- a/test/Microsoft.TestPlatform.AcceptanceTests/DataCollectionTests.cs +++ b/test/Microsoft.TestPlatform.AcceptanceTests/DataCollectionTests.cs @@ -121,6 +121,7 @@ private void VaildateDataCollectorOutput() this.StdOutputContains("Data collector 'SampleDataCollector' message: SessionEnded"); this.StdOutputContains("Data collector 'SampleDataCollector' message: my warning"); this.StdErrorContains("Data collector 'SampleDataCollector' message: Data collector caught an exception of type 'System.Exception': 'my exception'. More details:"); + this.StdOutputContains("Data collector 'SampleDataCollector' message: Dispose called."); // Verify attachments var isTestRunLevelAttachmentFound = false; diff --git a/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/DataCollectionRequestHandlerTests.cs b/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/DataCollectionRequestHandlerTests.cs index a05c601e4b..1effc2d0b8 100644 --- a/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/DataCollectionRequestHandlerTests.cs +++ b/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/DataCollectionRequestHandlerTests.cs @@ -193,6 +193,16 @@ public void ProcessRequestsShouldProcessRequests() this.mockCommunicationManager.Verify(x => x.SendMessage(MessageType.AfterTestRunEndResult, It.IsAny>()), Times.Once); } + [TestMethod] + public void ProcessRequestsShouldDisposeDataCollectorsOnAfterTestRunEnd() + { + this.mockCommunicationManager.SetupSequence(x => x.ReceiveMessage()).Returns(new Message() { MessageType = MessageType.AfterTestRunEnd, Payload = "false" }); + + this.requestHandler.ProcessRequests(); + + this.mockDataCollectionManager.Verify(x => x.Dispose()); + } + [TestMethod] public void ProcessRequestsShouldThrowExceptionIfThrownByCommunicationManager() { diff --git a/test/TestAssets/OutOfProcDataCollector/SampleDataCollector.cs b/test/TestAssets/OutOfProcDataCollector/SampleDataCollector.cs index ff4a3b69f5..0422dad3ee 100644 --- a/test/TestAssets/OutOfProcDataCollector/SampleDataCollector.cs +++ b/test/TestAssets/OutOfProcDataCollector/SampleDataCollector.cs @@ -77,5 +77,10 @@ public IEnumerable> GetTestExecutionEnvironmentVari { return new List> { new KeyValuePair("key", "value") }; } + + protected override void Dispose(bool disposing) + { + this.logger.LogWarning(this.context.SessionDataCollectionContext, "Dispose called."); + } } } \ No newline at end of file