[Harness] Split out classes from Jenkins.cs to separate files (#8046)
Co-authored-by: Premek Vysoky <prvysoky@microsoft.com>
This commit is contained in:
Родитель
19a4304c03
Коммит
65a7168c66
|
@ -3,15 +3,13 @@ using System.Collections.Generic;
|
|||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
using System.Xml.Xsl;
|
||||
using Xamarin;
|
||||
using Xamarin.Utils;
|
||||
using Xharness.Jenkins.TestTasks;
|
||||
using Xharness.Listeners;
|
||||
using Xharness.Logging;
|
||||
|
||||
|
|
|
@ -685,7 +685,7 @@ namespace Xharness
|
|||
AutoConfigureMac (false);
|
||||
}
|
||||
|
||||
var jenkins = new Jenkins ()
|
||||
var jenkins = new Jenkins.Jenkins ()
|
||||
{
|
||||
Harness = this,
|
||||
};
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,98 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Xharness.Jenkins
|
||||
{
|
||||
// This is a very simple class to manage the general concept of 'resource'.
|
||||
// Performance isn't important, so this is very simple.
|
||||
// Currently it's only used to make sure everything that happens on the desktop
|
||||
// is serialized (Jenkins.DesktopResource), but in the future the idea is to
|
||||
// make each connected device a separate resource, which will make it possible
|
||||
// to run tests in parallel across devices (and at the same time use the desktop
|
||||
// to build the next test project).
|
||||
class Resource
|
||||
{
|
||||
public string Name;
|
||||
public string Description;
|
||||
ConcurrentQueue<TaskCompletionSource<IAcquiredResource>> queue = new ConcurrentQueue<TaskCompletionSource<IAcquiredResource>> ();
|
||||
ConcurrentQueue<TaskCompletionSource<IAcquiredResource>> exclusive_queue = new ConcurrentQueue<TaskCompletionSource<IAcquiredResource>> ();
|
||||
bool exclusive;
|
||||
|
||||
public int Users { get; private set; }
|
||||
public int QueuedUsers => queue.Count + exclusive_queue.Count;
|
||||
public int MaxConcurrentUsers { get; set; } = 1;
|
||||
|
||||
public Resource (string name, int max_concurrent_users = 1, string description = null)
|
||||
{
|
||||
this.Name = name;
|
||||
this.MaxConcurrentUsers = max_concurrent_users;
|
||||
this.Description = description ?? name;
|
||||
}
|
||||
|
||||
public Task<IAcquiredResource> AcquireConcurrentAsync ()
|
||||
{
|
||||
lock (queue) {
|
||||
if (!exclusive && Users < MaxConcurrentUsers) {
|
||||
Users++;
|
||||
return Task.FromResult<IAcquiredResource> (new AcquiredResource (this));
|
||||
} else {
|
||||
var tcs = new TaskCompletionSource<IAcquiredResource> (new AcquiredResource (this));
|
||||
queue.Enqueue (tcs);
|
||||
return tcs.Task;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Task<IAcquiredResource> AcquireExclusiveAsync ()
|
||||
{
|
||||
lock (queue) {
|
||||
if (Users == 0) {
|
||||
Users++;
|
||||
exclusive = true;
|
||||
return Task.FromResult<IAcquiredResource> (new AcquiredResource (this));
|
||||
} else {
|
||||
var tcs = new TaskCompletionSource<IAcquiredResource> (new AcquiredResource (this));
|
||||
exclusive_queue.Enqueue (tcs);
|
||||
return tcs.Task;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Release ()
|
||||
{
|
||||
lock (queue) {
|
||||
Users--;
|
||||
exclusive = false;
|
||||
if (queue.TryDequeue (out TaskCompletionSource<IAcquiredResource> tcs)) {
|
||||
Users++;
|
||||
tcs.SetResult ((IAcquiredResource) tcs.Task.AsyncState);
|
||||
} else if (Users == 0 && exclusive_queue.TryDequeue (out tcs)) {
|
||||
Users++;
|
||||
exclusive = true;
|
||||
tcs.SetResult ((IAcquiredResource) tcs.Task.AsyncState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AcquiredResource : IAcquiredResource
|
||||
{
|
||||
public AcquiredResource (Resource resource)
|
||||
{
|
||||
this.Resource = resource;
|
||||
}
|
||||
|
||||
void IDisposable.Dispose ()
|
||||
{
|
||||
Resource.Release ();
|
||||
}
|
||||
|
||||
public Resource Resource { get; }
|
||||
}
|
||||
}
|
||||
|
||||
interface IAcquiredResource : IDisposable
|
||||
{
|
||||
Resource Resource { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Xharness.Jenkins
|
||||
{
|
||||
class Resources
|
||||
{
|
||||
readonly Resource [] resources;
|
||||
|
||||
public Resources (IEnumerable<Resource> resources)
|
||||
{
|
||||
this.resources = resources.ToArray ();
|
||||
}
|
||||
|
||||
public Task<IAcquiredResource> AcquireAnyConcurrentAsync ()
|
||||
{
|
||||
if (resources.Length == 0)
|
||||
throw new Exception ("No resources");
|
||||
|
||||
if (resources.Length == 1)
|
||||
return resources [0].AcquireConcurrentAsync ();
|
||||
|
||||
// We try to acquire every resource
|
||||
// When the first one succeeds, we set the result to true
|
||||
// We immediately release any other resources we acquire.
|
||||
var tcs = new TaskCompletionSource<IAcquiredResource> ();
|
||||
for (int i = 0; i < resources.Length; i++) {
|
||||
resources [i].AcquireConcurrentAsync ().ContinueWith ((v) => {
|
||||
var ar = v.Result;
|
||||
if (!tcs.TrySetResult (ar))
|
||||
ar.Dispose ();
|
||||
});
|
||||
}
|
||||
|
||||
return tcs.Task;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Xharness.Jenkins.TestTasks
|
||||
{
|
||||
// This class groups simulator run tasks according to the
|
||||
// simulator they'll run from, so that we minimize switching
|
||||
// between different simulators (which is slow).
|
||||
class AggregatedRunSimulatorTask : TestTask
|
||||
{
|
||||
public IEnumerable<RunSimulatorTask> Tasks;
|
||||
|
||||
// Due to parallelization this isn't the same as the sum of the duration for all the build tasks.
|
||||
Stopwatch build_timer = new Stopwatch ();
|
||||
public TimeSpan BuildDuration { get { return build_timer.Elapsed; } }
|
||||
|
||||
Stopwatch run_timer = new Stopwatch ();
|
||||
public TimeSpan RunDuration { get { return run_timer.Elapsed; } }
|
||||
|
||||
public AggregatedRunSimulatorTask (IEnumerable<RunSimulatorTask> tasks)
|
||||
{
|
||||
this.Tasks = tasks;
|
||||
}
|
||||
|
||||
protected override void PropagateResults ()
|
||||
{
|
||||
foreach (var task in Tasks) {
|
||||
task.ExecutionResult = ExecutionResult;
|
||||
task.FailureMessage = FailureMessage;
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync ()
|
||||
{
|
||||
if (Tasks.All ((v) => v.Ignored)) {
|
||||
ExecutionResult = TestExecutingResult.Ignored;
|
||||
return;
|
||||
}
|
||||
|
||||
// First build everything. This is required for the run simulator
|
||||
// task to properly configure the simulator.
|
||||
build_timer.Start ();
|
||||
await Task.WhenAll (Tasks.Select ((v) => v.BuildAsync ()).Distinct ());
|
||||
build_timer.Stop ();
|
||||
|
||||
var executingTasks = Tasks.Where ((v) => !v.Ignored && !v.Failed);
|
||||
if (!executingTasks.Any ()) {
|
||||
ExecutionResult = TestExecutingResult.Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
using (var desktop = await NotifyBlockingWaitAsync (Jenkins.DesktopResource.AcquireExclusiveAsync ())) {
|
||||
run_timer.Start ();
|
||||
|
||||
// We need to set the dialog permissions for all the apps
|
||||
// before launching the simulator, because once launched
|
||||
// the simulator caches the values in-memory.
|
||||
foreach (var task in executingTasks) {
|
||||
await task.VerifyRunAsync ();
|
||||
await task.SelectSimulatorAsync ();
|
||||
}
|
||||
|
||||
var devices = executingTasks.First ().Simulators;
|
||||
Jenkins.MainLog.WriteLine ("Selected simulator: {0}", devices.Length > 0 ? devices [0].Name : "none");
|
||||
|
||||
foreach (var dev in devices)
|
||||
await dev.PrepareSimulatorAsync (Jenkins.MainLog, executingTasks.Select ((v) => v.BundleIdentifier).ToArray ());
|
||||
|
||||
foreach (var task in executingTasks) {
|
||||
task.AcquiredResource = desktop;
|
||||
try {
|
||||
await task.RunAsync ();
|
||||
} finally {
|
||||
task.AcquiredResource = null;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var dev in devices)
|
||||
await dev.ShutdownAsync (Jenkins.MainLog);
|
||||
|
||||
await SimDevice.KillEverythingAsync (Jenkins.MainLog);
|
||||
|
||||
run_timer.Stop ();
|
||||
}
|
||||
|
||||
if (Tasks.All ((v) => v.Ignored)) {
|
||||
ExecutionResult = TestExecutingResult.Ignored;
|
||||
} else {
|
||||
ExecutionResult = Tasks.Any ((v) => v.Failed) ? TestExecutingResult.Failed : TestExecutingResult.Succeeded;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
using Xamarin.Utils;
|
||||
using Xharness.Logging;
|
||||
|
||||
namespace Xharness.Jenkins.TestTasks
|
||||
{
|
||||
abstract class BuildProjectTask : BuildToolTask
|
||||
{
|
||||
public string SolutionPath;
|
||||
|
||||
public bool RestoreNugets {
|
||||
get {
|
||||
return TestProject.RestoreNugetsInProject || !string.IsNullOrEmpty (SolutionPath);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool SupportsParallelExecution {
|
||||
get {
|
||||
return Platform.ToString ().StartsWith ("Mac", StringComparison.Ordinal);
|
||||
}
|
||||
}
|
||||
|
||||
async Task<TestExecutingResult> RestoreNugetsAsync (string projectPath, ILog log, bool useXIBuild = false)
|
||||
{
|
||||
using (var resource = await Jenkins.NugetResource.AcquireExclusiveAsync ()) {
|
||||
// we do not want to use xibuild on solutions, we will have some failures with Mac Full
|
||||
var isSolution = projectPath.EndsWith (".sln", StringComparison.Ordinal);
|
||||
if (!File.Exists (projectPath))
|
||||
throw new FileNotFoundException ("Could not find the solution whose nugets to restore.", projectPath);
|
||||
|
||||
using (var nuget = new Process ()) {
|
||||
nuget.StartInfo.FileName = useXIBuild && !isSolution ? Harness.XIBuildPath :
|
||||
"/Library/Frameworks/Mono.framework/Versions/Current/Commands/nuget";
|
||||
var args = new List<string> ();
|
||||
args.Add ((useXIBuild && !isSolution ? "/" : "") + "restore"); // diff param depending on the tool
|
||||
args.Add (projectPath);
|
||||
if (useXIBuild && !isSolution)
|
||||
args.Add ("/verbosity:detailed");
|
||||
else {
|
||||
args.Add ("-verbosity");
|
||||
args.Add ("detailed");
|
||||
}
|
||||
nuget.StartInfo.Arguments = StringUtils.FormatArguments (args);
|
||||
SetEnvironmentVariables (nuget);
|
||||
LogEvent (log, "Restoring nugets for {0} ({1}) on path {2}", TestName, Mode, projectPath);
|
||||
|
||||
var timeout = TimeSpan.FromMinutes (15);
|
||||
var result = await nuget.RunAsync (log, true, timeout);
|
||||
if (result.TimedOut) {
|
||||
log.WriteLine ("Nuget restore timed out after {0} seconds.", timeout.TotalSeconds);
|
||||
return TestExecutingResult.TimedOut;
|
||||
} else if (!result.Succeeded) {
|
||||
return TestExecutingResult.Failed;
|
||||
}
|
||||
}
|
||||
|
||||
LogEvent (log, "Restoring nugets completed for {0} ({1}) on path {2}", TestName, Mode, projectPath);
|
||||
return TestExecutingResult.Succeeded;
|
||||
}
|
||||
}
|
||||
|
||||
List<string> GetNestedReferenceProjects (string csproj)
|
||||
{
|
||||
if (!File.Exists (csproj))
|
||||
throw new FileNotFoundException ("Could not find the project whose reference projects needed to be found.", csproj);
|
||||
var result = new List<string> ();
|
||||
var doc = new XmlDocument ();
|
||||
doc.Load (csproj.Replace ("\\", "/"));
|
||||
foreach (var referenceProject in doc.GetProjectReferences ()) {
|
||||
var fixPath = referenceProject.Replace ("\\", "/"); // do the replace in case we use win paths
|
||||
result.Add (fixPath);
|
||||
// get all possible references
|
||||
result.AddRange (GetNestedReferenceProjects (fixPath));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// This method must be called with the desktop resource acquired
|
||||
// (which is why it takes an IAcquiredResources as a parameter without using it in the function itself).
|
||||
protected async Task RestoreNugetsAsync (ILog log, IAcquiredResource resource, bool useXIBuild = false)
|
||||
{
|
||||
if (!RestoreNugets)
|
||||
return;
|
||||
|
||||
if (!File.Exists (SolutionPath ?? TestProject.Path))
|
||||
throw new FileNotFoundException ("Could not find the solution whose nugets to restore.", SolutionPath ?? TestProject.Path);
|
||||
|
||||
// might happen that the project does contain reference projects with nugets, grab the reference projects and ensure
|
||||
// thast they have the nugets restored (usually, watch os test projects
|
||||
if (SolutionPath == null) {
|
||||
var references = GetNestedReferenceProjects (TestProject.Path);
|
||||
foreach (var referenceProject in references) {
|
||||
var execResult = await RestoreNugetsAsync (referenceProject, log, useXIBuild); // do the replace in case we use win paths
|
||||
if (execResult == TestExecutingResult.TimedOut) {
|
||||
ExecutionResult = execResult;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// restore for the main project/solution]
|
||||
ExecutionResult = await RestoreNugetsAsync (SolutionPath ?? TestProject.Path, log, useXIBuild);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Xharness.Jenkins.TestTasks
|
||||
{
|
||||
abstract class BuildToolTask : TestTask
|
||||
{
|
||||
public bool SpecifyPlatform = true;
|
||||
public bool SpecifyConfiguration = true;
|
||||
|
||||
public override string Mode {
|
||||
get { return Platform.ToString (); }
|
||||
set { throw new NotSupportedException (); }
|
||||
}
|
||||
|
||||
public virtual Task CleanAsync ()
|
||||
{
|
||||
Console.WriteLine ("Clean is not implemented for {0}", GetType ().Name);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
using Xamarin;
|
||||
using Xamarin.Utils;
|
||||
using Xharness.Logging;
|
||||
|
||||
namespace Xharness.Jenkins.TestTasks
|
||||
{
|
||||
class MacExecuteTask : MacTask
|
||||
{
|
||||
public string Path;
|
||||
public bool BCLTest;
|
||||
public bool IsUnitTest;
|
||||
|
||||
public MacExecuteTask (BuildToolTask build_task)
|
||||
: base (build_task)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool SupportsParallelExecution {
|
||||
get {
|
||||
if (TestName.Contains ("xammac")) {
|
||||
// We run the xammac tests in both Debug and Release configurations.
|
||||
// These tests are not written to support parallel execution
|
||||
// (there are hard coded paths used for instance), so disable
|
||||
// parallel execution for these tests.
|
||||
return false;
|
||||
}
|
||||
if (BCLTest) {
|
||||
// We run the BCL tests in multiple flavors (Full/Modern),
|
||||
// and the BCL tests are not written to support parallel execution,
|
||||
// so disable parallel execution for these tests.
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.SupportsParallelExecution;
|
||||
}
|
||||
}
|
||||
|
||||
public override IEnumerable<Log> AggregatedLogs {
|
||||
get {
|
||||
return base.AggregatedLogs.Union (BuildTask.Logs);
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task RunTestAsync ()
|
||||
{
|
||||
var projectDir = System.IO.Path.GetDirectoryName (ProjectFile);
|
||||
var name = System.IO.Path.GetFileName (projectDir);
|
||||
if (string.Equals ("mac", name, StringComparison.OrdinalIgnoreCase))
|
||||
name = System.IO.Path.GetFileName (System.IO.Path.GetDirectoryName (projectDir));
|
||||
var suffix = string.Empty;
|
||||
switch (Platform) {
|
||||
case TestPlatform.Mac_Modern:
|
||||
suffix = "-modern";
|
||||
break;
|
||||
case TestPlatform.Mac_Full:
|
||||
suffix = "-full";
|
||||
break;
|
||||
case TestPlatform.Mac_System:
|
||||
suffix = "-system";
|
||||
break;
|
||||
}
|
||||
if (ProjectFile.EndsWith (".sln", StringComparison.Ordinal)) {
|
||||
Path = System.IO.Path.Combine (System.IO.Path.GetDirectoryName (ProjectFile), "bin", BuildTask.ProjectPlatform, BuildTask.ProjectConfiguration + suffix, name + ".app", "Contents", "MacOS", name);
|
||||
} else {
|
||||
var project = new XmlDocument ();
|
||||
project.LoadWithoutNetworkAccess (ProjectFile);
|
||||
var outputPath = project.GetOutputPath (BuildTask.ProjectPlatform, BuildTask.ProjectConfiguration).Replace ('\\', '/');
|
||||
var assemblyName = project.GetAssemblyName ();
|
||||
Path = System.IO.Path.Combine (System.IO.Path.GetDirectoryName (ProjectFile), outputPath, assemblyName + ".app", "Contents", "MacOS", assemblyName);
|
||||
}
|
||||
|
||||
using (var resource = await NotifyAndAcquireDesktopResourceAsync ()) {
|
||||
using (var proc = new Process ()) {
|
||||
proc.StartInfo.FileName = Path;
|
||||
if (IsUnitTest) {
|
||||
var xml = Logs.CreateFile ($"test-{Platform}-{Timestamp}.xml", LogType.NUnitResult.ToString ());
|
||||
proc.StartInfo.Arguments = StringUtils.FormatArguments ($"-result=" + xml);
|
||||
}
|
||||
if (!Harness.GetIncludeSystemPermissionTests (Platform, false))
|
||||
proc.StartInfo.EnvironmentVariables ["DISABLE_SYSTEM_PERMISSION_TESTS"] = "1";
|
||||
proc.StartInfo.EnvironmentVariables ["MONO_DEBUG"] = "no-gdb-backtrace";
|
||||
Jenkins.MainLog.WriteLine ("Executing {0} ({1})", TestName, Mode);
|
||||
var log = Logs.Create ($"execute-{Platform}-{Timestamp}.txt", LogType.ExecutionLog.ToString ());
|
||||
if (!Harness.DryRun) {
|
||||
ExecutionResult = TestExecutingResult.Running;
|
||||
|
||||
var snapshot = new CrashReportSnapshot () { Device = false, Harness = Harness, Log = log, Logs = Logs, LogDirectory = LogDirectory };
|
||||
await snapshot.StartCaptureAsync ();
|
||||
|
||||
ProcessExecutionResult result = null;
|
||||
try {
|
||||
var timeout = TimeSpan.FromMinutes (20);
|
||||
|
||||
result = await proc.RunAsync (log, true, timeout);
|
||||
if (result.TimedOut) {
|
||||
FailureMessage = $"Execution timed out after {timeout.TotalSeconds} seconds.";
|
||||
log.WriteLine (FailureMessage);
|
||||
ExecutionResult = TestExecutingResult.TimedOut;
|
||||
} else if (result.Succeeded) {
|
||||
ExecutionResult = TestExecutingResult.Succeeded;
|
||||
} else {
|
||||
ExecutionResult = TestExecutingResult.Failed;
|
||||
FailureMessage = result.ExitCode != 1 ? $"Test run crashed (exit code: {result.ExitCode})." : "Test run failed.";
|
||||
log.WriteLine (FailureMessage);
|
||||
}
|
||||
} finally {
|
||||
await snapshot.EndCaptureAsync (TimeSpan.FromSeconds (Succeeded ? 0 : result?.ExitCode > 1 ? 120 : 5));
|
||||
}
|
||||
}
|
||||
Jenkins.MainLog.WriteLine ("Executed {0} ({1})", TestName, Mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
using System;
|
||||
|
||||
namespace Xharness.Jenkins.TestTasks
|
||||
{
|
||||
abstract class MacTask : RunTestTask
|
||||
{
|
||||
public MacTask (BuildToolTask build_task)
|
||||
: base (build_task)
|
||||
{
|
||||
}
|
||||
|
||||
public override string Mode {
|
||||
get {
|
||||
switch (Platform) {
|
||||
case TestPlatform.Mac:
|
||||
return "Mac";
|
||||
case TestPlatform.Mac_Modern:
|
||||
return "Mac Modern";
|
||||
case TestPlatform.Mac_Full:
|
||||
return "Mac Full";
|
||||
case TestPlatform.Mac_System:
|
||||
return "Mac System";
|
||||
default:
|
||||
throw new NotImplementedException (Platform.ToString ());
|
||||
}
|
||||
}
|
||||
set {
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using Xharness.Logging;
|
||||
|
||||
namespace Xharness.Jenkins.TestTasks
|
||||
{
|
||||
class MakeTask : BuildToolTask
|
||||
{
|
||||
public string Target;
|
||||
public string WorkingDirectory;
|
||||
public TimeSpan Timeout = TimeSpan.FromMinutes (5);
|
||||
|
||||
protected override async Task ExecuteAsync ()
|
||||
{
|
||||
using (var resource = await NotifyAndAcquireDesktopResourceAsync ()) {
|
||||
using (var make = new Process ()) {
|
||||
make.StartInfo.FileName = "make";
|
||||
make.StartInfo.WorkingDirectory = WorkingDirectory;
|
||||
make.StartInfo.Arguments = Target;
|
||||
SetEnvironmentVariables (make);
|
||||
var log = Logs.Create ($"make-{Platform}-{Timestamp}.txt", LogType.BuildLog.ToString ());
|
||||
LogEvent (log, "Making {0} in {1}", Target, WorkingDirectory);
|
||||
if (!Harness.DryRun) {
|
||||
var timeout = Timeout;
|
||||
var result = await make.RunAsync (log, true, timeout);
|
||||
if (result.TimedOut) {
|
||||
ExecutionResult = TestExecutingResult.TimedOut;
|
||||
log.WriteLine ("Make timed out after {0} seconds.", timeout.TotalSeconds);
|
||||
} else if (result.Succeeded) {
|
||||
ExecutionResult = TestExecutingResult.Succeeded;
|
||||
} else {
|
||||
ExecutionResult = TestExecutingResult.Failed;
|
||||
}
|
||||
}
|
||||
using (var reader = log.GetReader ())
|
||||
AddWrenchLogFiles (reader);
|
||||
Jenkins.MainLog.WriteLine ("Made {0} ({1})", TestName, Mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,191 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
using Xamarin;
|
||||
using Xamarin.Utils;
|
||||
using Xharness.Logging;
|
||||
|
||||
namespace Xharness.Jenkins.TestTasks
|
||||
{
|
||||
class NUnitExecuteTask : RunTestTask
|
||||
{
|
||||
public string TestLibrary;
|
||||
public string TestExecutable;
|
||||
public string WorkingDirectory;
|
||||
public bool ProduceHtmlReport = true;
|
||||
public bool InProcess;
|
||||
public TimeSpan Timeout = TimeSpan.FromMinutes (10);
|
||||
|
||||
public NUnitExecuteTask (BuildToolTask build_task)
|
||||
: base (build_task)
|
||||
{
|
||||
}
|
||||
|
||||
public void FindNUnitConsoleExecutable (ILog log)
|
||||
{
|
||||
if (!string.IsNullOrEmpty (TestExecutable)) {
|
||||
log.WriteLine ("Using existing executable: {0}", TestExecutable);
|
||||
return;
|
||||
}
|
||||
|
||||
var packages_conf = Path.Combine (Path.GetDirectoryName (TestProject.Path), "packages.config");
|
||||
var nunit_version = string.Empty;
|
||||
var is_packageref = false;
|
||||
const string default_nunit_version = "3.9.0";
|
||||
|
||||
if (!File.Exists (packages_conf)) {
|
||||
var xml = new XmlDocument ();
|
||||
xml.LoadWithoutNetworkAccess (TestProject.Path);
|
||||
var packageref = xml.SelectSingleNode ("//*[local-name()='PackageReference' and @Include = 'NUnit.ConsoleRunner']");
|
||||
if (packageref != null) {
|
||||
is_packageref = true;
|
||||
nunit_version = packageref.Attributes ["Version"].InnerText;
|
||||
log.WriteLine ("Found PackageReference in {0} for NUnit.ConsoleRunner {1}", TestProject, nunit_version);
|
||||
} else {
|
||||
nunit_version = default_nunit_version;
|
||||
log.WriteLine ("No packages.config found for {0}: assuming nunit version is {1}", TestProject, nunit_version);
|
||||
}
|
||||
} else {
|
||||
using (var str = new StreamReader (packages_conf)) {
|
||||
using (var reader = XmlReader.Create (str)) {
|
||||
while (reader.Read ()) {
|
||||
if (reader.NodeType != XmlNodeType.Element)
|
||||
continue;
|
||||
if (reader.Name != "package")
|
||||
continue;
|
||||
var id = reader.GetAttribute ("id");
|
||||
if (id != "NUnit.ConsoleRunner" && id != "NUnit.Runners")
|
||||
continue;
|
||||
nunit_version = reader.GetAttribute ("version");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nunit_version == string.Empty) {
|
||||
nunit_version = default_nunit_version;
|
||||
log.WriteLine ("Could not find the NUnit.ConsoleRunner element in {0}, using the default version ({1})", packages_conf, nunit_version);
|
||||
} else {
|
||||
log.WriteLine ("Found the NUnit.ConsoleRunner/NUnit.Runners element in {0} for {2}, version is: {1}", packages_conf, nunit_version, TestProject.Path);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_packageref) {
|
||||
TestExecutable = Path.Combine (Harness.RootDirectory, "..", "tools", $"nunit3-console-{nunit_version}");
|
||||
if (!File.Exists (TestExecutable))
|
||||
throw new FileNotFoundException ($"The helper script to execute the unit tests does not exist: {TestExecutable}");
|
||||
WorkingDirectory = Path.GetDirectoryName (TestProject.Path);
|
||||
} else if (nunit_version [0] == '2') {
|
||||
TestExecutable = Path.Combine (Harness.RootDirectory, "..", "packages", "NUnit.Runners." + nunit_version, "tools", "nunit-console.exe");
|
||||
WorkingDirectory = Path.Combine (Path.GetDirectoryName (TestExecutable), "lib");
|
||||
} else {
|
||||
TestExecutable = Path.Combine (Harness.RootDirectory, "..", "packages", "NUnit.ConsoleRunner." + nunit_version, "tools", "nunit3-console.exe");
|
||||
WorkingDirectory = Path.GetDirectoryName (TestLibrary);
|
||||
}
|
||||
TestExecutable = Path.GetFullPath (TestExecutable);
|
||||
WorkingDirectory = Path.GetFullPath (WorkingDirectory);
|
||||
if (!File.Exists (TestExecutable))
|
||||
throw new FileNotFoundException ($"The nunit executable '{TestExecutable}' doesn't exist.");
|
||||
}
|
||||
|
||||
public bool IsNUnit3 {
|
||||
get {
|
||||
return Path.GetFileName (TestExecutable).Contains ("unit3-console");
|
||||
}
|
||||
}
|
||||
public override IEnumerable<Log> AggregatedLogs {
|
||||
get {
|
||||
return base.AggregatedLogs.Union (BuildTask.Logs);
|
||||
}
|
||||
}
|
||||
|
||||
public override string Mode {
|
||||
get {
|
||||
return base.Mode ?? "NUnit";
|
||||
}
|
||||
set {
|
||||
base.Mode = value;
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task RunTestAsync ()
|
||||
{
|
||||
using (var resource = await NotifyAndAcquireDesktopResourceAsync ()) {
|
||||
var xmlLog = Logs.CreateFile ($"log-{Timestamp}.xml", LogType.XmlLog.ToString ());
|
||||
var log = Logs.Create ($"execute-{Timestamp}.txt", LogType.ExecutionLog.ToString ());
|
||||
FindNUnitConsoleExecutable (log);
|
||||
using (var proc = new Process ()) {
|
||||
|
||||
proc.StartInfo.WorkingDirectory = WorkingDirectory;
|
||||
proc.StartInfo.FileName = Harness.XIBuildPath;
|
||||
var args = new List<string> ();
|
||||
args.Add ("-t");
|
||||
args.Add ("--");
|
||||
args.Add (Path.GetFullPath (TestExecutable));
|
||||
args.Add (Path.GetFullPath (TestLibrary));
|
||||
if (IsNUnit3) {
|
||||
args.Add ("-result=" + xmlLog + ";format=nunit2");
|
||||
args.Add ("--labels=All");
|
||||
if (InProcess)
|
||||
args.Add ("--inprocess");
|
||||
} else {
|
||||
args.Add ("-xml=" + xmlLog);
|
||||
args.Add ("-labels");
|
||||
}
|
||||
proc.StartInfo.Arguments = StringUtils.FormatArguments (args);
|
||||
SetEnvironmentVariables (proc);
|
||||
foreach (DictionaryEntry de in proc.StartInfo.EnvironmentVariables)
|
||||
log.WriteLine ($"export {de.Key}={de.Value}");
|
||||
Jenkins.MainLog.WriteLine ("Executing {0} ({1})", TestName, Mode);
|
||||
if (!Harness.DryRun) {
|
||||
ExecutionResult = TestExecutingResult.Running;
|
||||
var result = await proc.RunAsync (log, true, Timeout);
|
||||
if (result.TimedOut) {
|
||||
FailureMessage = $"Execution timed out after {Timeout.TotalMinutes} minutes.";
|
||||
log.WriteLine (FailureMessage);
|
||||
ExecutionResult = TestExecutingResult.TimedOut;
|
||||
} else if (result.Succeeded) {
|
||||
ExecutionResult = TestExecutingResult.Succeeded;
|
||||
} else {
|
||||
ExecutionResult = TestExecutingResult.Failed;
|
||||
FailureMessage = $"Execution failed with exit code {result.ExitCode}";
|
||||
}
|
||||
}
|
||||
Jenkins.MainLog.WriteLine ("Executed {0} ({1})", TestName, Mode);
|
||||
}
|
||||
|
||||
if (ProduceHtmlReport) {
|
||||
try {
|
||||
var output = Logs.Create ($"Log-{Timestamp}.html", "HTML log");
|
||||
using (var srt = new StringReader (File.ReadAllText (Path.Combine (Harness.RootDirectory, "HtmlTransform.xslt")))) {
|
||||
using (var sri = File.OpenRead (xmlLog)) {
|
||||
using (var xrt = XmlReader.Create (srt)) {
|
||||
using (var xri = XmlReader.Create (sri)) {
|
||||
var xslt = new System.Xml.Xsl.XslCompiledTransform ();
|
||||
xslt.Load (xrt);
|
||||
using (var xwo = XmlWriter.Create (output as TextWriter, xslt.OutputSettings)) // use OutputSettings of xsl, so it can be output as HTML
|
||||
{
|
||||
xslt.Transform (xri, xwo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.WriteLine ("Failed to produce HTML report: {0}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Reset ()
|
||||
{
|
||||
base.Reset ();
|
||||
BuildTask?.Reset ();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,182 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Xharness.Jenkins.TestTasks
|
||||
{
|
||||
class RunDeviceTask : RunXITask<Device>
|
||||
{
|
||||
AppInstallMonitorLog install_log;
|
||||
public override string ProgressMessage {
|
||||
get {
|
||||
var log = install_log;
|
||||
if (log == null)
|
||||
return base.ProgressMessage;
|
||||
|
||||
var percent_complete = log.CopyingApp ? log.AppPercentComplete : log.WatchAppPercentComplete;
|
||||
var bytes = log.CopyingApp ? log.AppBytes : log.WatchAppBytes;
|
||||
var total_bytes = log.CopyingApp ? log.AppTotalBytes : log.WatchAppTotalBytes;
|
||||
var elapsed = log.CopyingApp ? log.AppCopyDuration : log.WatchAppCopyDuration;
|
||||
var speed_bps = elapsed.Ticks == 0 ? -1 : bytes / elapsed.TotalSeconds;
|
||||
var estimated_left = TimeSpan.FromSeconds ((total_bytes - bytes) / speed_bps);
|
||||
var transfer_percent = 100 * (double) bytes / total_bytes;
|
||||
var str = log.CopyingApp ? "App" : "Watch App";
|
||||
var rv = $"{str} installation: {percent_complete}% done.\n" +
|
||||
$"\tApp size: {total_bytes:N0} bytes ({total_bytes / 1024.0 / 1024.0:N2} MB)\n" +
|
||||
$"\tTransferred: {bytes:N0} bytes ({bytes / 1024.0 / 1024.0:N2} MB)\n" +
|
||||
$"\tTransferred in {elapsed.TotalSeconds:#.#}s ({elapsed})\n" +
|
||||
$"\tTransfer speed: {speed_bps:N0} B/s ({speed_bps / 1024.0 / 1024.0:N} MB/s, {60 * speed_bps / 1024.0 / 1024.0:N2} MB/m)\n" +
|
||||
$"\tEstimated time left: {estimated_left.TotalSeconds:#.#}s ({estimated_left})";
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
public RunDeviceTask (XBuildTask build_task, IEnumerable<Device> candidates)
|
||||
: base (build_task, candidates.OrderBy ((v) => v.DebugSpeed))
|
||||
{
|
||||
switch (build_task.Platform) {
|
||||
case TestPlatform.iOS:
|
||||
case TestPlatform.iOS_Unified:
|
||||
case TestPlatform.iOS_Unified32:
|
||||
case TestPlatform.iOS_Unified64:
|
||||
AppRunnerTarget = AppRunnerTarget.Device_iOS;
|
||||
break;
|
||||
case TestPlatform.iOS_TodayExtension64:
|
||||
AppRunnerTarget = AppRunnerTarget.Device_iOS;
|
||||
break;
|
||||
case TestPlatform.tvOS:
|
||||
AppRunnerTarget = AppRunnerTarget.Device_tvOS;
|
||||
break;
|
||||
case TestPlatform.watchOS:
|
||||
case TestPlatform.watchOS_32:
|
||||
case TestPlatform.watchOS_64_32:
|
||||
AppRunnerTarget = AppRunnerTarget.Device_watchOS;
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException ();
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task RunTestAsync ()
|
||||
{
|
||||
Jenkins.MainLog.WriteLine ("Running '{0}' on device (candidates: '{1}')", ProjectFile, string.Join ("', '", Candidates.Select ((v) => v.Name).ToArray ()));
|
||||
|
||||
var uninstall_log = Logs.Create ($"uninstall-{Timestamp}.log", "Uninstall log");
|
||||
using (var device_resource = await NotifyBlockingWaitAsync (Jenkins.GetDeviceResources (Candidates).AcquireAnyConcurrentAsync ())) {
|
||||
try {
|
||||
// Set the device we acquired.
|
||||
Device = Candidates.First ((d) => d.UDID == device_resource.Resource.Name);
|
||||
if (Device.DevicePlatform == DevicePlatform.watchOS)
|
||||
CompanionDevice = Jenkins.Devices.FindCompanionDevice (Jenkins.DeviceLoadLog, Device);
|
||||
Jenkins.MainLog.WriteLine ("Acquired device '{0}' for '{1}'", Device.Name, ProjectFile);
|
||||
|
||||
runner = new AppRunner {
|
||||
Harness = Harness,
|
||||
ProjectFile = ProjectFile,
|
||||
Target = AppRunnerTarget,
|
||||
LogDirectory = LogDirectory,
|
||||
MainLog = uninstall_log,
|
||||
DeviceName = Device.Name,
|
||||
CompanionDeviceName = CompanionDevice?.Name,
|
||||
Configuration = ProjectConfiguration,
|
||||
TimeoutMultiplier = TimeoutMultiplier,
|
||||
Variation = Variation,
|
||||
BuildTask = BuildTask,
|
||||
};
|
||||
|
||||
// Sometimes devices can't upgrade (depending on what has changed), so make sure to uninstall any existing apps first.
|
||||
if (Jenkins.UninstallTestApp) {
|
||||
runner.MainLog = uninstall_log;
|
||||
var uninstall_result = await runner.UninstallAsync ();
|
||||
if (!uninstall_result.Succeeded)
|
||||
MainLog.WriteLine ($"Pre-run uninstall failed, exit code: {uninstall_result.ExitCode} (this hopefully won't affect the test result)");
|
||||
} else {
|
||||
uninstall_log.WriteLine ($"Pre-run uninstall skipped.");
|
||||
}
|
||||
|
||||
if (!Failed) {
|
||||
// Install the app
|
||||
this.install_log = new AppInstallMonitorLog (Logs.Create ($"install-{Timestamp}.log", "Install log"));
|
||||
try {
|
||||
runner.MainLog = this.install_log;
|
||||
var install_result = await runner.InstallAsync (install_log.CancellationToken);
|
||||
if (!install_result.Succeeded) {
|
||||
FailureMessage = $"Install failed, exit code: {install_result.ExitCode}.";
|
||||
ExecutionResult = TestExecutingResult.Failed;
|
||||
if (Harness.InCI)
|
||||
XmlResultParser.GenerateFailure (Logs, "install", runner.AppName, runner.Variation,
|
||||
$"AppInstallation on {runner.DeviceName}", $"Install failed on {runner.DeviceName}, exit code: {install_result.ExitCode}",
|
||||
install_log.FullPath, Harness.XmlJargon);
|
||||
}
|
||||
} finally {
|
||||
this.install_log.Dispose ();
|
||||
this.install_log = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Failed) {
|
||||
// Run the app
|
||||
runner.MainLog = Logs.Create ($"run-{Device.UDID}-{Timestamp}.log", "Run log");
|
||||
await runner.RunAsync ();
|
||||
|
||||
if (!string.IsNullOrEmpty (runner.FailureMessage))
|
||||
FailureMessage = runner.FailureMessage;
|
||||
else if (runner.Result != TestExecutingResult.Succeeded)
|
||||
FailureMessage = GuessFailureReason (runner.MainLog);
|
||||
|
||||
if (runner.Result == TestExecutingResult.Succeeded && Platform == TestPlatform.iOS_TodayExtension64) {
|
||||
// For the today extension, the main app is just a single test.
|
||||
// This is because running the today extension will not wake up the device,
|
||||
// nor will it close & reopen the today app (but launching the main app
|
||||
// will do both of these things, preparing the device for launching the today extension).
|
||||
|
||||
AppRunner todayRunner = new AppRunner {
|
||||
Harness = Harness,
|
||||
ProjectFile = TestProject.GetTodayExtension ().Path,
|
||||
Target = AppRunnerTarget,
|
||||
LogDirectory = LogDirectory,
|
||||
MainLog = Logs.Create ($"extension-run-{Device.UDID}-{Timestamp}.log", "Extension run log"),
|
||||
DeviceName = Device.Name,
|
||||
CompanionDeviceName = CompanionDevice?.Name,
|
||||
Configuration = ProjectConfiguration,
|
||||
Variation = Variation,
|
||||
BuildTask = BuildTask,
|
||||
};
|
||||
additional_runner = todayRunner;
|
||||
await todayRunner.RunAsync ();
|
||||
foreach (var log in todayRunner.Logs.Where ((v) => !v.Description.StartsWith ("Extension ", StringComparison.Ordinal)))
|
||||
log.Description = "Extension " + log.Description [0].ToString ().ToLower () + log.Description.Substring (1);
|
||||
ExecutionResult = todayRunner.Result;
|
||||
|
||||
if (!string.IsNullOrEmpty (todayRunner.FailureMessage))
|
||||
FailureMessage = todayRunner.FailureMessage;
|
||||
} else {
|
||||
ExecutionResult = runner.Result;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
// Uninstall again, so that we don't leave junk behind and fill up the device.
|
||||
if (Jenkins.UninstallTestApp) {
|
||||
runner.MainLog = uninstall_log;
|
||||
var uninstall_result = await runner.UninstallAsync ();
|
||||
if (!uninstall_result.Succeeded)
|
||||
MainLog.WriteLine ($"Post-run uninstall failed, exit code: {uninstall_result.ExitCode} (this won't affect the test result)");
|
||||
} else {
|
||||
uninstall_log.WriteLine ($"Post-run uninstall skipped.");
|
||||
}
|
||||
|
||||
// Also clean up after us locally.
|
||||
if (Harness.InCI || Jenkins.CleanSuccessfulTestRuns && Succeeded)
|
||||
await BuildTask.CleanAsync ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override string XIMode {
|
||||
get {
|
||||
return "device";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Xharness.Jenkins.TestTasks
|
||||
{
|
||||
class RunSimulatorTask : RunXITask<SimDevice>
|
||||
{
|
||||
public IAcquiredResource AcquiredResource;
|
||||
|
||||
public SimDevice[] Simulators {
|
||||
get {
|
||||
if (Device == null) {
|
||||
return new SimDevice [] { };
|
||||
} else if (CompanionDevice == null) {
|
||||
return new SimDevice [] { Device };
|
||||
} else {
|
||||
return new SimDevice [] { Device, CompanionDevice };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public RunSimulatorTask (XBuildTask build_task, IEnumerable<SimDevice> candidates = null)
|
||||
: base (build_task, candidates)
|
||||
{
|
||||
var project = Path.GetFileNameWithoutExtension (ProjectFile);
|
||||
if (project.EndsWith ("-tvos", StringComparison.Ordinal)) {
|
||||
AppRunnerTarget = AppRunnerTarget.Simulator_tvOS;
|
||||
} else if (project.EndsWith ("-watchos", StringComparison.Ordinal)) {
|
||||
AppRunnerTarget = AppRunnerTarget.Simulator_watchOS;
|
||||
} else {
|
||||
AppRunnerTarget = AppRunnerTarget.Simulator_iOS;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task FindSimulatorAsync ()
|
||||
{
|
||||
if (Device != null)
|
||||
return;
|
||||
|
||||
var asyncEnumerable = Candidates as IAsyncEnumerable;
|
||||
if (asyncEnumerable != null)
|
||||
await asyncEnumerable.ReadyTask;
|
||||
|
||||
if (!Candidates.Any ()) {
|
||||
ExecutionResult = TestExecutingResult.DeviceNotFound;
|
||||
FailureMessage = "No applicable devices found.";
|
||||
} else {
|
||||
Device = Candidates.First ();
|
||||
if (Platform == TestPlatform.watchOS)
|
||||
CompanionDevice = Jenkins.Simulators.FindCompanionDevice (Jenkins.SimulatorLoadLog, Device);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public async Task SelectSimulatorAsync ()
|
||||
{
|
||||
if (Finished)
|
||||
return;
|
||||
|
||||
if (!BuildTask.Succeeded) {
|
||||
ExecutionResult = TestExecutingResult.BuildFailure;
|
||||
return;
|
||||
}
|
||||
|
||||
await FindSimulatorAsync ();
|
||||
|
||||
var clean_state = false;//Platform == TestPlatform.watchOS;
|
||||
runner = new AppRunner () {
|
||||
Harness = Harness,
|
||||
ProjectFile = ProjectFile,
|
||||
EnsureCleanSimulatorState = clean_state,
|
||||
Target = AppRunnerTarget,
|
||||
LogDirectory = LogDirectory,
|
||||
MainLog = Logs.Create ($"run-{Device.UDID}-{Timestamp}.log", "Run log"),
|
||||
Configuration = ProjectConfiguration,
|
||||
TimeoutMultiplier = TimeoutMultiplier,
|
||||
Variation = Variation,
|
||||
BuildTask = BuildTask,
|
||||
};
|
||||
runner.Simulators = Simulators;
|
||||
runner.Initialize ();
|
||||
}
|
||||
|
||||
Task<IAcquiredResource> AcquireResourceAsync ()
|
||||
{
|
||||
if (AcquiredResource != null) {
|
||||
// We don't own the acquired resource, so wrap it in a class that won't dispose it.
|
||||
return Task.FromResult<IAcquiredResource> (new NondisposedResource () { Wrapped = AcquiredResource });
|
||||
} else {
|
||||
return Jenkins.DesktopResource.AcquireExclusiveAsync ();
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task RunTestAsync ()
|
||||
{
|
||||
Jenkins.MainLog.WriteLine ("Running XI on '{0}' ({2}) for {1}", Device?.Name, ProjectFile, Device?.UDID);
|
||||
|
||||
ExecutionResult = ExecutionResult & ~TestExecutingResult.InProgressMask | TestExecutingResult.Running;
|
||||
await BuildTask.RunAsync ();
|
||||
if (!BuildTask.Succeeded) {
|
||||
ExecutionResult = TestExecutingResult.BuildFailure;
|
||||
return;
|
||||
}
|
||||
using (var resource = await NotifyBlockingWaitAsync (AcquireResourceAsync ())) {
|
||||
if (runner == null)
|
||||
await SelectSimulatorAsync ();
|
||||
await runner.RunAsync ();
|
||||
}
|
||||
ExecutionResult = runner.Result;
|
||||
|
||||
KnownFailure = null;
|
||||
if (Jenkins.IsHE0038Error (runner.MainLog))
|
||||
KnownFailure = $"<a href='https://github.com/xamarin/maccore/issues/581'>HE0038</a>";
|
||||
}
|
||||
|
||||
protected override string XIMode {
|
||||
get {
|
||||
return "simulator";
|
||||
}
|
||||
}
|
||||
|
||||
class NondisposedResource : IAcquiredResource
|
||||
{
|
||||
public IAcquiredResource Wrapped;
|
||||
|
||||
public Resource Resource {
|
||||
get {
|
||||
return Wrapped.Resource;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose ()
|
||||
{
|
||||
// Nope, no disposing here.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Xharness.Logging;
|
||||
|
||||
namespace Xharness.Jenkins.TestTasks
|
||||
{
|
||||
internal abstract class RunTestTask : TestTask
|
||||
{
|
||||
public readonly BuildToolTask BuildTask;
|
||||
public double TimeoutMultiplier { get; set; } = 1;
|
||||
|
||||
public RunTestTask (BuildToolTask build_task)
|
||||
{
|
||||
this.BuildTask = build_task;
|
||||
|
||||
Jenkins = build_task.Jenkins;
|
||||
TestProject = build_task.TestProject;
|
||||
Platform = build_task.Platform;
|
||||
ProjectPlatform = build_task.ProjectPlatform;
|
||||
ProjectConfiguration = build_task.ProjectConfiguration;
|
||||
if (build_task.HasCustomTestName)
|
||||
TestName = build_task.TestName;
|
||||
}
|
||||
|
||||
public override IEnumerable<Log> AggregatedLogs {
|
||||
get {
|
||||
var rv = base.AggregatedLogs;
|
||||
if (BuildTask != null)
|
||||
rv = rv.Union (BuildTask.AggregatedLogs);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
public override TestExecutingResult ExecutionResult {
|
||||
get {
|
||||
// When building, the result is the build result.
|
||||
if ((BuildTask.ExecutionResult & (TestExecutingResult.InProgress | TestExecutingResult.Waiting)) != 0)
|
||||
return BuildTask.ExecutionResult & ~TestExecutingResult.InProgressMask | TestExecutingResult.Building;
|
||||
return base.ExecutionResult;
|
||||
}
|
||||
set {
|
||||
base.ExecutionResult = value;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> BuildAsync ()
|
||||
{
|
||||
if (Finished)
|
||||
return true;
|
||||
|
||||
await VerifyBuildAsync ();
|
||||
if (Finished)
|
||||
return BuildTask.Succeeded;
|
||||
|
||||
ExecutionResult = TestExecutingResult.Building;
|
||||
await BuildTask.RunAsync ();
|
||||
if (!BuildTask.Succeeded) {
|
||||
if (BuildTask.TimedOut) {
|
||||
ExecutionResult = TestExecutingResult.TimedOut;
|
||||
} else {
|
||||
ExecutionResult = TestExecutingResult.BuildFailure;
|
||||
}
|
||||
FailureMessage = BuildTask.FailureMessage;
|
||||
if (Harness.InCI && BuildTask is XBuildTask projectTask)
|
||||
XmlResultParser.GenerateFailure (Logs, "build", projectTask.TestName, projectTask.Variation, "AppBuild", $"App could not be built {FailureMessage}.", projectTask.BuildLog.FullPath, Harness.XmlJargon);
|
||||
} else {
|
||||
ExecutionResult = TestExecutingResult.Built;
|
||||
}
|
||||
return BuildTask.Succeeded;
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync ()
|
||||
{
|
||||
if (Finished)
|
||||
return;
|
||||
|
||||
await VerifyRunAsync ();
|
||||
if (Finished)
|
||||
return;
|
||||
|
||||
if (!await BuildAsync ())
|
||||
return;
|
||||
|
||||
if (BuildOnly) {
|
||||
ExecutionResult = TestExecutingResult.BuildSucceeded;
|
||||
return;
|
||||
}
|
||||
|
||||
ExecutionResult = TestExecutingResult.Running;
|
||||
duration.Restart (); // don't count the build time.
|
||||
await RunTestAsync ();
|
||||
}
|
||||
|
||||
protected abstract Task RunTestAsync ();
|
||||
// VerifyBuild is called in BuildAsync to verify that the task can be built.
|
||||
// Typically used to fail tasks if there's not enough disk space.
|
||||
public virtual Task VerifyBuildAsync ()
|
||||
{
|
||||
return VerifyDiskSpaceAsync ();
|
||||
}
|
||||
|
||||
public override void Reset ()
|
||||
{
|
||||
base.Reset ();
|
||||
BuildTask.Reset ();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Xharness.Logging;
|
||||
|
||||
namespace Xharness.Jenkins.TestTasks
|
||||
{
|
||||
abstract class RunXITask<TDevice> : RunTestTask where TDevice : class, IDevice
|
||||
{
|
||||
public AppRunnerTarget AppRunnerTarget;
|
||||
|
||||
protected AppRunner runner;
|
||||
protected AppRunner additional_runner;
|
||||
|
||||
public IEnumerable<TDevice> Candidates { get; }
|
||||
|
||||
public TDevice Device { get; protected set; }
|
||||
|
||||
public TDevice CompanionDevice { get; protected set; }
|
||||
|
||||
public string BundleIdentifier {
|
||||
get { return runner.BundleIdentifier; }
|
||||
}
|
||||
|
||||
public RunXITask (BuildToolTask build_task, IEnumerable<TDevice> candidates)
|
||||
: base (build_task)
|
||||
{
|
||||
this.Candidates = candidates;
|
||||
}
|
||||
|
||||
public override IEnumerable<Log> AggregatedLogs {
|
||||
get {
|
||||
var rv = base.AggregatedLogs;
|
||||
if (runner != null)
|
||||
rv = rv.Union (runner.Logs);
|
||||
if (additional_runner != null)
|
||||
rv = rv.Union (additional_runner.Logs);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
public override string Mode {
|
||||
get {
|
||||
|
||||
switch (Platform) {
|
||||
case TestPlatform.tvOS:
|
||||
case TestPlatform.watchOS:
|
||||
return Platform.ToString () + " - " + XIMode;
|
||||
case TestPlatform.watchOS_32:
|
||||
return "watchOS 32-bits - " + XIMode;
|
||||
case TestPlatform.watchOS_64_32:
|
||||
return "watchOS 64-bits (ARM64_32) - " + XIMode;
|
||||
case TestPlatform.iOS_Unified32:
|
||||
return "iOS Unified 32-bits - " + XIMode;
|
||||
case TestPlatform.iOS_Unified64:
|
||||
return "iOS Unified 64-bits - " + XIMode;
|
||||
case TestPlatform.iOS_TodayExtension64:
|
||||
return "iOS Unified Today Extension 64-bits - " + XIMode;
|
||||
case TestPlatform.iOS_Unified:
|
||||
return "iOS Unified - " + XIMode;
|
||||
default:
|
||||
throw new NotImplementedException ();
|
||||
}
|
||||
}
|
||||
set { throw new NotImplementedException (); }
|
||||
}
|
||||
|
||||
public override async Task VerifyRunAsync ()
|
||||
{
|
||||
await base.VerifyRunAsync ();
|
||||
if (Finished)
|
||||
return;
|
||||
|
||||
var enumerable = Candidates;
|
||||
var asyncEnumerable = enumerable as IAsyncEnumerable;
|
||||
if (asyncEnumerable != null)
|
||||
await asyncEnumerable.ReadyTask;
|
||||
if (!enumerable.Any ()) {
|
||||
ExecutionResult = TestExecutingResult.DeviceNotFound;
|
||||
FailureMessage = "No applicable devices found.";
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract string XIMode { get; }
|
||||
|
||||
public override void Reset ()
|
||||
{
|
||||
base.Reset ();
|
||||
runner = null;
|
||||
additional_runner = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using Xharness.Logging;
|
||||
|
||||
namespace Xharness.Jenkins.TestTasks
|
||||
{
|
||||
class RunXtroTask : MacExecuteTask
|
||||
{
|
||||
|
||||
public string WorkingDirectory;
|
||||
|
||||
public RunXtroTask (BuildToolTask build_task) : base (build_task)
|
||||
{
|
||||
}
|
||||
|
||||
protected override async Task RunTestAsync ()
|
||||
{
|
||||
var projectDir = System.IO.Path.GetDirectoryName (ProjectFile);
|
||||
var name = System.IO.Path.GetFileName (projectDir);
|
||||
|
||||
using (var resource = await NotifyAndAcquireDesktopResourceAsync ()) {
|
||||
using (var proc = new Process ()) {
|
||||
proc.StartInfo.FileName = "/Library/Frameworks/Mono.framework/Commands/mono";
|
||||
var reporter = System.IO.Path.Combine (WorkingDirectory, "xtro-report/bin/Debug/xtro-report.exe");
|
||||
var results = System.IO.Path.Combine (Logs.Directory, $"xtro-{Timestamp}");
|
||||
proc.StartInfo.Arguments = $"--debug {reporter} {WorkingDirectory} {results}";
|
||||
|
||||
Jenkins.MainLog.WriteLine ("Executing {0} ({1})", TestName, Mode);
|
||||
var log = Logs.Create ($"execute-xtro-{Timestamp}.txt", LogType.ExecutionLog.ToString ());
|
||||
log.WriteLine ("{0} {1}", proc.StartInfo.FileName, proc.StartInfo.Arguments);
|
||||
if (!Harness.DryRun) {
|
||||
ExecutionResult = TestExecutingResult.Running;
|
||||
|
||||
var snapshot = new CrashReportSnapshot () { Device = false, Harness = Harness, Log = log, Logs = Logs, LogDirectory = LogDirectory };
|
||||
await snapshot.StartCaptureAsync ();
|
||||
|
||||
try {
|
||||
var timeout = TimeSpan.FromMinutes (20);
|
||||
|
||||
var result = await proc.RunAsync (log, true, timeout);
|
||||
if (result.TimedOut) {
|
||||
FailureMessage = $"Execution timed out after {timeout.TotalSeconds} seconds.";
|
||||
log.WriteLine (FailureMessage);
|
||||
ExecutionResult = TestExecutingResult.TimedOut;
|
||||
} else if (result.Succeeded) {
|
||||
ExecutionResult = TestExecutingResult.Succeeded;
|
||||
} else {
|
||||
ExecutionResult = TestExecutingResult.Failed;
|
||||
FailureMessage = result.ExitCode != 1 ? $"Test run crashed (exit code: {result.ExitCode})." : "Test run failed.";
|
||||
log.WriteLine (FailureMessage);
|
||||
}
|
||||
} finally {
|
||||
await snapshot.EndCaptureAsync (TimeSpan.FromSeconds (Succeeded ? 0 : 5));
|
||||
}
|
||||
}
|
||||
Jenkins.MainLog.WriteLine ("Executed {0} ({1})", TestName, Mode);
|
||||
|
||||
Logs.AddFile (System.IO.Path.Combine (results, "index.html"), "HTML Report");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,459 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
using Xamarin;
|
||||
using Xharness.Logging;
|
||||
|
||||
namespace Xharness.Jenkins.TestTasks
|
||||
{
|
||||
abstract class TestTask
|
||||
{
|
||||
static int counter;
|
||||
public readonly int ID = counter++;
|
||||
|
||||
bool? supports_parallel_execution;
|
||||
|
||||
public Jenkins Jenkins;
|
||||
public Harness Harness { get { return Jenkins.Harness; } }
|
||||
public TestProject TestProject;
|
||||
public string ProjectFile { get { return TestProject?.Path; } }
|
||||
public string ProjectConfiguration;
|
||||
public string ProjectPlatform;
|
||||
public Dictionary<string, string> Environment = new Dictionary<string, string> ();
|
||||
|
||||
public Func<Task> Dependency; // a task that's feteched and awaited before this task's ExecuteAsync method
|
||||
public Task InitialTask; // a task that's executed before this task's ExecuteAsync method.
|
||||
public Task CompletedTask; // a task that's executed after this task's ExecuteAsync method.
|
||||
|
||||
public bool BuildOnly;
|
||||
public string KnownFailure;
|
||||
|
||||
// VerifyRun is called in RunInternalAsync/ExecuteAsync to verify that the task can be executed/run.
|
||||
// Typically used to fail tasks that don't have an available device, or if there's not enough disk space.
|
||||
public virtual Task VerifyRunAsync ()
|
||||
{
|
||||
return VerifyDiskSpaceAsync ();
|
||||
}
|
||||
|
||||
static DriveInfo RootDrive;
|
||||
protected Task VerifyDiskSpaceAsync ()
|
||||
{
|
||||
if (Finished)
|
||||
return Task.CompletedTask;
|
||||
|
||||
if (RootDrive == null)
|
||||
RootDrive = new DriveInfo ("/");
|
||||
var afs = RootDrive.AvailableFreeSpace;
|
||||
const long minSpaceRequirement = 1024 * 1024 * 1024; /* 1 GB */
|
||||
if (afs < minSpaceRequirement) {
|
||||
FailureMessage = $"Not enough space on the root drive '{RootDrive.Name}': {afs / (1024.0 * 1024):#.##} MB left of {minSpaceRequirement / (1024.0 * 1024):#.##} MB required";
|
||||
ExecutionResult = TestExecutingResult.Failed;
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void CloneTestProject (TestProject project)
|
||||
{
|
||||
// Don't build in the original project directory
|
||||
// We can build multiple projects in parallel, and if some of those
|
||||
// projects have the same project dependencies, then we may end up
|
||||
// building the same (dependent) project simultaneously (and they can
|
||||
// stomp on eachother).
|
||||
// So we clone the project file to a separate directory and build there instead.
|
||||
// This is done asynchronously to speed to the initial test load.
|
||||
TestProject = project.Clone ();
|
||||
InitialTask = TestProject.CreateCopyAsync ();
|
||||
}
|
||||
|
||||
protected Stopwatch duration = new Stopwatch ();
|
||||
public TimeSpan Duration {
|
||||
get {
|
||||
return duration.Elapsed;
|
||||
}
|
||||
}
|
||||
|
||||
protected Stopwatch waitingDuration = new Stopwatch ();
|
||||
public TimeSpan WaitingDuration => waitingDuration.Elapsed;
|
||||
|
||||
TestExecutingResult execution_result;
|
||||
public virtual TestExecutingResult ExecutionResult {
|
||||
get {
|
||||
return execution_result;
|
||||
}
|
||||
set {
|
||||
execution_result = value;
|
||||
}
|
||||
}
|
||||
|
||||
string failure_message;
|
||||
public string FailureMessage {
|
||||
get { return failure_message; }
|
||||
set {
|
||||
failure_message = value;
|
||||
MainLog.WriteLine (failure_message);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual string ProgressMessage { get; }
|
||||
|
||||
public bool NotStarted { get { return (ExecutionResult & TestExecutingResult.StateMask) == TestExecutingResult.NotStarted; } }
|
||||
public bool InProgress { get { return (ExecutionResult & TestExecutingResult.InProgress) == TestExecutingResult.InProgress; } }
|
||||
public bool Waiting { get { return (ExecutionResult & TestExecutingResult.Waiting) == TestExecutingResult.Waiting; } }
|
||||
public bool Finished { get { return (ExecutionResult & TestExecutingResult.Finished) == TestExecutingResult.Finished; } }
|
||||
|
||||
public bool Building { get { return (ExecutionResult & TestExecutingResult.Building) == TestExecutingResult.Building; } }
|
||||
public bool Built { get { return (ExecutionResult & TestExecutingResult.Built) == TestExecutingResult.Built; } }
|
||||
public bool Running { get { return (ExecutionResult & TestExecutingResult.Running) == TestExecutingResult.Running; } }
|
||||
|
||||
public bool BuildSucceeded { get { return (ExecutionResult & TestExecutingResult.BuildSucceeded) == TestExecutingResult.BuildSucceeded; } }
|
||||
public bool Succeeded { get { return (ExecutionResult & TestExecutingResult.Succeeded) == TestExecutingResult.Succeeded; } }
|
||||
public bool Failed { get { return (ExecutionResult & TestExecutingResult.Failed) == TestExecutingResult.Failed; } }
|
||||
public bool Ignored {
|
||||
get { return ExecutionResult == TestExecutingResult.Ignored; }
|
||||
set {
|
||||
if (ExecutionResult != TestExecutingResult.NotStarted && ExecutionResult != TestExecutingResult.Ignored)
|
||||
throw new InvalidOperationException ();
|
||||
ExecutionResult = value ? TestExecutingResult.Ignored : TestExecutingResult.NotStarted;
|
||||
}
|
||||
}
|
||||
public bool DeviceNotFound { get { return ExecutionResult == TestExecutingResult.DeviceNotFound; } }
|
||||
|
||||
public bool Crashed { get { return (ExecutionResult & TestExecutingResult.Crashed) == TestExecutingResult.Crashed; } }
|
||||
public bool TimedOut { get { return (ExecutionResult & TestExecutingResult.TimedOut) == TestExecutingResult.TimedOut; } }
|
||||
public bool BuildFailure { get { return (ExecutionResult & TestExecutingResult.BuildFailure) == TestExecutingResult.BuildFailure; } }
|
||||
public bool HarnessException { get { return (ExecutionResult & TestExecutingResult.HarnessException) == TestExecutingResult.HarnessException; } }
|
||||
|
||||
public virtual string Mode { get; set; }
|
||||
public virtual string Variation { get; set; }
|
||||
|
||||
protected static string Timestamp {
|
||||
get {
|
||||
return Harness.Timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasCustomTestName {
|
||||
get {
|
||||
return test_name != null;
|
||||
}
|
||||
}
|
||||
|
||||
string test_name;
|
||||
public virtual string TestName {
|
||||
get {
|
||||
if (test_name != null)
|
||||
return test_name;
|
||||
|
||||
var rv = Path.GetFileNameWithoutExtension (ProjectFile);
|
||||
if (rv == null)
|
||||
return $"unknown test name ({GetType ().Name}";
|
||||
switch (Platform) {
|
||||
case TestPlatform.Mac:
|
||||
return rv;
|
||||
case TestPlatform.Mac_Modern:
|
||||
return rv;//.Substring (0, rv.Length - "-unified".Length);
|
||||
case TestPlatform.Mac_Full:
|
||||
return rv.Substring (0, rv.Length - "-full".Length);
|
||||
case TestPlatform.Mac_System:
|
||||
return rv.Substring (0, rv.Length - "-system".Length);
|
||||
default:
|
||||
if (rv.EndsWith ("-watchos", StringComparison.Ordinal)) {
|
||||
return rv.Substring (0, rv.Length - 8);
|
||||
} else if (rv.EndsWith ("-tvos", StringComparison.Ordinal)) {
|
||||
return rv.Substring (0, rv.Length - 5);
|
||||
} else if (rv.EndsWith ("-unified", StringComparison.Ordinal)) {
|
||||
return rv.Substring (0, rv.Length - 8);
|
||||
} else if (rv.EndsWith ("-today", StringComparison.Ordinal)) {
|
||||
return rv.Substring (0, rv.Length - 6);
|
||||
} else {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
set {
|
||||
test_name = value;
|
||||
}
|
||||
}
|
||||
|
||||
public TestPlatform Platform { get; set; }
|
||||
|
||||
public List<Resource> Resources = new List<Resource> ();
|
||||
|
||||
ILog test_log;
|
||||
public ILog MainLog {
|
||||
get {
|
||||
if (test_log == null)
|
||||
test_log = Logs.Create ($"main-{Timestamp}.log", "Main log");
|
||||
return test_log;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IEnumerable<Log> AggregatedLogs {
|
||||
get {
|
||||
return Logs;
|
||||
}
|
||||
}
|
||||
|
||||
public string LogDirectory {
|
||||
get {
|
||||
var rv = Path.Combine (Jenkins.LogDirectory, TestName, ID.ToString ());
|
||||
Directory.CreateDirectory (rv);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
ILogs logs;
|
||||
public ILogs Logs {
|
||||
get {
|
||||
return logs ?? (logs = new Logs (LogDirectory));
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<string> referencedNunitAndXunitTestAssemblies;
|
||||
public IEnumerable<string> ReferencedNunitAndXunitTestAssemblies {
|
||||
get {
|
||||
if (referencedNunitAndXunitTestAssemblies != null)
|
||||
return referencedNunitAndXunitTestAssemblies;
|
||||
|
||||
if (TestName.Contains ("BCL tests group")) { // avoid loading unrelated projects
|
||||
if (!File.Exists (ProjectFile))
|
||||
return Enumerable.Empty<string> ();
|
||||
|
||||
var csproj = new XmlDocument ();
|
||||
try {
|
||||
csproj.LoadWithoutNetworkAccess (ProjectFile.Replace ("\\", "/"));
|
||||
referencedNunitAndXunitTestAssemblies = csproj.GetNunitAndXunitTestReferences ();
|
||||
} catch (Exception e) {
|
||||
referencedNunitAndXunitTestAssemblies = new string [] { $"Exception: {e.Message}", $"Filename: {ProjectFile}" };
|
||||
}
|
||||
} else {
|
||||
referencedNunitAndXunitTestAssemblies = Enumerable.Empty<string> ();
|
||||
}
|
||||
return referencedNunitAndXunitTestAssemblies;
|
||||
}
|
||||
}
|
||||
|
||||
Task execute_task;
|
||||
async Task RunInternalAsync ()
|
||||
{
|
||||
if (Finished)
|
||||
return;
|
||||
|
||||
ExecutionResult = ExecutionResult & ~TestExecutingResult.StateMask | TestExecutingResult.InProgress;
|
||||
|
||||
try {
|
||||
if (Dependency != null)
|
||||
await Dependency ();
|
||||
|
||||
if (InitialTask != null)
|
||||
await InitialTask;
|
||||
|
||||
await VerifyRunAsync ();
|
||||
if (Finished)
|
||||
return;
|
||||
|
||||
duration.Start ();
|
||||
|
||||
execute_task = ExecuteAsync ();
|
||||
await execute_task;
|
||||
|
||||
if (CompletedTask != null) {
|
||||
if (CompletedTask.Status == TaskStatus.Created)
|
||||
CompletedTask.Start ();
|
||||
await CompletedTask;
|
||||
}
|
||||
|
||||
ExecutionResult = ExecutionResult & ~TestExecutingResult.StateMask | TestExecutingResult.Finished;
|
||||
if ((ExecutionResult & ~TestExecutingResult.StateMask) == 0)
|
||||
throw new Exception ("Result not set!");
|
||||
} catch (Exception e) {
|
||||
using (var log = Logs.Create ($"execution-failure-{Timestamp}.log", "Execution failure")) {
|
||||
ExecutionResult = TestExecutingResult.HarnessException;
|
||||
FailureMessage = $"Harness exception for '{TestName}': {e}";
|
||||
log.WriteLine (FailureMessage);
|
||||
}
|
||||
PropagateResults ();
|
||||
} finally {
|
||||
logs?.Dispose ();
|
||||
duration.Stop ();
|
||||
}
|
||||
|
||||
Jenkins.GenerateReport ();
|
||||
}
|
||||
|
||||
protected virtual void PropagateResults ()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void Reset ()
|
||||
{
|
||||
test_log = null;
|
||||
failure_message = null;
|
||||
logs = null;
|
||||
duration.Reset ();
|
||||
execution_result = TestExecutingResult.NotStarted;
|
||||
execute_task = null;
|
||||
}
|
||||
|
||||
public Task RunAsync ()
|
||||
{
|
||||
if (execute_task == null)
|
||||
execute_task = RunInternalAsync ();
|
||||
return execute_task;
|
||||
}
|
||||
|
||||
protected abstract Task ExecuteAsync ();
|
||||
|
||||
public override string ToString ()
|
||||
{
|
||||
return ExecutionResult.ToString ();
|
||||
}
|
||||
|
||||
protected void SetEnvironmentVariables (Process process)
|
||||
{
|
||||
var xcodeRoot = Harness.XcodeRoot;
|
||||
|
||||
switch (Platform) {
|
||||
case TestPlatform.iOS:
|
||||
case TestPlatform.iOS_Unified:
|
||||
case TestPlatform.iOS_Unified32:
|
||||
case TestPlatform.iOS_Unified64:
|
||||
case TestPlatform.iOS_TodayExtension64:
|
||||
case TestPlatform.tvOS:
|
||||
case TestPlatform.watchOS:
|
||||
case TestPlatform.watchOS_32:
|
||||
case TestPlatform.watchOS_64_32:
|
||||
process.StartInfo.EnvironmentVariables ["MD_APPLE_SDK_ROOT"] = xcodeRoot;
|
||||
process.StartInfo.EnvironmentVariables ["MD_MTOUCH_SDK_ROOT"] = Path.Combine (Harness.IOS_DESTDIR, "Library", "Frameworks", "Xamarin.iOS.framework", "Versions", "Current");
|
||||
process.StartInfo.EnvironmentVariables ["TargetFrameworkFallbackSearchPaths"] = Path.Combine (Harness.IOS_DESTDIR, "Library", "Frameworks", "Mono.framework", "External", "xbuild-frameworks");
|
||||
process.StartInfo.EnvironmentVariables ["MSBuildExtensionsPathFallbackPathsOverride"] = Path.Combine (Harness.IOS_DESTDIR, "Library", "Frameworks", "Mono.framework", "External", "xbuild");
|
||||
break;
|
||||
case TestPlatform.Mac:
|
||||
case TestPlatform.Mac_Modern:
|
||||
case TestPlatform.Mac_Full:
|
||||
case TestPlatform.Mac_System:
|
||||
process.StartInfo.EnvironmentVariables ["MD_APPLE_SDK_ROOT"] = xcodeRoot;
|
||||
process.StartInfo.EnvironmentVariables ["TargetFrameworkFallbackSearchPaths"] = Path.Combine (Harness.MAC_DESTDIR, "Library", "Frameworks", "Mono.framework", "External", "xbuild-frameworks");
|
||||
process.StartInfo.EnvironmentVariables ["MSBuildExtensionsPathFallbackPathsOverride"] = Path.Combine (Harness.MAC_DESTDIR, "Library", "Frameworks", "Mono.framework", "External", "xbuild");
|
||||
process.StartInfo.EnvironmentVariables ["XamarinMacFrameworkRoot"] = Path.Combine (Harness.MAC_DESTDIR, "Library", "Frameworks", "Xamarin.Mac.framework", "Versions", "Current");
|
||||
process.StartInfo.EnvironmentVariables ["XAMMAC_FRAMEWORK_PATH"] = Path.Combine (Harness.MAC_DESTDIR, "Library", "Frameworks", "Xamarin.Mac.framework", "Versions", "Current");
|
||||
break;
|
||||
case TestPlatform.All:
|
||||
// Don't set:
|
||||
// MSBuildExtensionsPath
|
||||
// TargetFrameworkFallbackSearchPaths
|
||||
// because these values used by both XM and XI and we can't set it to two different values at the same time.
|
||||
// Any test that depends on these values should not be using 'TestPlatform.All'
|
||||
process.StartInfo.EnvironmentVariables ["MD_APPLE_SDK_ROOT"] = xcodeRoot;
|
||||
process.StartInfo.EnvironmentVariables ["MD_MTOUCH_SDK_ROOT"] = Path.Combine (Harness.IOS_DESTDIR, "Library", "Frameworks", "Xamarin.iOS.framework", "Versions", "Current");
|
||||
process.StartInfo.EnvironmentVariables ["XamarinMacFrameworkRoot"] = Path.Combine (Harness.MAC_DESTDIR, "Library", "Frameworks", "Xamarin.Mac.framework", "Versions", "Current");
|
||||
process.StartInfo.EnvironmentVariables ["XAMMAC_FRAMEWORK_PATH"] = Path.Combine (Harness.MAC_DESTDIR, "Library", "Frameworks", "Xamarin.Mac.framework", "Versions", "Current");
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException ();
|
||||
}
|
||||
|
||||
foreach (var kvp in Environment)
|
||||
process.StartInfo.EnvironmentVariables [kvp.Key] = kvp.Value;
|
||||
}
|
||||
|
||||
protected void AddWrenchLogFiles (StreamReader stream)
|
||||
{
|
||||
string line;
|
||||
while ((line = stream.ReadLine ()) != null) {
|
||||
if (!line.StartsWith ("@MonkeyWrench: ", StringComparison.Ordinal))
|
||||
continue;
|
||||
|
||||
var cmd = line.Substring ("@MonkeyWrench:".Length).TrimStart ();
|
||||
var colon = cmd.IndexOf (':');
|
||||
if (colon <= 0)
|
||||
continue;
|
||||
var name = cmd.Substring (0, colon);
|
||||
switch (name) {
|
||||
case "AddFile":
|
||||
var src = cmd.Substring (name.Length + 1).Trim ();
|
||||
Logs.AddFile (src);
|
||||
break;
|
||||
default:
|
||||
Harness.HarnessLog.WriteLine ("Unknown @MonkeyWrench command in {0}: {1}", TestName, name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void LogEvent (ILog log, string text, params object [] args)
|
||||
{
|
||||
Jenkins.MainLog.WriteLine (text, args);
|
||||
log.WriteLine (text, args);
|
||||
}
|
||||
|
||||
public string GuessFailureReason (ILog log)
|
||||
{
|
||||
try {
|
||||
using (var reader = log.GetReader ()) {
|
||||
string line;
|
||||
var error_msg = new System.Text.RegularExpressions.Regex ("([A-Z][A-Z][0-9][0-9][0-9][0-9]:.*)");
|
||||
while ((line = reader.ReadLine ()) != null) {
|
||||
var match = error_msg.Match (line);
|
||||
if (match.Success)
|
||||
return match.Groups [1].Captures [0].Value;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Harness.Log ("Failed to guess failure reason: {0}", e.Message);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// This method will set (and clear) the Waiting flag correctly while waiting on a resource
|
||||
// It will also pause the duration.
|
||||
public async Task<IAcquiredResource> NotifyBlockingWaitAsync (Task<IAcquiredResource> task)
|
||||
{
|
||||
var rv = new BlockingWait ();
|
||||
|
||||
// Stop the timer while we're waiting for a resource
|
||||
duration.Stop ();
|
||||
waitingDuration.Start ();
|
||||
ExecutionResult = ExecutionResult | TestExecutingResult.Waiting;
|
||||
rv.Wrapped = await task;
|
||||
ExecutionResult = ExecutionResult & ~TestExecutingResult.Waiting;
|
||||
waitingDuration.Stop ();
|
||||
duration.Start ();
|
||||
rv.OnDispose = duration.Stop;
|
||||
return rv;
|
||||
}
|
||||
|
||||
public virtual bool SupportsParallelExecution {
|
||||
get {
|
||||
return supports_parallel_execution ?? true;
|
||||
}
|
||||
set {
|
||||
supports_parallel_execution = value;
|
||||
}
|
||||
}
|
||||
|
||||
protected Task<IAcquiredResource> NotifyAndAcquireDesktopResourceAsync ()
|
||||
{
|
||||
return NotifyBlockingWaitAsync (SupportsParallelExecution ? Jenkins.DesktopResource.AcquireConcurrentAsync () : Jenkins.DesktopResource.AcquireExclusiveAsync ());
|
||||
}
|
||||
|
||||
class BlockingWait : IAcquiredResource, IDisposable
|
||||
{
|
||||
public IAcquiredResource Wrapped;
|
||||
public Action OnDispose;
|
||||
|
||||
public Resource Resource { get { return Wrapped.Resource; } }
|
||||
|
||||
public void Dispose ()
|
||||
{
|
||||
OnDispose ();
|
||||
Wrapped.Dispose ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
using Xamarin;
|
||||
using Xamarin.Utils;
|
||||
using Xharness.Logging;
|
||||
|
||||
namespace Xharness.Jenkins.TestTasks
|
||||
{
|
||||
class XBuildTask : BuildProjectTask
|
||||
{
|
||||
public bool UseMSBuild;
|
||||
public ILog BuildLog;
|
||||
|
||||
protected override async Task ExecuteAsync ()
|
||||
{
|
||||
using (var resource = await NotifyAndAcquireDesktopResourceAsync ()) {
|
||||
BuildLog = Logs.Create ($"build-{Platform}-{Timestamp}.txt", LogType.BuildLog.ToString ());
|
||||
var binlogPath = BuildLog.FullPath.Replace (".txt", ".binlog");
|
||||
|
||||
await RestoreNugetsAsync (BuildLog, resource, useXIBuild: true);
|
||||
|
||||
using (var xbuild = new Process ()) {
|
||||
xbuild.StartInfo.FileName = Harness.XIBuildPath;
|
||||
var args = new List<string> ();
|
||||
args.Add ("--");
|
||||
args.Add ("/verbosity:diagnostic");
|
||||
args.Add ($"/bl:{binlogPath}");
|
||||
if (SpecifyPlatform)
|
||||
args.Add ($"/p:Platform={ProjectPlatform}");
|
||||
if (SpecifyConfiguration)
|
||||
args.Add ($"/p:Configuration={ProjectConfiguration}");
|
||||
args.Add (ProjectFile);
|
||||
xbuild.StartInfo.Arguments = StringUtils.FormatArguments (args);
|
||||
SetEnvironmentVariables (xbuild);
|
||||
if (UseMSBuild)
|
||||
xbuild.StartInfo.EnvironmentVariables ["MSBuildExtensionsPath"] = null;
|
||||
LogEvent (BuildLog, "Building {0} ({1})", TestName, Mode);
|
||||
if (!Harness.DryRun) {
|
||||
var timeout = TimeSpan.FromMinutes (60);
|
||||
var result = await xbuild.RunAsync (BuildLog, true, timeout);
|
||||
if (result.TimedOut) {
|
||||
ExecutionResult = TestExecutingResult.TimedOut;
|
||||
BuildLog.WriteLine ("Build timed out after {0} seconds.", timeout.TotalSeconds);
|
||||
} else if (result.Succeeded) {
|
||||
ExecutionResult = TestExecutingResult.Succeeded;
|
||||
} else {
|
||||
ExecutionResult = TestExecutingResult.Failed;
|
||||
}
|
||||
}
|
||||
Jenkins.MainLog.WriteLine ("Built {0} ({1})", TestName, Mode);
|
||||
}
|
||||
|
||||
BuildLog.Dispose ();
|
||||
}
|
||||
}
|
||||
|
||||
async Task CleanProjectAsync (ILog log, string project_file, string project_platform, string project_configuration)
|
||||
{
|
||||
// Don't require the desktop resource here, this shouldn't be that resource sensitive
|
||||
using (var xbuild = new Process ()) {
|
||||
xbuild.StartInfo.FileName = Harness.XIBuildPath;
|
||||
var args = new List<string> ();
|
||||
args.Add ("--");
|
||||
args.Add ("/verbosity:diagnostic");
|
||||
if (project_platform != null)
|
||||
args.Add ($"/p:Platform={project_platform}");
|
||||
if (project_configuration != null)
|
||||
args.Add ($"/p:Configuration={project_configuration}");
|
||||
args.Add (project_file);
|
||||
args.Add ("/t:Clean");
|
||||
xbuild.StartInfo.Arguments = StringUtils.FormatArguments (args);
|
||||
SetEnvironmentVariables (xbuild);
|
||||
LogEvent (log, "Cleaning {0} ({1}) - {2}", TestName, Mode, project_file);
|
||||
var timeout = TimeSpan.FromMinutes (1);
|
||||
await xbuild.RunAsync (log, true, timeout);
|
||||
log.WriteLine ("Clean timed out after {0} seconds.", timeout.TotalSeconds);
|
||||
Jenkins.MainLog.WriteLine ("Cleaned {0} ({1})", TestName, Mode);
|
||||
}
|
||||
}
|
||||
|
||||
public async override Task CleanAsync ()
|
||||
{
|
||||
var log = Logs.Create ($"clean-{Platform}-{Timestamp}.txt", "Clean log");
|
||||
await CleanProjectAsync (log, ProjectFile, SpecifyPlatform ? ProjectPlatform : null, SpecifyConfiguration ? ProjectConfiguration : null);
|
||||
|
||||
// Iterate over all the project references as well.
|
||||
var doc = new XmlDocument ();
|
||||
doc.LoadWithoutNetworkAccess (ProjectFile);
|
||||
foreach (var pr in doc.GetProjectReferences ()) {
|
||||
var path = pr.Replace ('\\', '/');
|
||||
await CleanProjectAsync (log, path, SpecifyPlatform ? ProjectPlatform : null, SpecifyConfiguration ? ProjectConfiguration : null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Xharness;
|
||||
|
||||
namespace Xharness
|
||||
{
|
||||
|
|
|
@ -24,4 +24,5 @@ using System.Runtime.CompilerServices;
|
|||
|
||||
//[assembly: AssemblyDelaySign(false)]
|
||||
//[assembly: AssemblyKeyFile("")]
|
||||
[assembly: InternalsVisibleTo("Xharness.Tests")]
|
||||
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
using System;
|
||||
|
||||
namespace Xharness
|
||||
{
|
||||
[Flags]
|
||||
public enum TestExecutingResult
|
||||
{
|
||||
NotStarted = 0,
|
||||
InProgress = 0x1,
|
||||
Finished = 0x2,
|
||||
Waiting = 0x4,
|
||||
StateMask = NotStarted + InProgress + Waiting + Finished,
|
||||
|
||||
// In progress state
|
||||
Building = 0x10 + InProgress,
|
||||
BuildQueued = 0x10 + InProgress + Waiting,
|
||||
Built = 0x20 + InProgress,
|
||||
Running = 0x40 + InProgress,
|
||||
RunQueued = 0x40 + InProgress + Waiting,
|
||||
InProgressMask = 0x10 + 0x20 + 0x40,
|
||||
|
||||
// Finished results
|
||||
Succeeded = 0x100 + Finished,
|
||||
Failed = 0x200 + Finished,
|
||||
Ignored = 0x400 + Finished,
|
||||
DeviceNotFound = 0x800 + Finished,
|
||||
|
||||
// Finished & Failed results
|
||||
Crashed = 0x1000 + Failed,
|
||||
TimedOut = 0x2000 + Failed,
|
||||
HarnessException = 0x4000 + Failed,
|
||||
BuildFailure = 0x8000 + Failed,
|
||||
|
||||
// Other results
|
||||
BuildSucceeded = 0x10000 + Succeeded,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
namespace Xharness
|
||||
{
|
||||
public enum TestPlatform
|
||||
{
|
||||
None,
|
||||
All,
|
||||
|
||||
iOS,
|
||||
iOS_Unified,
|
||||
iOS_Unified32,
|
||||
iOS_Unified64,
|
||||
iOS_TodayExtension64,
|
||||
tvOS,
|
||||
watchOS,
|
||||
watchOS_32,
|
||||
watchOS_64_32,
|
||||
|
||||
Mac,
|
||||
Mac_Modern,
|
||||
Mac_Full,
|
||||
Mac_System,
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ using System.Linq;
|
|||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
using Xamarin;
|
||||
using Xharness.Jenkins.TestTasks;
|
||||
|
||||
namespace Xharness
|
||||
{
|
||||
|
|
|
@ -5,11 +5,10 @@ using System.Linq;
|
|||
using System.Xml.Linq;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Xharness;
|
||||
using Xharness.Logging;
|
||||
using static Xharness.XmlResultParser;
|
||||
|
||||
namespace Xharness.Tests {
|
||||
namespace Xharness.Tests
|
||||
{
|
||||
|
||||
[TestFixture]
|
||||
public class XmlResultParserTests {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\..\packages\NUnit.3.12.0\build\NUnit.props" Condition="Exists('..\..\..\packages\NUnit.3.12.0\build\NUnit.props')" />
|
||||
<PropertyGroup>
|
||||
|
@ -8,7 +8,7 @@
|
|||
<OutputType>Library</OutputType>
|
||||
<RootNamespace>Xharness.Tests</RootNamespace>
|
||||
<AssemblyName>Xharness.Tests</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
|
@ -26,177 +26,39 @@
|
|||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="nunit.framework">
|
||||
<Reference Include="Castle.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\packages\Castle.Core.4.4.0\lib\net45\Castle.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Moq, Version=4.13.0.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\packages\Moq.4.13.1\lib\net45\Moq.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=3.12.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\packages\NUnit.3.12.0\lib\net45\nunit.framework.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\packages\System.Runtime.CompilerServices.Unsafe.4.5.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\packages\System.Threading.Tasks.Extensions.4.5.1\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Web" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Runtime.CompilerServices.Unsafe">
|
||||
<HintPath>..\..\..\packages\System.Runtime.CompilerServices.Unsafe.4.5.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Threading.Tasks.Extensions">
|
||||
<HintPath>..\..\..\packages\System.Threading.Tasks.Extensions.4.5.1\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Castle.Core">
|
||||
<HintPath>..\..\..\packages\Castle.Core.4.4.0\lib\net45\Castle.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Moq">
|
||||
<HintPath>..\..\..\packages\Moq.4.13.1\lib\net45\Moq.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\AppRunner.cs">
|
||||
<Link>AppRunner.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\DeviceLogCapturer.cs">
|
||||
<Link>DeviceLogCapturer.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Extensions.cs">
|
||||
<Link>Extensions.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\GitHub.cs">
|
||||
<Link>GitHub.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Harness.cs">
|
||||
<Link>Harness.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\iOSTarget.cs">
|
||||
<Link>iOSTarget.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Jenkins.cs">
|
||||
<Link>Jenkins.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MacTarget.cs">
|
||||
<Link>MacTarget.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MakefileGenerator.cs">
|
||||
<Link>MakefileGenerator.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MonoNativeInfo.cs">
|
||||
<Link>MonoNativeInfo.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Process_Extensions.cs">
|
||||
<Link>Process_Extensions.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\ProjectFileExtensions.cs">
|
||||
<Link>ProjectFileExtensions.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Simulators.cs">
|
||||
<Link>Simulators.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\SolutionGenerator.cs">
|
||||
<Link>SolutionGenerator.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Target.cs">
|
||||
<Link>Target.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\TestProject.cs">
|
||||
<Link>TestProject.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\TodayExtensionTarget.cs">
|
||||
<Link>TodayExtensionTarget.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\TVOSTarget.cs">
|
||||
<Link>TVOSTarget.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\UnifiedTarget.cs">
|
||||
<Link>UnifiedTarget.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\WatchOSTarget.cs">
|
||||
<Link>WatchOSTarget.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\XmlResultParser.cs">
|
||||
<Link>XmlResultParser.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\BCLTestImporter\BCLTestAssemblyDefinition.cs">
|
||||
<Link>BCLTestImporter\BCLTestAssemblyDefinition.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\BCLTestImporter\BCLTestImportTargetFactory.cs">
|
||||
<Link>BCLTestImporter\BCLTestImportTargetFactory.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\BCLTestImporter\BCLTestInfoPlistGenerator.cs">
|
||||
<Link>BCLTestImporter\BCLTestInfoPlistGenerator.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\BCLTestImporter\BCLTestProjectDefinition.cs">
|
||||
<Link>BCLTestImporter\BCLTestProjectDefinition.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\BCLTestImporter\BCLTestProjectGenerator.cs">
|
||||
<Link>BCLTestImporter\BCLTestProjectGenerator.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\BCLTestImporter\Platform.cs">
|
||||
<Link>BCLTestImporter\Platform.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\BCLTestImporter\RegisterTypeGenerator.cs">
|
||||
<Link>BCLTestImporter\RegisterTypeGenerator.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\mtouch\Cache.cs">
|
||||
<Link>Cache.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\..\tools\common\PListExtensions.cs">
|
||||
<Link>PListExtensions.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\..\tools\common\StringUtils.cs">
|
||||
<Link>StringUtils.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\..\tools\mtouch\SdkVersions.cs">
|
||||
<Link>SdkVersions.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="BCLTestImporter\Tests\BCLTestInfoPlistGeneratorTest.cs" />
|
||||
<Compile Include="BCLTestImporter\Tests\BCLTestProjectGeneratorTest.cs" />
|
||||
<Compile Include="BCLTestImporter\Tests\TestAssemblyDefinitionTest.cs" />
|
||||
<Compile Include="BCLTestImporter\Tests\TestProjectDefinitionTest.cs" />
|
||||
<Compile Include="Tests\XmlResultParserTests.cs" />
|
||||
<Compile Include="..\Logging\ILogs.cs">
|
||||
<Link>Logging\ILogs.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Logging\Logs.cs">
|
||||
<Link>Logging\Logs.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Logging\Log.cs">
|
||||
<Link>Logging\Log.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Logging\ILog.cs">
|
||||
<Link>Logging\ILog.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Logging\ILogFile.cs">
|
||||
<Link>Logging\ILogFile.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="Logging\Tests\LogsTest.cs" />
|
||||
<Compile Include="..\Logging\LogFile.cs">
|
||||
<Link>Logging\LogFile.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="Logging\Tests\LogFileTest.cs" />
|
||||
<Compile Include="Logging\Tests\ConsoleLogTest.cs" />
|
||||
<Compile Include="..\Logging\ConsoleLog.cs">
|
||||
<Link>Logging\ConsoleLog.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Logging\CallbackLog.cs">
|
||||
<Link>Logging\CallbackLog.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="Logging\Tests\CallbackLogTest.cs" />
|
||||
<Compile Include="..\Logging\CaptureLog.cs">
|
||||
<Link>Logging\CaptureLog.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="Logging\Tests\CaptureLogTest.cs" />
|
||||
<Compile Include="..\Listeners\SimpleFileListener.cs">
|
||||
<Link>Listeners\SimpleFileListener.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Listeners\SimpleHttpListener.cs">
|
||||
<Link>Listeners\SimpleHttpListener.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Listeners\SimpleListener.cs">
|
||||
<Link>Listeners\SimpleListener.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Listeners\SimpleTcpListener.cs">
|
||||
<Link>Listeners\SimpleTcpListener.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Listeners\SimpleListenerFactory.cs">
|
||||
<Link>Listeners\SimpleListenerFactory.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="Listeners\Tests\SimpleListenerFactoryTest.cs" />
|
||||
<Compile Include="Listeners\Tests\SimpleFileListenerTest.cs" />
|
||||
<Compile Include="Listeners\Tests\SimpleTcpListenerTest.cs" />
|
||||
|
@ -204,21 +66,23 @@
|
|||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="BCLTestImporter\" />
|
||||
<Folder Include="BCLTestImporter\Tests\" />
|
||||
<Folder Include="Tests\" />
|
||||
<Folder Include="Samples\" />
|
||||
<Folder Include="Logging\" />
|
||||
<Folder Include="Logging\Tests\" />
|
||||
<Folder Include="Listeners\" />
|
||||
<Folder Include="Listeners\Tests\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Samples\NUnitV2Sample.xml" />
|
||||
<EmbeddedResource Include="Samples\NUnitV3Sample.xml" />
|
||||
<EmbeddedResource Include="Samples\TouchUnitSample.xml" />
|
||||
<EmbeddedResource Include="Samples\xUnitSample.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\xharness.csproj">
|
||||
<Project>{e1f53f80-8399-499b-8017-c414b9cd263b}</Project>
|
||||
<Name>xharness</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\packages\NUnit.3.12.0\build\NUnit.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\NUnit.3.12.0\build\NUnit.props'))" />
|
||||
</Target>
|
||||
</Project>
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Castle.Core" version="4.4.0" targetFramework="net48" />
|
||||
<package id="Moq" version="4.13.1" targetFramework="net48" />
|
||||
<package id="NUnit" version="3.12.0" targetFramework="net47" />
|
||||
<package id="System.Runtime.CompilerServices.Unsafe" version="4.5.0" targetFramework="net47" />
|
||||
<package id="System.Threading.Tasks.Extensions" version="4.5.1" targetFramework="net47" />
|
||||
<package id="Castle.Core" version="4.4.0" targetFramework="net472" />
|
||||
<package id="Moq" version="4.13.1" targetFramework="net472" />
|
||||
<package id="NUnit" version="3.12.0" targetFramework="net472" />
|
||||
<package id="System.Runtime.CompilerServices.Unsafe" version="4.5.0" targetFramework="net472" />
|
||||
<package id="System.Threading.Tasks.Extensions" version="4.5.1" targetFramework="net472" />
|
||||
</packages>
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
|
@ -70,6 +70,24 @@
|
|||
<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\XBuildTask.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Harness.cs" />
|
||||
|
@ -89,7 +107,7 @@
|
|||
<Compile Include="MakefileGenerator.cs" />
|
||||
<Compile Include="SolutionGenerator.cs" />
|
||||
<Compile Include="MacTarget.cs" />
|
||||
<Compile Include="Jenkins.cs" />
|
||||
<Compile Include="Jenkins\Jenkins.cs" />
|
||||
<Compile Include="Process_Extensions.cs" />
|
||||
<Compile Include="Simulators.cs" />
|
||||
<Compile Include="TestProject.cs" />
|
||||
|
@ -141,4 +159,4 @@
|
|||
</Content>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
</Project>
|
Загрузка…
Ссылка в новой задаче