diff --git a/tests/xharness/AppRunner.cs b/tests/xharness/AppRunner.cs index f8ce1a6bfe..9ce7db3ece 100644 --- a/tests/xharness/AppRunner.cs +++ b/tests/xharness/AppRunner.cs @@ -8,12 +8,11 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml; -using Xamarin; -using Xamarin.Utils; using Xharness.Execution; using Xharness.Jenkins.TestTasks; using Xharness.Listeners; using Xharness.Logging; +using Xharness.Utilities; namespace Xharness { diff --git a/tests/xharness/CrashReportSnapshot.cs b/tests/xharness/CrashReportSnapshot.cs new file mode 100644 index 0000000000..8fbec9bde7 --- /dev/null +++ b/tests/xharness/CrashReportSnapshot.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Xharness.Logging; + +namespace Xharness +{ + public class CrashReportSnapshot + { + public Harness 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; } + + public HashSet InitialSet { get; private set; } + public IEnumerable Reports { get; private set; } + + public async Task StartCaptureAsync () + { + InitialSet = await Harness.CreateCrashReportsSnapshotAsync (Log, !Device, DeviceName); + } + + public async Task EndCaptureAsync (TimeSpan timeout) + { + // Check for crash reports + var crash_report_search_done = false; + var crash_report_search_timeout = timeout.TotalSeconds; + var watch = new Stopwatch (); + watch.Start (); + do { + var end_crashes = await Harness.CreateCrashReportsSnapshotAsync (Log, !Device, DeviceName); + end_crashes.ExceptWith (InitialSet); + Reports = end_crashes; + if (end_crashes.Count > 0) { + Log.WriteLine ("Found {0} new crash report(s)", end_crashes.Count); + List crash_reports; + if (!Device) { + crash_reports = new List (end_crashes.Count); + foreach (var path in end_crashes) { + Logs.AddFile (path, $"Crash report: {Path.GetFileName (path)}"); + } + } else { + // Download crash reports from the device. We put them in the project directory so that they're automatically deleted on wrench + // (if we put them in /tmp, they'd never be deleted). + var downloaded_crash_reports = new List (); + foreach (var file in end_crashes) { + var name = Path.GetFileName (file); + var crash_report_target = Logs.Create (name, $"Crash report: {name}", timestamp: false); + var sb = new List (); + 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 ("--devname"); + sb.Add (DeviceName); + } + 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); + downloaded_crash_reports.Add (crash_report_target); + } else { + Log.WriteLine ("Could not download crash report {0}", file); + } + } + crash_reports = downloaded_crash_reports; + } + foreach (var cp in crash_reports) { + Harness.LogWrench ("@MonkeyWrench: AddFile: {0}", cp.Path); + Log.WriteLine (" {0}", cp.Path); + } + crash_report_search_done = true; + } else { + if (watch.Elapsed.TotalSeconds > crash_report_search_timeout) { + crash_report_search_done = true; + } else { + Log.WriteLine ("No crash reports, waiting a second to see if the crash report service just didn't complete in time ({0})", (int) (crash_report_search_timeout - watch.Elapsed.TotalSeconds)); + Thread.Sleep (TimeSpan.FromSeconds (1)); + } + } + } while (!crash_report_search_done); + } + } +} diff --git a/tests/xharness/DeviceLogCapturer.cs b/tests/xharness/DeviceLogCapturer.cs index a912208002..7b2fc19726 100644 --- a/tests/xharness/DeviceLogCapturer.cs +++ b/tests/xharness/DeviceLogCapturer.cs @@ -1,11 +1,9 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.IO; -using System.Text; using System.Threading; -using Xamarin.Utils; using Xharness.Logging; +using Xharness.Utilities; namespace Xharness { diff --git a/tests/xharness/Execution/ProcessManager.cs b/tests/xharness/Execution/ProcessManager.cs index 3e572d48ca..a5ee9a2269 100644 --- a/tests/xharness/Execution/ProcessManager.cs +++ b/tests/xharness/Execution/ProcessManager.cs @@ -7,9 +7,8 @@ using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; -using Xamarin.Utils; -using Xharness; using Xharness.Logging; +using Xharness.Utilities; namespace Xharness.Execution { public class ProcessManager : IProcessManager { diff --git a/tests/xharness/Harness.cs b/tests/xharness/Harness.cs index b003f58a5c..1d9bc358d5 100644 --- a/tests/xharness/Harness.cs +++ b/tests/xharness/Harness.cs @@ -5,13 +5,13 @@ using System.IO; using System.Linq; using System.Security.Cryptography; using System.Text; -using System.Threading; using System.Threading.Tasks; using System.Xml; -using Xamarin.Utils; using Xharness.BCLTestImporter; using Xharness.Logging; using Xharness.Execution; +using Xharness.Targets; +using Xharness.Utilities; namespace Xharness { @@ -846,84 +846,4 @@ namespace Xharness } } - - public class CrashReportSnapshot - { - public Harness 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; } - - public HashSet InitialSet { get; private set; } - public IEnumerable Reports { get; private set; } - - public async Task StartCaptureAsync () - { - InitialSet = await Harness.CreateCrashReportsSnapshotAsync (Log, !Device, DeviceName); - } - - public async Task EndCaptureAsync (TimeSpan timeout) - { - // Check for crash reports - var crash_report_search_done = false; - var crash_report_search_timeout = timeout.TotalSeconds; - var watch = new Stopwatch (); - watch.Start (); - do { - var end_crashes = await Harness.CreateCrashReportsSnapshotAsync (Log, !Device, DeviceName); - end_crashes.ExceptWith (InitialSet); - Reports = end_crashes; - if (end_crashes.Count > 0) { - Log.WriteLine ("Found {0} new crash report(s)", end_crashes.Count); - List crash_reports; - if (!Device) { - crash_reports = new List (end_crashes.Count); - foreach (var path in end_crashes) { - Logs.AddFile (path, $"Crash report: {Path.GetFileName (path)}"); - } - } else { - // Download crash reports from the device. We put them in the project directory so that they're automatically deleted on wrench - // (if we put them in /tmp, they'd never be deleted). - var downloaded_crash_reports = new List (); - foreach (var file in end_crashes) { - var name = Path.GetFileName (file); - var crash_report_target = Logs.Create (name, $"Crash report: {name}", timestamp: false); - var sb = new List (); - 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 ("--devname"); - sb.Add (DeviceName); - } - 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); - downloaded_crash_reports.Add (crash_report_target); - } else { - Log.WriteLine ("Could not download crash report {0}", file); - } - } - crash_reports = downloaded_crash_reports; - } - foreach (var cp in crash_reports) { - Harness.LogWrench ("@MonkeyWrench: AddFile: {0}", cp.Path); - Log.WriteLine (" {0}", cp.Path); - } - crash_report_search_done = true; - } else { - if (watch.Elapsed.TotalSeconds > crash_report_search_timeout) { - crash_report_search_done = true; - } else { - Log.WriteLine ("No crash reports, waiting a second to see if the crash report service just didn't complete in time ({0})", (int) (crash_report_search_timeout - watch.Elapsed.TotalSeconds)); - Thread.Sleep (TimeSpan.FromSeconds (1)); - } - } - } while (!crash_report_search_done); - } - } } diff --git a/tests/xharness/Jenkins/Jenkins.cs b/tests/xharness/Jenkins/Jenkins.cs index 7aee70cf48..a2ad1a483d 100644 --- a/tests/xharness/Jenkins/Jenkins.cs +++ b/tests/xharness/Jenkins/Jenkins.cs @@ -9,6 +9,7 @@ using System.Text; using Xharness.Logging; using Xharness.Execution; using Xharness.Jenkins.TestTasks; +using Xharness.Utilities; namespace Xharness.Jenkins { diff --git a/tests/xharness/Jenkins/TestTasks/BuildProjectTask.cs b/tests/xharness/Jenkins/TestTasks/BuildProjectTask.cs index 27e80adf8f..04efe55400 100644 --- a/tests/xharness/Jenkins/TestTasks/BuildProjectTask.cs +++ b/tests/xharness/Jenkins/TestTasks/BuildProjectTask.cs @@ -4,8 +4,8 @@ using System.Diagnostics; using System.IO; using System.Threading.Tasks; using System.Xml; -using Xamarin.Utils; using Xharness.Logging; +using Xharness.Utilities; namespace Xharness.Jenkins.TestTasks { diff --git a/tests/xharness/Jenkins/TestTasks/DotNetTestTask.cs b/tests/xharness/Jenkins/TestTasks/DotNetTestTask.cs index f9598716cf..7ceaae0869 100644 --- a/tests/xharness/Jenkins/TestTasks/DotNetTestTask.cs +++ b/tests/xharness/Jenkins/TestTasks/DotNetTestTask.cs @@ -1,10 +1,6 @@ -using System.Collections; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Threading.Tasks; -using Xamarin.Utils; -using Xharness.Execution; using Xharness.Logging; namespace Xharness.Jenkins.TestTasks { diff --git a/tests/xharness/Jenkins/TestTasks/MSBuildTask.cs b/tests/xharness/Jenkins/TestTasks/MSBuildTask.cs index ab0f6adaee..ec76713216 100644 --- a/tests/xharness/Jenkins/TestTasks/MSBuildTask.cs +++ b/tests/xharness/Jenkins/TestTasks/MSBuildTask.cs @@ -3,9 +3,8 @@ using System.Collections.Generic; using System.Diagnostics; using System.Threading.Tasks; using System.Xml; -using Xamarin; -using Xamarin.Utils; using Xharness.Logging; +using Xharness.Utilities; namespace Xharness.Jenkins.TestTasks { diff --git a/tests/xharness/Jenkins/TestTasks/MacExecuteTask.cs b/tests/xharness/Jenkins/TestTasks/MacExecuteTask.cs index 8f0c008626..491fdbeac3 100644 --- a/tests/xharness/Jenkins/TestTasks/MacExecuteTask.cs +++ b/tests/xharness/Jenkins/TestTasks/MacExecuteTask.cs @@ -4,10 +4,9 @@ using System.Diagnostics; using System.Linq; using System.Threading.Tasks; using System.Xml; -using Xamarin; -using Xamarin.Utils; using Xharness.Execution; using Xharness.Logging; +using Xharness.Utilities; namespace Xharness.Jenkins.TestTasks { diff --git a/tests/xharness/Jenkins/TestTasks/NUnitExecuteTask.cs b/tests/xharness/Jenkins/TestTasks/NUnitExecuteTask.cs index c9b9a17a77..3bc600632c 100644 --- a/tests/xharness/Jenkins/TestTasks/NUnitExecuteTask.cs +++ b/tests/xharness/Jenkins/TestTasks/NUnitExecuteTask.cs @@ -6,10 +6,9 @@ using System.IO; using System.Linq; using System.Threading.Tasks; using System.Xml; -using Xamarin; -using Xamarin.Utils; using Xharness.Execution; using Xharness.Logging; +using Xharness.Utilities; namespace Xharness.Jenkins.TestTasks { diff --git a/tests/xharness/Jenkins/TestTasks/RunTestTask.cs b/tests/xharness/Jenkins/TestTasks/RunTestTask.cs index 27c94952fe..ad2e4b9a00 100644 --- a/tests/xharness/Jenkins/TestTasks/RunTestTask.cs +++ b/tests/xharness/Jenkins/TestTasks/RunTestTask.cs @@ -4,9 +4,9 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; -using Xamarin.Utils; using Xharness.Execution; using Xharness.Logging; +using Xharness.Utilities; namespace Xharness.Jenkins.TestTasks { diff --git a/tests/xharness/Jenkins/TestTasks/TestTask.cs b/tests/xharness/Jenkins/TestTasks/TestTask.cs index fac330167f..e57f8cc4f8 100644 --- a/tests/xharness/Jenkins/TestTasks/TestTask.cs +++ b/tests/xharness/Jenkins/TestTasks/TestTask.cs @@ -5,8 +5,8 @@ using System.IO; using System.Linq; using System.Threading.Tasks; using System.Xml; -using Xamarin; using Xharness.Logging; +using Xharness.Utilities; namespace Xharness.Jenkins.TestTasks { diff --git a/tests/xharness/MakefileGenerator.cs b/tests/xharness/MakefileGenerator.cs index e9c77b05c0..0dad09cf1d 100644 --- a/tests/xharness/MakefileGenerator.cs +++ b/tests/xharness/MakefileGenerator.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; -using Xharness; +using Xharness.Targets; namespace Xharness { diff --git a/tests/xharness/MonoNativeInfo.cs b/tests/xharness/MonoNativeInfo.cs index dabe71bccc..2a03dea431 100644 --- a/tests/xharness/MonoNativeInfo.cs +++ b/tests/xharness/MonoNativeInfo.cs @@ -26,8 +26,7 @@ using System; using System.IO; using System.Xml; - -using Xamarin; +using Xharness.Utilities; namespace Xharness { diff --git a/tests/xharness/Program.cs b/tests/xharness/Program.cs index 9df95b2c2f..4989e3122b 100644 --- a/tests/xharness/Program.cs +++ b/tests/xharness/Program.cs @@ -1,6 +1,7 @@ using System; using Mono.Options; +using Xharness.Utilities; namespace Xharness { class MainClass { diff --git a/tests/xharness/Simulators.cs b/tests/xharness/Simulators.cs index d244eb048e..908adefa82 100644 --- a/tests/xharness/Simulators.cs +++ b/tests/xharness/Simulators.cs @@ -8,10 +8,9 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml; -using Xamarin; -using Xamarin.Utils; using Xharness.Execution; using Xharness.Logging; +using Xharness.Utilities; namespace Xharness { diff --git a/tests/xharness/SolutionGenerator.cs b/tests/xharness/SolutionGenerator.cs index dad729e2b6..d248201746 100644 --- a/tests/xharness/SolutionGenerator.cs +++ b/tests/xharness/SolutionGenerator.cs @@ -3,7 +3,8 @@ using System.Collections.Generic; using System.IO; using System.Xml; using System.Text; -using Xamarin; +using Xharness.Utilities; +using Xharness.Targets; namespace Xharness { diff --git a/tests/xharness/MacTarget.cs b/tests/xharness/Targets/MacTarget.cs similarity index 98% rename from tests/xharness/MacTarget.cs rename to tests/xharness/Targets/MacTarget.cs index 1254292bfe..fc2b703d3f 100644 --- a/tests/xharness/MacTarget.cs +++ b/tests/xharness/Targets/MacTarget.cs @@ -1,11 +1,8 @@ using System; using System.Collections.Generic; -using System.IO; -using System.Xml; +using Xharness.Utilities; -using Xamarin; - -namespace Xharness +namespace Xharness.Targets { public class MacTarget : Target { diff --git a/tests/xharness/TVOSTarget.cs b/tests/xharness/Targets/TVOSTarget.cs similarity index 97% rename from tests/xharness/TVOSTarget.cs rename to tests/xharness/Targets/TVOSTarget.cs index 7c8e0ba05b..d43142828c 100644 --- a/tests/xharness/TVOSTarget.cs +++ b/tests/xharness/Targets/TVOSTarget.cs @@ -1,10 +1,9 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Xml; -using Xamarin; +using Xharness.Utilities; -namespace Xharness +namespace Xharness.Targets { public class TVOSTarget : iOSTarget { diff --git a/tests/xharness/Target.cs b/tests/xharness/Targets/Target.cs similarity index 99% rename from tests/xharness/Target.cs rename to tests/xharness/Targets/Target.cs index 1a94734a92..fd8de12da6 100644 --- a/tests/xharness/Target.cs +++ b/tests/xharness/Targets/Target.cs @@ -3,9 +3,9 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Xml; -using Xamarin; +using Xharness.Utilities; -namespace Xharness +namespace Xharness.Targets { public abstract class Target { @@ -218,4 +218,3 @@ namespace Xharness public string Guid; } } - diff --git a/tests/xharness/TodayExtensionTarget.cs b/tests/xharness/Targets/TodayExtensionTarget.cs similarity index 99% rename from tests/xharness/TodayExtensionTarget.cs rename to tests/xharness/Targets/TodayExtensionTarget.cs index d2d5cad191..6b62ca84ab 100644 --- a/tests/xharness/TodayExtensionTarget.cs +++ b/tests/xharness/Targets/TodayExtensionTarget.cs @@ -2,9 +2,9 @@ using System; using System.Collections.Generic; using System.IO; using System.Xml; -using Xamarin; +using Xharness.Utilities; -namespace Xharness +namespace Xharness.Targets { public class TodayExtensionTarget : UnifiedTarget { diff --git a/tests/xharness/UnifiedTarget.cs b/tests/xharness/Targets/UnifiedTarget.cs similarity index 98% rename from tests/xharness/UnifiedTarget.cs rename to tests/xharness/Targets/UnifiedTarget.cs index eddc76a752..e0fb3b73f1 100644 --- a/tests/xharness/UnifiedTarget.cs +++ b/tests/xharness/Targets/UnifiedTarget.cs @@ -1,10 +1,9 @@ using System; using System.IO; using System.Xml; +using Xharness.Utilities; -using Xamarin; - -namespace Xharness +namespace Xharness.Targets { public class UnifiedTarget : iOSTarget { @@ -149,4 +148,3 @@ namespace Xharness } } } - diff --git a/tests/xharness/WatchOSTarget.cs b/tests/xharness/Targets/WatchOSTarget.cs similarity index 99% rename from tests/xharness/WatchOSTarget.cs rename to tests/xharness/Targets/WatchOSTarget.cs index 90a4aa8802..f90ddf8e9c 100644 --- a/tests/xharness/WatchOSTarget.cs +++ b/tests/xharness/Targets/WatchOSTarget.cs @@ -2,11 +2,9 @@ using System.Collections.Generic; using System.IO; using System.Xml; +using Xharness.Utilities; -using Xamarin; -using Xamarin.Utils; - -namespace Xharness +namespace Xharness.Targets { public class WatchOSTarget : iOSTarget { @@ -287,4 +285,3 @@ namespace Xharness } } } - diff --git a/tests/xharness/iOSTarget.cs b/tests/xharness/Targets/iOSTarget.cs similarity index 95% rename from tests/xharness/iOSTarget.cs rename to tests/xharness/Targets/iOSTarget.cs index 73d8628b3f..6cd4f97603 100644 --- a/tests/xharness/iOSTarget.cs +++ b/tests/xharness/Targets/iOSTarget.cs @@ -1,9 +1,8 @@ using System.IO; using System.Xml; +using Xharness.Utilities; -using Xamarin; - -namespace Xharness +namespace Xharness.Targets { // iOS here means Xamarin.iOS, not iOS as opposed to tvOS/watchOS. public class iOSTarget : Target diff --git a/tests/xharness/TestProject.cs b/tests/xharness/TestProject.cs index ce59401875..de59357231 100644 --- a/tests/xharness/TestProject.cs +++ b/tests/xharness/TestProject.cs @@ -4,8 +4,8 @@ using System.IO; using System.Linq; using System.Threading.Tasks; using System.Xml; -using Xamarin; using Xharness.Jenkins.TestTasks; +using Xharness.Utilities; namespace Xharness { @@ -121,7 +121,7 @@ namespace Xharness internal async Task CreateCopyAsync (TestTask test = null) { - var directory = Xamarin.Cache.CreateTemporaryDirectory (test?.TestName ?? System.IO.Path.GetFileNameWithoutExtension (Path)); + var directory = TempDirectory.CreateTemporaryDirectory (test?.TestName ?? System.IO.Path.GetFileNameWithoutExtension (Path)); Directory.CreateDirectory (directory); var original_path = Path; Path = System.IO.Path.Combine (directory, System.IO.Path.GetFileName (Path)); diff --git a/tests/xharness/Extensions.cs b/tests/xharness/Utilities/Extensions.cs similarity index 98% rename from tests/xharness/Extensions.cs rename to tests/xharness/Utilities/Extensions.cs index 21baad095c..1ffd9089ef 100644 --- a/tests/xharness/Extensions.cs +++ b/tests/xharness/Utilities/Extensions.cs @@ -3,10 +3,8 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -namespace Xharness -{ - public static class Extensions - { +namespace Xharness.Utilities { + public static class Extensions { public static string AsString (this AppRunnerTarget @this) { switch (@this) { diff --git a/tests/xharness/Utilities/PlistExtensions.cs b/tests/xharness/Utilities/PlistExtensions.cs new file mode 100644 index 0000000000..dd26d5d6d8 --- /dev/null +++ b/tests/xharness/Utilities/PlistExtensions.cs @@ -0,0 +1,134 @@ +using System.IO; +using System.Xml; + +namespace Xharness.Utilities +{ + static class PListExtensions + { + public static void LoadWithoutNetworkAccess (this XmlDocument doc, string filename) + { + using (var fs = new FileStream (filename, FileMode.Open, FileAccess.Read)) { + var settings = new XmlReaderSettings () { + XmlResolver = null, + DtdProcessing = DtdProcessing.Parse, + }; + using (var reader = XmlReader.Create (fs, settings)) { + doc.Load (reader); + } + } + } + + public static void LoadXmlWithoutNetworkAccess (this XmlDocument doc, string xml) + { + using (var fs = new StringReader (xml)) { + var settings = new XmlReaderSettings () { + XmlResolver = null, + DtdProcessing = DtdProcessing.Parse, + }; + using (var reader = XmlReader.Create (fs, settings)) { + doc.Load (reader); + } + } + } + + public static void SetMinimumOSVersion (this XmlDocument plist, string value) + { + plist.SetPListStringValue ("MinimumOSVersion", value); + } + + public static void SetMinimummacOSVersion (this XmlDocument plist, string value) + { + plist.SetPListStringValue ("LSMinimumSystemVersion", value); + } + + public static void SetCFBundleDisplayName (this XmlDocument plist, string value) + { + plist.SetPListStringValue ("CFBundleDisplayName", value); + } + + public static string GetMinimumOSVersion (this XmlDocument plist) + { + return plist.GetPListStringValue ("MinimumOSVersion"); + } + + public static void SetCFBundleIdentifier (this XmlDocument plist, string value) + { + plist.SetPListStringValue ("CFBundleIdentifier", value); + } + + public static void SetCFBundleName (this XmlDocument plist, string value) + { + plist.SetPListStringValue ("CFBundleName", value); + } + + public static void SetUIDeviceFamily (this XmlDocument plist, params int [] families) + { + plist.SetPListArrayOfIntegerValues ("UIDeviceFamily", families); + } + + public static string GetCFBundleIdentifier (this XmlDocument plist) + { + return plist.GetPListStringValue ("CFBundleIdentifier"); + } + + public static string GetNSExtensionPointIdentifier (this XmlDocument plist) + { + return plist.SelectSingleNode ("//dict/key[text()='NSExtensionPointIdentifier']")?.NextSibling?.InnerText; + } + + public static void SetPListStringValue (this XmlDocument plist, string node, string value) + { + var element = plist.SelectSingleNode ("//dict/key[text()='" + node + "']"); + if (element == null) { + plist.AddPListStringValue (node, value); + } else { + element.NextSibling.InnerText = value; + } + } + + public static void AddPListStringValue (this XmlDocument plist, string node, string value) + { + var keyElement = plist.CreateElement ("key"); + keyElement.InnerText = node; + var valueElement = plist.CreateElement ("string"); + valueElement.InnerText = value; + var root = plist.SelectSingleNode ("//dict"); + root.AppendChild (keyElement); + root.AppendChild (valueElement); + } + + public static void AddPListKeyValuePair (this XmlDocument plist, string node, string valueType, string value) + { + var keyElement = plist.CreateElement ("key"); + keyElement.InnerText = node; + var valueElement = plist.CreateElement (valueType); + valueElement.InnerXml = value; + var root = plist.SelectSingleNode ("//dict"); + root.AppendChild (keyElement); + root.AppendChild (valueElement); + } + + public static bool ContainsKey (this XmlDocument plist, string key) + { + return plist.SelectSingleNode ("//dict/key[text()='" + key + "']") != null; + } + + private static void SetPListArrayOfIntegerValues (this XmlDocument plist, string node, params int [] values) + { + var key = plist.SelectSingleNode ("//dict/key[text()='" + node + "']"); + key.ParentNode.RemoveChild (key.NextSibling); + var array = plist.CreateElement ("array"); + foreach (var value in values) { + var element = plist.CreateElement ("integer"); + element.InnerText = value.ToString (); + array.AppendChild (element); + } + key.ParentNode.InsertAfter (array, key); + } + + private static string GetPListStringValue (this XmlDocument plist, string node) + { + return plist.SelectSingleNode ("//dict/key[text()='" + node + "']").NextSibling.InnerText; + } + } +} diff --git a/tests/xharness/ProjectFileExtensions.cs b/tests/xharness/Utilities/ProjectFileExtensions.cs similarity index 94% rename from tests/xharness/ProjectFileExtensions.cs rename to tests/xharness/Utilities/ProjectFileExtensions.cs index e170e8241c..c4d792d8b1 100644 --- a/tests/xharness/ProjectFileExtensions.cs +++ b/tests/xharness/Utilities/ProjectFileExtensions.cs @@ -2,10 +2,8 @@ using System; using System.Collections.Generic; using System.Linq; using System.Xml; -using Xamarin.Utils; -namespace Xharness -{ +namespace Xharness.Utilities { static class ProjectFileExtensions { const string MSBuild_Namespace = "http://schemas.microsoft.com/developer/msbuild/2003"; @@ -206,11 +204,6 @@ namespace Xharness SetTopLevelPropertyGroupValue (csproj, "TargetFrameworkIdentifier", value); } - public static void SetTargetFrameworkVersion (this XmlDocument csproj, string value) - { - SetTopLevelPropertyGroupValue (csproj, "TargetFrameworkVersion", value); - } - public static void SetTopLevelPropertyGroupValue (this XmlDocument csproj, string key, string value) { var firstPropertyGroups = csproj.SelectNodes ("//*[local-name() = 'PropertyGroup']")[0]; @@ -251,16 +244,6 @@ namespace Xharness SetAssemblyReference (csproj, "Xamarin.iOS", value); } - public static void AddReference (this XmlDocument csproj, string projectName) - { - var reference = csproj.SelectSingleNode ("/*/*/*[local-name() = 'Reference' and @Include = 'System']"); - var node = csproj.CreateElement ("Reference", MSBuild_Namespace); - var include_attribute = csproj.CreateAttribute ("Include"); - include_attribute.Value = projectName; - node.Attributes.Append (include_attribute); - reference.ParentNode.AppendChild (node); - } - public static void SetAssemblyReference (this XmlDocument csproj, string current, string value) { var project = csproj.ChildNodes [1]; @@ -276,17 +259,6 @@ namespace Xharness reference.ParentNode.RemoveChild (reference); } - public static void SetHintPath (this XmlDocument csproj, string current, string value) - { - var project = csproj.ChildNodes [1]; - var reference = csproj.SelectSingleNode ("/*/*/*[local-name() = 'Reference' and @Include = '" + current + "']"); - if (reference != null) { - var hintPath = csproj.CreateElement ("HintPath", MSBuild_Namespace); - hintPath.InnerText = value; - reference.AppendChild (hintPath); - } - } - public static void AddCompileInclude (this XmlDocument csproj, string link, string include, bool prepend = false) { var compile_node = csproj.SelectSingleNode ("//*[local-name() = 'Compile']"); @@ -376,17 +348,6 @@ namespace Xharness pg.AppendChild (mea); } } - public static string GetExtraMtouchArgs (this XmlDocument csproj, string platform, string configuration) - { - var mtouchExtraArgs = csproj.SelectNodes ("//*[local-name() = 'MtouchExtraArgs']"); - foreach (XmlNode mea in mtouchExtraArgs) { - if (!IsNodeApplicable (mea, platform, configuration)) - continue; - return mea.InnerText; - } - - return string.Empty; - } public static string GetMtouchLink (this XmlDocument csproj, string platform, string configuration) { @@ -441,7 +402,7 @@ namespace Xharness } } - public static string GetNode (this XmlDocument csproj, string name, string platform, string configuration) + static string GetNode (this XmlDocument csproj, string name, string platform, string configuration) { foreach (var pg in GetPropertyGroups (csproj, platform, configuration)) { foreach (XmlNode node in pg.ChildNodes) @@ -517,16 +478,6 @@ namespace Xharness } } - public static void SetArchitecture (this XmlDocument csproj, string platform, string configuration, string architecture) - { - var nodes = csproj.SelectNodes ("/*/*/*[local-name() = 'MtouchArch']"); - foreach (XmlNode n in nodes) { - if (!IsNodeApplicable (n, platform, configuration)) - continue; - n.InnerText = architecture; - } - } - public static void FixArchitectures (this XmlDocument csproj, string simulator_arch, string device_arch, string platform = null, string configuration = null) { var nodes = csproj.SelectNodes ("/*/*/*[local-name() = 'MtouchArch']"); diff --git a/tests/xharness/Utilities/StringUtils.cs b/tests/xharness/Utilities/StringUtils.cs new file mode 100644 index 0000000000..cf0f0711ba --- /dev/null +++ b/tests/xharness/Utilities/StringUtils.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Xharness.Utilities +{ + class StringUtils + { + static readonly char shellQuoteChar; + static readonly char [] mustQuoteCharacters = new char [] { ' ', '\'', ',', '$', '\\' }; + static readonly char [] mustQuoteCharactersProcess = { ' ', '\\', '"', '\'' }; + + static StringUtils () + { + PlatformID pid = Environment.OSVersion.Platform; + if ((int) pid != 128 && pid != PlatformID.Unix && pid != PlatformID.MacOSX) + shellQuoteChar = '"'; // Windows + else + shellQuoteChar = '\''; // !Windows + } + + public static string FormatArguments (params string [] arguments) + { + return FormatArguments ((IList) arguments); + } + + public static string FormatArguments (IList arguments) + { + return string.Join (" ", QuoteForProcess (arguments)); + } + + static string [] QuoteForProcess (params string [] array) + { + if (array == null || array.Length == 0) + return array; + + var rv = new string [array.Length]; + for (var i = 0; i < array.Length; i++) + rv [i] = QuoteForProcess (array [i]); + return rv; + } + + public static string Quote (string f) + { + if (string.IsNullOrEmpty (f)) + return f ?? string.Empty; + + if (f.IndexOfAny (mustQuoteCharacters) == -1) + return f; + + var s = new StringBuilder (); + + s.Append (shellQuoteChar); + foreach (var c in f) { + if (c == '\'' || c == '"' || c == '\\') + s.Append ('\\'); + + s.Append (c); + } + s.Append (shellQuoteChar); + + return s.ToString (); + } + + // Quote input according to how System.Diagnostics.Process needs it quoted. + static string QuoteForProcess (string f) + { + if (string.IsNullOrEmpty (f)) + return f ?? string.Empty; + + if (f.IndexOfAny (mustQuoteCharactersProcess) == -1) + return f; + + var s = new StringBuilder (); + + s.Append ('"'); + foreach (var c in f) { + if (c == '"') { + s.Append ('\\'); + s.Append (c).Append (c); + } else if (c == '\\') { + s.Append (c); + } + s.Append (c); + } + s.Append ('"'); + + return s.ToString (); + } + + static string [] QuoteForProcess (IList arguments) + { + if (arguments == null) + return Array.Empty (); + return QuoteForProcess (arguments.ToArray ()); + } + } +} diff --git a/tests/xharness/Utilities/TempDirectory.cs b/tests/xharness/Utilities/TempDirectory.cs new file mode 100644 index 0000000000..54b826644c --- /dev/null +++ b/tests/xharness/Utilities/TempDirectory.cs @@ -0,0 +1,56 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; + +namespace Xharness.Utilities +{ + // A class that creates temporary directories next to the test assembly, and cleans the output on startup + // Advantages: + // * The temporary directories are automatically cleaned on Wrench (unlike /tmp, which isn't) + // * The temporary directories stay after a test is run (until a new test run is started), + // which makes it easier to re-run (copy-paste) commands that failed. + public static class TempDirectory + { + static string root; + static int lastNumber; + + static TempDirectory () + { + root = Path.Combine (Path.GetDirectoryName (System.Reflection.Assembly.GetExecutingAssembly ().Location), "tmp-test-dir"); + if (Directory.Exists (root)) + Directory.Delete (root, true); + Directory.CreateDirectory (root); + } + + [DllImport ("libc", SetLastError = true)] + static extern int mkdir (string path, ushort mode); + + public static string CreateTemporaryDirectory (string name = null) + { + if (string.IsNullOrEmpty (name)) { + var calling_method = new System.Diagnostics.StackFrame (1).GetMethod (); + if (calling_method != null) { + name = calling_method.DeclaringType.FullName + "." + calling_method.Name; + } else { + name = "unknown-test"; + } + } + + var rv = Path.Combine (root, name); + for (int i = lastNumber; i < 10000 + lastNumber; i++) { + // There's no way to know if Directory.CreateDirectory + // created the directory or not (which would happen if the directory + // already existed). Checking if the directory exists before + // creating it would result in a race condition if multiple + // threads create temporary directories at the same time. + if (mkdir (rv, Convert.ToUInt16 ("777", 8)) == 0) { + lastNumber = i; + return rv; + } + rv = Path.Combine (root, name + i); + } + + throw new Exception ("Could not create temporary directory"); + } + } +} diff --git a/tests/xharness/Xharness.Tests/Utilities/Tests/StringUtilsTests.cs b/tests/xharness/Xharness.Tests/Utilities/Tests/StringUtilsTests.cs new file mode 100644 index 0000000000..d7dacfb14c --- /dev/null +++ b/tests/xharness/Xharness.Tests/Utilities/Tests/StringUtilsTests.cs @@ -0,0 +1,49 @@ +using System; +using System.Diagnostics; +using NUnit.Framework; +using Xharness.Utilities; + +namespace Xharness.Tests.Utilities.Tests { + + [TestFixture] + public class StringUtilsTests { + + static readonly char shellQuoteChar = + (int)Environment.OSVersion.Platform != 128 + && Environment.OSVersion.Platform != PlatformID.Unix + && Environment.OSVersion.Platform != PlatformID.MacOSX + ? '"' // Windows + : '\''; // !Windows + + [Test] + public void NoEscapingNeeded () + { + Assert.AreEqual ("foo", StringUtils.Quote ("foo")); + } + + [TestCase ("foo bar", "foo bar", Description = "Space")] + [TestCase ("foo \"bar\"", "foo \\\"bar\\\"", Description = "Quotes")] + [TestCase ("foo bar's", "foo bar\\\'s", Description = "Apostrophe")] + [TestCase ("foo $bar's", "foo $bar\\\'s", Description = "Dollar sign")] + public void QuoteForProcessTest (string input, string expected) + { + Assert.AreEqual (shellQuoteChar + expected + shellQuoteChar, StringUtils.Quote (input)); + } + + [Test] + public void FormatArgumentsTest () + { + var p = new Process (); + p.StartInfo.RedirectStandardOutput = true; + p.StartInfo.UseShellExecute = false; + p.StartInfo.FileName = "/bin/echo"; + + var complexInput = "\\\"\"\"'"; + + p.StartInfo.Arguments = StringUtils.FormatArguments ("-n", "foo", complexInput, "bar"); + p.Start (); + var output = p.StandardOutput.ReadToEnd (); + Assert.AreEqual ($"foo {complexInput} bar", output, "echo"); + } + } +} diff --git a/tests/xharness/Xharness.Tests/Xharness.Tests.csproj b/tests/xharness/Xharness.Tests/Xharness.Tests.csproj index 3ef5200016..f85ee5f70b 100644 --- a/tests/xharness/Xharness.Tests/Xharness.Tests.csproj +++ b/tests/xharness/Xharness.Tests/Xharness.Tests.csproj @@ -62,13 +62,8 @@ - - Execution\ProcessManager.cs - - - Execution\IProcessManager.cs - + diff --git a/tests/xharness/XmlResultParser.cs b/tests/xharness/XmlResultParser.cs index 47d1119590..1394fb6ff2 100644 --- a/tests/xharness/XmlResultParser.cs +++ b/tests/xharness/XmlResultParser.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Xml; using System.Xml.Linq; using Xharness.Logging; +using Xharness.Utilities; namespace Xharness { diff --git a/tests/xharness/xharness.csproj b/tests/xharness/xharness.csproj index 6c929a413c..f76162cff2 100644 --- a/tests/xharness/xharness.csproj +++ b/tests/xharness/xharness.csproj @@ -7,7 +7,7 @@ 2.0 {E1F53F80-8399-499B-8017-C414B9CD263B} Exe - xharness + Xharness xharness v4.7.2 8.0 @@ -71,58 +71,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - PListExtensions.cs - - - - - - - SdkVersions.cs - - - - - - - - - - - - Cache.cs - - - - StringUtils.cs - - - - @@ -130,30 +79,72 @@ - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + SdkVersions.cs +