[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.Logging;
|
||||
using Xharness.Utilities;
|
||||
using System.Net;
|
||||
|
||||
namespace Xharness
|
||||
{
|
||||
namespace Xharness {
|
||||
|
||||
public enum RunMode {
|
||||
Sim32,
|
||||
Sim64,
|
||||
|
@ -136,9 +135,6 @@ namespace Xharness
|
|||
|
||||
AppInformation Initialize ()
|
||||
{
|
||||
if (AppInformation != null)
|
||||
return AppInformation;
|
||||
|
||||
var csproj = new XmlDocument ();
|
||||
csproj.LoadWithoutNetworkAccess (projectFilePath);
|
||||
string appName = csproj.GetAssemblyName ();
|
||||
|
@ -238,8 +234,6 @@ namespace Xharness
|
|||
|
||||
public async Task<ProcessExecutionResult> InstallAsync (CancellationToken cancellation_token)
|
||||
{
|
||||
var appInfo = Initialize ();
|
||||
|
||||
if (isSimulator) {
|
||||
// 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.");
|
||||
|
@ -256,7 +250,7 @@ namespace Xharness
|
|||
args.Add ("-v");
|
||||
|
||||
args.Add ("--installdev");
|
||||
args.Add (appInfo.AppPath);
|
||||
args.Add (AppInformation.AppPath);
|
||||
AddDeviceName (args, companionDeviceName ?? deviceName);
|
||||
|
||||
if (mode == RunMode.WatchOS) {
|
||||
|
@ -264,16 +258,14 @@ namespace Xharness
|
|||
args.Add ("ios,watchos");
|
||||
}
|
||||
|
||||
var totalSize = Directory.GetFiles (appInfo.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");
|
||||
var totalSize = Directory.GetFiles (AppInformation.AppPath, "*", SearchOption.AllDirectories).Select ((v) => new FileInfo (v).Length).Sum ();
|
||||
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);
|
||||
}
|
||||
|
||||
public async Task<ProcessExecutionResult> UninstallAsync ()
|
||||
{
|
||||
var appInfo = Initialize ();
|
||||
|
||||
if (isSimulator)
|
||||
throw new InvalidOperationException ("Uninstalling from a simulator is not supported.");
|
||||
|
||||
|
@ -288,7 +280,7 @@ namespace Xharness
|
|||
args.Add ("-v");
|
||||
|
||||
args.Add ("--uninstalldevbundleid");
|
||||
args.Add (appInfo.BundleIdentifier);
|
||||
args.Add (AppInformation.BundleIdentifier);
|
||||
AddDeviceName (args, companionDeviceName ?? deviceName);
|
||||
|
||||
return await processManager.ExecuteCommandAsync (harness.MlaunchPath, args, MainLog, TimeSpan.FromMinutes (1));
|
||||
|
@ -366,10 +358,10 @@ namespace Xharness
|
|||
MainLog.WriteLine (new string ('#', 10));
|
||||
MainLog.WriteLine ("End of xml results.");
|
||||
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;
|
||||
} 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");
|
||||
crashed = true;
|
||||
parseResult.crashed = true;
|
||||
|
@ -407,19 +399,19 @@ namespace Xharness
|
|||
if (resultLine != null) {
|
||||
var tests_run = resultLine.Replace ("Tests run: ", "");
|
||||
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");
|
||||
return false;
|
||||
} 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");
|
||||
return true;
|
||||
}
|
||||
} 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;
|
||||
} 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");
|
||||
crashed = true;
|
||||
return false;
|
||||
|
@ -436,19 +428,10 @@ namespace Xharness
|
|||
ILog listener_log = null;
|
||||
ILog run_log = MainLog;
|
||||
|
||||
var appInfo = Initialize ();
|
||||
|
||||
if (!isSimulator)
|
||||
FindDevice ();
|
||||
|
||||
crash_reports = new CrashReportSnapshot () {
|
||||
Device = !isSimulator,
|
||||
DeviceName = deviceName,
|
||||
Harness = harness,
|
||||
Log = MainLog,
|
||||
Logs = Logs,
|
||||
LogDirectory = logDirectory,
|
||||
};
|
||||
crash_reports = new CrashReportSnapshot (harness, MainLog, Logs, isDevice: !isSimulator, deviceName);
|
||||
|
||||
var args = new List<string> ();
|
||||
if (!string.IsNullOrEmpty (harness.XcodeRoot)) {
|
||||
|
@ -539,12 +522,12 @@ namespace Xharness
|
|||
bool launch_failure = false;
|
||||
|
||||
if (IsExtension) {
|
||||
switch (appInfo.Extension) {
|
||||
switch (AppInformation.Extension) {
|
||||
case Extension.TodayExtension:
|
||||
args.Add (isSimulator ? "--launchsimbundleid" : "--launchdevbundleid");
|
||||
args.Add ("todayviewforextensions:" + appInfo.BundleIdentifier);
|
||||
args.Add ("todayviewforextensions:" + AppInformation.BundleIdentifier);
|
||||
args.Add ("--observe-extension");
|
||||
args.Add (appInfo.LaunchAppPath);
|
||||
args.Add (AppInformation.LaunchAppPath);
|
||||
break;
|
||||
case Extension.WatchKit2:
|
||||
default:
|
||||
|
@ -552,7 +535,7 @@ namespace Xharness
|
|||
}
|
||||
} else {
|
||||
args.Add (isSimulator ? "--launchsim" : "--launchdev");
|
||||
args.Add (appInfo.LaunchAppPath);
|
||||
args.Add (AppInformation.LaunchAppPath);
|
||||
}
|
||||
if (!isSimulator)
|
||||
args.Add ("--disable-memory-limits");
|
||||
|
@ -588,14 +571,14 @@ namespace Xharness
|
|||
log.StartCapture ();
|
||||
Logs.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) {
|
||||
foreach (var sim in simulators)
|
||||
await sim.PrepareSimulatorAsync (MainLog, appInfo.BundleIdentifier);
|
||||
await sim.PrepareSimulatorAsync (MainLog, AppInformation.BundleIdentifier);
|
||||
}
|
||||
|
||||
args.Add ($"--device=:v2:udid={simulator.UDID}");
|
||||
|
@ -656,7 +639,7 @@ namespace Xharness
|
|||
log.StopCapture ();
|
||||
|
||||
} 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) {
|
||||
args.Add ("--attach-native-debugger"); // this prevents the watch from backgrounding the app.
|
||||
|
@ -718,7 +701,7 @@ namespace Xharness
|
|||
// Upload the system log
|
||||
if (File.Exists (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
|
||||
var crashed = false;
|
||||
if (File.Exists (listener_log.FullPath)) {
|
||||
harness.LogWrench ("@MonkeyWrench: AddFile: {0}", listener_log.FullPath);
|
||||
success = TestsSucceeded (AppInformation, listener_log.FullPath, timed_out, out crashed);
|
||||
WrenchLog.WriteLine ("AddFile: {0}", listener_log.FullPath);
|
||||
success = TestsSucceeded (this.AppInformation, listener_log.FullPath, timed_out, out crashed);
|
||||
} 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");
|
||||
success = false;
|
||||
} 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");
|
||||
success = false;
|
||||
} 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)");
|
||||
crashed = true;
|
||||
success = false;
|
||||
|
@ -802,9 +785,9 @@ namespace Xharness
|
|||
if (harness.InCI) {
|
||||
XmlResultParser.GenerateFailure (Logs,
|
||||
"crash",
|
||||
appInfo.AppName,
|
||||
AppInformation.AppName,
|
||||
variation,
|
||||
$"App Crash {appInfo.AppName} {variation}",
|
||||
$"App Crash {AppInformation.AppName} {variation}",
|
||||
$"App crashed {crash_reason}.",
|
||||
crash_reports.Log.FullPath,
|
||||
harness.XmlJargon);
|
||||
|
@ -826,9 +809,9 @@ namespace Xharness
|
|||
XmlResultParser.GenerateFailure (
|
||||
Logs,
|
||||
"crash",
|
||||
appInfo.AppName,
|
||||
AppInformation.AppName,
|
||||
variation,
|
||||
$"App Crash {appInfo.AppName} {variation}",
|
||||
$"App Crash {AppInformation.AppName} {variation}",
|
||||
$"App crashed: {FailureMessage}",
|
||||
crash_reports.Log.FullPath,
|
||||
harness.XmlJargon);
|
||||
|
@ -840,9 +823,9 @@ namespace Xharness
|
|||
XmlResultParser.GenerateFailure (
|
||||
Logs,
|
||||
"launch",
|
||||
appInfo.AppName,
|
||||
AppInformation.AppName,
|
||||
variation,
|
||||
$"App Launch {appInfo.AppName} {variation} on {deviceName}",
|
||||
$"App Launch {AppInformation.AppName} {variation} on {deviceName}",
|
||||
$"{FailureMessage} on {deviceName}",
|
||||
MainLog.FullPath,
|
||||
XmlResultJargon.NUnitV3);
|
||||
|
@ -865,7 +848,7 @@ namespace Xharness
|
|||
if (isTcp) {
|
||||
XmlResultParser.GenerateFailure (Logs,
|
||||
"tcp-connection",
|
||||
appInfo.AppName,
|
||||
AppInformation.AppName,
|
||||
variation,
|
||||
$"TcpConnection on {deviceName}",
|
||||
$"Device {deviceName} could not reach the host over tcp.",
|
||||
|
@ -875,10 +858,10 @@ namespace Xharness
|
|||
} else if (timed_out && harness.InCI) {
|
||||
XmlResultParser.GenerateFailure (Logs,
|
||||
"timeout",
|
||||
appInfo.AppName,
|
||||
AppInformation.AppName,
|
||||
variation,
|
||||
$"App Timeout {appInfo.AppName} {variation} on bot {deviceName}",
|
||||
$"{appInfo.AppName} {variation} Test run timed out after {timeout.TotalMinutes} minute(s) on bot {deviceName}.",
|
||||
$"App Timeout {AppInformation.AppName} {variation} on bot {deviceName}",
|
||||
$"{AppInformation.AppName} {variation} Test run timed out after {timeout.TotalMinutes} minute(s) on bot {deviceName}.",
|
||||
MainLog.FullPath,
|
||||
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.IO;
|
||||
using System.Collections.Generic;
|
||||
using Xharness.Utilities;
|
||||
|
||||
namespace Xharness.BCLTestImporter {
|
||||
// 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) {
|
||||
iOSMonoSDKPath = Harness.MONO_IOS_SDK_DESTDIR,
|
||||
MacMonoSDKPath = Harness.MONO_MAC_SDK_DESTDIR,
|
||||
GuidGenerator = Harness.NewStableGuid,
|
||||
GuidGenerator = Helpers.GenerateStableGuid,
|
||||
GroupTests = Harness.InCI || Harness.UseGroupedApps,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -10,19 +10,27 @@ namespace Xharness
|
|||
{
|
||||
public class CrashReportSnapshot
|
||||
{
|
||||
public IHarness Harness { get; set; }
|
||||
public ILog Log { get; set; }
|
||||
public ILogs Logs { get; set; }
|
||||
public string LogDirectory { get; set; }
|
||||
public bool Device { get; set; }
|
||||
public string DeviceName { get; set; }
|
||||
readonly IHarness harness;
|
||||
readonly bool isDevice;
|
||||
readonly string deviceName;
|
||||
|
||||
public ILog Log { get; }
|
||||
public ILogs Logs { get; }
|
||||
|
||||
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 ()
|
||||
{
|
||||
InitialSet = await Harness.CreateCrashReportsSnapshotAsync (Log, !Device, DeviceName);
|
||||
InitialSet = await CreateCrashReportsSnapshotAsync ();
|
||||
}
|
||||
|
||||
public async Task EndCaptureAsync (TimeSpan timeout)
|
||||
|
@ -33,13 +41,12 @@ namespace Xharness
|
|||
var watch = new Stopwatch ();
|
||||
watch.Start ();
|
||||
do {
|
||||
var end_crashes = await Harness.CreateCrashReportsSnapshotAsync (Log, !Device, DeviceName);
|
||||
var end_crashes = await CreateCrashReportsSnapshotAsync ();
|
||||
end_crashes.ExceptWith (InitialSet);
|
||||
Reports = end_crashes;
|
||||
if (end_crashes.Count > 0) {
|
||||
Log.WriteLine ("Found {0} new crash report(s)", end_crashes.Count);
|
||||
List<ILogFile> crash_reports;
|
||||
if (!Device) {
|
||||
if (!isDevice) {
|
||||
crash_reports = new List<ILogFile> (end_crashes.Count);
|
||||
foreach (var path in end_crashes) {
|
||||
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-to={crash_report_target.Path}");
|
||||
sb.Add ("--sdkroot");
|
||||
sb.Add (Harness.XcodeRoot);
|
||||
if (!string.IsNullOrEmpty (DeviceName)) {
|
||||
sb.Add (harness.XcodeRoot);
|
||||
if (!string.IsNullOrEmpty (deviceName)) {
|
||||
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) {
|
||||
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);
|
||||
} else {
|
||||
Log.WriteLine ("Could not download crash report {0}", file);
|
||||
|
@ -72,7 +79,7 @@ namespace Xharness
|
|||
crash_reports = downloaded_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);
|
||||
}
|
||||
crash_report_search_done = true;
|
||||
|
@ -86,5 +93,59 @@ namespace Xharness
|
|||
}
|
||||
} 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.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
using Xharness.BCLTestImporter;
|
||||
|
@ -29,7 +26,7 @@ namespace Xharness
|
|||
|
||||
public class HarnessConfiguration {
|
||||
public bool AutoConf { get; set; }
|
||||
public string Configuration { get; set; } = "Debug";
|
||||
public string BuildConfiguration { get; set; } = "Debug";
|
||||
public bool DryRun { get; set; }
|
||||
public Dictionary<string, string> EnvironmentVariables { get; set; } = new Dictionary<string, string> ();
|
||||
public bool? IncludeSystemPermissionTests { get; set; }
|
||||
|
@ -42,7 +39,6 @@ namespace Xharness
|
|||
public string PeriodicCommand { get; set; }
|
||||
public string PeriodicCommandArguments { get; set; }
|
||||
public TimeSpan PeriodicCommandInterval { get; set; }
|
||||
public string RootDirectory { get; set; }
|
||||
public string SdkRoot { get; set; }
|
||||
public AppRunnerTarget Target { get; set; }
|
||||
public double TimeoutInMinutes { get; set; } = 15;
|
||||
|
@ -55,8 +51,6 @@ namespace Xharness
|
|||
|
||||
public interface IHarness {
|
||||
HarnessAction Action { get; }
|
||||
string BCLTodayExtensionTemplate { get; }
|
||||
string Configuration { get; }
|
||||
bool DisableWatchOSOnWrench { get; }
|
||||
string DOTNET { get; }
|
||||
bool DryRun { get; }
|
||||
|
@ -71,7 +65,6 @@ namespace Xharness
|
|||
bool? IncludeSystemPermissionTests { get; set; }
|
||||
string IOS_DESTDIR { get; }
|
||||
List<iOSTestProject> IOSTestProjects { get; }
|
||||
bool IsBetaXcode { get; }
|
||||
string JENKINS_RESULTS_DIRECTORY { get; }
|
||||
string JenkinsConfiguration { get; }
|
||||
HashSet<string> Labels { get; }
|
||||
|
@ -88,12 +81,9 @@ namespace Xharness
|
|||
string PeriodicCommandArguments { get; }
|
||||
TimeSpan PeriodicCommandInterval { get; }
|
||||
IProcessManager ProcessManager { get; }
|
||||
string SdkRoot { get; }
|
||||
AppRunnerTarget Target { get; }
|
||||
double Timeout { get; }
|
||||
string TodayContainerTemplate { get; }
|
||||
string TodayExtensionTemplate { get; }
|
||||
string TVOS_MONO_PATH { get; }
|
||||
bool UseGroupedApps { get; }
|
||||
int Verbosity { get; }
|
||||
string WatchOSAppTemplate { get; }
|
||||
|
@ -103,28 +93,19 @@ namespace Xharness
|
|||
Version XcodeVersion { get; }
|
||||
string XIBuildPath { 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);
|
||||
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 (string message);
|
||||
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);
|
||||
Task<ILogFile> SymbolicateCrashReportAsync (ILogs logs, ILog log, ILogFile report);
|
||||
int Uninstall ();
|
||||
}
|
||||
|
||||
public class Harness : IHarness {
|
||||
readonly AppRunnerTarget target;
|
||||
readonly string buildConfiguration = "Debug";
|
||||
string sdkRoot;
|
||||
|
||||
public HarnessAction Action { get; }
|
||||
public int Verbosity { get; }
|
||||
public ILog HarnessLog { get; set; }
|
||||
|
@ -132,15 +113,9 @@ namespace Xharness
|
|||
public XmlResultJargon XmlJargon { get; }
|
||||
public IProcessManager ProcessManager { get; }
|
||||
|
||||
public string XIBuildPath {
|
||||
get { return Path.GetFullPath (Path.Combine (RootDirectory, "..", "tools", "xibuild", "xibuild")); }
|
||||
}
|
||||
public string XIBuildPath => Path.GetFullPath (Path.Combine (RootDirectory, "..", "tools", "xibuild", "xibuild"));
|
||||
|
||||
public static string Timestamp {
|
||||
get {
|
||||
return $"{DateTime.Now:yyyyMMdd_HHmmss}";
|
||||
}
|
||||
}
|
||||
public static string Timestamp => $"{DateTime.Now:yyyyMMdd_HHmmss}";
|
||||
|
||||
// This is the maccore/tests directory.
|
||||
static string root_directory;
|
||||
|
@ -199,19 +174,16 @@ namespace Xharness
|
|||
public string DOTNET { get; private set; }
|
||||
|
||||
// Run
|
||||
public AppRunnerTarget Target { get; }
|
||||
public string SdkRoot { get; private set; }
|
||||
public string Configuration { get; }
|
||||
public string LogDirectory { get; }
|
||||
public string LogDirectory { get; } = Environment.CurrentDirectory;
|
||||
public double Timeout { get; } = 15; // 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 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 PeriodicCommand { 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
|
||||
public bool? IncludeSystemPermissionTests { get; set; }
|
||||
|
||||
|
@ -224,7 +196,7 @@ namespace Xharness
|
|||
throw new ArgumentNullException (nameof (configuration));
|
||||
|
||||
autoConf = configuration.AutoConf;
|
||||
Configuration = configuration.Configuration ?? throw new ArgumentNullException (nameof (configuration));
|
||||
buildConfiguration = configuration.BuildConfiguration ?? throw new ArgumentNullException (nameof (configuration));
|
||||
DryRun = configuration.DryRun;
|
||||
IncludeSystemPermissionTests = configuration.IncludeSystemPermissionTests;
|
||||
IOSTestProjects = configuration.IOSTestProjects;
|
||||
|
@ -235,9 +207,8 @@ namespace Xharness
|
|||
PeriodicCommand = configuration.PeriodicCommand;
|
||||
PeriodicCommandArguments = configuration.PeriodicCommandArguments;
|
||||
PeriodicCommandInterval = configuration.PeriodicCommandInterval;
|
||||
RootDirectory = configuration.RootDirectory;
|
||||
SdkRoot = configuration.SdkRoot;
|
||||
Target = configuration.Target;
|
||||
sdkRoot = configuration.SdkRoot;
|
||||
target = configuration.Target;
|
||||
Timeout = configuration.TimeoutInMinutes;
|
||||
useSystemXamarinIOSMac = configuration.UseSystemXamarinIOSMac;
|
||||
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)
|
||||
{
|
||||
var p = path;
|
||||
|
@ -302,7 +266,7 @@ namespace Xharness
|
|||
|
||||
public string XcodeRoot {
|
||||
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 {
|
||||
get {
|
||||
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"]);
|
||||
MAC_DESTDIR = make_config ["MAC_DESTDIR"];
|
||||
IOS_DESTDIR = make_config ["IOS_DESTDIR"];
|
||||
if (string.IsNullOrEmpty (SdkRoot))
|
||||
SdkRoot = make_config ["XCODE_DEVELOPER_ROOT"];
|
||||
if (string.IsNullOrEmpty (sdkRoot))
|
||||
sdkRoot = make_config ["XCODE_DEVELOPER_ROOT"];
|
||||
MONO_IOS_SDK_DESTDIR = make_config ["MONO_IOS_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"]);
|
||||
|
@ -602,7 +513,7 @@ namespace Xharness
|
|||
}
|
||||
}
|
||||
|
||||
public int Configure ()
|
||||
int Configure ()
|
||||
{
|
||||
return mac ? AutoConfigureMac (true) : ConfigureIOS ();
|
||||
}
|
||||
|
@ -680,7 +591,7 @@ namespace Xharness
|
|||
return rv;
|
||||
}
|
||||
|
||||
public int Install ()
|
||||
int Install ()
|
||||
{
|
||||
if (HarnessLog == null)
|
||||
HarnessLog = new ConsoleLog ();
|
||||
|
@ -690,11 +601,11 @@ namespace Xharness
|
|||
new SimulatorsLoaderFactory (this, ProcessManager),
|
||||
new SimpleListenerFactory (),
|
||||
new DeviceLoaderFactory (this, ProcessManager),
|
||||
Target,
|
||||
target,
|
||||
this,
|
||||
HarnessLog,
|
||||
project.Path,
|
||||
Configuration);
|
||||
buildConfiguration);
|
||||
|
||||
using (var install_log = new AppInstallMonitorLog (runner.MainLog)) {
|
||||
var rv = runner.InstallAsync (install_log.CancellationToken).Result;
|
||||
|
@ -705,7 +616,7 @@ namespace Xharness
|
|||
return 0;
|
||||
}
|
||||
|
||||
public int Uninstall ()
|
||||
int Uninstall ()
|
||||
{
|
||||
if (HarnessLog == null)
|
||||
HarnessLog = new ConsoleLog ();
|
||||
|
@ -715,11 +626,11 @@ namespace Xharness
|
|||
new SimulatorsLoaderFactory (this, ProcessManager),
|
||||
new SimpleListenerFactory (),
|
||||
new DeviceLoaderFactory (this, ProcessManager),
|
||||
Target,
|
||||
target,
|
||||
this,
|
||||
HarnessLog,
|
||||
project.Path,
|
||||
Configuration);
|
||||
buildConfiguration);
|
||||
|
||||
var rv = runner.UninstallAsync ().Result;
|
||||
if (!rv.Succeeded)
|
||||
|
@ -728,7 +639,7 @@ namespace Xharness
|
|||
return 0;
|
||||
}
|
||||
|
||||
public int Run ()
|
||||
int Run ()
|
||||
{
|
||||
if (HarnessLog == null)
|
||||
HarnessLog = new ConsoleLog ();
|
||||
|
@ -738,11 +649,11 @@ namespace Xharness
|
|||
new SimulatorsLoaderFactory (this, ProcessManager),
|
||||
new SimpleListenerFactory (),
|
||||
new DeviceLoaderFactory (this, ProcessManager),
|
||||
Target,
|
||||
target,
|
||||
this,
|
||||
HarnessLog,
|
||||
project.Path,
|
||||
Configuration);
|
||||
buildConfiguration);
|
||||
|
||||
var rv = runner.RunAsync ().Result;
|
||||
if (rv != 0)
|
||||
|
@ -751,7 +662,7 @@ namespace Xharness
|
|||
return 0;
|
||||
}
|
||||
|
||||
public void Log (int min_level, string message)
|
||||
void Log (int min_level, string message)
|
||||
{
|
||||
if (Verbosity < min_level)
|
||||
return;
|
||||
|
@ -777,23 +688,6 @@ namespace Xharness
|
|||
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 {
|
||||
get {
|
||||
// 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) {
|
||||
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;
|
||||
public bool DisableWatchOSOnWrench {
|
||||
get {
|
||||
|
@ -927,65 +785,5 @@ namespace Xharness
|
|||
{
|
||||
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:
|
||||
// - enabled by default if using a beta Xcode, otherwise disabled by default
|
||||
changed = SetEnabled (labels, "old-simulator", ref IncludeOldSimulatorTests);
|
||||
if (!changed && Harness.IsBetaXcode) {
|
||||
if (!changed && IsBetaXcode) {
|
||||
IncludeOldSimulatorTests = true;
|
||||
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.
|
||||
bool SetEnabled (HashSet<string> labels, string testname, ref bool value)
|
||||
{
|
||||
|
|
|
@ -91,7 +91,7 @@ namespace Xharness.Jenkins.TestTasks
|
|||
if (!Harness.DryRun) {
|
||||
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 ();
|
||||
|
||||
ProcessExecutionResult result = null;
|
||||
|
|
|
@ -29,7 +29,7 @@ namespace Xharness.Jenkins.TestTasks
|
|||
if (!Harness.DryRun) {
|
||||
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 ();
|
||||
|
||||
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 },
|
||||
{ "configure", "Creates project files and makefiles.", (v) => action = HarnessAction.Configure },
|
||||
{ "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)) },
|
||||
{ "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 },
|
||||
|
@ -47,7 +47,7 @@ namespace Xharness {
|
|||
{ "sdkroot=", "Where Xcode is", (v) => configuration.SdkRoot = v },
|
||||
{ "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 () },
|
||||
{ "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 },
|
||||
{ "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) },
|
||||
|
|
|
@ -182,7 +182,7 @@ namespace Xharness.Targets
|
|||
{
|
||||
base.PostProcessExecutableProject ();
|
||||
|
||||
ProjectGuid = "{" + Harness.NewStableGuid ().ToString ().ToUpper () + "}";
|
||||
ProjectGuid = "{" + Helpers.GenerateStableGuid ().ToString ().ToUpper () + "}";
|
||||
inputProject.SetProjectGuid (ProjectGuid);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ namespace Xharness.Targets
|
|||
csproj.SetProjectReferenceInclude ("TodayExtension.csproj", TodayExtensionProjectPath.Replace ('/', '\\'));
|
||||
csproj.FixCompileInclude ("Main.cs", Path.Combine (Harness.TodayContainerTemplate, "Main.cs").Replace ('/', '\\'));
|
||||
csproj.FixInfoPListInclude (suffix, IsGeneratedBclTest ? GeneratedPath : null);
|
||||
TodayContainerGuid = "{" + Harness.NewStableGuid ().ToString ().ToUpper () + "}";
|
||||
TodayContainerGuid = "{" + Helpers.GenerateStableGuid ().ToString ().ToUpper () + "}";
|
||||
ProjectGuid = TodayContainerGuid;
|
||||
csproj.SetProjectGuid (TodayContainerGuid);
|
||||
if (MonoNativeInfo != null) {
|
||||
|
|
|
@ -38,7 +38,7 @@ namespace Xharness.Targets
|
|||
csproj.FindAndReplace ("%WATCHEXTENSION_CSPROJ%", Path.GetFileName (WatchOSExtensionProjectPath));
|
||||
csproj.SetProjectReferenceValue (Path.GetFileName (WatchOSExtensionProjectPath), "Project", WatchOSExtensionGuid);
|
||||
csproj.SetProjectReferenceValue (Path.GetFileName (WatchOSExtensionProjectPath), "Name", Path.GetFileNameWithoutExtension (WatchOSExtensionProjectPath));
|
||||
WatchOSAppGuid = "{" + Harness.NewStableGuid ().ToString ().ToUpper () + "}";
|
||||
WatchOSAppGuid = "{" + Helpers.GenerateStableGuid ().ToString ().ToUpper () + "}";
|
||||
csproj.SetProjectGuid (WatchOSAppGuid);
|
||||
csproj.FixInfoPListInclude (suffix);
|
||||
if (MonoNativeInfo != null) {
|
||||
|
@ -66,7 +66,7 @@ namespace Xharness.Targets
|
|||
csproj.FindAndReplace ("%CONTAINER_PATH%", Path.GetFullPath (Harness.WatchOSContainerTemplate).Replace ('/', '\\') + "\\");
|
||||
csproj.FindAndReplace ("%WATCHAPP_CSPROJ%", Path.GetFileName (WatchOSAppProjectPath));
|
||||
csproj.SetProjectReferenceValue (Path.GetFileName (WatchOSAppProjectPath), "Name", Path.GetFileNameWithoutExtension (WatchOSAppProjectPath));
|
||||
WatchOSGuid = "{" + Harness.NewStableGuid ().ToString ().ToUpper () + "}";
|
||||
WatchOSGuid = "{" + Helpers.GenerateStableGuid ().ToString ().ToUpper () + "}";
|
||||
csproj.SetProjectGuid (WatchOSGuid);
|
||||
csproj.FixInfoPListInclude (Suffix);
|
||||
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" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AppInstallMonitorLog.cs" />
|
||||
<Compile Include="AppRunner.cs" />
|
||||
<Compile Include="AppRunnerTarget.cs" />
|
||||
<Compile Include="BCLTestImporter\BCLTestAssemblyDefinition.cs" />
|
||||
|
@ -133,6 +134,7 @@
|
|||
<Compile Include="Logging\Log.cs" />
|
||||
<Compile Include="Logging\LogFile.cs" />
|
||||
<Compile Include="Logging\Logs.cs" />
|
||||
<Compile Include="Logging\WrenchLog.cs" />
|
||||
<Compile Include="MakefileGenerator.cs" />
|
||||
<Compile Include="MonoNativeInfo.cs" />
|
||||
<Compile Include="NoDeviceFoundException.cs" />
|
||||
|
@ -150,6 +152,7 @@
|
|||
<Compile Include="TestPlatform.cs" />
|
||||
<Compile Include="TestProject.cs" />
|
||||
<Compile Include="Utilities\Extensions.cs" />
|
||||
<Compile Include="Utilities\Helpers.cs" />
|
||||
<Compile Include="Utilities\PlistExtensions.cs" />
|
||||
<Compile Include="Utilities\ProjectFileExtensions.cs" />
|
||||
<Compile Include="Utilities\StringUtils.cs" />
|
||||
|
@ -179,17 +182,7 @@
|
|||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</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.macOS.csproj.in" />
|
||||
|
|
Загрузка…
Ссылка в новой задаче