Merge remote-tracking branch 'origin/master' into dotnet-bgen

This commit is contained in:
Rolf Bjarne Kvinge 2020-03-10 13:45:50 +01:00
Родитель d7d9baef2d fa7ffbcabc
Коммит 5c323cebb4
35 изменённых файлов: 530 добавлений и 267 удалений

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

@ -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
{

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

@ -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<string> InitialSet { get; private set; }
public IEnumerable<string> 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<ILogFile> crash_reports;
if (!Device) {
crash_reports = new List<ILogFile> (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<ILogFile> ();
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<string> ();
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);
}
}
}

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

@ -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
{

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

@ -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 {

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

@ -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<string> InitialSet { get; private set; }
public IEnumerable<string> 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<ILogFile> crash_reports;
if (!Device) {
crash_reports = new List<ILogFile> (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<ILogFile> ();
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<string> ();
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);
}
}
}

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

@ -9,6 +9,7 @@ using System.Text;
using Xharness.Logging;
using Xharness.Execution;
using Xharness.Jenkins.TestTasks;
using Xharness.Utilities;
namespace Xharness.Jenkins
{

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

@ -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
{

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

@ -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 {

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

@ -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
{

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

@ -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
{

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

@ -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
{

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

@ -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
{

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

@ -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
{

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

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Xharness;
using Xharness.Targets;
namespace Xharness
{

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

@ -26,8 +26,7 @@
using System;
using System.IO;
using System.Xml;
using Xamarin;
using Xharness.Utilities;
namespace Xharness
{

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

@ -1,6 +1,7 @@
using System;
using Mono.Options;
using Xharness.Utilities;
namespace Xharness {
class MainClass {

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

@ -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
{

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

@ -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
{

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

@ -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
{

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

@ -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
{

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

@ -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;
}
}

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

@ -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
{

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

@ -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
}
}
}

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

@ -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
}
}
}

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

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

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

@ -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));

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

@ -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) {

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

@ -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;
}
}
}

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

@ -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']");

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

@ -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<string>) arguments);
}
public static string FormatArguments (IList<string> 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<string> arguments)
{
if (arguments == null)
return Array.Empty<string> ();
return QuoteForProcess (arguments.ToArray ());
}
}
}

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

@ -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");
}
}
}

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

@ -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");
}
}
}

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

@ -62,13 +62,8 @@
<Compile Include="Listeners\Tests\SimpleListenerFactoryTest.cs" />
<Compile Include="Listeners\Tests\SimpleFileListenerTest.cs" />
<Compile Include="Listeners\Tests\SimpleTcpListenerTest.cs" />
<Compile Include="..\Execution\ProcessManager.cs">
<Link>Execution\ProcessManager.cs</Link>
</Compile>
<Compile Include="..\Execution\IProcessManager.cs">
<Link>Execution\IProcessManager.cs</Link>
</Compile>
<Compile Include="Execution\Tests\ProcessManagerTests.cs" />
<Compile Include="Utilities\Tests\StringUtilsTests.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

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

@ -5,6 +5,7 @@ using System.Linq;
using System.Xml;
using System.Xml.Linq;
using Xharness.Logging;
using Xharness.Utilities;
namespace Xharness {

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

@ -7,7 +7,7 @@
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{E1F53F80-8399-499B-8017-C414B9CD263B}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>xharness</RootNamespace>
<RootNamespace>Xharness</RootNamespace>
<AssemblyName>xharness</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<LangVersion>8.0</LangVersion>
@ -71,58 +71,7 @@
<PackageReference Include="Mono.Options" Version="5.3.0.1" />
</ItemGroup>
<ItemGroup>
<Compile Include="Jenkins\TestTasks\AggregatedRunSimulatorTask.cs" />
<Compile Include="Jenkins\TestTasks\BuildProjectTask.cs" />
<Compile Include="Jenkins\TestTasks\BuildToolTask.cs" />
<Compile Include="Jenkins\TestTasks\MacExecuteTask.cs" />
<Compile Include="Jenkins\TestTasks\MacTask.cs" />
<Compile Include="Jenkins\TestTasks\MakeTask.cs" />
<Compile Include="Jenkins\TestTasks\NUnitExecuteTask.cs" />
<Compile Include="Jenkins\Resource.cs" />
<Compile Include="Jenkins\Resources.cs" />
<Compile Include="Jenkins\TestTasks\RunDeviceTask.cs" />
<Compile Include="Jenkins\TestTasks\RunSimulatorTask.cs" />
<Compile Include="Jenkins\TestTasks\RunTestTask.cs" />
<Compile Include="Jenkins\TestTasks\RunXITask.cs" />
<Compile Include="Jenkins\TestTasks\RunXtroTask.cs" />
<Compile Include="TestExecutingResult.cs" />
<Compile Include="TestPlatform.cs" />
<Compile Include="Jenkins\TestTasks\TestTask.cs" />
<Compile Include="Jenkins\TestTasks\MSBuildTask.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Harness.cs" />
<Compile Include="ProjectFileExtensions.cs" />
<Compile Include="..\..\tools\common\PListExtensions.cs">
<Link>PListExtensions.cs</Link>
</Compile>
<Compile Include="WatchOSTarget.cs" />
<Compile Include="TVOSTarget.cs" />
<Compile Include="Target.cs" />
<Compile Include="UnifiedTarget.cs" />
<Compile Include="AppRunner.cs" />
<Compile Include="..\..\tools\mtouch\SdkVersions.cs">
<Link>SdkVersions.cs</Link>
</Compile>
<Compile Include="DeviceLogCapturer.cs" />
<Compile Include="MakefileGenerator.cs" />
<Compile Include="SolutionGenerator.cs" />
<Compile Include="MacTarget.cs" />
<Compile Include="Jenkins\Jenkins.cs" />
<Compile Include="Simulators.cs" />
<Compile Include="TestProject.cs" />
<Compile Include="GitHub.cs" />
<Compile Include="Extensions.cs" />
<Compile Include="..\mtouch\Cache.cs">
<Link>Cache.cs</Link>
</Compile>
<Compile Include="TodayExtensionTarget.cs" />
<Compile Include="..\..\tools\common\StringUtils.cs">
<Link>StringUtils.cs</Link>
</Compile>
<Compile Include="iOSTarget.cs" />
<Compile Include="MonoNativeInfo.cs" />
<Compile Include="XmlResultParser.cs" />
<Compile Include="BCLTestImporter\BCLTestAssemblyDefinition.cs" />
<Compile Include="BCLTestImporter\BCLTestImportTargetFactory.cs" />
<Compile Include="BCLTestImporter\BCLTestInfoPlistGenerator.cs" />
@ -130,30 +79,72 @@
<Compile Include="BCLTestImporter\BCLTestProjectGenerator.cs" />
<Compile Include="BCLTestImporter\Platform.cs" />
<Compile Include="BCLTestImporter\RegisterTypeGenerator.cs" />
<Compile Include="Logging\ILogs.cs" />
<Compile Include="Logging\Logs.cs" />
<Compile Include="Logging\Log.cs" />
<Compile Include="Logging\ILog.cs" />
<Compile Include="Logging\ILogFile.cs" />
<Compile Include="Logging\LogFile.cs" />
<Compile Include="Logging\ConsoleLog.cs" />
<Compile Include="Logging\CallbackLog.cs" />
<Compile Include="Logging\CaptureLog.cs" />
<Compile Include="CrashReportSnapshot.cs" />
<Compile Include="DeviceLogCapturer.cs" />
<Compile Include="Execution\IProcessManager.cs" />
<Compile Include="Execution\ProcessManager.cs" />
<Compile Include="GitHub.cs" />
<Compile Include="Harness.cs" />
<Compile Include="Jenkins\Jenkins.cs" />
<Compile Include="Jenkins\Resource.cs" />
<Compile Include="Jenkins\Resources.cs" />
<Compile Include="Jenkins\TestTasks\AggregatedRunSimulatorTask.cs" />
<Compile Include="Jenkins\TestTasks\BuildProjectTask.cs" />
<Compile Include="Jenkins\TestTasks\BuildToolTask.cs" />
<Compile Include="Jenkins\TestTasks\DotNetBuildTask.cs" />
<Compile Include="Jenkins\TestTasks\DotNetTestTask.cs" />
<Compile Include="Jenkins\TestTasks\MacExecuteTask.cs" />
<Compile Include="Jenkins\TestTasks\MacTask.cs" />
<Compile Include="Jenkins\TestTasks\MakeTask.cs" />
<Compile Include="Jenkins\TestTasks\MSBuildTask.cs" />
<Compile Include="Jenkins\TestTasks\NUnitExecuteTask.cs" />
<Compile Include="Jenkins\TestTasks\RunDeviceTask.cs" />
<Compile Include="Jenkins\TestTasks\RunSimulatorTask.cs" />
<Compile Include="Jenkins\TestTasks\RunTestTask.cs" />
<Compile Include="Jenkins\TestTasks\RunXITask.cs" />
<Compile Include="Jenkins\TestTasks\RunXtroTask.cs" />
<Compile Include="Jenkins\TestTasks\TestTask.cs" />
<Compile Include="Listeners\SimpleFileListener.cs" />
<Compile Include="Listeners\SimpleHttpListener.cs" />
<Compile Include="Listeners\SimpleListener.cs" />
<Compile Include="Listeners\SimpleTcpListener.cs" />
<Compile Include="Listeners\SimpleListenerFactory.cs" />
<Compile Include="Execution\IProcessManager.cs" />
<Compile Include="Execution\ProcessManager.cs" />
<Compile Include="Jenkins\TestTasks\DotNetTestTask.cs" />
<Compile Include="Jenkins\TestTasks\DotNetBuildTask.cs" />
<Compile Include="Listeners\SimpleTcpListener.cs" />
<Compile Include="Logging\CallbackLog.cs" />
<Compile Include="Logging\CaptureLog.cs" />
<Compile Include="Logging\ConsoleLog.cs" />
<Compile Include="Logging\ILog.cs" />
<Compile Include="Logging\ILogFile.cs" />
<Compile Include="Logging\ILogs.cs" />
<Compile Include="Logging\Log.cs" />
<Compile Include="Logging\LogFile.cs" />
<Compile Include="Logging\Logs.cs" />
<Compile Include="MakefileGenerator.cs" />
<Compile Include="MonoNativeInfo.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Simulators.cs" />
<Compile Include="SolutionGenerator.cs" />
<Compile Include="Targets\iOSTarget.cs" />
<Compile Include="Targets\MacTarget.cs" />
<Compile Include="Targets\Target.cs" />
<Compile Include="Targets\TodayExtensionTarget.cs" />
<Compile Include="Targets\TVOSTarget.cs" />
<Compile Include="Targets\UnifiedTarget.cs" />
<Compile Include="Targets\WatchOSTarget.cs" />
<Compile Include="TestExecutingResult.cs" />
<Compile Include="TestPlatform.cs" />
<Compile Include="TestProject.cs" />
<Compile Include="Utilities\Extensions.cs" />
<Compile Include="Utilities\PlistExtensions.cs" />
<Compile Include="Utilities\ProjectFileExtensions.cs" />
<Compile Include="Utilities\StringUtils.cs" />
<Compile Include="Utilities\TempDirectory.cs" />
<Compile Include="XmlResultParser.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="BCLTestImporter\" />
<Folder Include="Logging\" />
<Folder Include="Listeners\" />
<Folder Include="Execution\" />
<Compile Include="..\..\tools\mtouch\SdkVersions.cs">
<Link>SdkVersions.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup>
<Content Include="xharness.js">