[Harness] Move utilities out of Harness.cs (#8125)
Co-authored-by: Premek Vysoky <prvysoky@microsoft.com>
This commit is contained in:
Родитель
13a56ffb95
Коммит
5f2905c5a4
|
@ -0,0 +1,111 @@
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using Xharness.Logging;
|
||||||
|
|
||||||
|
namespace Xharness {
|
||||||
|
// Monitor the output from 'mlaunch --installdev' and cancel the installation if there's no output for 1 minute.
|
||||||
|
class AppInstallMonitorLog : Log {
|
||||||
|
public override string FullPath => copy_to.FullPath;
|
||||||
|
|
||||||
|
ILog copy_to;
|
||||||
|
CancellationTokenSource cancellation_source;
|
||||||
|
|
||||||
|
public bool CopyingApp;
|
||||||
|
public bool CopyingWatchApp;
|
||||||
|
public TimeSpan AppCopyDuration;
|
||||||
|
public TimeSpan WatchAppCopyDuration;
|
||||||
|
public Stopwatch AppCopyStart = new Stopwatch ();
|
||||||
|
public Stopwatch WatchAppCopyStart = new Stopwatch ();
|
||||||
|
public int AppPercentComplete;
|
||||||
|
public int WatchAppPercentComplete;
|
||||||
|
public long AppBytes;
|
||||||
|
public long WatchAppBytes;
|
||||||
|
public long AppTotalBytes;
|
||||||
|
public long WatchAppTotalBytes;
|
||||||
|
|
||||||
|
public CancellationToken CancellationToken {
|
||||||
|
get {
|
||||||
|
return cancellation_source.Token;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public AppInstallMonitorLog (ILog copy_to)
|
||||||
|
: base (copy_to.Logs, $"Watch transfer log for {copy_to.Description}")
|
||||||
|
{
|
||||||
|
this.copy_to = copy_to;
|
||||||
|
cancellation_source = new CancellationTokenSource ();
|
||||||
|
cancellation_source.Token.Register (() => {
|
||||||
|
copy_to.WriteLine ("App installation cancelled: it timed out after no output for 1 minute.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Encoding Encoding => copy_to.Encoding;
|
||||||
|
public override void Flush ()
|
||||||
|
{
|
||||||
|
copy_to.Flush ();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override StreamReader GetReader ()
|
||||||
|
{
|
||||||
|
return copy_to.GetReader ();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose (bool disposing)
|
||||||
|
{
|
||||||
|
base.Dispose (disposing);
|
||||||
|
copy_to.Dispose ();
|
||||||
|
cancellation_source.Dispose ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResetTimer ()
|
||||||
|
{
|
||||||
|
cancellation_source.CancelAfter (TimeSpan.FromMinutes (1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void WriteLine (string value)
|
||||||
|
{
|
||||||
|
var v = value.Trim ();
|
||||||
|
if (v.StartsWith ("Installing application bundle", StringComparison.Ordinal)) {
|
||||||
|
if (!CopyingApp) {
|
||||||
|
CopyingApp = true;
|
||||||
|
AppCopyStart.Start ();
|
||||||
|
} else if (!CopyingWatchApp) {
|
||||||
|
CopyingApp = false;
|
||||||
|
CopyingWatchApp = true;
|
||||||
|
AppCopyStart.Stop ();
|
||||||
|
WatchAppCopyStart.Start ();
|
||||||
|
}
|
||||||
|
} else if (v.StartsWith ("PercentComplete: ", StringComparison.Ordinal) && int.TryParse (v.Substring ("PercentComplete: ".Length).Trim (), out var percent)) {
|
||||||
|
if (CopyingApp)
|
||||||
|
AppPercentComplete = percent;
|
||||||
|
else if (CopyingWatchApp)
|
||||||
|
WatchAppPercentComplete = percent;
|
||||||
|
} else if (v.StartsWith ("NumBytes: ", StringComparison.Ordinal) && int.TryParse (v.Substring ("NumBytes: ".Length).Trim (), out var num_bytes)) {
|
||||||
|
if (CopyingApp) {
|
||||||
|
AppBytes = num_bytes;
|
||||||
|
AppCopyDuration = AppCopyStart.Elapsed;
|
||||||
|
} else if (CopyingWatchApp) {
|
||||||
|
WatchAppBytes = num_bytes;
|
||||||
|
WatchAppCopyDuration = WatchAppCopyStart.Elapsed;
|
||||||
|
}
|
||||||
|
} else if (v.StartsWith ("TotalBytes: ", StringComparison.Ordinal) && int.TryParse (v.Substring ("TotalBytes: ".Length).Trim (), out var total_bytes)) {
|
||||||
|
if (CopyingApp)
|
||||||
|
AppTotalBytes = total_bytes;
|
||||||
|
else if (CopyingWatchApp)
|
||||||
|
WatchAppTotalBytes = total_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResetTimer ();
|
||||||
|
|
||||||
|
copy_to.WriteLine (value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write (byte [] buffer, int offset, int count)
|
||||||
|
{
|
||||||
|
copy_to.Write (buffer, offset, count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,10 +14,9 @@ using Xharness.Jenkins.TestTasks;
|
||||||
using Xharness.Listeners;
|
using Xharness.Listeners;
|
||||||
using Xharness.Logging;
|
using Xharness.Logging;
|
||||||
using Xharness.Utilities;
|
using Xharness.Utilities;
|
||||||
using System.Net;
|
|
||||||
|
|
||||||
namespace Xharness
|
namespace Xharness {
|
||||||
{
|
|
||||||
public enum RunMode {
|
public enum RunMode {
|
||||||
Sim32,
|
Sim32,
|
||||||
Sim64,
|
Sim64,
|
||||||
|
@ -136,9 +135,6 @@ namespace Xharness
|
||||||
|
|
||||||
AppInformation Initialize ()
|
AppInformation Initialize ()
|
||||||
{
|
{
|
||||||
if (AppInformation != null)
|
|
||||||
return AppInformation;
|
|
||||||
|
|
||||||
var csproj = new XmlDocument ();
|
var csproj = new XmlDocument ();
|
||||||
csproj.LoadWithoutNetworkAccess (projectFilePath);
|
csproj.LoadWithoutNetworkAccess (projectFilePath);
|
||||||
string appName = csproj.GetAssemblyName ();
|
string appName = csproj.GetAssemblyName ();
|
||||||
|
@ -238,8 +234,6 @@ namespace Xharness
|
||||||
|
|
||||||
public async Task<ProcessExecutionResult> InstallAsync (CancellationToken cancellation_token)
|
public async Task<ProcessExecutionResult> InstallAsync (CancellationToken cancellation_token)
|
||||||
{
|
{
|
||||||
var appInfo = Initialize ();
|
|
||||||
|
|
||||||
if (isSimulator) {
|
if (isSimulator) {
|
||||||
// We reset the simulator when running, so a separate install step does not make much sense.
|
// We reset the simulator when running, so a separate install step does not make much sense.
|
||||||
throw new InvalidOperationException ("Installing to a simulator is not supported.");
|
throw new InvalidOperationException ("Installing to a simulator is not supported.");
|
||||||
|
@ -256,7 +250,7 @@ namespace Xharness
|
||||||
args.Add ("-v");
|
args.Add ("-v");
|
||||||
|
|
||||||
args.Add ("--installdev");
|
args.Add ("--installdev");
|
||||||
args.Add (appInfo.AppPath);
|
args.Add (AppInformation.AppPath);
|
||||||
AddDeviceName (args, companionDeviceName ?? deviceName);
|
AddDeviceName (args, companionDeviceName ?? deviceName);
|
||||||
|
|
||||||
if (mode == RunMode.WatchOS) {
|
if (mode == RunMode.WatchOS) {
|
||||||
|
@ -264,16 +258,14 @@ namespace Xharness
|
||||||
args.Add ("ios,watchos");
|
args.Add ("ios,watchos");
|
||||||
}
|
}
|
||||||
|
|
||||||
var totalSize = Directory.GetFiles (appInfo.AppPath, "*", SearchOption.AllDirectories).Select ((v) => new FileInfo (v).Length).Sum ();
|
var totalSize = Directory.GetFiles (AppInformation.AppPath, "*", SearchOption.AllDirectories).Select ((v) => new FileInfo (v).Length).Sum ();
|
||||||
MainLog.WriteLine ($"Installing '{appInfo.AppPath}' to '{companionDeviceName ?? deviceName}'. Size: {totalSize} bytes = {totalSize / 1024.0 / 1024.0:N2} MB");
|
MainLog.WriteLine ($"Installing '{AppInformation.AppPath}' to '{companionDeviceName ?? deviceName}'. Size: {totalSize} bytes = {totalSize / 1024.0 / 1024.0:N2} MB");
|
||||||
|
|
||||||
return await processManager.ExecuteCommandAsync (harness.MlaunchPath, args, MainLog, TimeSpan.FromHours (1), cancellation_token: cancellation_token);
|
return await processManager.ExecuteCommandAsync (harness.MlaunchPath, args, MainLog, TimeSpan.FromHours (1), cancellation_token: cancellation_token);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ProcessExecutionResult> UninstallAsync ()
|
public async Task<ProcessExecutionResult> UninstallAsync ()
|
||||||
{
|
{
|
||||||
var appInfo = Initialize ();
|
|
||||||
|
|
||||||
if (isSimulator)
|
if (isSimulator)
|
||||||
throw new InvalidOperationException ("Uninstalling from a simulator is not supported.");
|
throw new InvalidOperationException ("Uninstalling from a simulator is not supported.");
|
||||||
|
|
||||||
|
@ -288,7 +280,7 @@ namespace Xharness
|
||||||
args.Add ("-v");
|
args.Add ("-v");
|
||||||
|
|
||||||
args.Add ("--uninstalldevbundleid");
|
args.Add ("--uninstalldevbundleid");
|
||||||
args.Add (appInfo.BundleIdentifier);
|
args.Add (AppInformation.BundleIdentifier);
|
||||||
AddDeviceName (args, companionDeviceName ?? deviceName);
|
AddDeviceName (args, companionDeviceName ?? deviceName);
|
||||||
|
|
||||||
return await processManager.ExecuteCommandAsync (harness.MlaunchPath, args, MainLog, TimeSpan.FromMinutes (1));
|
return await processManager.ExecuteCommandAsync (harness.MlaunchPath, args, MainLog, TimeSpan.FromMinutes (1));
|
||||||
|
@ -366,10 +358,10 @@ namespace Xharness
|
||||||
MainLog.WriteLine (new string ('#', 10));
|
MainLog.WriteLine (new string ('#', 10));
|
||||||
MainLog.WriteLine ("End of xml results.");
|
MainLog.WriteLine ("End of xml results.");
|
||||||
if (timed_out) {
|
if (timed_out) {
|
||||||
harness.LogWrench ($"@MonkeyWrench: AddSummary: <b><i>{mode} timed out</i></b><br/>");
|
WrenchLog.WriteLine ($"AddSummary: <b><i>{mode} timed out</i></b><br/>");
|
||||||
return parseResult;
|
return parseResult;
|
||||||
} else {
|
} else {
|
||||||
harness.LogWrench ($"@MonkeyWrench: AddSummary: <b><i>{mode} crashed</i></b><br/>");
|
WrenchLog.WriteLine ($"AddSummary: <b><i>{mode} crashed</i></b><br/>");
|
||||||
MainLog.WriteLine ("Test run crashed");
|
MainLog.WriteLine ("Test run crashed");
|
||||||
crashed = true;
|
crashed = true;
|
||||||
parseResult.crashed = true;
|
parseResult.crashed = true;
|
||||||
|
@ -407,19 +399,19 @@ namespace Xharness
|
||||||
if (resultLine != null) {
|
if (resultLine != null) {
|
||||||
var tests_run = resultLine.Replace ("Tests run: ", "");
|
var tests_run = resultLine.Replace ("Tests run: ", "");
|
||||||
if (failed) {
|
if (failed) {
|
||||||
harness.LogWrench ("@MonkeyWrench: AddSummary: <b>{0} failed: {1}</b><br/>", mode, tests_run);
|
WrenchLog.WriteLine ("AddSummary: <b>{0} failed: {1}</b><br/>", mode, tests_run);
|
||||||
MainLog.WriteLine ("Test run failed");
|
MainLog.WriteLine ("Test run failed");
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
harness.LogWrench ("@MonkeyWrench: AddSummary: {0} succeeded: {1}<br/>", mode, tests_run);
|
WrenchLog.WriteLine ("AddSummary: {0} succeeded: {1}<br/>", mode, tests_run);
|
||||||
MainLog.WriteLine ("Test run succeeded");
|
MainLog.WriteLine ("Test run succeeded");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else if (timed_out) {
|
} else if (timed_out) {
|
||||||
harness.LogWrench ("@MonkeyWrench: AddSummary: <b><i>{0} timed out</i></b><br/>", mode);
|
WrenchLog.WriteLine ("AddSummary: <b><i>{0} timed out</i></b><br/>", mode);
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
harness.LogWrench ("@MonkeyWrench: AddSummary: <b><i>{0} crashed</i></b><br/>", mode);
|
WrenchLog.WriteLine ("AddSummary: <b><i>{0} crashed</i></b><br/>", mode);
|
||||||
MainLog.WriteLine ("Test run crashed");
|
MainLog.WriteLine ("Test run crashed");
|
||||||
crashed = true;
|
crashed = true;
|
||||||
return false;
|
return false;
|
||||||
|
@ -436,19 +428,10 @@ namespace Xharness
|
||||||
ILog listener_log = null;
|
ILog listener_log = null;
|
||||||
ILog run_log = MainLog;
|
ILog run_log = MainLog;
|
||||||
|
|
||||||
var appInfo = Initialize ();
|
|
||||||
|
|
||||||
if (!isSimulator)
|
if (!isSimulator)
|
||||||
FindDevice ();
|
FindDevice ();
|
||||||
|
|
||||||
crash_reports = new CrashReportSnapshot () {
|
crash_reports = new CrashReportSnapshot (harness, MainLog, Logs, isDevice: !isSimulator, deviceName);
|
||||||
Device = !isSimulator,
|
|
||||||
DeviceName = deviceName,
|
|
||||||
Harness = harness,
|
|
||||||
Log = MainLog,
|
|
||||||
Logs = Logs,
|
|
||||||
LogDirectory = logDirectory,
|
|
||||||
};
|
|
||||||
|
|
||||||
var args = new List<string> ();
|
var args = new List<string> ();
|
||||||
if (!string.IsNullOrEmpty (harness.XcodeRoot)) {
|
if (!string.IsNullOrEmpty (harness.XcodeRoot)) {
|
||||||
|
@ -539,12 +522,12 @@ namespace Xharness
|
||||||
bool launch_failure = false;
|
bool launch_failure = false;
|
||||||
|
|
||||||
if (IsExtension) {
|
if (IsExtension) {
|
||||||
switch (appInfo.Extension) {
|
switch (AppInformation.Extension) {
|
||||||
case Extension.TodayExtension:
|
case Extension.TodayExtension:
|
||||||
args.Add (isSimulator ? "--launchsimbundleid" : "--launchdevbundleid");
|
args.Add (isSimulator ? "--launchsimbundleid" : "--launchdevbundleid");
|
||||||
args.Add ("todayviewforextensions:" + appInfo.BundleIdentifier);
|
args.Add ("todayviewforextensions:" + AppInformation.BundleIdentifier);
|
||||||
args.Add ("--observe-extension");
|
args.Add ("--observe-extension");
|
||||||
args.Add (appInfo.LaunchAppPath);
|
args.Add (AppInformation.LaunchAppPath);
|
||||||
break;
|
break;
|
||||||
case Extension.WatchKit2:
|
case Extension.WatchKit2:
|
||||||
default:
|
default:
|
||||||
|
@ -552,7 +535,7 @@ namespace Xharness
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
args.Add (isSimulator ? "--launchsim" : "--launchdev");
|
args.Add (isSimulator ? "--launchsim" : "--launchdev");
|
||||||
args.Add (appInfo.LaunchAppPath);
|
args.Add (AppInformation.LaunchAppPath);
|
||||||
}
|
}
|
||||||
if (!isSimulator)
|
if (!isSimulator)
|
||||||
args.Add ("--disable-memory-limits");
|
args.Add ("--disable-memory-limits");
|
||||||
|
@ -588,14 +571,14 @@ namespace Xharness
|
||||||
log.StartCapture ();
|
log.StartCapture ();
|
||||||
Logs.Add (log);
|
Logs.Add (log);
|
||||||
systemLogs.Add (log);
|
systemLogs.Add (log);
|
||||||
harness.LogWrench ("@MonkeyWrench: AddFile: {0}", log.Path);
|
WrenchLog.WriteLine ("AddFile: {0}", log.Path);
|
||||||
}
|
}
|
||||||
|
|
||||||
MainLog.WriteLine ("*** Executing {0}/{1} in the simulator ***", appInfo.AppName, mode);
|
MainLog.WriteLine ("*** Executing {0}/{1} in the simulator ***", AppInformation.AppName, mode);
|
||||||
|
|
||||||
if (EnsureCleanSimulatorState) {
|
if (EnsureCleanSimulatorState) {
|
||||||
foreach (var sim in simulators)
|
foreach (var sim in simulators)
|
||||||
await sim.PrepareSimulatorAsync (MainLog, appInfo.BundleIdentifier);
|
await sim.PrepareSimulatorAsync (MainLog, AppInformation.BundleIdentifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
args.Add ($"--device=:v2:udid={simulator.UDID}");
|
args.Add ($"--device=:v2:udid={simulator.UDID}");
|
||||||
|
@ -656,7 +639,7 @@ namespace Xharness
|
||||||
log.StopCapture ();
|
log.StopCapture ();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
MainLog.WriteLine ("*** Executing {0}/{1} on device '{2}' ***", appInfo.AppName, mode, deviceName);
|
MainLog.WriteLine ("*** Executing {0}/{1} on device '{2}' ***", AppInformation.AppName, mode, deviceName);
|
||||||
|
|
||||||
if (mode == RunMode.WatchOS) {
|
if (mode == RunMode.WatchOS) {
|
||||||
args.Add ("--attach-native-debugger"); // this prevents the watch from backgrounding the app.
|
args.Add ("--attach-native-debugger"); // this prevents the watch from backgrounding the app.
|
||||||
|
@ -718,7 +701,7 @@ namespace Xharness
|
||||||
// Upload the system log
|
// Upload the system log
|
||||||
if (File.Exists (device_system_log.FullPath)) {
|
if (File.Exists (device_system_log.FullPath)) {
|
||||||
MainLog.WriteLine ("A capture of the device log is: {0}", device_system_log.FullPath);
|
MainLog.WriteLine ("A capture of the device log is: {0}", device_system_log.FullPath);
|
||||||
harness.LogWrench ("@MonkeyWrench: AddFile: {0}", device_system_log.FullPath);
|
WrenchLog.WriteLine ("AddFile: {0}", device_system_log.FullPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -728,18 +711,18 @@ namespace Xharness
|
||||||
// check the final status
|
// check the final status
|
||||||
var crashed = false;
|
var crashed = false;
|
||||||
if (File.Exists (listener_log.FullPath)) {
|
if (File.Exists (listener_log.FullPath)) {
|
||||||
harness.LogWrench ("@MonkeyWrench: AddFile: {0}", listener_log.FullPath);
|
WrenchLog.WriteLine ("AddFile: {0}", listener_log.FullPath);
|
||||||
success = TestsSucceeded (AppInformation, listener_log.FullPath, timed_out, out crashed);
|
success = TestsSucceeded (this.AppInformation, listener_log.FullPath, timed_out, out crashed);
|
||||||
} else if (timed_out) {
|
} else if (timed_out) {
|
||||||
harness.LogWrench ("@MonkeyWrench: AddSummary: <b><i>{0} never launched</i></b><br/>", mode);
|
WrenchLog.WriteLine ("AddSummary: <b><i>{0} never launched</i></b><br/>", mode);
|
||||||
MainLog.WriteLine ("Test run never launched");
|
MainLog.WriteLine ("Test run never launched");
|
||||||
success = false;
|
success = false;
|
||||||
} else if (launch_failure) {
|
} else if (launch_failure) {
|
||||||
harness.LogWrench ("@MonkeyWrench: AddSummary: <b><i>{0} failed to launch</i></b><br/>", mode);
|
WrenchLog.WriteLine ("AddSummary: <b><i>{0} failed to launch</i></b><br/>", mode);
|
||||||
MainLog.WriteLine ("Test run failed to launch");
|
MainLog.WriteLine ("Test run failed to launch");
|
||||||
success = false;
|
success = false;
|
||||||
} else {
|
} else {
|
||||||
harness.LogWrench ("@MonkeyWrench: AddSummary: <b><i>{0} crashed at startup (no log)</i></b><br/>", mode);
|
WrenchLog.WriteLine ("AddSummary: <b><i>{0} crashed at startup (no log)</i></b><br/>", mode);
|
||||||
MainLog.WriteLine ("Test run crashed before it started (no log file produced)");
|
MainLog.WriteLine ("Test run crashed before it started (no log file produced)");
|
||||||
crashed = true;
|
crashed = true;
|
||||||
success = false;
|
success = false;
|
||||||
|
@ -802,9 +785,9 @@ namespace Xharness
|
||||||
if (harness.InCI) {
|
if (harness.InCI) {
|
||||||
XmlResultParser.GenerateFailure (Logs,
|
XmlResultParser.GenerateFailure (Logs,
|
||||||
"crash",
|
"crash",
|
||||||
appInfo.AppName,
|
AppInformation.AppName,
|
||||||
variation,
|
variation,
|
||||||
$"App Crash {appInfo.AppName} {variation}",
|
$"App Crash {AppInformation.AppName} {variation}",
|
||||||
$"App crashed {crash_reason}.",
|
$"App crashed {crash_reason}.",
|
||||||
crash_reports.Log.FullPath,
|
crash_reports.Log.FullPath,
|
||||||
harness.XmlJargon);
|
harness.XmlJargon);
|
||||||
|
@ -826,9 +809,9 @@ namespace Xharness
|
||||||
XmlResultParser.GenerateFailure (
|
XmlResultParser.GenerateFailure (
|
||||||
Logs,
|
Logs,
|
||||||
"crash",
|
"crash",
|
||||||
appInfo.AppName,
|
AppInformation.AppName,
|
||||||
variation,
|
variation,
|
||||||
$"App Crash {appInfo.AppName} {variation}",
|
$"App Crash {AppInformation.AppName} {variation}",
|
||||||
$"App crashed: {FailureMessage}",
|
$"App crashed: {FailureMessage}",
|
||||||
crash_reports.Log.FullPath,
|
crash_reports.Log.FullPath,
|
||||||
harness.XmlJargon);
|
harness.XmlJargon);
|
||||||
|
@ -840,9 +823,9 @@ namespace Xharness
|
||||||
XmlResultParser.GenerateFailure (
|
XmlResultParser.GenerateFailure (
|
||||||
Logs,
|
Logs,
|
||||||
"launch",
|
"launch",
|
||||||
appInfo.AppName,
|
AppInformation.AppName,
|
||||||
variation,
|
variation,
|
||||||
$"App Launch {appInfo.AppName} {variation} on {deviceName}",
|
$"App Launch {AppInformation.AppName} {variation} on {deviceName}",
|
||||||
$"{FailureMessage} on {deviceName}",
|
$"{FailureMessage} on {deviceName}",
|
||||||
MainLog.FullPath,
|
MainLog.FullPath,
|
||||||
XmlResultJargon.NUnitV3);
|
XmlResultJargon.NUnitV3);
|
||||||
|
@ -865,7 +848,7 @@ namespace Xharness
|
||||||
if (isTcp) {
|
if (isTcp) {
|
||||||
XmlResultParser.GenerateFailure (Logs,
|
XmlResultParser.GenerateFailure (Logs,
|
||||||
"tcp-connection",
|
"tcp-connection",
|
||||||
appInfo.AppName,
|
AppInformation.AppName,
|
||||||
variation,
|
variation,
|
||||||
$"TcpConnection on {deviceName}",
|
$"TcpConnection on {deviceName}",
|
||||||
$"Device {deviceName} could not reach the host over tcp.",
|
$"Device {deviceName} could not reach the host over tcp.",
|
||||||
|
@ -875,10 +858,10 @@ namespace Xharness
|
||||||
} else if (timed_out && harness.InCI) {
|
} else if (timed_out && harness.InCI) {
|
||||||
XmlResultParser.GenerateFailure (Logs,
|
XmlResultParser.GenerateFailure (Logs,
|
||||||
"timeout",
|
"timeout",
|
||||||
appInfo.AppName,
|
AppInformation.AppName,
|
||||||
variation,
|
variation,
|
||||||
$"App Timeout {appInfo.AppName} {variation} on bot {deviceName}",
|
$"App Timeout {AppInformation.AppName} {variation} on bot {deviceName}",
|
||||||
$"{appInfo.AppName} {variation} Test run timed out after {timeout.TotalMinutes} minute(s) on bot {deviceName}.",
|
$"{AppInformation.AppName} {variation} Test run timed out after {timeout.TotalMinutes} minute(s) on bot {deviceName}.",
|
||||||
MainLog.FullPath,
|
MainLog.FullPath,
|
||||||
harness.XmlJargon);
|
harness.XmlJargon);
|
||||||
}
|
}
|
||||||
|
@ -900,107 +883,4 @@ namespace Xharness
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Monitor the output from 'mlaunch --installdev' and cancel the installation if there's no output for 1 minute.
|
|
||||||
class AppInstallMonitorLog : Log {
|
|
||||||
public override string FullPath => copy_to.FullPath;
|
|
||||||
|
|
||||||
ILog copy_to;
|
|
||||||
CancellationTokenSource cancellation_source;
|
|
||||||
|
|
||||||
public bool CopyingApp;
|
|
||||||
public bool CopyingWatchApp;
|
|
||||||
public TimeSpan AppCopyDuration;
|
|
||||||
public TimeSpan WatchAppCopyDuration;
|
|
||||||
public Stopwatch AppCopyStart = new Stopwatch ();
|
|
||||||
public Stopwatch WatchAppCopyStart = new Stopwatch ();
|
|
||||||
public int AppPercentComplete;
|
|
||||||
public int WatchAppPercentComplete;
|
|
||||||
public long AppBytes;
|
|
||||||
public long WatchAppBytes;
|
|
||||||
public long AppTotalBytes;
|
|
||||||
public long WatchAppTotalBytes;
|
|
||||||
|
|
||||||
public CancellationToken CancellationToken {
|
|
||||||
get {
|
|
||||||
return cancellation_source.Token;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public AppInstallMonitorLog (ILog copy_to)
|
|
||||||
: base (copy_to.Logs, $"Watch transfer log for {copy_to.Description}")
|
|
||||||
{
|
|
||||||
this.copy_to = copy_to;
|
|
||||||
cancellation_source = new CancellationTokenSource ();
|
|
||||||
cancellation_source.Token.Register (() => {
|
|
||||||
copy_to.WriteLine ("App installation cancelled: it timed out after no output for 1 minute.");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Encoding Encoding => copy_to.Encoding;
|
|
||||||
public override void Flush ()
|
|
||||||
{
|
|
||||||
copy_to.Flush ();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override StreamReader GetReader ()
|
|
||||||
{
|
|
||||||
return copy_to.GetReader ();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Dispose (bool disposing)
|
|
||||||
{
|
|
||||||
base.Dispose (disposing);
|
|
||||||
copy_to.Dispose ();
|
|
||||||
cancellation_source.Dispose ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ResetTimer ()
|
|
||||||
{
|
|
||||||
cancellation_source.CancelAfter (TimeSpan.FromMinutes (1));
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void WriteLine (string value)
|
|
||||||
{
|
|
||||||
var v = value.Trim ();
|
|
||||||
if (v.StartsWith ("Installing application bundle", StringComparison.Ordinal)) {
|
|
||||||
if (!CopyingApp) {
|
|
||||||
CopyingApp = true;
|
|
||||||
AppCopyStart.Start ();
|
|
||||||
} else if (!CopyingWatchApp) {
|
|
||||||
CopyingApp = false;
|
|
||||||
CopyingWatchApp = true;
|
|
||||||
AppCopyStart.Stop ();
|
|
||||||
WatchAppCopyStart.Start ();
|
|
||||||
}
|
|
||||||
} else if (v.StartsWith ("PercentComplete: ", StringComparison.Ordinal) && int.TryParse (v.Substring ("PercentComplete: ".Length).Trim (), out var percent)) {
|
|
||||||
if (CopyingApp)
|
|
||||||
AppPercentComplete = percent;
|
|
||||||
else if (CopyingWatchApp)
|
|
||||||
WatchAppPercentComplete = percent;
|
|
||||||
} else if (v.StartsWith ("NumBytes: ", StringComparison.Ordinal) && int.TryParse (v.Substring ("NumBytes: ".Length).Trim (), out var num_bytes)) {
|
|
||||||
if (CopyingApp) {
|
|
||||||
AppBytes = num_bytes;
|
|
||||||
AppCopyDuration = AppCopyStart.Elapsed;
|
|
||||||
} else if (CopyingWatchApp) {
|
|
||||||
WatchAppBytes = num_bytes;
|
|
||||||
WatchAppCopyDuration = WatchAppCopyStart.Elapsed;
|
|
||||||
}
|
|
||||||
} else if (v.StartsWith ("TotalBytes: ", StringComparison.Ordinal) && int.TryParse (v.Substring ("TotalBytes: ".Length).Trim (), out var total_bytes)) {
|
|
||||||
if (CopyingApp)
|
|
||||||
AppTotalBytes = total_bytes;
|
|
||||||
else if (CopyingWatchApp)
|
|
||||||
WatchAppTotalBytes = total_bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
ResetTimer ();
|
|
||||||
|
|
||||||
copy_to.WriteLine (value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Write (byte [] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
copy_to.Write (buffer, offset, count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Xharness.Utilities;
|
||||||
|
|
||||||
namespace Xharness.BCLTestImporter {
|
namespace Xharness.BCLTestImporter {
|
||||||
// Class that is use as the connection between xharness and the BCLImporter
|
// Class that is use as the connection between xharness and the BCLImporter
|
||||||
|
@ -20,7 +21,7 @@ namespace Xharness.BCLTestImporter {
|
||||||
projectGenerator = new BCLTestProjectGenerator (outputDir, Harness.MONO_PATH) {
|
projectGenerator = new BCLTestProjectGenerator (outputDir, Harness.MONO_PATH) {
|
||||||
iOSMonoSDKPath = Harness.MONO_IOS_SDK_DESTDIR,
|
iOSMonoSDKPath = Harness.MONO_IOS_SDK_DESTDIR,
|
||||||
MacMonoSDKPath = Harness.MONO_MAC_SDK_DESTDIR,
|
MacMonoSDKPath = Harness.MONO_MAC_SDK_DESTDIR,
|
||||||
GuidGenerator = Harness.NewStableGuid,
|
GuidGenerator = Helpers.GenerateStableGuid,
|
||||||
GroupTests = Harness.InCI || Harness.UseGroupedApps,
|
GroupTests = Harness.InCI || Harness.UseGroupedApps,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,19 +10,27 @@ namespace Xharness
|
||||||
{
|
{
|
||||||
public class CrashReportSnapshot
|
public class CrashReportSnapshot
|
||||||
{
|
{
|
||||||
public IHarness Harness { get; set; }
|
readonly IHarness harness;
|
||||||
public ILog Log { get; set; }
|
readonly bool isDevice;
|
||||||
public ILogs Logs { get; set; }
|
readonly string deviceName;
|
||||||
public string LogDirectory { get; set; }
|
|
||||||
public bool Device { get; set; }
|
public ILog Log { get; }
|
||||||
public string DeviceName { get; set; }
|
public ILogs Logs { get; }
|
||||||
|
|
||||||
public HashSet<string> InitialSet { get; private set; }
|
public HashSet<string> InitialSet { get; private set; }
|
||||||
public IEnumerable<string> Reports { get; private set; }
|
|
||||||
|
public CrashReportSnapshot (IHarness harness, ILog log, ILogs logs, bool isDevice, string deviceName)
|
||||||
|
{
|
||||||
|
this.harness = harness ?? throw new ArgumentNullException (nameof (harness));
|
||||||
|
this.Log = log ?? throw new ArgumentNullException (nameof (log));
|
||||||
|
this.Logs = logs ?? throw new ArgumentNullException (nameof (logs));
|
||||||
|
this.isDevice = isDevice;
|
||||||
|
this.deviceName = deviceName;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task StartCaptureAsync ()
|
public async Task StartCaptureAsync ()
|
||||||
{
|
{
|
||||||
InitialSet = await Harness.CreateCrashReportsSnapshotAsync (Log, !Device, DeviceName);
|
InitialSet = await CreateCrashReportsSnapshotAsync ();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task EndCaptureAsync (TimeSpan timeout)
|
public async Task EndCaptureAsync (TimeSpan timeout)
|
||||||
|
@ -33,13 +41,12 @@ namespace Xharness
|
||||||
var watch = new Stopwatch ();
|
var watch = new Stopwatch ();
|
||||||
watch.Start ();
|
watch.Start ();
|
||||||
do {
|
do {
|
||||||
var end_crashes = await Harness.CreateCrashReportsSnapshotAsync (Log, !Device, DeviceName);
|
var end_crashes = await CreateCrashReportsSnapshotAsync ();
|
||||||
end_crashes.ExceptWith (InitialSet);
|
end_crashes.ExceptWith (InitialSet);
|
||||||
Reports = end_crashes;
|
|
||||||
if (end_crashes.Count > 0) {
|
if (end_crashes.Count > 0) {
|
||||||
Log.WriteLine ("Found {0} new crash report(s)", end_crashes.Count);
|
Log.WriteLine ("Found {0} new crash report(s)", end_crashes.Count);
|
||||||
List<ILogFile> crash_reports;
|
List<ILogFile> crash_reports;
|
||||||
if (!Device) {
|
if (!isDevice) {
|
||||||
crash_reports = new List<ILogFile> (end_crashes.Count);
|
crash_reports = new List<ILogFile> (end_crashes.Count);
|
||||||
foreach (var path in end_crashes) {
|
foreach (var path in end_crashes) {
|
||||||
Logs.AddFile (path, $"Crash report: {Path.GetFileName (path)}");
|
Logs.AddFile (path, $"Crash report: {Path.GetFileName (path)}");
|
||||||
|
@ -55,15 +62,15 @@ namespace Xharness
|
||||||
sb.Add ($"--download-crash-report={file}");
|
sb.Add ($"--download-crash-report={file}");
|
||||||
sb.Add ($"--download-crash-report-to={crash_report_target.Path}");
|
sb.Add ($"--download-crash-report-to={crash_report_target.Path}");
|
||||||
sb.Add ("--sdkroot");
|
sb.Add ("--sdkroot");
|
||||||
sb.Add (Harness.XcodeRoot);
|
sb.Add (harness.XcodeRoot);
|
||||||
if (!string.IsNullOrEmpty (DeviceName)) {
|
if (!string.IsNullOrEmpty (deviceName)) {
|
||||||
sb.Add ("--devname");
|
sb.Add ("--devname");
|
||||||
sb.Add (DeviceName);
|
sb.Add (deviceName);
|
||||||
}
|
}
|
||||||
var result = await Harness.ProcessManager.ExecuteCommandAsync (Harness.MlaunchPath, sb, Log, TimeSpan.FromMinutes (1));
|
var result = await harness.ProcessManager.ExecuteCommandAsync (harness.MlaunchPath, sb, Log, TimeSpan.FromMinutes (1));
|
||||||
if (result.Succeeded) {
|
if (result.Succeeded) {
|
||||||
Log.WriteLine ("Downloaded crash report {0} to {1}", file, crash_report_target.Path);
|
Log.WriteLine ("Downloaded crash report {0} to {1}", file, crash_report_target.Path);
|
||||||
crash_report_target = await Harness.SymbolicateCrashReportAsync (Logs, Log, crash_report_target);
|
crash_report_target = await SymbolicateCrashReportAsync (crash_report_target);
|
||||||
downloaded_crash_reports.Add (crash_report_target);
|
downloaded_crash_reports.Add (crash_report_target);
|
||||||
} else {
|
} else {
|
||||||
Log.WriteLine ("Could not download crash report {0}", file);
|
Log.WriteLine ("Could not download crash report {0}", file);
|
||||||
|
@ -72,7 +79,7 @@ namespace Xharness
|
||||||
crash_reports = downloaded_crash_reports;
|
crash_reports = downloaded_crash_reports;
|
||||||
}
|
}
|
||||||
foreach (var cp in crash_reports) {
|
foreach (var cp in crash_reports) {
|
||||||
Harness.LogWrench ("@MonkeyWrench: AddFile: {0}", cp.Path);
|
WrenchLog.WriteLine ("AddFile: {0}", cp.Path);
|
||||||
Log.WriteLine (" {0}", cp.Path);
|
Log.WriteLine (" {0}", cp.Path);
|
||||||
}
|
}
|
||||||
crash_report_search_done = true;
|
crash_report_search_done = true;
|
||||||
|
@ -86,5 +93,59 @@ namespace Xharness
|
||||||
}
|
}
|
||||||
} while (!crash_report_search_done);
|
} while (!crash_report_search_done);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async Task<ILogFile> SymbolicateCrashReportAsync (ILogFile report)
|
||||||
|
{
|
||||||
|
var symbolicatecrash = Path.Combine (harness.XcodeRoot, "Contents/SharedFrameworks/DTDeviceKitBase.framework/Versions/A/Resources/symbolicatecrash");
|
||||||
|
if (!File.Exists (symbolicatecrash))
|
||||||
|
symbolicatecrash = Path.Combine (harness.XcodeRoot, "Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash");
|
||||||
|
|
||||||
|
if (!File.Exists (symbolicatecrash)) {
|
||||||
|
Log.WriteLine ("Can't symbolicate {0} because the symbolicatecrash script {1} does not exist", report.Path, symbolicatecrash);
|
||||||
|
return report;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (harness.XcodeRoot, "Contents", "Developer") } };
|
||||||
|
var rv = await harness.ProcessManager.ExecuteCommandAsync (symbolicatecrash, new [] { report.Path }, symbolicated, TimeSpan.FromMinutes (1), environment);
|
||||||
|
if (rv.Succeeded) {;
|
||||||
|
Log.WriteLine ("Symbolicated {0} successfully.", report.Path);
|
||||||
|
return symbolicated;
|
||||||
|
} else {
|
||||||
|
Log.WriteLine ("Failed to symbolicate {0}.", report.Path);
|
||||||
|
return report;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task<HashSet<string>> CreateCrashReportsSnapshotAsync ()
|
||||||
|
{
|
||||||
|
var rv = new HashSet<string> ();
|
||||||
|
|
||||||
|
if (!isDevice) {
|
||||||
|
var dir = Path.Combine (Environment.GetEnvironmentVariable ("HOME"), "Library", "Logs", "DiagnosticReports");
|
||||||
|
if (Directory.Exists (dir))
|
||||||
|
rv.UnionWith (Directory.EnumerateFiles (dir));
|
||||||
|
} else {
|
||||||
|
var tmp = Path.GetTempFileName ();
|
||||||
|
try {
|
||||||
|
var sb = new List<string> ();
|
||||||
|
sb.Add ($"--list-crash-reports={tmp}");
|
||||||
|
sb.Add ("--sdkroot");
|
||||||
|
sb.Add (harness.XcodeRoot);
|
||||||
|
if (!string.IsNullOrEmpty (deviceName)) {
|
||||||
|
sb.Add ("--devname");
|
||||||
|
sb.Add (deviceName);
|
||||||
|
}
|
||||||
|
var result = await harness.ProcessManager.ExecuteCommandAsync (harness.MlaunchPath, sb, Log, TimeSpan.FromMinutes (1));
|
||||||
|
if (result.Succeeded)
|
||||||
|
rv.UnionWith (File.ReadAllLines (tmp));
|
||||||
|
} finally {
|
||||||
|
File.Delete (tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Security.Cryptography;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
using Xharness.BCLTestImporter;
|
using Xharness.BCLTestImporter;
|
||||||
|
@ -29,7 +26,7 @@ namespace Xharness
|
||||||
|
|
||||||
public class HarnessConfiguration {
|
public class HarnessConfiguration {
|
||||||
public bool AutoConf { get; set; }
|
public bool AutoConf { get; set; }
|
||||||
public string Configuration { get; set; } = "Debug";
|
public string BuildConfiguration { get; set; } = "Debug";
|
||||||
public bool DryRun { get; set; }
|
public bool DryRun { get; set; }
|
||||||
public Dictionary<string, string> EnvironmentVariables { get; set; } = new Dictionary<string, string> ();
|
public Dictionary<string, string> EnvironmentVariables { get; set; } = new Dictionary<string, string> ();
|
||||||
public bool? IncludeSystemPermissionTests { get; set; }
|
public bool? IncludeSystemPermissionTests { get; set; }
|
||||||
|
@ -42,7 +39,6 @@ namespace Xharness
|
||||||
public string PeriodicCommand { get; set; }
|
public string PeriodicCommand { get; set; }
|
||||||
public string PeriodicCommandArguments { get; set; }
|
public string PeriodicCommandArguments { get; set; }
|
||||||
public TimeSpan PeriodicCommandInterval { get; set; }
|
public TimeSpan PeriodicCommandInterval { get; set; }
|
||||||
public string RootDirectory { get; set; }
|
|
||||||
public string SdkRoot { get; set; }
|
public string SdkRoot { get; set; }
|
||||||
public AppRunnerTarget Target { get; set; }
|
public AppRunnerTarget Target { get; set; }
|
||||||
public double TimeoutInMinutes { get; set; } = 15;
|
public double TimeoutInMinutes { get; set; } = 15;
|
||||||
|
@ -55,8 +51,6 @@ namespace Xharness
|
||||||
|
|
||||||
public interface IHarness {
|
public interface IHarness {
|
||||||
HarnessAction Action { get; }
|
HarnessAction Action { get; }
|
||||||
string BCLTodayExtensionTemplate { get; }
|
|
||||||
string Configuration { get; }
|
|
||||||
bool DisableWatchOSOnWrench { get; }
|
bool DisableWatchOSOnWrench { get; }
|
||||||
string DOTNET { get; }
|
string DOTNET { get; }
|
||||||
bool DryRun { get; }
|
bool DryRun { get; }
|
||||||
|
@ -71,7 +65,6 @@ namespace Xharness
|
||||||
bool? IncludeSystemPermissionTests { get; set; }
|
bool? IncludeSystemPermissionTests { get; set; }
|
||||||
string IOS_DESTDIR { get; }
|
string IOS_DESTDIR { get; }
|
||||||
List<iOSTestProject> IOSTestProjects { get; }
|
List<iOSTestProject> IOSTestProjects { get; }
|
||||||
bool IsBetaXcode { get; }
|
|
||||||
string JENKINS_RESULTS_DIRECTORY { get; }
|
string JENKINS_RESULTS_DIRECTORY { get; }
|
||||||
string JenkinsConfiguration { get; }
|
string JenkinsConfiguration { get; }
|
||||||
HashSet<string> Labels { get; }
|
HashSet<string> Labels { get; }
|
||||||
|
@ -88,12 +81,9 @@ namespace Xharness
|
||||||
string PeriodicCommandArguments { get; }
|
string PeriodicCommandArguments { get; }
|
||||||
TimeSpan PeriodicCommandInterval { get; }
|
TimeSpan PeriodicCommandInterval { get; }
|
||||||
IProcessManager ProcessManager { get; }
|
IProcessManager ProcessManager { get; }
|
||||||
string SdkRoot { get; }
|
|
||||||
AppRunnerTarget Target { get; }
|
|
||||||
double Timeout { get; }
|
double Timeout { get; }
|
||||||
string TodayContainerTemplate { get; }
|
string TodayContainerTemplate { get; }
|
||||||
string TodayExtensionTemplate { get; }
|
string TodayExtensionTemplate { get; }
|
||||||
string TVOS_MONO_PATH { get; }
|
|
||||||
bool UseGroupedApps { get; }
|
bool UseGroupedApps { get; }
|
||||||
int Verbosity { get; }
|
int Verbosity { get; }
|
||||||
string WatchOSAppTemplate { get; }
|
string WatchOSAppTemplate { get; }
|
||||||
|
@ -103,28 +93,19 @@ namespace Xharness
|
||||||
Version XcodeVersion { get; }
|
Version XcodeVersion { get; }
|
||||||
string XIBuildPath { get; }
|
string XIBuildPath { get; }
|
||||||
XmlResultJargon XmlJargon { get; }
|
XmlResultJargon XmlJargon { get; }
|
||||||
|
|
||||||
int Configure ();
|
|
||||||
Task<HashSet<string>> CreateCrashReportsSnapshotAsync (ILog log, bool simulatorOrDesktop, string device);
|
|
||||||
int Execute ();
|
|
||||||
Task<ProcessExecutionResult> ExecuteXcodeCommandAsync (string executable, IList<string> args, ILog log, TimeSpan timeout);
|
Task<ProcessExecutionResult> ExecuteXcodeCommandAsync (string executable, IList<string> args, ILog log, TimeSpan timeout);
|
||||||
bool GetIncludeSystemPermissionTests (TestPlatform platform, bool device);
|
bool GetIncludeSystemPermissionTests (TestPlatform platform, bool device);
|
||||||
int Install ();
|
|
||||||
int Jenkins ();
|
|
||||||
void Log (int min_level, string message);
|
|
||||||
void Log (int min_level, string message, params object [] args);
|
void Log (int min_level, string message, params object [] args);
|
||||||
void Log (string message);
|
void Log (string message);
|
||||||
void Log (string message, params object [] args);
|
void Log (string message, params object [] args);
|
||||||
void LogWrench (string message);
|
|
||||||
void LogWrench (string message, params object [] args);
|
|
||||||
Guid NewStableGuid (string seed = null);
|
|
||||||
int Run ();
|
|
||||||
void Save (StringWriter doc, string path);
|
void Save (StringWriter doc, string path);
|
||||||
Task<ILogFile> SymbolicateCrashReportAsync (ILogs logs, ILog log, ILogFile report);
|
|
||||||
int Uninstall ();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Harness : IHarness {
|
public class Harness : IHarness {
|
||||||
|
readonly AppRunnerTarget target;
|
||||||
|
readonly string buildConfiguration = "Debug";
|
||||||
|
string sdkRoot;
|
||||||
|
|
||||||
public HarnessAction Action { get; }
|
public HarnessAction Action { get; }
|
||||||
public int Verbosity { get; }
|
public int Verbosity { get; }
|
||||||
public ILog HarnessLog { get; set; }
|
public ILog HarnessLog { get; set; }
|
||||||
|
@ -132,15 +113,9 @@ namespace Xharness
|
||||||
public XmlResultJargon XmlJargon { get; }
|
public XmlResultJargon XmlJargon { get; }
|
||||||
public IProcessManager ProcessManager { get; }
|
public IProcessManager ProcessManager { get; }
|
||||||
|
|
||||||
public string XIBuildPath {
|
public string XIBuildPath => Path.GetFullPath (Path.Combine (RootDirectory, "..", "tools", "xibuild", "xibuild"));
|
||||||
get { return Path.GetFullPath (Path.Combine (RootDirectory, "..", "tools", "xibuild", "xibuild")); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string Timestamp {
|
public static string Timestamp => $"{DateTime.Now:yyyyMMdd_HHmmss}";
|
||||||
get {
|
|
||||||
return $"{DateTime.Now:yyyyMMdd_HHmmss}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is the maccore/tests directory.
|
// This is the maccore/tests directory.
|
||||||
static string root_directory;
|
static string root_directory;
|
||||||
|
@ -199,19 +174,16 @@ namespace Xharness
|
||||||
public string DOTNET { get; private set; }
|
public string DOTNET { get; private set; }
|
||||||
|
|
||||||
// Run
|
// Run
|
||||||
public AppRunnerTarget Target { get; }
|
public string LogDirectory { get; } = Environment.CurrentDirectory;
|
||||||
public string SdkRoot { get; private set; }
|
|
||||||
public string Configuration { get; }
|
|
||||||
public string LogDirectory { get; }
|
|
||||||
public double Timeout { get; } = 15; // in minutes
|
public double Timeout { get; } = 15; // in minutes
|
||||||
public double LaunchTimeout { get; } // in minutes
|
public double LaunchTimeout { get; } // in minutes
|
||||||
public bool DryRun { get; } // Most things don't support this. If you need it somewhere, implement it!
|
public bool DryRun { get; } // Most things don't support this. If you need it somewhere, implement it!
|
||||||
public string JenkinsConfiguration { get; }
|
public string JenkinsConfiguration { get; }
|
||||||
public Dictionary<string, string> EnvironmentVariables { get; }
|
public Dictionary<string, string> EnvironmentVariables { get; } = new Dictionary<string, string> ();
|
||||||
public string MarkdownSummaryPath { get; }
|
public string MarkdownSummaryPath { get; }
|
||||||
public string PeriodicCommand { get; }
|
public string PeriodicCommand { get; }
|
||||||
public string PeriodicCommandArguments { get; }
|
public string PeriodicCommandArguments { get; }
|
||||||
public TimeSpan PeriodicCommandInterval { get; }
|
public TimeSpan PeriodicCommandInterval { get;}
|
||||||
// whether tests that require access to system resources (system contacts, photo library, etc) should be executed or not
|
// whether tests that require access to system resources (system contacts, photo library, etc) should be executed or not
|
||||||
public bool? IncludeSystemPermissionTests { get; set; }
|
public bool? IncludeSystemPermissionTests { get; set; }
|
||||||
|
|
||||||
|
@ -224,7 +196,7 @@ namespace Xharness
|
||||||
throw new ArgumentNullException (nameof (configuration));
|
throw new ArgumentNullException (nameof (configuration));
|
||||||
|
|
||||||
autoConf = configuration.AutoConf;
|
autoConf = configuration.AutoConf;
|
||||||
Configuration = configuration.Configuration ?? throw new ArgumentNullException (nameof (configuration));
|
buildConfiguration = configuration.BuildConfiguration ?? throw new ArgumentNullException (nameof (configuration));
|
||||||
DryRun = configuration.DryRun;
|
DryRun = configuration.DryRun;
|
||||||
IncludeSystemPermissionTests = configuration.IncludeSystemPermissionTests;
|
IncludeSystemPermissionTests = configuration.IncludeSystemPermissionTests;
|
||||||
IOSTestProjects = configuration.IOSTestProjects;
|
IOSTestProjects = configuration.IOSTestProjects;
|
||||||
|
@ -235,9 +207,8 @@ namespace Xharness
|
||||||
PeriodicCommand = configuration.PeriodicCommand;
|
PeriodicCommand = configuration.PeriodicCommand;
|
||||||
PeriodicCommandArguments = configuration.PeriodicCommandArguments;
|
PeriodicCommandArguments = configuration.PeriodicCommandArguments;
|
||||||
PeriodicCommandInterval = configuration.PeriodicCommandInterval;
|
PeriodicCommandInterval = configuration.PeriodicCommandInterval;
|
||||||
RootDirectory = configuration.RootDirectory;
|
sdkRoot = configuration.SdkRoot;
|
||||||
SdkRoot = configuration.SdkRoot;
|
target = configuration.Target;
|
||||||
Target = configuration.Target;
|
|
||||||
Timeout = configuration.TimeoutInMinutes;
|
Timeout = configuration.TimeoutInMinutes;
|
||||||
useSystemXamarinIOSMac = configuration.UseSystemXamarinIOSMac;
|
useSystemXamarinIOSMac = configuration.UseSystemXamarinIOSMac;
|
||||||
Verbosity = configuration.Verbosity;
|
Verbosity = configuration.Verbosity;
|
||||||
|
@ -280,13 +251,6 @@ namespace Xharness
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsBetaXcode {
|
|
||||||
get {
|
|
||||||
// There's no string.Contains (string, StringComparison) overload, so use IndexOf instead.
|
|
||||||
return XcodeRoot.IndexOf ("beta", StringComparison.OrdinalIgnoreCase) >= 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static string FindXcode (string path)
|
static string FindXcode (string path)
|
||||||
{
|
{
|
||||||
var p = path;
|
var p = path;
|
||||||
|
@ -302,7 +266,7 @@ namespace Xharness
|
||||||
|
|
||||||
public string XcodeRoot {
|
public string XcodeRoot {
|
||||||
get {
|
get {
|
||||||
return FindXcode (SdkRoot);
|
return FindXcode (sdkRoot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -318,59 +282,6 @@ namespace Xharness
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object mlaunch_lock = new object ();
|
|
||||||
string DownloadMlaunch ()
|
|
||||||
{
|
|
||||||
// NOTE: the filename part in the url must be unique so that the caching logic works properly.
|
|
||||||
var mlaunch_url = "https://dl.xamarin.com/ios/mlaunch-acdb43d346c431b2c40663c938c919dcb0e91bd7.zip";
|
|
||||||
var extraction_dir = Path.Combine (Path.GetTempPath (), Path.GetFileNameWithoutExtension (mlaunch_url));
|
|
||||||
var mlaunch_path = Path.Combine (extraction_dir, "bin", "mlaunch");
|
|
||||||
|
|
||||||
lock (mlaunch_lock) {
|
|
||||||
if (File.Exists (mlaunch_path))
|
|
||||||
return mlaunch_path;
|
|
||||||
|
|
||||||
try {
|
|
||||||
var local_zip = extraction_dir + ".zip";
|
|
||||||
Log ("Downloading mlaunch to: {0}", local_zip);
|
|
||||||
var wc = new System.Net.WebClient ();
|
|
||||||
wc.DownloadFile (mlaunch_url, local_zip);
|
|
||||||
Log ("Downloaded mlaunch.");
|
|
||||||
|
|
||||||
var tmp_extraction_dir = extraction_dir + ".tmp";
|
|
||||||
if (Directory.Exists (tmp_extraction_dir))
|
|
||||||
Directory.Delete (tmp_extraction_dir, true);
|
|
||||||
if (Directory.Exists (extraction_dir))
|
|
||||||
Directory.Delete (extraction_dir, true);
|
|
||||||
|
|
||||||
Log ("Extracting mlaunch...");
|
|
||||||
using (var p = new Process ()) {
|
|
||||||
p.StartInfo.FileName = "unzip";
|
|
||||||
p.StartInfo.Arguments = StringUtils.FormatArguments ("-d", tmp_extraction_dir, local_zip);
|
|
||||||
Log ("{0} {1}", p.StartInfo.FileName, p.StartInfo.Arguments);
|
|
||||||
p.Start ();
|
|
||||||
p.WaitForExit ();
|
|
||||||
if (p.ExitCode != 0) {
|
|
||||||
Log ("Could not unzip mlaunch, exit code: {0}", p.ExitCode);
|
|
||||||
return mlaunch_path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Directory.Move (tmp_extraction_dir, extraction_dir);
|
|
||||||
|
|
||||||
Log ("Final mlaunch path: {0}", mlaunch_path);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log ("Could not download mlaunch: {0}", e);
|
|
||||||
}
|
|
||||||
return mlaunch_path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string MtouchPath {
|
|
||||||
get {
|
|
||||||
return Path.Combine (IOS_DESTDIR, "Library", "Frameworks", "Xamarin.iOS.framework", "Versions", "Current", "bin", "mtouch");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string MlaunchPath {
|
public string MlaunchPath {
|
||||||
get {
|
get {
|
||||||
return Path.Combine (IOS_DESTDIR, "Library", "Frameworks", "Xamarin.iOS.framework", "Versions", "Current", "bin", "mlaunch");
|
return Path.Combine (IOS_DESTDIR, "Library", "Frameworks", "Xamarin.iOS.framework", "Versions", "Current", "bin", "mlaunch");
|
||||||
|
@ -390,8 +301,8 @@ namespace Xharness
|
||||||
INCLUDE_MAC = make_config.ContainsKey ("INCLUDE_MAC") && !string.IsNullOrEmpty (make_config ["INCLUDE_MAC"]);
|
INCLUDE_MAC = make_config.ContainsKey ("INCLUDE_MAC") && !string.IsNullOrEmpty (make_config ["INCLUDE_MAC"]);
|
||||||
MAC_DESTDIR = make_config ["MAC_DESTDIR"];
|
MAC_DESTDIR = make_config ["MAC_DESTDIR"];
|
||||||
IOS_DESTDIR = make_config ["IOS_DESTDIR"];
|
IOS_DESTDIR = make_config ["IOS_DESTDIR"];
|
||||||
if (string.IsNullOrEmpty (SdkRoot))
|
if (string.IsNullOrEmpty (sdkRoot))
|
||||||
SdkRoot = make_config ["XCODE_DEVELOPER_ROOT"];
|
sdkRoot = make_config ["XCODE_DEVELOPER_ROOT"];
|
||||||
MONO_IOS_SDK_DESTDIR = make_config ["MONO_IOS_SDK_DESTDIR"];
|
MONO_IOS_SDK_DESTDIR = make_config ["MONO_IOS_SDK_DESTDIR"];
|
||||||
MONO_MAC_SDK_DESTDIR = make_config ["MONO_MAC_SDK_DESTDIR"];
|
MONO_MAC_SDK_DESTDIR = make_config ["MONO_MAC_SDK_DESTDIR"];
|
||||||
ENABLE_XAMARIN = make_config.ContainsKey ("ENABLE_XAMARIN") && !string.IsNullOrEmpty (make_config ["ENABLE_XAMARIN"]);
|
ENABLE_XAMARIN = make_config.ContainsKey ("ENABLE_XAMARIN") && !string.IsNullOrEmpty (make_config ["ENABLE_XAMARIN"]);
|
||||||
|
@ -602,7 +513,7 @@ namespace Xharness
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Configure ()
|
int Configure ()
|
||||||
{
|
{
|
||||||
return mac ? AutoConfigureMac (true) : ConfigureIOS ();
|
return mac ? AutoConfigureMac (true) : ConfigureIOS ();
|
||||||
}
|
}
|
||||||
|
@ -680,7 +591,7 @@ namespace Xharness
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Install ()
|
int Install ()
|
||||||
{
|
{
|
||||||
if (HarnessLog == null)
|
if (HarnessLog == null)
|
||||||
HarnessLog = new ConsoleLog ();
|
HarnessLog = new ConsoleLog ();
|
||||||
|
@ -690,11 +601,11 @@ namespace Xharness
|
||||||
new SimulatorsLoaderFactory (this, ProcessManager),
|
new SimulatorsLoaderFactory (this, ProcessManager),
|
||||||
new SimpleListenerFactory (),
|
new SimpleListenerFactory (),
|
||||||
new DeviceLoaderFactory (this, ProcessManager),
|
new DeviceLoaderFactory (this, ProcessManager),
|
||||||
Target,
|
target,
|
||||||
this,
|
this,
|
||||||
HarnessLog,
|
HarnessLog,
|
||||||
project.Path,
|
project.Path,
|
||||||
Configuration);
|
buildConfiguration);
|
||||||
|
|
||||||
using (var install_log = new AppInstallMonitorLog (runner.MainLog)) {
|
using (var install_log = new AppInstallMonitorLog (runner.MainLog)) {
|
||||||
var rv = runner.InstallAsync (install_log.CancellationToken).Result;
|
var rv = runner.InstallAsync (install_log.CancellationToken).Result;
|
||||||
|
@ -705,7 +616,7 @@ namespace Xharness
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Uninstall ()
|
int Uninstall ()
|
||||||
{
|
{
|
||||||
if (HarnessLog == null)
|
if (HarnessLog == null)
|
||||||
HarnessLog = new ConsoleLog ();
|
HarnessLog = new ConsoleLog ();
|
||||||
|
@ -715,11 +626,11 @@ namespace Xharness
|
||||||
new SimulatorsLoaderFactory (this, ProcessManager),
|
new SimulatorsLoaderFactory (this, ProcessManager),
|
||||||
new SimpleListenerFactory (),
|
new SimpleListenerFactory (),
|
||||||
new DeviceLoaderFactory (this, ProcessManager),
|
new DeviceLoaderFactory (this, ProcessManager),
|
||||||
Target,
|
target,
|
||||||
this,
|
this,
|
||||||
HarnessLog,
|
HarnessLog,
|
||||||
project.Path,
|
project.Path,
|
||||||
Configuration);
|
buildConfiguration);
|
||||||
|
|
||||||
var rv = runner.UninstallAsync ().Result;
|
var rv = runner.UninstallAsync ().Result;
|
||||||
if (!rv.Succeeded)
|
if (!rv.Succeeded)
|
||||||
|
@ -728,7 +639,7 @@ namespace Xharness
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Run ()
|
int Run ()
|
||||||
{
|
{
|
||||||
if (HarnessLog == null)
|
if (HarnessLog == null)
|
||||||
HarnessLog = new ConsoleLog ();
|
HarnessLog = new ConsoleLog ();
|
||||||
|
@ -738,11 +649,11 @@ namespace Xharness
|
||||||
new SimulatorsLoaderFactory (this, ProcessManager),
|
new SimulatorsLoaderFactory (this, ProcessManager),
|
||||||
new SimpleListenerFactory (),
|
new SimpleListenerFactory (),
|
||||||
new DeviceLoaderFactory (this, ProcessManager),
|
new DeviceLoaderFactory (this, ProcessManager),
|
||||||
Target,
|
target,
|
||||||
this,
|
this,
|
||||||
HarnessLog,
|
HarnessLog,
|
||||||
project.Path,
|
project.Path,
|
||||||
Configuration);
|
buildConfiguration);
|
||||||
|
|
||||||
var rv = runner.RunAsync ().Result;
|
var rv = runner.RunAsync ().Result;
|
||||||
if (rv != 0)
|
if (rv != 0)
|
||||||
|
@ -751,7 +662,7 @@ namespace Xharness
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Log (int min_level, string message)
|
void Log (int min_level, string message)
|
||||||
{
|
{
|
||||||
if (Verbosity < min_level)
|
if (Verbosity < min_level)
|
||||||
return;
|
return;
|
||||||
|
@ -777,23 +688,6 @@ namespace Xharness
|
||||||
Log (0, message, args);
|
Log (0, message, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LogWrench (string message, params object [] args)
|
|
||||||
{
|
|
||||||
// Disable this for now, since we're not uploading directly to wrench anymore, but instead using the Html Report.
|
|
||||||
//if (!InWrench)
|
|
||||||
// return;
|
|
||||||
|
|
||||||
//Console.WriteLine (message, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void LogWrench (string message)
|
|
||||||
{
|
|
||||||
if (!InCI)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Console.WriteLine (message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool InCI {
|
public bool InCI {
|
||||||
get {
|
get {
|
||||||
// We use the 'BUILD_REVISION' variable to detect whether we're running CI or not.
|
// We use the 'BUILD_REVISION' variable to detect whether we're running CI or not.
|
||||||
|
@ -827,7 +721,7 @@ namespace Xharness
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Jenkins ()
|
int Jenkins ()
|
||||||
{
|
{
|
||||||
if (autoConf) {
|
if (autoConf) {
|
||||||
AutoConfigureIOS ();
|
AutoConfigureIOS ();
|
||||||
|
@ -878,42 +772,6 @@ namespace Xharness
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Save (string doc, string path)
|
|
||||||
{
|
|
||||||
if (!File.Exists (path)) {
|
|
||||||
File.WriteAllText (path, doc);
|
|
||||||
Log (1, "Created {0}", path);
|
|
||||||
} else {
|
|
||||||
var existing = File.ReadAllText (path);
|
|
||||||
if (existing == doc) {
|
|
||||||
Log (1, "Not saved {0}, no change", path);
|
|
||||||
} else {
|
|
||||||
File.WriteAllText (path, doc);
|
|
||||||
Log (1, "Updated {0}", path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We want guids that nobody else has, but we also want to generate the same guid
|
|
||||||
// on subsequent invocations (so that csprojs don't change unnecessarily, which is
|
|
||||||
// annoying when XS reloads the projects, and also causes unnecessary rebuilds).
|
|
||||||
// Nothing really breaks when the sequence isn't identical from run to run, so
|
|
||||||
// this is just a best minimal effort.
|
|
||||||
static Random guid_generator = new Random (unchecked((int) 0xdeadf00d));
|
|
||||||
public Guid NewStableGuid (string seed = null)
|
|
||||||
{
|
|
||||||
var bytes = new byte [16];
|
|
||||||
if (seed == null) {
|
|
||||||
guid_generator.NextBytes (bytes);
|
|
||||||
} else {
|
|
||||||
using (var provider = MD5.Create ()) {
|
|
||||||
var inputBytes = Encoding.UTF8.GetBytes (seed);
|
|
||||||
bytes = provider.ComputeHash (inputBytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new Guid (bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool? disable_watchos_on_wrench;
|
bool? disable_watchos_on_wrench;
|
||||||
public bool DisableWatchOSOnWrench {
|
public bool DisableWatchOSOnWrench {
|
||||||
get {
|
get {
|
||||||
|
@ -927,65 +785,5 @@ namespace Xharness
|
||||||
{
|
{
|
||||||
return ProcessManager.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)
|
|
||||||
{
|
|
||||||
await ExecuteXcodeCommandAsync ("simctl", new [] { "list" }, log, TimeSpan.FromSeconds (10));
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<ILogFile> SymbolicateCrashReportAsync (ILogs logs, ILog log, ILogFile report)
|
|
||||||
{
|
|
||||||
var symbolicatecrash = Path.Combine (XcodeRoot, "Contents/SharedFrameworks/DTDeviceKitBase.framework/Versions/A/Resources/symbolicatecrash");
|
|
||||||
if (!File.Exists (symbolicatecrash))
|
|
||||||
symbolicatecrash = Path.Combine (XcodeRoot, "Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash");
|
|
||||||
|
|
||||||
if (!File.Exists (symbolicatecrash)) {
|
|
||||||
log.WriteLine ("Can't symbolicate {0} because the symbolicatecrash script {1} does not exist", report.Path, symbolicatecrash);
|
|
||||||
return report;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 ProcessManager.ExecuteCommandAsync (symbolicatecrash, new [] { report.Path }, symbolicated, TimeSpan.FromMinutes (1), environment);
|
|
||||||
if (rv.Succeeded) {
|
|
||||||
log.WriteLine ("Symbolicated {0} successfully.", report.Path);
|
|
||||||
return symbolicated;
|
|
||||||
} else {
|
|
||||||
log.WriteLine ("Failed to symbolicate {0}.", report.Path);
|
|
||||||
return report;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<HashSet<string>> CreateCrashReportsSnapshotAsync (ILog log, bool simulatorOrDesktop, string device)
|
|
||||||
{
|
|
||||||
var rv = new HashSet<string> ();
|
|
||||||
|
|
||||||
if (simulatorOrDesktop) {
|
|
||||||
var dir = Path.Combine (Environment.GetEnvironmentVariable ("HOME"), "Library", "Logs", "DiagnosticReports");
|
|
||||||
if (Directory.Exists (dir))
|
|
||||||
rv.UnionWith (Directory.EnumerateFiles (dir));
|
|
||||||
} else {
|
|
||||||
var tmp = Path.GetTempFileName ();
|
|
||||||
try {
|
|
||||||
var sb = new List<string> ();
|
|
||||||
sb.Add ($"--list-crash-reports={tmp}");
|
|
||||||
sb.Add ("--sdkroot");
|
|
||||||
sb.Add (XcodeRoot);
|
|
||||||
if (!string.IsNullOrEmpty (device)) {
|
|
||||||
sb.Add ("--devname");
|
|
||||||
sb.Add (device);
|
|
||||||
}
|
|
||||||
var result = await ProcessManager.ExecuteCommandAsync (MlaunchPath, sb, log, TimeSpan.FromMinutes (1));
|
|
||||||
if (result.Succeeded)
|
|
||||||
rv.UnionWith (File.ReadAllLines (tmp));
|
|
||||||
} finally {
|
|
||||||
File.Delete (tmp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -881,12 +881,14 @@ namespace Xharness.Jenkins
|
||||||
// old simulator tests is also a bit special:
|
// old simulator tests is also a bit special:
|
||||||
// - enabled by default if using a beta Xcode, otherwise disabled by default
|
// - enabled by default if using a beta Xcode, otherwise disabled by default
|
||||||
changed = SetEnabled (labels, "old-simulator", ref IncludeOldSimulatorTests);
|
changed = SetEnabled (labels, "old-simulator", ref IncludeOldSimulatorTests);
|
||||||
if (!changed && Harness.IsBetaXcode) {
|
if (!changed && IsBetaXcode) {
|
||||||
IncludeOldSimulatorTests = true;
|
IncludeOldSimulatorTests = true;
|
||||||
MainLog.WriteLine ("Enabled 'old-simulator' tests because we're using a beta Xcode.");
|
MainLog.WriteLine ("Enabled 'old-simulator' tests because we're using a beta Xcode.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsBetaXcode => Harness.XcodeRoot.IndexOf ("beta", StringComparison.OrdinalIgnoreCase) >= 0;
|
||||||
|
|
||||||
// Returns true if the value was changed.
|
// Returns true if the value was changed.
|
||||||
bool SetEnabled (HashSet<string> labels, string testname, ref bool value)
|
bool SetEnabled (HashSet<string> labels, string testname, ref bool value)
|
||||||
{
|
{
|
||||||
|
|
|
@ -91,7 +91,7 @@ namespace Xharness.Jenkins.TestTasks
|
||||||
if (!Harness.DryRun) {
|
if (!Harness.DryRun) {
|
||||||
ExecutionResult = TestExecutingResult.Running;
|
ExecutionResult = TestExecutingResult.Running;
|
||||||
|
|
||||||
var snapshot = new CrashReportSnapshot () { Device = false, Harness = Harness, Log = log, Logs = Logs, LogDirectory = LogDirectory };
|
var snapshot = new CrashReportSnapshot (Harness, log, Logs, isDevice: false, deviceName: null);
|
||||||
await snapshot.StartCaptureAsync ();
|
await snapshot.StartCaptureAsync ();
|
||||||
|
|
||||||
ProcessExecutionResult result = null;
|
ProcessExecutionResult result = null;
|
||||||
|
|
|
@ -29,7 +29,7 @@ namespace Xharness.Jenkins.TestTasks
|
||||||
if (!Harness.DryRun) {
|
if (!Harness.DryRun) {
|
||||||
ExecutionResult = TestExecutingResult.Running;
|
ExecutionResult = TestExecutingResult.Running;
|
||||||
|
|
||||||
var snapshot = new CrashReportSnapshot () { Device = false, Harness = Harness, Log = log, Logs = Logs, LogDirectory = LogDirectory };
|
var snapshot = new CrashReportSnapshot (Harness, log, Logs, isDevice: false, deviceName: null);
|
||||||
await snapshot.StartCaptureAsync ();
|
await snapshot.StartCaptureAsync ();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
namespace Xharness.Logging {
|
||||||
|
public static class WrenchLog {
|
||||||
|
|
||||||
|
public static void WriteLine (string message, params object [] args)
|
||||||
|
{
|
||||||
|
WriteLine (string.Format (message, args));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WriteLine (string message)
|
||||||
|
{
|
||||||
|
// Disable this for now, since we're not uploading directly to wrench anymore, but instead using the Html Report.
|
||||||
|
//if (!InWrench)
|
||||||
|
// return;
|
||||||
|
|
||||||
|
//Console.WriteLine ("@MonkeyWrench: " + message, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,7 +21,7 @@ namespace Xharness {
|
||||||
{ "mac", "Configure for Xamarin.Mac instead of iOS.", (v) => configuration.Mac = true },
|
{ "mac", "Configure for Xamarin.Mac instead of iOS.", (v) => configuration.Mac = true },
|
||||||
{ "configure", "Creates project files and makefiles.", (v) => action = HarnessAction.Configure },
|
{ "configure", "Creates project files and makefiles.", (v) => action = HarnessAction.Configure },
|
||||||
{ "autoconf", "Automatically decide what to configure.", (v) => configuration.AutoConf = true },
|
{ "autoconf", "Automatically decide what to configure.", (v) => configuration.AutoConf = true },
|
||||||
{ "rootdir=", "The root directory for the tests.", (v) => configuration.RootDirectory = v },
|
{ "rootdir=", "The root directory for the tests.", (v) => Harness.RootDirectory = v },
|
||||||
{ "project=", "Add a project file to process. This can be specified multiple times.", (v) => configuration.IOSTestProjects.Add (new iOSTestProject (v)) },
|
{ "project=", "Add a project file to process. This can be specified multiple times.", (v) => configuration.IOSTestProjects.Add (new iOSTestProject (v)) },
|
||||||
{ "watchos-container-template=", "The directory to use as a template for a watchos container app.", (v) => configuration.WatchOSContainerTemplate = v },
|
{ "watchos-container-template=", "The directory to use as a template for a watchos container app.", (v) => configuration.WatchOSContainerTemplate = v },
|
||||||
{ "watchos-app-template=", "The directory to use as a template for a watchos app.", (v) => configuration.WatchOSAppTemplate = v },
|
{ "watchos-app-template=", "The directory to use as a template for a watchos app.", (v) => configuration.WatchOSAppTemplate = v },
|
||||||
|
@ -47,7 +47,7 @@ namespace Xharness {
|
||||||
{ "sdkroot=", "Where Xcode is", (v) => configuration.SdkRoot = v },
|
{ "sdkroot=", "Where Xcode is", (v) => configuration.SdkRoot = v },
|
||||||
{ "sdkroot94=", "Where Xcode 9.4 is", (v) => Console.WriteLine ("--sdkroot94 is deprecated"), true },
|
{ "sdkroot94=", "Where Xcode 9.4 is", (v) => Console.WriteLine ("--sdkroot94 is deprecated"), true },
|
||||||
{ "target=", "Where to run the project ([ios|watchos|tvos]-[device|simulator|simulator-32|simulator-64]).", (v) => configuration.Target = v.ParseAsAppRunnerTarget () },
|
{ "target=", "Where to run the project ([ios|watchos|tvos]-[device|simulator|simulator-32|simulator-64]).", (v) => configuration.Target = v.ParseAsAppRunnerTarget () },
|
||||||
{ "configuration=", "Which configuration to run (defaults to Debug).", (v) => configuration.Configuration = v },
|
{ "configuration=", "Which configuration to run (defaults to Debug).", (v) => configuration.BuildConfiguration = v },
|
||||||
{ "logdirectory=", "Where to store logs.", (v) => configuration.LogDirectory = v },
|
{ "logdirectory=", "Where to store logs.", (v) => configuration.LogDirectory = v },
|
||||||
{ "logfile=", "Where to store the log.", (v) => Console.WriteLine("The logfile option is deprecated. Please use logdirectory."), true },
|
{ "logfile=", "Where to store the log.", (v) => Console.WriteLine("The logfile option is deprecated. Please use logdirectory."), true },
|
||||||
{ "timeout=", $"Timeout for a test run (in minutes). Default is {configuration.TimeoutInMinutes} minutes.", (v) => configuration.TimeoutInMinutes = double.Parse (v) },
|
{ "timeout=", $"Timeout for a test run (in minutes). Default is {configuration.TimeoutInMinutes} minutes.", (v) => configuration.TimeoutInMinutes = double.Parse (v) },
|
||||||
|
|
|
@ -182,7 +182,7 @@ namespace Xharness.Targets
|
||||||
{
|
{
|
||||||
base.PostProcessExecutableProject ();
|
base.PostProcessExecutableProject ();
|
||||||
|
|
||||||
ProjectGuid = "{" + Harness.NewStableGuid ().ToString ().ToUpper () + "}";
|
ProjectGuid = "{" + Helpers.GenerateStableGuid ().ToString ().ToUpper () + "}";
|
||||||
inputProject.SetProjectGuid (ProjectGuid);
|
inputProject.SetProjectGuid (ProjectGuid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,7 +63,7 @@ namespace Xharness.Targets
|
||||||
csproj.SetProjectReferenceInclude ("TodayExtension.csproj", TodayExtensionProjectPath.Replace ('/', '\\'));
|
csproj.SetProjectReferenceInclude ("TodayExtension.csproj", TodayExtensionProjectPath.Replace ('/', '\\'));
|
||||||
csproj.FixCompileInclude ("Main.cs", Path.Combine (Harness.TodayContainerTemplate, "Main.cs").Replace ('/', '\\'));
|
csproj.FixCompileInclude ("Main.cs", Path.Combine (Harness.TodayContainerTemplate, "Main.cs").Replace ('/', '\\'));
|
||||||
csproj.FixInfoPListInclude (suffix, IsGeneratedBclTest ? GeneratedPath : null);
|
csproj.FixInfoPListInclude (suffix, IsGeneratedBclTest ? GeneratedPath : null);
|
||||||
TodayContainerGuid = "{" + Harness.NewStableGuid ().ToString ().ToUpper () + "}";
|
TodayContainerGuid = "{" + Helpers.GenerateStableGuid ().ToString ().ToUpper () + "}";
|
||||||
ProjectGuid = TodayContainerGuid;
|
ProjectGuid = TodayContainerGuid;
|
||||||
csproj.SetProjectGuid (TodayContainerGuid);
|
csproj.SetProjectGuid (TodayContainerGuid);
|
||||||
if (MonoNativeInfo != null) {
|
if (MonoNativeInfo != null) {
|
||||||
|
|
|
@ -38,7 +38,7 @@ namespace Xharness.Targets
|
||||||
csproj.FindAndReplace ("%WATCHEXTENSION_CSPROJ%", Path.GetFileName (WatchOSExtensionProjectPath));
|
csproj.FindAndReplace ("%WATCHEXTENSION_CSPROJ%", Path.GetFileName (WatchOSExtensionProjectPath));
|
||||||
csproj.SetProjectReferenceValue (Path.GetFileName (WatchOSExtensionProjectPath), "Project", WatchOSExtensionGuid);
|
csproj.SetProjectReferenceValue (Path.GetFileName (WatchOSExtensionProjectPath), "Project", WatchOSExtensionGuid);
|
||||||
csproj.SetProjectReferenceValue (Path.GetFileName (WatchOSExtensionProjectPath), "Name", Path.GetFileNameWithoutExtension (WatchOSExtensionProjectPath));
|
csproj.SetProjectReferenceValue (Path.GetFileName (WatchOSExtensionProjectPath), "Name", Path.GetFileNameWithoutExtension (WatchOSExtensionProjectPath));
|
||||||
WatchOSAppGuid = "{" + Harness.NewStableGuid ().ToString ().ToUpper () + "}";
|
WatchOSAppGuid = "{" + Helpers.GenerateStableGuid ().ToString ().ToUpper () + "}";
|
||||||
csproj.SetProjectGuid (WatchOSAppGuid);
|
csproj.SetProjectGuid (WatchOSAppGuid);
|
||||||
csproj.FixInfoPListInclude (suffix);
|
csproj.FixInfoPListInclude (suffix);
|
||||||
if (MonoNativeInfo != null) {
|
if (MonoNativeInfo != null) {
|
||||||
|
@ -66,7 +66,7 @@ namespace Xharness.Targets
|
||||||
csproj.FindAndReplace ("%CONTAINER_PATH%", Path.GetFullPath (Harness.WatchOSContainerTemplate).Replace ('/', '\\') + "\\");
|
csproj.FindAndReplace ("%CONTAINER_PATH%", Path.GetFullPath (Harness.WatchOSContainerTemplate).Replace ('/', '\\') + "\\");
|
||||||
csproj.FindAndReplace ("%WATCHAPP_CSPROJ%", Path.GetFileName (WatchOSAppProjectPath));
|
csproj.FindAndReplace ("%WATCHAPP_CSPROJ%", Path.GetFileName (WatchOSAppProjectPath));
|
||||||
csproj.SetProjectReferenceValue (Path.GetFileName (WatchOSAppProjectPath), "Name", Path.GetFileNameWithoutExtension (WatchOSAppProjectPath));
|
csproj.SetProjectReferenceValue (Path.GetFileName (WatchOSAppProjectPath), "Name", Path.GetFileNameWithoutExtension (WatchOSAppProjectPath));
|
||||||
WatchOSGuid = "{" + Harness.NewStableGuid ().ToString ().ToUpper () + "}";
|
WatchOSGuid = "{" + Helpers.GenerateStableGuid ().ToString ().ToUpper () + "}";
|
||||||
csproj.SetProjectGuid (WatchOSGuid);
|
csproj.SetProjectGuid (WatchOSGuid);
|
||||||
csproj.FixInfoPListInclude (Suffix);
|
csproj.FixInfoPListInclude (Suffix);
|
||||||
if (MonoNativeInfo != null) {
|
if (MonoNativeInfo != null) {
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
using System;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Xharness.Utilities {
|
||||||
|
|
||||||
|
static class Helpers {
|
||||||
|
|
||||||
|
// We want guids that nobody else has, but we also want to generate the same guid
|
||||||
|
// on subsequent invocations (so that csprojs don't change unnecessarily, which is
|
||||||
|
// annoying when XS reloads the projects, and also causes unnecessary rebuilds).
|
||||||
|
// Nothing really breaks when the sequence isn't identical from run to run, so
|
||||||
|
// this is just a best minimal effort.
|
||||||
|
static Random guid_generator = new Random (unchecked ((int) 0xdeadf00d));
|
||||||
|
public static Guid GenerateStableGuid (string seed = null)
|
||||||
|
{
|
||||||
|
var bytes = new byte [16];
|
||||||
|
if (seed == null) {
|
||||||
|
guid_generator.NextBytes (bytes);
|
||||||
|
} else {
|
||||||
|
using (var provider = MD5.Create ()) {
|
||||||
|
var inputBytes = Encoding.UTF8.GetBytes (seed);
|
||||||
|
bytes = provider.ComputeHash (inputBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new Guid (bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -73,6 +73,7 @@
|
||||||
<PackageReference Include="Mono.Options" Version="5.3.0.1" />
|
<PackageReference Include="Mono.Options" Version="5.3.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="AppInstallMonitorLog.cs" />
|
||||||
<Compile Include="AppRunner.cs" />
|
<Compile Include="AppRunner.cs" />
|
||||||
<Compile Include="AppRunnerTarget.cs" />
|
<Compile Include="AppRunnerTarget.cs" />
|
||||||
<Compile Include="BCLTestImporter\BCLTestAssemblyDefinition.cs" />
|
<Compile Include="BCLTestImporter\BCLTestAssemblyDefinition.cs" />
|
||||||
|
@ -133,6 +134,7 @@
|
||||||
<Compile Include="Logging\Log.cs" />
|
<Compile Include="Logging\Log.cs" />
|
||||||
<Compile Include="Logging\LogFile.cs" />
|
<Compile Include="Logging\LogFile.cs" />
|
||||||
<Compile Include="Logging\Logs.cs" />
|
<Compile Include="Logging\Logs.cs" />
|
||||||
|
<Compile Include="Logging\WrenchLog.cs" />
|
||||||
<Compile Include="MakefileGenerator.cs" />
|
<Compile Include="MakefileGenerator.cs" />
|
||||||
<Compile Include="MonoNativeInfo.cs" />
|
<Compile Include="MonoNativeInfo.cs" />
|
||||||
<Compile Include="NoDeviceFoundException.cs" />
|
<Compile Include="NoDeviceFoundException.cs" />
|
||||||
|
@ -150,6 +152,7 @@
|
||||||
<Compile Include="TestPlatform.cs" />
|
<Compile Include="TestPlatform.cs" />
|
||||||
<Compile Include="TestProject.cs" />
|
<Compile Include="TestProject.cs" />
|
||||||
<Compile Include="Utilities\Extensions.cs" />
|
<Compile Include="Utilities\Extensions.cs" />
|
||||||
|
<Compile Include="Utilities\Helpers.cs" />
|
||||||
<Compile Include="Utilities\PlistExtensions.cs" />
|
<Compile Include="Utilities\PlistExtensions.cs" />
|
||||||
<Compile Include="Utilities\ProjectFileExtensions.cs" />
|
<Compile Include="Utilities\ProjectFileExtensions.cs" />
|
||||||
<Compile Include="Utilities\StringUtils.cs" />
|
<Compile Include="Utilities\StringUtils.cs" />
|
||||||
|
@ -179,17 +182,7 @@
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup />
|
||||||
<Folder Include="BCLTestImporter\Templates\" />
|
|
||||||
<Folder Include="BCLTestImporter\Templates\Managed\" />
|
|
||||||
<Folder Include="BCLTestImporter\Templates\Managed\Resources\" />
|
|
||||||
<Folder Include="BCLTestImporter\Templates\Managed\Resources\src\" />
|
|
||||||
<Folder Include="BCLTestImporter\Templates\Managed\Resources\src\common\TestRunner\" />
|
|
||||||
<Folder Include="BCLTestImporter\Templates\Managed\Resources\src\common\TestRunner\Core\" />
|
|
||||||
<Folder Include="BCLTestImporter\Templates\Managed\Resources\src\common\TestRunner\NUnit\" />
|
|
||||||
<Folder Include="BCLTestImporter\Templates\Managed\Resources\src\common\TestRunner\xUnit\" />
|
|
||||||
<Folder Include="BCLTestImporter\Xamarin\" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="BCLTestImporter\Templates\Managed\Resources\Managed.iOS.csproj.in" />
|
<EmbeddedResource Include="BCLTestImporter\Templates\Managed\Resources\Managed.iOS.csproj.in" />
|
||||||
<EmbeddedResource Include="BCLTestImporter\Templates\Managed\Resources\Managed.macOS.csproj.in" />
|
<EmbeddedResource Include="BCLTestImporter\Templates\Managed\Resources\Managed.macOS.csproj.in" />
|
||||||
|
|
Загрузка…
Ссылка в новой задаче