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 1 commit
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 |
---|---|---|
|
@@ -31,11 +31,11 @@ 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 readonly ManualResetEventSlim testHostConnectionTimeOut = new ManualResetEventSlim(false); | ||
|
||
private bool initialized; | ||
private string testHostProcessStdError; | ||
private int testHostProcessId; | ||
private bool testHostLaunchSuccess; | ||
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: testHostLaunched |
||
|
||
#region Constructors | ||
|
||
|
@@ -52,7 +52,7 @@ protected ProxyOperationManager(ITestRequestSender requestSender, ITestRuntimePr | |
this.testHostManager = testHostManager; | ||
this.processHelper = new ProcessHelper(); | ||
this.initialized = false; | ||
this.testHostProcessId = -1; | ||
this.testHostLaunchSuccess = false; | ||
} | ||
|
||
#endregion | ||
|
@@ -70,6 +70,8 @@ protected ProxyOperationManager(ITestRequestSender requestSender, ITestRuntimePr | |
|
||
#region IProxyOperationManager implementation. | ||
|
||
// ToDo: change SetupChannel to return bool, which will specify that a successfull connection with testhost was established 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. nit: inside SetupChannel method body. 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> | ||
/// Ensure that the engine is ready for test operations. | ||
/// Usually includes starting up the test host process. | ||
|
@@ -78,8 +80,6 @@ protected ProxyOperationManager(ITestRequestSender requestSender, ITestRuntimePr | |
/// <param name="cancellationToken"></param> | ||
public virtual void SetupChannel(IEnumerable<string> sources, CancellationToken cancellationToken) | ||
{ | ||
var connTimeout = this.connectionTimeout; | ||
|
||
if (!this.initialized) | ||
{ | ||
this.testHostProcessStdError = string.Empty; | ||
|
@@ -92,36 +92,23 @@ public virtual void SetupChannel(IEnumerable<string> sources, CancellationToken | |
// 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. nit: remove extra space 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 |
||
// Get the test process start info | ||
var testHostStartInfo = this.UpdateTestProcessStartInfo(this.testHostManager.GetTestHostProcessStartInfo(sources, null, connectionInfo)); | ||
try | ||
{ | ||
// Launch the test host. | ||
var hostLaunchedTask = this.testHostManager.LaunchTestHostAsync(testHostStartInfo, cancellationToken); | ||
this.testHostProcessId = hostLaunchedTask.Result; | ||
this.testHostLaunchSuccess = hostLaunchedTask.Result; | ||
} | ||
catch (Exception ex) | ||
{ | ||
throw new TestPlatformException(string.Format(CultureInfo.CurrentUICulture, ex.Message)); | ||
} | ||
|
||
// 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}", this.testHostProcessId, this.processHelper.GetProcessName(this.testHostProcessId)), | ||
OutputLevel.Information); | ||
|
||
// Increase connection timeout when debugging is enabled. | ||
connTimeout = 5 * this.connectionTimeout; | ||
} | ||
|
||
// Wait for a timeout for the client to connect. | ||
if (!this.RequestSender.WaitForRequestHandlerConnection(connTimeout)) | ||
// test host launched failed because of user cancellation or any other runtime exception | ||
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: comments start with Caps 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 |
||
// or connection with test host did not happen | ||
if (!this.testHostLaunchSuccess || this.testHostConnectionTimeOut.Wait(0)) | ||
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. ManualResetEventSlim.Wait == a bool type? |
||
{ | ||
var errorMsg = CrossPlatEngineResources.InitializationFailed; | ||
|
||
|
@@ -161,7 +148,7 @@ public virtual void Close() | |
try | ||
{ | ||
// do not send message if host did not launch | ||
if (this.testHostProcessId != -1) | ||
if (this.testHostLaunchSuccess) | ||
{ | ||
this.RequestSender.EndSession(); | ||
} | ||
|
@@ -179,15 +166,14 @@ public virtual void Close() | |
// 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)) | ||
if (!this.testHostExited.Wait(100)) | ||
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.
|
||
{ | ||
EqtTrace.Warning("ProxyOperationManager: Timed out waiting for test host to exit. Will terminate process."); | ||
this.testHostManager.CleanTestHostAsync(this.testHostProcessId, CancellationToken.None).Wait(); | ||
this.testHostManager.CleanTestHostAsync(CancellationToken.None).Wait(); | ||
} | ||
|
||
this.testHostManager.HostExited -= this.TestHostManagerHostExited; | ||
this.testHostManager.HostLaunched -= this.TestHostManagerHostLaunched; | ||
this.testHostManager.HostLaunchFailure -= this.TestHostManagerHostLaunchFailure; | ||
} | ||
} | ||
|
||
|
@@ -224,9 +210,28 @@ protected string GetTimestampedLogFile(string logFile) | |
Path.GetExtension(logFile))).AddDoubleQuote(); | ||
} | ||
|
||
private void TestHostManagerHostLaunched(object sender, HostProviderEventArgs e) | ||
public void TestHostManagerHostLaunched(object sender, HostProviderEventArgs e) | ||
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 is this public? |
||
{ | ||
EqtTrace.Verbose(e.Data); | ||
var connTimeout = this.connectionTimeout; | ||
|
||
// 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}", e.ProcessId, this.processHelper.GetProcessName(e.ProcessId)), | ||
OutputLevel.Information); | ||
|
||
// Increase connection timeout when debugging is enabled. | ||
connTimeout = 5 * this.connectionTimeout; | ||
} | ||
|
||
// Wait for a timeout for the client to connect. | ||
if (!this.RequestSender.WaitForRequestHandlerConnection(connTimeout)) | ||
{ | ||
this.testHostConnectionTimeOut.Set(); | ||
} | ||
} | ||
|
||
private void TestHostManagerHostExited(object sender, HostProviderEventArgs e) | ||
|
@@ -237,11 +242,5 @@ 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 |
---|---|---|
|
@@ -20,14 +20,10 @@ public interface ITestRuntimeProvider | |
#region events | ||
/// <summary> | ||
/// Raised when host is launched successfully | ||
/// Consumed by TestPlatform to initialize connection b/w test host and testplatform | ||
/// </summary> | ||
event EventHandler<HostProviderEventArgs> HostLaunched; | ||
|
||
/// <summary> | ||
/// 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> | ||
|
@@ -72,7 +68,7 @@ public interface ITestRuntimeProvider | |
/// <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, CancellationToken cancellationToken); | ||
Task<bool> 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. Update documentation |
||
|
||
/// <summary> | ||
/// Gets the start parameters for the test host. | ||
|
@@ -96,16 +92,13 @@ public interface ITestRuntimeProvider | |
/// <summary> | ||
/// Terminate the test host process. | ||
/// </summary> | ||
/// <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); | ||
Task CleanTestHostAsync(CancellationToken cancellationToken); | ||
} | ||
|
||
/// <summary> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -78,9 +78,6 @@ internal DefaultTestHostManager(IProcessHelper processHelper, IEnvironment envir | |
/// <inheritdoc/> | ||
public event EventHandler<HostProviderEventArgs> HostExited; | ||
|
||
/// <inheritdoc/> | ||
public event EventHandler<HostProviderEventArgs> HostLaunchFailure; | ||
|
||
/// <inheritdoc/> | ||
public bool Shared { get; private set; } | ||
|
||
|
@@ -117,7 +114,7 @@ public void SetCustomLauncher(ITestHostLauncher customLauncher) | |
} | ||
|
||
/// <inheritdoc/> | ||
public async Task<int> LaunchTestHostAsync(TestProcessStartInfo testHostStartInfo, CancellationToken cancellationToken) | ||
public virtual async Task<bool> LaunchTestHostAsync(TestProcessStartInfo testHostStartInfo, CancellationToken cancellationToken) | ||
{ | ||
return await Task.Run(() => this.LaunchHost(testHostStartInfo, cancellationToken), cancellationToken); | ||
} | ||
|
@@ -205,11 +202,11 @@ public void Initialize(IMessageLogger logger, string runsettingsXml) | |
} | ||
|
||
/// <inheritdoc/> | ||
public Task CleanTestHostAsync(int processId, CancellationToken cancellationToken) | ||
public Task CleanTestHostAsync(CancellationToken cancellationToken) | ||
{ | ||
try | ||
{ | ||
this.processHelper.TerminateProcess(processId); | ||
this.processHelper.TerminateProcess(this.testHostProcess.Id); | ||
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 |
||
} | ||
catch (Exception ex) | ||
{ | ||
|
@@ -225,7 +222,7 @@ public Task CleanTestHostAsync(int processId, CancellationToken cancellationToke | |
/// Raises HostLaunched event | ||
/// </summary> | ||
/// <param name="e">hostprovider event args</param> | ||
private void OnHostLaunched(HostProviderEventArgs e) | ||
protected virtual void OnHostLaunched(HostProviderEventArgs e) | ||
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 am not sure if making a event handler virtual is a good idea. |
||
{ | ||
this.HostLaunched.SafeInvoke(this, e, "HostProviderEvents.OnHostLaunched"); | ||
} | ||
|
@@ -234,7 +231,7 @@ private void OnHostLaunched(HostProviderEventArgs e) | |
/// Raises HostExited event | ||
/// </summary> | ||
/// <param name="e">hostprovider event args</param> | ||
private void OnHostExited(HostProviderEventArgs e) | ||
protected virtual void OnHostExited(HostProviderEventArgs e) | ||
{ | ||
if (!this.hostExitedEventRaised) | ||
{ | ||
|
@@ -243,12 +240,7 @@ private void OnHostExited(HostProviderEventArgs e) | |
} | ||
} | ||
|
||
private void OnHostLaunchFailure(HostProviderEventArgs e) | ||
{ | ||
this.HostLaunchFailure.SafeInvoke(this, e, "HostProviderEvents.HostLaunchError"); | ||
} | ||
|
||
private int LaunchHost(TestProcessStartInfo testHostStartInfo, CancellationToken cancellationToken) | ||
private bool LaunchHost(TestProcessStartInfo testHostStartInfo, CancellationToken cancellationToken) | ||
{ | ||
try | ||
{ | ||
|
@@ -270,13 +262,12 @@ private int LaunchHost(TestProcessStartInfo testHostStartInfo, CancellationToken | |
} | ||
catch (OperationCanceledException ex) | ||
{ | ||
this.OnHostLaunchFailure(new HostProviderEventArgs(ex.Message, -1, 0)); | ||
return -1; | ||
this.messageLogger.SendMessage(TestMessageLevel.Error, ex.Message); | ||
return false; | ||
} | ||
|
||
var pId = this.testHostProcess != null ? this.testHostProcess.Id : 0; | ||
this.OnHostLaunched(new HostProviderEventArgs("Test Runtime launched with Pid: " + pId)); | ||
return pId; | ||
this.OnHostLaunched(new HostProviderEventArgs("Test Runtime launched", 0, this.testHostProcess.Id)); | ||
return this.testHostProcess != null; | ||
} | ||
} | ||
} |
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.
nit: timeout is one word
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.
Looking at usage, suggest to name it as
channelConnected
.