Skip to content
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

Event based communication over sockets. #1294

Merged
merged 27 commits into from
Dec 11, 2017

Conversation

navin22
Copy link
Member

@navin22 navin22 commented Nov 13, 2017

Description

  • Using Event based communication over sockets using LengthPrefix communication channel.
  • Channel will now notify when the message is received.
  • Socket client and server and use one thread each, instead of using new thread at every read.
  • Related tests and added additional tests.
  • ToDo : Only main flow is converted to the socket client and server. Other flows like DataCollection is not on this.

Arun Mahapatra and others added 11 commits May 8, 2017 08:12
…d implementation).

Update test request handler to use the new socket implementation.
# Conflicts:
#	src/Microsoft.TestPlatform.CommunicationUtilities/TestRequestSender.cs
#	src/Microsoft.TestPlatform.CommunicationUtilities/TestRequestSender2.cs
#	test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/TestRequestSenderTests.cs
#	test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/TestRequestSenderTests2.cs
# Conflicts:
#	src/Microsoft.TestPlatform.CommunicationUtilities/TestRequestSender.cs
#	src/Microsoft.TestPlatform.CommunicationUtilities/TestRequestSender2.cs
#	test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/TestRequestSenderTests.cs
#	test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/TestRequestSenderTests2.cs

Added debug launches to debug tests.
Copy link
Contributor

@mayankbansal018 mayankbansal018 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not taken a look at tests yet, will do it later


/// <inheritdoc />
public void Start(string connectionInfo)
// public void Start(string connectionInfo)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: remove this

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

// {
// throw connectAsyncTask.Exception;
// }
if (this.Connected != null)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can Connected ever be null in this class?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's an event so it can be null. But in our case it will not be null.

