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 @@
 
   <ItemGroup>
     <ProjectReference Include="..\Microsoft.NET.Build.Extensions.Tasks\Microsoft.NET.Build.Extensions.Tasks.csproj" />
+    <ProjectReference Include="..\..\..\test\Microsoft.NET.TestFramework\Microsoft.NET.TestFramework.csproj" />
   </ItemGroup>
 
   <ItemGroup>
     <Compile Include="**\*.cs" />
-    <Compile Include="..\..\..\test\Common\Program.UnitTests.cs" />
+    <Compile Include="..\..\..\test\Common\Program.cs" />
     <Compile Include="..\Microsoft.NET.Build.Tasks.UnitTests\Mocks\MockBuildEngine.cs" />
     <Compile Include="..\Microsoft.NET.Build.Tasks.UnitTests\Mocks\MockTaskItem.cs" />
   </ItemGroup>
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 @@
 
   <ItemGroup>
     <Compile Include="**\*.cs" />
-    <Compile Include="..\..\..\test\Common\Program.UnitTests.cs" />
+    <Compile Include="..\..\..\test\Common\Program.cs" />
   </ItemGroup>
 
   <ItemGroup>
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<List<ITaskItem>> 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<List<ITaskItem>> PrepareWorkItem(ITaskItem xunitProject)
             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}";
+                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<string, string>()
                     {
                         { "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<string> TestConfigFiles { get; private set; } = new List<string>();
+        public List<(string name, string value)> EnvironmentVariables { get; set; } = [];
+
+        public List<string> TestConfigFiles { get; private set; } = [];
 
         public HashSet<string> TestListsToRun { get; private set; } = new HashSet<string>(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<string> GetXunitArgsFromTestConfig()
         {
             List<TestSpecifier> 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<string> GetOutputLineAsync(Predicate<string> 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)