[Harness] Move utilities out of Harness.cs (#8125)

Co-authored-by: Premek Vysoky <prvysoky@microsoft.com>
This commit is contained in:
Přemek Vysoký 2020-03-17 19:42:21 +01:00 коммит произвёл GitHub
Родитель 13a56ffb95
Коммит 5f2905c5a4
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
15 изменённых файлов: 318 добавлений и 425 удалений

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

@ -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" />