diff --git a/src/Tasks/Microsoft.NET.Build.Extensions.Tasks.UnitTests/Microsoft.NET.Build.Extensions.Tasks.UnitTests.csproj b/src/Tasks/Microsoft.NET.Build.Extensions.Tasks.UnitTests/Microsoft.NET.Build.Extensions.Tasks.UnitTests.csproj
index 109d01102759..3e0102c21879 100644
--- a/src/Tasks/Microsoft.NET.Build.Extensions.Tasks.UnitTests/Microsoft.NET.Build.Extensions.Tasks.UnitTests.csproj
+++ b/src/Tasks/Microsoft.NET.Build.Extensions.Tasks.UnitTests/Microsoft.NET.Build.Extensions.Tasks.UnitTests.csproj
@@ -24,11 +24,12 @@
+
-
+
diff --git a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/Microsoft.NET.Build.Tasks.UnitTests.csproj b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/Microsoft.NET.Build.Tasks.UnitTests.csproj
index 263fb5a44b49..7652a0865022 100644
--- a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/Microsoft.NET.Build.Tasks.UnitTests.csproj
+++ b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/Microsoft.NET.Build.Tasks.UnitTests.csproj
@@ -35,7 +35,7 @@
-
+
diff --git a/test/Common/Program.UnitTests.cs b/test/Common/Program.UnitTests.cs
deleted file mode 100644
index 31f308383400..000000000000
--- a/test/Common/Program.UnitTests.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-partial class Program
-{
- public static int Main(string[] args)
- {
- var newArgs = args.ToList();
-
- // Help argument needs to be the first one to xunit, so don't insert assembly location in that case
- if (args.Any(arg => arg.Equals("-help", StringComparison.CurrentCultureIgnoreCase) || arg.Equals("/?")))
- {
- newArgs.Insert(0, "/?");
- }
- else
- {
- newArgs.Insert(0, typeof(Program).Assembly.Location);
- }
-
- int returnCode = Xunit.ConsoleClient.Program.Main(newArgs.ToArray());
-
- return returnCode;
- }
-}
diff --git a/test/Common/Program.cs b/test/Common/Program.cs
index 8b54b8bd40a0..d395586c7655 100644
--- a/test/Common/Program.cs
+++ b/test/Common/Program.cs
@@ -1,5 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.NET.TestFramework;
+using Microsoft.NET.TestFramework.Commands;
#pragma warning disable SA1205 // Partial elements should declare access
partial class Program
diff --git a/test/HelixTasks/SDKCustomCreateXUnitWorkItemsWithTestExclusion.cs b/test/HelixTasks/SDKCustomCreateXUnitWorkItemsWithTestExclusion.cs
index 87f95c449cb7..65f2b992a07d 100644
--- a/test/HelixTasks/SDKCustomCreateXUnitWorkItemsWithTestExclusion.cs
+++ b/test/HelixTasks/SDKCustomCreateXUnitWorkItemsWithTestExclusion.cs
@@ -104,6 +104,14 @@ private async Task> PrepareWorkItem(ITaskItem xunitProject)
xunitProject.TryGetMetadata("ExcludeAdditionalParameters", out string ExcludeAdditionalParameters);
xunitProject.TryGetMetadata("Arguments", out string arguments);
+ 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)");
+ }
+ }
string assemblyName = Path.GetFileName(targetPath);
@@ -142,24 +150,21 @@ private async Task> PrepareWorkItem(ITaskItem xunitProject)
var partitionedWorkItem = new List();
foreach (var assemblyPartitionInfo in assemblyPartitionInfos)
{
- string command = $"{driver} exec {assemblyName} {testExecutionDirectory} {msbuildAdditionalSdkResolverFolder} {(XUnitArguments != null ? " " + XUnitArguments : "")} -xml testResults.xml {assemblyPartitionInfo.ClassListArgumentString} {arguments}";
+ string command;
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}";
+ command = $"{driver} test {assemblyName} -e HELIX_WORK_ITEM_TIMEOUT={timeout} {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))
+ else
{
- 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)");
- }
+ command = $"{driver} exec {assemblyName} -e HELIX_WORK_ITEM_TIMEOUT={timeout} {testExecutionDirectory} {msbuildAdditionalSdkResolverFolder} " +
+ $"{(XUnitArguments != null ? " " + XUnitArguments : "")} -xml testResults.xml {assemblyPartitionInfo.ClassListArgumentString} {arguments}";
}
+ Log.LogMessage($"Creating work item with properties Identity: {assemblyName}, PayloadDirectory: {publishDirectory}, Command: {command}");
+
partitionedWorkItem.Add(new Microsoft.Build.Utilities.TaskItem(assemblyPartitionInfo.DisplayName + testIdentityDifferentiator, new Dictionary()
{
{ "Identity", assemblyPartitionInfo.DisplayName + testIdentityDifferentiator},
diff --git a/test/Microsoft.NET.TestFramework/TestCommandLine.cs b/test/Microsoft.NET.TestFramework/TestCommandLine.cs
index f873ca8fab23..2519a063e819 100644
--- a/test/Microsoft.NET.TestFramework/TestCommandLine.cs
+++ b/test/Microsoft.NET.TestFramework/TestCommandLine.cs
@@ -29,7 +29,9 @@ public class TestCommandLine
public string MsbuildAdditionalSdkResolverFolder { get; set; }
- public List TestConfigFiles { get; private set; } = new List();
+ public List<(string name, string value)> EnvironmentVariables { get; set; } = [];
+
+ public List TestConfigFiles { get; private set; } = [];
public HashSet TestListsToRun { get; private set; } = new HashSet(StringComparer.OrdinalIgnoreCase);
@@ -91,6 +93,10 @@ public static TestCommandLine Parse(string[] args)
{
ret.TestListsToRun.Add(argStack.Pop());
}
+ else if (arg.Equals("-e", StringComparison.CurrentCultureIgnoreCase))
+ {
+ ret.EnvironmentVariables.Add(ParseEnvironmentVariableArg(argStack.Pop()));
+ }
else if (arg.Equals("-showSdkInfo", StringComparison.CurrentCultureIgnoreCase))
{
ret.ShowSdkInfo = true;
@@ -125,6 +131,17 @@ public static TestCommandLine Parse(string[] args)
return ret;
}
+ private static (string name, string value) ParseEnvironmentVariableArg(string arg)
+ {
+ var i = arg.IndexOf('=');
+ if (i <= 0)
+ {
+ throw new ArgumentException($"Invalid environment variable specification (expected 'name=value'): '{arg}'");
+ }
+
+ return (arg.Substring(0, i), arg.Substring(i + 1));
+ }
+
public List GetXunitArgsFromTestConfig()
{
List testsToSkip = new();
diff --git a/test/Microsoft.NET.TestFramework/TestContext.cs b/test/Microsoft.NET.TestFramework/TestContext.cs
index f7aae8e04cd4..93a5a47e1038 100644
--- a/test/Microsoft.NET.TestFramework/TestContext.cs
+++ b/test/Microsoft.NET.TestFramework/TestContext.cs
@@ -74,6 +74,11 @@ public static void Initialize(TestCommandLine commandLine)
CommandLoggingContext.SetVerbose(true);
Reporter.Reset();
+ foreach (var (name, value) in commandLine.EnvironmentVariables)
+ {
+ Environment.SetEnvironmentVariable(name, value);
+ }
+
Environment.SetEnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0");
// Reset this environment variable so that if the dotnet under test is different than the
diff --git a/test/dotnet-watch.Tests/Utilities/AwaitableProcess.cs b/test/dotnet-watch.Tests/Utilities/AwaitableProcess.cs
index 200c21a0e5fa..77da26b85b4c 100644
--- a/test/dotnet-watch.Tests/Utilities/AwaitableProcess.cs
+++ b/test/dotnet-watch.Tests/Utilities/AwaitableProcess.cs
@@ -8,6 +8,10 @@ namespace Microsoft.DotNet.Watcher.Tools
{
internal class AwaitableProcess : IDisposable
{
+ // cancel just before we hit timeout used on CI (XUnitWorkItemTimeout value in sdk\test\UnitTests.proj)
+ private static readonly TimeSpan s_timeout = Environment.GetEnvironmentVariable("HELIX_WORK_ITEM_TIMEOUT") is { } value
+ ? TimeSpan.Parse(value).Subtract(TimeSpan.FromSeconds(10)) : TimeSpan.FromMinutes(1);
+
private readonly object _testOutputLock = new();
private Process _process;
@@ -70,8 +74,7 @@ public async Task GetOutputLineAsync(Predicate success, Predicat
{
using var cancellationOnFailure = new CancellationTokenSource();
- // cancel just before we hit 2 minute time out used on CI (sdk\test\UnitTests.proj)
- cancellationOnFailure.CancelAfter(TimeSpan.FromSeconds(110));
+ cancellationOnFailure.CancelAfter(s_timeout);
var failedLineCount = 0;
while (!_source.Completion.IsCompleted && failedLineCount == 0)