diff --git a/src/Microsoft.VisualStudio.Extensibility.Testing.Xunit.Shared/Harness/VisualStudioInstance.cs b/src/Microsoft.VisualStudio.Extensibility.Testing.Xunit.Shared/Harness/VisualStudioInstance.cs index 260defe1..8e38aa9c 100644 --- a/src/Microsoft.VisualStudio.Extensibility.Testing.Xunit.Shared/Harness/VisualStudioInstance.cs +++ b/src/Microsoft.VisualStudio.Extensibility.Testing.Xunit.Shared/Harness/VisualStudioInstance.cs @@ -13,6 +13,7 @@ namespace Xunit.Harness using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Ipc; using System.Runtime.Serialization.Formatters; + using System.Threading.Tasks; using Microsoft.VisualStudio.IntegrationTestService; using Windows.Win32; using Windows.Win32.Foundation; @@ -27,9 +28,10 @@ internal class VisualStudioInstance private readonly IpcChannel _integrationServiceChannel; private readonly VisualStudio_InProc _inProc; - public VisualStudioInstance(Process hostProcess, DTE dte, Version version, ImmutableHashSet supportedPackageIds, string installationPath) + public VisualStudioInstance(Process hostProcess, Func stopWatchingProcessAsync, DTE dte, Version version, ImmutableHashSet supportedPackageIds, string installationPath) { HostProcess = hostProcess; + StopWatchingProcessAsync = stopWatchingProcessAsync; Dte = dte; Version = version; SupportedPackageIds = supportedPackageIds; @@ -87,6 +89,11 @@ internal Process HostProcess get; } + public Func StopWatchingProcessAsync + { + get; + } + public Version Version { get; @@ -217,6 +224,8 @@ public void Close(bool exitHostProcess = true) return; } + StopWatchingProcessAsync().Wait(); + CleanUp(); CloseRemotingService(); diff --git a/src/Microsoft.VisualStudio.Extensibility.Testing.Xunit.Shared/Harness/VisualStudioInstanceFactory.cs b/src/Microsoft.VisualStudio.Extensibility.Testing.Xunit.Shared/Harness/VisualStudioInstanceFactory.cs index 5c04a35d..9355c13f 100644 --- a/src/Microsoft.VisualStudio.Extensibility.Testing.Xunit.Shared/Harness/VisualStudioInstanceFactory.cs +++ b/src/Microsoft.VisualStudio.Extensibility.Testing.Xunit.Shared/Harness/VisualStudioInstanceFactory.cs @@ -166,7 +166,17 @@ private async Task UpdateCurrentlyRunningInstanceAsync(Version version, string? _currentlyRunningInstance.Close(exitHostProcess: false); } - _currentlyRunningInstance = new VisualStudioInstance(hostProcess, dte, actualVersion, supportedPackageIds, installationPath); + // Run the snapshot collection operation, but don't block on its completion for the actual test execution + var screenshotTaskCancellationSource = new CancellationTokenSource(); + var screenshotTask = Task.Run(() => TakeSnapshotEveryTimeSpanUntilProcessExitAsync(hostProcess, $"devenv{hostProcess.Id}", screenshotTaskCancellationSource.Token)); + Func stopWatchingProcessAsync = + async () => + { + screenshotTaskCancellationSource.Cancel(); + await screenshotTask.ConfigureAwait(false); + }; + + _currentlyRunningInstance = new VisualStudioInstance(hostProcess, stopWatchingProcessAsync, dte, actualVersion, supportedPackageIds, installationPath); if (shouldStartNewInstance) { var harnessAssemblyDirectory = Path.GetDirectoryName(typeof(VisualStudioInstanceFactory).Assembly.CodeBase); @@ -393,14 +403,14 @@ private static async Task StartNewVisualStudioProcessAsync(string insta if (version.Major >= 12) { var clearCacheProcess = Process.Start(CreateStartInfo(vsExeFile, silent: true, $"/clearcache {vsLaunchArgs}")); - TakeSnapshotEveryTimeSpanUntilProcessExit(clearCacheProcess, "clearcache"); + await TakeSnapshotEveryTimeSpanUntilProcessExitAsync(clearCacheProcess, "clearcache", CancellationToken.None); } var updateConfigProcess = Process.Start(CreateStartInfo(vsExeFile, silent: true, $"/updateconfiguration {vsLaunchArgs}")); - TakeSnapshotEveryTimeSpanUntilProcessExit(updateConfigProcess, "updateconfiguration"); + await TakeSnapshotEveryTimeSpanUntilProcessExitAsync(updateConfigProcess, "updateconfiguration", CancellationToken.None); var resetSettingsProcess = Process.Start(CreateStartInfo(vsExeFile, silent: true, $"/resetsettings General.vssettings /command \"File.Exit\" {vsLaunchArgs}")); - TakeSnapshotEveryTimeSpanUntilProcessExit(resetSettingsProcess, "resetsettings"); + await TakeSnapshotEveryTimeSpanUntilProcessExitAsync(resetSettingsProcess, "resetsettings", CancellationToken.None); // Make sure we kill any leftover processes spawned by the host IntegrationHelper.KillProcess("DbgCLR"); @@ -408,10 +418,6 @@ private static async Task StartNewVisualStudioProcessAsync(string insta IntegrationHelper.KillProcess("dexplore"); var process = Process.Start(CreateStartInfo(vsExeFile, silent: false, vsLaunchArgs)); - - // Run the snapshot collection operation, but don't block on its completion for the actual test execution - _ = Task.Run(() => TakeSnapshotEveryTimeSpanUntilProcessExit(process, $"devenv{process.Id}")); - Debug.WriteLine($"Launched a new instance of Visual Studio. (ID: {process.Id})"); return process; @@ -464,7 +470,7 @@ private static string ExtractIntegrationTestServiceExtension(string temporaryFol return path; } - private static void TakeSnapshotEveryTimeSpanUntilProcessExit(Process process, string commandBeingExecuted) + private static async Task TakeSnapshotEveryTimeSpanUntilProcessExitAsync(Process process, string commandBeingExecuted, CancellationToken cancellationToken) { var dir = DataCollectionService.GetLogDirectory(); if (!Directory.Exists(dir)) @@ -472,18 +478,22 @@ private static void TakeSnapshotEveryTimeSpanUntilProcessExit(Process process, s Directory.CreateDirectory(dir); } - using var cancellatokenSource = new CancellationTokenSource(); + using var cancellationTokenSource = new CancellationTokenSource(); + using var linkedCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationTokenSource.Token, cancellationToken); + + var screenshotTask = Task.Run(() => TakeScreenShotEveryTimeIntervalAsync(dir, commandBeingExecuted, linkedCancellationSource.Token), linkedCancellationSource.Token); try { - _ = Task.Run(() => TakeScreenShotEveryTimeIntervalAsync(dir, commandBeingExecuted, cancellatokenSource.Token)); process.WaitForExit(); } finally { - cancellatokenSource.Cancel(); + cancellationTokenSource.Cancel(); } + await screenshotTask.ConfigureAwait(false); + static async Task TakeScreenShotEveryTimeIntervalAsync(string directory, string commandBeingExecuted, CancellationToken cancellationToken) { var count = 1;