[Harness] Add strongly typed mlaunch args and escape them (#8167)

Co-authored-by: Premek Vysoky <prvysoky@microsoft.com>
This commit is contained in:
Přemek Vysoký 2020-03-25 15:59:29 +01:00 коммит произвёл GitHub
Родитель 13e199a685
Коммит d98c515a4c
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
12 изменённых файлов: 563 добавлений и 259 удалений

Просмотреть файл

@ -6,8 +6,9 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using Xharness.Hardware;
using Xharness.Execution;
using Xharness.Execution.Mlaunch;
using Xharness.Hardware;
using Xharness.Jenkins.TestTasks;
using Xharness.Listeners;
using Xharness.Logging;
@ -25,13 +26,10 @@ namespace Xharness {
readonly IDeviceLogCapturerFactory deviceLogCapturerFactory;
readonly ITestReporterFactory testReporterFactory;
readonly RunMode runMode;
readonly bool isSimulator;
readonly TestTarget target;
readonly IHarness harness;
readonly string variation;
readonly double timeoutMultiplier;
readonly string logDirectory;
string deviceName;
string companionDeviceName;
@ -51,7 +49,7 @@ namespace Xharness {
public RunMode RunMode { get; private set; }
bool IsExtension => AppInformation.Extension.HasValue;
public AppBundleInformation AppInformation { get; }
public TestExecutingResult Result { get; private set; }
@ -107,7 +105,6 @@ namespace Xharness {
this.companionDeviceName = companionDeviceName;
this.ensureCleanSimulatorState = ensureCleanSimulatorState;
this.simulators = simulators;
this.variation = variation;
this.BuildTask = buildTask;
this.target = target;
@ -121,8 +118,8 @@ namespace Xharness {
{
if (simulators != null)
return true;
var sims = simulatorsLoaderFactory.CreateLoader();
var sims = simulatorsLoaderFactory.CreateLoader ();
await sims.LoadAsync (Logs.Create ($"simulator-list-{Helpers.Timestamp}.log", "Simulator list"), false, false);
simulators = await sims.FindAsync (target, MainLog);
@ -133,14 +130,13 @@ namespace Xharness {
{
if (deviceName != null)
return;
deviceName = Environment.GetEnvironmentVariable ("DEVICE_NAME");
if (!string.IsNullOrEmpty (deviceName))
return;
var devs = devicesLoaderFactory.CreateLoader ();
Task.Run (async () =>
{
Task.Run (async () => {
await devs.LoadAsync (MainLog, false, false);
}).Wait ();
@ -165,8 +161,7 @@ namespace Xharness {
throw new NoDeviceFoundException ($"Could not find any applicable devices with device class(es): {string.Join (", ", deviceClasses)}");
} else if (selected.Count () > 1) {
selected_data = selected
.OrderBy ((dev) =>
{
.OrderBy ((dev) => {
Version v;
if (Version.TryParse (dev.ProductVersion, out v))
return v;
@ -193,21 +188,20 @@ namespace Xharness {
FindDevice ();
var args = new List<string> ();
var args = new MlaunchArguments ();
if (!string.IsNullOrEmpty (harness.XcodeRoot)) {
args.Add ("--sdkroot");
args.Add (harness.XcodeRoot);
args.Add (new SdkRootArgument (harness.XcodeRoot));
}
for (int i = -1; i < harness.Verbosity; i++)
args.Add ("-v");
args.Add ("--installdev");
args.Add (AppInformation.AppPath);
AddDeviceName (args, companionDeviceName ?? deviceName);
args.Add (new VerbosityArgument ());
args.Add (new InstallAppOnDeviceArgument (AppInformation.AppPath));
args.Add (new DeviceNameArgument (companionDeviceName ?? deviceName));
if (RunMode == RunMode.WatchOS) {
args.Add ("--device");
args.Add ("ios,watchos");
args.Add (new DeviceArgument ("ios,watchos"));
}
var totalSize = Directory.GetFiles (AppInformation.AppPath, "*", SearchOption.AllDirectories).Select ((v) => new FileInfo (v).Length).Sum ();
@ -223,17 +217,17 @@ namespace Xharness {
FindDevice ();
var args = new List<string> ();
if (!string.IsNullOrEmpty (harness.XcodeRoot)) {
args.Add ("--sdkroot");
args.Add (harness.XcodeRoot);
}
for (int i = -1; i < harness.Verbosity; i++)
args.Add ("-v");
var args = new MlaunchArguments ();
args.Add ("--uninstalldevbundleid");
args.Add (AppInformation.BundleIdentifier);
AddDeviceName (args, companionDeviceName ?? deviceName);
if (!string.IsNullOrEmpty (harness.XcodeRoot)) {
args.Add (new SdkRootArgument (harness.XcodeRoot));
}
for (int i = -1; i < harness.Verbosity; i++)
args.Add (new VerbosityArgument ());
args.Add (new UninstallAppFromDeviceArgument (AppInformation.BundleIdentifier));
args.Add (new DeviceNameArgument (companionDeviceName ?? deviceName));
return await ProcessManager.ExecuteCommandAsync (harness.MlaunchPath, args, MainLog, TimeSpan.FromMinutes (1));
}
@ -247,42 +241,42 @@ namespace Xharness {
if (!isSimulator)
FindDevice ();
var args = new List<string> ();
var args = new MlaunchArguments ();
if (!string.IsNullOrEmpty (harness.XcodeRoot)) {
args.Add ("--sdkroot");
args.Add (harness.XcodeRoot);
args.Add (new SdkRootArgument (harness.XcodeRoot));
}
for (int i = -1; i < harness.Verbosity; i++)
args.Add ("-v");
args.Add ("-argument=-connection-mode");
args.Add ("-argument=none"); // This will prevent the app from trying to connect to any IDEs
args.Add ("-argument=-app-arg:-autostart");
args.Add ("-setenv=NUNIT_AUTOSTART=true");
args.Add ("-argument=-app-arg:-autoexit");
args.Add ("-setenv=NUNIT_AUTOEXIT=true");
args.Add ("-argument=-app-arg:-enablenetwork");
args.Add ("-setenv=NUNIT_ENABLE_NETWORK=true");
args.Add (new VerbosityArgument ());
args.Add (new SetAppArgumentArgument ("-connection-mode"));
args.Add (new SetAppArgumentArgument ("none")); // This will prevent the app from trying to connect to any IDEs
args.Add (new SetAppArgumentArgument ("-autostart", true));
args.Add (new SetEnvVariableArgument ("NUNIT_AUTOSTART", true));
args.Add (new SetAppArgumentArgument ("-autoexit", true));
args.Add (new SetEnvVariableArgument ("NUNIT_AUTOEXIT", true));
args.Add (new SetAppArgumentArgument ("-enablenetwork", true));
args.Add (new SetEnvVariableArgument ("NUNIT_ENABLE_NETWORK", true));
// detect if we are using a jenkins bot.
var useXmlOutput = harness.InCI;
if (useXmlOutput) {
args.Add ("-setenv=NUNIT_ENABLE_XML_OUTPUT=true");
args.Add ("-setenv=NUNIT_ENABLE_XML_MODE=wrapped");
args.Add ("-setenv=NUNIT_XML_VERSION=nunitv3");
args.Add (new SetEnvVariableArgument ("NUNIT_ENABLE_XML_OUTPUT", true));
args.Add (new SetEnvVariableArgument ("NUNIT_ENABLE_XML_MODE", "wrapped"));
args.Add (new SetEnvVariableArgument ("NUNIT_XML_VERSION", "nunitv3"));
}
if (harness.InCI) {
// We use the 'BUILD_REVISION' variable to detect whether we're running CI or not.
args.Add ($"-setenv=BUILD_REVISION=${Environment.GetEnvironmentVariable ("BUILD_REVISION")}");
args.Add (new SetEnvVariableArgument ("BUILD_REVISION", "$" + Environment.GetEnvironmentVariable ("BUILD_REVISION")));
}
if (!harness.GetIncludeSystemPermissionTests (TestPlatform.iOS, !isSimulator))
args.Add ("-setenv=DISABLE_SYSTEM_PERMISSION_TESTS=1");
args.Add (new SetEnvVariableArgument ("DISABLE_SYSTEM_PERMISSION_TESTS", 1));
if (isSimulator) {
args.Add ("-argument=-app-arg:-hostname:127.0.0.1");
args.Add ("-setenv=NUNIT_HOSTNAME=127.0.0.1");
args.Add (new SetAppArgumentArgument ("-hostname:127.0.0.1", true));
args.Add (new SetEnvVariableArgument ("NUNIT_HOSTNAME", "127.0.0.1"));
} else {
var ips = new StringBuilder ();
var ipAddresses = System.Net.Dns.GetHostEntry (System.Net.Dns.GetHostName ()).AddressList;
@ -292,23 +286,24 @@ namespace Xharness {
ips.Append (ipAddresses [i].ToString ());
}
args.Add ($"-argument=-app-arg:-hostname:{ips}");
args.Add ($"-setenv=NUNIT_HOSTNAME={ips}");
var ipArg = ips.ToString ();
args.Add (new SetAppArgumentArgument ($"-hostname:{ipArg}", true));
args.Add (new SetEnvVariableArgument ("NUNIT_HOSTNAME", ipArg));
}
var listener_log = Logs.Create ($"test-{RunMode.ToString().ToLower()}-{Helpers.Timestamp}.log", LogType.TestLog.ToString (), timestamp: !useXmlOutput);
var listener_log = Logs.Create ($"test-{RunMode.ToString ().ToLower ()}-{Helpers.Timestamp}.log", LogType.TestLog.ToString (), timestamp: !useXmlOutput);
var (transport, listener, listenerTmpFile) = listenerFactory.Create (RunMode, MainLog, listener_log, isSimulator, true, useXmlOutput);
args.Add ($"-argument=-app-arg:-transport:{transport}");
args.Add ($"-setenv=NUNIT_TRANSPORT={transport.ToString ().ToUpper ()}");
if (transport == ListenerTransport.File)
args.Add ($"-setenv=NUNIT_LOG_FILE={listenerTmpFile}");
listener.Initialize ();
args.Add ($"-argument=-app-arg:-hostport:{listener.Port}");
args.Add ($"-setenv=NUNIT_HOSTPORT={listener.Port}");
args.Add (new SetAppArgumentArgument ($"-transport:{transport}", true));
args.Add (new SetEnvVariableArgument ("NUNIT_TRANSPORT", transport.ToString ().ToUpper ()));
if (transport == ListenerTransport.File)
args.Add (new SetEnvVariableArgument ("NUNIT_LOG_FILE", listenerTmpFile));
args.Add (new SetAppArgumentArgument ($"-hostport:{listener.Port}", true));
args.Add (new SetEnvVariableArgument ("NUNIT_HOSTPORT", listener.Port));
listener.StartAsync ();
@ -323,27 +318,26 @@ namespace Xharness {
.ContinueWith (testReporter.LaunchCallback)
.DoNotAwait ();
foreach (var kvp in harness.EnvironmentVariables)
args.Add ($"-setenv={kvp.Key}={kvp.Value}");
args.AddRange (harness.EnvironmentVariables.Select (kvp => new SetEnvVariableArgument (kvp.Key, kvp.Value)));
if (IsExtension) {
switch (AppInformation.Extension) {
case Extension.TodayExtension:
args.Add (isSimulator ? "--launchsimbundleid" : "--launchdevbundleid");
args.Add ("todayviewforextensions:" + AppInformation.BundleIdentifier);
args.Add ("--observe-extension");
args.Add (AppInformation.LaunchAppPath);
args.Add (isSimulator
? (MlaunchArgument) new LaunchSimulatorExtensionArgument (AppInformation.LaunchAppPath, AppInformation.BundleIdentifier)
: new LaunchDeviceExtensionArgument (AppInformation.LaunchAppPath, AppInformation.BundleIdentifier));
break;
case Extension.WatchKit2:
default:
throw new NotImplementedException ();
}
} else {
args.Add (isSimulator ? "--launchsim" : "--launchdev");
args.Add (AppInformation.LaunchAppPath);
args.Add (isSimulator
? (MlaunchArgument) new LaunchSimulatorArgument (AppInformation.LaunchAppPath)
: new LaunchDeviceArgument (AppInformation.LaunchAppPath));
}
if (!isSimulator)
args.Add ("--disable-memory-limits");
args.Add (new DisableMemoryLimitsArgument ());
if (isSimulator) {
if (!await FindSimulatorAsync ())
@ -352,13 +346,13 @@ namespace Xharness {
if (RunMode != RunMode.WatchOS) {
var stderr_tty = harness.GetStandardErrorTty ();
if (!string.IsNullOrEmpty (stderr_tty)) {
args.Add ($"--stdout={stderr_tty}");
args.Add ($"--stderr={stderr_tty}");
args.Add (new SetStdoutArgument (stderr_tty));
args.Add (new SetStderrArgument (stderr_tty));
} else {
var stdout_log = Logs.CreateFile ($"stdout-{Helpers.Timestamp}.log", "Standard output");
var stderr_log = Logs.CreateFile ($"stderr-{Helpers.Timestamp}.log", "Standard error");
args.Add ($"--stdout={stdout_log}");
args.Add ($"--stderr={stderr_log}");
args.Add (new SetStdoutArgument (stdout_log));
args.Add (new SetStderrArgument (stderr_log));
}
}
@ -388,7 +382,7 @@ namespace Xharness {
await sim.PrepareSimulatorAsync (MainLog, AppInformation.BundleIdentifier);
}
args.Add ($"--device=:v2:udid={simulator.UDID}");
args.Add (new SimulatorUDIDArgument (simulator.UDID));
await crashReporter.StartCaptureAsync ();
@ -408,12 +402,12 @@ namespace Xharness {
MainLog.WriteLine ("*** Executing {0}/{1} on device '{2}' ***", AppInformation.AppName, RunMode, deviceName);
if (RunMode == RunMode.WatchOS) {
args.Add ("--attach-native-debugger"); // this prevents the watch from backgrounding the app.
args.Add (new AttachNativeDebuggerArgument ()); // this prevents the watch from backgrounding the app.
} else {
args.Add ("--wait-for-exit");
args.Add (new WaitForExitArgument ());
}
AddDeviceName (args);
args.Add (new DeviceNameArgument (deviceName));
var deviceSystemLog = Logs.Create ($"device-{deviceName}-{Helpers.Timestamp}.log", "Device log");
var deviceLogCapturer = deviceLogCapturerFactory.Create (harness.HarnessLog, deviceSystemLog, deviceName);
@ -447,18 +441,5 @@ namespace Xharness {
(Result, FailureMessage) = await testReporter.ParseResult ();
return testReporter.Success.Value ? 0 : 1;
}
public void AddDeviceName (IList<string> args)
{
AddDeviceName (args, deviceName);
}
public static void AddDeviceName (IList<string> args, string device_name)
{
if (!string.IsNullOrEmpty (device_name)) {
args.Add ("--devname");
args.Add (device_name);
}
}
}
}

Просмотреть файл

@ -60,13 +60,16 @@ namespace Xharness
{
streamEnds = new CountdownEvent (2);
var sb = new List<string> {
"--logdev",
"--sdkroot",
xcodeRoot,
"--devname",
deviceName
};
process = new Process ();
process.StartInfo.FileName = mlaunchPath;
var sb = new List<string> ();
sb.Add ("--logdev");
sb.Add ("--sdkroot");
sb.Add (xcodeRoot);
AppRunner.AddDeviceName (sb, deviceName);
process.StartInfo.Arguments = StringUtils.FormatArguments (sb);
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;

Просмотреть файл

@ -1,10 +1,12 @@
namespace Xharness.Execution.Mlaunch {
using System;
namespace Xharness.Execution.Mlaunch {
/// <summary>
/// Specify the location of Apple SDKs, default to 'xcode-select' value.
/// </summary>
public sealed class SdkRootArgument : SingleValueArgument {
public SdkRootArgument (string sdkPath) : base ("sdkroot", sdkPath)
public SdkRootArgument (string sdkPath) : base ("sdkroot", sdkPath, false)
{
}
}
@ -18,6 +20,15 @@
}
}
/// <summary>
/// Write the syslog from the device to the console.
/// </summary>
public sealed class LogDevArgument : OptionArgument {
public LogDevArgument () : base ("logdev")
{
}
}
/// <summary>
/// List the available simulators. The output is xml, and written to the specified file.
/// </summary>
@ -37,14 +48,41 @@
}
/// <summary>
/// Specify which device (when many are present) the [install|lauch|kill|log]dev command applies
/// Specifies the device type to launch the simulator as.
/// </summary>
public sealed class DeviceNameArgument : SingleValueArgument {
public DeviceNameArgument (string deviceName) : base ("devname", deviceName)
public sealed class DeviceArgument : SingleValueArgument {
public DeviceArgument (string deviceType) : base ("device", deviceType)
{
}
}
/// <summary>
/// Specify which device (when many are present) the [install|lauch|kill|log]dev command applies.
/// </summary>
public sealed class DeviceNameArgument : SingleValueArgument {
public DeviceNameArgument (string deviceName) : base ("devname", deviceName, false)
{
}
}
/// <summary>
/// Install the specified iOS app bundle on the device.
/// </summary>
public sealed class InstallAppOnDeviceArgument : SingleValueArgument {
public InstallAppOnDeviceArgument (string appPath) : base ("installdev", appPath, false)
{
}
}
/// <summary>
/// Uninstall the specified bundle id from the device.
/// </summary>
public sealed class UninstallAppFromDeviceArgument : SingleValueArgument {
public UninstallAppFromDeviceArgument (string appBundleId) : base ("uninstalldevbundleid", appBundleId, false)
{
}
}
/// <summary>
/// Specify the output format for some commands as Default.
/// </summary>
@ -90,4 +128,162 @@
{
}
}
/// <summary>
/// Attach native debugger.
/// </summary>
public sealed class AttachNativeDebuggerArgument : OptionArgument {
public AttachNativeDebuggerArgument () : base ("attach-native-debugger")
{
}
}
/// <summary>
/// Attempt to disable memory limits for launched apps.
/// This is just an attempt, some or all usual limits may still be enforced.
/// </summary>
public sealed class DisableMemoryLimitsArgument : OptionArgument {
public DisableMemoryLimitsArgument () : base ("disable-memory-limits")
{
}
}
public sealed class WaitForExitArgument : OptionArgument {
public WaitForExitArgument () : base ("wait-for-exit")
{
}
}
/// <summary>
/// Launch the app with this command line argument. This must be specified multiple times for multiple arguments.
/// </summary>
public sealed class SetAppArgumentArgument : MlaunchArgument {
readonly string value;
public SetAppArgumentArgument (string value, bool isAppArg = false)
{
this.value = value ?? throw new ArgumentNullException (nameof (value));
if (isAppArg)
this.value = "-app-arg:" + this.value;
}
public override string AsCommandLineArgument () => "-argument=" + value;
}
/// <summary>
/// Set the environment variable in the application on startup.
/// </summary>
public sealed class SetEnvVariableArgument : MlaunchArgument {
readonly string variableName;
readonly string variableValue;
public SetEnvVariableArgument (string variableName, object variableValue)
{
this.variableName = variableName ?? throw new ArgumentNullException (nameof (variableName));
this.variableValue = variableValue?.ToString () ?? throw new ArgumentNullException (nameof (variableValue));
if (variableValue is bool)
this.variableValue = this.variableValue.ToLower ();
}
public override string AsCommandLineArgument () => Escape ($"-setenv={variableName}={variableValue}");
}
/// <summary>
/// Redirect the standard output for the simulated application to the specified file.
/// </summary>
public sealed class SetStdoutArgument : SingleValueArgument {
public SetStdoutArgument (string targetFile) : base ("stdout", targetFile)
{
}
}
/// <summary>
/// Redirect the standard error for the simulated application to the specified file.
/// </summary>
public sealed class SetStderrArgument : SingleValueArgument {
public SetStderrArgument (string targetFile) : base ("stderr", targetFile)
{
}
}
/// <summary>
/// Launch an app that is installed on device, specified by bundle identifier.
/// </summary>
public sealed class LaunchDeviceArgument : SingleValueArgument {
public LaunchDeviceArgument (string launchAppPath) : base ("launchdev", launchAppPath, false)
{
}
}
/// <summary>
/// Launch the specified MonoTouch.app in the simulator.
/// </summary>
public sealed class LaunchSimulatorArgument : SingleValueArgument {
public LaunchSimulatorArgument (string launchAppPath) : base ("launchsim", launchAppPath, false)
{
}
}
/// <summary>
/// Specify which simulator to launch.
/// </summary>
public sealed class SimulatorUDIDArgument : MlaunchArgument {
readonly string udid;
public SimulatorUDIDArgument (string udid)
{
this.udid = udid ?? throw new ArgumentNullException (nameof (udid));
}
public override string AsCommandLineArgument () => $"--device=:v2:udid={udid}";
}
/// <summary>
/// Launch an app that is installed on device, specified by bundle identifier.
/// </summary>
public sealed class LaunchSimulatorExtensionArgument : MlaunchArgument {
readonly string launchAppPath;
readonly string bundleId;
public LaunchSimulatorExtensionArgument (string launchAppPath, string bundleId)
{
this.launchAppPath = launchAppPath ?? throw new ArgumentNullException (nameof (launchAppPath));
this.bundleId = bundleId ?? throw new ArgumentNullException (nameof (bundleId));
}
public override string AsCommandLineArgument () => "--launchsimbundleid " +
"todayviewforextensions:" + Escape (bundleId) + " " +
"--observe-extension " + Escape (launchAppPath);
}
/// <summary>
/// Launch the specified bundle id in the simulator (which must already be installed).
/// </summary>
public sealed class LaunchDeviceExtensionArgument : MlaunchArgument {
readonly string launchAppPath;
readonly string bundleId;
public LaunchDeviceExtensionArgument (string launchAppPath, string bundleId)
{
this.launchAppPath = launchAppPath ?? throw new ArgumentNullException (nameof (launchAppPath));
this.bundleId = bundleId ?? throw new ArgumentNullException (nameof (bundleId));
}
public override string AsCommandLineArgument () => "--launchdevbundleid " +
"todayviewforextensions:" + Escape (bundleId) + " " +
"--observe-extension " + Escape (launchAppPath);
}
/// <summary>
/// Set the verbosity level. Can be used repeatedly to lower the level.
/// </summary>
public sealed class VerbosityArgument : MlaunchArgument {
public VerbosityArgument ()
{
}
public override string AsCommandLineArgument () => "-v";
}
}

Просмотреть файл

@ -1,6 +1,8 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Xharness.Utilities;
namespace Xharness.Execution.Mlaunch {
// mlaunch is really important and has a lot of arguments that are known but
@ -9,23 +11,43 @@ namespace Xharness.Execution.Mlaunch {
// needs a value does contain the value
public abstract class MlaunchArgument {
public abstract string AsCommandLineArgument ();
protected static string Escape (string value) => StringUtils.FormatArguments (value);
public override bool Equals (object obj)
{
return obj is MlaunchArgument arg && arg.AsCommandLineArgument () == AsCommandLineArgument ();
}
public override int GetHashCode ()
{
return AsCommandLineArgument ().GetHashCode ();
}
}
public abstract class SingleValueArgument : MlaunchArgument {
private readonly string argumentName;
private readonly string argumentValue;
readonly string argumentName;
readonly string argumentValue;
readonly bool useEqualSign;
protected SingleValueArgument (string argumentName, string argumentValue)
protected SingleValueArgument (string argumentName, string argumentValue, bool useEqualSign = true)
{
this.argumentName = argumentName ?? throw new ArgumentNullException (nameof (argumentName));
this.argumentValue = argumentValue ?? throw new ArgumentNullException (nameof (argumentValue));
this.useEqualSign = useEqualSign;
}
public override string AsCommandLineArgument () => $"--{argumentName}={argumentValue}";
public override string AsCommandLineArgument ()
{
if (useEqualSign)
return Escape ($"--{argumentName}={argumentValue}");
else
return $"--{argumentName} {Escape (argumentValue)}";
}
}
public abstract class OptionArgument : MlaunchArgument {
private readonly string argumentName;
readonly string argumentName;
protected OptionArgument (string argumentName)
{
@ -35,7 +57,7 @@ namespace Xharness.Execution.Mlaunch {
public override string AsCommandLineArgument () => $"--{argumentName}";
}
public class MlaunchArguments {
public class MlaunchArguments : IEnumerable<MlaunchArgument> {
readonly List<MlaunchArgument> arguments = new List<MlaunchArgument> ();
public MlaunchArguments (params MlaunchArgument [] args)
@ -44,8 +66,25 @@ namespace Xharness.Execution.Mlaunch {
}
public void Add (MlaunchArgument arg) => arguments.Add (arg);
public void AddRange (params MlaunchArgument [] args) => arguments.AddRange (args);
public void AddRange (IEnumerable<MlaunchArgument> args) => arguments.AddRange (args);
public string AsCommandLine () => string.Join (" ", arguments.Select (a => a.AsCommandLineArgument ()));
public IEnumerable<MlaunchArgument> GetArguments () => arguments;
public IEnumerator<MlaunchArgument> GetEnumerator () => arguments.GetEnumerator ();
IEnumerator IEnumerable.GetEnumerator () => arguments.GetEnumerator ();
public override string ToString () => AsCommandLine ();
public override bool Equals (object obj)
{
return obj is MlaunchArguments arg && arg.AsCommandLine () == AsCommandLine ();
}
public override int GetHashCode ()
{
return AsCommandLine ().GetHashCode ();
}
}
}

Просмотреть файл

@ -30,7 +30,7 @@ namespace Xharness.Utilities
return string.Join (" ", QuoteForProcess (arguments));
}
static string [] QuoteForProcess (params string [] array)
static string [] QuoteForProcess (params string [] array)
{
if (array == null || array.Length == 0)
return array;

Просмотреть файл

@ -1,47 +0,0 @@
using System.Collections;
using NUnit.Framework;
using Xharness.Execution.Mlaunch;
namespace Xharness.Tests.Execution.Tests {
[TestFixture]
public class MlaunchArgumentsTest {
public class CommandLineDataTestSource {
public static IEnumerable CommandLineArgs {
get {
string listDevFile = "/my/listdev.txt";
string listSimFile = "/my/listsim.txt";
string xmlOutputType = "XML";
yield return new TestCaseData (arg: new MlaunchArgument [] { new ListDevicesArgument (listDevFile) })
.Returns ($"--listdev={listDevFile}");
yield return new TestCaseData (arg: new MlaunchArgument [] { new ListSimulatorsArgument (listSimFile) })
.Returns ($"--listsim={listSimFile}");
yield return new TestCaseData (arg: new MlaunchArgument [] { new XmlOutputFormatArgument () })
.Returns ($"--output-format={xmlOutputType}");
yield return new TestCaseData (arg: new MlaunchArgument [] { new ListExtraDataArgument () })
.Returns ("--list-extra-data");
yield return new TestCaseData (arg: new MlaunchArgument [] { new ListDevicesArgument (listDevFile), new ListSimulatorsArgument (listSimFile) })
.Returns ($"--listdev={listDevFile} --listsim={listSimFile}");
yield return new TestCaseData (arg: new MlaunchArgument [] { new ListDevicesArgument (listDevFile), new ListExtraDataArgument () })
.Returns ($"--listdev={listDevFile} --list-extra-data");
yield return new TestCaseData (arg: new MlaunchArgument [] { new ListDevicesArgument (listDevFile), new XmlOutputFormatArgument (), new ListExtraDataArgument () })
.Returns ($"--listdev={listDevFile} --output-format={xmlOutputType} --list-extra-data");
}
}
}
[TestCaseSource (typeof (CommandLineDataTestSource), "CommandLineArgs")]
public string AsCommandLineTest (MlaunchArgument [] args)
{
return new MlaunchArguments (args).AsCommandLine ();
}
}
}

Просмотреть файл

@ -0,0 +1,128 @@
using System.Collections;
using System.Collections.Generic;
using NUnit.Framework;
using Xharness.Execution.Mlaunch;
using Xharness.Utilities;
namespace Xharness.Tests.Execution.Tests {
[TestFixture]
public class MlaunchArgumentsTests {
public class CommandLineDataTestSource {
public static IEnumerable CommandLineArgs {
get {
string listDevFile = "/my/listdev.txt";
string listSimFile = "/my/listsim.txt";
string xmlOutputType = "XML";
yield return new TestCaseData (arg:
new MlaunchArgument [] {
new ListDevicesArgument (listDevFile)
})
.Returns ($"--listdev={listDevFile}");
yield return new TestCaseData (arg:
new MlaunchArgument [] {
new ListSimulatorsArgument (listSimFile)
})
.Returns ($"--listsim={listSimFile}");
yield return new TestCaseData (arg:
new MlaunchArgument [] {
new XmlOutputFormatArgument ()
})
.Returns ($"--output-format={xmlOutputType}");
yield return new TestCaseData (arg:
new MlaunchArgument [] {
new ListExtraDataArgument ()
})
.Returns ("--list-extra-data");
yield return new TestCaseData (arg:
new MlaunchArgument [] {
new DownloadCrashReportToArgument ("/path/with spaces.txt"),
new DeviceNameArgument ("Test iPad")
})
.Returns ($"\"--download-crash-report-to=/path/with spaces.txt\" --devname \"Test iPad\"");
yield return new TestCaseData (arg:
new MlaunchArgument [] {
new SetEnvVariableArgument ("SOME_PARAM", "true"),
new SetEnvVariableArgument ("NUNIT_LOG_FILE", "/another space/path.txt")
})
.Returns ($"-setenv=SOME_PARAM=true \"-setenv=NUNIT_LOG_FILE=/another space/path.txt\"");
yield return new TestCaseData (arg:
new MlaunchArgument [] {
new ListDevicesArgument (listDevFile),
new XmlOutputFormatArgument (),
new ListExtraDataArgument ()
})
.Returns ($"--listdev={listDevFile} --output-format={xmlOutputType} --list-extra-data");
}
}
}
[TestCaseSource (typeof (CommandLineDataTestSource), "CommandLineArgs")]
public string AsCommandLineTest (MlaunchArgument [] args)
{
return new MlaunchArguments (args).AsCommandLine ();
}
[Test]
public void MlaunchArgumentAndProcessManagerTest ()
{
var oldArgs = new List<string> () {
"--download-crash-report-to=/path/with spaces.txt",
"--sdkroot",
"/path to xcode/spaces",
"--devname",
"Premek's iPhone",
};
var newArgs = new MlaunchArguments () {
new DownloadCrashReportToArgument ("/path/with spaces.txt"),
new SdkRootArgument ("/path to xcode/spaces"),
new DeviceNameArgument ("Premek's iPhone"),
};
var oldWayOfPassingArgs = StringUtils.FormatArguments (oldArgs);
var newWayOfPassingArgs = newArgs.AsCommandLine ();
Assert.AreEqual (oldWayOfPassingArgs, newWayOfPassingArgs, "Something changed when moving to MlaunchArguments");
}
[Test]
public void MlaunchArgumentEqualityTest ()
{
var arg1 = new DownloadCrashReportToArgument ("/path/with spaces.txt");
var arg2 = new DownloadCrashReportToArgument ("/path/with spaces.txt");
var arg3 = new DownloadCrashReportToArgument ("/path/with.txt");
Assert.AreEqual (arg1, arg2, "equality is broken");
Assert.AreNotEqual (arg1, arg3, "equality is broken");
}
[Test]
public void MlaunchArgumentsEqualityTest ()
{
var args1 = new MlaunchArgument [] {
new ListDevicesArgument ("foo"),
new ListSimulatorsArgument ("bar")
};
var args2 = new MlaunchArgument [] {
new ListDevicesArgument ("foo"),
new ListSimulatorsArgument ("bar")
};
var args3 = new MlaunchArgument [] {
new ListDevicesArgument ("foo"),
new ListSimulatorsArgument ("xyz")
};
Assert.AreEqual (args1, args2, "equality is broken");
Assert.AreNotEqual (args1, args3, "equality is broken");
}
}
}

Просмотреть файл

@ -70,14 +70,14 @@ namespace Xharness.Tests.Hardware.Tests {
await devices.LoadAsync (executionLog.Object);
});
MlaunchArgument sdkRootArg = passedArguments.GetArguments ().Where (a => a is SdkRootArgument).FirstOrDefault();
MlaunchArgument sdkRootArg = passedArguments.Where (a => a is SdkRootArgument).FirstOrDefault();
Assert.IsNotNull (sdkRootArg, "sdk arg missing");
AssertArgumentValue (sdkRootArg, sdkPath, "sdk arg wrong");
MlaunchArgument listDevArg = passedArguments.GetArguments ().Where (a => a is ListDevicesArgument).FirstOrDefault();
MlaunchArgument listDevArg = passedArguments.Where (a => a is ListDevicesArgument).FirstOrDefault();
Assert.IsNotNull (listDevArg, "list devices arg missing");
MlaunchArgument outputFormatArg = passedArguments.GetArguments ().Where (a => a is XmlOutputFormatArgument).FirstOrDefault();
MlaunchArgument outputFormatArg = passedArguments.Where (a => a is XmlOutputFormatArgument).FirstOrDefault();
Assert.IsNotNull (outputFormatArg, "output format arg missing");
}
@ -98,7 +98,7 @@ namespace Xharness.Tests.Hardware.Tests {
passedArguments = args;
// we get the temp file that was passed as the args, and write our sample xml, which will be parsed to get the devices :)
var tempPath = args.GetArguments ().Where (a => a is ListDevicesArgument).First ().AsCommandLineArgument ();
var tempPath = args.Where (a => a is ListDevicesArgument).First ().AsCommandLineArgument ();
tempPath = tempPath.Substring(tempPath.IndexOf('=') + 1);
var name = GetType ().Assembly.GetManifestResourceNames ().Where (a => a.EndsWith ("devices.xml", StringComparison.Ordinal)).FirstOrDefault ();
@ -117,18 +117,18 @@ namespace Xharness.Tests.Hardware.Tests {
// validate the execution of mlaunch
Assert.AreEqual (mlaunchPath, processPath, "process path");
MlaunchArgument sdkRootArg = passedArguments.GetArguments ().Where (a => a is SdkRootArgument).FirstOrDefault();
MlaunchArgument sdkRootArg = passedArguments.Where (a => a is SdkRootArgument).FirstOrDefault();
Assert.IsNotNull (sdkRootArg, "sdk arg missing");
AssertArgumentValue (sdkRootArg, sdkPath, "sdk arg wrong");
MlaunchArgument listDevArg = passedArguments.GetArguments ().Where (a => a is ListDevicesArgument).FirstOrDefault();
MlaunchArgument listDevArg = passedArguments.Where (a => a is ListDevicesArgument).FirstOrDefault();
Assert.IsNotNull (listDevArg, "list devices arg missing");
MlaunchArgument outputFormatArg = passedArguments.GetArguments ().Where (a => a is XmlOutputFormatArgument).FirstOrDefault();
MlaunchArgument outputFormatArg = passedArguments.Where (a => a is XmlOutputFormatArgument).FirstOrDefault();
Assert.IsNotNull (outputFormatArg, "output format arg missing");
if (extraData) {
MlaunchArgument listExtraDataArg = passedArguments.GetArguments ().Where (a => a is ListExtraDataArgument).FirstOrDefault();
MlaunchArgument listExtraDataArg = passedArguments.Where (a => a is ListExtraDataArgument).FirstOrDefault();
Assert.IsNotNull (listExtraDataArg, "list extra data arg missing");
}

Просмотреть файл

@ -71,14 +71,14 @@ namespace Xharness.Tests.Hardware.Tests {
});
// validate the execution of mlaunch
MlaunchArgument sdkRootArg = passedArguments.GetArguments ().Where (a => a is SdkRootArgument).FirstOrDefault();
MlaunchArgument sdkRootArg = passedArguments.Where (a => a is SdkRootArgument).FirstOrDefault();
Assert.IsNotNull (sdkRootArg, "sdk arg missing");
AssertArgumentValue (sdkRootArg, sdkPath, "sdk arg wrong");
MlaunchArgument listSimArg = passedArguments.GetArguments ().Where (a => a is ListSimulatorsArgument).FirstOrDefault();
MlaunchArgument listSimArg = passedArguments.Where (a => a is ListSimulatorsArgument).FirstOrDefault();
Assert.IsNotNull (listSimArg, "list devices arg missing");
MlaunchArgument outputFormatArg = passedArguments.GetArguments ().Where (a => a is XmlOutputFormatArgument).FirstOrDefault();
MlaunchArgument outputFormatArg = passedArguments.Where (a => a is XmlOutputFormatArgument).FirstOrDefault();
Assert.IsNotNull (outputFormatArg, "output format arg missing");
}
@ -109,7 +109,7 @@ namespace Xharness.Tests.Hardware.Tests {
passedArguments = args;
// we get the temp file that was passed as the args, and write our sample xml, which will be parsed to get the devices :)
var tempPath = args.GetArguments ().Where (a => a is ListSimulatorsArgument).First ().AsCommandLineArgument ();
var tempPath = args.Where (a => a is ListSimulatorsArgument).First ().AsCommandLineArgument ();
tempPath = tempPath.Substring(tempPath.IndexOf('=') + 1);
CopySampleData (tempPath);
@ -121,14 +121,14 @@ namespace Xharness.Tests.Hardware.Tests {
// validate the execution of mlaunch
Assert.AreEqual (mlaunchPath, processPath, "process path");
MlaunchArgument sdkRootArg = passedArguments.GetArguments ().Where (a => a is SdkRootArgument).FirstOrDefault();
MlaunchArgument sdkRootArg = passedArguments.Where (a => a is SdkRootArgument).FirstOrDefault();
Assert.IsNotNull (sdkRootArg, "sdk arg missing");
AssertArgumentValue (sdkRootArg, sdkPath, "sdk arg wrong");
MlaunchArgument listSimArg = passedArguments.GetArguments ().Where (a => a is ListSimulatorsArgument).FirstOrDefault();
MlaunchArgument listSimArg = passedArguments.Where (a => a is ListSimulatorsArgument).FirstOrDefault();
Assert.IsNotNull (listSimArg, "list devices arg missing");
MlaunchArgument outputFormatArg = passedArguments.GetArguments ().Where (a => a is XmlOutputFormatArgument).FirstOrDefault();
MlaunchArgument outputFormatArg = passedArguments.Where (a => a is XmlOutputFormatArgument).FirstOrDefault();
Assert.IsNotNull (outputFormatArg, "output format arg missing");
Assert.AreEqual (75, simulators.AvailableDevices.Count());
@ -158,7 +158,7 @@ namespace Xharness.Tests.Hardware.Tests {
passedArguments = args;
// we get the temp file that was passed as the args, and write our sample xml, which will be parsed to get the devices :)
var tempPath = args.GetArguments ().Where (a => a is ListSimulatorsArgument).First ().AsCommandLineArgument ();
var tempPath = args.Where (a => a is ListSimulatorsArgument).First ().AsCommandLineArgument ();
tempPath = tempPath.Substring(tempPath.IndexOf('=') + 1);
CopySampleData (tempPath);

Просмотреть файл

@ -9,9 +9,11 @@ using System.Threading.Tasks;
using Moq;
using NUnit.Framework;
using Xharness.Execution;
using Xharness.Execution.Mlaunch;
using Xharness.Hardware;
using Xharness.Listeners;
using Xharness.Logging;
using Xharness.Utilities;
namespace Xharness.Tests {
[TestFixture]
@ -223,6 +225,9 @@ namespace Xharness.Tests {
var processResult = new ProcessExecutionResult () { ExitCode = 1, TimedOut = false };
processManager.SetReturnsDefault (Task.FromResult (processResult));
devices.Setup (x => x.ConnectedDevices).Returns (mockDevices);
// Act
var appRunner = new AppRunner (processManager.Object,
appBundleInformationParser,
simulatorsFactory,
@ -239,28 +244,19 @@ namespace Xharness.Tests {
projectFilePath: projectFilePath,
buildConfiguration: "Debug");
devices.Setup (x => x.ConnectedDevices).Returns (mockDevices);
// Act
CancellationToken cancellationToken = new CancellationToken ();
var result = await appRunner.InstallAsync (cancellationToken);
// Verify
Assert.AreEqual (1, result.ExitCode);
var expectedArgs = $"--sdkroot /path/to/xcode -v -v -v " +
$"--installdev {StringUtils.FormatArguments (appPath)} " +
$"--devname \"Test iPad\"";
processManager.Verify (x => x.ExecuteCommandAsync (
"/path/to/mlaunch",
new List<string> () {
"--sdkroot",
"/path/to/xcode",
"-v",
"-v",
"-v",
"--installdev",
appPath,
"--devname",
"Test iPad"
},
It.Is<MlaunchArguments> (args => args.AsCommandLine () == expectedArgs),
mainLog.Object,
TimeSpan.FromHours (1),
null,
@ -277,6 +273,9 @@ namespace Xharness.Tests {
var processResult = new ProcessExecutionResult () { ExitCode = 3, TimedOut = false };
processManager.SetReturnsDefault (Task.FromResult (processResult));
devices.Setup (x => x.ConnectedDevices).Returns (mockDevices.Reverse ());
// Act
var appRunner = new AppRunner (processManager.Object,
appBundleInformationParser,
simulatorsFactory,
@ -293,24 +292,18 @@ namespace Xharness.Tests {
projectFilePath: Path.Combine (sampleProjectPath, "SystemXunit.csproj"),
buildConfiguration: "Debug");
devices.Setup (x => x.ConnectedDevices).Returns (mockDevices.Reverse ());
var result = await appRunner.UninstallAsync ();
// Verify
Assert.AreEqual (3, result.ExitCode);
var expectedArgs = $"--sdkroot /path/to/xcode -v -v " +
$"--uninstalldevbundleid {StringUtils.FormatArguments (appName)} " +
$"--devname \"Test iPad\"";
processManager.Verify (x => x.ExecuteCommandAsync (
"/path/to/mlaunch",
new List<string> () {
"--sdkroot",
"/path/to/xcode",
"-v",
"-v",
"--uninstalldevbundleid",
appName,
"--devname",
"Test iPad"
},
It.Is<MlaunchArguments> (args => args.AsCommandLine () == expectedArgs),
mainLog.Object,
TimeSpan.FromMinutes (1),
null,
@ -458,19 +451,20 @@ namespace Xharness.Tests {
It.IsAny<string> ()))
.Returns (captureLog.Object);
var expectedArgs = $"--sdkroot {xcodePath} -v -v -argument=-connection-mode -argument=none " +
$"-argument=-app-arg:-autostart -setenv=NUNIT_AUTOSTART=true -argument=-app-arg:-autoexit " +
$"-setenv=NUNIT_AUTOEXIT=true -argument=-app-arg:-enablenetwork -setenv=NUNIT_ENABLE_NETWORK=true " +
var expectedArgs = $"--sdkroot {StringUtils.FormatArguments (xcodePath)} -v -v " +
$"-argument=-connection-mode -argument=none -argument=-app-arg:-autostart " +
$"-setenv=NUNIT_AUTOSTART=true -argument=-app-arg:-autoexit -setenv=NUNIT_AUTOEXIT=true " +
$"-argument=-app-arg:-enablenetwork -setenv=NUNIT_ENABLE_NETWORK=true " +
$"-setenv=DISABLE_SYSTEM_PERMISSION_TESTS=1 -argument=-app-arg:-hostname:127.0.0.1 " +
$"-setenv=NUNIT_HOSTNAME=127.0.0.1 -argument=-app-arg:-transport:Tcp -setenv=NUNIT_TRANSPORT=TCP " +
$"-argument=-app-arg:-hostport:{simpleListener.Object.Port} -setenv=NUNIT_HOSTPORT={simpleListener.Object.Port} " +
$"-setenv=env1=value1 -setenv=env2=value2 --launchsim {appPath} --stdout=tty1 --stderr=tty1 " +
$"--device=:v2:udid={simulator.Object.UDID}";
$"-setenv=env1=value1 -setenv=env2=value2 --launchsim {StringUtils.FormatArguments (appPath)} " +
$"--stdout=tty1 --stderr=tty1 --device=:v2:udid={simulator.Object.UDID}";
processManager
.Setup (x => x.ExecuteCommandAsync (
mlaunchPath,
It.Is<IList<string>> (args => string.Join (" ", args) == expectedArgs),
It.Is<MlaunchArguments> (args => args.AsCommandLine () == expectedArgs),
mainLog.Object,
TimeSpan.FromMinutes (harness.Timeout * 2),
null,
@ -620,18 +614,18 @@ namespace Xharness.Tests {
ips.Append (ipAddresses [i].ToString ());
}
var expectedArgs = $"--sdkroot {xcodePath} -v -v -argument=-connection-mode -argument=none " +
var expectedArgs = $"--sdkroot {StringUtils.FormatArguments (xcodePath)} -v -v -argument=-connection-mode -argument=none " +
$"-argument=-app-arg:-autostart -setenv=NUNIT_AUTOSTART=true -argument=-app-arg:-autoexit " +
$"-setenv=NUNIT_AUTOEXIT=true -argument=-app-arg:-enablenetwork -setenv=NUNIT_ENABLE_NETWORK=true " +
$"-setenv=DISABLE_SYSTEM_PERMISSION_TESTS=1 -argument=-app-arg:-hostname:{ips} -setenv=NUNIT_HOSTNAME={ips} " +
$"-argument=-app-arg:-transport:Tcp -setenv=NUNIT_TRANSPORT=TCP -argument=-app-arg:-hostport:{simpleListener.Object.Port} " +
$"-setenv=NUNIT_HOSTPORT={simpleListener.Object.Port} -setenv=env1=value1 -setenv=env2=value2 " +
$"--launchdev {appPath} --disable-memory-limits --wait-for-exit --devname Test iPad";
$"--launchdev {StringUtils.FormatArguments (appPath)} --disable-memory-limits --wait-for-exit --devname \"Test iPad\"";
processManager
.Setup (x => x.ExecuteCommandAsync (
mlaunchPath,
It.Is<IList<string>> (args => string.Join (" ", args) == expectedArgs),
It.Is<MlaunchArguments> (args => args.AsCommandLine () == expectedArgs),
It.IsAny<ILog> (),
TimeSpan.FromMinutes (harness.Timeout * 2),
null,
@ -736,18 +730,18 @@ namespace Xharness.Tests {
ips.Append (ipAddresses [i].ToString ());
}
var expectedArgs = $"--sdkroot {xcodePath} -v -v -argument=-connection-mode -argument=none " +
var expectedArgs = $"--sdkroot {StringUtils.FormatArguments (xcodePath)} -v -v -argument=-connection-mode -argument=none " +
$"-argument=-app-arg:-autostart -setenv=NUNIT_AUTOSTART=true -argument=-app-arg:-autoexit " +
$"-setenv=NUNIT_AUTOEXIT=true -argument=-app-arg:-enablenetwork -setenv=NUNIT_ENABLE_NETWORK=true " +
$"-setenv=DISABLE_SYSTEM_PERMISSION_TESTS=1 -argument=-app-arg:-hostname:{ips} -setenv=NUNIT_HOSTNAME={ips} " +
$"-argument=-app-arg:-transport:Tcp -setenv=NUNIT_TRANSPORT=TCP -argument=-app-arg:-hostport:{simpleListener.Object.Port} " +
$"-setenv=NUNIT_HOSTPORT={simpleListener.Object.Port} -setenv=env1=value1 -setenv=env2=value2 " +
$"--launchdev {appPath} --disable-memory-limits --wait-for-exit --devname Test iPad";
$"--launchdev {StringUtils.FormatArguments (appPath)} --disable-memory-limits --wait-for-exit --devname \"Test iPad\"";
processManager
.Setup (x => x.ExecuteCommandAsync (
mlaunchPath,
It.Is<IList<string>> (args => string.Join (" ", args) == expectedArgs),
It.Is<MlaunchArguments> (args => args.AsCommandLine () == expectedArgs),
It.IsAny<ILog> (),
TimeSpan.FromMinutes (harness.Timeout * 2),
null,

Просмотреть файл

@ -8,14 +8,15 @@ using NUnit.Framework;
using Xharness.Execution;
using Xharness.Execution.Mlaunch;
using Xharness.Logging;
using Xharness.Utilities;
namespace Xharness.Tests {
[TestFixture]
public class CrashReportSnapshotTests {
public class CrashSnapshotReporterTests {
readonly string mlaunchPath = "./mlaunch";
string tempXcodeRoot;
string symbolicatePath;
Mock<IProcessManager> processManager;
Mock<ILog> log;
Mock<ILogs> logs;
@ -27,7 +28,7 @@ namespace Xharness.Tests {
log = new Mock<ILog> ();
logs = new Mock<ILogs> ();
tempXcodeRoot = Path.Combine (Path.GetTempPath (), Guid.NewGuid ().ToString());
tempXcodeRoot = Path.Combine (Path.GetTempPath (), Guid.NewGuid ().ToString ());
symbolicatePath = Path.Combine (tempXcodeRoot, "Contents", "SharedFrameworks", "DTDeviceKitBase.framework", "Versions", "A", "Resources");
// Create fake place for device logs
@ -39,7 +40,8 @@ namespace Xharness.Tests {
}
[TearDown]
public void TearDown () {
public void TearDown ()
{
Directory.Delete (tempXcodeRoot, true);
}
@ -50,51 +52,20 @@ namespace Xharness.Tests {
const string deviceName = "Sample-iPhone";
const string crashLogPath = "/path/to/crash.log";
const string symbolicateLogPath = "/path/to/" + deviceName+ ".symbolicated.log";
const string symbolicateLogPath = "/path/to/" + deviceName + ".symbolicated.log";
var crashReport = Mock.Of<ILogFile> (x => x.Path == crashLogPath);
var symbolicateReport = Mock.Of<ILogFile> (x => x.Path == symbolicateLogPath);
// Crash report is added
logs.Setup (x => x.Create (deviceName, "Crash report: " + deviceName, It.IsAny<bool> ()))
.Returns (crashReport);
// Symbolicate report is added
logs.Setup (x => x.Create ("crash.symbolicated.log", "Symbolicated crash report: crash.log", It.IsAny<bool> ()))
.Returns (symbolicateReport);
// List of crash reports is retrieved
processManager
.Setup (x => x.ExecuteCommandAsync (
mlaunchPath,
It.Is<MlaunchArguments> (args => args.AsCommandLine () == $"--list-crash-reports={tempFilePath} --sdkroot={tempXcodeRoot} --devname={deviceName}"),
log.Object,
TimeSpan.FromMinutes (1),
null,
null))
.ReturnsAsync (new ProcessExecutionResult () { ExitCode = 0 });
// Device crash log is downloaded
processManager
.Setup (x => x.ExecuteCommandAsync (
mlaunchPath,
It.Is<MlaunchArguments> (args => args.AsCommandLine () == $"--download-crash-report={deviceName} --download-crash-report-to={crashLogPath} --sdkroot={tempXcodeRoot} --devname={deviceName}"),
log.Object,
TimeSpan.FromMinutes (1),
null,
null))
.ReturnsAsync (new ProcessExecutionResult () { ExitCode = 0 });
// Symbolicate is ran
processManager
.Setup (x => x.ExecuteCommandAsync (
Path.Combine (symbolicatePath, "symbolicatecrash"),
It.Is<IList<string>> (args => args.First () == crashLogPath),
symbolicateReport,
TimeSpan.FromMinutes (1),
It.IsAny <Dictionary<string, string>>(),
null))
.ReturnsAsync (new ProcessExecutionResult () { ExitCode = 0 });
processManager.SetReturnsDefault (Task.FromResult (new ProcessExecutionResult () { ExitCode = 0 }));
// Act
var snapshotReport = new CrashSnapshotReporter (processManager.Object,
@ -109,14 +80,53 @@ namespace Xharness.Tests {
File.WriteAllLines (tempFilePath, new [] { "crash 1", "crash 2" });
await snapshotReport.StartCaptureAsync ();
File.WriteAllLines (tempFilePath, new [] { "Sample-iPhone" });
await snapshotReport.EndCaptureAsync (TimeSpan.FromSeconds (10));
// Verify all calls above
processManager.VerifyAll ();
// Verify
logs.VerifyAll ();
// List of crash reports is retrieved
processManager.Verify (
x => x.ExecuteCommandAsync (
mlaunchPath,
It.Is<MlaunchArguments> (args => args.AsCommandLine () ==
StringUtils.FormatArguments ($"--list-crash-reports={tempFilePath}") + " " +
$"--sdkroot {StringUtils.FormatArguments (tempXcodeRoot)} " +
$"--devname {StringUtils.FormatArguments (deviceName)}"),
log.Object,
TimeSpan.FromMinutes (1),
null,
null),
Times.Exactly (2));
// Device crash log is downloaded
processManager.Verify (
x => x.ExecuteCommandAsync (
mlaunchPath,
It.Is<MlaunchArguments> (args => args.AsCommandLine () ==
StringUtils.FormatArguments ($"--download-crash-report={deviceName}") + " " +
StringUtils.FormatArguments ($"--download-crash-report-to={crashLogPath}") + " " +
$"--sdkroot {StringUtils.FormatArguments (tempXcodeRoot)} " +
$"--devname {StringUtils.FormatArguments (deviceName)}"),
log.Object,
TimeSpan.FromMinutes (1),
null,
null),
Times.Once);
// Symbolicate is ran
processManager.Verify (
x => x.ExecuteCommandAsync (
Path.Combine (symbolicatePath, "symbolicatecrash"),
It.Is<IList<string>> (args => args.First () == crashLogPath),
symbolicateReport,
TimeSpan.FromMinutes (1),
It.IsAny<Dictionary<string, string>> (),
null),
Times.Once);
}
}
}

Просмотреть файл

@ -60,7 +60,7 @@
<Compile Include="BCLTestImporter\Tests\TestProjectDefinitionTest.cs" />
<Compile Include="Tests\AppBundleInformationParserTests.cs" />
<Compile Include="Tests\AppRunnerTests.cs" />
<Compile Include="Tests\CrashReportSnapshotTests.cs" />
<Compile Include="Tests\CrashSnapshotReporterTests.cs" />
<Compile Include="Tests\XmlResultParserTests.cs" />
<Compile Include="Logging\Tests\LogsTest.cs" />
<Compile Include="Logging\Tests\LogFileTest.cs" />
@ -71,7 +71,7 @@
<Compile Include="Listeners\Tests\SimpleFileListenerTest.cs" />
<Compile Include="Listeners\Tests\SimpleTcpListenerTest.cs" />
<Compile Include="Execution\Tests\ProcessManagerTests.cs" />
<Compile Include="Execution\Tests\MlaunchArgumentsTest.cs" />
<Compile Include="Execution\Tests\MlaunchArgumentsTests.cs" />
<Compile Include="Hardware\Tests\DeviceTest.cs" />
<Compile Include="Hardware\Tests\DevicesTest.cs" />
<Compile Include="Hardware\Tests\TCCDatabaseTests.cs" />