diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/VsTestConsoleProcessManager.cs b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/VsTestConsoleProcessManager.cs index 8cbd290e01..56b3f041cd 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/VsTestConsoleProcessManager.cs +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/VsTestConsoleProcessManager.cs @@ -9,10 +9,10 @@ namespace Microsoft.TestPlatform.VsTestConsole.TranslationLayer using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers; using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces; using System; - using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; + using System.Threading; using Resources = Microsoft.VisualStudio.TestPlatform.VsTestConsole.TranslationLayer.Resources.Resources; /// @@ -39,6 +39,11 @@ internal class VsTestConsoleProcessManager : IProcessManager /// private const string DIAG_ARGUMENT = "/diag:{0};tracelevel={1}"; + /// + /// EndSession timeout + /// + private const int ENDSESSIONTIMEOUT = 1000; + private string vstestConsolePath; private object syncObject = new object(); private bool vstestConsoleStarted = false; @@ -46,6 +51,7 @@ internal class VsTestConsoleProcessManager : IProcessManager private readonly bool isNetCoreRunner; private string dotnetExePath; private Process process; + private ManualResetEvent processExitedEvent = new ManualResetEvent(false); internal IFileHelper FileHelper { get; set; } @@ -143,15 +149,16 @@ public void StartProcess(ConsoleParameters consoleParameters) public void ShutdownProcess() { // Ideally process should die by itself - if (IsProcessInitialized()) + if(!processExitedEvent.WaitOne(ENDSESSIONTIMEOUT) && IsProcessInitialized()) { + EqtTrace.Info($"VsTestConsoleProcessManager.ShutDownProcess : Terminating vstest.console process after waiting for {ENDSESSIONTIMEOUT} milliseconds."); vstestConsoleExited = true; this.process.OutputDataReceived -= Process_OutputDataReceived; this.process.ErrorDataReceived -= Process_ErrorDataReceived; SafelyTerminateProcess(); this.process.Dispose(); this.process = null; - } + } } private void SafelyTerminateProcess() @@ -173,8 +180,8 @@ private void Process_Exited(object sender, EventArgs e) { lock (syncObject) { - ShutdownProcess(); - + processExitedEvent.Set(); + vstestConsoleExited = true; this.ProcessExited?.Invoke(sender, e); } } diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/VsTestConsoleWrapper.cs b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/VsTestConsoleWrapper.cs index f340ef50e3..e947ea8617 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/VsTestConsoleWrapper.cs +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/VsTestConsoleWrapper.cs @@ -258,6 +258,10 @@ public void EndSession() { this.requestSender.EndSession(); this.requestSender.Close(); + + // If vstest.console is still hanging around, it should be explicitly killed. + this.vstestConsoleProcessManager.ShutdownProcess(); + this.sessionStarted = false; } diff --git a/test/Microsoft.TestPlatform.AcceptanceTests/TranslationLayerTests/RunTests.cs b/test/Microsoft.TestPlatform.AcceptanceTests/TranslationLayerTests/RunTests.cs index cc3082edb2..ad155994c5 100644 --- a/test/Microsoft.TestPlatform.AcceptanceTests/TranslationLayerTests/RunTests.cs +++ b/test/Microsoft.TestPlatform.AcceptanceTests/TranslationLayerTests/RunTests.cs @@ -11,6 +11,7 @@ namespace Microsoft.TestPlatform.AcceptanceTests.TranslationLayerTests using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Collections.Generic; + using System.Diagnostics; using System.Linq; using VisualStudio.TestPlatform.ObjectModel.Logging; @@ -52,6 +53,25 @@ public void RunAllTests(RunnerInfo runnerInfo) Assert.AreEqual(2, this.runEventHandler.TestResults.Count(t => t.Outcome == TestOutcome.Skipped)); } + [TestMethod] + [NetFullTargetFrameworkDataSource] + [NetCoreTargetFrameworkDataSource] + public void EndSessionShouldEnsureVstestConsoleProcessDies(RunnerInfo runnerInfo) + { + var numOfProcesses = Process.GetProcessesByName("vstest.console").Length; + + AcceptanceTestBase.SetTestEnvironment(this.testEnvironment, runnerInfo); + this.Setup(); + + this.vstestConsoleWrapper.RunTests(this.GetTestAssemblies(), this.GetDefaultRunSettings(), this.runEventHandler); + this.vstestConsoleWrapper?.EndSession(); + + // Assert + Assert.AreEqual(numOfProcesses, Process.GetProcessesByName("vstest.console").Length); + + this.vstestConsoleWrapper = null; + } + [TestMethod] [NetFullTargetFrameworkDataSource] [NetCoreTargetFrameworkDataSource] diff --git a/test/TranslationLayer.UnitTests/VsTestConsoleWrapperTests.cs b/test/TranslationLayer.UnitTests/VsTestConsoleWrapperTests.cs index e9c89892d0..2691bb43c7 100644 --- a/test/TranslationLayer.UnitTests/VsTestConsoleWrapperTests.cs +++ b/test/TranslationLayer.UnitTests/VsTestConsoleWrapperTests.cs @@ -310,6 +310,7 @@ public void EndSessionShouldSucceed() this.mockRequestSender.Verify(rs => rs.EndSession(), Times.Once); this.mockRequestSender.Verify(rs => rs.Close(), Times.Once); + this.mockProcessManager.Verify(x => x.ShutdownProcess(), Times.Once); } } }