{
if (this.connectionInfo.Role == ConnectionRole.Client)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The connectioninfo here is that of the RuntimeProvider, which tells how the testhost will act, so we should reverse the role the of vstest.console right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it happens in another constructor. Ln 92-94.

// In case of DataCollection we only start dc.exe on initialize, & close once the TestRun is complete,
// So instead of resuing ProxyExecutionManager, we will close it here, & create new PEMWDC
// In Case of Abort, clean old one and create new proxyExecutionManager in place of old one.
if (!this.SharedHosts || testRunCompleteArgs.IsAborted || (proxyExecutionManager is ProxyExecutionManagerWithDataCollection))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did we remove these checks? were they bogus?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Earlier they were needed only for those scenarios, and in other scenarios they were reusing the execution manager. But since the test host dies it's not good to have that around. and was observing an exception, which tries to send message over a channel that's already closed.

{
if (this.connectionInfo.Role == ConnectionRole.Client)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please put a comment to move this to Communication factory, which returns ICommEndPoint based on Role, & ConnectionType (Socket, NamedPipe etc..) Once another communication mechanism is implemented

@navin22
Copy link
Member Author

navin22 commented Nov 15, 2017

@dotnet-bot test Windows_NT / Release Build

@navin22
Copy link
Member Author

navin22 commented Nov 15, 2017

@dotnet-bot test

@@ -24,11 +24,28 @@ public ConnectedEventArgs()
public ConnectedEventArgs(ICommunicationChannel channel)
{
this.Channel = channel;
this.Connected = true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is communicationutilities namespace in common assembly?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved the files.

{
using System;

public interface ICommunicationEndPoint
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doc?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sent it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be put it in vstest-docs, once refined.

@@ -57,10 +57,7 @@ public Task NotifyDataAvailable()
// connection is closed.
var data = this.reader.ReadString();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ReadString can throw the exception.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's caught in MessageLoopAsync method.

@@ -17,7 +17,7 @@ namespace Microsoft.VisualStudio.TestPlatform.CommunicationUtilities
/// <summary>
/// Communication server implementation over sockets.
/// </summary>
public class SocketServer : ICommunicationServer
public class SocketServer : ICommunicationEndPoint
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delete ICommunicationServer and ICommunicationClient.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

var ipEndPoint = endpoint.GetIPEndPoint();

// Don't start if the endpoint port is zero
this.tcpClient.ConnectAsync(ipEndPoint.Address, ipEndPoint.Port).ContinueWith(this.OnServerConnected);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add EqtTrace here saying that "Waiting for connecting to server".


// Don't start if the endpoint port is zero
this.tcpClient.ConnectAsync(ipEndPoint.Address, ipEndPoint.Port).ContinueWith(this.OnServerConnected);
return endpoint;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should return endpoint or ipEndPoint?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


/// <inheritdoc />
public void Start(string connectionInfo)
public string Start(string endpoint)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: endPoint.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

{
this.ServerConnected.SafeInvoke(this, new ConnectedEventArgs(this.channel), "SocketClient: ServerConnected");
if (connectAsyncTask.IsFaulted)
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add EqtTrace on failure and success scenarios.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

{
this.channel = this.channelFactory(this.tcpClient.GetStream());
this.ClientConnected.SafeInvoke(this, new ConnectedEventArgs(this.channel), "SocketServer: ClientConnected");
this.Connected.SafeInvoke(this, new ConnectedEventArgs(this.channel), "SocketServer: ClientConnected");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add EqtTrace on failure and success scenarios.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

{
private ICommunicationManager communicationManager;
// Time to wait for test host exit (in seconds)
private const int CLIENTPROCESSEXITWAIT = 10 * 1000;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: ClientProcessExitWaitTimeout?(Change it in other places too.)
In seconds?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

};
this.communicationEndpoint.Disconnected += (sender, args) =>
{
// If there's an disconnected event handler, call it
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On disconnect should set this.connected.Set()?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NA

else if (message.MessageType == MessageType.TestMessage)
{
// There are some test hosts which sends log messages, hence logging and ignoring.
var testMessage = this.dataSerializer.DeserializePayload<TestMessagePayload>(message);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is TestMessage expected? Should this log to client?

{
throw new TestPlatformException(string.Format(CultureInfo.CurrentUICulture, CommonResources.UnexpectedMessage, MessageType.VersionCheck, message.MessageType));
this.channel.MessageReceived -= this.onMessageReceived;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What will happens messages sent after version check message type, Like this

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reading from binary reader only when there's a corresponding event handler.

@@ -10,31 +10,20 @@
<TargetFrameworks>netcoreapp1.0;net451</TargetFrameworks>
<AssemblyName>Microsoft.TestPlatform.Common.UnitTests</AssemblyName>
</PropertyGroup>
<ItemGroup>
<!--<ItemGroup>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Remove comment.

@@ -7,12 +7,12 @@
<Import Project="$(TestPlatformRoot)scripts/build/TestPlatform.Settings.targets" />
<PropertyGroup>
<OutputType Condition=" '$(TargetFramework)' == 'netcoreapp1.0' ">Exe</OutputType>
<TargetFrameworks>netcoreapp1.0;net451</TargetFrameworks>
<TargetFrameworks>net451;netcoreapp1.0</TargetFrameworks>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revert the change.

<AssemblyName>Microsoft.TestPlatform.CommunicationUtilities.PlatformTests</AssemblyName>
<EnableCodeAnalysis>true</EnableCodeAnalysis>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.TestPlatform.CommunicationUtilities\Microsoft.TestPlatform.CommunicationUtilities.csproj" />
<!--<ProjectReference Include="..\..\src\Microsoft.TestPlatform.CommunicationUtilities\Microsoft.TestPlatform.CommunicationUtilities.csproj" />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove all the commented code.


protected IPEndPoint GetIpEndPoint(string value)
{
if (Uri.TryCreate(string.Concat("tcp://", value), UriKind.Absolute, out Uri uri))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DRY.

@@ -7,12 +7,12 @@
<Import Project="$(TestPlatformRoot)scripts/build/TestPlatform.Settings.targets" />
<PropertyGroup>
<OutputType Condition=" '$(TargetFramework)' == 'netcoreapp1.0' ">Exe</OutputType>
<TargetFrameworks>netcoreapp1.0;net451</TargetFrameworks>
<TargetFrameworks>net451;netcoreapp1.0</TargetFrameworks>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revert this change(And other places too).

{
throw new TestPlatformException(string.Format(CultureInfo.CurrentUICulture, CommonResources.VersionCheckFailed));
}
else
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some other message type can also come if the communication is very fast, for e.g. version check can also send https://github.com/Microsoft/vstest/blob/a32cf559805a61cd491b5574bd3f365df8943878/src/Microsoft.TestPlatform.CrossPlatEngine/EventHandlers/TestRequestHandler.cs#L102

which is Log message, & this will throw exception.

Ideal state would be to have one EventHandler method, which handles all possible message type, & throw if some garbage message comes.
Each time we are attaching an EventHandler, we can point to same method, & then detach once it is done

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added handler for message type MessageType.TestMessage

private void SetOperationComplete()
{
// Complete the currently ongoing operation (Discovery/Execution)
Interlocked.CompareExchange(ref this.operationCompleted, 1, 0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we using a Thread safe operation, are we expecting that this can hit from multiple threads?

}
this.channel = connectedArgs.Channel;
this.channel.MessageReceived += this.OnMessageReceived;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As soon as we set handler to receive messages, we can get a Test Discovery/Execution request before ProcessRequest which supply testHostManagerFactory.

So move attaching to event handler in ProcessRequest

Again this is a corner scenario, but can happen

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, there some unit test failing randomly because of this.I am handling this in the latest one. Please take a look.

@navin22
Copy link
Member Author

navin22 commented Nov 17, 2017

@dotnet-bot test Windows_NT / Debug Build

@navin22
Copy link
Member Author

navin22 commented Nov 19, 2017

@dotnet-bot test Windows_NT / Release Build

testRunEventsHandler.HandleTestRunComplete(completeArgs, null, null, null);
}

private void OnDiscoveryAbort(ITestDiscoveryEventsHandler2 eventHandler, Exception exception, bool getClientError)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OnDiscoveryAbort [](start = 21, length = 16)

exception handling has been removed.. any impact due to this ?

}
this.channel = connectedArgs.Channel;
this.channel.MessageReceived += this.OnMessageReceived;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MessageReceived [](start = 29, length = 15)

can the processing of a second message begin before the completion of an earlier one..? what is the impact..?

@mayankbansal018
Copy link
Contributor

@dotnet-bot test Windows_NT / Release Build

1 similar comment
@mayankbansal018
Copy link
Contributor

@dotnet-bot test Windows_NT / Release Build

@mayankbansal018 mayankbansal018 merged commit a89c524 into microsoft:master Dec 11, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants