From bd7a31d1b66bc634def4b6b30fe51c7a1d6d8b61 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Tue, 14 Jul 2020 17:10:58 +0200 Subject: [PATCH 1/4] [xharness] Generate BCL projects asynchronously. Split the BCL project generation in two: one part that figures out which projects to generate, and which is done synchronously (because we need it to compute the list of tests), then split out the actual generation and run it on a background thread (since that doesn't have to happen until we want to execute those tests). This speeds up launching xharness in server mode significantly (from ~2s to ~0.2s). --- tests/xharness/BCLTestImportTargetFactory.cs | 37 +-- .../Templates/ITemplatedProject.cs | 13 +- .../Templates/Managed/XamariniOSTemplate.cs | 236 ++++++++++-------- .../Tests/BCLTestImportTargetFactoryTest.cs | 14 +- 4 files changed, 168 insertions(+), 132 deletions(-) diff --git a/tests/xharness/BCLTestImportTargetFactory.cs b/tests/xharness/BCLTestImportTargetFactory.cs index 441d2ce4d6ea..eb32e79c133f 100644 --- a/tests/xharness/BCLTestImportTargetFactory.cs +++ b/tests/xharness/BCLTestImportTargetFactory.cs @@ -287,8 +287,8 @@ public BCLTestImportTargetFactory (string outputDirectory, string monoRootPath) /// has its own details. /// The dir where the projects will be saved. /// - public Task GenerateTestProjectsAsync (IEnumerable<(string Name, string [] Assemblies, string ExtraArgs, double TimeoutMultiplier)> projects, Platform platform) - => TemplatedProject.GenerateTestProjectsAsync (projects, platform); + public GeneratedProjects GenerateTestProjects (IEnumerable<(string Name, string [] Assemblies, string ExtraArgs, double TimeoutMultiplier)> projects, Platform platform) + => TemplatedProject.GenerateTestProjects (projects, platform); List<(string Name, string [] Assemblies, string ExtraArgs, double TimeoutMultiplier)> GetProjectDefinitions (ProjectsDefinitions definitions, Platform platform) { @@ -318,35 +318,34 @@ public Task GenerateTestProjectsAsync (IEnumerable<(string Na return testProjects; } - async Task Platforms, string Failure, double TimeoutMultiplier)>> GenerateAlliOSTestProjectsAsync () + List<(string Name, string Path, bool XUnit, string ExtraArgs, List Platforms, string Failure, double TimeoutMultiplier, Task GenerationCompleted)> GenerateAlliOSTestProjects () { - var projectPaths = new List<(string Name, string Path, bool XUnit, string ExtraArgs, List Platforms, string Failure, double TimeoutMultiplier)> (); - var projects = new Dictionary Platforms, string Failure, double TimeoutMultiplier)> (); + var projectPaths = new List<(string Name, string Path, bool XUnit, string ExtraArgs, List Platforms, string Failure, double TimeoutMultiplier, Task GenerationCompleted)> (); + var projects = new Dictionary Platforms, string Failure, double TimeoutMultiplier, Task GenerationCompleted)> (); foreach (var platform in new [] { Platform.iOS, Platform.TvOS, Platform.WatchOS }) { - var generated = await GenerateTestProjectsAsync (GetProjectDefinitions (commoniOSTestProjects, platform), platform); + var generated = GenerateTestProjects (GetProjectDefinitions (commoniOSTestProjects, platform), platform); foreach (var tp in generated) { if (!projects.ContainsKey (tp.Name)) { - projects [tp.Name] = (tp.Path, tp.XUnit, tp.ExtraArgs, new List { platform }, tp.Failure, tp.TimeoutMultiplier); + projects [tp.Name] = (tp.Path, tp.XUnit, tp.ExtraArgs, new List { platform }, tp.Failure, tp.TimeoutMultiplier, tp.GenerationCompleted); } else { var project = projects [tp.Name]; project.Platforms.Add (platform); project.TimeoutMultiplier += (tp.TimeoutMultiplier - 1); + project.GenerationCompleted = Task.WhenAll (project.GenerationCompleted, tp.GenerationCompleted); } } } // foreach platform // return the grouped projects - foreach (var name in projects.Keys) { - projectPaths.Add ((name, projects [name].Path, projects [name].XUnit, projects [name].ExtraArgs, projects [name].Platforms, projects [name].Failure, projects [name].TimeoutMultiplier)); + foreach (var kvp in projects) { + var name = kvp.Key; + var proj = kvp.Value; + projectPaths.Add ((kvp.Key, proj.Path, proj.XUnit, proj.ExtraArgs, proj.Platforms, proj.Failure, proj.TimeoutMultiplier, proj.GenerationCompleted)); } return projectPaths; } - public List<(string Name, string Path, bool XUnit, string ExtraArgs, List Platforms, string Failure, double TimeoutMultiplier)> GenerateAlliOSTestProjects () => GenerateAlliOSTestProjectsAsync ().Result; - - public Task GenerateAllMacTestProjectsAsync (Platform platform) => GenerateTestProjectsAsync (GetProjectDefinitions (macTestProjects, platform), platform); - - public GeneratedProjects GenerateAllMacTestProjects (Platform platform) => GenerateAllMacTestProjectsAsync (platform).Result; + public GeneratedProjects GenerateAllMacTestProjects (Platform platform) => GenerateTestProjects (GetProjectDefinitions (macTestProjects, platform), platform); // Map from the projects understood from the test importer to those that AppRunner and friends understand: public List GetiOSBclTargets () @@ -366,6 +365,7 @@ public List GetiOSBclTargets () RestoreNugetsInProject = true, MTouchExtraArgs = tp.ExtraArgs, TimeoutMultiplier = tp.TimeoutMultiplier, + Dependency = () => tp.GenerationCompleted, }); } return result; @@ -382,14 +382,19 @@ public List GetMacBclTargets (MacFlavors flavor) foreach (var tp in GenerateAllMacTestProjects (platform)) { var prefix = tp.XUnit ? "xUnit" : "NUnit"; var finalName = tp.Name.StartsWith ("mscorlib", StringComparison.Ordinal) ? tp.Name : $"[{prefix}] Mono {tp.Name}"; // mscorlib is our special test - result.Add (new MacTestProject (tp.Path, targetFrameworkFlavor: flavor) { + var proj = new MacTestProject (tp.Path, targetFrameworkFlavor: flavor) { Name = finalName, Platform = "AnyCPU", IsExecutableProject = true, FailureMessage = tp.Failure, RestoreNugetsInProject = true, MTouchExtraArgs = tp.ExtraArgs, - }); + }; + proj.Dependency = async () => { + await tp.GenerationCompleted; + proj.FailureMessage = tp.Failure; + }; + result.Add (proj); } return result; } diff --git a/tests/xharness/Microsoft.DotNet.XHarness.iOS.Shared/TestImporter/Templates/ITemplatedProject.cs b/tests/xharness/Microsoft.DotNet.XHarness.iOS.Shared/TestImporter/Templates/ITemplatedProject.cs index b0f874f446c5..5b930005c408 100644 --- a/tests/xharness/Microsoft.DotNet.XHarness.iOS.Shared/TestImporter/Templates/ITemplatedProject.cs +++ b/tests/xharness/Microsoft.DotNet.XHarness.iOS.Shared/TestImporter/Templates/ITemplatedProject.cs @@ -4,8 +4,17 @@ namespace Microsoft.DotNet.XHarness.iOS.Shared.TestImporter.Templates { + public class GeneratedProject { + public string Name; + public string Path; + public bool XUnit; + public string ExtraArgs; + public string Failure; + public double TimeoutMultiplier; + public Task GenerationCompleted; + } // less typing - public class GeneratedProjects : List<(string Name, string Path, bool XUnit, string ExtraArgs, string Failure, double TimeoutMultiplier)> { + public class GeneratedProjects : List { } // interface that represent a project that is created from a template. @@ -28,6 +37,6 @@ public interface ITemplatedProject { /// has its own details. /// The dir where the projects will be saved. /// - Task GenerateTestProjectsAsync (IEnumerable<(string Name, string [] Assemblies, string ExtraArgs, double TimeoutMultiplier)> projects, Platform platform); + GeneratedProjects GenerateTestProjects (IEnumerable<(string Name, string [] Assemblies, string ExtraArgs, double TimeoutMultiplier)> projects, Platform platform); } } diff --git a/tests/xharness/Microsoft.DotNet.XHarness.iOS.Shared/TestImporter/Templates/Managed/XamariniOSTemplate.cs b/tests/xharness/Microsoft.DotNet.XHarness.iOS.Shared/TestImporter/Templates/Managed/XamariniOSTemplate.cs index bac688757dc6..fd79000ea6c7 100644 --- a/tests/xharness/Microsoft.DotNet.XHarness.iOS.Shared/TestImporter/Templates/Managed/XamariniOSTemplate.cs +++ b/tests/xharness/Microsoft.DotNet.XHarness.iOS.Shared/TestImporter/Templates/Managed/XamariniOSTemplate.cs @@ -369,28 +369,28 @@ string GenerateIncludeFilesNode (string projectName, (string FailureMessage, Lis return contentFiles.ToString (); } - public async Task GenerateTestProjectsAsync (IEnumerable<(string Name, string [] Assemblies, string ExtraArgs, double TimeoutMultiplier)> projects, Platform platform) + public GeneratedProjects GenerateTestProjects (IEnumerable<(string Name, string [] Assemblies, string ExtraArgs, double TimeoutMultiplier)> projects, Platform platform) { // generate the template c# code before we create the diff projects GenerateSource (Path.Combine (OutputDirectoryPath, "templates")); var result = new GeneratedProjects (); switch (platform) { case Platform.WatchOS: - result = await GenerateWatchOSTestProjectsAsync (projects); + result = GenerateWatchOSTestProjects (projects); break; case Platform.iOS: case Platform.TvOS: - result = await GenerateiOSTestProjectsAsync (projects, platform); + result = GenerateiOSTestProjects (projects, platform); break; case Platform.MacOSFull: case Platform.MacOSModern: - result = await GenerateMacTestProjectsAsync (projects, platform); + result = GenerateMacTestProjects (projects, platform); break; } return result; } - #region Watch Porjects generation + #region Watch Projects generation string GenerateWatchProject (string projectName, string template, string infoPlistPath) { @@ -441,7 +441,7 @@ async Task GenerateWatchExtensionAsync (string projectName, Stream templ } // internal implementations that generate each of the diff projects - async Task GenerateWatchOSTestProjectsAsync (IEnumerable<(string Name, string [] Assemblies, string ExtraArgs, double TimeoutMultiplier)> projects) + GeneratedProjects GenerateWatchOSTestProjects (IEnumerable<(string Name, string [] Assemblies, string ExtraArgs, double TimeoutMultiplier)> projects) { var projectPaths = new GeneratedProjects (); foreach (var def in projects) { @@ -456,68 +456,75 @@ async Task GenerateWatchOSTestProjectsAsync (IEnumerable<(str if (!projectDefinition.Validate ()) throw new InvalidOperationException ("xUnit and NUnit assemblies cannot be mixed in a test project."); + var gp = new GeneratedProject (); var generatedCodeDir = Path.Combine (GeneratedCodePathRoot, projectDefinition.Name, "watch"); if (!Directory.Exists (generatedCodeDir)) { Directory.CreateDirectory (generatedCodeDir); } var registerTypePath = Path.Combine (generatedCodeDir, "RegisterType.cs"); - string failure = null; - string rootProjectPath = GetProjectPath (projectDefinition.Name, Platform.WatchOS); ; - try { - // create the plist for each of the apps - var projectData = new Dictionary (); - foreach (var appType in new [] { WatchAppType.Extension, WatchAppType.App }) { - (string plist, string project) data; - var plist = await InfoPlistGenerator.GenerateCodeAsync (GetPlistTemplate (appType), projectDefinition.Name); - data.plist = GetPListPath (generatedCodeDir, appType); - using (var file = new StreamWriter (data.plist, false)) { // false is do not append - await file.WriteAsync (plist); + gp.Name = projectDefinition.Name; + gp.Path = GetProjectPath (projectDefinition.Name, Platform.WatchOS); + gp.XUnit = projectDefinition.IsXUnit; + gp.ExtraArgs = projectDefinition.ExtraArgs; + gp.TimeoutMultiplier = def.TimeoutMultiplier; + var task = Task.Run (async () => { + try { + // create the plist for each of the apps + var projectData = new Dictionary (); + foreach (var appType in new [] { WatchAppType.Extension, WatchAppType.App }) { + (string plist, string project) data; + var plist = await InfoPlistGenerator.GenerateCodeAsync (GetPlistTemplate (appType), projectDefinition.Name); + data.plist = GetPListPath (generatedCodeDir, appType); + using (var file = new StreamWriter (data.plist, false)) { // false is do not append + await file.WriteAsync (plist); + } + + string generatedProject; + switch (appType) { + case WatchAppType.App: + generatedProject = await GenerateWatchAppAsync (projectDefinition.Name, GetProjectTemplate (appType), data.plist); + break; + default: + var info = projectDefinition.GetAssemblyInclusionInformation (Platform.WatchOS); + generatedProject = await GenerateWatchExtensionAsync (projectDefinition.Name, GetProjectTemplate (appType), data.plist, registerTypePath, info); + gp.Failure ??= info.FailureMessage; + break; + } + data.project = GetProjectPath (projectDefinition.Name, appType); + using (var file = new StreamWriter (data.project, false)) { // false is do not append + await file.WriteAsync (generatedProject); + } + + projectData [appType] = data; + } // foreach app type + + var rootPlist = await InfoPlistGenerator.GenerateCodeAsync (GetPlistTemplate (Platform.WatchOS), projectDefinition.Name); + var infoPlistPath = GetPListPath (generatedCodeDir, Platform.WatchOS); + using (var file = new StreamWriter (infoPlistPath, false)) { // false is do not append + await file.WriteAsync (rootPlist); } - string generatedProject; - switch (appType) { - case WatchAppType.App: - generatedProject = await GenerateWatchAppAsync (projectDefinition.Name, GetProjectTemplate (appType), data.plist); - break; - default: - var info = projectDefinition.GetAssemblyInclusionInformation (Platform.WatchOS); - generatedProject = await GenerateWatchExtensionAsync (projectDefinition.Name, GetProjectTemplate (appType), data.plist, registerTypePath, info); - failure ??= info.FailureMessage; - break; + using (var file = new StreamWriter (gp.Path, false)) // false is do not append + using (var reader = new StreamReader (GetProjectTemplate (Platform.WatchOS))) { + var template = await reader.ReadToEndAsync (); + var generatedRootProject = GenerateWatchProject (def.Name, template, infoPlistPath); + await file.WriteAsync (generatedRootProject); } - data.project = GetProjectPath (projectDefinition.Name, appType); - using (var file = new StreamWriter (data.project, false)) { // false is do not append - await file.WriteAsync (generatedProject); + var typesPerAssembly = projectDefinition.GetTypeForAssemblies (AssemblyLocator.GetAssembliesRootLocation (Platform.iOS), Platform.WatchOS); + var registerCode = await RegisterTypeGenerator.GenerateCodeAsync (typesPerAssembly, + projectDefinition.IsXUnit, GetRegisterTypeTemplate ()); + using (var file = new StreamWriter (registerTypePath, false)) { // false is do not append + await file.WriteAsync (registerCode); } - projectData [appType] = data; - } // foreach app type - - var rootPlist = await InfoPlistGenerator.GenerateCodeAsync (GetPlistTemplate (Platform.WatchOS), projectDefinition.Name); - var infoPlistPath = GetPListPath (generatedCodeDir, Platform.WatchOS); - using (var file = new StreamWriter (infoPlistPath, false)) { // false is do not append - await file.WriteAsync (rootPlist); + gp.Failure ??= typesPerAssembly.FailureMessage; + } catch (Exception e) { + gp.Failure = e.Message; } - - using (var file = new StreamWriter (rootProjectPath, false)) // false is do not append - using (var reader = new StreamReader (GetProjectTemplate (Platform.WatchOS))) { - var template = await reader.ReadToEndAsync (); - var generatedRootProject = GenerateWatchProject (def.Name, template, infoPlistPath); - await file.WriteAsync (generatedRootProject); - } - var typesPerAssembly = projectDefinition.GetTypeForAssemblies (AssemblyLocator.GetAssembliesRootLocation (Platform.iOS), Platform.WatchOS); - var registerCode = await RegisterTypeGenerator.GenerateCodeAsync (typesPerAssembly, - projectDefinition.IsXUnit, GetRegisterTypeTemplate ()); - using (var file = new StreamWriter (registerTypePath, false)) { // false is do not append - await file.WriteAsync (registerCode); - } - - failure ??= typesPerAssembly.FailureMessage; - } catch (Exception e) { - failure = e.Message; - } + }); + gp.GenerationCompleted = task; // we have the 3 projects we depend on, we need the root one, the one that will be used by harness - projectPaths.Add ((projectDefinition.Name, rootProjectPath, projectDefinition.IsXUnit, projectDefinition.ExtraArgs, failure, def.TimeoutMultiplier)); + projectPaths.Add (gp); } // foreach project return projectPaths; @@ -567,7 +574,7 @@ async Task GenerateAsync (string projectName, string registerPath, (stri } } - async Task GenerateiOSTestProjectsAsync (IEnumerable<(string Name, string [] Assemblies, string ExtraArgs, double TimeoutMultiplier)> projects, Platform platform) + GeneratedProjects GenerateiOSTestProjects (IEnumerable<(string Name, string [] Assemblies, string ExtraArgs, double TimeoutMultiplier)> projects, Platform platform) { if (platform == Platform.WatchOS) throw new ArgumentException (nameof (platform)); @@ -591,33 +598,40 @@ async Task GenerateiOSTestProjectsAsync (IEnumerable<(string var registerTypePath = Path.Combine (generatedCodeDir, "RegisterType.cs"); string projectPath = GetProjectPath (projectDefinition.Name, platform); - string failure = null; - try { - var plist = await InfoPlistGenerator.GenerateCodeAsync (GetPlistTemplate (platform), projectDefinition.Name); - var infoPlistPath = GetPListPath (generatedCodeDir, platform); - using (var file = new StreamWriter (infoPlistPath, false)) { // false is do not append - await file.WriteAsync (plist); - } + var gp = new GeneratedProject (); + gp.Name = projectDefinition.Name; + gp.Path = projectPath; + gp.XUnit = projectDefinition.IsXUnit; + gp.ExtraArgs = projectDefinition.ExtraArgs; + gp.TimeoutMultiplier = def.TimeoutMultiplier; + gp.GenerationCompleted = Task.Run (async () => { + try { + var plist = await InfoPlistGenerator.GenerateCodeAsync (GetPlistTemplate (platform), projectDefinition.Name); + var infoPlistPath = GetPListPath (generatedCodeDir, platform); + using (var file = new StreamWriter (infoPlistPath, false)) { // false is do not append + await file.WriteAsync (plist); + } - var info = projectDefinition.GetAssemblyInclusionInformation (platform); - var generatedProject = await GenerateAsync (projectDefinition.Name, registerTypePath, info, GetProjectTemplate (platform), infoPlistPath, platform); - using (var file = new StreamWriter (projectPath, false)) { // false is do not append - await file.WriteAsync (generatedProject); - } - var typesPerAssembly = projectDefinition.GetTypeForAssemblies (AssemblyLocator.GetAssembliesRootLocation (platform), platform); - var registerCode = await RegisterTypeGenerator.GenerateCodeAsync (typesPerAssembly, - projectDefinition.IsXUnit, GetRegisterTypeTemplate ()); + var info = projectDefinition.GetAssemblyInclusionInformation (platform); + var generatedProject = await GenerateAsync (projectDefinition.Name, registerTypePath, info, GetProjectTemplate (platform), infoPlistPath, platform); + using (var file = new StreamWriter (projectPath, false)) { // false is do not append + await file.WriteAsync (generatedProject); + } + var typesPerAssembly = projectDefinition.GetTypeForAssemblies (AssemblyLocator.GetAssembliesRootLocation (platform), platform); + var registerCode = await RegisterTypeGenerator.GenerateCodeAsync (typesPerAssembly, + projectDefinition.IsXUnit, GetRegisterTypeTemplate ()); - using (var file = new StreamWriter (registerTypePath, false)) { // false is do not append - await file.WriteAsync (registerCode); - } + using (var file = new StreamWriter (registerTypePath, false)) { // false is do not append + await file.WriteAsync (registerCode); + } - failure = failure ?? info.FailureMessage; - failure = failure ?? typesPerAssembly.FailureMessage; - } catch (Exception e) { - failure = e.Message; - } - projectPaths.Add ((projectDefinition.Name, projectPath, projectDefinition.IsXUnit, projectDefinition.ExtraArgs, failure, def.TimeoutMultiplier)); + gp.Failure ??= info.FailureMessage; + gp.Failure ??= typesPerAssembly.FailureMessage; + } catch (Exception e) { + gp.Failure = e.Message; + } + }); + projectPaths.Add (gp); } // foreach project return projectPaths; @@ -669,7 +683,7 @@ async Task GenerateMacAsync (string projectName, string registerPath, (s } } - async Task GenerateMacTestProjectsAsync (IEnumerable<(string Name, string [] Assemblies, string ExtraArgs, double TimeoutMultiplier)> projects, Platform platform) + GeneratedProjects GenerateMacTestProjects (IEnumerable<(string Name, string [] Assemblies, string ExtraArgs, double TimeoutMultiplier)> projects, Platform platform) { var projectPaths = new GeneratedProjects (); foreach (var def in projects) { @@ -686,34 +700,42 @@ async Task GenerateMacTestProjectsAsync (IEnumerable<(string Directory.CreateDirectory (generatedCodeDir); var registerTypePath = Path.Combine (generatedCodeDir, "RegisterType-mac.cs"); - var typesPerAssembly = projectDefinition.GetTypeForAssemblies (AssemblyLocator.GetAssembliesRootLocation (platform), platform); - var registerCode = await RegisterTypeGenerator.GenerateCodeAsync (typesPerAssembly, - projectDefinition.IsXUnit, GetRegisterTypeTemplate ()); - - using (var file = new StreamWriter (registerTypePath, false)) { // false is do not append - await file.WriteAsync (registerCode); - } var projectPath = GetProjectPath (projectDefinition.Name, platform); - string failure = null; - try { - var plist = await InfoPlistGenerator.GenerateCodeAsync (GetPlistTemplate (platform), projectDefinition.Name); - var infoPlistPath = GetPListPath (generatedCodeDir, platform); - using (var file = new StreamWriter (infoPlistPath, false)) { // false is do not append - await file.WriteAsync (plist); - } + var gp = new GeneratedProject (); + gp.Name = projectDefinition.Name; + gp.Path = projectPath; + gp.XUnit = projectDefinition.IsXUnit; + gp.ExtraArgs = projectDefinition.ExtraArgs; + gp.TimeoutMultiplier = def.TimeoutMultiplier; + gp.GenerationCompleted = Task.Run (async () => { + try { + var typesPerAssembly = projectDefinition.GetTypeForAssemblies (AssemblyLocator.GetAssembliesRootLocation (platform), platform); + var registerCode = await RegisterTypeGenerator.GenerateCodeAsync (typesPerAssembly, + projectDefinition.IsXUnit, GetRegisterTypeTemplate ()); + + using (var file = new StreamWriter (registerTypePath, false)) { // false is do not append + await file.WriteAsync (registerCode); + } + + var plist = await InfoPlistGenerator.GenerateCodeAsync (GetPlistTemplate (platform), projectDefinition.Name); + var infoPlistPath = GetPListPath (generatedCodeDir, platform); + using (var file = new StreamWriter (infoPlistPath, false)) { // false is do not append + await file.WriteAsync (plist); + } - var info = projectDefinition.GetAssemblyInclusionInformation (platform); - var generatedProject = await GenerateMacAsync (projectDefinition.Name, registerTypePath, - info, GetProjectTemplate (platform), infoPlistPath, platform); - using (var file = new StreamWriter (projectPath, false)) { // false is do not append - await file.WriteAsync (generatedProject); + var info = projectDefinition.GetAssemblyInclusionInformation (platform); + var generatedProject = await GenerateMacAsync (projectDefinition.Name, registerTypePath, + info, GetProjectTemplate (platform), infoPlistPath, platform); + using (var file = new StreamWriter (projectPath, false)) { // false is do not append + await file.WriteAsync (generatedProject); + } + gp.Failure ??= info.FailureMessage; + gp.Failure ??= typesPerAssembly.FailureMessage; + } catch (Exception e) { + gp.Failure = e.Message; } - failure = failure ?? info.FailureMessage; - failure = failure ?? typesPerAssembly.FailureMessage; - } catch (Exception e) { - failure = e.Message; - } - projectPaths.Add ((projectDefinition.Name, projectPath, projectDefinition.IsXUnit, projectDefinition.ExtraArgs, failure, def.TimeoutMultiplier)); + }); + projectPaths.Add (gp); } return projectPaths; diff --git a/tests/xharness/Xharness.Tests/Tests/BCLTestImportTargetFactoryTest.cs b/tests/xharness/Xharness.Tests/Tests/BCLTestImportTargetFactoryTest.cs index 23bd0adb526f..e598e0023edf 100644 --- a/tests/xharness/Xharness.Tests/Tests/BCLTestImportTargetFactoryTest.cs +++ b/tests/xharness/Xharness.Tests/Tests/BCLTestImportTargetFactoryTest.cs @@ -82,22 +82,22 @@ public void MacMonoSDKPathSetterTest () } [Test] - public async Task GenerateTestProjectsAsyncTest () + public void GenerateTestProjectsAsyncTest () { var projects = new GeneratedProjects () { - ( Name: "First project", Path: "", XUnit: false, ExtraArgs: "", Failure: "", TimeoutMultiplier: 1), - ( Name: "Second project", Path: "", XUnit: true, ExtraArgs: "", Failure: "", TimeoutMultiplier: 1), + new GeneratedProject { Name = "First project", Path = "", XUnit = false, ExtraArgs = "", Failure = "", TimeoutMultiplier = 1, GenerationCompleted = Task.CompletedTask, }, + new GeneratedProject { Name = "Second project", Path = "", XUnit = true, ExtraArgs = "", Failure = "", TimeoutMultiplier = 1, GenerationCompleted = Task.CompletedTask, }, }; var infos = new List<(string Name, string [] Assemblies, string ExtraArgs, double TimeoutMultiplier)> { ( Name: "First project", Assemblies: new string [] { }, ExtraArgs: "", TimeoutMultiplier: 1), ( Name: "Second project", Assemblies: new string [] { }, ExtraArgs: "", TimeoutMultiplier: 1), }; - template.Setup (t => t.GenerateTestProjectsAsync (It.IsAny> (), It.IsAny ())).Returns (() => { - return Task.FromResult (projects); + template.Setup (t => t.GenerateTestProjects (It.IsAny> (), It.IsAny ())).Returns (() => { + return projects; }); - var result = await generator.GenerateTestProjectsAsync (infos, Platform.iOS); + var result = generator.GenerateTestProjects (infos, Platform.iOS); Assert.AreEqual (projects.Count, result.Count); - template.Verify (t => t.GenerateTestProjectsAsync (It.IsAny> (), It.IsAny ())); + template.Verify (t => t.GenerateTestProjects (It.IsAny> (), It.IsAny ())); } } } From 5a4d8d709dd87ef57126d70601f666e612e337ea Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Fri, 17 Jul 2020 13:29:46 +0200 Subject: [PATCH 2/4] [xharness] Fix a few remaining issues. --- tests/xharness/BCLTestImportTargetFactory.cs | 10 +++++++--- .../Templates/Managed/XamariniOSTemplate.cs | 5 ++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/tests/xharness/BCLTestImportTargetFactory.cs b/tests/xharness/BCLTestImportTargetFactory.cs index eb32e79c133f..e937c594d1e9 100644 --- a/tests/xharness/BCLTestImportTargetFactory.cs +++ b/tests/xharness/BCLTestImportTargetFactory.cs @@ -355,7 +355,7 @@ public List GetiOSBclTargets () foreach (var tp in GenerateAlliOSTestProjects ()) { var prefix = tp.XUnit ? "xUnit" : "NUnit"; var finalName = tp.Name.StartsWith ("mscorlib", StringComparison.Ordinal) ? tp.Name : $"[{prefix}] Mono {tp.Name}"; // mscorlib is our special test - result.Add (new iOSTestProject (tp.Path) { + var proj = new iOSTestProject (tp.Path) { Name = finalName, SkipiOSVariation = !tp.Platforms.Contains (Platform.iOS), SkiptvOSVariation = !tp.Platforms.Contains (Platform.TvOS), @@ -365,8 +365,12 @@ public List GetiOSBclTargets () RestoreNugetsInProject = true, MTouchExtraArgs = tp.ExtraArgs, TimeoutMultiplier = tp.TimeoutMultiplier, - Dependency = () => tp.GenerationCompleted, - }); + }; + proj.Dependency = async () => { + await tp.GenerationCompleted; + proj.FailureMessage = tp.Failure; + }; + result.Add (proj); } return result; } diff --git a/tests/xharness/Microsoft.DotNet.XHarness.iOS.Shared/TestImporter/Templates/Managed/XamariniOSTemplate.cs b/tests/xharness/Microsoft.DotNet.XHarness.iOS.Shared/TestImporter/Templates/Managed/XamariniOSTemplate.cs index 6eadfcdc8203..3bd09626cdfe 100644 --- a/tests/xharness/Microsoft.DotNet.XHarness.iOS.Shared/TestImporter/Templates/Managed/XamariniOSTemplate.cs +++ b/tests/xharness/Microsoft.DotNet.XHarness.iOS.Shared/TestImporter/Templates/Managed/XamariniOSTemplate.cs @@ -510,9 +510,8 @@ GeneratedProjects GenerateWatchOSTestProjects (IEnumerable<(string Name, string await file.WriteAsync (rootPlist); } - using (var file = new StreamWriter (gp.Path, false)) // false is do not append - using (var reader = new StreamReader (GetProjectTemplate (Platform.WatchOS))) { - var template = await reader.ReadToEndAsync (); + using (var file = new StreamWriter (gp.Path, false)) { // false is do not append + var template = GetProjectTemplate (Platform.WatchOS); var generatedRootProject = GenerateWatchProject (def.Name, template, infoPlistPath); await file.WriteAsync (generatedRootProject); } From eaf38ca013dfb09ab895365de2804fb36360c048 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Fri, 17 Jul 2020 13:59:47 +0200 Subject: [PATCH 3/4] [xharness] Don't generate into the same directory/files for macOS Modern and macOS Full. --- .../TestImporter/Templates/Managed/XamariniOSTemplate.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/xharness/Microsoft.DotNet.XHarness.iOS.Shared/TestImporter/Templates/Managed/XamariniOSTemplate.cs b/tests/xharness/Microsoft.DotNet.XHarness.iOS.Shared/TestImporter/Templates/Managed/XamariniOSTemplate.cs index 3bd09626cdfe..8afe595c2402 100644 --- a/tests/xharness/Microsoft.DotNet.XHarness.iOS.Shared/TestImporter/Templates/Managed/XamariniOSTemplate.cs +++ b/tests/xharness/Microsoft.DotNet.XHarness.iOS.Shared/TestImporter/Templates/Managed/XamariniOSTemplate.cs @@ -696,7 +696,7 @@ GeneratedProjects GenerateMacTestProjects (IEnumerable<(string Name, string [] A if (!projectDefinition.Validate ()) throw new InvalidOperationException ("xUnit and NUnit assemblies cannot be mixed in a test project."); // generate the required type registration info - var generatedCodeDir = Path.Combine (GeneratedCodePathRoot, projectDefinition.Name, "mac"); + var generatedCodeDir = Path.Combine (GeneratedCodePathRoot, projectDefinition.Name, platform.ToString ()); Directory.CreateDirectory (generatedCodeDir); var registerTypePath = Path.Combine (generatedCodeDir, "RegisterType-mac.cs"); From e3dea82aad2bfbd2a42902e1832485605f7df687 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Fri, 17 Jul 2020 14:10:58 +0200 Subject: [PATCH 4/4] [xharness] Try harder to make lldb quit after trying to get a backtrace. When a process times out, we try to print a stack trace for all threads. This involves executing "lldb --source