New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adding CancellationToken to Testhost launch async #917
Changes from 4 commits
7d07627
f164a45
dcdd23b
4abac27
6798fa3
6c78e6b
c7cc72f
cb512af
6e54fb6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,20 +7,19 @@ namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Client | |
using System.Collections.Generic; | ||
using System.Globalization; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Reflection; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Extensions; | ||
using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Interfaces; | ||
using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Extensions; | ||
using Microsoft.VisualStudio.TestPlatform.ObjectModel; | ||
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Host; | ||
using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions; | ||
using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions.Interfaces; | ||
using Microsoft.VisualStudio.TestPlatform.Utilities; | ||
|
||
using CrossPlatEngineResources = Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Resources.Resources; | ||
using System.Reflection; | ||
using System.Linq; | ||
|
||
/// <summary> | ||
/// Base class for any operations that the client needs to drive through the engine. | ||
|
@@ -32,6 +31,7 @@ public abstract class ProxyOperationManager | |
private readonly int connectionTimeout; | ||
private readonly string versionCheckPropertyName = "IsVersionCheckRequired"; | ||
private readonly ManualResetEventSlim testHostExited = new ManualResetEventSlim(false); | ||
private readonly ManualResetEventSlim testHostLaunchError = new ManualResetEventSlim(false); | ||
|
||
private bool initialized; | ||
private string testHostProcessStdError; | ||
|
@@ -75,7 +75,8 @@ protected ProxyOperationManager(ITestRequestSender requestSender, ITestRuntimePr | |
/// Usually includes starting up the test host process. | ||
/// </summary> | ||
/// <param name="sources">List of test sources.</param> | ||
public virtual void SetupChannel(IEnumerable<string> sources) | ||
/// <param name="cancellationToken"></param> | ||
public virtual void SetupChannel(IEnumerable<string> sources, CancellationToken cancellationToken) | ||
{ | ||
var connTimeout = this.connectionTimeout; | ||
|
||
|
@@ -91,29 +92,28 @@ public virtual void SetupChannel(IEnumerable<string> sources) | |
// Subscribe to TestHost Event | ||
this.testHostManager.HostLaunched += this.TestHostManagerHostLaunched; | ||
this.testHostManager.HostExited += this.TestHostManagerHostExited; | ||
this.testHostManager.HostLaunchFailure += this.TestHostManagerHostLaunchFailure; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not use the Exited event for error too? |
||
|
||
// Get the test process start info | ||
var testHostStartInfo = this.UpdateTestProcessStartInfo(this.testHostManager.GetTestHostProcessStartInfo(sources, null, connectionInfo)); | ||
|
||
// Launch the test host. | ||
var hostLaunchedTask = this.testHostManager.LaunchTestHostAsync(testHostStartInfo); | ||
|
||
try | ||
{ | ||
// Launch the test host. | ||
var hostLaunchedTask = this.testHostManager.LaunchTestHostAsync(testHostStartInfo, cancellationToken); | ||
this.testHostProcessId = hostLaunchedTask.Result; | ||
} | ||
catch (OperationCanceledException ex) | ||
catch (Exception ex) | ||
{ | ||
throw new TestPlatformException(string.Format(CultureInfo.CurrentUICulture, ex.Message)); | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: remove extra space |
||
// Warn the user that execution will wait for debugger attach. | ||
var hostDebugEnabled = Environment.GetEnvironmentVariable("VSTEST_HOST_DEBUG"); | ||
if (!string.IsNullOrEmpty(hostDebugEnabled) && hostDebugEnabled.Equals("1", StringComparison.Ordinal)) | ||
{ | ||
ConsoleOutput.Instance.WriteLine(CrossPlatEngineResources.HostDebuggerWarning, OutputLevel.Warning); | ||
ConsoleOutput.Instance.WriteLine( | ||
string.Format("Process Id: {0}, Name: {1}", hostLaunchedTask.Result, this.processHelper.GetProcessName(hostLaunchedTask.Result)), | ||
string.Format("Process Id: {0}, Name: {1}", this.testHostProcessId, this.processHelper.GetProcessName(this.testHostProcessId)), | ||
OutputLevel.Information); | ||
|
||
// Increase connection timeout when debugging is enabled. | ||
|
@@ -125,7 +125,7 @@ public virtual void SetupChannel(IEnumerable<string> sources) | |
{ | ||
var errorMsg = CrossPlatEngineResources.InitializationFailed; | ||
|
||
if (!string.IsNullOrWhiteSpace(this.testHostProcessStdError.ToString())) | ||
if (!string.IsNullOrWhiteSpace(this.testHostProcessStdError)) | ||
{ | ||
// Testhost failed with error | ||
errorMsg = string.Format(CrossPlatEngineResources.TestHostExitedWithError, this.testHostProcessStdError); | ||
|
@@ -160,7 +160,11 @@ public virtual void Close() | |
{ | ||
try | ||
{ | ||
this.RequestSender.EndSession(); | ||
// do not send message if host did not launch | ||
if (this.testHostProcessId != -1) | ||
{ | ||
this.RequestSender.EndSession(); | ||
} | ||
} | ||
catch (Exception ex) | ||
{ | ||
|
@@ -172,16 +176,18 @@ public virtual void Close() | |
{ | ||
this.initialized = false; | ||
|
||
// We don't need to terminate if the test host has already terminated. The upper bound | ||
// for wait should be 100ms. | ||
if (this.testHostProcessId != -1 && !this.testHostExited.Wait(100)) | ||
// We don't need to terminate if the test host has already terminated. | ||
// If the host launch fails, please clean test host. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can the pattern be simple?
If there were no test host, it is up to Thinking aloud, does |
||
// The upper bound for wait should be 100ms. | ||
if ((this.testHostProcessId != -1 && !this.testHostExited.Wait(100)) || this.testHostLaunchError.Wait(0)) | ||
{ | ||
EqtTrace.Warning("ProxyOperationManager: Timed out waiting for test host to exit. Will terminate process."); | ||
this.testHostManager.TerminateAsync(this.testHostProcessId, CancellationToken.None).Wait(); | ||
this.testHostManager.CleanTestHostAsync(this.testHostProcessId, CancellationToken.None).Wait(); | ||
} | ||
|
||
this.testHostManager.HostExited -= this.TestHostManagerHostExited; | ||
this.testHostManager.HostLaunched -= this.TestHostManagerHostLaunched; | ||
this.testHostManager.HostLaunchFailure -= this.TestHostManagerHostLaunchFailure; | ||
} | ||
} | ||
|
||
|
@@ -205,7 +211,9 @@ protected virtual TestProcessStartInfo UpdateTestProcessStartInfo(TestProcessSta | |
protected string GetTimestampedLogFile(string logFile) | ||
{ | ||
if (string.IsNullOrWhiteSpace(logFile)) | ||
{ | ||
return null; | ||
} | ||
|
||
return Path.ChangeExtension( | ||
logFile, | ||
|
@@ -229,5 +237,11 @@ private void TestHostManagerHostExited(object sender, HostProviderEventArgs e) | |
|
||
this.testHostExited.Set(); | ||
} | ||
|
||
private void TestHostManagerHostLaunchFailure(object sender, HostProviderEventArgs e) | ||
{ | ||
EqtTrace.Verbose(e.Data); | ||
this.testHostLaunchError.Set(); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,12 +5,12 @@ namespace Microsoft.VisualStudio.TestPlatform.ObjectModel.Host | |
{ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
using Microsoft.VisualStudio.TestPlatform.ObjectModel; | ||
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces; | ||
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; | ||
using System.Threading; | ||
|
||
/// <summary> | ||
/// Interface for TestRuntimeProvider which manages test host processes for test engine. | ||
|
@@ -24,7 +24,12 @@ public interface ITestRuntimeProvider | |
event EventHandler<HostProviderEventArgs> HostLaunched; | ||
|
||
/// <summary> | ||
/// Raised when host is reports Error | ||
/// Raised when host launch reports an Error | ||
/// </summary> | ||
event EventHandler<HostProviderEventArgs> HostLaunchFailure; | ||
|
||
/// <summary> | ||
/// Raised when host is cleaned up and removes all it's dependecies | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: spell check There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. resolved |
||
/// </summary> | ||
event EventHandler<HostProviderEventArgs> HostExited; | ||
|
||
|
@@ -65,8 +70,9 @@ public interface ITestRuntimeProvider | |
/// Launches the test host for discovery/execution. | ||
/// </summary> | ||
/// <param name="testHostStartInfo">Start parameters for the test host.</param> | ||
/// <param name="cancellationToken"></param> | ||
/// <returns>ProcessId of launched Process. 0 means not launched.</returns> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Need to update There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. resolved |
||
Task<int> LaunchTestHostAsync(TestProcessStartInfo testHostStartInfo); | ||
Task<int> LaunchTestHostAsync(TestProcessStartInfo testHostStartInfo, CancellationToken cancellationToken); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the return code is used to terminate the host later. Will the return type be always an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If Runtime provider need to keep additional information, then it can store that info locally. As for lauchasync it's job it to return whether host was successfully launched or not. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Make this a |
||
|
||
/// <summary> | ||
/// Gets the start parameters for the test host. | ||
|
@@ -83,15 +89,23 @@ public interface ITestRuntimeProvider | |
/// mechanism. E.g. for .net core, extensions are discovered from the <c>testproject.deps.json</c> file. | ||
/// </summary> | ||
/// <param name="sources">List of test sources.</param> | ||
/// <param name="extensions"></param> | ||
/// <returns>List of paths to extension assemblies.</returns> | ||
IEnumerable<string> GetTestPlatformExtensions(IEnumerable<string> sources, IEnumerable<string> extensions); | ||
|
||
/// <summary> | ||
/// Terminate the test host process. | ||
/// </summary> | ||
/// <param name="processId">Process identifier for the test host.</param> | ||
/// <param name="cancellationToken">Cancellation token.</param> | ||
Task TerminateAsync(int processId, CancellationToken cancellationToken); | ||
/// <param name="processId"> | ||
/// Process identifier for the test host. | ||
/// </param> | ||
/// <param name="cancellationToken"> | ||
/// Cancellation token. | ||
/// </param> | ||
/// <returns> | ||
/// The <see cref="Task"/>. | ||
/// </returns> | ||
Task CleanTestHostAsync(int processId, CancellationToken cancellationToken); | ||
} | ||
|
||
/// <summary> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we consider defining this CancellationTokenSource in ProxyOperationManager?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
next PR