Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix remote testhost #3492

Merged
merged 25 commits into from
Mar 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 16 additions & 8 deletions src/Microsoft.TestPlatform.CommunicationUtilities/SocketServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,17 +65,25 @@ protected SocketServer(Func<Stream, ICommunicationChannel> channelFactory)

public string Start(string endPoint)
{
_tcpListener = new TcpListener(endPoint.GetIpEndPoint());
try
{
_tcpListener = new TcpListener(endPoint.GetIpEndPoint());

_tcpListener.Start();
_tcpListener.Start();

_endPoint = _tcpListener.LocalEndpoint.ToString();
EqtTrace.Info("SocketServer.Start: Listening on endpoint : {0}", _endPoint);
_endPoint = _tcpListener.LocalEndpoint.ToString();
EqtTrace.Info("SocketServer.Start: Listening on endpoint : {0}", _endPoint);

// Serves a single client at the moment. An error in connection, or message loop just
// terminates the entire server.
_tcpListener.AcceptTcpClientAsync().ContinueWith(t => OnClientConnected(t.Result));
return _endPoint;
// Serves a single client at the moment. An error in connection, or message loop just
// terminates the entire server.
_tcpListener.AcceptTcpClientAsync().ContinueWith(t => OnClientConnected(t.Result));
return _endPoint;
}
catch (SocketException ex)
{
EqtTrace.Error("Failed for address {0}, with: {1}", endPoint, ex);
throw;
}
}

/// <inheritdoc />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public TestRequestSender(ProtocolConfig protocolConfig, ITestRuntimeProvider run
internal TestRequestSender(
ITestRuntimeProvider runtimeProvider,
ICommunicationEndPoint communicationEndPoint,
TestHostConnectionInfo connectionInfo,
TestHostConnectionInfo testhostConnectionInfo,
IDataSerializer serializer,
ProtocolConfig protocolConfig,
int clientExitedWaitTime)
Expand All @@ -97,18 +97,21 @@ internal TestRequestSender(

_highestSupportedVersion = protocolConfig.Version;

// The connectionInfo here is that of RuntimeProvider, so reverse the role of runner.

_runtimeProvider = runtimeProvider;

// TODO: In various places TestRequest sender is instantiated, and we can't easily inject the factory, so this is last
// resort of getting the dependency into the execution flow.
_communicationEndpoint = communicationEndPoint
#if DEBUG
?? TestServiceLocator.Get<ICommunicationEndPoint>(connectionInfo.Endpoint)
?? TestServiceLocator.Get<ICommunicationEndPoint>(testhostConnectionInfo.Endpoint)
#endif
?? SetCommunicationEndPoint();
_connectionInfo.Endpoint = connectionInfo.Endpoint;
_connectionInfo.Role = connectionInfo.Role == ConnectionRole.Host
?? SetCommunicationEndPoint(testhostConnectionInfo);

// The connectionInfo here is what is provided to testhost, but we are in runner, and so the role needs
// to be reversed. If testhost starts as client, then runner must be host, and in reverse.
_connectionInfo.Endpoint = testhostConnectionInfo.Endpoint;
_connectionInfo.Role = testhostConnectionInfo.Role == ConnectionRole.Host
? ConnectionRole.Client
: ConnectionRole.Host;
}
Expand Down Expand Up @@ -732,10 +735,12 @@ private void SetOperationComplete()
Interlocked.CompareExchange(ref _operationCompleted, 1, 0);
}

