/// <summary> /// Prepares HelixWorkItem given xUnit project information. /// </summary> /// <param name="publishPath">The non-relative path to the publish directory.</param> /// <returns>An ITaskItem instance representing the prepared HelixWorkItem.</returns> private async Task <List <ITaskItem> > PrepareWorkItem(ITaskItem xunitProject) { // Forces this task to run asynchronously await Task.Yield(); if (!xunitProject.GetRequiredMetadata(Log, "PublishDirectory", out string publishDirectory)) { return(null); } if (!xunitProject.GetRequiredMetadata(Log, "TargetPath", out string targetPath)) { return(null); } if (!xunitProject.GetRequiredMetadata(Log, "RuntimeTargetFramework", out string runtimeTargetFramework)) { return(null); } xunitProject.TryGetMetadata("Arguments", out string arguments); string assemblyName = Path.GetFileName(targetPath); var runtimeTargetFrameworkParsed = NuGetFramework.Parse(runtimeTargetFramework); if (runtimeTargetFrameworkParsed.Framework != ".NETCoreApp") { throw new NotImplementedException("does not support non core runtime target"); } string driver = $"{PathToDotnet} exec "; // On mac due to https://github.com/dotnet/sdk/issues/3923, we run against workitem directory // but on Windows, if we running against working item diretory, we would hit long path. string testExecutionDirectory = IsPosixShell ? "-testExecutionDirectory $TestExecutionDirectory" : "-testExecutionDirectory %TestExecutionDirectory%"; string msbuildAdditionalSdkResolverFolder = IsPosixShell ? "" : "-msbuildAdditionalSdkResolverFolder %HELIX_CORRELATION_PAYLOAD%\\r"; var scheduler = new AssemblyScheduler(methodLimit: 40); var assemblyPartitionInfos = scheduler.Schedule(targetPath); var partitionedWorkItem = new List <ITaskItem>(); foreach (var assemblyPartitionInfo in assemblyPartitionInfos) { string command = $"{driver}{assemblyName} {testExecutionDirectory} {msbuildAdditionalSdkResolverFolder} {(XUnitArguments != null ? " " + XUnitArguments : "")} -xml testResults.xml {assemblyPartitionInfo.ClassListArgumentString} {arguments}"; Log.LogMessage($"Creating work item with properties Identity: {assemblyName}, PayloadDirectory: {publishDirectory}, Command: {command}"); TimeSpan timeout = TimeSpan.FromMinutes(5); if (!string.IsNullOrEmpty(XUnitWorkItemTimeout)) { if (!TimeSpan.TryParse(XUnitWorkItemTimeout, out timeout)) { Log.LogWarning($"Invalid value \"{XUnitWorkItemTimeout}\" provided for XUnitWorkItemTimeout; falling back to default value of \"00:05:00\" (5 minutes)"); } } partitionedWorkItem.Add(new Microsoft.Build.Utilities.TaskItem(assemblyPartitionInfo.DisplayName, new Dictionary <string, string>() { { "Identity", assemblyPartitionInfo.DisplayName }, { "PayloadDirectory", publishDirectory }, { "Command", command }, { "Timeout", timeout.ToString() }, })); } return(partitionedWorkItem); }
/// <summary> /// Prepares HelixWorkItem given xUnit project information. /// </summary> /// <param name="publishPath">The non-relative path to the publish directory.</param> /// <returns>An ITaskItem instance representing the prepared HelixWorkItem.</returns> private async Task <List <ITaskItem> > PrepareWorkItem(ITaskItem xunitProject) { // Forces this task to run asynchronously await Task.Yield(); if (!xunitProject.GetRequiredMetadata(Log, "PublishDirectory", out string publishDirectory)) { return(null); } if (!xunitProject.GetRequiredMetadata(Log, "TargetPath", out string targetPath)) { return(null); } if (!xunitProject.GetRequiredMetadata(Log, "RuntimeTargetFramework", out string runtimeTargetFramework)) { return(null); } xunitProject.TryGetMetadata("Arguments", out string arguments); string assemblyName = Path.GetFileName(targetPath); string driver = $"{PathToDotnet}"; // netfx tests should only run on Windows full framework for testing VS scenarios // These tests have to be executed slightly differently and we give them a different Identity so ADO can tell them apart var runtimeTargetFrameworkParsed = NuGetFramework.Parse(runtimeTargetFramework); var testIdentityDifferentiator = ""; bool netFramework = false; if (runtimeTargetFrameworkParsed.Framework == ".NETFramework") { testIdentityDifferentiator = ".netfx"; netFramework = true; } else if (runtimeTargetFrameworkParsed.Framework != ".NETCoreApp") { throw new NotImplementedException("does not support non support the runtime specified"); } // On mac due to https://github.com/dotnet/sdk/issues/3923, we run against workitem directory // but on Windows, if we running against working item diretory, we would hit long path. string testExecutionDirectory = netFramework ? "-e DOTNET_SDK_TEST_EXECUTION_DIRECTORY=%TestExecutionDirectory%" : IsPosixShell ? "-testExecutionDirectory $TestExecutionDirectory" : "-testExecutionDirectory %TestExecutionDirectory%"; string msbuildAdditionalSdkResolverFolder = netFramework ? "-e DOTNET_SDK_TEST_MSBUILDSDKRESOLVER_FOLDER=%HELIX_CORRELATION_PAYLOAD%\\r" : IsPosixShell ? "" : "-msbuildAdditionalSdkResolverFolder %HELIX_CORRELATION_PAYLOAD%\\r"; var scheduler = new AssemblyScheduler(methodLimit: 32); var assemblyPartitionInfos = scheduler.Schedule(targetPath, netFramework: netFramework); var partitionedWorkItem = new List <ITaskItem>(); foreach (var assemblyPartitionInfo in assemblyPartitionInfos) { string command = $"{driver} exec {assemblyName} {testExecutionDirectory} {msbuildAdditionalSdkResolverFolder} {(XUnitArguments != null ? " " + XUnitArguments : "")} -xml testResults.xml {assemblyPartitionInfo.ClassListArgumentString} {arguments}"; if (netFramework) { var testFilter = String.IsNullOrEmpty(assemblyPartitionInfo.ClassListArgumentString) ? "" : $"--filter \"{assemblyPartitionInfo.ClassListArgumentString}\""; command = $"{driver} test {assemblyName} {testExecutionDirectory} {msbuildAdditionalSdkResolverFolder} {(XUnitArguments != null ? " " + XUnitArguments : "")} --results-directory .\\ --logger trx {testFilter}"; } Log.LogMessage($"Creating work item with properties Identity: {assemblyName}, PayloadDirectory: {publishDirectory}, Command: {command}"); TimeSpan timeout = TimeSpan.FromMinutes(5); if (!string.IsNullOrEmpty(XUnitWorkItemTimeout)) { if (!TimeSpan.TryParse(XUnitWorkItemTimeout, out timeout)) { Log.LogWarning($"Invalid value \"{XUnitWorkItemTimeout}\" provided for XUnitWorkItemTimeout; falling back to default value of \"00:05:00\" (5 minutes)"); } } partitionedWorkItem.Add(new Microsoft.Build.Utilities.TaskItem(assemblyPartitionInfo.DisplayName + testIdentityDifferentiator, new Dictionary <string, string>() { { "Identity", assemblyPartitionInfo.DisplayName + testIdentityDifferentiator }, { "PayloadDirectory", publishDirectory }, { "Command", command }, { "Timeout", timeout.ToString() }, })); } return(partitionedWorkItem); }