[Harness] Refactor process management to be testable. (#8053)
Move all the extension methods to a class. After this refactor, we will be able to DI the manager in the other classes and assert that the processes are called with the correct parameters without the need of launching them. Also added tests for the manager. We create a dummy console app that will be executed by the tests. The console app has a number of parameters that will be used to ensure that the new process behaves as we want: - Use the passed exit code. - Create child proecesses if needed. - Sleep to force a timeout. - Writer messages to stdout and stderr. Our tests call the dummy app and ensures that the results match the behaviour expected by the dummy app. Co-authored-by: Přemek Vysoký <premek.vysoky@microsoft.com>
This commit is contained in:
Родитель
65a7168c66
Коммит
597b7d7b0b
|
@ -9,6 +9,8 @@ using System.Threading;
|
|||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
using Xamarin;
|
||||
using Xamarin.Utils;
|
||||
using Xharness.Execution;
|
||||
using Xharness.Jenkins.TestTasks;
|
||||
using Xharness.Listeners;
|
||||
using Xharness.Logging;
|
||||
|
@ -124,6 +126,8 @@ namespace Xharness
|
|||
}
|
||||
}
|
||||
|
||||
public IProcessManager ProcessManager { get; set; } = new ProcessManager ();
|
||||
|
||||
string mode;
|
||||
|
||||
async Task<bool> FindSimulatorAsync ()
|
||||
|
@ -302,7 +306,7 @@ namespace Xharness
|
|||
var totalSize = Directory.GetFiles (appPath, "*", SearchOption.AllDirectories).Select ((v) => new FileInfo (v).Length).Sum ();
|
||||
main_log.WriteLine ($"Installing '{appPath}' to '{companion_device_name ?? device_name}'. Size: {totalSize} bytes = {totalSize / 1024.0 / 1024.0:N2} MB");
|
||||
|
||||
return await ProcessHelper.ExecuteCommandAsync (Harness.MlaunchPath, args, main_log, TimeSpan.FromHours (1), cancellation_token: cancellation_token);
|
||||
return await ProcessManager.ExecuteCommandAsync (Harness.MlaunchPath, args, main_log, TimeSpan.FromHours (1), cancellation_token: cancellation_token);
|
||||
}
|
||||
|
||||
public async Task<ProcessExecutionResult> UninstallAsync ()
|
||||
|
@ -326,7 +330,7 @@ namespace Xharness
|
|||
args.Add (bundle_identifier);
|
||||
AddDeviceName (args, companion_device_name ?? device_name);
|
||||
|
||||
return await ProcessHelper.ExecuteCommandAsync (Harness.MlaunchPath, args, main_log, TimeSpan.FromMinutes (1));
|
||||
return await ProcessManager.ExecuteCommandAsync (Harness.MlaunchPath, args, main_log, TimeSpan.FromMinutes (1));
|
||||
}
|
||||
|
||||
bool ensure_clean_simulator_state = true;
|
||||
|
@ -655,7 +659,7 @@ namespace Xharness
|
|||
|
||||
main_log.WriteLine ("Starting test run");
|
||||
|
||||
var result = await ProcessHelper.ExecuteCommandAsync (Harness.MlaunchPath, args, run_log, timeout, cancellation_token: cancellation_source.Token);
|
||||
var result = await ProcessManager.ExecuteCommandAsync (Harness.MlaunchPath, args, run_log, timeout, cancellation_token: cancellation_source.Token);
|
||||
if (result.TimedOut) {
|
||||
timed_out = true;
|
||||
success = false;
|
||||
|
@ -692,7 +696,7 @@ namespace Xharness
|
|||
var timeoutType = launchTimedout ? "Launch" : "Completion";
|
||||
var timeoutValue = launchTimedout ? Harness.LaunchTimeout : timeout.TotalSeconds;
|
||||
main_log.WriteLine ($"{timeoutType} timed out after {timeoutValue} seconds");
|
||||
await Process_Extensions.KillTreeAsync (pid, main_log, true);
|
||||
await ProcessManager.KillTreeAsync (pid, main_log, true);
|
||||
} else {
|
||||
main_log.WriteLine ("Could not find pid in mtouch output.");
|
||||
}
|
||||
|
@ -740,7 +744,7 @@ namespace Xharness
|
|||
});
|
||||
var runLog = Log.CreateAggregatedLog (callbackLog, main_log);
|
||||
var timeoutWatch = Stopwatch.StartNew ();
|
||||
var result = await ProcessHelper.ExecuteCommandAsync (Harness.MlaunchPath, args, runLog, timeout, cancellation_token: cancellation_source.Token);
|
||||
var result = await ProcessManager.ExecuteCommandAsync (Harness.MlaunchPath, args, runLog, timeout, cancellation_token: cancellation_source.Token);
|
||||
|
||||
if (!waitedForExit && !result.TimedOut) {
|
||||
// mlaunch couldn't wait for exit for some reason. Let's assume the app exits when the test listener completes.
|
||||
|
|
|
@ -67,7 +67,7 @@ namespace Xharness
|
|||
if (process.WaitForExit ((int) TimeSpan.FromSeconds (5).TotalMilliseconds))
|
||||
return;
|
||||
|
||||
process.KillTreeAsync (Harness.HarnessLog, diagnostics: false).Wait ();
|
||||
Harness.ProcessManager.KillTreeAsync (process, Harness.HarnessLog, diagnostics: false).Wait ();
|
||||
process.Dispose ();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{2318BC20-40F1-4A12-B695-10B0433CFB35}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>DummyTestProcess</RootNamespace>
|
||||
<AssemblyName>DummyTestProcess</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\Xharness.Tests\bin\Debug</OutputPath>
|
||||
<DefineConstants>DEBUG;</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ExternalConsole>true</ExternalConsole>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>.\Xharness.Tests\bin\Release</OutputPath>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ExternalConsole>true</ExternalConsole>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="Mono.Options">
|
||||
<HintPath>..\..\..\packages\Mono.Options.6.6.0.161\lib\net40\Mono.Options.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
|
@ -0,0 +1,86 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using Mono.Options;
|
||||
|
||||
namespace DummyTestProcess {
|
||||
|
||||
// The following is a dummy console application that we are going to be using
|
||||
// for testing purposes. The application takes a number of commands that will
|
||||
// help to test diff situations:
|
||||
//
|
||||
// * Exit codes.
|
||||
// * Timeouts
|
||||
// * Child processes.
|
||||
// * Provided parameters
|
||||
//
|
||||
// The console worjs as following:
|
||||
//
|
||||
// -exit-code: Provide the exit code that will be returned after execution
|
||||
// -timeout: The time the application will be idle. This will be used to
|
||||
// fake a timeout in the xharness ide.
|
||||
// -children: Create a number of childre processes.
|
||||
//
|
||||
// By default, the application will alsways print the arguments that have been
|
||||
// provided and will run for the given timeout time.
|
||||
class MainClass {
|
||||
|
||||
public static void Main (string [] args)
|
||||
{
|
||||
var timeout = TimeSpan.FromMinutes(1);
|
||||
var childrenCount = 0;
|
||||
var exitCode = 0;
|
||||
var showHelp = false;
|
||||
string stdout = "";
|
||||
string stderr = "";
|
||||
List<Process> children;
|
||||
|
||||
var os = new OptionSet () {
|
||||
{ "h|?|help", "Displays the help", (v) => showHelp = true },
|
||||
{ "exit-code=", "Exit code to be used by the application", (v) => int.TryParse (v, out exitCode) },
|
||||
{ "stdout=", "Message to print in stdout. Default is null", (v) => stdout = v },
|
||||
{ "stderr=", "Message to print in stderr. Default is null", (v) => stderr = v },
|
||||
{ "timeout=", "Timeout for a test run (in seconds). Default is 1 minute.", (v) =>
|
||||
{
|
||||
if (int.TryParse (v, out var newTimeout)) {
|
||||
timeout = TimeSpan.FromSeconds(newTimeout);
|
||||
}
|
||||
}
|
||||
},
|
||||
{ "children=", "The number of children processes that will be created by the application", (v) => int.TryParse (v, out childrenCount) },
|
||||
};
|
||||
|
||||
_ = os.Parse (args); // dont care about any extra args, ignore them
|
||||
|
||||
|
||||
if (showHelp) {
|
||||
os.WriteOptionDescriptions (Console.Out);
|
||||
Environment.Exit (0);
|
||||
}
|
||||
|
||||
// spawn the number of children needed, we spawn the same process with the same args BUT no children
|
||||
if (childrenCount > 0) {
|
||||
Console.WriteLine (childrenCount);
|
||||
children = new List<Process> (childrenCount);
|
||||
for (var i = 0; i < childrenCount; i++) {
|
||||
Console.WriteLine (i);
|
||||
var p = new Process ();
|
||||
p.StartInfo.FileName = "mono";
|
||||
p.StartInfo.Arguments = $"DummyTestProcess.exe -exit-code={exitCode} -timeout={timeout.Seconds}";
|
||||
p.Start ();
|
||||
children.Add (p);
|
||||
}
|
||||
}
|
||||
// write something to the stdout and stderr to test the output
|
||||
if (!string.IsNullOrEmpty (stdout))
|
||||
Console.WriteLine (stdout);
|
||||
if (!string.IsNullOrEmpty (stderr))
|
||||
Console.Error.WriteLine (stderr);
|
||||
// sleep for the required timeout
|
||||
Thread.Sleep (timeout);
|
||||
Environment.Exit (exitCode);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
// Information about this assembly is defined by the following attributes.
|
||||
// Change them to the values specific to your project.
|
||||
|
||||
[assembly: AssemblyTitle ("DummyTestProcess")]
|
||||
[assembly: AssemblyDescription ("")]
|
||||
[assembly: AssemblyConfiguration ("")]
|
||||
[assembly: AssemblyCompany ("")]
|
||||
[assembly: AssemblyProduct ("")]
|
||||
[assembly: AssemblyCopyright ("${AuthorCopyright}")]
|
||||
[assembly: AssemblyTrademark ("")]
|
||||
[assembly: AssemblyCulture ("")]
|
||||
|
||||
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
|
||||
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
|
||||
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
|
||||
|
||||
[assembly: AssemblyVersion ("1.0.*")]
|
||||
|
||||
// The following attributes are used to specify the signing key for the assembly,
|
||||
// if desired. See the Mono documentation for more information about signing.
|
||||
|
||||
//[assembly: AssemblyDelaySign(false)]
|
||||
//[assembly: AssemblyKeyFile("")]
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Mono.Options" version="6.6.0.161" targetFramework="net472" />
|
||||
</packages>
|
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Xharness.Logging;
|
||||
|
||||
namespace Xharness.Execution {
|
||||
|
||||
public class ProcessExecutionResult {
|
||||
public bool TimedOut { get; set; }
|
||||
public int ExitCode { get; set; }
|
||||
|
||||
public bool Succeeded => !TimedOut && ExitCode == 0;
|
||||
}
|
||||
|
||||
// interface that helps to manage the different processes in the app.
|
||||
public interface IProcessManager {
|
||||
Task<ProcessExecutionResult> ExecuteCommandAsync (string filename, IList<string> args, ILog log, TimeSpan timeout, Dictionary<string, string> environment_variables = null, CancellationToken? cancellation_token = null);
|
||||
Task<ProcessExecutionResult> RunAsync (Process process, ILog log, CancellationToken? cancellationToken = null, bool? diagnostics = null);
|
||||
Task<ProcessExecutionResult> RunAsync (Process process, ILog log, TimeSpan? timeout = null, Dictionary<string, string> environment_variables = null, CancellationToken? cancellation_token = null, bool? diagnostics = null);
|
||||
Task<ProcessExecutionResult> RunAsync (Process process, ILog log, ILog stdoutLog, ILog stderrLog, TimeSpan? timeout = null, Dictionary<string, string> environment_variables = null, CancellationToken? cancellation_token = null, bool? diagnostics = null);
|
||||
Task<ProcessExecutionResult> RunAsync (Process process, ILog log, TextWriter StdoutStream, TextWriter StderrStream, TimeSpan? timeout = null, Dictionary<string, string> environment_variables = null, CancellationToken? cancellation_token = null, bool? diagnostics = null);
|
||||
Task<bool> WaitForExitAsync (Process process, TimeSpan? timeout = null);
|
||||
Task KillTreeAsync (Process process, ILog log, bool? diagnostics = true);
|
||||
Task KillTreeAsync (int pid, ILog log, bool? diagnostics = true);
|
||||
void GetChildrenPS (ILog log, List<int> list, int pid);
|
||||
}
|
||||
}
|
|
@ -8,26 +8,21 @@ using System.Text;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Xamarin.Utils;
|
||||
using Xharness;
|
||||
using Xharness.Logging;
|
||||
|
||||
namespace Xharness
|
||||
{
|
||||
public class ProcessExecutionResult
|
||||
{
|
||||
public bool TimedOut { get; set; }
|
||||
public int ExitCode { get; set; }
|
||||
namespace Xharness.Execution {
|
||||
public class ProcessManager : IProcessManager {
|
||||
public ProcessManager ()
|
||||
{
|
||||
}
|
||||
|
||||
public bool Succeeded { get { return !TimedOut && ExitCode == 0; } }
|
||||
}
|
||||
|
||||
public static class ProcessHelper
|
||||
{
|
||||
public static async Task<ProcessExecutionResult> ExecuteCommandAsync (string filename, IList<string> args, ILog log, TimeSpan timeout, Dictionary<string, string> environment_variables = null, CancellationToken? cancellation_token = null)
|
||||
public async Task<ProcessExecutionResult> ExecuteCommandAsync (string filename, IList<string> args, ILog log, TimeSpan timeout, Dictionary<string, string> environment_variables = null, CancellationToken? cancellation_token = null)
|
||||
{
|
||||
using (var p = new Process ()) {
|
||||
p.StartInfo.FileName = filename;
|
||||
p.StartInfo.Arguments = StringUtils.FormatArguments (args);
|
||||
return await p.RunAsync (log, true, timeout, environment_variables, cancellation_token);
|
||||
return await RunAsync (p, log, timeout, environment_variables, cancellation_token);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,21 +48,18 @@ namespace Xharness
|
|||
});
|
||||
return rv.Task;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Process_Extensions
|
||||
{
|
||||
public static async Task<ProcessExecutionResult> RunAsync (this Process process, ILog log, CancellationToken? cancellation_token = null, bool? diagnostics = null)
|
||||
public async Task<ProcessExecutionResult> RunAsync (Process process, ILog log, CancellationToken? cancellation_token = null, bool? diagnostics = null)
|
||||
{
|
||||
return await RunAsync (process, log, log, log, cancellation_token: cancellation_token, diagnostics: diagnostics);
|
||||
}
|
||||
|
||||
public static Task<ProcessExecutionResult> RunAsync (this Process process, ILog log, bool append = true, TimeSpan? timeout = null, Dictionary<string, string> environment_variables = null, CancellationToken? cancellation_token = null, bool? diagnostics = null)
|
||||
public Task<ProcessExecutionResult> RunAsync (Process process, ILog log, TimeSpan? timeout = null, Dictionary<string, string> environment_variables = null, CancellationToken? cancellation_token = null, bool? diagnostics = null)
|
||||
{
|
||||
return RunAsync (process, log, log, log, timeout, environment_variables, cancellation_token, diagnostics);
|
||||
}
|
||||
|
||||
public static Task<ProcessExecutionResult> RunAsync (this Process process, ILog log, ILog stdoutLog, ILog stderrLog, TimeSpan? timeout = null, Dictionary<string, string> environment_variables = null, CancellationToken? cancellation_token = null, bool? diagnostics = null)
|
||||
public Task<ProcessExecutionResult> RunAsync (Process process, ILog log, ILog stdoutLog, ILog stderrLog, TimeSpan? timeout = null, Dictionary<string, string> environment_variables = null, CancellationToken? cancellation_token = null, bool? diagnostics = null)
|
||||
{
|
||||
if (stdoutLog is TextWriter StdoutStream && stderrLog is TextWriter StderrStream) {
|
||||
return RunAsync (process, log, StdoutStream, StderrStream, timeout, environment_variables, cancellation_token, diagnostics);
|
||||
|
@ -76,7 +68,7 @@ namespace Xharness
|
|||
}
|
||||
}
|
||||
|
||||
public static async Task<ProcessExecutionResult> RunAsync (this Process process, ILog log, TextWriter StdoutStream, TextWriter StderrStream, TimeSpan? timeout = null, Dictionary<string, string> environment_variables = null, CancellationToken? cancellation_token = null, bool? diagnostics = null)
|
||||
public async Task<ProcessExecutionResult> RunAsync (Process process, ILog log, TextWriter StdoutStream, TextWriter StderrStream, TimeSpan? timeout = null, Dictionary<string, string> environment_variables = null, CancellationToken? cancellation_token = null, bool? diagnostics = null)
|
||||
{
|
||||
var stdout_completion = new TaskCompletionSource<bool> ();
|
||||
var stderr_completion = new TaskCompletionSource<bool> ();
|
||||
|
@ -149,19 +141,19 @@ namespace Xharness
|
|||
}
|
||||
if (!hasExited) {
|
||||
StderrStream.WriteLine ($"Execution of {pid} was cancelled.");
|
||||
ProcessHelper.kill (pid, 9);
|
||||
kill (pid, 9);
|
||||
}
|
||||
});
|
||||
|
||||
if (timeout.HasValue) {
|
||||
if (!await process.WaitForExitAsync (timeout.Value)) {
|
||||
await process.KillTreeAsync (log, diagnostics ?? true);
|
||||
if (!await WaitForExitAsync (process, timeout.Value)) {
|
||||
await KillTreeAsync (process, log, diagnostics ?? true);
|
||||
rv.TimedOut = true;
|
||||
lock (StderrStream)
|
||||
log.WriteLine ($"{pid} Execution timed out after {timeout.Value.TotalSeconds} seconds and the process was killed.");
|
||||
}
|
||||
}
|
||||
await process.WaitForExitAsync ();
|
||||
await WaitForExitAsync (process);
|
||||
Task.WaitAll (new Task [] { stderr_completion.Task, stdout_completion.Task }, TimeSpan.FromSeconds (1));
|
||||
|
||||
try {
|
||||
|
@ -173,7 +165,7 @@ namespace Xharness
|
|||
return rv;
|
||||
}
|
||||
|
||||
public async static Task<bool> WaitForExitAsync (this Process process, TimeSpan? timeout = null)
|
||||
public async Task<bool> WaitForExitAsync (Process process, TimeSpan? timeout = null)
|
||||
{
|
||||
if (process.HasExited)
|
||||
return true;
|
||||
|
@ -205,12 +197,12 @@ namespace Xharness
|
|||
}
|
||||
}
|
||||
|
||||
public static Task KillTreeAsync (this Process @this, ILog log, bool? diagnostics = true)
|
||||
public Task KillTreeAsync (Process process, ILog log, bool? diagnostics = true)
|
||||
{
|
||||
return KillTreeAsync (@this.Id, log, diagnostics);
|
||||
return KillTreeAsync (process.Id, log, diagnostics);
|
||||
}
|
||||
|
||||
public static async Task KillTreeAsync (int pid, ILog log, bool? diagnostics = true)
|
||||
public async Task KillTreeAsync (int pid, ILog log, bool? diagnostics = true)
|
||||
{
|
||||
var pids = new List<int> ();
|
||||
GetChildrenPS (log, pids, pid);
|
||||
|
@ -220,7 +212,7 @@ namespace Xharness
|
|||
log.WriteLine ("Writing process list:");
|
||||
ps.StartInfo.FileName = "ps";
|
||||
ps.StartInfo.Arguments = "-A -o pid,ruser,ppid,pgid,%cpu=%CPU,%mem=%MEM,flags=FLAGS,lstart,rss,vsz,tty,state,time,command";
|
||||
await ps.RunAsync (log, true, TimeSpan.FromSeconds (5), diagnostics: false);
|
||||
await RunAsync (ps, log, TimeSpan.FromSeconds (5), diagnostics: false);
|
||||
}
|
||||
|
||||
foreach (var diagnose_pid in pids) {
|
||||
|
@ -238,7 +230,7 @@ namespace Xharness
|
|||
File.WriteAllText (template, commands.ToString ());
|
||||
|
||||
log.WriteLine ($"Printing backtrace for pid={pid}");
|
||||
await dbg.RunAsync (log, true, TimeSpan.FromSeconds (30), diagnostics: false);
|
||||
await RunAsync (dbg, log, TimeSpan.FromSeconds (30), diagnostics: false);
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
|
@ -253,14 +245,14 @@ namespace Xharness
|
|||
// Send SIGABRT since that produces a crash report
|
||||
// lldb may fail to attach to system processes, but crash reports will still be produced with potentially helpful stack traces.
|
||||
for (int i = 0; i < pids.Count; i++)
|
||||
ProcessHelper.kill (pids [i], 6);
|
||||
kill (pids [i], 6);
|
||||
|
||||
// send kill -9 anyway as a last resort
|
||||
for (int i = 0; i < pids.Count; i++)
|
||||
ProcessHelper.kill (pids [i], 9);
|
||||
kill (pids [i], 9);
|
||||
}
|
||||
|
||||
static void GetChildrenPS (ILog log, List<int> list, int pid)
|
||||
public void GetChildrenPS (ILog log, List<int> list, int pid)
|
||||
{
|
||||
string stdout;
|
||||
|
|
@ -8,11 +8,13 @@ using System.Runtime.Serialization.Json;
|
|||
using System.Xml;
|
||||
using System.Text;
|
||||
using Xamarin;
|
||||
using Xharness.Execution;
|
||||
|
||||
namespace Xharness
|
||||
{
|
||||
public static class GitHub
|
||||
{
|
||||
static IProcessManager ProcessManager { get; set; } = new ProcessManager ();
|
||||
static WebClient CreateClient ()
|
||||
{
|
||||
var client = new WebClient ();
|
||||
|
@ -152,7 +154,7 @@ namespace Xharness
|
|||
git.StartInfo.FileName = "git";
|
||||
git.StartInfo.Arguments = $"diff-tree --no-commit-id --name-only -r {base_commit}..{head_commit}";
|
||||
var output = new StringWriter ();
|
||||
var rv = git.RunAsync (harness.HarnessLog, output, output).Result;
|
||||
var rv = ProcessManager.RunAsync (git, harness.HarnessLog, output, output).Result;
|
||||
if (rv.Succeeded)
|
||||
return output.ToString ().Split (new char [] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ using System.Xml;
|
|||
using Xamarin.Utils;
|
||||
using Xharness.BCLTestImporter;
|
||||
using Xharness.Logging;
|
||||
using Xharness.Execution;
|
||||
|
||||
namespace Xharness
|
||||
{
|
||||
|
@ -32,6 +33,7 @@ namespace Xharness
|
|||
public bool UseSystem { get; set; } // if the system XI/XM should be used, or the locally build XI/XM.
|
||||
public HashSet<string> Labels { get; } = new HashSet<string> ();
|
||||
public XmlResultJargon XmlJargon { get; set; } = XmlResultJargon.NUnitV3;
|
||||
public IProcessManager ProcessManager { get; set; } = new ProcessManager ();
|
||||
|
||||
public string XIBuildPath {
|
||||
get { return Path.GetFullPath (Path.Combine (RootDirectory, "..", "tools", "xibuild", "xibuild")); }
|
||||
|
@ -779,7 +781,7 @@ namespace Xharness
|
|||
|
||||
public Task<ProcessExecutionResult> ExecuteXcodeCommandAsync (string executable, IList<string> args, ILog log, TimeSpan timeout)
|
||||
{
|
||||
return ProcessHelper.ExecuteCommandAsync (Path.Combine (XcodeRoot, "Contents", "Developer", "usr", "bin", executable), args, log, timeout: timeout);
|
||||
return ProcessManager.ExecuteCommandAsync (Path.Combine (XcodeRoot, "Contents", "Developer", "usr", "bin", executable), args, log, timeout: timeout);
|
||||
}
|
||||
|
||||
public async Task ShowSimulatorList (Log log)
|
||||
|
@ -801,7 +803,7 @@ namespace Xharness
|
|||
var name = Path.GetFileName (report.Path);
|
||||
var symbolicated = logs.Create (Path.ChangeExtension (name, ".symbolicated.log"), $"Symbolicated crash report: {name}", timestamp: false);
|
||||
var environment = new Dictionary<string, string> { { "DEVELOPER_DIR", Path.Combine (XcodeRoot, "Contents", "Developer") } };
|
||||
var rv = await ProcessHelper.ExecuteCommandAsync (symbolicatecrash, new [] { report.Path }, symbolicated, TimeSpan.FromMinutes (1), environment);
|
||||
var rv = await ProcessManager.ExecuteCommandAsync (symbolicatecrash, new [] { report.Path }, symbolicated, TimeSpan.FromMinutes (1), environment);
|
||||
if (rv.Succeeded) {;
|
||||
log.WriteLine ("Symbolicated {0} successfully.", report.Path);
|
||||
return symbolicated;
|
||||
|
@ -830,7 +832,7 @@ namespace Xharness
|
|||
sb.Add ("--devname");
|
||||
sb.Add (device);
|
||||
}
|
||||
var result = await ProcessHelper.ExecuteCommandAsync (MlaunchPath, sb, log, TimeSpan.FromMinutes (1));
|
||||
var result = await ProcessManager.ExecuteCommandAsync (MlaunchPath, sb, log, TimeSpan.FromMinutes (1));
|
||||
if (result.Succeeded)
|
||||
rv.UnionWith (File.ReadAllLines (tmp));
|
||||
} finally {
|
||||
|
@ -895,7 +897,7 @@ namespace Xharness
|
|||
sb.Add ("--devname");
|
||||
sb.Add (DeviceName);
|
||||
}
|
||||
var result = await ProcessHelper.ExecuteCommandAsync (Harness.MlaunchPath, sb, Log, TimeSpan.FromMinutes (1));
|
||||
var result = await Harness.ProcessManager.ExecuteCommandAsync (Harness.MlaunchPath, sb, Log, TimeSpan.FromMinutes (1));
|
||||
if (result.Succeeded) {
|
||||
Log.WriteLine ("Downloaded crash report {0} to {1}", file, crash_report_target.Path);
|
||||
crash_report_target = await Harness.SymbolicateCrashReportAsync (Logs, Log, crash_report_target);
|
||||
|
|
|
@ -7,6 +7,7 @@ using System.Net;
|
|||
using System.Threading.Tasks;
|
||||
using System.Text;
|
||||
using Xharness.Logging;
|
||||
using Xharness.Execution;
|
||||
using Xharness.Jenkins.TestTasks;
|
||||
|
||||
namespace Xharness.Jenkins
|
||||
|
@ -1212,7 +1213,7 @@ namespace Xharness.Jenkins
|
|||
using (var process = new Process ()) {
|
||||
process.StartInfo.FileName = Harness.PeriodicCommand;
|
||||
process.StartInfo.Arguments = Harness.PeriodicCommandArguments;
|
||||
var rv = await process.RunAsync (periodic_loc, timeout: Harness.PeriodicCommandInterval);
|
||||
var rv = await Harness.ProcessManager.RunAsync (process, periodic_loc, timeout: Harness.PeriodicCommandInterval);
|
||||
if (!rv.Succeeded)
|
||||
periodic_loc.WriteLine ($"Periodic command failed with exit code {rv.ExitCode} (Timed out: {rv.TimedOut})");
|
||||
}
|
||||
|
@ -1278,7 +1279,7 @@ namespace Xharness.Jenkins
|
|||
|
||||
void BuildTestLibraries ()
|
||||
{
|
||||
ProcessHelper.ExecuteCommandAsync ("make", new [] { "all", $"-j{Environment.ProcessorCount}", "-C", Path.Combine (Harness.RootDirectory, "test-libraries") }, MainLog, TimeSpan.FromMinutes (10)).Wait ();
|
||||
Harness.ProcessManager.ExecuteCommandAsync ("make", new [] { "all", $"-j{Environment.ProcessorCount}", "-C", Path.Combine (Harness.RootDirectory, "test-libraries") }, MainLog, TimeSpan.FromMinutes (10)).Wait ();
|
||||
}
|
||||
|
||||
Task RunTestServer ()
|
||||
|
|
|
@ -50,7 +50,7 @@ namespace Xharness.Jenkins.TestTasks
|
|||
LogEvent (log, "Restoring nugets for {0} ({1}) on path {2}", TestName, Mode, projectPath);
|
||||
|
||||
var timeout = TimeSpan.FromMinutes (15);
|
||||
var result = await nuget.RunAsync (log, true, timeout);
|
||||
var result = await ProcessManager.RunAsync (nuget, log, timeout);
|
||||
if (result.TimedOut) {
|
||||
log.WriteLine ("Nuget restore timed out after {0} seconds.", timeout.TotalSeconds);
|
||||
return TestExecutingResult.TimedOut;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Xharness.Execution;
|
||||
|
||||
namespace Xharness.Jenkins.TestTasks
|
||||
{
|
||||
|
@ -7,6 +8,7 @@ namespace Xharness.Jenkins.TestTasks
|
|||
{
|
||||
public bool SpecifyPlatform = true;
|
||||
public bool SpecifyConfiguration = true;
|
||||
public IProcessManager ProcessManager { get; set; } = new ProcessManager ();
|
||||
|
||||
public override string Mode {
|
||||
get { return Platform.ToString (); }
|
||||
|
|
|
@ -6,6 +6,7 @@ using System.Threading.Tasks;
|
|||
using System.Xml;
|
||||
using Xamarin;
|
||||
using Xamarin.Utils;
|
||||
using Xharness.Execution;
|
||||
using Xharness.Logging;
|
||||
|
||||
namespace Xharness.Jenkins.TestTasks
|
||||
|
@ -15,6 +16,7 @@ namespace Xharness.Jenkins.TestTasks
|
|||
public string Path;
|
||||
public bool BCLTest;
|
||||
public bool IsUnitTest;
|
||||
public IProcessManager ProcessManager { get; set; } = new ProcessManager ();
|
||||
|
||||
public MacExecuteTask (BuildToolTask build_task)
|
||||
: base (build_task)
|
||||
|
@ -97,7 +99,7 @@ namespace Xharness.Jenkins.TestTasks
|
|||
try {
|
||||
var timeout = TimeSpan.FromMinutes (20);
|
||||
|
||||
result = await proc.RunAsync (log, true, timeout);
|
||||
result = await ProcessManager.RunAsync (proc, log, timeout);
|
||||
if (result.TimedOut) {
|
||||
FailureMessage = $"Execution timed out after {timeout.TotalSeconds} seconds.";
|
||||
log.WriteLine (FailureMessage);
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace Xharness.Jenkins.TestTasks
|
|||
LogEvent (log, "Making {0} in {1}", Target, WorkingDirectory);
|
||||
if (!Harness.DryRun) {
|
||||
var timeout = Timeout;
|
||||
var result = await make.RunAsync (log, true, timeout);
|
||||
var result = await ProcessManager.RunAsync (make, log, timeout);
|
||||
if (result.TimedOut) {
|
||||
ExecutionResult = TestExecutingResult.TimedOut;
|
||||
log.WriteLine ("Make timed out after {0} seconds.", timeout.TotalSeconds);
|
||||
|
|
|
@ -8,6 +8,7 @@ using System.Threading.Tasks;
|
|||
using System.Xml;
|
||||
using Xamarin;
|
||||
using Xamarin.Utils;
|
||||
using Xharness.Execution;
|
||||
using Xharness.Logging;
|
||||
|
||||
namespace Xharness.Jenkins.TestTasks
|
||||
|
@ -20,6 +21,7 @@ namespace Xharness.Jenkins.TestTasks
|
|||
public bool ProduceHtmlReport = true;
|
||||
public bool InProcess;
|
||||
public TimeSpan Timeout = TimeSpan.FromMinutes (10);
|
||||
IProcessManager ProcessManager { get; set; } = new ProcessManager ();
|
||||
|
||||
public NUnitExecuteTask (BuildToolTask build_task)
|
||||
: base (build_task)
|
||||
|
@ -143,7 +145,7 @@ namespace Xharness.Jenkins.TestTasks
|
|||
Jenkins.MainLog.WriteLine ("Executing {0} ({1})", TestName, Mode);
|
||||
if (!Harness.DryRun) {
|
||||
ExecutionResult = TestExecutingResult.Running;
|
||||
var result = await proc.RunAsync (log, true, Timeout);
|
||||
var result = await ProcessManager.RunAsync (proc, log, Timeout);
|
||||
if (result.TimedOut) {
|
||||
FailureMessage = $"Execution timed out after {Timeout.TotalMinutes} minutes.";
|
||||
log.WriteLine (FailureMessage);
|
||||
|
|
|
@ -38,7 +38,7 @@ namespace Xharness.Jenkins.TestTasks
|
|||
try {
|
||||
var timeout = TimeSpan.FromMinutes (20);
|
||||
|
||||
var result = await proc.RunAsync (log, true, timeout);
|
||||
var result = await ProcessManager.RunAsync (proc, log, timeout);
|
||||
if (result.TimedOut) {
|
||||
FailureMessage = $"Execution timed out after {timeout.TotalSeconds} seconds.";
|
||||
log.WriteLine (FailureMessage);
|
||||
|
|
|
@ -40,7 +40,7 @@ namespace Xharness.Jenkins.TestTasks
|
|||
LogEvent (BuildLog, "Building {0} ({1})", TestName, Mode);
|
||||
if (!Harness.DryRun) {
|
||||
var timeout = TimeSpan.FromMinutes (60);
|
||||
var result = await xbuild.RunAsync (BuildLog, true, timeout);
|
||||
var result = await ProcessManager.RunAsync (xbuild, BuildLog, timeout);
|
||||
if (result.TimedOut) {
|
||||
ExecutionResult = TestExecutingResult.TimedOut;
|
||||
BuildLog.WriteLine ("Build timed out after {0} seconds.", timeout.TotalSeconds);
|
||||
|
@ -75,7 +75,7 @@ namespace Xharness.Jenkins.TestTasks
|
|||
SetEnvironmentVariables (xbuild);
|
||||
LogEvent (log, "Cleaning {0} ({1}) - {2}", TestName, Mode, project_file);
|
||||
var timeout = TimeSpan.FromMinutes (1);
|
||||
await xbuild.RunAsync (log, true, timeout);
|
||||
await ProcessManager.RunAsync (xbuild, log, timeout);
|
||||
log.WriteLine ("Clean timed out after {0} seconds.", timeout.TotalSeconds);
|
||||
Jenkins.MainLog.WriteLine ("Cleaned {0} ({1})", TestName, Mode);
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ using System.Threading.Tasks;
|
|||
using System.Xml;
|
||||
using Xamarin;
|
||||
using Xamarin.Utils;
|
||||
using Xharness.Execution;
|
||||
using Xharness.Logging;
|
||||
|
||||
namespace Xharness
|
||||
|
@ -59,7 +60,7 @@ namespace Xharness
|
|||
process.StartInfo.FileName = Harness.MlaunchPath;
|
||||
process.StartInfo.Arguments = string.Format ("--sdkroot {0} --listsim {1}", Harness.XcodeRoot, tmpfile);
|
||||
log.WriteLine ("Launching {0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
|
||||
var rv = await process.RunAsync (log, false, timeout: TimeSpan.FromSeconds (30));
|
||||
var rv = await Harness.ProcessManager.RunAsync (process, log, timeout: TimeSpan.FromSeconds (30));
|
||||
if (!rv.Succeeded)
|
||||
throw new Exception ("Failed to list simulators.");
|
||||
log.WriteLine ("Result:");
|
||||
|
@ -450,6 +451,7 @@ namespace Xharness
|
|||
{
|
||||
public string UDID { get; set; }
|
||||
public string Name { get; set; }
|
||||
static IProcessManager ProcessManager { get; set; } = new ProcessManager ();
|
||||
public string SimRuntime;
|
||||
public string SimDeviceType;
|
||||
public string DataPath;
|
||||
|
@ -488,14 +490,14 @@ namespace Xharness
|
|||
|
||||
public static async Task KillEverythingAsync (ILog log)
|
||||
{
|
||||
await ProcessHelper.ExecuteCommandAsync ("launchctl", new [] { "remove", "com.apple.CoreSimulator.CoreSimulatorService" }, log, TimeSpan.FromSeconds (10));
|
||||
await ProcessManager.ExecuteCommandAsync ("launchctl", new [] { "remove", "com.apple.CoreSimulator.CoreSimulatorService" }, log, TimeSpan.FromSeconds (10));
|
||||
|
||||
var to_kill = new string [] { "iPhone Simulator", "iOS Simulator", "Simulator", "Simulator (Watch)", "com.apple.CoreSimulator.CoreSimulatorService", "ibtoold" };
|
||||
|
||||
var args = new List<string> ();
|
||||
args.Add ("-9");
|
||||
args.AddRange (to_kill);
|
||||
await ProcessHelper.ExecuteCommandAsync ("killall", args, log, TimeSpan.FromSeconds (10));
|
||||
await ProcessManager.ExecuteCommandAsync ("killall", args, log, TimeSpan.FromSeconds (10));
|
||||
|
||||
foreach (var dir in new string [] {
|
||||
Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.UserProfile), "Library", "Saved Application State", "com.apple.watchsimulator.savedState"),
|
||||
|
@ -599,7 +601,7 @@ namespace Xharness
|
|||
}
|
||||
}
|
||||
args.Add (sql.ToString ());
|
||||
var rv = await ProcessHelper.ExecuteCommandAsync ("sqlite3", args, log, TimeSpan.FromSeconds (5));
|
||||
var rv = await ProcessManager.ExecuteCommandAsync ("sqlite3", args, log, TimeSpan.FromSeconds (5));
|
||||
if (!rv.Succeeded) {
|
||||
failure = true;
|
||||
break;
|
||||
|
@ -614,7 +616,7 @@ namespace Xharness
|
|||
}
|
||||
|
||||
log.WriteLine ("Current TCC database contents:");
|
||||
await ProcessHelper.ExecuteCommandAsync ("sqlite3", new [] { TCC_db, ".dump" }, log, TimeSpan.FromSeconds (5));
|
||||
await ProcessManager.ExecuteCommandAsync ("sqlite3", new [] { TCC_db, ".dump" }, log, TimeSpan.FromSeconds (5));
|
||||
}
|
||||
|
||||
async Task OpenSimulator (ILog log)
|
||||
|
@ -629,7 +631,7 @@ namespace Xharness
|
|||
simulator_app = Path.Combine (Harness.XcodeRoot, "Contents", "Developer", "Applications", "iOS Simulator.app");
|
||||
}
|
||||
|
||||
await ProcessHelper.ExecuteCommandAsync ("open", new [] { "-a", simulator_app, "--args", "-CurrentDeviceUDID", UDID }, log, TimeSpan.FromSeconds (15));
|
||||
await ProcessManager.ExecuteCommandAsync ("open", new [] { "-a", simulator_app, "--args", "-CurrentDeviceUDID", UDID }, log, TimeSpan.FromSeconds (15));
|
||||
}
|
||||
|
||||
public async Task PrepareSimulatorAsync (ILog log, params string[] bundle_identifiers)
|
||||
|
@ -727,7 +729,7 @@ namespace Xharness
|
|||
process.StartInfo.FileName = Harness.MlaunchPath;
|
||||
process.StartInfo.Arguments = string.Format ("--sdkroot {0} --listdev={1} {2} --output-format=xml", Harness.XcodeRoot, tmpfile, extra_data ? "--list-extra-data" : string.Empty);
|
||||
log.WriteLine ("Launching {0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
|
||||
var rv = await process.RunAsync (log, false, timeout: TimeSpan.FromSeconds (120));
|
||||
var rv = await Harness.ProcessManager.RunAsync (process, log, timeout: TimeSpan.FromSeconds (120));
|
||||
if (!rv.Succeeded)
|
||||
throw new Exception ("Failed to list devices.");
|
||||
log.WriteLine ("Result:");
|
||||
|
|
|
@ -0,0 +1,178 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Xharness.Execution;
|
||||
using Xharness.Logging;
|
||||
|
||||
namespace Xharness.Tests.Execution.Tests {
|
||||
|
||||
[TestFixture]
|
||||
public class ProcessManagerTests {
|
||||
|
||||
// not very portable, but we want to make sure that the class does the right thing
|
||||
// maybe in the futue we could create a dummy process that will help us
|
||||
string logPath;
|
||||
string stdoutLogPath;
|
||||
string stderrLogPath;
|
||||
string stdoutMessage;
|
||||
string stderrMessage;
|
||||
Mock<ILogs> logs;
|
||||
ILog executionLog;
|
||||
ILog stdoutLog;
|
||||
ILog stderrLog;
|
||||
string dummyProcess;
|
||||
Process testProcess;
|
||||
ProcessManager manager;
|
||||
Dictionary<string, string> environmentVariables = new Dictionary<string, string> ();
|
||||
|
||||
[SetUp]
|
||||
public void SetUp ()
|
||||
{
|
||||
logPath = Path.GetTempFileName ();
|
||||
stderrLogPath = Path.GetTempFileName ();
|
||||
stdoutLogPath = Path.GetTempFileName ();
|
||||
stdoutMessage = "Hola mundo!!!";
|
||||
stderrMessage = "Adios mundo cruel";
|
||||
logs = new Mock<ILogs> ();
|
||||
executionLog = new LogFile (logs.Object, "my execution log", logPath);
|
||||
stdoutLog = new LogFile (logs.Object, "my stdout log", stdoutLogPath);
|
||||
stderrLog = new LogFile (logs.Object, "my stderr log", stderrLogPath);
|
||||
dummyProcess = Path.Combine (Path.GetDirectoryName (GetType ().Assembly.Location), "DummyTestProcess.exe");
|
||||
manager = new ProcessManager ();
|
||||
testProcess = new Process ();
|
||||
testProcess.StartInfo.FileName = "mono";
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown ()
|
||||
{
|
||||
executionLog?.Dispose ();
|
||||
executionLog = null;
|
||||
stdoutLog?.Dispose ();
|
||||
stdoutLog = null;
|
||||
stderrLog?.Dispose ();
|
||||
stderrLog = null;
|
||||
testProcess?.Dispose ();
|
||||
testProcess = null;
|
||||
manager = null;
|
||||
if (File.Exists (logPath))
|
||||
File.Delete (logPath);
|
||||
if (File.Exists (stderrLogPath))
|
||||
File.Delete (stderrLogPath);
|
||||
if(File.Exists (stdoutLogPath))
|
||||
File.Delete (stdoutLogPath);
|
||||
}
|
||||
|
||||
void AssertStdoutAndStderr ()
|
||||
{
|
||||
bool stdoutFound = false;
|
||||
bool stderrFound = false;
|
||||
|
||||
using (var reader = new StreamReader (logPath)) {
|
||||
string line;
|
||||
while ((line = reader.ReadLine ()) != null) {
|
||||
if (line.Contains (stdoutMessage))
|
||||
stdoutFound = true;
|
||||
if (line.Contains (stderrMessage))
|
||||
stderrFound = true;
|
||||
}
|
||||
}
|
||||
Assert.IsTrue (stdoutFound, "stdout was not captured");
|
||||
Assert.IsTrue (stderrFound, "stderr was not captured");
|
||||
}
|
||||
|
||||
[TestCase (0, 1, true, false, Description = "Success")] // 0, short timeout, success, no timeout
|
||||
[TestCase (1, 1, false, false, Description = "Failure")] // 1, short timeout, failure, no timeout
|
||||
[TestCase (0, 60, false, true, Description = "Timeout" )] // 0, long timeout, failure, timeout
|
||||
public async Task ExecuteCommandAsyncTest (int resultCode, int timeoutCount, bool success, bool timeout)
|
||||
{
|
||||
var args = new List<string> ();
|
||||
args.Add (dummyProcess);
|
||||
args.Add ($"--exit-code={resultCode}");
|
||||
args.Add ($"--timeout={timeoutCount}");
|
||||
args.Add ($"--stdout=\"{stdoutMessage}\"");
|
||||
args.Add ($"--stderr=\"{stderrMessage}\"");
|
||||
var result = await manager.ExecuteCommandAsync ("mono", args, executionLog, new TimeSpan (0, 0, 5));
|
||||
if (!timeout)
|
||||
Assert.AreEqual (resultCode, result.ExitCode, "exit code");
|
||||
Assert.AreEqual (success, result.Succeeded, "success");
|
||||
Assert.AreEqual (timeout, result.TimedOut, "timeout");
|
||||
AssertStdoutAndStderr ();
|
||||
}
|
||||
|
||||
[TestCase (0, 1, true, false, Description = "Success")] // 0, short timeout, success, no timeout
|
||||
[TestCase (1, 1, false, false, Description = "Failure")] // 1, short timeout, failure, no timeout
|
||||
public async Task RunAsyncProcessNoArgsTest (int resultCode, int timeoutCount, bool success, bool timeout)
|
||||
{
|
||||
var source = new CancellationTokenSource ();
|
||||
testProcess.StartInfo.Arguments = $"{dummyProcess} --exit-code={resultCode} --timeout={timeoutCount} --stdout=\"{stdoutMessage}\" --stderr=\"{stderrMessage}\"";
|
||||
var result = await manager.RunAsync (testProcess, executionLog, source.Token);
|
||||
if (!timeout)
|
||||
Assert.AreEqual (resultCode, result.ExitCode, "exit code");
|
||||
Assert.AreEqual (success, result.Succeeded, "success");
|
||||
Assert.AreEqual (timeout, result.TimedOut, "timeout");
|
||||
AssertStdoutAndStderr ();
|
||||
}
|
||||
|
||||
[TestCase (0, 1, true, false, Description = "Success")] // 0, short timeout, success, no timeout
|
||||
[TestCase (1, 1, false, false, Description = "Failure")] // 1, short timeout, failure, no timeout
|
||||
[TestCase (0, 60, false, true, Description = "Timeout")] // 0, long timeout, failure, timeout
|
||||
public async Task RunAsycnProcessAppendSuccessTest (int resultCode, int timeoutCounter, bool success, bool timeout)
|
||||
{
|
||||
// write some trash in the test log so that we ensure that we did append
|
||||
string oldMessage = "Hello hello!";
|
||||
using (var writer = new StreamWriter (logPath)) {
|
||||
writer.WriteLine (oldMessage);
|
||||
}
|
||||
testProcess.StartInfo.Arguments = $"{dummyProcess} --exit-code={resultCode} --timeout={timeoutCounter} --stdout=\"{stdoutMessage}\" --stderr=\"{stderrMessage}\"";
|
||||
var result = await manager.RunAsync (testProcess, executionLog, new TimeSpan (0, 0, 5));
|
||||
if (!timeout)
|
||||
Assert.AreEqual (resultCode, result.ExitCode, "exit code");
|
||||
Assert.AreEqual (timeout, result.TimedOut, "timeout");
|
||||
Assert.AreEqual (success, result.Succeeded, "success");
|
||||
AssertStdoutAndStderr ();
|
||||
}
|
||||
|
||||
[TestCase (0, 1, true, false, Description = "Success")] // 0, short timeout, success, no timeout
|
||||
[TestCase (1, 1, false, false, Description = "Failure")] // 1, short timeout, failure, no timeout
|
||||
[TestCase (0, 60, false, true, Description = "Timeout")] // 0, long timeout, failure, timeout
|
||||
public async Task RunAsyncProcessTextWritersTest (int resultCode, int timeoutCounter, bool success, bool timeout)
|
||||
{
|
||||
//Task<ProcessExecutionResult> RunAsync (Process process, ILog log, ILog stdoutLog, ILog stderrLog, TimeSpan? timeout = null, Dictionary<string, string> environment_variables = null, CancellationToken? cancellation_token = null, bool? diagnostics = null);
|
||||
var source = new CancellationTokenSource ();
|
||||
testProcess.StartInfo.Arguments = $"{dummyProcess} --exit-code={resultCode} --timeout={timeoutCounter} --stdout=\"{stdoutMessage}\" --stderr=\"{stderrMessage}\"";
|
||||
var result = await manager.RunAsync (testProcess, executionLog, stdoutLog, stderrLog, new TimeSpan (0, 0, 5), environmentVariables, source.Token);
|
||||
if (!timeout)
|
||||
Assert.AreEqual (resultCode, result.ExitCode, "exit code");
|
||||
Assert.AreEqual (timeout, result.TimedOut, "timeout");
|
||||
Assert.AreEqual (success, result.Succeeded, "success");
|
||||
// assert the diff logs have the correct line
|
||||
bool stdoutFound = false;
|
||||
bool stderrFound = false;
|
||||
|
||||
using (var reader = new StreamReader (stdoutLogPath)) {
|
||||
string line;
|
||||
while ((line = reader.ReadLine ()) != null) {
|
||||
if (line.Contains (stdoutMessage))
|
||||
stdoutFound = true;
|
||||
}
|
||||
}
|
||||
Assert.IsTrue (stdoutFound, "stdout was not captured");
|
||||
|
||||
using (var reader = new StreamReader (stderrLogPath)) {
|
||||
string line;
|
||||
while ((line = reader.ReadLine ()) != null) {
|
||||
if (line.Contains (stderrMessage))
|
||||
stderrFound = true;
|
||||
}
|
||||
}
|
||||
Assert.IsTrue (stderrFound, "stderr was not captured");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -62,6 +62,13 @@
|
|||
<Compile Include="Listeners\Tests\SimpleListenerFactoryTest.cs" />
|
||||
<Compile Include="Listeners\Tests\SimpleFileListenerTest.cs" />
|
||||
<Compile Include="Listeners\Tests\SimpleTcpListenerTest.cs" />
|
||||
<Compile Include="..\Execution\ProcessManager.cs">
|
||||
<Link>Execution\ProcessManager.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Execution\IProcessManager.cs">
|
||||
<Link>Execution\IProcessManager.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="Execution\Tests\ProcessManagerTests.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
|
@ -73,6 +80,9 @@
|
|||
<EmbeddedResource Include="Samples\xUnitSample.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../DummyTestProcess/DummyTestProcess.csproj">
|
||||
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\xharness.csproj">
|
||||
<Project>{e1f53f80-8399-499b-8017-c414b9cd263b}</Project>
|
||||
<Name>xharness</Name>
|
||||
|
|
|
@ -108,7 +108,6 @@
|
|||
<Compile Include="SolutionGenerator.cs" />
|
||||
<Compile Include="MacTarget.cs" />
|
||||
<Compile Include="Jenkins\Jenkins.cs" />
|
||||
<Compile Include="Process_Extensions.cs" />
|
||||
<Compile Include="Simulators.cs" />
|
||||
<Compile Include="TestProject.cs" />
|
||||
<Compile Include="GitHub.cs" />
|
||||
|
@ -144,11 +143,14 @@
|
|||
<Compile Include="Listeners\SimpleListener.cs" />
|
||||
<Compile Include="Listeners\SimpleTcpListener.cs" />
|
||||
<Compile Include="Listeners\SimpleListenerFactory.cs" />
|
||||
<Compile Include="Execution\IProcessManager.cs" />
|
||||
<Compile Include="Execution\ProcessManager.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="BCLTestImporter\" />
|
||||
<Folder Include="Logging\" />
|
||||
<Folder Include="Listeners\" />
|
||||
<Folder Include="Execution\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="xharness.js">
|
||||
|
|
|
@ -5,6 +5,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "xharness", "xharness.csproj
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xharness.Tests", "Xharness.Tests\Xharness.Tests.csproj", "{AD1B78C3-6A36-40D0-B45D-246504A39D64}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DummyTestProcess", "DummyTestProcess\DummyTestProcess.csproj", "{2318BC20-40F1-4A12-B695-10B0433CFB35}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -19,5 +21,9 @@ Global
|
|||
{AD1B78C3-6A36-40D0-B45D-246504A39D64}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{AD1B78C3-6A36-40D0-B45D-246504A39D64}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{AD1B78C3-6A36-40D0-B45D-246504A39D64}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2318BC20-40F1-4A12-B695-10B0433CFB35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2318BC20-40F1-4A12-B695-10B0433CFB35}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2318BC20-40F1-4A12-B695-10B0433CFB35}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2318BC20-40F1-4A12-B695-10B0433CFB35}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
Загрузка…
Ссылка в новой задаче