Skip to content

Commit

Permalink
Test runtime provider Interface changes (#593)
Browse files Browse the repository at this point in the history
* Passing runsettings as xml for test runtime providers,
to analyze settings themselves

* Clean up for ITestRunTimeProvider

* Build error

* Resharper Refactoring

* PR Comments

* build error

* Documentation for methods corrected
Added required test cases

* Clean up API's
  • Loading branch information
mayankbansal018 authored Mar 20, 2017
1 parent 9e7266f commit d189e71
Show file tree
Hide file tree
Showing 15 changed files with 813 additions and 561 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public ITestRuntimeProvider GetTestHostManagerByUri(string hostUri)
return null;
}

public ITestRuntimeProvider GetTestHostManagerByRunConfiguration(RunConfiguration runConfiguration)
public ITestRuntimeProvider GetTestHostManagerByRunConfiguration(string runConfiguration)
{
foreach (var testExtension in this.testHostExtensionManager.TestExtensions)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Client
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Interfaces;
using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers;
Expand All @@ -18,22 +18,18 @@ namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Client
using Microsoft.VisualStudio.TestPlatform.Utilities;

using CrossPlatEngineResources = Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Resources.Resources;
using System.Threading.Tasks;

/// <summary>
/// Base class for any operations that the client needs to drive through the engine.
/// </summary>
public abstract class ProxyOperationManager
{
private readonly ITestRuntimeProvider testHostManager;

private bool initialized;

private readonly IProcessHelper processHelper;
private readonly int connectionTimeout;

private StringBuilder testHostProcessStdError;

private readonly IProcessHelper processHelper;
private bool initialized;
private string testHostProcessStdError;

#region Constructors

Expand Down Expand Up @@ -78,7 +74,7 @@ public virtual void SetupChannel(IEnumerable<string> sources)

if (!this.initialized)
{
this.testHostProcessStdError = new StringBuilder(this.ErrorLength, this.ErrorLength);
this.testHostProcessStdError = string.Empty;

var portNumber = this.RequestSender.InitializeCommunication();
var processId = this.processHelper.GetCurrentProcessId();
Expand All @@ -88,18 +84,18 @@ public virtual void SetupChannel(IEnumerable<string> sources)
var testHostStartInfo = this.testHostManager.GetTestHostProcessStartInfo(sources, null, connectionInfo);

// Subscribe to TestHost Event
this.testHostManager.HostLaunched += TestHostManager_HostLaunched;
this.testHostManager.HostExited += TestHostManager_HostExited;
this.testHostManager.HostLaunched += this.TestHostManagerHostLaunched;
this.testHostManager.HostExited += this.TestHostManagerHostExited;

this.UpdateTestProcessStartInfo(testHostStartInfo);

// Launch the test host.
CancellationTokenSource hostLaunchCTS = new CancellationTokenSource();
CancellationTokenSource hostLaunchCts = new CancellationTokenSource();
Task<int> hostLaunchedTask = this.testHostManager.LaunchTestHostAsync(testHostStartInfo);

try
{
hostLaunchedTask.Wait(hostLaunchCTS.Token);
hostLaunchedTask.Wait(hostLaunchCts.Token);
}
catch (OperationCanceledException ex)
{
Expand Down Expand Up @@ -127,7 +123,7 @@ public virtual void SetupChannel(IEnumerable<string> sources)
{
var errorMsg = CrossPlatEngineResources.InitializationFailed;

if (!string.IsNullOrWhiteSpace(this.testHostProcessStdError.ToString()))
if (!string.IsNullOrWhiteSpace(this.testHostProcessStdError))
{
// Testhost failed with error
errorMsg = string.Format(CrossPlatEngineResources.TestHostExitedWithError, this.testHostProcessStdError);
Expand All @@ -137,17 +133,6 @@ public virtual void SetupChannel(IEnumerable<string> sources)
}
}

private void TestHostManager_HostLaunched(object sender, HostProviderEventArgs e)
{
EqtTrace.Verbose(e.Data);
}

private void TestHostManager_HostExited(object sender, HostProviderEventArgs e)
{
this.testHostProcessStdError.Clear();
this.testHostProcessStdError.Append(e.Data);
}

/// <summary>
/// Closes the channel, terminate test host process.
/// </summary>
Expand All @@ -161,8 +146,8 @@ public virtual void Close()
finally
{
this.initialized = false;
this.testHostManager.HostExited -= TestHostManager_HostExited;
this.testHostManager.HostLaunched -= TestHostManager_HostLaunched;
this.testHostManager.HostExited -= this.TestHostManagerHostExited;
this.testHostManager.HostLaunched -= this.TestHostManagerHostLaunched;
}
}

Expand All @@ -185,18 +170,25 @@ protected virtual TestProcessStartInfo UpdateTestProcessStartInfo(TestProcessSta

protected string GetTimestampedLogFile(string logFile)
{
return Path.ChangeExtension(logFile,
string.Format("host.{0}_{1}{2}", DateTime.Now.ToString("yy-MM-dd_HH-mm-ss_fffff"),
Thread.CurrentThread.ManagedThreadId, Path.GetExtension(logFile)));
return Path.ChangeExtension(
logFile,
string.Format(
"host.{0}_{1}{2}",
DateTime.Now.ToString("yy-MM-dd_HH-mm-ss_fffff"),
Thread.CurrentThread.ManagedThreadId,
Path.GetExtension(logFile)));
}

/// <summary>
/// Returns the current error data in stream
/// Written purely for UT as of now.
/// </summary>
protected virtual string GetStandardError()
private void TestHostManagerHostLaunched(object sender, HostProviderEventArgs e)
{
return testHostProcessStdError.ToString();
EqtTrace.Verbose(e.Data);
}

private void TestHostManagerHostExited(object sender, HostProviderEventArgs e)
{
this.testHostProcessStdError = e.Data;

this.RequestSender.OnClientProcessExit(this.testHostProcessStdError);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public virtual int LaunchDataCollector(IDictionary<string, string> environmentVa

var argumentsString = string.Join(" ", commandLineArguments);

this.DataCollectorProcess = this.processHelper.LaunchProcess(dataCollectorProcessPath, argumentsString, processWorkingDirectory, environmentVariables, null);
this.DataCollectorProcess = this.processHelper.LaunchProcess(dataCollectorProcessPath, argumentsString, processWorkingDirectory, environmentVariables, null, null);
return this.DataCollectorProcess.Id;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ public virtual int LaunchDataCollector(IDictionary<string, string> environmentVa
var cliArgs = string.Join(" ", commandLineArguments);
var argumentsString = string.Format("{0} {1} {2} ", args, dataCollectorAssemblyPath, cliArgs);

this.DataCollectorProcess = this.processHelper.LaunchProcess(currentProcessFileName, argumentsString, currentWorkingDirectory, environmentVariables, null);
this.DataCollectorProcess = this.processHelper.LaunchProcess(currentProcessFileName, argumentsString, currentWorkingDirectory, environmentVariables, null, null);
return this.DataCollectorProcess.Id;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ internal interface IProcessHelper
/// </summary>
/// <param name="processPath">The full file name of the process.</param>
/// <param name="arguments">The command-line arguments.</param>
/// <param name="environmentVariables">Environment variables to set while bootstrapping the process.</param>
/// <param name="workingDirectory">The working directory for this process.</param>
/// <param name="exitCallback">Call back for on process exit</param>
/// <param name="environmentVariables">Environment variables to set while bootstrapping the process.</param>
/// <param name="errorCallback">Call back for to read error stream data</param>
/// <param name="exitCallBack">Call back for on process exit</param>
/// <returns>The process created.</returns>
Process LaunchProcess(string processPath, string arguments, string workingDirectory, IDictionary<string, string> environmentVariables, Action<Process, string> errorCallback);
Process LaunchProcess(string processPath, string arguments, string workingDirectory, IDictionary<string, string> environmentVariables, Action<Process, string> errorCallback, Action<Process> exitCallBack);

/// <summary>
/// Gets the current process file path.
Expand All @@ -36,9 +37,9 @@ internal interface IProcessHelper
string GetTestEngineDirectory();

/// <summary>
/// Gets the pid of test engine.
/// Gets the process id of test engine.
/// </summary>
/// <returns>pid of test engine.</returns>
/// <returns>process id of test engine.</returns>
int GetCurrentProcessId();

/// <summary>
Expand All @@ -47,5 +48,13 @@ internal interface IProcessHelper
/// <param name="processId">process id</param>
/// <returns>Name of process</returns>
string GetProcessName(int processId);

/// <summary>
/// False if process has not exited, True otherwise. Set exitCode only if process has exited.
/// </summary>
/// <param name="process">process parameter</param>
/// <param name="exitCode">return value of exitCode</param>
/// <returns>False if process has not exited, True otherwise</returns>
bool TryGetExitCode(Process process, out int exitCode);
}
}
48 changes: 32 additions & 16 deletions src/Microsoft.TestPlatform.CrossPlatEngine/Helpers/ProcessHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers
{
using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;

Expand All @@ -17,16 +17,8 @@ namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers
/// </summary>
internal class ProcessHelper : IProcessHelper
{
/// <summary>
/// Launches the process with the arguments provided.
/// </summary>
/// <param name="processPath">The path to the process.</param>
/// <param name="arguments">Process arguments.</param>
/// <param name="workingDirectory">Working directory of the process.</param>
/// <param name="errorCallback"></param>
/// <returns>The process spawned.</returns>
/// <exception cref="Exception">Throws any exception that could result as part of the launch.</exception>
public Process LaunchProcess(string processPath, string arguments, string workingDirectory, IDictionary<string, string> envVariables, Action<Process, string> errorCallback)
/// <inheritdoc/>
public Process LaunchProcess(string processPath, string arguments, string workingDirectory, IDictionary<string, string> envVariables, Action<Process, string> errorCallback, Action<Process> exitCallBack)
{
var process = new Process();
try
Expand All @@ -53,10 +45,24 @@ public Process LaunchProcess(string processPath, string arguments, string workin
process.ErrorDataReceived += (sender, args) => errorCallback(sender as Process, args.Data);
}

if (exitCallBack != null)
{
process.Exited += (sender, args) =>
{
// Call WaitForExit again to ensure all streams are flushed
var p = sender as Process;
p.WaitForExit();
exitCallBack(p);
};
}

EqtTrace.Verbose("ProcessHelper: Starting process '{0}' with command line '{1}'", processPath, arguments);
process.Start();

process.BeginErrorReadLine();
if (errorCallback != null)
{
process.BeginErrorReadLine();
}
}
catch (Exception exception)
{
Expand All @@ -71,10 +77,7 @@ public Process LaunchProcess(string processPath, string arguments, string workin
return process;
}

/// <summary>
/// Gets the current process file path.
/// </summary>
/// <returns> The current process file path. </returns>
/// <inheritdoc/>
public string GetCurrentProcessFileName()
{
return Process.GetCurrentProcess().MainModule.FileName;
Expand All @@ -97,5 +100,18 @@ public string GetProcessName(int processId)
{
return Process.GetProcessById(processId).ProcessName;
}

/// <inheritdoc/>
public bool TryGetExitCode(Process process, out int exitCode)
{
if (process.HasExited)
{
exitCode = process.ExitCode;
return true;
}

exitCode = 0;
return false;
}
}
}
Loading

0 comments on commit d189e71

Please sign in to comment.