-
Notifications
You must be signed in to change notification settings - Fork 323
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
Cancel/abort not honored when sent before custom host launch #1543
Conversation
@@ -383,7 +383,7 @@ private bool LaunchHost(TestProcessStartInfo testHostStartInfo, CancellationToke | |||
} | |||
else | |||
{ | |||
int processId = this.customTestHostLauncher.LaunchTestHost(testHostStartInfo); | |||
int processId = this.customTestHostLauncher.LaunchTestHost(testHostStartInfo, cancellationToken); |
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.
We are breaking the API here. Is customTestHostLauncher used by others also? Interface ITesHostLauncher is in ObjectModel.
If its used by others, we need to introduce something called IsCancellable in ITestHostLauncher which will be used here to switch between overriden calls of customTestHostLauncher.LaunchTestHost.
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.
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.
I confirmed from Mayank. It will not be breaking change.
@@ -5,6 +5,7 @@ namespace Microsoft.VisualStudio.TestPlatform.Client.DesignMode | |||
{ | |||
using Microsoft.VisualStudio.TestPlatform.ObjectModel; | |||
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces; | |||
using System.Threading; |
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.
Threading [](start = 17, length = 9)
nitpick: sort using..
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.
done
@@ -265,7 +275,7 @@ public void Abort() | |||
{ | |||
EqtTrace.Verbose("TestRunRequest.Abort: Aborting."); | |||
|
|||
lock (this.syncObject) | |||
lock (this.abortSyncObject) |
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.
abortSyncObject [](start = 23, length = 15)
isn't the usage of the sync object to synchronize the access to the executionmanager ?
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.
All execution related requests, like ExecuteAsync, HandleTestRunComplete, HandleTestRunStatsChange, HandleLogMessage are under same sync object.
But we want to respect cancel, abort and don't want to wait if some of the above request are going on. Thus separate lock objects for them
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.
Potentially reuse the same object for cancel and abort then?
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.
done.
@@ -160,7 +160,7 @@ public Collection<AttachmentSet> SendAfterTestRunStartAndGetResult(ITestMessageE | |||
|
|||
// Cycle through the messages that the datacollector sends. | |||
// Currently each of the operations are not separate tasks since they should not each take much time. This is just a notification. | |||
while (!isDataCollectionComplete) | |||
while (!isDataCollectionComplete && !isCancelled) |
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.
isCancelled [](start = 49, length = 11)
won't we fail to receive data (testruncomplete )
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.
In case of cancellation and abort, we dont get any attachments from data collectors and simply cancels the run. So adding isCancelled will do the same thing. For non-cancelled run, we will be able to successfully able to receive data
@@ -260,13 +270,19 @@ public void InitializeExecution(IEnumerable<string> pathToAdditionalExtensions) | |||
|
|||
/// <inheritdoc /> | |||
public void StartTestRun(TestRunCriteriaWithSources runCriteria, ITestRunEventsHandler eventHandler) |
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.
StartTestRun [](start = 20, length = 12)
why do we need two signatures..? TP is an internal interface..?
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.
done
/// <param name="runCriteria">RunCriteria for test run</param> | ||
/// <param name="eventHandler">EventHandler for test run events</param> | ||
/// <param name="cancellationToken">Cancellation token</param> | ||
void StartTestRun(TestRunCriteriaWithTests runCriteria, ITestRunEventsHandler eventHandler, CancellationToken cancellationToken); |
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.
StartTestRun [](start = 13, length = 12)
same question.. is this a public interface ?
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.
Done. Modified existing methods.
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.
🕐
@@ -383,7 +383,7 @@ private bool LaunchHost(TestProcessStartInfo testHostStartInfo, CancellationToke | |||
} | |||
else | |||
{ | |||
int processId = this.customTestHostLauncher.LaunchTestHost(testHostStartInfo); | |||
int processId = this.customTestHostLauncher.LaunchTestHost(testHostStartInfo, cancellationToken); |
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.
Add this is dotnet runtime
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.
done
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.
done
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.
@@ -250,6 +253,9 @@ public int LaunchCustomHost(TestProcessStartInfo testProcessStartInfo) | |||
waitHandle.Set(); | |||
}; | |||
|
|||
// Registering cancellationToken to set waitHandle (whenever request is cancelled). | |||
var cancellationTokenRegistration = cancellationToken.Register(() => waitHandle.Set()); |
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.
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.
done.
@@ -554,15 +559,18 @@ private string GetAbortErrorMessage(Exception exception, bool getClientError) | |||
|
|||
// Set a default message and wait for test host to exit for a moment | |||
reason = CommonResources.UnableToCommunicateToTestHost; | |||
if (this.clientExited.Wait(this.clientExitedWaitTime)) | |||
if (this.clientExited.Wait(this.clientExitedWaitTime) && !cancellationToken.IsCancellationRequested) |
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.
hmm, should this be the first condition then?
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.
No. If cancellation flag is checked first and it returns false, and then while waiting cancellation occurs, then waitHandle will be set because of cancellation and thus enter in if condition (which is wrong as wait is set by cancel)
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.
I was thinking the other way round where we actually end up waiting for a while, figure out its cancelled and head to the else part which seems like a wasted wait...
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.
We are already doing that in this PR. Earlier I did a CancellationToken.Register(() => this.clientExited) which I latter changed to this.clientExited.Wait(this.clientExitedWaitTime, CancellationToken) on your recommendation. In either case, clientExited will return immediately if cancellation happened. Let me know if i am understanding it incorrectly.
@@ -184,6 +189,24 @@ public virtual bool SetupChannel(IEnumerable<string> sources, CancellationToken | |||
return true; | |||
} | |||
|
|||
private bool LaunchTestHostAsync(TestProcessStartInfo testHostStartInfo, CancellationToken token) | |||
{ | |||
this.CancellationTokenSource.Token.ThrowIfCancellationRequested(); |
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 this be called out in the protocol doc? Looks like a cancel is going to be an abrupt stop to a request no matter where in the sequence we are currently.
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.
We are cancelling abruptly only before the test execution is actually started and then sending HandleTestRunComplete.
Once test execution is started by test host, we send cancel request to test host and which passes the cancel request to adapters.
@cltshivash @singhsarab Should this be documented?
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.
yup, got that but most clients would not expect a TestRunComplete when negotiating protocol unless told otherwise.... Was a separate message for cancellation considered for scenarios like these? Might be a bigger change but worth a fluid protocol.
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.
TestRequestManager today waits for completion in all scenarios. I will check if this is already documented and if not, will discuss with the team and document it.
/// <param name="defaultTestHostStartInfo">Default TestHost Process Info</param> | ||
/// <param name="cancellationToken">The cancellation Token.</param> | ||
/// <returns>Process id of the launched test host</returns> | ||
int LaunchTestHost(TestProcessStartInfo defaultTestHostStartInfo, CancellationToken cancellationToken); |
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.
Will require a corresponding change in TPV1 OM. TE implements this for debugging which would need a change as well.
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.
This interface is used by run time provider only for launching the custom host via custom host launcher.
Why changes are required in TPV1 OM?
Even though if TE uses it as an interface for their internal purpose, there will no breaking change here (as TPv1 OM is used both while build and run time. TPV2 OM is not loaded.) Please correct me if i am missing something here.
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.
Fare, but shouldn't we be consistent? Bare in mind that the Console wrapper and interfaces assemblies are loaded in VS and this could result in inadvertent runtime failures.
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.
@cltshivash @singhsarab @AbhitejJohn
Am I missing something here? AFIAK ITestHostLauncher is meant only for runtime provider and it is loaded in vstest console only. Neither translation layer, nor test host or any other process from TP side uses this except vstest.console.
If TE is using this interface for their internal purpose, then it is using TPV1 OM and loading TPV1 OM. So how the behavior is inconsistent? I don't feel comfortable changing TPV1 OM unless it's a breaking change.
Do validate that UWP does work with these changes |
…into cancelNotHonored
Issue:
#1540
Changes done:
@AbhitejJohn Please verify IDE side changes considering above changes. I have done validations from my end. Let me know if there are any issues.
@mayankbansal018 Regarding # 7, do we need make any changes in UWP runtime provider to respect cancellation?