private ICommunicationEndPoint SetCommunicationEndPoint()
private ICommunicationEndPoint SetCommunicationEndPoint(TestHostConnectionInfo testhostConnectionInfo)
{
// 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 (_connectionInfo.Role == ConnectionRole.Client)
// The connectionInfo here is what is provided to testhost, but we are in runner, and so the role needs
// to be reversed. If testhost starts as client, then runner must be host, and in reverse.
if (testhostConnectionInfo.Role != ConnectionRole.Client)
{
EqtTrace.Verbose("TestRequestSender is acting as client.");
return new SocketClient();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,12 @@
using System.IO;
using System.Linq;

using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces;

using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.ObjectModel;

namespace Microsoft.VisualStudio.TestPlatform.CommunicationUtilities;

/// <summary>
/// Converts paths in received and sent objects, to make testhost seem like it run a local test,
/// while it was in fact running a test on a remote system, in a totally different path. This is for UWP which
Expand All @@ -32,8 +30,13 @@ internal class PathConverter : IPathConverter

public PathConverter(string originalPath!!, string deploymentPath!!, IFileHelper fileHelper!!)
{
_originalPath = fileHelper.GetFullPath(originalPath).TrimEnd('\\').TrimEnd('/') + Path.DirectorySeparatorChar;
_deploymentPath = fileHelper.GetFullPath(deploymentPath).TrimEnd('\\').TrimEnd('/') + Path.DirectorySeparatorChar;
string unquotedOriginalPath = originalPath.Trim('\"');
string normalizedLocalPath = fileHelper.GetFullPath(unquotedOriginalPath).TrimEnd('\\').TrimEnd('/') + Path.DirectorySeparatorChar;
_originalPath = normalizedLocalPath;

string unquotedDeploymentPath = deploymentPath.Trim('\"');
string normalizedDeploymentPath = fileHelper.GetFullPath(unquotedDeploymentPath).TrimEnd('\\').TrimEnd('/') + Path.DirectorySeparatorChar;
_deploymentPath = normalizedDeploymentPath;
}

public string? UpdatePath(string? path, PathConversionDirection updateDirection)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;

using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.EventHandlers;
Expand Down Expand Up @@ -108,11 +109,20 @@ protected TestRequestHandler(IDataSerializer dataSerializer, ICommunicationEndpo
/// <inheritdoc />
public virtual void InitializeCommunication()
{
if (this is IDeploymentAwareTestRequestHandler self
&& !string.IsNullOrWhiteSpace(self.LocalPath)
&& !string.IsNullOrWhiteSpace(self.RemotePath))
if (this is IDeploymentAwareTestRequestHandler self)
{
_pathConverter = new PathConverter(self.LocalPath, self.RemotePath, _fileHelper);
var currentProcessPath = Process.GetCurrentProcess().MainModule.FileName;
var currentProcessDirectory = !string.IsNullOrWhiteSpace(currentProcessPath)
? System.IO.Path.GetDirectoryName(currentProcessPath)
: null;
var remotePath = Environment.GetEnvironmentVariable("VSTEST_UWP_DEPLOY_REMOTE_PATH") ?? self.RemotePath ?? currentProcessDirectory;

var localPath = Environment.GetEnvironmentVariable("VSTEST_UWP_DEPLOY_LOCAL_PATH") ?? self.LocalPath;
if (!string.IsNullOrWhiteSpace(localPath)
&& !string.IsNullOrWhiteSpace(remotePath))
{
_pathConverter = new PathConverter(localPath, remotePath, _fileHelper);
}
}

_communicationEndPoint = _communicationEndpointFactory.Create(ConnectionInfo.Role);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ private static string FindAttachVs()
return fromPath;
}

var parent = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var parent = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
while (parent != null)
{
var path = Path.Combine(parent, @"src\AttachVS\bin\Debug\net472\AttachVS.exe");
Expand Down Expand Up @@ -156,7 +156,7 @@ internal static void WaitForDebugger(string environmentVariable)

while (!Debugger.IsAttached)
{
Thread.Sleep(1000);
Task.Delay(1000).GetAwaiter().GetResult();
}

Break();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ private static CultureInfo GetOverriddenUiLanguage()
catch (CultureNotFoundException) { }
}

#if !NETCOREAPP1_0
#if !NETCOREAPP1_0 && !NETSTANDARD1_3
// VSLANG=<lcid> is set by VS and we respect that as well so that we will respect the VS
// language preference if we're invoked by VS.
string vsLang = Environment.GetEnvironmentVariable(Vslang);
Expand All @@ -66,7 +66,7 @@ private static void FlowOverrideToChildProcesses(CultureInfo language)
{
// Do not override any environment variables that are already set as we do not want to clobber a more granular setting with our global setting.
SetIfNotAlreadySet(DotnetCliUiLanguage, language.Name);
#if !NETCOREAPP1_0
#if !NETCOREAPP1_0 && !NETSTANDARD1_3
SetIfNotAlreadySet(Vslang, language.LCID.ToString()); // for tools following VS guidelines to just work in CLI
#endif
SetIfNotAlreadySet(PreferredUiLang, language.Name); // for C#/VB targets that pass $(PreferredUILang) to compiler
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,32 @@ public void WriteLine(PlatformTraceLevel traceLevel, string message)
}

var level = Enum.GetName(typeof(PlatformTraceLevel), traceLevel);

Debug.WriteLine($"[{level}] {message}");
}

public bool InitializeVerboseTrace(string customLogFile)
{
#if DEBUG
// We don't have access to System.Diagnostics.Trace on netstandard1.3
// so we write to Debug. No need to initialize for non-debug builds.
return true;
#else
return false;
#endif
}

public bool InitializeTrace(string customLogFile, PlatformTraceLevel traceLevel)
{
_traceLevel = traceLevel;

#if DEBUG
// We don't have access to System.Diagnostics.Trace on netstandard1.3
// so we write to Debug. No need to initialize for non-debug builds.
return true;
#else
return false;
#endif
}

public bool ShouldTrace(PlatformTraceLevel traceLevel)
Expand Down
10 changes: 7 additions & 3 deletions src/testhost.x86/DefaultEngineInvoker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,14 @@ public void Invoke(IDictionary<string, string?> argsDictionary)
// beacuse of it's public interface, we work around that by making it implement a second interface
if (_requestHandler is IDeploymentAwareTestRequestHandler deployedHandler)
{
if (argsDictionary.ContainsKey(RemotePath) && argsDictionary.ContainsKey(LocalPath))
if (argsDictionary.TryGetValue(RemotePath, out var remotePath) && remotePath != null)
{
deployedHandler.LocalPath = argsDictionary[LocalPath]!;
deployedHandler.RemotePath = argsDictionary[RemotePath]!;
deployedHandler.RemotePath = remotePath;
}

if (argsDictionary.TryGetValue(LocalPath, out var localPath) && localPath != null)
{
deployedHandler.LocalPath = localPath;
}
}

Expand Down