diff --git a/scripts/test.sh b/scripts/test.sh
index e0f22848db..31f30794e9 100644
--- a/scripts/test.sh
+++ b/scripts/test.sh
@@ -113,7 +113,7 @@ function invoke_test()
local dotnet=$(_get_dotnet_path)
local vstest=$TP_OUT_DIR/$TPB_Configuration/$TPB_TargetFrameworkCore/vstest.console.dll
- find ./test -path $PROJECT_NAME_PATTERNS | xargs $dotnet $vstest --parallel
+ find ./test -path $PROJECT_NAME_PATTERNS | xargs --verbose $dotnet $vstest --parallel
}
#
diff --git a/src/Microsoft.TestPlatform.Common/Interfaces/Communication/ICommunicationClient.cs b/src/Microsoft.TestPlatform.Common/Interfaces/Communication/ICommunicationClient.cs
deleted file mode 100644
index 333769ecbb..0000000000
--- a/src/Microsoft.TestPlatform.Common/Interfaces/Communication/ICommunicationClient.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-namespace Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Interfaces
-{
- using System;
-
- ///
- /// Client interface for inter-process communications in test platform.
- ///
- public interface ICommunicationClient
- {
- ///
- /// Event raised when the client is connected to server.
- ///
- event EventHandler ServerConnected;
-
- ///
- /// Event raise when connection with server is broken.
- ///
- event EventHandler ServerDisconnected;
-
- ///
- /// Connect to the server specified in .
- ///
- /// Parameters to connect to server.
- void Start(string connectionInfo);
-
- ///
- /// Close the communication channel and stop the client.
- ///
- void Stop();
- }
-}
diff --git a/src/Microsoft.TestPlatform.Common/Interfaces/Communication/ICommunicationServer.cs b/src/Microsoft.TestPlatform.Common/Interfaces/Communication/ICommunicationServer.cs
deleted file mode 100644
index b5101d4907..0000000000
--- a/src/Microsoft.TestPlatform.Common/Interfaces/Communication/ICommunicationServer.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-namespace Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Interfaces
-{
- using System;
-
- ///
- /// Server interface for inter-process communications in test platform. A server can serve only a single
- /// client.
- ///
- public interface ICommunicationServer
- {
- ///
- /// Event raised when a client is connected to the server.
- ///
- event EventHandler ClientConnected;
-
- ///
- /// Event raised when a client is disconnected from the server.
- ///
- event EventHandler ClientDisconnected;
-
- ///
- /// Starts a communication server.
- ///
- /// Connection parameters for a client to connect.
- string Start();
-
- ///
- /// Stops the server and closes the underlying communication channel.
- ///
- void Stop();
- }
-}
diff --git a/src/Microsoft.TestPlatform.CommunicationUtilities/DataCollectionRequestHandler.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/DataCollectionRequestHandler.cs
index 5674482b9e..6d5908923d 100644
--- a/src/Microsoft.TestPlatform.CommunicationUtilities/DataCollectionRequestHandler.cs
+++ b/src/Microsoft.TestPlatform.CommunicationUtilities/DataCollectionRequestHandler.cs
@@ -121,11 +121,10 @@ public static DataCollectionRequestHandler Create(
ICommunicationManager communicationManager,
IMessageSink messageSink)
{
+ ValidateArg.NotNull(communicationManager, nameof(communicationManager));
+ ValidateArg.NotNull(messageSink, nameof(messageSink));
if (Instance == null)
{
- ValidateArg.NotNull(communicationManager, nameof(communicationManager));
- ValidateArg.NotNull(messageSink, nameof(messageSink));
-
lock (SyncObject)
{
if (Instance == null)
diff --git a/src/Microsoft.TestPlatform.CommunicationUtilities/Friends.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/Friends.cs
index f0e7dcadc7..0e55268a01 100644
--- a/src/Microsoft.TestPlatform.CommunicationUtilities/Friends.cs
+++ b/src/Microsoft.TestPlatform.CommunicationUtilities/Friends.cs
@@ -4,6 +4,7 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Microsoft.TestPlatform.CommunicationUtilities.UnitTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")]
+[assembly: InternalsVisibleTo("Microsoft.TestPlatform.CommunicationUtilities.PlatformTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")]
[assembly: InternalsVisibleTo("vstest.console.UnitTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")]
[assembly: InternalsVisibleTo("Microsoft.TestPlatform.CrossPlatEngine, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")]
[assembly: InternalsVisibleTo("Microsoft.TestPlatform.CrossPlatEngine.UnitTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")]
diff --git a/src/Microsoft.TestPlatform.Common/Interfaces/Communication/CommunicationException.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/Interfaces/Communication/CommunicationException.cs
similarity index 100%
rename from src/Microsoft.TestPlatform.Common/Interfaces/Communication/CommunicationException.cs
rename to src/Microsoft.TestPlatform.CommunicationUtilities/Interfaces/Communication/CommunicationException.cs
diff --git a/src/Microsoft.TestPlatform.Common/Interfaces/Communication/ConnectedEventArgs.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/Interfaces/Communication/ConnectedEventArgs.cs
similarity index 70%
rename from src/Microsoft.TestPlatform.Common/Interfaces/Communication/ConnectedEventArgs.cs
rename to src/Microsoft.TestPlatform.CommunicationUtilities/Interfaces/Communication/ConnectedEventArgs.cs
index e7cd06042f..63747df454 100644
--- a/src/Microsoft.TestPlatform.Common/Interfaces/Communication/ConnectedEventArgs.cs
+++ b/src/Microsoft.TestPlatform.CommunicationUtilities/Interfaces/Communication/ConnectedEventArgs.cs
@@ -24,11 +24,28 @@ public ConnectedEventArgs()
public ConnectedEventArgs(ICommunicationChannel channel)
{
this.Channel = channel;
+ this.Connected = true;
+ }
+
+ public ConnectedEventArgs(Exception faultException)
+ {
+ this.Connected = false;
+ this.Fault = faultException;
}
///
/// Gets the communication channel based on this connection.
///
public ICommunicationChannel Channel { get; private set; }
+
+ ///
+ /// Gets true if it's connected.
+ ///
+ public bool Connected { get; private set; }
+
+ ///
+ /// Gets the exception if it's not connected.
+ ///
+ public Exception Fault { get; private set; }
}
}
diff --git a/src/Microsoft.TestPlatform.Common/Interfaces/Communication/DisconnectedEventArgs.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/Interfaces/Communication/DisconnectedEventArgs.cs
similarity index 100%
rename from src/Microsoft.TestPlatform.Common/Interfaces/Communication/DisconnectedEventArgs.cs
rename to src/Microsoft.TestPlatform.CommunicationUtilities/Interfaces/Communication/DisconnectedEventArgs.cs
diff --git a/src/Microsoft.TestPlatform.Common/Interfaces/Communication/ICommunicationChannel.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/Interfaces/Communication/ICommunicationChannel.cs
similarity index 100%
rename from src/Microsoft.TestPlatform.Common/Interfaces/Communication/ICommunicationChannel.cs
rename to src/Microsoft.TestPlatform.CommunicationUtilities/Interfaces/Communication/ICommunicationChannel.cs
diff --git a/src/Microsoft.TestPlatform.Common/Interfaces/Communication/MessageReceivedEventArgs.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/Interfaces/Communication/MessageReceivedEventArgs.cs
similarity index 100%
rename from src/Microsoft.TestPlatform.Common/Interfaces/Communication/MessageReceivedEventArgs.cs
rename to src/Microsoft.TestPlatform.CommunicationUtilities/Interfaces/Communication/MessageReceivedEventArgs.cs
diff --git a/src/Microsoft.TestPlatform.CommunicationUtilities/Interfaces/ICommunicationEndpoint.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/Interfaces/ICommunicationEndpoint.cs
new file mode 100644
index 0000000000..e44e3e49a9
--- /dev/null
+++ b/src/Microsoft.TestPlatform.CommunicationUtilities/Interfaces/ICommunicationEndpoint.cs
@@ -0,0 +1,32 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Interfaces
+{
+ using System;
+
+ public interface ICommunicationEndPoint
+ {
+ ///
+ /// Event raised when an endPoint is connected.
+ ///
+ event EventHandler Connected;
+
+ ///
+ /// Event raised when an endPoint is disconnected.
+ ///
+ event EventHandler Disconnected;
+
+ ///
+ /// Starts the endPoint and channel.
+ ///
+ /// Address to connect
+ /// Address of the connected endPoint
+ string Start(string endPoint);
+
+ ///
+ /// Stops the endPoint and closes the underlying communication channel.
+ ///
+ void Stop();
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.TestPlatform.CommunicationUtilities/LengthPrefixCommunicationChannel.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/LengthPrefixCommunicationChannel.cs
index 7e8422f172..48e506e5cb 100644
--- a/src/Microsoft.TestPlatform.CommunicationUtilities/LengthPrefixCommunicationChannel.cs
+++ b/src/Microsoft.TestPlatform.CommunicationUtilities/LengthPrefixCommunicationChannel.cs
@@ -55,10 +55,9 @@ public Task NotifyDataAvailable()
// Try read data even if no one is listening to the data stream. Some server
// implementations (like Sockets) depend on the read operation to determine if a
// connection is closed.
- var data = this.reader.ReadString();
-
if (this.MessageReceived != null)
{
+ var data = this.reader.ReadString();
this.MessageReceived.SafeInvoke(this, new MessageReceivedEventArgs { Data = data }, "LengthPrefixCommunicationChannel: MessageReceived");
}
diff --git a/src/Microsoft.TestPlatform.CommunicationUtilities/SocketClient.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/SocketClient.cs
index 587096c2d6..1fc7739f72 100644
--- a/src/Microsoft.TestPlatform.CommunicationUtilities/SocketClient.cs
+++ b/src/Microsoft.TestPlatform.CommunicationUtilities/SocketClient.cs
@@ -17,7 +17,7 @@ namespace Microsoft.VisualStudio.TestPlatform.CommunicationUtilities
///
/// Communication client implementation over sockets.
///
- public class SocketClient : ICommunicationClient
+ public class SocketClient : ICommunicationEndPoint
{
private readonly CancellationTokenSource cancellation;
private readonly TcpClient tcpClient;
@@ -41,16 +41,24 @@ protected SocketClient(Func channelFactory)
}
///
- public event EventHandler ServerConnected;
+ public event EventHandler Connected;
///
- public event EventHandler ServerDisconnected;
+ public event EventHandler Disconnected;
///
- public void Start(string connectionInfo)
+ public string Start(string endPoint)
{
- this.tcpClient.ConnectAsync(IPAddress.Loopback, int.Parse(connectionInfo))
- .ContinueWith(this.OnServerConnected);
+ var ipEndPoint = endPoint.GetIPEndPoint();
+
+ if (EqtTrace.IsVerboseEnabled)
+ {
+ EqtTrace.Verbose("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 ipEndPoint.ToString();
}
///
@@ -65,22 +73,33 @@ public void Stop()
private void OnServerConnected(Task connectAsyncTask)
{
- if (connectAsyncTask.IsFaulted)
+ if (this.Connected != null)
{
- throw connectAsyncTask.Exception;
- }
+ if (connectAsyncTask.IsFaulted)
+ {
+ this.Connected.SafeInvoke(this, new ConnectedEventArgs(connectAsyncTask.Exception), "SocketClient: ServerConnected");
+ if (EqtTrace.IsVerboseEnabled)
+ {
+ EqtTrace.Verbose("Unable to connect to server, Exception occured : {0}", connectAsyncTask.Exception);
+ }
+ }
+ else
+ {
+ this.channel = this.channelFactory(this.tcpClient.GetStream());
+ this.Connected.SafeInvoke(this, new ConnectedEventArgs(this.channel), "SocketClient: ServerConnected");
- this.channel = this.channelFactory(this.tcpClient.GetStream());
- if (this.ServerConnected != null)
- {
- this.ServerConnected.SafeInvoke(this, new ConnectedEventArgs(this.channel), "SocketClient: ServerConnected");
-
- // Start the message loop
- Task.Run(() => this.tcpClient.MessageLoopAsync(
- this.channel,
- this.Stop,
- this.cancellation.Token))
- .ConfigureAwait(false);
+ if (EqtTrace.IsVerboseEnabled)
+ {
+ EqtTrace.Verbose("Connected to server, and starting MessageLoopAsync");
+ }
+
+ // Start the message loop
+ Task.Run(() => this.tcpClient.MessageLoopAsync(
+ this.channel,
+ this.Stop,
+ this.cancellation.Token))
+ .ConfigureAwait(false);
+ }
}
}
@@ -102,7 +121,7 @@ private void Stop(Exception error)
this.channel.Dispose();
this.cancellation.Dispose();
- this.ServerDisconnected?.SafeInvoke(this, new DisconnectedEventArgs(), "SocketClient: ServerDisconnected");
+ this.Disconnected?.SafeInvoke(this, new DisconnectedEventArgs(), "SocketClient: ServerDisconnected");
}
}
}
diff --git a/src/Microsoft.TestPlatform.CommunicationUtilities/SocketServer.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/SocketServer.cs
index a514048a1b..8057e48af6 100644
--- a/src/Microsoft.TestPlatform.CommunicationUtilities/SocketServer.cs
+++ b/src/Microsoft.TestPlatform.CommunicationUtilities/SocketServer.cs
@@ -17,7 +17,7 @@ namespace Microsoft.VisualStudio.TestPlatform.CommunicationUtilities
///
/// Communication server implementation over sockets.
///
- public class SocketServer : ICommunicationServer
+ public class SocketServer : ICommunicationEndPoint
{
private readonly CancellationTokenSource cancellation;
@@ -35,7 +35,7 @@ public class SocketServer : ICommunicationServer
/// Initializes a new instance of the class.
///
public SocketServer()
- : this((stream) => new LengthPrefixCommunicationChannel(stream))
+ : this(stream => new LengthPrefixCommunicationChannel(stream))
{
}
@@ -53,21 +53,19 @@ protected SocketServer(Func channelFactory)
}
///
- public event EventHandler ClientConnected;
+ public event EventHandler Connected;
///
- public event EventHandler ClientDisconnected;
+ public event EventHandler Disconnected;
- ///
- public string Start()
+ public string Start(string endPoint)
{
- var endpoint = new IPEndPoint(IPAddress.Loopback, 0);
- this.tcpListener = new TcpListener(endpoint);
+ this.tcpListener = new TcpListener(endPoint.GetIPEndPoint());
this.tcpListener.Start();
- var connectionInfo = ((IPEndPoint)this.tcpListener.LocalEndpoint).Port.ToString();
- EqtTrace.Info("SocketServer: Listening on port : {0}", connectionInfo);
+ var connectionInfo = ((IPEndPoint)this.tcpListener.LocalEndpoint).ToString();
+ EqtTrace.Info("SocketServer: Listening on end point : {0}", connectionInfo);
// Serves a single client at the moment. An error in connection, or message loop just
// terminates the entire server.
@@ -90,10 +88,15 @@ private void OnClientConnected(TcpClient client)
this.tcpClient = client;
this.tcpClient.Client.NoDelay = true;
- if (this.ClientConnected != null)
+ if (this.Connected != null)
{
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");
+
+ if (EqtTrace.IsVerboseEnabled)
+ {
+ EqtTrace.Verbose("Client connected, and starting MessageLoopAsync");
+ }
// Start the message loop
Task.Run(() => this.tcpClient.MessageLoopAsync(this.channel, error => this.Stop(error), this.cancellation.Token)).ConfigureAwait(false);
@@ -121,7 +124,7 @@ private void Stop(Exception error)
this.channel.Dispose();
this.cancellation.Dispose();
- this.ClientDisconnected?.SafeInvoke(this, new DisconnectedEventArgs { Error = error }, "SocketServer: ClientDisconnected");
+ this.Disconnected?.SafeInvoke(this, new DisconnectedEventArgs { Error = error }, "SocketServer: ClientDisconnected");
}
}
}
diff --git a/src/Microsoft.TestPlatform.CommunicationUtilities/TcpClientExtensions.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/TcpClientExtensions.cs
index c77871e7e3..5ccc88f2d7 100644
--- a/src/Microsoft.TestPlatform.CommunicationUtilities/TcpClientExtensions.cs
+++ b/src/Microsoft.TestPlatform.CommunicationUtilities/TcpClientExtensions.cs
@@ -5,6 +5,7 @@ namespace Microsoft.VisualStudio.TestPlatform.CommunicationUtilities
{
using System;
using System.IO;
+ using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
@@ -69,5 +70,20 @@ internal static Task MessageLoopAsync(
return Task.FromResult(0);
}
+
+ ///
+ /// Converts a given string endpoint address to valid Ipv4, Ipv6 IPEndpoint
+ ///
+ /// Input endpoint address
+ /// IPEndpoint from give string, if its not a valid string. It will create endpoint with loop back address with port 0
+ internal static IPEndPoint GetIPEndPoint(this string value)
+ {
+ if (Uri.TryCreate(string.Concat("tcp://", value), UriKind.Absolute, out Uri uri))
+ {
+ return new IPEndPoint(IPAddress.Parse(uri.Host), uri.Port < 0 ? 0 : uri.Port);
+ }
+
+ return new IPEndPoint(IPAddress.Loopback, 0);
+ }
}
}
diff --git a/src/Microsoft.TestPlatform.CommunicationUtilities/TestRequestSender.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/TestRequestSender.cs
index 041ec198e7..deae160a5c 100644
--- a/src/Microsoft.TestPlatform.CommunicationUtilities/TestRequestSender.cs
+++ b/src/Microsoft.TestPlatform.CommunicationUtilities/TestRequestSender.cs
@@ -6,460 +6,561 @@ namespace Microsoft.VisualStudio.TestPlatform.CommunicationUtilities
using System;
using System.Collections.Generic;
using System.Globalization;
- using System.IO;
+ using System.Net;
using System.Threading;
- using System.Threading.Tasks;
+
using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Interfaces;
using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
+
using CommonResources = Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Resources.Resources;
///
- /// Utility class that facilitates the IPC comunication. Acts as server.
+ /// Test request sender implementation.
///
- public sealed class TestRequestSender : ITestRequestSender
+ public class TestRequestSender : ITestRequestSender
{
- private ICommunicationManager communicationManager;
+ // Time to wait for test host exit
+ private const int ClientProcessExitWaitTimeout = 10 * 1000;
+
+ private readonly IDataSerializer dataSerializer;
+
+ private readonly ManualResetEventSlim connected;
+
+ private readonly ManualResetEventSlim clientExited;
+
+ private readonly int clientExitedWaitTime;
+
+ private ICommunicationEndPoint communicationEndpoint;
+
+ private ICommunicationChannel channel;
- private ITransport transport;
+ private EventHandler onMessageReceived;
- private bool sendMessagesToRemoteHost = true;
+ private Action onDisconnected;
- private IDataSerializer dataSerializer;
+ // Set to 1 if Discovery/Execution is complete, i.e. complete handlers have been invoked
+ private int operationCompleted;
+
+ private ITestMessageEventHandler messageEventHandler;
+
+ private string clientExitErrorMessage;
// Set default to 1, if protocol version check does not happen
// that implies host is using version 1
private int protocolVersion = 1;
private int highestSupportedVersion = 2;
-
- ///
- /// Use to cancel blocking tasks associated with testhost process
- ///
- private CancellationTokenSource clientExitCancellationSource;
-
- private string clientExitErrorMessage;
+ private TestHostConnectionInfo connectionInfo;
///
/// Initializes a new instance of the class.
///
- /// Protocol related information
+ /// Protocol configuration.
/// Transport layer to set up connection
public TestRequestSender(ProtocolConfig protocolConfig, TestHostConnectionInfo connectionInfo)
- : this(new SocketCommunicationManager(), connectionInfo, JsonDataSerializer.Instance, protocolConfig)
+ : this(connectionInfo, JsonDataSerializer.Instance, protocolConfig, ClientProcessExitWaitTimeout)
{
+ this.SetCommunicationEndPoint();
}
- ///
- /// Initializes a new instance of the class.
- ///
- /// Communication Manager for sending and receiving messages.
- /// ConnectionInfo to set up transport layer
- /// Serializer for serialization and deserialization of the messages.
- /// Protocol related information
- internal TestRequestSender(ICommunicationManager communicationManager, TestHostConnectionInfo connectionInfo, IDataSerializer dataSerializer, ProtocolConfig protocolConfig)
+ internal TestRequestSender(
+ TestHostConnectionInfo connectionInfo,
+ IDataSerializer serializer,
+ ProtocolConfig protocolConfig,
+ int clientExitedWaitTime)
{
+ this.dataSerializer = serializer;
+ this.connected = new ManualResetEventSlim(false);
+ this.clientExited = new ManualResetEventSlim(false);
+ this.clientExitedWaitTime = clientExitedWaitTime;
+ this.operationCompleted = 0;
+
this.highestSupportedVersion = protocolConfig.Version;
- this.communicationManager = communicationManager;
// The connectionInfo here is that of RuntimeProvider, so reverse the role of runner.
- connectionInfo.Role = connectionInfo.Role == ConnectionRole.Host
- ? ConnectionRole.Client
- : ConnectionRole.Host;
+ this.connectionInfo.Endpoint = connectionInfo.Endpoint;
+ this.connectionInfo.Role = connectionInfo.Role == ConnectionRole.Host
+ ? ConnectionRole.Client
+ : ConnectionRole.Host;
+ }
- this.transport = new SocketTransport(communicationManager, connectionInfo);
- this.dataSerializer = dataSerializer;
+ ///
+ /// Initializes a new instance of the class.
+ /// Used only for testing to inject communication endpoint.
+ ///
+ /// Communication server implementation.
+ /// ConnectionInfo to set up transport layer
+ /// Serializer implementation.
+ /// Protocol configuration.
+ /// Time to wait for client process exit.
+ internal TestRequestSender(
+ ICommunicationEndPoint communicationEndPoint,
+ TestHostConnectionInfo connectionInfo,
+ IDataSerializer serializer,
+ ProtocolConfig protocolConfig,
+ int clientExitedWaitTime)
+ : this(connectionInfo, serializer, protocolConfig, clientExitedWaitTime)
+ {
+ this.communicationEndpoint = communicationEndPoint;
}
- ///
+ ///
public int InitializeCommunication()
{
- this.clientExitCancellationSource = new CancellationTokenSource();
+ // this.clientExitCancellationSource = new CancellationTokenSource();
this.clientExitErrorMessage = string.Empty;
- return this.transport.Initialize().Port;
- }
- ///
- public bool WaitForRequestHandlerConnection(int clientConnectionTimeout)
- {
- return this.transport.WaitForConnection(clientConnectionTimeout);
- }
+ this.communicationEndpoint.Connected += (sender, args) =>
+ {
+ this.channel = args.Channel;
+ this.connected.Set();
+ };
+ this.communicationEndpoint.Disconnected += (sender, args) =>
+ {
+ // If there's an disconnected event handler, call it
+ this.onDisconnected?.Invoke(args);
+ };
- ///
- public void Dispose()
- {
- this.transport.Dispose();
+ // Server start returns the listener port
+ // return int.Parse(this.communicationServer.Start());
+ var endpoint = this.communicationEndpoint.Start(this.connectionInfo.Endpoint);
+ return endpoint.GetIPEndPoint().Port;
}
- ///
- public void Close()
+ ///
+ public bool WaitForRequestHandlerConnection(int connectionTimeout)
{
- this.Dispose();
- EqtTrace.Info("Closing the connection");
+ return this.connected.Wait(connectionTimeout);
}
- ///
+ ///
public void CheckVersionWithTestHost()
{
- this.communicationManager.SendMessage(MessageType.VersionCheck, payload: this.highestSupportedVersion);
+ // Negotiation follows these steps:
+ // Runner sends highest supported version to Test host
+ // Test host sends the version it can support (must be less than highest) to runner
+ // Error case: test host can send a protocol error if it cannot find a supported version
+ var protocolNegotiated = new ManualResetEvent(false);
+ this.onMessageReceived = (sender, args) =>
+ {
+ var message = this.dataSerializer.DeserializeMessage(args.Data);
- var message = this.communicationManager.ReceiveMessage();
+ if (message.MessageType == MessageType.VersionCheck)
+ {
+ this.protocolVersion = this.dataSerializer.DeserializePayload(message);
- if (message.MessageType == MessageType.VersionCheck)
- {
- this.protocolVersion = this.dataSerializer.DeserializePayload(message);
+ EqtTrace.Info(@"TestRequestSender: VersionCheck Succeeded, NegotiatedVersion = {0}", this.protocolVersion);
+ }
- EqtTrace.Info("TestRequestSender: VersionCheck Succeeded, NegotiatedVersion = {0}", this.protocolVersion);
- }
- else if (message.MessageType == MessageType.ProtocolError)
+ // TRH can also send TestMessage if tracing is enabled, so log it at runner end
+ else if (message.MessageType == MessageType.TestMessage)
+ {
+ // Only Deserialize if Tracing is enabled
+ if (EqtTrace.IsInfoEnabled)
+ {
+ var testMessagePayload = this.dataSerializer.DeserializePayload(message);
+ EqtTrace.Info("TestRequestSender.CheckVersionWithTestHost: " + testMessagePayload.Message);
+ }
+ }
+ else if (message.MessageType == MessageType.ProtocolError)
+ {
+ throw new TestPlatformException(string.Format(CultureInfo.CurrentUICulture, CommonResources.VersionCheckFailed));
+ }
+ else
+ {
+ throw new TestPlatformException(string.Format(
+ CultureInfo.CurrentUICulture,
+ CommonResources.UnexpectedMessage,
+ MessageType.VersionCheck,
+ message.MessageType));
+ }
+
+ protocolNegotiated.Set();
+ };
+ this.channel.MessageReceived += this.onMessageReceived;
+
+ try
{
- // TODO : Payload for ProtocolError needs to finalized.
- throw new TestPlatformException(string.Format(CultureInfo.CurrentUICulture, CommonResources.VersionCheckFailed));
+ // Send the protocol negotiation request. Note that we always serialize this data
+ // without any versioning in the message itself.
+ var data = this.dataSerializer.SerializePayload(MessageType.VersionCheck, this.highestSupportedVersion);
+ this.channel.Send(data);
+
+ // Wait for negotiation response
+ if (!protocolNegotiated.WaitOne(60 * 1000))
+ {
+ throw new TestPlatformException(string.Format(CultureInfo.CurrentUICulture, CommonResources.VersionCheckTimedout));
+ }
}
- else
+ finally
{
- throw new TestPlatformException(string.Format(CultureInfo.CurrentUICulture, CommonResources.UnexpectedMessage, MessageType.VersionCheck, message.MessageType));
+ this.channel.MessageReceived -= this.onMessageReceived;
+ this.onMessageReceived = null;
}
}
- ///
- public void InitializeDiscovery(IEnumerable pathToAdditionalExtensions)
- {
- this.communicationManager.SendMessage(MessageType.DiscoveryInitialize, pathToAdditionalExtensions, version: this.protocolVersion);
- }
+ #region Discovery Protocol
- ///
- public void InitializeExecution(IEnumerable pathToAdditionalExtensions)
+ ///
+ public void InitializeDiscovery(IEnumerable pathToAdditionalExtensions)
{
- this.communicationManager.SendMessage(MessageType.ExecutionInitialize, pathToAdditionalExtensions, version: this.protocolVersion);
+ var message = this.dataSerializer.SerializePayload(
+ MessageType.DiscoveryInitialize,
+ pathToAdditionalExtensions,
+ this.protocolVersion);
+ this.channel.Send(message);
}
///
public void DiscoverTests(DiscoveryCriteria discoveryCriteria, ITestDiscoveryEventsHandler2 discoveryEventsHandler)
{
- try
- {
- this.communicationManager.SendMessage(MessageType.StartDiscovery, discoveryCriteria, version: this.protocolVersion);
-
- var isDiscoveryComplete = false;
-
- // Cycle through the messages that the testhost sends.
- // Currently each of the operations are not separate tasks since they should not each take much time. This is just a notification.
- while (!isDiscoveryComplete)
+ this.messageEventHandler = discoveryEventsHandler;
+ this.onDisconnected = (disconnectedEventArgs) =>
{
- var rawMessage = this.TryReceiveRawMessage();
- if (EqtTrace.IsVerboseEnabled)
- {
- EqtTrace.Verbose("Received message: {0}", rawMessage);
- }
-
- // Send raw message first to unblock handlers waiting to send message to IDEs
- discoveryEventsHandler.HandleRawMessage(rawMessage);
-
- var message = this.dataSerializer.DeserializeMessage(rawMessage);
- if (string.Equals(MessageType.TestCasesFound, message.MessageType))
- {
- var testCases = this.dataSerializer.DeserializePayload>(message);
- discoveryEventsHandler.HandleDiscoveredTests(testCases);
- }
- else if (string.Equals(MessageType.DiscoveryComplete, message.MessageType))
- {
- var discoveryCompletePayload = this.dataSerializer.DeserializePayload(message);
-
- var discoveryCompleteEventArgs = new DiscoveryCompleteEventArgs(discoveryCompletePayload.TotalTests, discoveryCompletePayload.IsAborted);
- discoveryCompleteEventArgs.Metrics = discoveryCompletePayload.Metrics;
- discoveryEventsHandler.HandleDiscoveryComplete(
- discoveryCompleteEventArgs,
- discoveryCompletePayload.LastDiscoveredTests);
- isDiscoveryComplete = true;
- }
- else if (string.Equals(MessageType.TestMessage, message.MessageType))
- {
- var testMessagePayload = this.dataSerializer.DeserializePayload(message);
- discoveryEventsHandler.HandleLogMessage(
- testMessagePayload.MessageLevel,
- testMessagePayload.Message);
- }
- }
- }
- catch (Exception ex)
- {
- this.OnDiscoveryAbort(discoveryEventsHandler, ex);
- }
+ this.OnDiscoveryAbort(discoveryEventsHandler, disconnectedEventArgs.Error, true);
+ };
+ this.onMessageReceived = (sender, args) => this.OnDiscoveryMessageReceived(discoveryEventsHandler, args);
+
+ this.channel.MessageReceived += this.onMessageReceived;
+ var message = this.dataSerializer.SerializePayload(
+ MessageType.StartDiscovery,
+ discoveryCriteria,
+ this.protocolVersion);
+ this.channel.Send(message);
}
+ #endregion
- ///
- /// Ends the session with the test host.
- ///
- public void EndSession()
- {
- // don't try to communicate if connection is broken
- if (!this.sendMessagesToRemoteHost)
- {
- EqtTrace.Error("Connection has been broken: not sending SessionEnd message");
- return;
- }
+ #region Execution Protocol
- this.communicationManager.SendMessage(MessageType.SessionEnd);
+ ///
+ public void InitializeExecution(IEnumerable pathToAdditionalExtensions)
+ {
+ var message = this.dataSerializer.SerializePayload(
+ MessageType.ExecutionInitialize,
+ pathToAdditionalExtensions,
+ this.protocolVersion);
+ this.channel.Send(message);
}
- ///
- /// Executes tests on the sources specified with the criteria mentioned.
- ///
- /// The test run criteria.
- /// The handler for execution events from the test host.
+ ///
public void StartTestRun(TestRunCriteriaWithSources runCriteria, ITestRunEventsHandler eventHandler)
{
- this.StartTestRunAndListenAndReportTestResults(MessageType.StartTestExecutionWithSources, runCriteria, eventHandler);
+ this.messageEventHandler = eventHandler;
+ this.onDisconnected = (disconnectedEventArgs) =>
+ {
+ this.OnTestRunAbort(eventHandler, disconnectedEventArgs.Error, true);
+ };
+ this.onMessageReceived = (sender, args) => this.OnExecutionMessageReceived(sender, args, eventHandler);
+ this.channel.MessageReceived += this.onMessageReceived;
+
+ var message = this.dataSerializer.SerializePayload(
+ MessageType.StartTestExecutionWithSources,
+ runCriteria,
+ this.protocolVersion);
+ this.channel.Send(message);
}
- ///
- /// Executes the specified tests with the criteria mentioned.
- ///
- /// The test run criteria.
- /// The handler for execution events from the test host.
+ ///
public void StartTestRun(TestRunCriteriaWithTests runCriteria, ITestRunEventsHandler eventHandler)
{
- this.StartTestRunAndListenAndReportTestResults(MessageType.StartTestExecutionWithTests, runCriteria, eventHandler);
+ this.messageEventHandler = eventHandler;
+ this.onDisconnected = (disconnectedEventArgs) =>
+ {
+ this.OnTestRunAbort(eventHandler, disconnectedEventArgs.Error, true);
+ };
+ this.onMessageReceived = (sender, args) => this.OnExecutionMessageReceived(sender, args, eventHandler);
+ this.channel.MessageReceived += this.onMessageReceived;
+
+ var message = this.dataSerializer.SerializePayload(
+ MessageType.StartTestExecutionWithTests,
+ runCriteria,
+ this.protocolVersion);
+ this.channel.Send(message);
}
- ///
- /// Send the cancel message to test host
- ///
+ ///
public void SendTestRunCancel()
{
- this.communicationManager.SendMessage(MessageType.CancelTestRun);
+ this.channel?.Send(this.dataSerializer.SerializeMessage(MessageType.CancelTestRun));
}
- ///
- /// Send the Abort test run message
- ///
+ ///
public void SendTestRunAbort()
{
- this.communicationManager.SendMessage(MessageType.AbortTestRun);
+ this.channel?.Send(this.dataSerializer.SerializeMessage(MessageType.AbortTestRun));
}
- ///
- /// Handles exit of the client process.
- ///
- /// Standard Error.
+ #endregion
+
+ ///
+ public void EndSession()
+ {
+ if (!this.IsOperationComplete())
+ {
+ this.channel.Send(this.dataSerializer.SerializeMessage(MessageType.SessionEnd));
+ }
+ }
+
+ ///
public void OnClientProcessExit(string stdError)
{
+ // This method is called on test host exit. If test host has any errors, stdError
+ // provides the crash call stack.
+ EqtTrace.Info("TestRequestSender.OnClientProcessExit: Test host process exited. Standard error: " + stdError);
this.clientExitErrorMessage = stdError;
- this.clientExitCancellationSource.Cancel();
+ this.clientExited.Set();
+
+ // Note that we're not explicitly disconnecting the communication channel; wait for the
+ // channel to determine the disconnection on its own.
}
- private void StartTestRunAndListenAndReportTestResults(
- string messageType,
- object payload,
- ITestRunEventsHandler eventHandler)
+ ///
+ public void Close()
{
- try
- {
- this.communicationManager.SendMessage(messageType, payload, version: this.protocolVersion);
+ this.Dispose();
+ EqtTrace.Info("Closing the connection");
+ }
- // This needs to happen asynchronously.
- Task.Run(() => this.ListenAndReportTestResults(eventHandler));
- }
- catch (Exception exception)
+ ///
+ public void Dispose()
+ {
+ if (this.channel != null)
{
- this.OnTestRunAbort(eventHandler, exception);
+ this.channel.MessageReceived -= this.onMessageReceived;
}
+
+ this.communicationEndpoint.Stop();
}
- private void ListenAndReportTestResults(ITestRunEventsHandler testRunEventsHandler)
+ private void OnExecutionMessageReceived(object sender, MessageReceivedEventArgs messageReceived, ITestRunEventsHandler testRunEventsHandler)
{
- var isTestRunComplete = false;
-
- // Cycle through the messages that the testhost sends.
- // Currently each of the operations are not separate tasks since they should not each take much time. This is just a notification.
- while (!isTestRunComplete)
+ try
{
- try
- {
- var rawMessage = this.TryReceiveRawMessage();
+ var rawMessage = messageReceived.Data;
- // Send raw message first to unblock handlers waiting to send message to IDEs
- testRunEventsHandler.HandleRawMessage(rawMessage);
+ // Send raw message first to unblock handlers waiting to send message to IDEs
+ testRunEventsHandler.HandleRawMessage(rawMessage);
- var message = this.dataSerializer.DeserializeMessage(rawMessage);
- if (string.Equals(MessageType.TestRunStatsChange, message.MessageType))
- {
- var testRunChangedArgs = this.dataSerializer.DeserializePayload(
- message);
+ var message = this.dataSerializer.DeserializeMessage(rawMessage);
+ switch (message.MessageType)
+ {
+ case MessageType.TestRunStatsChange:
+ var testRunChangedArgs = this.dataSerializer.DeserializePayload(message);
testRunEventsHandler.HandleTestRunStatsChange(testRunChangedArgs);
- }
- else if (string.Equals(MessageType.ExecutionComplete, message.MessageType))
- {
- var testRunCompletePayload =
- this.dataSerializer.DeserializePayload(message);
+ break;
+ case MessageType.ExecutionComplete:
+ var testRunCompletePayload = this.dataSerializer.DeserializePayload(message);
testRunEventsHandler.HandleTestRunComplete(
testRunCompletePayload.TestRunCompleteArgs,
testRunCompletePayload.LastRunTests,
testRunCompletePayload.RunAttachments,
testRunCompletePayload.ExecutorUris);
- isTestRunComplete = true;
- }
- else if (string.Equals(MessageType.TestMessage, message.MessageType))
- {
+
+ this.SetOperationComplete();
+ break;
+ case MessageType.TestMessage:
var testMessagePayload = this.dataSerializer.DeserializePayload(message);
- testRunEventsHandler.HandleLogMessage(
- testMessagePayload.MessageLevel,
- testMessagePayload.Message);
- }
- else if (string.Equals(MessageType.LaunchAdapterProcessWithDebuggerAttached, message.MessageType))
- {
+ testRunEventsHandler.HandleLogMessage(testMessagePayload.MessageLevel, testMessagePayload.Message);
+ break;
+ case MessageType.LaunchAdapterProcessWithDebuggerAttached:
var testProcessStartInfo = this.dataSerializer.DeserializePayload(message);
int processId = testRunEventsHandler.LaunchProcessWithDebuggerAttached(testProcessStartInfo);
- this.communicationManager.SendMessage(
- MessageType.LaunchAdapterProcessWithDebuggerAttachedCallback,
- processId,
- version: this.protocolVersion);
- }
- }
- catch (IOException exception)
- {
- // To avoid further communication with remote host
- this.sendMessagesToRemoteHost = false;
+ var data =
+ this.dataSerializer.SerializePayload(
+ MessageType.LaunchAdapterProcessWithDebuggerAttachedCallback,
+ processId,
+ this.protocolVersion);
- this.OnTestRunAbort(testRunEventsHandler, exception);
- isTestRunComplete = true;
- }
- catch (Exception exception)
- {
- this.OnTestRunAbort(testRunEventsHandler, exception);
- isTestRunComplete = true;
+ this.channel.Send(data);
+ break;
}
}
- }
-
- private void CleanupCommunicationIfProcessExit()
- {
- if (this.clientExitCancellationSource != null && this.clientExitCancellationSource.IsCancellationRequested)
+ catch (Exception exception)
{
- this.communicationManager.StopServer();
+ this.OnTestRunAbort(testRunEventsHandler, exception, false);
}
}
- private void OnTestRunAbort(ITestRunEventsHandler testRunEventsHandler, Exception exception)
+ private void OnDiscoveryMessageReceived(ITestDiscoveryEventsHandler2 discoveryEventsHandler, MessageReceivedEventArgs args)
{
try
{
- EqtTrace.Error("Server: TestExecution: Aborting test run because {0}", exception);
+ var rawMessage = args.Data;
- var reason = string.Format(CommonResources.AbortedTestRun, exception?.Message);
-
- // log console message to vstest console
- testRunEventsHandler.HandleLogMessage(TestMessageLevel.Error, reason);
-
- // log console message to vstest console wrapper
- var testMessagePayload = new TestMessagePayload { MessageLevel = TestMessageLevel.Error, Message = reason };
- var rawMessage = this.dataSerializer.SerializePayload(MessageType.TestMessage, testMessagePayload);
- testRunEventsHandler.HandleRawMessage(rawMessage);
-
- // notify test run abort to vstest console wrapper.
- var completeArgs = new TestRunCompleteEventArgs(null, false, true, exception, null, TimeSpan.Zero);
- var payload = new TestRunCompletePayload { TestRunCompleteArgs = completeArgs };
- rawMessage = this.dataSerializer.SerializePayload(MessageType.ExecutionComplete, payload);
- testRunEventsHandler.HandleRawMessage(rawMessage);
+ // Currently each of the operations are not separate tasks since they should not each take much time. This is just a notification.
+ if (EqtTrace.IsInfoEnabled)
+ {
+ EqtTrace.Info("TestRequestSender: Received message: {0}", rawMessage);
+ }
- // notify of a test run complete and bail out.
- testRunEventsHandler.HandleTestRunComplete(completeArgs, null, null, null);
+ // Send raw message first to unblock handlers waiting to send message to IDEs
+ discoveryEventsHandler.HandleRawMessage(rawMessage);
- this.CleanupCommunicationIfProcessExit();
+ var data = this.dataSerializer.DeserializeMessage(rawMessage);
+ switch (data.MessageType)
+ {
+ case MessageType.TestCasesFound:
+ var testCases = this.dataSerializer.DeserializePayload>(data);
+ discoveryEventsHandler.HandleDiscoveredTests(testCases);
+ break;
+ case MessageType.DiscoveryComplete:
+ var discoveryCompletePayload =
+ this.dataSerializer.DeserializePayload(data);
+ var discoveryCompleteEventArgs = new DiscoveryCompleteEventArgs(discoveryCompletePayload.TotalTests, discoveryCompletePayload.IsAborted);
+ discoveryCompleteEventArgs.Metrics = discoveryCompletePayload.Metrics;
+ discoveryEventsHandler.HandleDiscoveryComplete(
+ discoveryCompleteEventArgs,
+ discoveryCompletePayload.LastDiscoveredTests);
+ this.SetOperationComplete();
+ break;
+ case MessageType.TestMessage:
+ var testMessagePayload = this.dataSerializer.DeserializePayload(
+ data);
+ discoveryEventsHandler.HandleLogMessage(
+ testMessagePayload.MessageLevel,
+ testMessagePayload.Message);
+ break;
+ }
}
catch (Exception ex)
{
- EqtTrace.Error(ex);
- throw ex;
+ this.OnDiscoveryAbort(discoveryEventsHandler, ex, false);
}
}
- private void OnDiscoveryAbort(ITestDiscoveryEventsHandler2 eventHandler, Exception exception)
+ private void OnTestRunAbort(ITestRunEventsHandler testRunEventsHandler, Exception exception, bool getClientError)
{
- try
+ if (this.IsOperationComplete())
{
- EqtTrace.Error("Server: TestExecution: Aborting test discovery because {0}", exception);
+ EqtTrace.Verbose("TestRequestSender: OnTestRunAbort: Operation is already complete. Skip error message.");
+ return;
+ }
- var reason = string.Format(CommonResources.AbortedTestDiscovery, exception?.Message);
+ EqtTrace.Verbose("TestRequestSender: OnTestRunAbort: Set operation complete.");
+ this.SetOperationComplete();
- // Log to vstest console
- eventHandler.HandleLogMessage(TestMessageLevel.Error, reason);
+ var reason = this.GetAbortErrorMessage(exception, getClientError);
+ EqtTrace.Error("TestRequestSender: Aborting test run because {0}", reason);
+ this.LogErrorMessage(string.Format(CommonResources.AbortedTestRun, reason));
- // Log to vs ide test output
- var testMessagePayload = new TestMessagePayload { MessageLevel = TestMessageLevel.Error, Message = reason };
- var rawMessage = this.dataSerializer.SerializePayload(MessageType.TestMessage, testMessagePayload);
- eventHandler.HandleRawMessage(rawMessage);
+ // notify test run abort to vstest console wrapper.
+ var completeArgs = new TestRunCompleteEventArgs(null, false, true, exception, null, TimeSpan.Zero);
+ var payload = new TestRunCompletePayload { TestRunCompleteArgs = completeArgs };
+ var rawMessage = this.dataSerializer.SerializePayload(MessageType.ExecutionComplete, payload);
+ testRunEventsHandler.HandleRawMessage(rawMessage);
- // Notify discovery abort to IDE test output
- var payload = new DiscoveryCompletePayload()
- {
- IsAborted = true,
- LastDiscoveredTests = null,
- TotalTests = -1
- };
- rawMessage = this.dataSerializer.SerializePayload(MessageType.DiscoveryComplete, payload);
- eventHandler.HandleRawMessage(rawMessage);
+ // notify of a test run complete and bail out.
+ testRunEventsHandler.HandleTestRunComplete(completeArgs, null, null, null);
+ }
- // Complete discovery
- var discoveryCompleteEventArgs = new DiscoveryCompleteEventArgs(-1, true);
+ private void OnDiscoveryAbort(ITestDiscoveryEventsHandler2 eventHandler, Exception exception, bool getClientError)
+ {
+ if (this.IsOperationComplete())
+ {
+ EqtTrace.Verbose("TestRequestSender: OnDiscoveryAbort: Operation is already complete. Skip error message.");
+ return;
+ }
- eventHandler.HandleDiscoveryComplete(discoveryCompleteEventArgs, null);
+ EqtTrace.Verbose("TestRequestSender: OnDiscoveryAbort: Set operation complete.");
+ this.SetOperationComplete();
- this.CleanupCommunicationIfProcessExit();
- }
- catch (Exception ex)
+ var discoveryCompleteEventArgs = new DiscoveryCompleteEventArgs(-1, true);
+ var reason = this.GetAbortErrorMessage(exception, getClientError);
+ EqtTrace.Error("TestRequestSender: Aborting test discovery because {0}", reason);
+ this.LogErrorMessage(string.Format(CommonResources.AbortedTestDiscovery, reason));
+
+ // Notify discovery abort to IDE test output
+ var payload = new DiscoveryCompletePayload()
{
- EqtTrace.Error(ex);
- throw ex;
- }
+ IsAborted = true,
+ LastDiscoveredTests = null,
+ TotalTests = -1
+ };
+ var rawMessage = this.dataSerializer.SerializePayload(MessageType.DiscoveryComplete, payload);
+ eventHandler.HandleRawMessage(rawMessage);
+
+ // Complete discovery
+ eventHandler.HandleDiscoveryComplete(discoveryCompleteEventArgs, null);
}
- private string TryReceiveRawMessage()
+ private string GetAbortErrorMessage(Exception exception, bool getClientError)
{
- string message = null;
- var receiverMessageTask = this.communicationManager.ReceiveRawMessageAsync(this.clientExitCancellationSource.Token);
- receiverMessageTask.Wait();
- message = receiverMessageTask.Result;
+ EqtTrace.Verbose("TestRequestSender: GetAbortErrorMessage: Exception: " + exception);
- if (message == null)
+ // It is also possible for an operation to abort even if client has not
+ // disconnected, e.g. if there's an error parsing the response from test host. We
+ // want the exception to be available in those scenarios.
+ var reason = exception?.Message;
+ if (getClientError)
{
- EqtTrace.Warning("TestRequestSender: Communication channel with test host is broken.");
- var reason = CommonResources.UnableToCommunicateToTestHost;
- if (!string.IsNullOrWhiteSpace(this.clientExitErrorMessage))
+ EqtTrace.Verbose("TestRequestSender: GetAbortErrorMessage: Client has disconnected. Wait for standard error.");
+
+ // Set a default message and wait for test host to exit for a moment
+ reason = CommonResources.UnableToCommunicateToTestHost;
+ if (this.clientExited.Wait(this.clientExitedWaitTime))
{
+ EqtTrace.Info("TestRequestSender: GetAbortErrorMessage: Received test host error message.");
reason = this.clientExitErrorMessage;
}
- else
- {
- // Test host process has not exited yet. We will wait for exit to allow us gather
- // standard error
- var processWaitEvent = new ManualResetEventSlim();
- try
- {
- EqtTrace.Info("TestRequestSender: Waiting for test host to exit.");
- processWaitEvent.Wait(TimeSpan.FromSeconds(10), this.clientExitCancellationSource.Token);
- // Use a default error message
- EqtTrace.Info("TestRequestSender: Timed out waiting for test host to exit.");
- reason = CommonResources.UnableToCommunicateToTestHost;
- }
- catch (OperationCanceledException)
- {
- EqtTrace.Info("TestRequestSender: Got error message from test host.");
- reason = this.clientExitErrorMessage;
- }
- }
+ EqtTrace.Info("TestRequestSender: GetAbortErrorMessage: Timed out waiting for test host error message.");
+ }
+
+ return reason;
+ }
- EqtTrace.Error("TestRequestSender: Unable to receive message from testhost: {0}", reason);
- throw new IOException(reason);
+ private void LogErrorMessage(string message)
+ {
+ if (this.messageEventHandler == null)
+ {
+ EqtTrace.Error("TestRequestSender.LogErrorMessage: Message event handler not set. Error: " + message);
+ return;
}
- return message;
+ // Log to vstest console
+ this.messageEventHandler.HandleLogMessage(TestMessageLevel.Error, message);
+
+ // Log to vs ide test output
+ var testMessagePayload = new TestMessagePayload { MessageLevel = TestMessageLevel.Error, Message = message };
+ var rawMessage = this.dataSerializer.SerializePayload(MessageType.TestMessage, testMessagePayload);
+ this.messageEventHandler.HandleRawMessage(rawMessage);
+ }
+
+ private bool IsOperationComplete()
+ {
+ return this.operationCompleted == 1;
+ }
+
+ private void SetOperationComplete()
+ {
+ // Complete the currently ongoing operation (Discovery/Execution)
+ Interlocked.CompareExchange(ref this.operationCompleted, 1, 0);
+ }
+
+ private void SetCommunicationEndPoint()
+ {
+ // TODO: Use factory to get the communication endpoint. It will abstract out the type of communication endpoint like socket, shared memory or named pipe etc.,
+ if (this.connectionInfo.Role == ConnectionRole.Client)
+ {
+ this.communicationEndpoint = new SocketClient();
+ if (EqtTrace.IsVerboseEnabled)
+ {
+ EqtTrace.Verbose("TestRequestSender is acting as client");
+ }
+ }
+ else
+ {
+ this.communicationEndpoint = new SocketServer();
+ if (EqtTrace.IsVerboseEnabled)
+ {
+ EqtTrace.Verbose("TestRequestSender is acting as server");
+ }
+ }
}
}
}
diff --git a/src/Microsoft.TestPlatform.CommunicationUtilities/TestRequestSender2.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/TestRequestSender2.cs
deleted file mode 100644
index 81f7cba580..0000000000
--- a/src/Microsoft.TestPlatform.CommunicationUtilities/TestRequestSender2.cs
+++ /dev/null
@@ -1,507 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-namespace Microsoft.VisualStudio.TestPlatform.CommunicationUtilities
-{
- using System;
- using System.Collections.Generic;
- using System.Globalization;
- using System.Threading;
- using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Interfaces;
- using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.ObjectModel;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
- using CommonResources = Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Resources.Resources;
-
- ///
- /// Test request sender implementation.
- ///
- public class TestRequestSender2 : ITestRequestSender
- {
- // Time to wait for test host exit (in seconds)
- private const int CLIENTPROCESSEXITWAIT = 10 * 1000;
-
- private readonly ICommunicationServer communicationServer;
-
- private readonly IDataSerializer dataSerializer;
-
- private readonly ManualResetEventSlim connected;
-
- private readonly ManualResetEventSlim clientExited;
-
- private readonly int clientExitedWaitTime;
-
- private ICommunicationChannel channel;
-
- private EventHandler onMessageReceived;
-
- private Action onDisconnected;
-
- // Set to 1 if Discovery/Execution is complete, i.e. complete handlers have been invoked
- private int operationCompleted;
-
- private ITestMessageEventHandler messageEventHandler;
-
- private string clientExitErrorMessage;
-
- // Set default to 1, if protocol version check does not happen
- // that implies host is using version 1
- private int protocolVersion = 1;
-
- private int highestSupportedVersion = 2;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// Protocol configuration.
- public TestRequestSender2(ProtocolConfig protocolConfig)
- : this(new SocketServer(), JsonDataSerializer.Instance, protocolConfig, CLIENTPROCESSEXITWAIT)
- {
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// Communication server implementation.
- /// Serializer implementation.
- /// Protocol configuration.
- /// Time to wait for client process exit.
- protected TestRequestSender2(
- ICommunicationServer server,
- IDataSerializer serializer,
- ProtocolConfig protocolConfig,
- int clientExitedWaitTime)
- {
- this.communicationServer = server;
- this.dataSerializer = serializer;
- this.connected = new ManualResetEventSlim(false);
- this.clientExited = new ManualResetEventSlim(false);
- this.clientExitedWaitTime = clientExitedWaitTime;
- this.operationCompleted = 0;
-
- this.highestSupportedVersion = protocolConfig.Version;
- }
-
- ///
- public int InitializeCommunication()
- {
- this.communicationServer.ClientConnected += (sender, args) =>
- {
- this.channel = args.Channel;
- this.connected.Set();
- };
- this.communicationServer.ClientDisconnected += (sender, args) =>
- {
- // If there's an disconnected event handler, call it
- this.onDisconnected?.Invoke(args);
- };
-
- // Server start returns the listener port
- return int.Parse(this.communicationServer.Start());
- }
-
- ///
- public bool WaitForRequestHandlerConnection(int connectionTimeout)
- {
- return this.connected.Wait(connectionTimeout);
- }
-
- ///
- public void CheckVersionWithTestHost()
- {
- // Negotiation follows these steps:
- // Runner sends highest supported version to Test host
- // Test host sends the version it can support (must be less than highest) to runner
- // Error case: test host can send a protocol error if it cannot find a supported version
- var protocolNegotiated = new ManualResetEvent(false);
- this.onMessageReceived = (sender, args) =>
- {
- var message = this.dataSerializer.DeserializeMessage(args.Data);
-
- if (message.MessageType == MessageType.VersionCheck)
- {
- this.protocolVersion = this.dataSerializer.DeserializePayload(message);
-
- EqtTrace.Info(@"TestRequestSender: VersionCheck Succeeded, NegotiatedVersion = {0}", this.protocolVersion);
- }
- else if (message.MessageType == MessageType.ProtocolError)
- {
- throw new TestPlatformException(string.Format(CultureInfo.CurrentUICulture, CommonResources.VersionCheckFailed));
- }
- else
- {
- throw new TestPlatformException(string.Format(
- CultureInfo.CurrentUICulture,
- CommonResources.UnexpectedMessage,
- MessageType.VersionCheck,
- message.MessageType));
- }
-
- protocolNegotiated.Set();
- };
- this.channel.MessageReceived += this.onMessageReceived;
-
- try
- {
- // Send the protocol negotiation request. Note that we always serialize this data
- // without any versioning in the message itself.
- var data = this.dataSerializer.SerializePayload(MessageType.VersionCheck, this.highestSupportedVersion);
- this.channel.Send(data);
-
- // Wait for negotiation response
- if (!protocolNegotiated.WaitOne(60 * 1000))
- {
- throw new TestPlatformException(string.Format(CultureInfo.CurrentUICulture, CommonResources.VersionCheckTimedout));
- }
- }
- finally
- {
- this.channel.MessageReceived -= this.onMessageReceived;
- this.onMessageReceived = null;
- }
- }
-
- #region Discovery Protocol
-
- ///
- public void InitializeDiscovery(IEnumerable pathToAdditionalExtensions)
- {
- var message = this.dataSerializer.SerializePayload(
- MessageType.DiscoveryInitialize,
- pathToAdditionalExtensions,
- this.protocolVersion);
- this.channel.Send(message);
- }
-
- ///
- public void DiscoverTests(DiscoveryCriteria discoveryCriteria, ITestDiscoveryEventsHandler2 discoveryEventsHandler)
- {
- this.messageEventHandler = discoveryEventsHandler;
- this.onDisconnected = (disconnectedEventArgs) =>
- {
- this.OnDiscoveryAbort(discoveryEventsHandler, disconnectedEventArgs.Error, true);
- };
- this.onMessageReceived = (sender, args) =>
- {
- try
- {
- var rawMessage = args.Data;
-
- // Currently each of the operations are not separate tasks since they should not each take much time. This is just a notification.
- if (EqtTrace.IsInfoEnabled)
- {
- EqtTrace.Info("TestRequestSender: Received message: {0}", rawMessage);
- }
-
- // Send raw message first to unblock handlers waiting to send message to IDEs
- discoveryEventsHandler.HandleRawMessage(rawMessage);
-
- var data = this.dataSerializer.DeserializeMessage(rawMessage);
- switch (data.MessageType)
- {
- case MessageType.TestCasesFound:
- var testCases = this.dataSerializer.DeserializePayload>(data);
- discoveryEventsHandler.HandleDiscoveredTests(testCases);
- break;
- case MessageType.DiscoveryComplete:
- var discoveryCompletePayload =
- this.dataSerializer.DeserializePayload(data);
-
- var discoveryCompleteEventsArgs = new DiscoveryCompleteEventArgs(discoveryCompletePayload.TotalTests, discoveryCompletePayload.IsAborted);
- discoveryCompleteEventsArgs.Metrics = discoveryCompletePayload.Metrics;
- discoveryEventsHandler.HandleDiscoveryComplete(
- discoveryCompleteEventsArgs,
- discoveryCompletePayload.LastDiscoveredTests);
-
- this.SetOperationComplete();
- break;
- case MessageType.TestMessage:
- var testMessagePayload = this.dataSerializer.DeserializePayload(
- data);
- discoveryEventsHandler.HandleLogMessage(
- testMessagePayload.MessageLevel,
- testMessagePayload.Message);
- break;
- }
- }
- catch (Exception ex)
- {
- this.OnDiscoveryAbort(discoveryEventsHandler, ex, false);
- }
- };
-
- this.channel.MessageReceived += this.onMessageReceived;
- var message = this.dataSerializer.SerializePayload(
- MessageType.StartDiscovery,
- discoveryCriteria,
- this.protocolVersion);
- this.channel.Send(message);
- }
-
- #endregion
-
- #region Execution Protocol
-
- ///
- public void InitializeExecution(IEnumerable pathToAdditionalExtensions)
- {
- var message = this.dataSerializer.SerializePayload(
- MessageType.ExecutionInitialize,
- pathToAdditionalExtensions,
- this.protocolVersion);
- this.channel.Send(message);
- }
-
- ///
- public void StartTestRun(TestRunCriteriaWithSources runCriteria, ITestRunEventsHandler eventHandler)
- {
- this.messageEventHandler = eventHandler;
- this.onDisconnected = (disconnectedEventArgs) =>
- {
- this.OnTestRunAbort(eventHandler, disconnectedEventArgs.Error, true);
- };
- this.onMessageReceived = (sender, args) => this.OnExecutionMessageReceived(sender, args, eventHandler);
- this.channel.MessageReceived += this.onMessageReceived;
-
- var message = this.dataSerializer.SerializePayload(
- MessageType.StartTestExecutionWithSources,
- runCriteria,
- this.protocolVersion);
- this.channel.Send(message);
- }
-
- ///
- public void StartTestRun(TestRunCriteriaWithTests runCriteria, ITestRunEventsHandler eventHandler)
- {
- this.messageEventHandler = eventHandler;
- this.onDisconnected = (disconnectedEventArgs) =>
- {
- this.OnTestRunAbort(eventHandler, disconnectedEventArgs.Error, true);
- };
- this.onMessageReceived = (sender, args) => this.OnExecutionMessageReceived(sender, args, eventHandler);
- this.channel.MessageReceived += this.onMessageReceived;
-
- var message = this.dataSerializer.SerializePayload(
- MessageType.StartTestExecutionWithTests,
- runCriteria,
- this.protocolVersion);
- this.channel.Send(message);
- }
-
- ///
- public void SendTestRunCancel()
- {
- this.channel?.Send(this.dataSerializer.SerializeMessage(MessageType.CancelTestRun));
- }
-
- ///
- public void SendTestRunAbort()
- {
- this.channel?.Send(this.dataSerializer.SerializeMessage(MessageType.AbortTestRun));
- }
-
- #endregion
-
- ///
- public void EndSession()
- {
- if (!this.IsOperationComplete())
- {
- this.channel.Send(this.dataSerializer.SerializeMessage(MessageType.SessionEnd));
- }
- }
-
- ///
- public void OnClientProcessExit(string stdError)
- {
- // This method is called on test host exit. If test host has any errors, stdError
- // provides the crash call stack.
- EqtTrace.Info("TestRequestSender.OnClientProcessExit: Test host process exited. Standard error: " + stdError);
- this.clientExitErrorMessage = stdError;
- this.clientExited.Set();
-
- // Note that we're not explicitly disconnecting the communication channel; wait for the
- // channel to determine the disconnection on its own.
- }
-
- ///
- public void Close()
- {
- this.Dispose();
- EqtTrace.Info("Closing the connection");
- }
-
- ///
- public void Dispose()
- {
- if (this.channel != null)
- {
- this.channel.MessageReceived -= this.onMessageReceived;
- }
-
- this.communicationServer.Stop();
- }
-
- private void OnExecutionMessageReceived(object sender, MessageReceivedEventArgs messageReceived, ITestRunEventsHandler testRunEventsHandler)
- {
- try
- {
- var rawMessage = messageReceived.Data;
-
- // Send raw message first to unblock handlers waiting to send message to IDEs
- testRunEventsHandler.HandleRawMessage(rawMessage);
-
- var message = this.dataSerializer.DeserializeMessage(rawMessage);
- switch (message.MessageType)
- {
- case MessageType.TestRunStatsChange:
- var testRunChangedArgs = this.dataSerializer.DeserializePayload(message);
- testRunEventsHandler.HandleTestRunStatsChange(testRunChangedArgs);
- break;
- case MessageType.ExecutionComplete:
- var testRunCompletePayload = this.dataSerializer.DeserializePayload(message);
-
- testRunEventsHandler.HandleTestRunComplete(
- testRunCompletePayload.TestRunCompleteArgs,
- testRunCompletePayload.LastRunTests,
- testRunCompletePayload.RunAttachments,
- testRunCompletePayload.ExecutorUris);
-
- this.SetOperationComplete();
- break;
- case MessageType.TestMessage:
- var testMessagePayload = this.dataSerializer.DeserializePayload(message);
- testRunEventsHandler.HandleLogMessage(testMessagePayload.MessageLevel, testMessagePayload.Message);
- break;
- case MessageType.LaunchAdapterProcessWithDebuggerAttached:
- var testProcessStartInfo = this.dataSerializer.DeserializePayload(message);
- int processId = testRunEventsHandler.LaunchProcessWithDebuggerAttached(testProcessStartInfo);
-
- var data =
- this.dataSerializer.SerializePayload(
- MessageType.LaunchAdapterProcessWithDebuggerAttachedCallback,
- processId,
- this.protocolVersion);
-
- this.channel.Send(data);
- break;
- }
- }
- catch (Exception exception)
- {
- this.OnTestRunAbort(testRunEventsHandler, exception, false);
- }
- }
-
- private void OnTestRunAbort(ITestRunEventsHandler testRunEventsHandler, Exception exception, bool getClientError)
- {
- if (this.IsOperationComplete())
- {
- EqtTrace.Verbose("TestRequestSender: OnTestRunAbort: Operation is already complete. Skip error message.");
- return;
- }
-
- EqtTrace.Verbose("TestRequestSender: OnTestRunAbort: Set operation complete.");
- this.SetOperationComplete();
-
- var reason = this.GetAbortErrorMessage(exception, getClientError);
- EqtTrace.Error("TestRequestSender: Aborting test run because {0}", reason);
- this.LogErrorMessage(string.Format(CommonResources.AbortedTestRun, reason));
-
- // notify test run abort to vstest console wrapper.
- var completeArgs = new TestRunCompleteEventArgs(null, false, true, exception, null, TimeSpan.Zero);
- var payload = new TestRunCompletePayload { TestRunCompleteArgs = completeArgs };
- var rawMessage = this.dataSerializer.SerializePayload(MessageType.ExecutionComplete, payload);
- testRunEventsHandler.HandleRawMessage(rawMessage);
-
- // notify of a test run complete and bail out.
- testRunEventsHandler.HandleTestRunComplete(completeArgs, null, null, null);
- }
-
- private void OnDiscoveryAbort(ITestDiscoveryEventsHandler2 eventHandler, Exception exception, bool getClientError)
- {
- if (this.IsOperationComplete())
- {
- EqtTrace.Verbose("TestRequestSender: OnDiscoveryAbort: Operation is already complete. Skip error message.");
- return;
- }
-
- EqtTrace.Verbose("TestRequestSender: OnDiscoveryAbort: Set operation complete.");
- this.SetOperationComplete();
-
- var reason = this.GetAbortErrorMessage(exception, getClientError);
- EqtTrace.Error("TestRequestSender: Aborting test discovery because {0}", reason);
- this.LogErrorMessage(string.Format(CommonResources.AbortedTestDiscovery, reason));
-
- // Notify discovery abort to IDE test output
- var payload = new DiscoveryCompletePayload()
- {
- IsAborted = true,
- LastDiscoveredTests = null,
- TotalTests = -1
- };
- var rawMessage = this.dataSerializer.SerializePayload(MessageType.DiscoveryComplete, payload);
- eventHandler.HandleRawMessage(rawMessage);
-
- // Complete discovery
- var discoveryCompleteEventsArgs = new DiscoveryCompleteEventArgs(-1, true);
-
- eventHandler.HandleDiscoveryComplete(discoveryCompleteEventsArgs, null);
- }
-
- private string GetAbortErrorMessage(Exception exception, bool getClientError)
- {
- EqtTrace.Verbose("TestRequestSender: GetAbortErrorMessage: Exception: " + exception);
-
- // It is also possible for an operation to abort even if client has not
- // disconnected, e.g. if there's an error parsing the response from test host. We
- // want the exception to be available in those scenarios.
- var reason = exception?.Message;
- if (getClientError)
- {
- EqtTrace.Verbose("TestRequestSender: GetAbortErrorMessage: Client has disconnected. Wait for standard error.");
-
- // Set a default message and wait for test host to exit for a moment
- reason = CommonResources.UnableToCommunicateToTestHost;
- if (this.clientExited.Wait(this.clientExitedWaitTime))
- {
- EqtTrace.Info("TestRequestSender: GetAbortErrorMessage: Received test host error message.");
- reason = this.clientExitErrorMessage;
- }
-
- EqtTrace.Info("TestRequestSender: GetAbortErrorMessage: Timed out waiting for test host error message.");
- }
-
- return reason;
- }
-
- private void LogErrorMessage(string message)
- {
- if (this.messageEventHandler == null)
- {
- EqtTrace.Error("TestRequestSender.LogErrorMessage: Message event handler not set. Error: " + message);
- return;
- }
-
- // Log to vstest console
- this.messageEventHandler.HandleLogMessage(TestMessageLevel.Error, message);
-
- // Log to vs ide test output
- var testMessagePayload = new TestMessagePayload { MessageLevel = TestMessageLevel.Error, Message = message };
- var rawMessage = this.dataSerializer.SerializePayload(MessageType.TestMessage, testMessagePayload);
- this.messageEventHandler.HandleRawMessage(rawMessage);
- }
-
- private bool IsOperationComplete()
- {
- return this.operationCompleted == 1;
- }
-
- private void SetOperationComplete()
- {
- // Complete the currently ongoing operation (Discovery/Execution)
- Interlocked.CompareExchange(ref this.operationCompleted, 1, 0);
- }
- }
-}
diff --git a/src/Microsoft.TestPlatform.CoreUtilities/Utilities/StringExtensions.cs b/src/Microsoft.TestPlatform.CoreUtilities/Utilities/StringExtensions.cs
index 1e04c64a9b..f7a618c5fe 100644
--- a/src/Microsoft.TestPlatform.CoreUtilities/Utilities/StringExtensions.cs
+++ b/src/Microsoft.TestPlatform.CoreUtilities/Utilities/StringExtensions.cs
@@ -3,6 +3,9 @@
namespace Microsoft.VisualStudio.TestPlatform.CoreUtilities.Extensions
{
+ using System;
+ using System.Net;
+
public static class StringExtensions
{
///
diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Client/Parallel/ParallelProxyExecutionManager.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/Client/Parallel/ParallelProxyExecutionManager.cs
index dfa9d73f64..b1c889af95 100644
--- a/src/Microsoft.TestPlatform.CrossPlatEngine/Client/Parallel/ParallelProxyExecutionManager.cs
+++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Client/Parallel/ParallelProxyExecutionManager.cs
@@ -197,22 +197,17 @@ public bool HandlePartialRunComplete(
return true;
}
- // 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))
- {
- if (EqtTrace.IsVerboseEnabled)
- {
- EqtTrace.Verbose("ParallelProxyExecutionManager: HandlePartialRunComplete: Replace execution manager. Shared: {0}, Aborted: {1}.", this.SharedHosts, testRunCompleteArgs.IsAborted);
- }
- this.RemoveManager(proxyExecutionManager);
- proxyExecutionManager = CreateNewConcurrentManager();
- var parallelEventsHandler = this.GetEventsHandler(proxyExecutionManager);
- this.AddManager(proxyExecutionManager, parallelEventsHandler);
+ if (EqtTrace.IsVerboseEnabled)
+ {
+ EqtTrace.Verbose("ParallelProxyExecutionManager: HandlePartialRunComplete: Replace execution manager. Shared: {0}, Aborted: {1}.", this.SharedHosts, testRunCompleteArgs.IsAborted);
}
+ this.RemoveManager(proxyExecutionManager);
+ proxyExecutionManager = CreateNewConcurrentManager();
+ var parallelEventsHandler = this.GetEventsHandler(proxyExecutionManager);
+ this.AddManager(proxyExecutionManager, parallelEventsHandler);
+
// If cancel is triggered for any one run or abort is requested by test platform, there is no reason to fetch next source
// and queue another test run
if (!testRunCompleteArgs.IsCanceled && !abortRequested)
diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/EventHandlers/TestRequestHandler.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/EventHandlers/TestRequestHandler.cs
index f5a36bcb4e..7989de7478 100644
--- a/src/Microsoft.TestPlatform.CrossPlatEngine/EventHandlers/TestRequestHandler.cs
+++ b/src/Microsoft.TestPlatform.CrossPlatEngine/EventHandlers/TestRequestHandler.cs
@@ -19,281 +19,178 @@ namespace Microsoft.VisualStudio.TestPlatform.CommunicationUtilities
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities;
using Microsoft.VisualStudio.TestPlatform.Utilities;
-
using CrossPlatResources = Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Resources.Resources;
+ using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine;
- ///
- /// Utility class to fecilitate the IPC comunication. Acts as Client.
- ///
public class TestRequestHandler : IDisposable, ITestRequestHandler
{
- private ICommunicationManager communicationManager;
+ private readonly IDataSerializer dataSerializer;
+ private ITestHostManagerFactory testHostManagerFactory;
+ private ICommunicationEndPoint communicationEndPoint;
+ private int protocolVersion = 1;
- private ITransport transport;
+ private TestHostConnectionInfo connectionInfo;
- private IDataSerializer dataSerializer;
+ private int highestSupportedVersion = 2;
+ private JobQueue jobQueue;
+ private ICommunicationChannel channel;
- private Action onAckMessageRecieved;
+ private ManualResetEventSlim requestSenderConnected;
+ private ManualResetEventSlim testHostManagerFactoryReady;
- private int highestSupportedVersion = 2;
+ private ManualResetEventSlim sessionCompleted;
- // Set default to 1, if protocol version check does not happen
- // that implies runner is using version 1
- private int protocolVersion = 1;
+ private Action onAckMessageRecieved;
- public TestRequestHandler(TestHostConnectionInfo connectionInfo)
- : this(new SocketCommunicationManager(), connectionInfo, JsonDataSerializer.Instance)
+ ///
+ /// Initializes a new instance of the .
+ ///
+ public TestRequestHandler(TestHostConnectionInfo connectionInfo) : this(connectionInfo, JsonDataSerializer.Instance)
{
}
- internal TestRequestHandler(ICommunicationManager communicationManager, TestHostConnectionInfo connectionInfo, IDataSerializer dataSerializer)
+ protected TestRequestHandler(TestHostConnectionInfo connectionInfo, ICommunicationEndPoint communicationEndpoint, IDataSerializer dataSerializer, JobQueue jobQueue, Action onAckMessageRecieved)
{
- this.communicationManager = communicationManager;
- this.transport = new SocketTransport(communicationManager, connectionInfo);
+ this.communicationEndPoint = communicationEndpoint;
+ this.connectionInfo = connectionInfo;
this.dataSerializer = dataSerializer;
+ this.requestSenderConnected = new ManualResetEventSlim(false);
+ this.testHostManagerFactoryReady = new ManualResetEventSlim(false);
+ this.sessionCompleted = new ManualResetEventSlim(false);
+ this.onAckMessageRecieved = onAckMessageRecieved;
+ this.jobQueue = jobQueue;
}
- ///
- public void InitializeCommunication()
+ protected TestRequestHandler(TestHostConnectionInfo connectionInfo, IDataSerializer dataSerializer)
{
- this.transport.Initialize();
- }
-
- ///
- public bool WaitForRequestSenderConnection(int connectionTimeout)
- {
- return this.transport.WaitForConnection(connectionTimeout);
- }
+ this.connectionInfo = connectionInfo;
+ this.dataSerializer = dataSerializer;
+ this.requestSenderConnected = new ManualResetEventSlim(false);
+ this.sessionCompleted = new ManualResetEventSlim(false);
+ this.testHostManagerFactoryReady = new ManualResetEventSlim(false);
+ this.onAckMessageRecieved = (message) => { throw new NotImplementedException(); };
- ///
- /// Listens to the commands from server
- ///
- /// the test host manager.
- public void ProcessRequests(ITestHostManagerFactory testHostManagerFactory)
- {
- bool isSessionEnd = false;
+ this.SetCommunicationEndPoint();
- var jobQueue = new JobQueue(
- action => { action(); },
+ this.jobQueue = new JobQueue(
+ (action) => { action(); },
"TestHostOperationQueue",
500,
25000000,
true,
(message) => EqtTrace.Error(message));
+ }
- do
+ ///
+ public virtual void InitializeCommunication()
+ {
+ this.communicationEndPoint.Connected += (sender, connectedArgs) =>
{
- var message = this.communicationManager.ReceiveMessage();
- if (EqtTrace.IsInfoEnabled)
+ if (!connectedArgs.Connected)
{
- EqtTrace.Info("TestRequestHandler.ProcessRequests: received message: {0}", message);
+ requestSenderConnected.Set();
+ throw connectedArgs.Fault;
}
+ this.channel = connectedArgs.Channel;
+ this.channel.MessageReceived += this.OnMessageReceived;
+ requestSenderConnected.Set();
+ };
- switch (message.MessageType)
- {
- case MessageType.VersionCheck:
- var version = this.dataSerializer.DeserializePayload(message);
- this.protocolVersion = Math.Min(version, highestSupportedVersion);
- this.communicationManager.SendMessage(MessageType.VersionCheck, this.protocolVersion);
-
- // Can only do this after InitializeCommunication because TestHost cannot "Send Log" unless communications are initialized
- if (!string.IsNullOrEmpty(EqtTrace.LogFile))
- {
- this.SendLog(TestMessageLevel.Informational, string.Format(CrossPlatResources.TesthostDiagLogOutputFile, EqtTrace.LogFile));
- }
- else if (!string.IsNullOrEmpty(EqtTrace.ErrorOnInitialization))
- {
- this.SendLog(TestMessageLevel.Warning, EqtTrace.ErrorOnInitialization);
- }
-
- break;
-
- case MessageType.DiscoveryInitialize:
- {
- EqtTrace.Info("Discovery Session Initialize.");
- var pathToAdditionalExtensions = this.dataSerializer.DeserializePayload>(message);
- jobQueue.QueueJob(
- () => testHostManagerFactory.GetDiscoveryManager()
- .Initialize(pathToAdditionalExtensions),
- 0);
- break;
- }
-
- case MessageType.StartDiscovery:
- {
- EqtTrace.Info("Discovery started.");
-
- var discoveryEventsHandler = new TestDiscoveryEventHandler(this);
- var discoveryCriteria = this.dataSerializer.DeserializePayload(message);
- jobQueue.QueueJob(
- () => testHostManagerFactory.GetDiscoveryManager()
- .DiscoverTests(discoveryCriteria, discoveryEventsHandler),
- 0);
-
- break;
- }
-
- case MessageType.ExecutionInitialize:
- {
- EqtTrace.Info("Discovery Session Initialize.");
- var pathToAdditionalExtensions = this.dataSerializer.DeserializePayload>(message);
- jobQueue.QueueJob(
- () => testHostManagerFactory.GetExecutionManager()
- .Initialize(pathToAdditionalExtensions),
- 0);
- break;
- }
-
- case MessageType.StartTestExecutionWithSources:
- {
- EqtTrace.Info("Execution started.");
- var testRunEventsHandler = new TestRunEventsHandler(this);
-
- var testRunCriteriaWithSources = this.dataSerializer.DeserializePayload(message);
- jobQueue.QueueJob(
- () =>
- testHostManagerFactory.GetExecutionManager()
- .StartTestRun(
- testRunCriteriaWithSources.AdapterSourceMap,
- testRunCriteriaWithSources.Package,
- testRunCriteriaWithSources.RunSettings,
- testRunCriteriaWithSources.TestExecutionContext,
- this.GetTestCaseEventsHandler(testRunCriteriaWithSources.RunSettings),
- testRunEventsHandler),
- 0);
-
- break;
- }
-
- case MessageType.StartTestExecutionWithTests:
- {
- EqtTrace.Info("Execution started.");
- var testRunEventsHandler = new TestRunEventsHandler(this);
-
- var testRunCriteriaWithTests =
- this.communicationManager.DeserializePayload(message);
-
- jobQueue.QueueJob(
- () =>
- testHostManagerFactory.GetExecutionManager()
- .StartTestRun(
- testRunCriteriaWithTests.Tests,
- testRunCriteriaWithTests.Package,
- testRunCriteriaWithTests.RunSettings,
- testRunCriteriaWithTests.TestExecutionContext,
- this.GetTestCaseEventsHandler(testRunCriteriaWithTests.RunSettings),
- testRunEventsHandler),
- 0);
-
- break;
- }
-
- case MessageType.CancelTestRun:
- jobQueue.Pause();
- testHostManagerFactory.GetExecutionManager().Cancel();
- break;
-
- case MessageType.LaunchAdapterProcessWithDebuggerAttachedCallback:
- this.onAckMessageRecieved?.Invoke(message);
- break;
+ this.communicationEndPoint.Start(connectionInfo.Endpoint);
+ }
- case MessageType.AbortTestRun:
- jobQueue.Pause();
- testHostManagerFactory.GetExecutionManager().Abort();
- break;
+ ///
+ public bool WaitForRequestSenderConnection(int connectionTimeout)
+ {
+ return requestSenderConnected.Wait(connectionTimeout);
+ }
- case MessageType.SessionEnd:
- {
- EqtTrace.Info("Session End message received from server. Closing the connection.");
- isSessionEnd = true;
- this.Close();
- break;
- }
-
- case MessageType.SessionAbort:
- {
- // Dont do anything for now.
- break;
- }
-
- default:
- {
- EqtTrace.Info("Invalid Message types");
- break;
- }
- }
- }
- while (!isSessionEnd);
+ ///
+ public void ProcessRequests(ITestHostManagerFactory testHostManagerFactory)
+ {
+ this.testHostManagerFactory = testHostManagerFactory;
+ this.testHostManagerFactoryReady.Set();
+ this.sessionCompleted.Wait();
}
- ///
+ ///
public void Dispose()
{
- this.transport.Dispose();
+ this.communicationEndPoint.Stop();
+ this.channel?.Dispose();
}
- ///
+ ///
public void Close()
{
this.Dispose();
EqtTrace.Info("Closing the connection !");
}
- ///
+ ///
public void SendTestCases(IEnumerable discoveredTestCases)
{
- this.communicationManager.SendMessage(MessageType.TestCasesFound, discoveredTestCases, this.protocolVersion);
+ var data = this.dataSerializer.SerializePayload(MessageType.TestCasesFound, discoveredTestCases, this.protocolVersion);
+ this.channel.Send(data);
}
- ///
+ ///
public void SendTestRunStatistics(TestRunChangedEventArgs testRunChangedArgs)
{
- this.communicationManager.SendMessage(MessageType.TestRunStatsChange, testRunChangedArgs, this.protocolVersion);
+ var data = this.dataSerializer.SerializePayload(MessageType.TestRunStatsChange, testRunChangedArgs, this.protocolVersion);
+ this.channel.Send(data);
}
- ///
+ ///
public void SendLog(TestMessageLevel messageLevel, string message)
{
- var testMessagePayload = new TestMessagePayload { MessageLevel = messageLevel, Message = message };
- this.communicationManager.SendMessage(MessageType.TestMessage, testMessagePayload, this.protocolVersion);
+ var data = this.dataSerializer.SerializePayload(
+ MessageType.TestMessage,
+ new TestMessagePayload { MessageLevel = messageLevel, Message = message },
+ this.protocolVersion);
+ this.channel.Send(data);
}
- ///
+ ///
public void SendExecutionComplete(
- TestRunCompleteEventArgs testRunCompleteArgs,
- TestRunChangedEventArgs lastChunkArgs,
- ICollection runContextAttachments,
- ICollection executorUris)
+ TestRunCompleteEventArgs testRunCompleteArgs,
+ TestRunChangedEventArgs lastChunkArgs,
+ ICollection runContextAttachments,
+ ICollection executorUris)
{
- var payload = new TestRunCompletePayload
- {
- TestRunCompleteArgs = testRunCompleteArgs,
- LastRunTests = lastChunkArgs,
- RunAttachments = runContextAttachments,
- ExecutorUris = executorUris
- };
-
- this.communicationManager.SendMessage(MessageType.ExecutionComplete, payload, this.protocolVersion);
+ var data = this.dataSerializer.SerializePayload(
+ MessageType.ExecutionComplete,
+ new TestRunCompletePayload
+ {
+ TestRunCompleteArgs = testRunCompleteArgs,
+ LastRunTests = lastChunkArgs,
+ RunAttachments = runContextAttachments,
+ ExecutorUris = executorUris
+ },
+ this.protocolVersion);
+ this.channel.Send(data);
}
- ///
+ ///
public void DiscoveryComplete(DiscoveryCompleteEventArgs discoveryCompleteEventArgs, IEnumerable lastChunk)
{
- var discoveryCompletePayload = new DiscoveryCompletePayload
- {
- TotalTests = discoveryCompleteEventArgs.TotalCount,
- LastDiscoveredTests = discoveryCompleteEventArgs.IsAborted ? null : lastChunk,
- IsAborted = discoveryCompleteEventArgs.IsAborted,
- Metrics = discoveryCompleteEventArgs.Metrics
- };
-
- this.communicationManager.SendMessage(MessageType.DiscoveryComplete, discoveryCompletePayload, this.protocolVersion);
+ var data = this.dataSerializer.SerializePayload(
+ MessageType.DiscoveryComplete,
+ new DiscoveryCompletePayload
+ {
+ TotalTests = discoveryCompleteEventArgs.TotalCount,
+ LastDiscoveredTests = discoveryCompleteEventArgs.IsAborted ? null : lastChunk,
+ IsAborted = discoveryCompleteEventArgs.IsAborted,
+ Metrics = discoveryCompleteEventArgs.Metrics
+ },
+ this.protocolVersion);
+ this.channel.Send(data);
}
- ///
+ ///
public int LaunchProcessWithDebuggerAttached(TestProcessStartInfo testProcessStartInfo)
{
- var waitHandle = new AutoResetEvent(false);
+ var waitHandle = new ManualResetEventSlim(false);
Message ackMessage = null;
this.onAckMessageRecieved = (ackRawMessage) =>
{
@@ -301,24 +198,196 @@ public int LaunchProcessWithDebuggerAttached(TestProcessStartInfo testProcessSta
waitHandle.Set();
};
- this.communicationManager.SendMessage(MessageType.LaunchAdapterProcessWithDebuggerAttached, testProcessStartInfo, this.protocolVersion);
+ var data = dataSerializer.SerializePayload(MessageType.LaunchAdapterProcessWithDebuggerAttached,
+ testProcessStartInfo, protocolVersion);
+
+ this.channel.Send(data);
- waitHandle.WaitOne();
+ EqtTrace.Verbose("Waiting for LaunchAdapterProcessWithDebuggerAttached ack");
+ waitHandle.Wait();
this.onAckMessageRecieved = null;
return this.dataSerializer.DeserializePayload(ackMessage);
}
+ public void OnMessageReceived(object sender, MessageReceivedEventArgs messageReceivedArgs)
+ {
+ var message = this.dataSerializer.DeserializeMessage(messageReceivedArgs.Data);
+
+ if (EqtTrace.IsInfoEnabled)
+ {
+ EqtTrace.Info("TestRequestHandler.ProcessRequests: received message: {0}", message);
+ }
+
+ switch (message.MessageType)
+ {
+ case MessageType.VersionCheck:
+ var version = this.dataSerializer.DeserializePayload(message);
+ this.protocolVersion = Math.Min(version, highestSupportedVersion);
+
+ // Send the negotiated protocol to request sender
+ this.channel.Send(this.dataSerializer.SerializePayload(MessageType.VersionCheck, this.protocolVersion));
+
+ // Can only do this after InitializeCommunication because TestHost cannot "Send Log" unless communications are initialized
+ if (!string.IsNullOrEmpty(EqtTrace.LogFile))
+ {
+ this.SendLog(TestMessageLevel.Informational, string.Format(CrossPlatResources.TesthostDiagLogOutputFile, EqtTrace.LogFile));
+ }
+ else if (!string.IsNullOrEmpty(EqtTrace.ErrorOnInitialization))
+ {
+ this.SendLog(TestMessageLevel.Warning, EqtTrace.ErrorOnInitialization);
+ }
+ break;
+
+ case MessageType.DiscoveryInitialize:
+ {
+ EqtTrace.Info("Discovery Session Initialize.");
+ this.testHostManagerFactoryReady.Wait();
+ var pathToAdditionalExtensions = message.Payload.ToObject>();
+ jobQueue.QueueJob(
+ () =>
+ testHostManagerFactory.GetDiscoveryManager().Initialize(pathToAdditionalExtensions), 0);
+ break;
+ }
+
+ case MessageType.StartDiscovery:
+ {
+ EqtTrace.Info("Discovery started.");
+ this.testHostManagerFactoryReady.Wait();
+ var discoveryEventsHandler = new TestDiscoveryEventHandler(this);
+ var discoveryCriteria = message.Payload.ToObject();
+ jobQueue.QueueJob(
+ () =>
+ testHostManagerFactory.GetDiscoveryManager()
+ .DiscoverTests(discoveryCriteria, discoveryEventsHandler), 0);
+
+ break;
+ }
+
+ case MessageType.ExecutionInitialize:
+ {
+ EqtTrace.Info("Discovery Session Initialize.");
+ this.testHostManagerFactoryReady.Wait();
+ var pathToAdditionalExtensions = message.Payload.ToObject>();
+ jobQueue.QueueJob(
+ () =>
+ testHostManagerFactory.GetExecutionManager().Initialize(pathToAdditionalExtensions), 0);
+ break;
+ }
+
+ case MessageType.StartTestExecutionWithSources:
+ {
+ EqtTrace.Info("Execution started.");
+ var testRunEventsHandler = new TestRunEventsHandler(this);
+ this.testHostManagerFactoryReady.Wait();
+ var testRunCriteriaWithSources = message.Payload.ToObject();
+ jobQueue.QueueJob(
+ () =>
+ testHostManagerFactory.GetExecutionManager()
+ .StartTestRun(
+ testRunCriteriaWithSources.AdapterSourceMap,
+ testRunCriteriaWithSources.Package,
+ testRunCriteriaWithSources.RunSettings,
+ testRunCriteriaWithSources.TestExecutionContext,
+ this.GetTestCaseEventsHandler(testRunCriteriaWithSources.RunSettings),
+ testRunEventsHandler),
+ 0);
+
+ break;
+ }
+
+ case MessageType.StartTestExecutionWithTests:
+ {
+ EqtTrace.Info("Execution started.");
+ var testRunEventsHandler = new TestRunEventsHandler(this);
+ this.testHostManagerFactoryReady.Wait();
+ var testRunCriteriaWithTests =
+ this.dataSerializer.DeserializePayload(message);
+
+ jobQueue.QueueJob(
+ () =>
+ testHostManagerFactory.GetExecutionManager()
+ .StartTestRun(
+ testRunCriteriaWithTests.Tests,
+ testRunCriteriaWithTests.Package,
+ testRunCriteriaWithTests.RunSettings,
+ testRunCriteriaWithTests.TestExecutionContext,
+ this.GetTestCaseEventsHandler(testRunCriteriaWithTests.RunSettings),
+ testRunEventsHandler),
+ 0);
+
+ break;
+ }
+
+ case MessageType.CancelTestRun:
+ jobQueue.Pause();
+ this.testHostManagerFactoryReady.Wait();
+ testHostManagerFactory.GetExecutionManager().Cancel();
+ break;
+
+ case MessageType.LaunchAdapterProcessWithDebuggerAttachedCallback:
+ this.onAckMessageRecieved?.Invoke(message);
+ break;
+
+ case MessageType.AbortTestRun:
+ jobQueue.Pause();
+ this.testHostManagerFactoryReady.Wait();
+ testHostManagerFactory.GetExecutionManager().Abort();
+ break;
+
+ case MessageType.SessionEnd:
+ {
+ EqtTrace.Info("Session End message received from server. Closing the connection.");
+ sessionCompleted.Set();
+ this.Close();
+ break;
+ }
+
+ case MessageType.SessionAbort:
+ {
+ // Dont do anything for now.
+ break;
+ }
+
+ default:
+ {
+ EqtTrace.Info("Invalid Message types");
+ break;
+ }
+ }
+ }
+
private ITestCaseEventsHandler GetTestCaseEventsHandler(string runSettings)
{
ITestCaseEventsHandler testCaseEventsHandler = null;
+ // Listen to test case events only if data collection is enabled
if ((XmlRunSettingsUtilities.IsDataCollectionEnabled(runSettings) && DataCollectionTestCaseEventSender.Instance != null) || XmlRunSettingsUtilities.IsInProcDataCollectionEnabled(runSettings))
{
testCaseEventsHandler = new TestCaseEventsHandler();
- var testEventsPublisher = testCaseEventsHandler as ITestEventsPublisher;
}
return testCaseEventsHandler;
}
+
+ private void SetCommunicationEndPoint()
+ {
+ if (this.connectionInfo.Role == ConnectionRole.Host)
+ {
+ this.communicationEndPoint = new SocketServer();
+ if (EqtTrace.IsVerboseEnabled)
+ {
+ EqtTrace.Verbose("TestRequestHanlder is acting as server");
+ }
+ }
+ else
+ {
+ this.communicationEndPoint = new SocketClient();
+ if (EqtTrace.IsVerboseEnabled)
+ {
+ EqtTrace.Verbose("TestRequestHanlder is acting as client");
+ }
+ }
+ }
+
}
-}
\ No newline at end of file
+}
diff --git a/test/Microsoft.TestPlatform.Common.UnitTests/Microsoft.TestPlatform.Common.UnitTests.csproj b/test/Microsoft.TestPlatform.Common.UnitTests/Microsoft.TestPlatform.Common.UnitTests.csproj
index 038790082b..0933c29489 100644
--- a/test/Microsoft.TestPlatform.Common.UnitTests/Microsoft.TestPlatform.Common.UnitTests.csproj
+++ b/test/Microsoft.TestPlatform.Common.UnitTests/Microsoft.TestPlatform.Common.UnitTests.csproj
@@ -12,17 +12,6 @@
-
- true
-
-
- true
-
-
-
- true
-
-
diff --git a/test/Microsoft.TestPlatform.CommunicationUtilities.PlatformTests/SocketClientTests.cs b/test/Microsoft.TestPlatform.CommunicationUtilities.PlatformTests/SocketClientTests.cs
index 5386451deb..8b450ef6c7 100644
--- a/test/Microsoft.TestPlatform.CommunicationUtilities.PlatformTests/SocketClientTests.cs
+++ b/test/Microsoft.TestPlatform.CommunicationUtilities.PlatformTests/SocketClientTests.cs
@@ -18,7 +18,7 @@ public class SocketClientTests : SocketTestsBase, IDisposable
{
private readonly TcpListener tcpListener;
- private readonly ICommunicationClient socketClient;
+ private readonly ICommunicationEndPoint socketClient;
private TcpClient tcpClient;
@@ -139,7 +139,7 @@ protected override ICommunicationChannel SetupChannel(out ConnectedEventArgs con
ICommunicationChannel channel = null;
ConnectedEventArgs serverConnectedEvent = null;
ManualResetEvent waitEvent = new ManualResetEvent(false);
- this.socketClient.ServerConnected += (sender, eventArgs) =>
+ this.socketClient.Connected += (sender, eventArgs) =>
{
serverConnectedEvent = eventArgs;
channel = eventArgs.Channel;
@@ -163,8 +163,11 @@ protected override ICommunicationChannel SetupChannel(out ConnectedEventArgs con
private ManualResetEvent SetupClientDisconnect(out ICommunicationChannel channel)
{
var waitEvent = new ManualResetEvent(false);
- this.socketClient.ServerDisconnected += (s, e) => { waitEvent.Set(); };
+ this.socketClient.Disconnected += (s, e) => { waitEvent.Set(); };
channel = this.SetupChannel(out ConnectedEventArgs _);
+ channel.MessageReceived += (sender, args) =>
+ {
+ };
return waitEvent;
}
@@ -172,7 +175,7 @@ private string StartLocalServer()
{
this.tcpListener.Start();
- return ((IPEndPoint)this.tcpListener.LocalEndpoint).Port.ToString();
+ return ((IPEndPoint)this.tcpListener.LocalEndpoint).ToString();
}
}
}
diff --git a/test/Microsoft.TestPlatform.CommunicationUtilities.PlatformTests/SocketServerTests.cs b/test/Microsoft.TestPlatform.CommunicationUtilities.PlatformTests/SocketServerTests.cs
index eb3e463f61..69cc154485 100644
--- a/test/Microsoft.TestPlatform.CommunicationUtilities.PlatformTests/SocketServerTests.cs
+++ b/test/Microsoft.TestPlatform.CommunicationUtilities.PlatformTests/SocketServerTests.cs
@@ -9,7 +9,6 @@ namespace Microsoft.TestPlatform.CommunicationUtilities.PlatformTests
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
-
using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities;
using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Interfaces;
using Microsoft.VisualStudio.TestTools.UnitTesting;
@@ -18,8 +17,8 @@ namespace Microsoft.TestPlatform.CommunicationUtilities.PlatformTests
public class SocketServerTests : SocketTestsBase, IDisposable
{
private readonly TcpClient tcpClient;
-
- private readonly ICommunicationServer socketServer;
+ private readonly string defaultConnection = IPAddress.Loopback.ToString() + ":0";
+ private readonly ICommunicationEndPoint socketServer;
public SocketServerTests()
{
@@ -45,17 +44,17 @@ public void Dispose()
[TestMethod]
public async Task SocketServerStartShouldHostServer()
{
- var connectionInfo = this.socketServer.Start();
+ var connectionInfo = this.socketServer.Start(this.defaultConnection);
Assert.IsFalse(string.IsNullOrEmpty(connectionInfo));
- await this.ConnectToServer(int.Parse(connectionInfo));
+ await this.ConnectToServer(connectionInfo.GetIPEndPoint().Port);
Assert.IsTrue(this.tcpClient.Connected);
}
[TestMethod]
public void SocketServerStopShouldStopListening()
{
- var connectionInfo = this.socketServer.Start();
+ var connectionInfo = this.socketServer.Start(this.defaultConnection);
this.socketServer.Stop();
@@ -63,7 +62,7 @@ public void SocketServerStopShouldStopListening()
{
// This method throws ExtendedSocketException (which is private). It is not possible
// to use Assert.ThrowsException in this case.
- this.ConnectToServer(int.Parse(connectionInfo)).GetAwaiter().GetResult();
+ this.ConnectToServer(connectionInfo.GetIPEndPoint().Port).GetAwaiter().GetResult();
}
catch (SocketException)
{
@@ -74,7 +73,7 @@ public void SocketServerStopShouldStopListening()
public void SocketServerStopShouldCloseClient()
{
ManualResetEvent waitEvent = new ManualResetEvent(false);
- this.socketServer.ClientDisconnected += (s, e) => { waitEvent.Set(); };
+ this.socketServer.Disconnected += (s, e) => { waitEvent.Set(); };
this.SetupChannel(out ConnectedEventArgs clientConnected);
this.socketServer.Stop();
@@ -88,7 +87,7 @@ public void SocketServerStopShouldRaiseClientDisconnectedEventOnClientDisconnect
{
DisconnectedEventArgs disconnected = null;
ManualResetEvent waitEvent = new ManualResetEvent(false);
- this.socketServer.ClientDisconnected += (s, e) =>
+ this.socketServer.Disconnected += (s, e) =>
{
disconnected = e;
waitEvent.Set();
@@ -105,13 +104,13 @@ public void SocketServerStopShouldRaiseClientDisconnectedEventOnClientDisconnect
[TestMethod]
public void SocketServerStopShouldCloseChannel()
{
- ManualResetEvent waitEvent = new ManualResetEvent(false);
+ var waitEvent = new ManualResetEventSlim(false);
var channel = this.SetupChannel(out ConnectedEventArgs clientConnected);
- this.socketServer.ClientDisconnected += (s, e) => { waitEvent.Set(); };
+ this.socketServer.Disconnected += (s, e) => { waitEvent.Set(); };
this.socketServer.Stop();
- waitEvent.WaitOne();
+ waitEvent.Wait();
Assert.ThrowsException(() => channel.Send(DUMMYDATA));
}
@@ -120,13 +119,17 @@ public void SocketServerShouldRaiseClientDisconnectedEventIfConnectionIsBroken()
{
DisconnectedEventArgs clientDisconnected = null;
ManualResetEvent waitEvent = new ManualResetEvent(false);
- this.socketServer.ClientDisconnected += (sender, eventArgs) =>
+ this.socketServer.Disconnected += (sender, eventArgs) =>
{
clientDisconnected = eventArgs;
waitEvent.Set();
};
var channel = this.SetupChannel(out ConnectedEventArgs clientConnected);
+ channel.MessageReceived += (sender, args) =>
+ {
+ };
+
// Close the client channel. Message loop should stop.
#if NET451
// tcpClient.Close() calls tcpClient.Dispose().
@@ -139,20 +142,31 @@ public void SocketServerShouldRaiseClientDisconnectedEventIfConnectionIsBroken()
Assert.IsTrue(clientDisconnected.Error is IOException);
}
+ [TestMethod]
+ public async Task SocketEndpointShouldInitializeChannelOnServerConnection()
+ {
+ var channel = this.SetupChannel(out ConnectedEventArgs _);
+
+ await channel.Send(DUMMYDATA);
+
+ Assert.AreEqual(DUMMYDATA, ReadData(this.Client));
+ }
+
protected override ICommunicationChannel SetupChannel(out ConnectedEventArgs connectedEvent)
{
ICommunicationChannel channel = null;
ConnectedEventArgs clientConnectedEvent = null;
ManualResetEvent waitEvent = new ManualResetEvent(false);
- this.socketServer.ClientConnected += (sender, eventArgs) =>
+ this.socketServer.Connected += (sender, eventArgs) =>
{
clientConnectedEvent = eventArgs;
channel = eventArgs.Channel;
waitEvent.Set();
};
- var connectionInfo = this.socketServer.Start();
- this.ConnectToServer(int.Parse(connectionInfo)).GetAwaiter().GetResult();
+ var connectionInfo = this.socketServer.Start(this.defaultConnection);
+ var port = connectionInfo.GetIPEndPoint().Port;
+ this.ConnectToServer(port).GetAwaiter().GetResult();
waitEvent.WaitOne();
connectedEvent = clientConnectedEvent;
diff --git a/test/Microsoft.TestPlatform.CommunicationUtilities.PlatformTests/SocketTestsBase.cs b/test/Microsoft.TestPlatform.CommunicationUtilities.PlatformTests/SocketTestsBase.cs
index 3efaed098c..e8ce03cf8f 100644
--- a/test/Microsoft.TestPlatform.CommunicationUtilities.PlatformTests/SocketTestsBase.cs
+++ b/test/Microsoft.TestPlatform.CommunicationUtilities.PlatformTests/SocketTestsBase.cs
@@ -3,10 +3,13 @@
namespace Microsoft.TestPlatform.CommunicationUtilities.PlatformTests
{
+ using System;
using System.IO;
+ using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
+ using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities;
using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Interfaces;
using Microsoft.VisualStudio.TestTools.UnitTesting;
@@ -26,16 +29,6 @@ public void SocketEndpointStartShouldRaiseServerConnectedEventOnServerConnection
Assert.IsNotNull(connectedEventArgs);
}
- [TestMethod]
- public async Task SocketEndpointShouldInitializeChannelOnServerConnection()
- {
- var channel = this.SetupChannel(out ConnectedEventArgs _);
-
- await channel.Send(DUMMYDATA);
-
- Assert.AreEqual(DUMMYDATA, ReadData(this.Client));
- }
-
[TestMethod]
public void SocketEndpointShouldNotifyChannelOnDataAvailable()
{
diff --git a/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/LengthPrefixCommunicationChannelTests.cs b/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/LengthPrefixCommunicationChannelTests.cs
index 0a5b69473e..57b7369216 100644
--- a/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/LengthPrefixCommunicationChannelTests.cs
+++ b/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/LengthPrefixCommunicationChannelTests.cs
@@ -110,7 +110,7 @@ public async Task MessageReceivedShouldProvideDataOverStream()
}
[TestMethod]
- public async Task NotifyDataAvailableShouldReadStreamIfNoListenersAreRegistered()
+ public async Task NotifyDataAvailableShouldNotReadStreamIfNoListenersAreRegistered()
{
this.writer.Write(DUMMYDATA);
SeekToBeginning(this.stream);
@@ -119,7 +119,7 @@ public async Task NotifyDataAvailableShouldReadStreamIfNoListenersAreRegistered(
// Data is read irrespective of listeners. See note in NotifyDataAvailable
// implementation.
- Assert.AreEqual(11, this.stream.Position);
+ Assert.AreEqual(0, this.stream.Position);
}
[TestMethod]
diff --git a/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/TestRequestSenderTests.cs b/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/TestRequestSenderTests.cs
index 0417cd5813..94dfe51409 100644
--- a/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/TestRequestSenderTests.cs
+++ b/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/TestRequestSenderTests.cs
@@ -1,10 +1,11 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
+// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.TestPlatform.CommunicationUtilities.UnitTests
{
using System;
using System.Collections.Generic;
+ using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
@@ -22,617 +23,743 @@ namespace Microsoft.TestPlatform.CommunicationUtilities.UnitTests
[TestClass]
public class TestRequestSenderTests
{
+ private const int DUMMYPROTOCOLVERSION = 42;
+ private const int DEFAULTPROTOCOLVERSION = 1;
+ private const int DUMMYNEGOTIATEDPROTOCOLVERSION = 41;
+ private const int CLIENTPROCESSEXITWAIT = 10 * 1000;
+
+ private readonly Mock mockServer;
+ private readonly Mock mockDataSerializer;
+ private readonly Mock mockChannel;
+
+ private readonly ConnectedEventArgs connectedEventArgs;
+ private readonly List pathToAdditionalExtensions = new List { "Hello", "World" };
+ private readonly Mock mockDiscoveryEventsHandler;
+ private readonly Mock mockExecutionEventsHandler;
+ private readonly TestRunCriteriaWithSources testRunCriteriaWithSources;
+ private TestHostConnectionInfo connectionInfo;
private ITestRequestSender testRequestSender;
- private Mock mockCommunicationManager;
- private Mock mockDataSerializer;
private ProtocolConfig protocolConfig = new ProtocolConfig { Version = 2 };
- private TestHostConnectionInfo connectionInfo;
- [TestInitialize]
- public void TestInit()
+ public TestRequestSenderTests()
{
this.connectionInfo = new TestHostConnectionInfo
{
- Endpoint = IPAddress.Loopback + ":0",
- Role = ConnectionRole.Client,
- Transport = Transport.Sockets
- };
- this.mockCommunicationManager = new Mock();
+ Endpoint = IPAddress.Loopback + ":123",
+ Role = ConnectionRole.Client,
+ Transport = Transport.Sockets
+ };
+ this.mockChannel = new Mock();
+ this.mockServer = new Mock();
this.mockDataSerializer = new Mock();
- this.testRequestSender = new TestRequestSender(this.mockCommunicationManager.Object, this.connectionInfo, this.mockDataSerializer.Object, this.protocolConfig);
- this.CheckAndSetProtocolVersion();
+ this.testRequestSender = new TestableTestRequestSender(this.mockServer.Object, this.connectionInfo, this.mockDataSerializer.Object, new ProtocolConfig { Version = DUMMYPROTOCOLVERSION });
+
+ this.connectedEventArgs = new ConnectedEventArgs(this.mockChannel.Object);
+ this.mockDiscoveryEventsHandler = new Mock();
+ this.mockExecutionEventsHandler = new Mock();
+ this.testRunCriteriaWithSources = new TestRunCriteriaWithSources(new Dictionary>(), "runsettings", null, null);
}
[TestMethod]
public void InitializeCommunicationShouldHostServerAndAcceptClient()
{
- this.mockCommunicationManager.Setup(mc => mc.HostServer(new IPEndPoint(IPAddress.Loopback, 0))).Returns(new IPEndPoint(IPAddress.Loopback, 123));
-
- var port = this.testRequestSender.InitializeCommunication();
+ var port = this.SetupFakeCommunicationChannel();
- this.mockCommunicationManager.Verify(mc => mc.HostServer(new IPEndPoint(IPAddress.Loopback, 0)), Times.Once);
- this.mockCommunicationManager.Verify(mc => mc.AcceptClientAsync(), Times.Once);
- Assert.AreEqual(port, 123, "Correct port must be returned.");
+ Assert.AreEqual(port, "123", "Correct port must be returned.");
}
[TestMethod]
- public void InitializeCommunicationShouldSetUpClientIfTestRunnerIsClient()
+ public void WaitForRequestHandlerConnectionShouldWaitForClientToConnect()
{
- this.mockCommunicationManager.Setup(mc => mc.SetupClientAsync(new IPEndPoint(IPAddress.Loopback, 0)));
+ this.SetupFakeCommunicationChannel();
- // These settings are that of Test runtime(testhost)
- this.connectionInfo = new TestHostConnectionInfo
- {
- Endpoint = IPAddress.Loopback + ":0",
- Role = ConnectionRole.Host,
- Transport = Transport.Sockets
- };
- this.testRequestSender = new TestRequestSender(this.mockCommunicationManager.Object, this.connectionInfo, this.mockDataSerializer.Object, this.protocolConfig);
- this.CheckAndSetProtocolVersion();
+ var connected = this.testRequestSender.WaitForRequestHandlerConnection(1);
+
+ Assert.IsTrue(connected);
+ }
- this.testRequestSender.InitializeCommunication();
+ [TestMethod]
+ public void CloseShouldCallStopServerOnCommunicationManager()
+ {
+ this.testRequestSender.Close();
- this.mockCommunicationManager.Verify(mc => mc.SetupClientAsync(new IPEndPoint(IPAddress.Loopback, 0)), Times.Once);
+ this.mockServer.Verify(mc => mc.Stop(), Times.Once);
}
[TestMethod]
- public void WaitForRequestHandlerConnectionShouldCallWaitForClientConnection()
+ public void DisposeShouldCallStopServerOnCommunicationManager()
{
- this.testRequestSender.WaitForRequestHandlerConnection(123);
+ this.testRequestSender.Dispose();
- this.mockCommunicationManager.Verify(mc => mc.WaitForClientConnection(123), Times.Once);
+ this.mockServer.Verify(mc => mc.Stop(), Times.Once);
}
[TestMethod]
- public void WaitForRequestHandlerConnectionShouldCallWaitForServerConnectionIfTestRunnerIsClient()
+ public void EndSessionShouldSendSessionEndMessage()
{
- // These settings are that of Test runtime(testhost)
- this.connectionInfo = new TestHostConnectionInfo
- {
- Endpoint = IPAddress.Loopback + ":0",
- Role = ConnectionRole.Host,
- Transport = Transport.Sockets
- };
+ this.SetupFakeCommunicationChannel();
- this.testRequestSender = new TestRequestSender(this.mockCommunicationManager.Object, this.connectionInfo, this.mockDataSerializer.Object, this.protocolConfig);
- this.CheckAndSetProtocolVersion();
+ this.testRequestSender.EndSession();
- this.testRequestSender.WaitForRequestHandlerConnection(123);
+ this.mockDataSerializer.Verify(ds => ds.SerializeMessage(MessageType.SessionEnd), Times.Once);
+ this.mockChannel.Verify(c => c.Send(It.IsAny()), Times.Once);
+ }
- this.mockCommunicationManager.Verify(mc => mc.WaitForServerConnection(123), Times.Once);
+ [TestMethod]
+ public void EndSessionShouldNotSendSessionEndMessageIfClientDisconnected()
+ {
+ this.SetupFakeCommunicationChannel();
+ this.testRequestSender.DiscoverTests(new DiscoveryCriteria(), this.mockDiscoveryEventsHandler.Object);
+ this.RaiseClientDisconnectedEvent();
+
+ this.testRequestSender.EndSession();
+
+ this.mockDataSerializer.Verify(ds => ds.SerializeMessage(MessageType.SessionEnd), Times.Never);
}
[TestMethod]
- public void CloseShouldCallStopServerOnCommunicationManager()
+ public void EndSessionShouldNotSendSessionEndMessageIfTestHostProcessExited()
{
- this.testRequestSender.Close();
+ this.SetupFakeCommunicationChannel();
+ this.testRequestSender.StartTestRun(this.testRunCriteriaWithSources, this.mockExecutionEventsHandler.Object);
+ this.testRequestSender.OnClientProcessExit("Dummy Message");
+
+ this.testRequestSender.EndSession();
- this.mockCommunicationManager.Verify(mc => mc.StopServer(), Times.Once);
+ this.mockDataSerializer.Verify(ds => ds.SerializeMessage(MessageType.SessionEnd), Times.Once);
+ }
+
+ [DataTestMethod]
+ [DataRow("")]
+ [DataRow(" ")]
+ [DataRow(null)]
+ public void OnClientProcessExitShouldSendErrorMessageIfStdErrIsEmpty(string stderr)
+ {
+ this.SetupFakeCommunicationChannel();
+ this.testRequestSender.DiscoverTests(new DiscoveryCriteria(), this.mockDiscoveryEventsHandler.Object);
+
+ this.testRequestSender.OnClientProcessExit(stderr);
+
+ var expectedErrorMessage = "Reason: " + stderr;
+ this.RaiseClientDisconnectedEvent();
+ this.mockDiscoveryEventsHandler.Verify(eh => eh.HandleLogMessage(TestMessageLevel.Error, It.Is(s => s.EndsWith(expectedErrorMessage))), Times.Once);
}
[TestMethod]
- public void DisposeShouldCallStopServerOnCommunicationManager()
+ public void OnClientProcessExitShouldNotSendErrorMessageIfOperationNotStarted()
{
- this.testRequestSender.Dispose();
+ this.SetupFakeCommunicationChannel();
- this.mockCommunicationManager.Verify(mc => mc.StopServer(), Times.Once);
+ this.testRequestSender.OnClientProcessExit("Dummy Stderr");
+
+ this.RaiseClientDisconnectedEvent();
+ this.mockDiscoveryEventsHandler.Verify(eh => eh.HandleLogMessage(TestMessageLevel.Error, It.Is(s => s.Contains("Dummy Stderr"))), Times.Never);
}
[TestMethod]
- public void VersionCheckWithTestHostShouldCheckVersionIfVersionCheckPassesReturnTrue()
+ public void OnClientProcessExitShouldNotSendRawMessageIfOperationNotStarted()
{
- var mockCommunicationManager = new Mock();
- var message = new Message() { MessageType = MessageType.VersionCheck, Payload = this.protocolConfig.Version };
- mockCommunicationManager.Setup(mc => mc.ReceiveMessage()).Returns(message);
- var testRequestSender = new TestRequestSender(mockCommunicationManager.Object, default(TestHostConnectionInfo), this.mockDataSerializer.Object, this.protocolConfig);
+ this.SetupFakeCommunicationChannel();
- testRequestSender.CheckVersionWithTestHost();
+ this.testRequestSender.OnClientProcessExit("Dummy Stderr");
- mockCommunicationManager.Verify(mc => mc.SendMessage(MessageType.VersionCheck, this.protocolConfig.Version), Times.Once);
+ this.RaiseClientDisconnectedEvent();
+ this.mockDataSerializer.Verify(ds => ds.SerializePayload(MessageType.TestMessage, It.Is(p => p.Message.Contains("Dummy Stderr"))), Times.Never);
+ this.mockExecutionEventsHandler.Verify(eh => eh.HandleRawMessage(It.IsAny()), Times.Never);
}
+ #region Version Check Tests
+
[TestMethod]
- public void VersionCheckWithTestHostShouldBeAbleToReceiveProtocolErrorAndThrowException()
+ public void CheckVersionWithTestHostShouldSendHighestSupportedVersion()
{
- var mockCommunicationManager = new Mock();
- var message = new Message() { MessageType = MessageType.ProtocolError, Payload = null };
- mockCommunicationManager.Setup(mc => mc.ReceiveMessage()).Returns(message);
- var testRequestSender = new TestRequestSender(mockCommunicationManager.Object, default(TestHostConnectionInfo), this.mockDataSerializer.Object, this.protocolConfig);
+ this.SetupDeserializeMessage(MessageType.VersionCheck, 99);
+ this.SetupRaiseMessageReceivedOnCheckVersion();
+ this.SetupFakeCommunicationChannel();
- var ex = Assert.ThrowsException(() => testRequestSender.CheckVersionWithTestHost());
+ this.testRequestSender.CheckVersionWithTestHost();
- Assert.AreEqual("Protocol version check failed. Make sure test runner and host are compatible.", ex.Message);
+ this.mockDataSerializer.Verify(ds => ds.SerializePayload(MessageType.VersionCheck, DUMMYPROTOCOLVERSION), Times.Once);
+ this.mockChannel.Verify(mc => mc.Send(It.IsAny()), Times.Once);
}
[TestMethod]
- public void VersionCheckWithTestHostForInvalidMessageShouldThrowException()
+ public void CheckVersionWithTestHostShouldThrowIfTestHostVersionDoesNotMatch()
{
- var mockCommunicationManager = new Mock();
- var message = new Message() { MessageType = MessageType.TestCasesFound, Payload = null };
- mockCommunicationManager.Setup(mc => mc.ReceiveMessage()).Returns(message);
- var testRequestSender = new TestRequestSender(mockCommunicationManager.Object, default(TestHostConnectionInfo), this.mockDataSerializer.Object, this.protocolConfig);
+ this.SetupDeserializeMessage(MessageType.ProtocolError, string.Empty);
+ this.SetupRaiseMessageReceivedOnCheckVersion();
+ this.SetupFakeCommunicationChannel();
+
+ Assert.ThrowsException(() => this.testRequestSender.CheckVersionWithTestHost());
+ }
- var ex = Assert.ThrowsException(() => testRequestSender.CheckVersionWithTestHost());
+ [TestMethod]
+ public void CheckVersionWithTestHostShouldThrowIfUnexpectedResponseIsReceived()
+ {
+ this.SetupDeserializeMessage(MessageType.TestCasesFound, string.Empty);
+ this.SetupRaiseMessageReceivedOnCheckVersion();
+ this.SetupFakeCommunicationChannel();
- Assert.AreEqual("Unexpected message received. Expected MessageType : ProtocolVersion Actual MessageType: TestDiscovery.TestFound", ex.Message);
+ Assert.ThrowsException(() => this.testRequestSender.CheckVersionWithTestHost());
}
+ #endregion
+
+ #region Discovery Protocol Tests
[TestMethod]
public void InitializeDiscoveryShouldSendCommunicationMessageWithCorrectParameters()
{
- var paths = new List() { "Hello", "World" };
- this.CheckAndSetProtocolVersion();
- this.testRequestSender.InitializeDiscovery(paths);
+ this.SetupFakeCommunicationChannel();
- this.mockCommunicationManager.Verify(mc => mc.SendMessage(MessageType.DiscoveryInitialize, paths, this.protocolConfig.Version), Times.Once);
+ this.testRequestSender.InitializeDiscovery(this.pathToAdditionalExtensions);
+
+ this.mockDataSerializer.Verify(d => d.SerializePayload(MessageType.DiscoveryInitialize, this.pathToAdditionalExtensions, 1), Times.Once);
+ this.mockChannel.Verify(mc => mc.Send(It.IsAny()), Times.Once);
}
[TestMethod]
- public void InitializeExecutionShouldSendCommunicationMessageWithCorrectParameters()
+ public void InitializeDiscoveryShouldSendCommunicationMessageWithCorrectParametersWithVersion()
{
- var paths = new List() { "Hello", "World" };
- this.testRequestSender.InitializeExecution(paths);
+ this.SetupFakeChannelWithVersionNegotiation(DUMMYNEGOTIATEDPROTOCOLVERSION);
+
+ this.testRequestSender.InitializeDiscovery(this.pathToAdditionalExtensions);
- this.mockCommunicationManager.Verify(mc => mc.SendMessage(MessageType.ExecutionInitialize, paths, this.protocolConfig.Version), Times.Once);
+ this.mockDataSerializer.Verify(d => d.SerializePayload(MessageType.DiscoveryInitialize, this.pathToAdditionalExtensions, DUMMYNEGOTIATEDPROTOCOLVERSION), Times.Once);
}
[TestMethod]
- public void DiscoverTestsShouldCallHandleDiscoveredTestsOnTestCaseEvent()
+ public void DiscoverTestsShouldSendStartDiscoveryMessageOnChannel()
{
- var sources = new List() { "Hello", "World" };
- string settingsXml = "SettingsXml";
- var mockHandler = new Mock();
- var discoveryCriteria = new DiscoveryCriteria(sources, 100, settingsXml);
+ this.SetupFakeCommunicationChannel();
- var testCases = new List() { new TestCase("x.y.z", new Uri("x://y"), "x.dll") };
- var rawMessage = "OnDiscoveredTests";
- var message = new Message() { MessageType = MessageType.TestCasesFound, Payload = null };
+ this.testRequestSender.DiscoverTests(new DiscoveryCriteria(), this.mockDiscoveryEventsHandler.Object);
- this.SetupReceiveRawMessageAsyncAndDeserializeMessageAndInitialize(rawMessage, message);
- this.mockDataSerializer.Setup(ds => ds.DeserializePayload>(message)).Returns(testCases);
+ this.mockDataSerializer.Verify(
+ s => s.SerializePayload(MessageType.StartDiscovery, It.IsAny(), DEFAULTPROTOCOLVERSION),
+ Times.Once);
+ this.mockChannel.Verify(mc => mc.Send(It.IsAny()), Times.Once);
+ }
- var completePayload = new DiscoveryCompletePayload()
- {
- IsAborted = false,
- LastDiscoveredTests = null,
- TotalTests = 1
- };
- var completeMessage = new Message() { MessageType = MessageType.DiscoveryComplete, Payload = null };
- mockHandler.Setup(mh => mh.HandleDiscoveredTests(testCases)).Callback(
- () =>
- {
- this.mockDataSerializer.Setup(ds => ds.DeserializeMessage(It.IsAny())).Returns(completeMessage);
- this.mockDataSerializer.Setup(ds => ds.DeserializePayload(completeMessage)).Returns(completePayload);
- });
+ [TestMethod]
+ public void DiscoverTestsShouldSendStartDiscoveryMessageOnChannelWithVersion()
+ {
+ this.SetupFakeChannelWithVersionNegotiation(DUMMYNEGOTIATEDPROTOCOLVERSION);
- this.testRequestSender.DiscoverTests(discoveryCriteria, mockHandler.Object);
+ this.testRequestSender.DiscoverTests(new DiscoveryCriteria(), this.mockDiscoveryEventsHandler.Object);
- this.mockCommunicationManager.Verify(mc => mc.SendMessage(MessageType.StartDiscovery, discoveryCriteria, this.protocolConfig.Version), Times.Once);
- this.mockDataSerializer.Verify(ds => ds.DeserializeMessage(rawMessage), Times.Exactly(2));
- mockHandler.Verify(mh => mh.HandleDiscoveredTests(testCases), Times.Once);
- mockHandler.Verify(mh => mh.HandleRawMessage(rawMessage), Times.Exactly(2));
+ this.mockDataSerializer.Verify(
+ s => s.SerializePayload(MessageType.StartDiscovery, It.IsAny(), DUMMYNEGOTIATEDPROTOCOLVERSION),
+ Times.Once);
}
[TestMethod]
- public void DiscoverTestsShouldCallHandleLogMessageOnTestMessage()
+ public void DiscoverTestsShouldNotifyRawMessageOnMessageReceived()
{
- var sources = new List() { "Hello", "World" };
- string settingsXml = "SettingsXml";
- var mockHandler = new Mock();
- var discoveryCriteria = new DiscoveryCriteria(sources, 100, settingsXml);
+ this.SetupDeserializeMessage(MessageType.TestMessage, new TestMessagePayload());
+ this.SetupFakeCommunicationChannel();
- var rawMessage = "TestMessage";
- var messagePayload = new TestMessagePayload() { MessageLevel = TestMessageLevel.Error, Message = rawMessage };
- var message = new Message() { MessageType = MessageType.TestMessage, Payload = null };
+ this.testRequestSender.DiscoverTests(new DiscoveryCriteria(), this.mockDiscoveryEventsHandler.Object);
- this.SetupReceiveRawMessageAsyncAndDeserializeMessageAndInitialize(rawMessage, message);
- this.mockDataSerializer.Setup(ds => ds.DeserializePayload(message)).Returns(messagePayload);
+ this.RaiseMessageReceivedEvent();
+ this.mockDiscoveryEventsHandler.Verify(eh => eh.HandleRawMessage("DummyData"), Times.Once);
+ }
- var completePayload = new DiscoveryCompletePayload()
- {
- IsAborted = false,
- LastDiscoveredTests = null,
- TotalTests = 1
- };
- var completeMessage = new Message() { MessageType = MessageType.DiscoveryComplete, Payload = null };
- mockHandler.Setup(mh => mh.HandleLogMessage(TestMessageLevel.Error, rawMessage)).Callback(
- () =>
- {
- this.mockDataSerializer.Setup(ds => ds.DeserializeMessage(It.IsAny())).Returns(completeMessage);
- this.mockDataSerializer.Setup(ds => ds.DeserializePayload(completeMessage)).Returns(completePayload);
- });
+ [TestMethod]
+ public void DiscoverTestsShouldNotifyDiscoveredTestsOnTestCasesFoundMessageReceived()
+ {
+ this.SetupDeserializeMessage>(MessageType.TestCasesFound, new TestCase[2]);
+ this.SetupFakeCommunicationChannel();
- this.testRequestSender.DiscoverTests(discoveryCriteria, mockHandler.Object);
+ this.testRequestSender.DiscoverTests(new DiscoveryCriteria(), this.mockDiscoveryEventsHandler.Object);
- this.mockCommunicationManager.Verify(mc => mc.SendMessage(MessageType.StartDiscovery, discoveryCriteria, this.protocolConfig.Version), Times.Once);
- this.mockDataSerializer.Verify(ds => ds.DeserializeMessage(rawMessage), Times.Exactly(2));
- mockHandler.Verify(mh => mh.HandleLogMessage(TestMessageLevel.Error, rawMessage), Times.Once);
- mockHandler.Verify(mh => mh.HandleRawMessage(rawMessage), Times.Exactly(2));
+ this.RaiseMessageReceivedEvent();
+ this.mockDiscoveryEventsHandler.Verify(eh => eh.HandleDiscoveredTests(It.Is>(t => t.Count() == 2)));
}
[TestMethod]
- public void DiscoverTestsShouldCallHandleDiscoveryCompleteOnDiscoveryCompletion()
+ public void DiscoverTestsShouldNotifyDiscoveryCompleteOnCompleteMessageReceived()
{
- var sources = new List() { "Hello", "World" };
- string settingsXml = "SettingsXml";
- var mockHandler = new Mock();
- var discoveryCriteria = new DiscoveryCriteria(sources, 100, settingsXml);
+ var completePayload = new DiscoveryCompletePayload { TotalTests = 10, IsAborted = false };
+ this.SetupDeserializeMessage(MessageType.DiscoveryComplete, completePayload);
+ this.SetupFakeCommunicationChannel();
- var rawMessage = "RunComplete";
- var completePayload = new DiscoveryCompletePayload()
- {
- IsAborted = false,
- LastDiscoveredTests = null,
- TotalTests = 1
- };
- var message = new Message() { MessageType = MessageType.DiscoveryComplete, Payload = null };
+ this.testRequestSender.DiscoverTests(new DiscoveryCriteria(), this.mockDiscoveryEventsHandler.Object);
+
+ this.RaiseMessageReceivedEvent();
+ this.mockDiscoveryEventsHandler.Verify(eh => eh.HandleDiscoveryComplete(It.Is(dc => dc.IsAborted == false && dc.TotalCount == 10), null));
+ }
- this.SetupReceiveRawMessageAsyncAndDeserializeMessageAndInitialize(rawMessage, message);
- this.mockDataSerializer.Setup(ds => ds.DeserializePayload(message)).Returns(completePayload);
+ [TestMethod]
+ public void DiscoverTestShouldNotifyLogMessageOnTestMessageReceived()
+ {
+ var message = new TestMessagePayload { MessageLevel = TestMessageLevel.Error, Message = "Message1" };
+ this.SetupDeserializeMessage(MessageType.TestMessage, message);
+ this.SetupFakeCommunicationChannel();
- this.testRequestSender.DiscoverTests(discoveryCriteria, mockHandler.Object);
+ this.testRequestSender.DiscoverTests(new DiscoveryCriteria(), this.mockDiscoveryEventsHandler.Object);
- this.mockCommunicationManager.Verify(mc => mc.SendMessage(MessageType.StartDiscovery, discoveryCriteria, this.protocolConfig.Version), Times.Once);
- this.mockDataSerializer.Verify(ds => ds.DeserializeMessage(rawMessage), Times.Once);
- mockHandler.Verify(mh => mh.HandleDiscoveryComplete(It.IsAny(), null), Times.Once);
- mockHandler.Verify(mh => mh.HandleRawMessage(rawMessage), Times.Once);
+ this.RaiseMessageReceivedEvent();
+ this.mockDiscoveryEventsHandler.Verify(eh => eh.HandleLogMessage(TestMessageLevel.Error, "Message1"));
}
[TestMethod]
- public void DiscoverTestsShouldCollectMetricsOnHandleDiscoveryComplete()
+ public void DiscoverTestShouldNotifyLogMessageIfExceptionThrownOnMessageReceived()
{
- var dict = new Dictionary();
- dict.Add("DummyMessage", "DummyValue");
+ this.SetupExceptionOnMessageReceived();
+ this.SetupFakeCommunicationChannel();
- var mockHandler = new Mock();
- var rawMessage = "RunComplete";
- var completePayload = new DiscoveryCompletePayload()
- {
- IsAborted = false,
- LastDiscoveredTests = null,
- TotalTests = 1,
- Metrics = dict
- };
- var message = new Message() { MessageType = MessageType.DiscoveryComplete, Payload = null };
+ this.testRequestSender.DiscoverTests(new DiscoveryCriteria(), this.mockDiscoveryEventsHandler.Object);
- this.SetupReceiveRawMessageAsyncAndDeserializeMessageAndInitialize(rawMessage, message);
- this.mockDataSerializer.Setup(ds => ds.DeserializePayload(message)).Returns(completePayload);
+ this.RaiseMessageReceivedEvent();
+ this.mockDiscoveryEventsHandler.Verify(eh => eh.HandleLogMessage(TestMessageLevel.Error, It.Is(s => s.Contains("Dummy Message"))));
+ this.mockDiscoveryEventsHandler.Verify(eh => eh.HandleRawMessage("SerializedMessage"), Times.Once);
+ }
- DiscoveryCompleteEventArgs actualDiscoveryCompleteEventArgs = null;
- mockHandler.Setup(md => md.HandleDiscoveryComplete(It.IsAny(), null))
- .Callback>(
- (discoveryCompleteEventArgs, testCase) =>
- {
- actualDiscoveryCompleteEventArgs = discoveryCompleteEventArgs;
- });
+ [TestMethod]
+ public void DiscoverTestShouldNotifyDiscoveryCompleteIfExceptionThrownOnMessageReceived()
+ {
+ this.SetupExceptionOnMessageReceived();
+ this.SetupFakeCommunicationChannel();
- // Act.
- this.testRequestSender.DiscoverTests(new DiscoveryCriteria(), mockHandler.Object);
+ this.testRequestSender.DiscoverTests(new DiscoveryCriteria(), this.mockDiscoveryEventsHandler.Object);
- // Verify
- Assert.AreEqual(actualDiscoveryCompleteEventArgs.Metrics, dict);
+ this.RaiseMessageReceivedEvent();
+ this.mockDiscoveryEventsHandler.Verify(eh => eh.HandleDiscoveryComplete(It.Is(dc => dc.IsAborted == true && dc.TotalCount == -1), null));
}
[TestMethod]
- public void DiscoverTestsShouldHandleExceptionOnSendMessage()
+ public void DiscoverTestsShouldNotAbortDiscoveryIfClientDisconnectedAndOperationIsComplete()
{
- var sources = new List() { "Hello", "World" };
- string settingsXml = "SettingsXml";
- var mockHandler = new Mock();
- var discoveryCriteria = new DiscoveryCriteria(sources, 100, settingsXml);
- var exception = new Exception();
- this.mockCommunicationManager.Setup(cm => cm.SendMessage(MessageType.StartDiscovery, discoveryCriteria, this.protocolConfig.Version))
- .Throws(exception);
+ var completePayload = new DiscoveryCompletePayload { TotalTests = 10, IsAborted = false };
+ this.SetupDeserializeMessage(MessageType.DiscoveryComplete, completePayload);
+ this.SetupFakeCommunicationChannel();
+ this.testRequestSender.DiscoverTests(new DiscoveryCriteria(), this.mockDiscoveryEventsHandler.Object);
+ this.RaiseMessageReceivedEvent(); // Raise discovery complete
- this.testRequestSender.DiscoverTests(discoveryCriteria, mockHandler.Object);
+ this.RaiseClientDisconnectedEvent();
- mockHandler.Verify(mh => mh.HandleDiscoveryComplete(It.IsAny(), null), Times.Once);
- mockHandler.Verify(mh => mh.HandleLogMessage(TestMessageLevel.Error, It.IsAny()), Times.Once);
- mockHandler.Verify(mh => mh.HandleRawMessage(It.IsAny()), Times.Exactly(2));
+ this.mockDiscoveryEventsHandler.Verify(eh => eh.HandleLogMessage(TestMessageLevel.Error, It.IsAny()), Times.Never);
+ this.mockDiscoveryEventsHandler.Verify(eh => eh.HandleDiscoveryComplete(new DiscoveryCompleteEventArgs(-1, true), null), Times.Never);
}
[TestMethod]
- public void DiscoverTestsShouldHandleDiscoveryCompleteOnCommunicationFailure()
+ public void DiscoverTestShouldNotifyLogMessageIfClientDisconnected()
{
- this.DiscoverTestsErrorScenarioTestTemplates(CommunicationUtilitiesResources.UnableToCommunicateToTestHost, (s) => { });
+ // Expect default error message since we've not set any client exit message
+ var expectedErrorMessage = "Reason: Unable to communicate";
+ this.SetupFakeCommunicationChannel();
+ this.mockDataSerializer.Setup(ds => ds.SerializePayload(MessageType.TestMessage, It.Is(p => p.Message.Contains(expectedErrorMessage))))
+ .Returns("Serialized error");
+ this.testRequestSender.DiscoverTests(new DiscoveryCriteria(), this.mockDiscoveryEventsHandler.Object);
+
+ this.RaiseClientDisconnectedEvent();
+
+ this.mockDiscoveryEventsHandler.Verify(eh => eh.HandleLogMessage(TestMessageLevel.Error, It.Is(s => s.Contains(expectedErrorMessage))));
+ this.mockDiscoveryEventsHandler.Verify(eh => eh.HandleRawMessage(It.Is(s => !string.IsNullOrEmpty(s) && s.Equals("Serialized error"))), Times.Once);
}
[TestMethod]
- public void DiscoverTestsShouldHandleDiscoveryCompleteOnProcessExit()
+ public void DiscoverTestShouldNotifyLogMessageIfClientDisconnectedWithClientExit()
{
- this.DiscoverTestsErrorScenarioTestTemplates("Error Message", (s) => this.testRequestSender.OnClientProcessExit(s));
+ this.SetupFakeCommunicationChannel();
+ this.mockDataSerializer.Setup(ds => ds.SerializePayload(MessageType.TestMessage, It.Is(p => p.Message.Contains("Dummy Stderr"))))
+ .Returns("Serialized Stderr");
+ this.testRequestSender.DiscoverTests(new DiscoveryCriteria(), this.mockDiscoveryEventsHandler.Object);
+ this.testRequestSender.OnClientProcessExit("Dummy Stderr");
+
+ this.RaiseClientDisconnectedEvent();
+
+ this.mockDiscoveryEventsHandler.Verify(eh => eh.HandleLogMessage(TestMessageLevel.Error, It.Is(s => s.Contains("Dummy Stderr"))), Times.Once);
+ this.mockDiscoveryEventsHandler.Verify(eh => eh.HandleRawMessage(It.Is(s => !string.IsNullOrEmpty(s) && s.Equals("Serialized Stderr"))), Times.Once);
}
- public void DiscoverTestsErrorScenarioTestTemplates(string errorMessage, Action exitCallback)
+ [TestMethod]
+ public void DiscoverTestShouldNotifyDiscoveryCompleteIfClientDisconnected()
{
- var sources = new List() { "Hello", "World" };
- string settingsXml = "SettingsXml";
- var mockHandler = new Mock();
- var discoveryCriteria = new DiscoveryCriteria(sources, 100, settingsXml);
- this.mockCommunicationManager.Setup(cm => cm.ReceiveRawMessageAsync(It.IsAny()))
- .Returns(Task.FromResult((string)null))
- .Callback(() => exitCallback(errorMessage));
+ this.SetupFakeCommunicationChannel();
+ this.testRequestSender.DiscoverTests(new DiscoveryCriteria(), this.mockDiscoveryEventsHandler.Object);
+
+ this.RaiseClientDisconnectedEvent();
+
+ this.mockDiscoveryEventsHandler.Verify(eh => eh.HandleDiscoveryComplete(It.Is(dc => dc.IsAborted == true && dc.TotalCount == -1), null));
+ }
- this.mockCommunicationManager.Setup(mc => mc.HostServer(It.IsAny()))
- .Returns(new IPEndPoint(IPAddress.Loopback, 0));
+ #endregion
- this.testRequestSender.InitializeCommunication();
- this.testRequestSender.DiscoverTests(discoveryCriteria, mockHandler.Object);
+ #region Execution Protocol Tests
- mockHandler.Verify(mh => mh.HandleDiscoveryComplete(It.IsAny(), null), Times.Once);
- mockHandler.Verify(mh => mh.HandleLogMessage(TestMessageLevel.Error, string.Format(CommunicationUtilitiesResources.AbortedTestDiscovery, errorMessage)), Times.Once);
- mockHandler.Verify(mh => mh.HandleRawMessage(It.IsAny()), Times.Exactly(2));
+ [TestMethod]
+ public void InitializeExecutionShouldSendCommunicationMessageWithCorrectParameters()
+ {
+ this.SetupFakeCommunicationChannel();
+
+ this.testRequestSender.InitializeExecution(this.pathToAdditionalExtensions);
+
+ this.mockDataSerializer.Verify(d => d.SerializePayload(MessageType.ExecutionInitialize, this.pathToAdditionalExtensions, 1), Times.Once);
+ this.mockChannel.Verify(mc => mc.Send(It.IsAny()), Times.Once);
}
[TestMethod]
- public void StartTestRunWithSourcesShouldCallHandleTestRunStatsChange()
+ public void InitializeExecutionShouldSendCommunicationMessageWithCorrectParametersWithVersion()
{
- var mockHandler = new Mock();
- var runCriteria = new TestRunCriteriaWithSources(null, null, null, null);
+ this.SetupFakeChannelWithVersionNegotiation(DUMMYNEGOTIATEDPROTOCOLVERSION);
- var testRunChangedArgs = new TestRunChangedEventArgs(null, null, null);
- var rawMessage = "OnTestRunStatsChange";
- var message = new Message() { MessageType = MessageType.TestRunStatsChange, Payload = null };
+ this.testRequestSender.InitializeExecution(this.pathToAdditionalExtensions);
- this.SetupReceiveRawMessageAsyncAndDeserializeMessageAndInitialize(rawMessage, message);
- this.mockDataSerializer.Setup(ds => ds.DeserializePayload(message)).Returns(testRunChangedArgs);
+ this.mockDataSerializer.Verify(d => d.SerializePayload(MessageType.ExecutionInitialize, this.pathToAdditionalExtensions, DUMMYNEGOTIATEDPROTOCOLVERSION), Times.Once);
+ }
- var completePayload = new TestRunCompletePayload()
- {
- ExecutorUris = null,
- LastRunTests = null,
- RunAttachments = null,
- TestRunCompleteArgs = null
- };
- var completeMessage = new Message() { MessageType = MessageType.ExecutionComplete, Payload = null };
- mockHandler.Setup(mh => mh.HandleTestRunStatsChange(testRunChangedArgs)).Callback(
- () =>
- {
- this.mockDataSerializer.Setup(ds => ds.DeserializeMessage(It.IsAny())).Returns(completeMessage);
- this.mockDataSerializer.Setup(ds => ds.DeserializePayload(completeMessage)).Returns(completePayload);
- });
+ [TestMethod]
+ public void StartTestRunShouldSendStartTestExecutionWithSourcesOnChannel()
+ {
+ this.SetupFakeCommunicationChannel();
- var waitHandle = new AutoResetEvent(false);
- mockHandler.Setup(mh => mh.HandleTestRunComplete(
- It.IsAny(),
- It.IsAny(),
- It.IsAny>(),
- It.IsAny>())).Callback(() => waitHandle.Set());
+ this.testRequestSender.StartTestRun(this.testRunCriteriaWithSources, this.mockExecutionEventsHandler.Object);
- this.testRequestSender.StartTestRun(runCriteria, mockHandler.Object);
+ this.mockDataSerializer.Verify(d => d.SerializePayload(MessageType.StartTestExecutionWithSources, this.testRunCriteriaWithSources, DEFAULTPROTOCOLVERSION), Times.Once);
+ this.mockChannel.Verify(mc => mc.Send(It.IsAny()), Times.Once);
+ }
- waitHandle.WaitOne();
+ [TestMethod]
+ public void StartTestRunShouldSendStartTestExecutionWithSourcesOnChannelWithVersion()
+ {
+ this.SetupFakeChannelWithVersionNegotiation(DUMMYNEGOTIATEDPROTOCOLVERSION);
- this.mockCommunicationManager.Verify(mc => mc.SendMessage(MessageType.StartTestExecutionWithSources, runCriteria, this.protocolConfig.Version), Times.Once);
+ this.testRequestSender.StartTestRun(this.testRunCriteriaWithSources, this.mockExecutionEventsHandler.Object);
- // One for run stats and another for runcomplete
- this.mockDataSerializer.Verify(ds => ds.DeserializeMessage(rawMessage), Times.Exactly(2));
- mockHandler.Verify(mh => mh.HandleTestRunStatsChange(testRunChangedArgs), Times.Once);
- mockHandler.Verify(mh => mh.HandleRawMessage(rawMessage), Times.Exactly(2));
+ this.mockDataSerializer.Verify(d => d.SerializePayload(MessageType.StartTestExecutionWithSources, this.testRunCriteriaWithSources, DUMMYNEGOTIATEDPROTOCOLVERSION), Times.Once);
}
[TestMethod]
- public void StartTestRunWithTestsShouldCallHandleTestRunStatsChange()
+ public void StartTestRunWithTestsShouldSendStartTestExecutionWithTestsOnChannel()
{
- var mockHandler = new Mock();
- var runCriteria = new TestRunCriteriaWithTests(null, null, null, null);
+ var runCriteria = new TestRunCriteriaWithTests(new TestCase[2], "runsettings", null, null);
+ this.SetupFakeCommunicationChannel();
- var testRunChangedArgs = new TestRunChangedEventArgs(null, null, null);
- var rawMessage = "OnTestRunStatsChange";
- var message = new Message() { MessageType = MessageType.TestRunStatsChange, Payload = null };
+ this.testRequestSender.StartTestRun(runCriteria, this.mockExecutionEventsHandler.Object);
- this.SetupReceiveRawMessageAsyncAndDeserializeMessageAndInitialize(rawMessage, message);
- this.mockDataSerializer.Setup(ds => ds.DeserializePayload(message)).Returns(testRunChangedArgs);
+ this.mockDataSerializer.Verify(d => d.SerializePayload(MessageType.StartTestExecutionWithTests, runCriteria, DEFAULTPROTOCOLVERSION), Times.Once);
+ this.mockChannel.Verify(mc => mc.Send(It.IsAny()), Times.Once);
+ }
- var completePayload = new TestRunCompletePayload()
- {
- ExecutorUris = null,
- LastRunTests = null,
- RunAttachments = null,
- TestRunCompleteArgs = null
- };
- var completeMessage = new Message() { MessageType = MessageType.ExecutionComplete, Payload = null };
- var waitHandle = new AutoResetEvent(false);
- mockHandler.Setup(mh => mh.HandleTestRunStatsChange(testRunChangedArgs)).Callback(
- () =>
- {
- this.mockDataSerializer.Setup(ds => ds.DeserializeMessage(It.IsAny())).Returns(completeMessage);
- this.mockDataSerializer.Setup(ds => ds.DeserializePayload(completeMessage)).Returns(completePayload);
- waitHandle.Set();
- });
+ [TestMethod]
+ public void StartTestRunWithTestsShouldSendStartTestExecutionWithTestsOnChannelWithVersion()
+ {
+ var runCriteria = new TestRunCriteriaWithTests(new TestCase[2], "runsettings", null, null);
+ this.SetupFakeChannelWithVersionNegotiation(DUMMYNEGOTIATEDPROTOCOLVERSION);
+
+ this.testRequestSender.StartTestRun(runCriteria, this.mockExecutionEventsHandler.Object);
+
+ this.mockDataSerializer.Verify(d => d.SerializePayload(MessageType.StartTestExecutionWithTests, runCriteria, DUMMYNEGOTIATEDPROTOCOLVERSION), Times.Once);
+ }
- this.testRequestSender.StartTestRun(runCriteria, mockHandler.Object);
+ [TestMethod]
+ public void StartTestRunShouldNotifyRawMessageOnMessageReceived()
+ {
+ this.SetupDeserializeMessage(MessageType.TestMessage, new TestMessagePayload());
+ this.SetupFakeCommunicationChannel();
+
+ this.testRequestSender.StartTestRun(this.testRunCriteriaWithSources, this.mockExecutionEventsHandler.Object);
- waitHandle.WaitOne();
- this.mockCommunicationManager.Verify(mc => mc.SendMessage(MessageType.StartTestExecutionWithTests, runCriteria, this.protocolConfig.Version), Times.Once);
- mockHandler.Verify(mh => mh.HandleTestRunStatsChange(testRunChangedArgs), Times.Once);
- mockHandler.Verify(mh => mh.HandleRawMessage(rawMessage), Times.AtLeastOnce);
+ this.RaiseMessageReceivedEvent();
+ this.mockExecutionEventsHandler.Verify(eh => eh.HandleRawMessage("DummyData"), Times.Once);
}
[TestMethod]
- public void StartTestRunShouldCallHandleLogMessageOnTestMessage()
+ public void StartTestRunShouldNotifyTestRunStatsChangeOnRunStatsMessageReceived()
{
- var mockHandler = new Mock();
- var runCriteria = new TestRunCriteriaWithSources(null, null, null, null);
+ var testRunChangedArgs = new TestRunChangedEventArgs(
+ null,
+ new Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult[2],
+ new TestCase[2]);
+ this.SetupDeserializeMessage(MessageType.TestRunStatsChange, testRunChangedArgs);
+ this.SetupFakeCommunicationChannel();
- var rawMessage = "OnLogMessage";
- var message = new Message() { MessageType = MessageType.TestMessage, Payload = null };
- var payload = new TestMessagePayload() { MessageLevel = TestMessageLevel.Error, Message = rawMessage };
+ this.testRequestSender.StartTestRun(this.testRunCriteriaWithSources, this.mockExecutionEventsHandler.Object);
- this.SetupReceiveRawMessageAsyncAndDeserializeMessageAndInitialize(rawMessage, message);
- this.mockDataSerializer.Setup(ds => ds.DeserializePayload(message)).Returns(payload);
+ this.RaiseMessageReceivedEvent();
+ this.mockExecutionEventsHandler.Verify(eh => eh.HandleTestRunStatsChange(testRunChangedArgs), Times.Once);
+ }
- var completePayload = new TestRunCompletePayload()
+ [TestMethod]
+ public void StartTestRunShouldNotifyExecutionCompleteOnRunCompleteMessageReceived()
+ {
+ var testRunCompletePayload = new TestRunCompletePayload
{
- ExecutorUris = null, LastRunTests = null, RunAttachments = null, TestRunCompleteArgs = null
+ TestRunCompleteArgs = new TestRunCompleteEventArgs(null, false, false, null, null, TimeSpan.MaxValue),
+ LastRunTests = new TestRunChangedEventArgs(null, null, null),
+ RunAttachments = new List()
};
- var completeMessage = new Message() { MessageType = MessageType.ExecutionComplete, Payload = null };
- var waitHandle = new AutoResetEvent(false);
- mockHandler.Setup(mh => mh.HandleLogMessage(TestMessageLevel.Error, rawMessage)).Callback(
- () =>
- {
- this.mockDataSerializer.Setup(ds => ds.DeserializeMessage(It.IsAny())).Returns(completeMessage);
- this.mockDataSerializer.Setup(ds => ds.DeserializePayload(completeMessage)).Callback(() => { waitHandle.Set(); })
- .Returns(completePayload);
- });
+ this.SetupDeserializeMessage(MessageType.ExecutionComplete, testRunCompletePayload);
+ this.SetupFakeCommunicationChannel();
- this.testRequestSender.StartTestRun(runCriteria, mockHandler.Object);
- waitHandle.WaitOne();
+ this.testRequestSender.StartTestRun(this.testRunCriteriaWithSources, this.mockExecutionEventsHandler.Object);
+
+ this.RaiseMessageReceivedEvent();
+ this.mockExecutionEventsHandler.Verify(
+ eh => eh.HandleTestRunComplete(
+ testRunCompletePayload.TestRunCompleteArgs,
+ testRunCompletePayload.LastRunTests,
+ testRunCompletePayload.RunAttachments,
+ It.IsAny>()),
+ Times.Once);
+ }
+
+ [TestMethod]
+ public void StartTestRunShouldNotifyLogMessageOnTestMessageReceived()
+ {
+ var testMessagePayload = new TestMessagePayload { MessageLevel = TestMessageLevel.Error, Message = "Dummy" };
+ this.SetupDeserializeMessage(MessageType.TestMessage, testMessagePayload);
+ this.SetupFakeCommunicationChannel();
- this.mockCommunicationManager.Verify(mc => mc.SendMessage(MessageType.StartTestExecutionWithSources, runCriteria, this.protocolConfig.Version), Times.Once);
- this.mockDataSerializer.Verify(ds => ds.DeserializeMessage(It.IsAny()), Times.AtLeast(2));
+ this.testRequestSender.StartTestRun(this.testRunCriteriaWithSources, this.mockExecutionEventsHandler.Object);
- // Asserting that 'StartTestRun" should have been completed, & invoked only once
- this.mockDataSerializer.Verify(ds => ds.DeserializePayload(completeMessage), Times.Exactly(1));
- mockHandler.Verify(mh => mh.HandleLogMessage(payload.MessageLevel, payload.Message), Times.Once);
- mockHandler.Verify(mh => mh.HandleRawMessage(rawMessage), Times.AtLeastOnce);
+ this.RaiseMessageReceivedEvent();
+ this.mockExecutionEventsHandler.Verify(eh => eh.HandleLogMessage(TestMessageLevel.Error, "Dummy"), Times.Once);
}
[TestMethod]
- public void StartTestRunShouldCallLaunchProcessWithDebuggerAndWaitForCallback()
+ public void StartTestRunShouldNotifyLaunchWithDebuggerOnMessageReceived()
{
- var mockHandler = new Mock();
- var runCriteria = new TestRunCriteriaWithSources(null, null, null, null);
+ var launchMessagePayload = new TestProcessStartInfo();
+ this.SetupDeserializeMessage(MessageType.LaunchAdapterProcessWithDebuggerAttached, launchMessagePayload);
+ this.SetupFakeCommunicationChannel();
- var rawMessage = "LaunchProcessWithDebugger";
- var message = new Message() { MessageType = MessageType.LaunchAdapterProcessWithDebuggerAttached, Payload = null };
- var payload = new TestProcessStartInfo();
+ this.testRequestSender.StartTestRun(this.testRunCriteriaWithSources, this.mockExecutionEventsHandler.Object);
- this.SetupReceiveRawMessageAsyncAndDeserializeMessageAndInitialize(rawMessage, message);
- this.mockDataSerializer.Setup(ds => ds.DeserializePayload(message)).Returns(payload);
+ this.RaiseMessageReceivedEvent();
+ this.mockExecutionEventsHandler.Verify(eh => eh.LaunchProcessWithDebuggerAttached(launchMessagePayload), Times.Once);
+ }
- var completePayload = new TestRunCompletePayload()
- {
- ExecutorUris = null,
- LastRunTests = null,
- RunAttachments = null,
- TestRunCompleteArgs = null
- };
- var completeMessage = new Message() { MessageType = MessageType.ExecutionComplete, Payload = null };
- mockHandler.Setup(mh => mh.LaunchProcessWithDebuggerAttached(payload)).Callback(
- () =>
- {
- this.mockDataSerializer.Setup(ds => ds.DeserializeMessage(It.IsAny())).Returns(completeMessage);
- this.mockDataSerializer.Setup(ds => ds.DeserializePayload(completeMessage)).Returns(completePayload);
- });
+ [TestMethod]
+ public void StartTestRunShouldSendLaunchDebuggerAttachedCallbackOnMessageReceived()
+ {
+ var launchMessagePayload = new TestProcessStartInfo();
+ this.SetupDeserializeMessage(MessageType.LaunchAdapterProcessWithDebuggerAttached, launchMessagePayload);
+ this.SetupFakeCommunicationChannel();
- var waitHandle = new AutoResetEvent(false);
- mockHandler.Setup(mh => mh.HandleTestRunComplete(
- It.IsAny(),
- It.IsAny(),
- It.IsAny>(),
- It.IsAny>())).Callback(() => waitHandle.Set());
+ this.testRequestSender.StartTestRun(this.testRunCriteriaWithSources, this.mockExecutionEventsHandler.Object);
- this.testRequestSender.StartTestRun(runCriteria, mockHandler.Object);
+ this.RaiseMessageReceivedEvent();
+ this.mockDataSerializer.Verify(ds => ds.SerializePayload(MessageType.LaunchAdapterProcessWithDebuggerAttachedCallback, It.IsAny(), 1), Times.Once);
+ this.mockChannel.Verify(c => c.Send(It.IsAny()), Times.AtLeastOnce);
+ }
- waitHandle.WaitOne();
+ [TestMethod]
+ public void StartTestRunShouldSendLaunchDebuggerAttachedCallbackOnMessageReceivedWithVersion()
+ {
+ var launchMessagePayload = new TestProcessStartInfo();
+ this.SetupFakeChannelWithVersionNegotiation(DUMMYNEGOTIATEDPROTOCOLVERSION);
+ this.SetupDeserializeMessage(MessageType.LaunchAdapterProcessWithDebuggerAttached, launchMessagePayload);
- this.mockCommunicationManager.Verify(mc => mc.SendMessage(MessageType.StartTestExecutionWithSources, runCriteria, this.protocolConfig.Version), Times.Once);
+ this.testRequestSender.StartTestRun(this.testRunCriteriaWithSources, this.mockExecutionEventsHandler.Object);
- this.mockDataSerializer.Verify(ds => ds.DeserializeMessage(It.IsAny()), Times.Exactly(2));
- mockHandler.Verify(mh => mh.LaunchProcessWithDebuggerAttached(payload), Times.Once);
- mockHandler.Verify(mh => mh.HandleRawMessage(rawMessage), Times.Exactly(2));
- this.mockCommunicationManager.Verify(mc => mc.SendMessage(MessageType.LaunchAdapterProcessWithDebuggerAttachedCallback, It.IsAny