diff --git a/tests/xharness/Jenkins/Jenkins.cs b/tests/xharness/Jenkins/Jenkins.cs index a7e4a461474e..f1e3fc6676b8 100644 --- a/tests/xharness/Jenkins/Jenkins.cs +++ b/tests/xharness/Jenkins/Jenkins.cs @@ -15,7 +15,7 @@ using Xharness.TestTasks; namespace Xharness.Jenkins { - public class Jenkins + public class Jenkins : IResourceManager { readonly ISimulatorLoader simulators; readonly IHardwareDeviceLoader devices; @@ -80,8 +80,8 @@ public ILogs Logs { List Tasks = new List (); Dictionary DependencyTasks = new Dictionary (); - internal static Resource DesktopResource = new Resource ("Desktop", Environment.ProcessorCount); - internal static Resource NugetResource = new Resource ("Nuget", 1); // nuget is not parallel-safe :( + public Resource DesktopResource { get; } = new Resource ("Desktop", Environment.ProcessorCount); + public Resource NugetResource { get; } = new Resource ("Nuget", 1); // nuget is not parallel-safe :( static Dictionary device_resources = new Dictionary (); internal static Resources GetDeviceResources (IEnumerable devices) @@ -481,9 +481,7 @@ IEnumerable CreateTestVariations (IEnumerable tests, Func> CreateRunSimulatorTasksAsync () configurations = new string [] { "Debug" }; foreach (var config in configurations) { foreach (var pair in ps) { - var derived = new MSBuildTask (processManager) { - Jenkins = this, + var derived = new MSBuildTask (jenkins: this, testProject: project, processManager: processManager) { ProjectConfiguration = config, ProjectPlatform = "iPhoneSimulator", Platform = pair.Item2, @@ -561,8 +558,7 @@ async Task> CreateRunSimulatorTasksAsync () var rv = new List (); foreach (var taskGroup in testVariations.GroupBy ((RunSimulatorTask task) => task.Device?.UDID ?? task.Candidates.ToString ())) { - rv.Add (new AggregatedRunSimulatorTask (taskGroup) { - Jenkins = this, + rv.Add (new AggregatedRunSimulatorTask (jenkins: this, tasks: taskGroup) { TestName = $"Tests for {taskGroup.Key}", }); } @@ -584,8 +580,7 @@ Task> CreateRunDeviceTasksAsync () projectTasks.Clear (); if (!project.SkipiOSVariation) { - var build64 = new MSBuildTask (processManager) { - Jenkins = this, + var build64 = new MSBuildTask (jenkins: this, testProject: project, processManager: processManager) { ProjectConfiguration = "Debug64", ProjectPlatform = "iPhone", Platform = TestPlatform.iOS_Unified64, @@ -594,8 +589,7 @@ Task> CreateRunDeviceTasksAsync () build64.CloneTestProject (project); projectTasks.Add (new RunDeviceTask (devices, build64, processManager, devices.Connected64BitIOS.Where (d => project.IsSupported (d.DevicePlatform, d.ProductVersion))) { Ignored = !IncludeiOS64 }); - var build32 = new MSBuildTask (processManager) { - Jenkins = this, + var build32 = new MSBuildTask (jenkins: this, testProject: project, processManager: processManager) { ProjectConfiguration = project.Name != "dont link" ? "Debug32" : "Release32", ProjectPlatform = "iPhone", Platform = TestPlatform.iOS_Unified32, @@ -605,8 +599,7 @@ Task> CreateRunDeviceTasksAsync () projectTasks.Add (new RunDeviceTask (devices, build32, processManager, devices.Connected32BitIOS.Where (d => project.IsSupported (d.DevicePlatform, d.ProductVersion))) { Ignored = !IncludeiOS32 }); var todayProject = project.AsTodayExtensionProject (); - var buildToday = new MSBuildTask (processManager) { - Jenkins = this, + var buildToday = new MSBuildTask (jenkins: this, testProject: todayProject, processManager: processManager) { ProjectConfiguration = "Debug64", ProjectPlatform = "iPhone", Platform = TestPlatform.iOS_TodayExtension64, @@ -618,8 +611,7 @@ Task> CreateRunDeviceTasksAsync () if (!project.SkiptvOSVariation) { var tvOSProject = project.AsTvOSProject (); - var buildTV = new MSBuildTask (processManager) { - Jenkins = this, + var buildTV = new MSBuildTask (jenkins: this, testProject: tvOSProject, processManager: processManager) { ProjectConfiguration = "Debug", ProjectPlatform = "iPhone", Platform = TestPlatform.tvOS, @@ -632,8 +624,7 @@ Task> CreateRunDeviceTasksAsync () if (!project.SkipwatchOSVariation) { var watchOSProject = project.AsWatchOSProject (); if (!project.SkipwatchOS32Variation) { - var buildWatch32 = new MSBuildTask (processManager) { - Jenkins = this, + var buildWatch32 = new MSBuildTask (jenkins: this, testProject: watchOSProject, processManager: processManager) { ProjectConfiguration = "Debug32", ProjectPlatform = "iPhone", Platform = TestPlatform.watchOS_32, @@ -644,8 +635,7 @@ Task> CreateRunDeviceTasksAsync () } if (!project.SkipwatchOSARM64_32Variation) { - var buildWatch64_32 = new MSBuildTask (processManager) { - Jenkins = this, + var buildWatch64_32 = new MSBuildTask (jenkins: this, testProject: watchOSProject, processManager: processManager) { ProjectConfiguration = "Release64_32", // We don't support Debug for ARM64_32 yet. ProjectPlatform = "iPhone", Platform = TestPlatform.watchOS_64_32, @@ -940,10 +930,9 @@ Task PopulateTasksAsync () var crashReportSnapshotFactory = new CrashSnapshotReporterFactory (processManager); - var buildiOSMSBuild_net461 = new MSBuildTask (processManager) + var net461Project = new TestProject (Path.GetFullPath (Path.Combine (Harness.RootDirectory, "..", "msbuild", "tests", "Xamarin.iOS.Tasks.Tests", "Xamarin.iOS.Tasks.Tests.csproj"))); + var buildiOSMSBuild_net461 = new MSBuildTask (jenkins: this, testProject: net461Project, processManager: processManager) { - Jenkins = this, - TestProject = new TestProject (Path.GetFullPath (Path.Combine (Harness.RootDirectory, "..", "msbuild", "tests", "Xamarin.iOS.Tasks.Tests", "Xamarin.iOS.Tasks.Tests.csproj"))), SpecifyPlatform = false, SpecifyConfiguration = true, ProjectConfiguration = "Debug-net461", @@ -954,7 +943,7 @@ Task PopulateTasksAsync () var nunitExecutioniOSMSBuild_net461 = new NUnitExecuteTask (buildiOSMSBuild_net461, processManager) { TestLibrary = Path.Combine (Harness.RootDirectory, "..", "msbuild", "tests", "Xamarin.iOS.Tasks.Tests", "bin", "Debug-net461", "net461", "Xamarin.iOS.Tasks.Tests.dll"), - TestProject = buildiOSMSBuild_net461.TestProject, + TestProject = net461Project, ProjectConfiguration = "Debug-net461", Platform = TestPlatform.iOS, TestName = "MSBuild tests", @@ -965,9 +954,8 @@ Task PopulateTasksAsync () }; Tasks.Add (nunitExecutioniOSMSBuild_net461); - var buildiOSMSBuild_netstandard2 = new MSBuildTask (processManager) { - Jenkins = this, - TestProject = new TestProject (Path.GetFullPath (Path.Combine (Harness.RootDirectory, "..", "msbuild", "tests", "Xamarin.iOS.Tasks.Tests", "Xamarin.iOS.Tasks.Tests.csproj"))), + var netstandard2Project = new TestProject (Path.GetFullPath (Path.Combine (Harness.RootDirectory, "..", "msbuild", "tests", "Xamarin.iOS.Tasks.Tests", "Xamarin.iOS.Tasks.Tests.csproj"))); + var buildiOSMSBuild_netstandard2 = new MSBuildTask (jenkins: this, testProject: netstandard2Project, processManager: processManager) { SpecifyPlatform = false, SpecifyConfiguration = true, ProjectConfiguration = "Debug-netstandard2.0", @@ -977,7 +965,7 @@ Task PopulateTasksAsync () }; var nunitExecutioniOSMSBuild_netstandard2 = new NUnitExecuteTask (buildiOSMSBuild_netstandard2, processManager) { TestLibrary = Path.Combine (Harness.RootDirectory, "..", "msbuild", "tests", "Xamarin.iOS.Tasks.Tests", "bin", "Debug-netstandard2.0", "net461", "Xamarin.iOS.Tasks.Tests.dll"), - TestProject = buildiOSMSBuild_netstandard2.TestProject, + TestProject = netstandard2Project, ProjectConfiguration = "Debug-netstandard2.0", Platform = TestPlatform.iOS, TestName = "MSBuild tests", @@ -988,10 +976,9 @@ Task PopulateTasksAsync () }; Tasks.Add (nunitExecutioniOSMSBuild_netstandard2); - var buildInstallSources = new MSBuildTask (processManager) + var installSourcesProject = new TestProject (Path.GetFullPath (Path.Combine (Harness.RootDirectory, "..", "tools", "install-source", "InstallSourcesTests", "InstallSourcesTests.csproj"))); + var buildInstallSources = new MSBuildTask (jenkins: this, testProject: installSourcesProject, processManager: processManager) { - Jenkins = this, - TestProject = new TestProject (Path.GetFullPath (Path.Combine (Harness.RootDirectory, "..", "tools", "install-source", "InstallSourcesTests", "InstallSourcesTests.csproj"))), SpecifyPlatform = false, SpecifyConfiguration = false, Platform = TestPlatform.iOS, @@ -1000,7 +987,7 @@ Task PopulateTasksAsync () var nunitExecutionInstallSource = new NUnitExecuteTask (buildInstallSources, processManager) { TestLibrary = Path.Combine (Harness.RootDirectory, "..", "tools", "install-source", "InstallSourcesTests", "bin", "Release", "InstallSourcesTests.dll"), - TestProject = buildInstallSources.TestProject, + TestProject = installSourcesProject, Platform = TestPlatform.iOS, TestName = "Install Sources tests", Mode = "iOS", @@ -1039,10 +1026,9 @@ Task PopulateTasksAsync () throw new NotImplementedException (project.TargetFrameworkFlavors.ToString ()); } foreach (var config in configurations) { - MSBuildTask build = new MSBuildTask (processManager); + MSBuildTask build = new MSBuildTask (jenkins: this, testProject: project, processManager: processManager); build.Platform = platform; build.CloneTestProject (project); - build.Jenkins = this; build.SolutionPath = project.SolutionPath; build.ProjectConfiguration = config; build.ProjectPlatform = project.Platform; @@ -1082,9 +1068,8 @@ Task PopulateTasksAsync () } } - var buildMTouch = new MakeTask (processManager) + var buildMTouch = new MakeTask (jenkins: this, processManager: processManager) { - Jenkins = this, TestProject = new TestProject (Path.GetFullPath (Path.Combine (Harness.RootDirectory, "mtouch", "mtouch.sln"))), SpecifyPlatform = false, SpecifyConfiguration = false, @@ -1104,8 +1089,7 @@ Task PopulateTasksAsync () }; Tasks.Add (nunitExecutionMTouch); - var buildGenerator = new MakeTask (processManager) { - Jenkins = this, + var buildGenerator = new MakeTask (jenkins: this, processManager: processManager) { TestProject = new TestProject (Path.GetFullPath (Path.Combine (Harness.RootDirectory, "..", "src", "generator.sln"))), SpecifyPlatform = false, SpecifyConfiguration = false, @@ -1124,15 +1108,15 @@ Task PopulateTasksAsync () }; Tasks.Add (runGenerator); - var buildDotNetGenerator = new DotNetBuildTask (processManager) { - Jenkins = this, + var buildDotNetGeneratorProject = new TestProject (Path.GetFullPath (Path.Combine (Harness.RootDirectory, "bgen", "bgen-tests.csproj"))); + var buildDotNetGenerator = new DotNetBuildTask (jenkins: this, testProject: buildDotNetGeneratorProject, processManager: processManager) { TestProject = new TestProject (Path.GetFullPath (Path.Combine (Harness.RootDirectory, "bgen", "bgen-tests.csproj"))), SpecifyPlatform = false, SpecifyConfiguration = false, Platform = TestPlatform.iOS, }; var runDotNetGenerator = new DotNetTestTask (buildDotNetGenerator, processManager) { - TestProject = buildDotNetGenerator.TestProject, + TestProject = buildDotNetGeneratorProject, Platform = TestPlatform.iOS, TestName = "Generator tests", Mode = ".NET", @@ -1140,9 +1124,8 @@ Task PopulateTasksAsync () }; Tasks.Add (runDotNetGenerator); - var run_mmp = new MakeTask (processManager) + var run_mmp = new MakeTask (jenkins: this, processManager: processManager) { - Jenkins = this, Platform = TestPlatform.Mac, TestName = "MMP Regression Tests", Target = "all", // -j" + Environment.ProcessorCount, @@ -1159,9 +1142,8 @@ Task PopulateTasksAsync () run_mmp.Environment.Add ("BUILD_REVISION", "jenkins"); // This will print "@MonkeyWrench: AddFile: " lines, which we can use to get the log filenames. Tasks.Add (run_mmp); - var runMacBindingProject = new MakeTask (processManager) + var runMacBindingProject = new MakeTask (jenkins: this, processManager: processManager) { - Jenkins = this, Platform = TestPlatform.Mac, TestName = "Mac Binding Projects", Target = "all", @@ -1171,8 +1153,7 @@ Task PopulateTasksAsync () }; Tasks.Add (runMacBindingProject); - var buildXtroTests = new MakeTask (processManager) { - Jenkins = this, + var buildXtroTests = new MakeTask (jenkins: this, processManager: processManager) { Platform = TestPlatform.All, TestName = "Xtro", Target = "wrench", @@ -1181,7 +1162,6 @@ Task PopulateTasksAsync () Timeout = TimeSpan.FromMinutes (15), }; var runXtroReporter = new RunXtroTask (buildXtroTests, processManager, crashReportSnapshotFactory) { - Jenkins = this, Platform = TestPlatform.Mac, TestName = buildXtroTests.TestName, Ignored = buildXtroTests.Ignored, @@ -1189,8 +1169,7 @@ Task PopulateTasksAsync () }; Tasks.Add (runXtroReporter); - var buildCecilTests = new MakeTask (processManager) { - Jenkins = this, + var buildCecilTests = new MakeTask (jenkins: this, processManager: processManager) { Platform = TestPlatform.All, TestName = "Cecil", Target = "build", @@ -1209,8 +1188,7 @@ Task PopulateTasksAsync () }; Tasks.Add (runCecilTests); - var runDocsTests = new MakeTask (processManager) { - Jenkins = this, + var runDocsTests = new MakeTask (jenkins: this, processManager: processManager) { Platform = TestPlatform.All, TestName = "Documentation", Target = "wrench-docs", @@ -1220,9 +1198,8 @@ Task PopulateTasksAsync () }; Tasks.Add (runDocsTests); - var buildSampleTests = new MSBuildTask (processManager) { - Jenkins = this, - TestProject = new TestProject (Path.GetFullPath (Path.Combine (Harness.RootDirectory, "sampletester", "sampletester.sln"))), + var buildSampleTestsProject = new TestProject (Path.GetFullPath (Path.Combine (Harness.RootDirectory, "sampletester", "sampletester.sln"))); + var buildSampleTests = new MSBuildTask (jenkins: this, testProject: buildSampleTestsProject, processManager: processManager) { SpecifyPlatform = false, Platform = TestPlatform.All, ProjectConfiguration = "Debug", diff --git a/tests/xharness/Jenkins/TestTasks/AggregatedRunSimulatorTask.cs b/tests/xharness/Jenkins/TestTasks/AggregatedRunSimulatorTask.cs index 58aec21e8b71..7750ea442e5e 100644 --- a/tests/xharness/Jenkins/TestTasks/AggregatedRunSimulatorTask.cs +++ b/tests/xharness/Jenkins/TestTasks/AggregatedRunSimulatorTask.cs @@ -20,7 +20,7 @@ class AggregatedRunSimulatorTask : AppleTestTask Stopwatch run_timer = new Stopwatch (); public TimeSpan RunDuration { get { return run_timer.Elapsed; } } - public AggregatedRunSimulatorTask (IEnumerable tasks) + public AggregatedRunSimulatorTask (Jenkins jenkins, IEnumerable tasks) : base (jenkins) { this.Tasks = tasks; } diff --git a/tests/xharness/Jenkins/TestTasks/AppleTestTask.cs b/tests/xharness/Jenkins/TestTasks/AppleTestTask.cs index efe4208cab77..f7ab20073f1e 100644 --- a/tests/xharness/Jenkins/TestTasks/AppleTestTask.cs +++ b/tests/xharness/Jenkins/TestTasks/AppleTestTask.cs @@ -8,9 +8,10 @@ namespace Xharness.Jenkins.TestTasks { public abstract class AppleTestTask : Xharness.TestTasks.TestTasks { - public Jenkins Jenkins; + public Jenkins Jenkins { get; private set; } public Harness Harness { get { return Jenkins.Harness; } } + public override IResourceManager ResourceManager => Jenkins; public override string LogDirectory { get { @@ -20,11 +21,16 @@ public override string LogDirectory { } } + public AppleTestTask (Jenkins jenkins) : base () + { + Jenkins = jenkins ?? throw new ArgumentNullException (nameof (jenkins)); + } + public override void GenerateReport () => Jenkins.GenerateReport (); protected override void WriteLineToRunnerLog (string message) => Harness.HarnessLog.WriteLine (message); - protected override void SetEnvironmentVariables (Process process) + public override void SetEnvironmentVariables (Process process) { var xcodeRoot = Harness.XcodeRoot; @@ -77,8 +83,7 @@ protected override void SetEnvironmentVariables (Process process) } } - - protected override void LogEvent (ILog log, string text, params object [] args) + public override void LogEvent (ILog log, string text, params object [] args) { base.LogEvent (log, text, args); Jenkins.MainLog.WriteLine (text, args); diff --git a/tests/xharness/Jenkins/TestTasks/BuildProjectTask.cs b/tests/xharness/Jenkins/TestTasks/BuildProjectTask.cs index e75659fa640b..9d91a93cede9 100644 --- a/tests/xharness/Jenkins/TestTasks/BuildProjectTask.cs +++ b/tests/xharness/Jenkins/TestTasks/BuildProjectTask.cs @@ -1,116 +1,42 @@ using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; using System.Threading.Tasks; -using System.Xml; -using Microsoft.DotNet.XHarness.iOS.Shared; using Microsoft.DotNet.XHarness.iOS.Shared.Execution; using Microsoft.DotNet.XHarness.iOS.Shared.Logging; -using Microsoft.DotNet.XHarness.iOS.Shared.Utilities; using Xharness.TestTasks; namespace Xharness.Jenkins.TestTasks { abstract class BuildProjectTask : BuildToolTask { - public string SolutionPath; - protected BuildProjectTask (IProcessManager processManager) : base (processManager) - { + public string SolutionPath { + get => buildProjectTask.SolutionPath; + set => buildProjectTask.SolutionPath = value; } - public virtual bool RestoreNugets { - get { - return TestProject.RestoreNugetsInProject || !string.IsNullOrEmpty (SolutionPath); - } - } + Xharness.TestTasks.BuildProjectTask buildProjectTask; - public override bool SupportsParallelExecution { - get { - return Platform.ToString ().StartsWith ("Mac", StringComparison.Ordinal); + public override TestProject TestProject { + get => base.TestProject; + set { + base.TestProject = value; + buildProjectTask.TestProject = value; } } - async Task 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 (); - 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 ProcessManager.RunAsync (nuget, log, 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 GetNestedReferenceProjects (string csproj) + protected BuildProjectTask (Jenkins jenkins, TestProject testProject, IProcessManager processManager) : base (jenkins, processManager) { - if (!File.Exists (csproj)) - throw new FileNotFoundException ("Could not find the project whose reference projects needed to be found.", csproj); - var result = new List (); - 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; + buildProjectTask = new Xharness.TestTasks.BuildProjectTask (processManager, Jenkins, this, this); + TestProject = testProject ?? throw new ArgumentNullException (nameof (testProject)); } - // 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; + public virtual bool RestoreNugets => buildProjectTask.RestoreNugets; - if (!File.Exists (SolutionPath ?? TestProject.Path)) - throw new FileNotFoundException ("Could not find the solution whose nugets to restore.", SolutionPath ?? TestProject.Path); + public override bool SupportsParallelExecution => buildProjectTask.SupportsParallelExecution; - // 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); - } + // 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) => + ExecutionResult = await buildProjectTask.RestoreNugetsAsync (log, resource, useXIBuild); } } diff --git a/tests/xharness/Jenkins/TestTasks/BuildToolTask.cs b/tests/xharness/Jenkins/TestTasks/BuildToolTask.cs index 05dd70329a3a..47a48c9bf36b 100644 --- a/tests/xharness/Jenkins/TestTasks/BuildToolTask.cs +++ b/tests/xharness/Jenkins/TestTasks/BuildToolTask.cs @@ -9,6 +9,14 @@ public abstract class BuildToolTask : AppleTestTask public IProcessManager ProcessManager => buildToolTask.ProcessManager; + public override string TestName { + get => base.TestName; + set { + base.TestName = value; + buildToolTask.TestName = value; + } + } + public bool SpecifyPlatform { get => buildToolTask.SpecifyPlatform; set => buildToolTask.SpecifyPlatform = value; @@ -19,7 +27,15 @@ public bool SpecifyConfiguration { set => buildToolTask.SpecifyConfiguration = value; } - protected BuildToolTask (IProcessManager processManager) + public override TestProject TestProject { + get => base.TestProject; + set { + base.TestProject = value; + buildToolTask.TestProject = value; + } + } + + protected BuildToolTask (Jenkins jenkins, IProcessManager processManager) : base (jenkins) => buildToolTask = new Xharness.TestTasks.BuildToolTask (processManager); public override TestPlatform Platform { diff --git a/tests/xharness/Jenkins/TestTasks/DotNetBuildTask.cs b/tests/xharness/Jenkins/TestTasks/DotNetBuildTask.cs index 72cbe715a85e..9ae5cdf81c4b 100644 --- a/tests/xharness/Jenkins/TestTasks/DotNetBuildTask.cs +++ b/tests/xharness/Jenkins/TestTasks/DotNetBuildTask.cs @@ -4,7 +4,7 @@ namespace Xharness.Jenkins.TestTasks { class DotNetBuildTask : MSBuildTask { - public DotNetBuildTask (IProcessManager processManager) : base (processManager) + public DotNetBuildTask (Jenkins jenkins, TestProject testProject, IProcessManager processManager) : base (jenkins, testProject, processManager) { SetDotNetEnvironmentVariables (Environment); } diff --git a/tests/xharness/Jenkins/TestTasks/MSBuildTask.cs b/tests/xharness/Jenkins/TestTasks/MSBuildTask.cs index 2eaaba7f18c7..aba614671fb4 100644 --- a/tests/xharness/Jenkins/TestTasks/MSBuildTask.cs +++ b/tests/xharness/Jenkins/TestTasks/MSBuildTask.cs @@ -32,7 +32,7 @@ protected virtual List ToolArguments { } } - public MSBuildTask (IProcessManager processManager) : base (processManager) + public MSBuildTask (Jenkins jenkins, TestProject testProject, IProcessManager processManager) : base (jenkins, testProject, processManager) { } diff --git a/tests/xharness/Jenkins/TestTasks/MakeTask.cs b/tests/xharness/Jenkins/TestTasks/MakeTask.cs index b00d7dd1747d..f426594f2299 100644 --- a/tests/xharness/Jenkins/TestTasks/MakeTask.cs +++ b/tests/xharness/Jenkins/TestTasks/MakeTask.cs @@ -12,7 +12,7 @@ class MakeTask : BuildToolTask public string WorkingDirectory; public TimeSpan Timeout = TimeSpan.FromMinutes (5); - public MakeTask (IProcessManager processManager) : base (processManager) + public MakeTask (Jenkins jenkins, IProcessManager processManager) : base (jenkins, processManager) { } diff --git a/tests/xharness/Jenkins/TestTasks/RunTestTask.cs b/tests/xharness/Jenkins/TestTasks/RunTestTask.cs index 21349fa7dd6a..2e3c4670a405 100644 --- a/tests/xharness/Jenkins/TestTasks/RunTestTask.cs +++ b/tests/xharness/Jenkins/TestTasks/RunTestTask.cs @@ -20,12 +20,11 @@ internal abstract class RunTestTask : AppleTestTask public double TimeoutMultiplier { get; set; } = 1; public string WorkingDirectory; - public RunTestTask (BuildToolTask build_task, IProcessManager processManager) + public RunTestTask (BuildToolTask build_task, IProcessManager processManager) : base (build_task.Jenkins) { this.BuildTask = build_task; this.ProcessManager = processManager ?? throw new ArgumentNullException (nameof (processManager)); - Jenkins = build_task.Jenkins; TestProject = build_task.TestProject; Platform = build_task.Platform; ProjectPlatform = build_task.ProjectPlatform; diff --git a/tests/xharness/Microsoft.DotNet.XHarness.iOS.Shared/Logging/IEventLogger.cs b/tests/xharness/Microsoft.DotNet.XHarness.iOS.Shared/Logging/IEventLogger.cs new file mode 100644 index 000000000000..bccb7189a49b --- /dev/null +++ b/tests/xharness/Microsoft.DotNet.XHarness.iOS.Shared/Logging/IEventLogger.cs @@ -0,0 +1,5 @@ +namespace Microsoft.DotNet.XHarness.iOS.Shared.Logging { + public interface IEventLogger { + public void LogEvent (ILog log, string text, params object [] args); + } +} diff --git a/tests/xharness/TestTasks/BuildProjectTask.cs b/tests/xharness/TestTasks/BuildProjectTask.cs new file mode 100644 index 000000000000..a8e794e79f13 --- /dev/null +++ b/tests/xharness/TestTasks/BuildProjectTask.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Threading.Tasks; +using System.Xml; +using Microsoft.DotNet.XHarness.iOS.Shared; +using Microsoft.DotNet.XHarness.iOS.Shared.Execution; +using Microsoft.DotNet.XHarness.iOS.Shared.Logging; +using Microsoft.DotNet.XHarness.iOS.Shared.Utilities; + +namespace Xharness.TestTasks { + public class BuildProjectTask : BuildToolTask { + public IResourceManager ResourceManager { get; set; } + public IEnvManager EnviromentManager { get; set; } + public IEventLogger EventLogger { get; set; } + + public string SolutionPath { get; set; } + + public BuildProjectTask (IProcessManager processManager, IResourceManager resourceManager, IEventLogger eventLogger, IEnvManager envManager) : base (processManager) + { + ResourceManager = resourceManager ?? throw new ArgumentNullException (nameof (resourceManager)); + EventLogger = eventLogger ?? throw new ArgumentNullException (nameof (eventLogger)); + EnviromentManager = envManager ?? throw new ArgumentNullException (nameof (envManager)); + } + + public bool RestoreNugets { + get { + return TestProject.RestoreNugetsInProject || !string.IsNullOrEmpty (SolutionPath); + } + } + + public bool SupportsParallelExecution { + get { + return Platform.ToString ().StartsWith ("Mac", StringComparison.Ordinal); + } + } + + async Task RestoreNugetsAsync (string projectPath, ILog log, bool useXIBuild = false) + { + using (var resource = await ResourceManager.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 (); + 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); + EnviromentManager.SetEnvironmentVariables (nuget); + EventLogger.LogEvent (log, "Restoring nugets for {0} ({1}) on path {2}", TestName, Mode, projectPath); + + var timeout = TimeSpan.FromMinutes (15); + var result = await ProcessManager.RunAsync (nuget, log, 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; + } + } + + EventLogger.LogEvent (log, "Restoring nugets completed for {0} ({1}) on path {2}", TestName, Mode, projectPath); + return TestExecutingResult.Succeeded; + } + } + + List 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 (); + 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). + public async Task RestoreNugetsAsync (ILog log, IAcquiredResource resource, bool useXIBuild = false) + { + if (!RestoreNugets) + return TestExecutingResult.Ignored; + + 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) { + return execResult; + } + } + } + + // restore for the main project/solution] + return await RestoreNugetsAsync (SolutionPath ?? TestProject.Path, log, useXIBuild); + } + } +} diff --git a/tests/xharness/TestTasks/BuildToolTask.cs b/tests/xharness/TestTasks/BuildToolTask.cs index f8940cda7c07..671c3c65b4e9 100644 --- a/tests/xharness/TestTasks/BuildToolTask.cs +++ b/tests/xharness/TestTasks/BuildToolTask.cs @@ -6,8 +6,10 @@ namespace Xharness.TestTasks { public class BuildToolTask { + public string TestName { get; set; } public IProcessManager ProcessManager { get; } public TestPlatform Platform { get; set; } + public TestProject TestProject { get; set; } public bool SpecifyPlatform { get; set; } = true; public bool SpecifyConfiguration { get; set; } = true; diff --git a/tests/xharness/TestTasks/IEnvManager.cs b/tests/xharness/TestTasks/IEnvManager.cs new file mode 100644 index 000000000000..71fa96b08448 --- /dev/null +++ b/tests/xharness/TestTasks/IEnvManager.cs @@ -0,0 +1,12 @@ +using System; +using System.Diagnostics; + +namespace Xharness.TestTasks { + /// + /// Knows how to handle the different enviroment variables to be used with the processes executed by xharness and + /// the different tests tasks. + /// + public interface IEnvManager { + void SetEnvironmentVariables (Process process); + } +} diff --git a/tests/xharness/TestTasks/IResourceManager.cs b/tests/xharness/TestTasks/IResourceManager.cs new file mode 100644 index 000000000000..9871d1421f5b --- /dev/null +++ b/tests/xharness/TestTasks/IResourceManager.cs @@ -0,0 +1,12 @@ +namespace Xharness.TestTasks { + + /// + /// Managed the resorces that can be used by the different tests tasks so that when ran async they do not step + /// on eachother. + /// + public interface IResourceManager + { + Resource DesktopResource { get; } + Resource NugetResource { get; } + } +} diff --git a/tests/xharness/TestTasks/TestTask.cs b/tests/xharness/TestTasks/TestTask.cs index 8a2bdf6c0483..b07d130907f9 100644 --- a/tests/xharness/TestTasks/TestTask.cs +++ b/tests/xharness/TestTasks/TestTask.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -11,7 +11,7 @@ using Microsoft.DotNet.XHarness.iOS.Shared.Utilities; namespace Xharness.TestTasks { - public abstract class TestTasks + public abstract class TestTasks : IEnvManager, IEventLogger { static int counter; static DriveInfo RootDrive; @@ -27,7 +27,6 @@ public abstract class TestTasks public Func 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 TestProject TestProject; public List Resources = new List (); #endregion @@ -126,10 +125,11 @@ public IEnumerable ReferencedNunitAndXunitTestAssemblies { #region Abstract + public abstract IResourceManager ResourceManager { get; } public abstract void GenerateReport (); public abstract string LogDirectory { get; } + public abstract void SetEnvironmentVariables (Process process); protected abstract Task ExecuteAsync (); - protected abstract void SetEnvironmentVariables (Process process); protected abstract Task NotifyAndAcquireDesktopResourceAsync (); protected abstract void WriteLineToRunnerLog (string message); @@ -137,6 +137,7 @@ public IEnumerable ReferencedNunitAndXunitTestAssemblies { #region Virtual + public virtual TestProject TestProject { get; set; } public virtual TestPlatform Platform { get; set; } public virtual string ProgressMessage { get; } public virtual string Mode { get; set; } @@ -196,7 +197,7 @@ public virtual string TestName { protected virtual void PropagateResults () { } - protected virtual void LogEvent (ILog log, string text, params object [] args) => log.WriteLine (text, args); + public virtual void LogEvent (ILog log, string text, params object [] args) => log.WriteLine (text, args); public virtual void Reset () { diff --git a/tests/xharness/xharness.csproj b/tests/xharness/xharness.csproj index a01f24c6fe2c..6f83acc7a58d 100644 --- a/tests/xharness/xharness.csproj +++ b/tests/xharness/xharness.csproj @@ -1,4 +1,4 @@ - + Debug @@ -121,6 +121,9 @@ + + + @@ -143,4 +146,4 @@ - \ No newline at end of file +