From 5febea95c8fc85328525410f119d718b3543a3a1 Mon Sep 17 00:00:00 2001 From: Michael Simons Date: Fri, 10 Apr 2026 13:56:27 +0000 Subject: [PATCH 1/4] Reapply "Migrate test infrastructure from xUnit v2 to xUnit v3 (#52930)" This reverts commit f788ff9371956c17d96a500e4800a97092c437b4. --- Directory.Build.targets | 10 --- Directory.Packages.props | 13 ++-- build/SetupHelixEnvironment.cmd | 6 ++ build/SetupHelixEnvironment.sh | 6 ++ build/helix-debug-entitlements.plist | 8 +++ eng/Packages.props | 2 +- eng/Version.Details.props | 16 ++--- eng/Version.Details.xml | 8 +-- eng/Versions.props | 2 +- eng/dependabot/Packages.props | 2 +- ...DoNotUseCountWhenAnyCanBeUsedTests.Data.cs | 13 ++-- .../Test.Utilities/Test.Utilities.csproj | 2 +- test/Common/Program.cs | 32 --------- test/Directory.Build.props | 7 +- test/Directory.Build.targets | 7 +- ...omCreateXUnitWorkItemsWithTestExclusion.cs | 15 +++- .../DebugTestOutputLogger.cs | 18 ++++- ...oft.DotNet.HotReload.Test.Utilities.csproj | 1 + .../TestLogger.cs | 2 +- .../WatchSdkTest.cs | 2 +- .../WatchableApp.cs | 1 - .../DockerIsAvailableAndSupportsArchFact.cs | 11 ++- .../DockerIsAvailableAndSupportsArchTheory.cs | 11 ++- .../DockerRegistryManager.cs | 1 - .../DockerSupportsArchInlineData.cs | 20 ++++-- .../DockerTestsFixture.cs | 2 + .../DockerAvailableUtils.cs | 17 ++++- ...DepsJsonShouldContainVersionInformation.cs | 1 - .../CoreMSBuildAndWindowsOnlyFactAttribute.cs | 5 +- ...oreMSBuildAndWindowsOnlyTheoryAttribute.cs | 5 +- .../CoreMSBuildOnlyFactAttribute.cs | 5 +- .../CoreMSBuildOnlyTheoryAttribute.cs | 5 +- .../FullMSBuildOnlyFactAttribute.cs | 7 +- .../FullMSBuildOnlyTheoryAttribute.cs | 7 +- .../Attributes/MacOsOnlyFactAttribute.cs | 5 +- .../Attributes/PlatformSpecificFact.cs | 57 ++++----------- .../Attributes/PlatformSpecificTheory.cs | 41 ++++------- .../RequiresMSBuildVersionFactAttribute.cs | 5 +- .../RequiresMSBuildVersionTheoryAttribute.cs | 5 +- .../RequiresSpecificFrameworkFactAttribute.cs | 5 +- ...equiresSpecificFrameworkTheoryAttribute.cs | 6 +- ...OnlyRequiresMSBuildVersionFactAttribute.cs | 7 +- ...lyRequiresMSBuildVersionTheoryAttribute.cs | 5 +- .../Commands/SdkCommandSpec.cs | 12 ++++ .../Commands/TestCommand.cs | 9 ++- .../Microsoft.NET.TestFramework.csproj | 6 +- .../SharedTestOutputHelper.cs | 22 +++++- .../StringTestLogger.cs | 12 ++++ .../TestLoggerFactory.cs | 1 + ...rosoft.TemplateEngine.Cli.UnitTests.csproj | 6 +- .../Microsoft.Win32.Msi.Manual.Tests.csproj | 1 - .../Msbuild.Tests.Utilities.csproj | 1 - ...CommandLine.StaticCompletions.Tests.csproj | 3 +- test/UnitTests.proj | 3 +- .../ThirdPartyAnalyzerFormatterTests.cs | 6 +- .../XUnit/ConditionalFactAttribute.cs | 18 +++-- .../XUnit/MSBuildFactAttribute.cs | 25 +++++-- .../XUnit/MSBuildFactDiscoverer.cs | 28 -------- .../XUnit/MSBuildTestCase.cs | 72 ------------------- .../XUnit/MSBuildTheoryAttribute.cs | 28 ++++++-- .../XUnit/MSBuildTheoryDiscoverer.cs | 28 -------- .../Diagnostic/DiagnosticFixture.cs | 2 + .../Diagnostic/XunitNuGetLogger.cs | 3 +- .../DotnetNewDetailsTest.cs | 2 + .../DotnetNewHelpTests.Approval.cs | 18 +++-- .../DotnetNewInstallTests.cs | 3 +- .../DotnetNewTestTemplatesTests.cs | 6 ++ .../SharedHomeDirectory.cs | 2 +- .../TemplateDiscoveryTests.cs | 2 +- .../TemplateDiscoveryTool.cs | 2 +- .../WebProjectsTests.cs | 1 + .../dotnet-new.IntegrationTests.csproj | 6 +- .../TestUtilities/DotNetWatchTestBase.cs | 6 +- ...ModuleInitializer.cs => MSBuildFixture.cs} | 12 ++-- .../Package/Add/GivenDotnetPackageAdd.cs | 9 ++- .../Run/GivenDotnetRunIsInterrupted.cs | 4 ++ ...enDotnetTestBuildsAndRunsTestfromCsproj.cs | 4 +- test/dotnet.Tests/dotnet.Tests.csproj | 6 +- test/xunit-runner/XUnitRunner.targets | 24 ++++++- test/xunit.runner.json | 2 +- 80 files changed, 423 insertions(+), 378 deletions(-) create mode 100644 build/helix-debug-entitlements.plist delete mode 100644 test/Common/Program.cs delete mode 100644 test/dotnet-format.UnitTests/XUnit/MSBuildFactDiscoverer.cs delete mode 100644 test/dotnet-format.UnitTests/XUnit/MSBuildTestCase.cs delete mode 100644 test/dotnet-format.UnitTests/XUnit/MSBuildTheoryDiscoverer.cs rename test/dotnet-watch.Tests/TestUtilities/{ModuleInitializer.cs => MSBuildFixture.cs} (84%) diff --git a/Directory.Build.targets b/Directory.Build.targets index 057e70d4eb7d..0ef1d2edf1c4 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -25,16 +25,6 @@ - - - - - false - false - - diff --git a/Directory.Packages.props b/Directory.Packages.props index 21d623676105..bee4eacc21bf 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -54,7 +54,7 @@ - + @@ -75,11 +75,11 @@ - + - + - + @@ -144,10 +144,9 @@ - - - + + - $(NoWarn);NU5125;NU5123 + + $(NoWarn);NU5125;NU5123;xUnit1051 - false + + false embedded diff --git a/test/Directory.Build.targets b/test/Directory.Build.targets index edd1cbc3f09a..28bd22c5cdb6 100644 --- a/test/Directory.Build.targets +++ b/test/Directory.Build.targets @@ -5,6 +5,8 @@ + XUnitV3 + Exe true @@ -17,11 +19,6 @@ - - - - - diff --git a/test/HelixTasks/SDKCustomCreateXUnitWorkItemsWithTestExclusion.cs b/test/HelixTasks/SDKCustomCreateXUnitWorkItemsWithTestExclusion.cs index a3e8eb3e64e4..c8bfa9ef0dc4 100644 --- a/test/HelixTasks/SDKCustomCreateXUnitWorkItemsWithTestExclusion.cs +++ b/test/HelixTasks/SDKCustomCreateXUnitWorkItemsWithTestExclusion.cs @@ -36,6 +36,11 @@ public class SDKCustomCreateXUnitWorkItemsWithTestExclusion : Build.Utilities.Ta [Required] public bool IsPosixShell { get; set; } + /// + /// The runtime identifier of the target Helix queue (e.g. osx-arm64, linux-x64). + /// + public string TargetRid { get; set; } = ""; + /// /// Optional timeout for all created workitems /// Defaults to 300s @@ -158,7 +163,15 @@ private async Task ExecuteAsync() var testFilter = string.IsNullOrEmpty(assemblyPartitionInfo.ClassListArgumentString) ? "" : $"--filter \"{assemblyPartitionInfo.ClassListArgumentString}\""; - string command = $"{driver} test {assemblyName} -e HELIX_WORK_ITEM_TIMEOUT={timeout} {testExecutionDirectory} {msbuildAdditionalSdkResolverFolder} " + + // xUnit v3 tests run out-of-process: the VSTest adapter launches the AppHost executable. + // On POSIX, the execute bit is lost when the Helix SDK packages the payload as a zip archive, + // so we need to restore it before running. + string exeName = Path.GetFileNameWithoutExtension(assemblyName); + string chmodPrefix = IsPosixShell ? $"chmod +x {exeName} && " : ""; + // On macOS, ad-hoc sign the test exe with get-task-allow entitlement so createdump can attach via task_for_pid for crash dumps. + string codesignPrefix = IsPosixShell && TargetRid.StartsWith("osx") ? $"codesign -s - -f --entitlements $HELIX_CORRELATION_PAYLOAD/t/helix-debug-entitlements.plist {exeName} && " : ""; + + string command = $"{chmodPrefix}{codesignPrefix}{driver} test {assemblyName} -e HELIX_WORK_ITEM_TIMEOUT={timeout} {testExecutionDirectory} {msbuildAdditionalSdkResolverFolder} " + $"{(XUnitArguments != null ? " " + XUnitArguments : "")} --results-directory .{Path.DirectorySeparatorChar} --logger trx --logger \"console;verbosity=detailed\" --blame-hang --blame-hang-timeout 60m {testFilter} {enableDiagLogging} {arguments}"; Log.LogMessage($"Creating work item with properties Identity: {assemblyName}, PayloadDirectory: {publishDirectory}, Command: {command}"); diff --git a/test/Microsoft.DotNet.HotReload.Test.Utilities/DebugTestOutputLogger.cs b/test/Microsoft.DotNet.HotReload.Test.Utilities/DebugTestOutputLogger.cs index 411cbb360967..dd5cd0d09508 100644 --- a/test/Microsoft.DotNet.HotReload.Test.Utilities/DebugTestOutputLogger.cs +++ b/test/Microsoft.DotNet.HotReload.Test.Utilities/DebugTestOutputLogger.cs @@ -3,19 +3,35 @@ using System.Diagnostics; using System.Runtime.CompilerServices; -using Xunit.Abstractions; +using Xunit; namespace Microsoft.DotNet.Watch.UnitTests; public class DebugTestOutputLogger(ITestOutputHelper logger) : ITestOutputHelper { + private readonly StringBuilder _output = new(); + public event Action? OnMessage; + public string Output => _output.ToString(); + public void Log(string message, [CallerFilePath] string? testPath = null, [CallerLineNumber] int testLine = 0) => WriteLine($"[TEST {Path.GetFileName(testPath)}:{testLine}] {message}"); + public void Write(string message) + { + _output.Append(message); + Debug.Write(message); + logger.Write(message); + OnMessage?.Invoke(message); + } + + public void Write(string format, params object[] args) + => Write(string.Format(format, args)); + public void WriteLine(string message) { + _output.AppendLine(message); Debug.WriteLine(message); try diff --git a/test/Microsoft.DotNet.HotReload.Test.Utilities/Microsoft.DotNet.HotReload.Test.Utilities.csproj b/test/Microsoft.DotNet.HotReload.Test.Utilities/Microsoft.DotNet.HotReload.Test.Utilities.csproj index f267d2070886..113689abef9a 100644 --- a/test/Microsoft.DotNet.HotReload.Test.Utilities/Microsoft.DotNet.HotReload.Test.Utilities.csproj +++ b/test/Microsoft.DotNet.HotReload.Test.Utilities/Microsoft.DotNet.HotReload.Test.Utilities.csproj @@ -13,6 +13,7 @@ + diff --git a/test/Microsoft.DotNet.HotReload.Test.Utilities/TestLogger.cs b/test/Microsoft.DotNet.HotReload.Test.Utilities/TestLogger.cs index be7b5f747c80..0b8e8b83f2d1 100644 --- a/test/Microsoft.DotNet.HotReload.Test.Utilities/TestLogger.cs +++ b/test/Microsoft.DotNet.HotReload.Test.Utilities/TestLogger.cs @@ -3,7 +3,7 @@ using System.Collections.Immutable; using Microsoft.Extensions.Logging; -using Xunit.Abstractions; +using Xunit; namespace Microsoft.DotNet.Watch.UnitTests; diff --git a/test/Microsoft.DotNet.HotReload.Test.Utilities/WatchSdkTest.cs b/test/Microsoft.DotNet.HotReload.Test.Utilities/WatchSdkTest.cs index ece62d264dac..eb58e192490a 100644 --- a/test/Microsoft.DotNet.HotReload.Test.Utilities/WatchSdkTest.cs +++ b/test/Microsoft.DotNet.HotReload.Test.Utilities/WatchSdkTest.cs @@ -3,7 +3,7 @@ using System.Runtime.CompilerServices; using Microsoft.NET.TestFramework; -using Xunit.Abstractions; +using Xunit; namespace Microsoft.DotNet.Watch.UnitTests; diff --git a/test/Microsoft.DotNet.HotReload.Test.Utilities/WatchableApp.cs b/test/Microsoft.DotNet.HotReload.Test.Utilities/WatchableApp.cs index 2dacad1cb33a..8fa474f5caac 100644 --- a/test/Microsoft.DotNet.HotReload.Test.Utilities/WatchableApp.cs +++ b/test/Microsoft.DotNet.HotReload.Test.Utilities/WatchableApp.cs @@ -7,7 +7,6 @@ using Microsoft.DotNet.Cli.Utils; using Microsoft.NET.TestFramework; using Xunit; -using Xunit.Abstractions; namespace Microsoft.DotNet.Watch.UnitTests { diff --git a/test/Microsoft.NET.Build.Containers.IntegrationTests/DockerIsAvailableAndSupportsArchFact.cs b/test/Microsoft.NET.Build.Containers.IntegrationTests/DockerIsAvailableAndSupportsArchFact.cs index ae0e76f3c153..404b99624234 100644 --- a/test/Microsoft.NET.Build.Containers.IntegrationTests/DockerIsAvailableAndSupportsArchFact.cs +++ b/test/Microsoft.NET.Build.Containers.IntegrationTests/DockerIsAvailableAndSupportsArchFact.cs @@ -1,11 +1,18 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Runtime.CompilerServices; + namespace Microsoft.NET.Build.Containers.IntegrationTests; public class DockerIsAvailableAndSupportsArchFactAttribute : FactAttribute { - public DockerIsAvailableAndSupportsArchFactAttribute(string arch, bool checkContainerdStoreAvailability = false) + public DockerIsAvailableAndSupportsArchFactAttribute( + string arch, + bool checkContainerdStoreAvailability = false, + [CallerFilePath] string? sourceFilePath = null, + [CallerLineNumber] int sourceLineNumber = 0) + : base(sourceFilePath, sourceLineNumber) { if (!DockerSupportsArchHelper.DaemonIsAvailable) { diff --git a/test/Microsoft.NET.Build.Containers.IntegrationTests/DockerIsAvailableAndSupportsArchTheory.cs b/test/Microsoft.NET.Build.Containers.IntegrationTests/DockerIsAvailableAndSupportsArchTheory.cs index 382e57604fab..dffff31792da 100644 --- a/test/Microsoft.NET.Build.Containers.IntegrationTests/DockerIsAvailableAndSupportsArchTheory.cs +++ b/test/Microsoft.NET.Build.Containers.IntegrationTests/DockerIsAvailableAndSupportsArchTheory.cs @@ -1,11 +1,18 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Runtime.CompilerServices; + namespace Microsoft.NET.Build.Containers.IntegrationTests; public class DockerIsAvailableAndSupportsArchTheoryAttribute : TheoryAttribute { - public DockerIsAvailableAndSupportsArchTheoryAttribute(string arch, bool checkContainerdStoreAvailability = false) + public DockerIsAvailableAndSupportsArchTheoryAttribute( + string arch, + bool checkContainerdStoreAvailability = false, + [CallerFilePath] string? sourceFilePath = null, + [CallerLineNumber] int sourceLineNumber = 0) + : base(sourceFilePath, sourceLineNumber) { if (!DockerSupportsArchHelper.DaemonIsAvailable) { @@ -20,4 +27,4 @@ public DockerIsAvailableAndSupportsArchTheoryAttribute(string arch, bool checkCo base.Skip = $"Skipping test because Docker daemon does not support {arch}."; } } -} \ No newline at end of file +} diff --git a/test/Microsoft.NET.Build.Containers.IntegrationTests/DockerRegistryManager.cs b/test/Microsoft.NET.Build.Containers.IntegrationTests/DockerRegistryManager.cs index 5e3de400ddc3..e5b1de42ad57 100644 --- a/test/Microsoft.NET.Build.Containers.IntegrationTests/DockerRegistryManager.cs +++ b/test/Microsoft.NET.Build.Containers.IntegrationTests/DockerRegistryManager.cs @@ -3,7 +3,6 @@ using Microsoft.DotNet.Cli.Utils; using Microsoft.Extensions.Logging; -using Xunit.Sdk; namespace Microsoft.NET.Build.Containers.IntegrationTests; diff --git a/test/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchInlineData.cs b/test/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchInlineData.cs index 7944436b39c9..502bc3c98e22 100644 --- a/test/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchInlineData.cs +++ b/test/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchInlineData.cs @@ -3,7 +3,7 @@ using System.Reflection; using System.Text.Json; -using Xunit.Sdk; +using Xunit.v3; namespace Microsoft.NET.Build.Containers.IntegrationTests; @@ -18,17 +18,19 @@ public DockerSupportsArchInlineData(string arch, params object[] data) _data = data; } - public override IEnumerable GetData(MethodInfo testMethod) + public override bool SupportsDiscoveryEnumeration() => true; + + public override ValueTask> GetData(MethodInfo testMethod, Xunit.Sdk.DisposalTracker disposalTracker) { if (DockerSupportsArchHelper.DaemonSupportsArch(_arch)) { - return new object[][] { _data.Prepend(_arch).ToArray() }; + return new([ConvertDataRow(_data.Prepend(_arch).ToArray())]); } else { base.Skip = $"Skipping test because Docker daemon does not support {_arch}."; } - return Array.Empty(); + return new(Array.Empty()); } } @@ -103,6 +105,16 @@ private NullLogger() { } public static NullLogger Instance { get; } = new NullLogger(); + public string Output => string.Empty; + + public void Write(string message) + { + //do nothing + } + public void Write(string format, params object[] args) + { + //do nothing + } public void WriteLine(string message) { //do nothing diff --git a/test/Microsoft.NET.Build.Containers.IntegrationTests/DockerTestsFixture.cs b/test/Microsoft.NET.Build.Containers.IntegrationTests/DockerTestsFixture.cs index c417e23c6184..5a781aee4223 100644 --- a/test/Microsoft.NET.Build.Containers.IntegrationTests/DockerTestsFixture.cs +++ b/test/Microsoft.NET.Build.Containers.IntegrationTests/DockerTestsFixture.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using Xunit.Sdk; + namespace Microsoft.NET.Build.Containers.IntegrationTests; public sealed class DockerTestsFixture : IDisposable diff --git a/test/Microsoft.NET.Build.Containers.UnitTests/DockerAvailableUtils.cs b/test/Microsoft.NET.Build.Containers.UnitTests/DockerAvailableUtils.cs index b4f3c899b13c..585e359f213b 100644 --- a/test/Microsoft.NET.Build.Containers.UnitTests/DockerAvailableUtils.cs +++ b/test/Microsoft.NET.Build.Containers.UnitTests/DockerAvailableUtils.cs @@ -1,13 +1,19 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Runtime.CompilerServices; + namespace Microsoft.NET.Build.Containers.UnitTests; public class DockerAvailableTheoryAttribute : TheoryAttribute { public static string LocalRegistry => DockerCliStatus.LocalRegistry; - public DockerAvailableTheoryAttribute(bool skipPodman = false) + public DockerAvailableTheoryAttribute( + bool skipPodman = false, + [CallerFilePath] string? sourceFilePath = null, + [CallerLineNumber] int sourceLineNumber = 0) + : base(sourceFilePath, sourceLineNumber) { if (!DockerCliStatus.IsAvailable) { @@ -25,7 +31,12 @@ public class DockerAvailableFactAttribute : FactAttribute { public static string LocalRegistry => DockerCliStatus.LocalRegistry; - public DockerAvailableFactAttribute(bool skipPodman = false, bool checkContainerdStoreAvailability = false) + public DockerAvailableFactAttribute( + bool skipPodman = false, + bool checkContainerdStoreAvailability = false, + [CallerFilePath] string? sourceFilePath = null, + [CallerLineNumber] int sourceLineNumber = 0) + : base(sourceFilePath, sourceLineNumber) { if (!DockerCliStatus.IsAvailable) { @@ -38,7 +49,7 @@ public DockerAvailableFactAttribute(bool skipPodman = false, bool checkContainer else if (skipPodman && DockerCliStatus.Command == DockerCli.PodmanCommand) { base.Skip = $"Skipping test with {DockerCliStatus.Command} cli."; - } + } } } diff --git a/test/Microsoft.NET.Publish.Tests/GivenThatAPublishedDepsJsonShouldContainVersionInformation.cs b/test/Microsoft.NET.Publish.Tests/GivenThatAPublishedDepsJsonShouldContainVersionInformation.cs index f44f135cc36f..534429acbec9 100644 --- a/test/Microsoft.NET.Publish.Tests/GivenThatAPublishedDepsJsonShouldContainVersionInformation.cs +++ b/test/Microsoft.NET.Publish.Tests/GivenThatAPublishedDepsJsonShouldContainVersionInformation.cs @@ -5,7 +5,6 @@ using System.Runtime.CompilerServices; using Microsoft.Extensions.DependencyModel; -using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Newtonsoft.Json.Linq; using NuGet.Common; using NuGet.Frameworks; diff --git a/test/Microsoft.NET.TestFramework/Attributes/CoreMSBuildAndWindowsOnlyFactAttribute.cs b/test/Microsoft.NET.TestFramework/Attributes/CoreMSBuildAndWindowsOnlyFactAttribute.cs index ee3b0b7fac1b..cafada6546a2 100644 --- a/test/Microsoft.NET.TestFramework/Attributes/CoreMSBuildAndWindowsOnlyFactAttribute.cs +++ b/test/Microsoft.NET.TestFramework/Attributes/CoreMSBuildAndWindowsOnlyFactAttribute.cs @@ -1,11 +1,14 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Runtime.CompilerServices; + namespace Microsoft.NET.TestFramework { public class CoreMSBuildAndWindowsOnlyFactAttribute : FactAttribute { - public CoreMSBuildAndWindowsOnlyFactAttribute() + public CoreMSBuildAndWindowsOnlyFactAttribute([CallerFilePath] string? sourceFilePath = null, [CallerLineNumber] int sourceLineNumber = 0) + : base(sourceFilePath, sourceLineNumber) { if (SdkTestContext.Current.ToolsetUnderTest.ShouldUseFullFrameworkMSBuild || !RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { diff --git a/test/Microsoft.NET.TestFramework/Attributes/CoreMSBuildAndWindowsOnlyTheoryAttribute.cs b/test/Microsoft.NET.TestFramework/Attributes/CoreMSBuildAndWindowsOnlyTheoryAttribute.cs index 033d688f05a6..7eef533ff5a4 100644 --- a/test/Microsoft.NET.TestFramework/Attributes/CoreMSBuildAndWindowsOnlyTheoryAttribute.cs +++ b/test/Microsoft.NET.TestFramework/Attributes/CoreMSBuildAndWindowsOnlyTheoryAttribute.cs @@ -1,11 +1,14 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Runtime.CompilerServices; + namespace Microsoft.NET.TestFramework { public class CoreMSBuildAndWindowsOnlyTheoryAttribute : TheoryAttribute { - public CoreMSBuildAndWindowsOnlyTheoryAttribute() + public CoreMSBuildAndWindowsOnlyTheoryAttribute([CallerFilePath] string? sourceFilePath = null, [CallerLineNumber] int sourceLineNumber = 0) + : base(sourceFilePath, sourceLineNumber) { if (SdkTestContext.Current.ToolsetUnderTest.ShouldUseFullFrameworkMSBuild || !RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { diff --git a/test/Microsoft.NET.TestFramework/Attributes/CoreMSBuildOnlyFactAttribute.cs b/test/Microsoft.NET.TestFramework/Attributes/CoreMSBuildOnlyFactAttribute.cs index cd9c7efd0472..4ba1ef85ae9b 100644 --- a/test/Microsoft.NET.TestFramework/Attributes/CoreMSBuildOnlyFactAttribute.cs +++ b/test/Microsoft.NET.TestFramework/Attributes/CoreMSBuildOnlyFactAttribute.cs @@ -1,11 +1,14 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Runtime.CompilerServices; + namespace Microsoft.NET.TestFramework { public class CoreMSBuildOnlyFactAttribute : FactAttribute { - public CoreMSBuildOnlyFactAttribute() + public CoreMSBuildOnlyFactAttribute([CallerFilePath] string? sourceFilePath = null, [CallerLineNumber] int sourceLineNumber = 0) + : base(sourceFilePath, sourceLineNumber) { if (SdkTestContext.Current.ToolsetUnderTest.ShouldUseFullFrameworkMSBuild) { diff --git a/test/Microsoft.NET.TestFramework/Attributes/CoreMSBuildOnlyTheoryAttribute.cs b/test/Microsoft.NET.TestFramework/Attributes/CoreMSBuildOnlyTheoryAttribute.cs index 729d2e6848ba..67fc95f1f41f 100644 --- a/test/Microsoft.NET.TestFramework/Attributes/CoreMSBuildOnlyTheoryAttribute.cs +++ b/test/Microsoft.NET.TestFramework/Attributes/CoreMSBuildOnlyTheoryAttribute.cs @@ -1,11 +1,14 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Runtime.CompilerServices; + namespace Microsoft.NET.TestFramework { public class CoreMSBuildOnlyTheoryAttribute : TheoryAttribute { - public CoreMSBuildOnlyTheoryAttribute() + public CoreMSBuildOnlyTheoryAttribute([CallerFilePath] string? sourceFilePath = null, [CallerLineNumber] int sourceLineNumber = 0) + : base(sourceFilePath, sourceLineNumber) { if (SdkTestContext.Current.ToolsetUnderTest.ShouldUseFullFrameworkMSBuild) { diff --git a/test/Microsoft.NET.TestFramework/Attributes/FullMSBuildOnlyFactAttribute.cs b/test/Microsoft.NET.TestFramework/Attributes/FullMSBuildOnlyFactAttribute.cs index e9ce263c7325..a59f0473eed7 100644 --- a/test/Microsoft.NET.TestFramework/Attributes/FullMSBuildOnlyFactAttribute.cs +++ b/test/Microsoft.NET.TestFramework/Attributes/FullMSBuildOnlyFactAttribute.cs @@ -1,11 +1,14 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Runtime.CompilerServices; + namespace Microsoft.NET.TestFramework { public class FullMSBuildOnlyFactAttribute : FactAttribute { - public FullMSBuildOnlyFactAttribute() + public FullMSBuildOnlyFactAttribute([CallerFilePath] string? sourceFilePath = null, [CallerLineNumber] int sourceLineNumber = 0) + : base(sourceFilePath, sourceLineNumber) { if (!SdkTestContext.Current.ToolsetUnderTest.ShouldUseFullFrameworkMSBuild) { diff --git a/test/Microsoft.NET.TestFramework/Attributes/FullMSBuildOnlyTheoryAttribute.cs b/test/Microsoft.NET.TestFramework/Attributes/FullMSBuildOnlyTheoryAttribute.cs index e1f35bf516bf..d4435af75e75 100644 --- a/test/Microsoft.NET.TestFramework/Attributes/FullMSBuildOnlyTheoryAttribute.cs +++ b/test/Microsoft.NET.TestFramework/Attributes/FullMSBuildOnlyTheoryAttribute.cs @@ -1,11 +1,14 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Runtime.CompilerServices; + namespace Microsoft.NET.TestFramework { public class FullMSBuildOnlyTheoryAttribute : TheoryAttribute { - public FullMSBuildOnlyTheoryAttribute() + public FullMSBuildOnlyTheoryAttribute([CallerFilePath] string? sourceFilePath = null, [CallerLineNumber] int sourceLineNumber = 0) + : base(sourceFilePath, sourceLineNumber) { if (!SdkTestContext.Current.ToolsetUnderTest.ShouldUseFullFrameworkMSBuild) { diff --git a/test/Microsoft.NET.TestFramework/Attributes/MacOsOnlyFactAttribute.cs b/test/Microsoft.NET.TestFramework/Attributes/MacOsOnlyFactAttribute.cs index c3eabb4b9065..d007a9b3110d 100644 --- a/test/Microsoft.NET.TestFramework/Attributes/MacOsOnlyFactAttribute.cs +++ b/test/Microsoft.NET.TestFramework/Attributes/MacOsOnlyFactAttribute.cs @@ -1,11 +1,14 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Runtime.CompilerServices; + namespace Microsoft.NET.TestFramework { public class MacOsOnlyFactAttribute : FactAttribute { - public MacOsOnlyFactAttribute() + public MacOsOnlyFactAttribute([CallerFilePath] string? sourceFilePath = null, [CallerLineNumber] int sourceLineNumber = 0) + : base(sourceFilePath, sourceLineNumber) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { diff --git a/test/Microsoft.NET.TestFramework/Attributes/PlatformSpecificFact.cs b/test/Microsoft.NET.TestFramework/Attributes/PlatformSpecificFact.cs index bb521f0a1ffe..6089d870179e 100644 --- a/test/Microsoft.NET.TestFramework/Attributes/PlatformSpecificFact.cs +++ b/test/Microsoft.NET.TestFramework/Attributes/PlatformSpecificFact.cs @@ -1,59 +1,30 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Runtime.CompilerServices; + namespace Microsoft.NET.TestFramework { /// /// Controls which platforms and architectures a test should run on or be skipped on. - /// The constructor parameter specifies platforms to include (run on). - /// Named properties , , and - /// provide additional filtering. + /// specifies platforms to include (run on). + /// Optional parameters provide additional skip-based filtering. /// public class PlatformSpecificFact : FactAttribute { internal const Architecture NoArchitectureFilter = (Architecture)(-1); - private readonly TestPlatforms _platforms; - private string? _skip; - - public PlatformSpecificFact() : this(TestPlatforms.Any) - { - } - - public PlatformSpecificFact(TestPlatforms platforms) - { - _platforms = platforms; - } - - /// - /// Platforms to skip on, even if included by the constructor parameter. - /// When is also set, both must match for the test to be skipped. - /// - public TestPlatforms SkipPlatforms { get; set; } - - /// - /// Restrict the test to run only on this process architecture. - /// Tests on other architectures are skipped. - /// - public Architecture Architecture { get; set; } = NoArchitectureFilter; - - /// - /// Architecture to skip on. When is also set, - /// both must match for the test to be skipped. When used alone, skips on the - /// specified architecture regardless of platform. - /// - public Architecture SkipArchitecture { get; set; } = NoArchitectureFilter; - - /// - /// Reason or tracking issue URL for why the test is skipped. - /// Used as the Skip message when a skip condition matches. - /// - public string? SkipReason { get; set; } - - public override string? Skip + public PlatformSpecificFact( + TestPlatforms platforms = TestPlatforms.Any, + TestPlatforms skipPlatforms = 0, + Architecture architecture = NoArchitectureFilter, + Architecture skipArchitecture = NoArchitectureFilter, + string? skipReason = null, + [CallerFilePath] string? sourceFilePath = null, + [CallerLineNumber] int sourceLineNumber = 0) + : base(sourceFilePath, sourceLineNumber) { - get => _skip ?? EvaluateSkip(_platforms, SkipPlatforms, Architecture, SkipArchitecture, SkipReason); - set => _skip = value; + Skip = EvaluateSkip(platforms, skipPlatforms, architecture, skipArchitecture, skipReason); } internal static string? EvaluateSkip( diff --git a/test/Microsoft.NET.TestFramework/Attributes/PlatformSpecificTheory.cs b/test/Microsoft.NET.TestFramework/Attributes/PlatformSpecificTheory.cs index 81a1cd634e7e..d44e14e1115d 100644 --- a/test/Microsoft.NET.TestFramework/Attributes/PlatformSpecificTheory.cs +++ b/test/Microsoft.NET.TestFramework/Attributes/PlatformSpecificTheory.cs @@ -1,42 +1,27 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Runtime.CompilerServices; + namespace Microsoft.NET.TestFramework { /// /// Controls which platforms and architectures a theory should run on or be skipped on. - /// See for full documentation on the filtering properties. + /// See for full documentation on the filtering parameters. /// public class PlatformSpecificTheory : TheoryAttribute { - private readonly TestPlatforms _platforms; - private string? _skip; - - public PlatformSpecificTheory() : this(TestPlatforms.Any) - { - } - - public PlatformSpecificTheory(TestPlatforms platforms) - { - _platforms = platforms; - } - - /// - public TestPlatforms SkipPlatforms { get; set; } - - /// - public Architecture Architecture { get; set; } = PlatformSpecificFact.NoArchitectureFilter; - - /// - public Architecture SkipArchitecture { get; set; } = PlatformSpecificFact.NoArchitectureFilter; - - /// - public string? SkipReason { get; set; } - - public override string? Skip + public PlatformSpecificTheory( + TestPlatforms platforms = TestPlatforms.Any, + TestPlatforms skipPlatforms = 0, + Architecture architecture = PlatformSpecificFact.NoArchitectureFilter, + Architecture skipArchitecture = PlatformSpecificFact.NoArchitectureFilter, + string? skipReason = null, + [CallerFilePath] string? sourceFilePath = null, + [CallerLineNumber] int sourceLineNumber = 0) + : base(sourceFilePath, sourceLineNumber) { - get => _skip ?? PlatformSpecificFact.EvaluateSkip(_platforms, SkipPlatforms, Architecture, SkipArchitecture, SkipReason); - set => _skip = value; + Skip = PlatformSpecificFact.EvaluateSkip(platforms, skipPlatforms, architecture, skipArchitecture, skipReason); } } } diff --git a/test/Microsoft.NET.TestFramework/Attributes/RequiresMSBuildVersionFactAttribute.cs b/test/Microsoft.NET.TestFramework/Attributes/RequiresMSBuildVersionFactAttribute.cs index ae359363546e..cdc9ba84f4d5 100644 --- a/test/Microsoft.NET.TestFramework/Attributes/RequiresMSBuildVersionFactAttribute.cs +++ b/test/Microsoft.NET.TestFramework/Attributes/RequiresMSBuildVersionFactAttribute.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Runtime.CompilerServices; + namespace Microsoft.NET.TestFramework { public class RequiresMSBuildVersionFactAttribute : FactAttribute @@ -10,7 +12,8 @@ public class RequiresMSBuildVersionFactAttribute : FactAttribute /// public string? Reason { get; set; } - public RequiresMSBuildVersionFactAttribute(string version) + public RequiresMSBuildVersionFactAttribute(string version, [CallerFilePath] string? sourceFilePath = null, [CallerLineNumber] int sourceLineNumber = 0) + : base(sourceFilePath, sourceLineNumber) { RequiresMSBuildVersionTheoryAttribute.CheckForRequiredMSBuildVersion(this, version); } diff --git a/test/Microsoft.NET.TestFramework/Attributes/RequiresMSBuildVersionTheoryAttribute.cs b/test/Microsoft.NET.TestFramework/Attributes/RequiresMSBuildVersionTheoryAttribute.cs index b09480505f59..23f9bd6138ac 100644 --- a/test/Microsoft.NET.TestFramework/Attributes/RequiresMSBuildVersionTheoryAttribute.cs +++ b/test/Microsoft.NET.TestFramework/Attributes/RequiresMSBuildVersionTheoryAttribute.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Runtime.CompilerServices; + namespace Microsoft.NET.TestFramework { public class RequiresMSBuildVersionTheoryAttribute : TheoryAttribute @@ -10,7 +12,8 @@ public class RequiresMSBuildVersionTheoryAttribute : TheoryAttribute /// public string? Reason { get; set; } - public RequiresMSBuildVersionTheoryAttribute(string version) + public RequiresMSBuildVersionTheoryAttribute(string version, [CallerFilePath] string? sourceFilePath = null, [CallerLineNumber] int sourceLineNumber = 0) + : base(sourceFilePath, sourceLineNumber) { CheckForRequiredMSBuildVersion(this, version); } diff --git a/test/Microsoft.NET.TestFramework/Attributes/RequiresSpecificFrameworkFactAttribute.cs b/test/Microsoft.NET.TestFramework/Attributes/RequiresSpecificFrameworkFactAttribute.cs index 8bbd151553f6..d3a76dcd5e97 100644 --- a/test/Microsoft.NET.TestFramework/Attributes/RequiresSpecificFrameworkFactAttribute.cs +++ b/test/Microsoft.NET.TestFramework/Attributes/RequiresSpecificFrameworkFactAttribute.cs @@ -3,11 +3,14 @@ #if NETCOREAPP +using System.Runtime.CompilerServices; + namespace Microsoft.NET.TestFramework { public class RequiresSpecificFrameworkFactAttribute : FactAttribute { - public RequiresSpecificFrameworkFactAttribute(string framework) + public RequiresSpecificFrameworkFactAttribute(string framework, [CallerFilePath] string? sourceFilePath = null, [CallerLineNumber] int sourceLineNumber = 0) + : base(sourceFilePath, sourceLineNumber) { if (!EnvironmentInfo.SupportsTargetFramework(framework)) { diff --git a/test/Microsoft.NET.TestFramework/Attributes/RequiresSpecificFrameworkTheoryAttribute.cs b/test/Microsoft.NET.TestFramework/Attributes/RequiresSpecificFrameworkTheoryAttribute.cs index 65550cdb8879..95d563362313 100644 --- a/test/Microsoft.NET.TestFramework/Attributes/RequiresSpecificFrameworkTheoryAttribute.cs +++ b/test/Microsoft.NET.TestFramework/Attributes/RequiresSpecificFrameworkTheoryAttribute.cs @@ -1,15 +1,17 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. #if NETCOREAPP +using System.Runtime.CompilerServices; using Microsoft.DotNet.Tools.Test.Utilities; namespace Microsoft.NET.TestFramework { public class RequiresSpecificFrameworkTheoryAttribute : TheoryAttribute { - public RequiresSpecificFrameworkTheoryAttribute(string framework) + public RequiresSpecificFrameworkTheoryAttribute(string framework, [CallerFilePath] string? sourceFilePath = null, [CallerLineNumber] int sourceLineNumber = 0) + : base(sourceFilePath, sourceLineNumber) { if (!EnvironmentInfo.SupportsTargetFramework(framework)) { diff --git a/test/Microsoft.NET.TestFramework/Attributes/WindowsOnlyRequiresMSBuildVersionFactAttribute.cs b/test/Microsoft.NET.TestFramework/Attributes/WindowsOnlyRequiresMSBuildVersionFactAttribute.cs index 73121c780841..55df869206e1 100644 --- a/test/Microsoft.NET.TestFramework/Attributes/WindowsOnlyRequiresMSBuildVersionFactAttribute.cs +++ b/test/Microsoft.NET.TestFramework/Attributes/WindowsOnlyRequiresMSBuildVersionFactAttribute.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Runtime.CompilerServices; + namespace Microsoft.NET.TestFramework { public class WindowsOnlyRequiresMSBuildVersionFactAttribute : FactAttribute @@ -9,8 +11,9 @@ public class WindowsOnlyRequiresMSBuildVersionFactAttribute : FactAttribute /// Gets or sets the reason for potentially skipping the test if conditions are not met. /// public string? Reason { get; set; } - - public WindowsOnlyRequiresMSBuildVersionFactAttribute(string version) + + public WindowsOnlyRequiresMSBuildVersionFactAttribute(string version, [CallerFilePath] string? sourceFilePath = null, [CallerLineNumber] int sourceLineNumber = 0) + : base(sourceFilePath, sourceLineNumber) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { diff --git a/test/Microsoft.NET.TestFramework/Attributes/WindowsOnlyRequiresMSBuildVersionTheoryAttribute.cs b/test/Microsoft.NET.TestFramework/Attributes/WindowsOnlyRequiresMSBuildVersionTheoryAttribute.cs index f9c43590585e..2b8664843cfe 100644 --- a/test/Microsoft.NET.TestFramework/Attributes/WindowsOnlyRequiresMSBuildVersionTheoryAttribute.cs +++ b/test/Microsoft.NET.TestFramework/Attributes/WindowsOnlyRequiresMSBuildVersionTheoryAttribute.cs @@ -1,11 +1,14 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Runtime.CompilerServices; + namespace Microsoft.NET.TestFramework { public class WindowsOnlyRequiresMSBuildVersionTheoryAttribute : TheoryAttribute { - public WindowsOnlyRequiresMSBuildVersionTheoryAttribute(string version) + public WindowsOnlyRequiresMSBuildVersionTheoryAttribute(string version, [CallerFilePath] string? sourceFilePath = null, [CallerLineNumber] int sourceLineNumber = 0) + : base(sourceFilePath, sourceLineNumber) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { diff --git a/test/Microsoft.NET.TestFramework/Commands/SdkCommandSpec.cs b/test/Microsoft.NET.TestFramework/Commands/SdkCommandSpec.cs index 9bc513f6ff6c..6ae759d32a0e 100644 --- a/test/Microsoft.NET.TestFramework/Commands/SdkCommandSpec.cs +++ b/test/Microsoft.NET.TestFramework/Commands/SdkCommandSpec.cs @@ -3,6 +3,9 @@ using System.Diagnostics; using Microsoft.DotNet.Cli.Utils; +#if NET +using System.Runtime.InteropServices; +#endif namespace Microsoft.NET.TestFramework.Commands { @@ -21,6 +24,8 @@ public class SdkCommandSpec public bool DisableOutputAndErrorRedirection { get; set; } + public bool CreateNewProcessGroup { get; set; } + private string EscapeArgs() { // Note: this doesn't handle invoking .cmd files via "cmd /c" on Windows, which probably won't be necessary here @@ -61,6 +66,13 @@ public ProcessStartInfo ToProcessStartInfo(bool doNotEscapeArguments = false) ret.WorkingDirectory = WorkingDirectory; } +#if NET + if (CreateNewProcessGroup && RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + ret.CreateNewProcessGroup = true; + } +#endif + return ret; } } diff --git a/test/Microsoft.NET.TestFramework/Commands/TestCommand.cs b/test/Microsoft.NET.TestFramework/Commands/TestCommand.cs index 56cf1002d2ae..6bb45283efe5 100644 --- a/test/Microsoft.NET.TestFramework/Commands/TestCommand.cs +++ b/test/Microsoft.NET.TestFramework/Commands/TestCommand.cs @@ -24,6 +24,12 @@ public abstract class TestCommand public bool DisableOutputAndErrorRedirection { get; set; } + /// + /// When true, the child process is launched in a new process group so that + /// console signals (e.g. Ctrl+C) sent to it do not propagate to the test host. + /// + public bool CreateNewProcessGroup { get; set; } + // These only work via Execute(), not when using GetProcessStartInfo() public Action? CommandOutputHandler { get; set; } public Action? ProcessStartedHandler { get; set; } @@ -116,6 +122,7 @@ private SdkCommandSpec CreateCommandSpec(IEnumerable args) commandSpec.RedirectStandardInput = RedirectStandardInput; commandSpec.DisableOutputAndErrorRedirection = DisableOutputAndErrorRedirection; + commandSpec.CreateNewProcessGroup = CreateNewProcessGroup; return commandSpec; } @@ -203,7 +210,7 @@ public virtual CommandResult Execute(IEnumerable args) public static void LogCommandResult(ITestOutputHelper log, CommandResult result) { log.WriteLine($"> {result.StartInfo.FileName} {result.StartInfo.Arguments}"); - log.WriteLine(result.StdOut); + log.WriteLine(result.StdOut ?? string.Empty); if (!string.IsNullOrEmpty(result.StdErr)) { diff --git a/test/Microsoft.NET.TestFramework/Microsoft.NET.TestFramework.csproj b/test/Microsoft.NET.TestFramework/Microsoft.NET.TestFramework.csproj index 6751b7251938..6aea55861808 100644 --- a/test/Microsoft.NET.TestFramework/Microsoft.NET.TestFramework.csproj +++ b/test/Microsoft.NET.TestFramework/Microsoft.NET.TestFramework.csproj @@ -60,12 +60,13 @@ --> - + - + + @@ -75,7 +76,6 @@ - diff --git a/test/Microsoft.NET.TestFramework/SharedTestOutputHelper.cs b/test/Microsoft.NET.TestFramework/SharedTestOutputHelper.cs index d3591610f624..c7f1a510fbbd 100644 --- a/test/Microsoft.NET.TestFramework/SharedTestOutputHelper.cs +++ b/test/Microsoft.NET.TestFramework/SharedTestOutputHelper.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using Xunit.Sdk; +using Xunit.v3; namespace Microsoft.NET.TestFramework; @@ -12,19 +13,38 @@ namespace Microsoft.NET.TestFramework; public class SharedTestOutputHelper : ITestOutputHelper { private readonly IMessageSink _sink; + private readonly StringBuilder _output = new(); public SharedTestOutputHelper(IMessageSink sink) { _sink = sink; } + public string Output => _output.ToString(); + + public void Write(string message) + { + _output.Append(message); + _sink.OnMessage(new DiagnosticMessage(message)); + } + + public void Write(string format, params object[] args) + { + var formatted = string.Format(format, args); + _output.Append(formatted); + _sink.OnMessage(new DiagnosticMessage(formatted)); + } + public void WriteLine(string message) { + _output.AppendLine(message); _sink.OnMessage(new DiagnosticMessage(message)); } public void WriteLine(string format, params object[] args) { - _sink.OnMessage(new DiagnosticMessage(format, args)); + var formatted = string.Format(format, args); + _output.AppendLine(formatted); + _sink.OnMessage(new DiagnosticMessage(formatted)); } } diff --git a/test/Microsoft.NET.TestFramework/StringTestLogger.cs b/test/Microsoft.NET.TestFramework/StringTestLogger.cs index a5323123fba4..1d08f176332b 100644 --- a/test/Microsoft.NET.TestFramework/StringTestLogger.cs +++ b/test/Microsoft.NET.TestFramework/StringTestLogger.cs @@ -7,6 +7,18 @@ public class StringTestLogger : ITestOutputHelper { StringBuilder _stringBuilder = new(); + public string Output => _stringBuilder.ToString(); + + public void Write(string message) + { + _stringBuilder.Append(message); + } + + public void Write(string format, params object[] args) + { + _stringBuilder.Append(string.Format(format, args)); + } + public void WriteLine(string message) { _stringBuilder.AppendLine(message); diff --git a/test/Microsoft.NET.TestFramework/TestLoggerFactory.cs b/test/Microsoft.NET.TestFramework/TestLoggerFactory.cs index 40ec82ed3041..02982eb098a9 100644 --- a/test/Microsoft.NET.TestFramework/TestLoggerFactory.cs +++ b/test/Microsoft.NET.TestFramework/TestLoggerFactory.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.Extensions.Logging; +using Xunit.Sdk; namespace Microsoft.NET.TestFramework { diff --git a/test/Microsoft.TemplateEngine.Cli.UnitTests/Microsoft.TemplateEngine.Cli.UnitTests.csproj b/test/Microsoft.TemplateEngine.Cli.UnitTests/Microsoft.TemplateEngine.Cli.UnitTests.csproj index bf6139c9af64..b1e1394cf095 100644 --- a/test/Microsoft.TemplateEngine.Cli.UnitTests/Microsoft.TemplateEngine.Cli.UnitTests.csproj +++ b/test/Microsoft.TemplateEngine.Cli.UnitTests/Microsoft.TemplateEngine.Cli.UnitTests.csproj @@ -16,10 +16,10 @@ - + - - + + diff --git a/test/Microsoft.Win32.Msi.Manual.Tests/Microsoft.Win32.Msi.Manual.Tests.csproj b/test/Microsoft.Win32.Msi.Manual.Tests/Microsoft.Win32.Msi.Manual.Tests.csproj index a1465b5f3026..c13835ee50fe 100644 --- a/test/Microsoft.Win32.Msi.Manual.Tests/Microsoft.Win32.Msi.Manual.Tests.csproj +++ b/test/Microsoft.Win32.Msi.Manual.Tests/Microsoft.Win32.Msi.Manual.Tests.csproj @@ -22,6 +22,5 @@ - diff --git a/test/Msbuild.Tests.Utilities/Msbuild.Tests.Utilities.csproj b/test/Msbuild.Tests.Utilities/Msbuild.Tests.Utilities.csproj index 9df4924b8ce4..889e80b3c357 100644 --- a/test/Msbuild.Tests.Utilities/Msbuild.Tests.Utilities.csproj +++ b/test/Msbuild.Tests.Utilities/Msbuild.Tests.Utilities.csproj @@ -16,7 +16,6 @@ - diff --git a/test/System.CommandLine.StaticCompletions.Tests/System.CommandLine.StaticCompletions.Tests.csproj b/test/System.CommandLine.StaticCompletions.Tests/System.CommandLine.StaticCompletions.Tests.csproj index b5959c334766..349ac13587c3 100644 --- a/test/System.CommandLine.StaticCompletions.Tests/System.CommandLine.StaticCompletions.Tests.csproj +++ b/test/System.CommandLine.StaticCompletions.Tests/System.CommandLine.StaticCompletions.Tests.csproj @@ -10,8 +10,7 @@ - - + diff --git a/test/UnitTests.proj b/test/UnitTests.proj index 2eef751199e5..0920f1672a93 100644 --- a/test/UnitTests.proj +++ b/test/UnitTests.proj @@ -114,6 +114,7 @@ + @@ -141,7 +142,7 @@ . $HELIX_CORRELATION_PAYLOAD/t/SetupHelixEnvironment.sh;$(HelixPreCommands) PowerShell -ExecutionPolicy ByPass "dotnet nuget locals all -l | ForEach-Object { $_.Split(' ')[1]} | Where-Object{$_ -like '*cache'} | Get-ChildItem -Recurse -File -Filter '*.dat' | Measure";$(HelixPostCommands) PowerShell -ExecutionPolicy ByPass "Get-ChildItem -Recurse -File -Filter '*hangdump.dmp' | Copy-Item -Destination $env:HELIX_WORKITEM_UPLOAD_ROOT";$(HelixPostCommands) - find "$HELIX_WORKITEM_UPLOAD_ROOT/../../.." -name '*hangdump.dmp' -exec cp {} "$HELIX_WORKITEM_UPLOAD_ROOT" \;;$(HelixPostCommands) + find "$HELIX_WORKITEM_UPLOAD_ROOT/../../.." -name '*hangdump.dmp' -print0 | xargs -0 -I@ cp @ "$HELIX_WORKITEM_UPLOAD_ROOT";$(HelixPostCommands) $(Version) $(RepoRoot)artifacts\bin\Microsoft.DotNet.MSBuildSdkResolver $(RepoRoot)artifacts\tmp\HelixStage0.tar.gz diff --git a/test/dotnet-format.UnitTests/Analyzers/ThirdPartyAnalyzerFormatterTests.cs b/test/dotnet-format.UnitTests/Analyzers/ThirdPartyAnalyzerFormatterTests.cs index 07fae27e9946..939463a91d90 100644 --- a/test/dotnet-format.UnitTests/Analyzers/ThirdPartyAnalyzerFormatterTests.cs +++ b/test/dotnet-format.UnitTests/Analyzers/ThirdPartyAnalyzerFormatterTests.cs @@ -26,7 +26,7 @@ public ThirdPartyAnalyzerFormatterTests(ITestOutputHelper output) TestOutputHelper = output; } - public async Task InitializeAsync() + public async ValueTask InitializeAsync() { var logger = new TestLogger(); @@ -52,11 +52,11 @@ public async Task InitializeAsync() } } - public Task DisposeAsync() + public ValueTask DisposeAsync() { _analyzerReferencesProject = null; - return Task.CompletedTask; + return ValueTask.CompletedTask; } private IEnumerable GetAnalyzerReferences(string prefix) diff --git a/test/dotnet-format.UnitTests/XUnit/ConditionalFactAttribute.cs b/test/dotnet-format.UnitTests/XUnit/ConditionalFactAttribute.cs index e887376ef1cf..c7434c6c9d1e 100644 --- a/test/dotnet-format.UnitTests/XUnit/ConditionalFactAttribute.cs +++ b/test/dotnet-format.UnitTests/XUnit/ConditionalFactAttribute.cs @@ -1,6 +1,8 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Runtime.CompilerServices; + namespace Microsoft.CodeAnalysis.Tools.Tests.XUnit { public class ConditionalFactAttribute : FactAttribute @@ -11,7 +13,7 @@ public class ConditionalFactAttribute : FactAttribute /// skipped vs. conditionally skipped which is the entire point of this attribute. /// [Obsolete("ConditionalFact should use Reason or AlwaysSkip", error: true)] - public new string Skip + public new string? Skip { get => base.Skip; set => base.Skip = value; @@ -21,7 +23,7 @@ public class ConditionalFactAttribute : FactAttribute /// Used to unconditionally Skip a test. For the rare occasion when a conditional test needs to be /// unconditionally skipped (typically short term for a bug to be fixed). /// - public string AlwaysSkip + public string? AlwaysSkip { get => base.Skip; set => base.Skip = value; @@ -29,7 +31,8 @@ public string AlwaysSkip public string? Reason { get; set; } - public ConditionalFactAttribute(params Type[] skipConditions) + public ConditionalFactAttribute(Type[] skipConditions, [CallerFilePath] string? sourceFilePath = null, [CallerLineNumber] int sourceLineNumber = 0) + : base(sourceFilePath, sourceLineNumber) { foreach (var skipCondition in skipConditions) { @@ -51,7 +54,7 @@ public class ConditionalTheoryAttribute : TheoryAttribute /// skipped vs. conditionally skipped which is the entire point of this attribute. /// [Obsolete("ConditionalTheory should use Reason or AlwaysSkip")] - public new string Skip + public new string? Skip { get => base.Skip; set => base.Skip = value; @@ -61,7 +64,7 @@ public class ConditionalTheoryAttribute : TheoryAttribute /// Used to unconditionally Skip a test. For the rare occasion when a conditional test needs to be /// unconditionally skipped (typically short term for a bug to be fixed). /// - public string AlwaysSkip + public string? AlwaysSkip { get => base.Skip; set => base.Skip = value; @@ -69,7 +72,8 @@ public string AlwaysSkip public string? Reason { get; set; } - public ConditionalTheoryAttribute(params Type[] skipConditions) + public ConditionalTheoryAttribute(Type[] skipConditions, [CallerFilePath] string? sourceFilePath = null, [CallerLineNumber] int sourceLineNumber = 0) + : base(sourceFilePath, sourceLineNumber) { foreach (var skipCondition in skipConditions) { diff --git a/test/dotnet-format.UnitTests/XUnit/MSBuildFactAttribute.cs b/test/dotnet-format.UnitTests/XUnit/MSBuildFactAttribute.cs index 384f87887586..97caeb8011b7 100644 --- a/test/dotnet-format.UnitTests/XUnit/MSBuildFactAttribute.cs +++ b/test/dotnet-format.UnitTests/XUnit/MSBuildFactAttribute.cs @@ -1,17 +1,34 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Xunit.Sdk; +using System.Reflection; +using System.Runtime.CompilerServices; +using Microsoft.CodeAnalysis.Tools.Workspaces; +using Xunit.v3; namespace Microsoft.CodeAnalysis.Tools.Tests.XUnit { [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] - [XunitTestCaseDiscoverer("Microsoft.CodeAnalysis.Tools.Tests.XUnit.MSBuildFactDiscoverer", "dotnet-format.UnitTests")] - public sealed class MSBuildFactAttribute : ConditionalFactAttribute + public sealed class MSBuildFactAttribute : ConditionalFactAttribute, IBeforeAfterTestAttribute { public MSBuildFactAttribute(params Type[] skipConditions) : base(skipConditions) { } + + public MSBuildFactAttribute([CallerFilePath] string? sourceFilePath = null, [CallerLineNumber] int sourceLineNumber = 0) + : base(Array.Empty(), sourceFilePath, sourceLineNumber) + { + } + + public void Before(MethodInfo methodUnderTest, IXunitTest test) + { + MSBuildWorkspaceLoader.Guard.Wait(); + } + + public void After(MethodInfo methodUnderTest, IXunitTest test) + { + MSBuildWorkspaceLoader.Guard.Release(); + } } } diff --git a/test/dotnet-format.UnitTests/XUnit/MSBuildFactDiscoverer.cs b/test/dotnet-format.UnitTests/XUnit/MSBuildFactDiscoverer.cs deleted file mode 100644 index 6262d6d78a66..000000000000 --- a/test/dotnet-format.UnitTests/XUnit/MSBuildFactDiscoverer.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Xunit.Sdk; - -namespace Microsoft.CodeAnalysis.Tools.Tests.XUnit -{ - - public sealed class MSBuildFactDiscoverer : IXunitTestCaseDiscoverer - { - private readonly FactDiscoverer _factDiscoverer; - - public MSBuildFactDiscoverer(IMessageSink diagnosticMessageSink) - { - _factDiscoverer = new FactDiscoverer(diagnosticMessageSink); - } - - public IEnumerable Discover( - ITestFrameworkDiscoveryOptions discoveryOptions, - ITestMethod testMethod, - IAttributeInfo factAttribute) - { - return _factDiscoverer - .Discover(discoveryOptions, testMethod, factAttribute) - .Select(testCase => new MSBuildTestCase(testCase)); - } - } -} diff --git a/test/dotnet-format.UnitTests/XUnit/MSBuildTestCase.cs b/test/dotnet-format.UnitTests/XUnit/MSBuildTestCase.cs deleted file mode 100644 index 0104480fdff9..000000000000 --- a/test/dotnet-format.UnitTests/XUnit/MSBuildTestCase.cs +++ /dev/null @@ -1,72 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#nullable disable - -using System.Diagnostics; -using Microsoft.CodeAnalysis.Tools.Workspaces; -using Xunit.Sdk; - -namespace Microsoft.CodeAnalysis.Tools.Tests.XUnit -{ - [DebuggerDisplay(@"\{ class = {TestMethod.TestClass.Class.Name}, method = {TestMethod.Method.Name}, display = {DisplayName}, skip = {SkipReason} \}")] - public sealed class MSBuildTestCase : Xunit.LongLivedMarshalByRefObject, IXunitTestCase - { - private IXunitTestCase _testCase; - - public string DisplayName => _testCase.DisplayName; - public IMethodInfo Method => _testCase.Method; - public string SkipReason => _testCase.SkipReason; - public ITestMethod TestMethod => _testCase.TestMethod; - public object[] TestMethodArguments => _testCase.TestMethodArguments; - public Dictionary> Traits => _testCase.Traits; - public string UniqueID => _testCase.UniqueID; - - public ISourceInformation SourceInformation - { - get => _testCase.SourceInformation; - set => _testCase.SourceInformation = value; - } - - public Exception InitializationException => _testCase.InitializationException; - - public int Timeout => _testCase.Timeout; - - public MSBuildTestCase(IXunitTestCase testCase) - { - _testCase = testCase ?? throw new ArgumentNullException(nameof(testCase)); - } - - [Obsolete("Called by the deserializer", error: true)] - public MSBuildTestCase() { } - - public async Task RunAsync( - IMessageSink diagnosticMessageSink, - IMessageBus messageBus, - object[] constructorArguments, - ExceptionAggregator aggregator, - CancellationTokenSource cancellationTokenSource) - { - await MSBuildWorkspaceLoader.Guard.WaitAsync(); - try - { - var runner = new XunitTestCaseRunner(this, DisplayName, SkipReason, constructorArguments, TestMethodArguments, messageBus, aggregator, cancellationTokenSource); - return await runner.RunAsync(); - } - finally - { - MSBuildWorkspaceLoader.Guard.Release(); - } - } - - public void Deserialize(IXunitSerializationInfo info) - { - _testCase = info.GetValue("InnerTestCase"); - } - - public void Serialize(IXunitSerializationInfo info) - { - info.AddValue("InnerTestCase", _testCase); - } - } -} diff --git a/test/dotnet-format.UnitTests/XUnit/MSBuildTheoryAttribute.cs b/test/dotnet-format.UnitTests/XUnit/MSBuildTheoryAttribute.cs index 27b9f39b4d44..abf7c371c024 100644 --- a/test/dotnet-format.UnitTests/XUnit/MSBuildTheoryAttribute.cs +++ b/test/dotnet-format.UnitTests/XUnit/MSBuildTheoryAttribute.cs @@ -1,17 +1,33 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Xunit.Sdk; +using System.Reflection; +using System.Runtime.CompilerServices; +using Microsoft.CodeAnalysis.Tools.Workspaces; +using Xunit.v3; namespace Microsoft.CodeAnalysis.Tools.Tests.XUnit { [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] - [XunitTestCaseDiscoverer("Microsoft.CodeAnalysis.Tools.Tests.XUnit.MSBuildTheoryDiscoverer", "dotnet-format.UnitTests")] - public sealed class MSBuildTheoryAttribute : ConditionalTheoryAttribute + public sealed class MSBuildTheoryAttribute : ConditionalTheoryAttribute, IBeforeAfterTestAttribute { - public MSBuildTheoryAttribute(params Type[] skipConditions) - : base(skipConditions) + public MSBuildTheoryAttribute(params Type[] skipConditions) : base(skipConditions) { } + + public MSBuildTheoryAttribute([CallerFilePath] string? sourceFilePath = null, [CallerLineNumber] int sourceLineNumber = 0) + : base(Array.Empty(), sourceFilePath, sourceLineNumber) + { + } + + public void Before(MethodInfo methodUnderTest, IXunitTest test) + { + MSBuildWorkspaceLoader.Guard.Wait(); + } + + public void After(MethodInfo methodUnderTest, IXunitTest test) + { + MSBuildWorkspaceLoader.Guard.Release(); + } } } diff --git a/test/dotnet-format.UnitTests/XUnit/MSBuildTheoryDiscoverer.cs b/test/dotnet-format.UnitTests/XUnit/MSBuildTheoryDiscoverer.cs deleted file mode 100644 index 22ce8042aaf7..000000000000 --- a/test/dotnet-format.UnitTests/XUnit/MSBuildTheoryDiscoverer.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Xunit.Sdk; - -namespace Microsoft.CodeAnalysis.Tools.Tests.XUnit -{ - - public sealed class MSBuildTheoryDiscoverer : IXunitTestCaseDiscoverer - { - private readonly TheoryDiscoverer _theoryDiscoverer; - - public MSBuildTheoryDiscoverer(IMessageSink diagnosticMessageSink) - { - _theoryDiscoverer = new TheoryDiscoverer(diagnosticMessageSink); - } - - public IEnumerable Discover( - ITestFrameworkDiscoveryOptions discoveryOptions, - ITestMethod testMethod, - IAttributeInfo factAttribute) - { - return _theoryDiscoverer - .Discover(discoveryOptions, testMethod, factAttribute) - .Select(testCase => new MSBuildTestCase(testCase)); - } - } -} diff --git a/test/dotnet-new.IntegrationTests/Diagnostic/DiagnosticFixture.cs b/test/dotnet-new.IntegrationTests/Diagnostic/DiagnosticFixture.cs index df772d9846de..2eab6d6d2b9f 100644 --- a/test/dotnet-new.IntegrationTests/Diagnostic/DiagnosticFixture.cs +++ b/test/dotnet-new.IntegrationTests/Diagnostic/DiagnosticFixture.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using Xunit.Sdk; + namespace Microsoft.DotNet.Cli.New.IntegrationTests { public class DiagnosticFixture diff --git a/test/dotnet-new.IntegrationTests/Diagnostic/XunitNuGetLogger.cs b/test/dotnet-new.IntegrationTests/Diagnostic/XunitNuGetLogger.cs index b25feb42326f..af6202fb527b 100644 --- a/test/dotnet-new.IntegrationTests/Diagnostic/XunitNuGetLogger.cs +++ b/test/dotnet-new.IntegrationTests/Diagnostic/XunitNuGetLogger.cs @@ -2,7 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using NuGet.Common; -using DiagnosticMessage = Xunit.Sdk.DiagnosticMessage; +using Xunit.Sdk; +using DiagnosticMessage = Xunit.v3.DiagnosticMessage; namespace Microsoft.DotNet.Cli.New.IntegrationTests { diff --git a/test/dotnet-new.IntegrationTests/DotnetNewDetailsTest.cs b/test/dotnet-new.IntegrationTests/DotnetNewDetailsTest.cs index 80fa07d8d410..364e95c6580c 100644 --- a/test/dotnet-new.IntegrationTests/DotnetNewDetailsTest.cs +++ b/test/dotnet-new.IntegrationTests/DotnetNewDetailsTest.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using Xunit.Sdk; + namespace Microsoft.DotNet.Cli.New.IntegrationTests { public partial class DotnetNewDetailsTest : BaseIntegrationTest, IClassFixture diff --git a/test/dotnet-new.IntegrationTests/DotnetNewHelpTests.Approval.cs b/test/dotnet-new.IntegrationTests/DotnetNewHelpTests.Approval.cs index 464fc117c1c4..974ef1352b55 100644 --- a/test/dotnet-new.IntegrationTests/DotnetNewHelpTests.Approval.cs +++ b/test/dotnet-new.IntegrationTests/DotnetNewHelpTests.Approval.cs @@ -208,12 +208,16 @@ public Task CannotShowHelpForTemplate_FullNameMatch() [Fact] public Task CannotShowHelpForTemplate_WhenAmbiguousLanguageChoice() { + // Use a dedicated home directory to avoid conflicts with other tests that install + // templates with the same 'basic' short name. Tests are not guaranteed to execute + // in declared order. string workingDirectory = CreateTemporaryFolder(); - InstallTestTemplate("TemplateResolution/DifferentLanguagesGroup/BasicFSharp", _log, _fixture.HomeDirectory, workingDirectory); - InstallTestTemplate("TemplateResolution/DifferentLanguagesGroup/BasicVB", _log, _fixture.HomeDirectory, workingDirectory); + string homeDirectory = CreateTemporaryFolder("Home"); + InstallTestTemplate("TemplateResolution/DifferentLanguagesGroup/BasicFSharp", _log, homeDirectory, workingDirectory); + InstallTestTemplate("TemplateResolution/DifferentLanguagesGroup/BasicVB", _log, homeDirectory, workingDirectory); CommandResult commandResult = new DotnetNewCommand(_log, "basic", "--help") - .WithCustomHive(_fixture.HomeDirectory) + .WithCustomHive(homeDirectory) .WithWorkingDirectory(workingDirectory) .Execute(); @@ -393,11 +397,15 @@ public Task CanShowHelpForTemplate_ConditionalParams() [Fact] public Task CanShowHelpForTemplateWhenRequiredParamIsMissed() { + // Use a dedicated home directory to avoid conflicts with other tests that install + // templates with the same 'basic' short name. Tests are not guaranteed to execute + // in declared order. string workingDirectory = CreateTemporaryFolder(); - InstallTestTemplate($"TemplateResolution/MissedRequiredParameter/BasicTemplate1", _log, _fixture.HomeDirectory, workingDirectory); + string homeDirectory = CreateTemporaryFolder("Home"); + InstallTestTemplate($"TemplateResolution/MissedRequiredParameter/BasicTemplate1", _log, homeDirectory, workingDirectory); CommandResult commandResult = new DotnetNewCommand(_log, "basic", "--help") - .WithCustomHive(_fixture.HomeDirectory) + .WithCustomHive(homeDirectory) .WithWorkingDirectory(workingDirectory) .Execute(); diff --git a/test/dotnet-new.IntegrationTests/DotnetNewInstallTests.cs b/test/dotnet-new.IntegrationTests/DotnetNewInstallTests.cs index c88f7e31f49b..d8c4a5d28d58 100644 --- a/test/dotnet-new.IntegrationTests/DotnetNewInstallTests.cs +++ b/test/dotnet-new.IntegrationTests/DotnetNewInstallTests.cs @@ -5,7 +5,8 @@ using System.Text.RegularExpressions; using Microsoft.DotNet.Cli.Utils; using Microsoft.TemplateEngine.TestHelper; -using DiagnosticMessage = Xunit.Sdk.DiagnosticMessage; +using Xunit.Sdk; +using DiagnosticMessage = Xunit.v3.DiagnosticMessage; namespace Microsoft.DotNet.Cli.New.IntegrationTests { diff --git a/test/dotnet-new.IntegrationTests/DotnetNewTestTemplatesTests.cs b/test/dotnet-new.IntegrationTests/DotnetNewTestTemplatesTests.cs index 2e6128de171b..a61bbcc94336 100644 --- a/test/dotnet-new.IntegrationTests/DotnetNewTestTemplatesTests.cs +++ b/test/dotnet-new.IntegrationTests/DotnetNewTestTemplatesTests.cs @@ -48,6 +48,12 @@ public static class Languages private class NullTestOutputHelper : ITestOutputHelper { + public string Output => string.Empty; + + public void Write(string message) { } + + public void Write(string format, params object[] args) { } + public void WriteLine(string message) { } public void WriteLine(string format, params object[] args) { } diff --git a/test/dotnet-new.IntegrationTests/SharedHomeDirectory.cs b/test/dotnet-new.IntegrationTests/SharedHomeDirectory.cs index e7b91db01f32..fd946bf2aa4d 100644 --- a/test/dotnet-new.IntegrationTests/SharedHomeDirectory.cs +++ b/test/dotnet-new.IntegrationTests/SharedHomeDirectory.cs @@ -1,7 +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 SharedTestOutputHelper = Microsoft.TemplateEngine.TestHelper.SharedTestOutputHelper; +using Xunit.Sdk; namespace Microsoft.DotNet.Cli.New.IntegrationTests { diff --git a/test/dotnet-new.IntegrationTests/TemplateDiscoveryTests.cs b/test/dotnet-new.IntegrationTests/TemplateDiscoveryTests.cs index a87b0fc3cbc2..c65e813bd950 100644 --- a/test/dotnet-new.IntegrationTests/TemplateDiscoveryTests.cs +++ b/test/dotnet-new.IntegrationTests/TemplateDiscoveryTests.cs @@ -17,7 +17,7 @@ public TemplateDiscoveryTests(ITestOutputHelper log, TemplateDiscoveryTool templ } #pragma warning disable xUnit1004 // Test methods should not be skipped - [PlatformSpecificFact(SkipPlatforms = TestPlatforms.OSX, SkipArchitecture = Architecture.Arm64, SkipReason = "https://github.com/dotnet/sdk/issues/53569")] + [PlatformSpecificFact(skipPlatforms: TestPlatforms.OSX, skipArchitecture: Architecture.Arm64, skipReason: "https://github.com/dotnet/sdk/issues/53569")] #pragma warning restore xUnit1004 // Test methods should not be skipped public async Task CanRunDiscoveryTool() { diff --git a/test/dotnet-new.IntegrationTests/TemplateDiscoveryTool.cs b/test/dotnet-new.IntegrationTests/TemplateDiscoveryTool.cs index 48034a63a83c..9d2d0a86a677 100644 --- a/test/dotnet-new.IntegrationTests/TemplateDiscoveryTool.cs +++ b/test/dotnet-new.IntegrationTests/TemplateDiscoveryTool.cs @@ -1,7 +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 SharedTestOutputHelper = Microsoft.TemplateEngine.TestHelper.SharedTestOutputHelper; +using Xunit.Sdk; namespace Microsoft.DotNet.Cli.New.IntegrationTests { diff --git a/test/dotnet-new.IntegrationTests/WebProjectsTests.cs b/test/dotnet-new.IntegrationTests/WebProjectsTests.cs index 7cd274107a21..59cc08e55c0c 100644 --- a/test/dotnet-new.IntegrationTests/WebProjectsTests.cs +++ b/test/dotnet-new.IntegrationTests/WebProjectsTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.DotNet.Cli.Utils; +using Xunit.Sdk; namespace Microsoft.DotNet.Cli.New.IntegrationTests { diff --git a/test/dotnet-new.IntegrationTests/dotnet-new.IntegrationTests.csproj b/test/dotnet-new.IntegrationTests/dotnet-new.IntegrationTests.csproj index 06014f7dfcda..e87d35c501b9 100644 --- a/test/dotnet-new.IntegrationTests/dotnet-new.IntegrationTests.csproj +++ b/test/dotnet-new.IntegrationTests/dotnet-new.IntegrationTests.csproj @@ -13,10 +13,10 @@ - + - - + + diff --git a/test/dotnet-watch.Tests/TestUtilities/DotNetWatchTestBase.cs b/test/dotnet-watch.Tests/TestUtilities/DotNetWatchTestBase.cs index 3bbbeddb34d3..00e66f1f7762 100644 --- a/test/dotnet-watch.Tests/TestUtilities/DotNetWatchTestBase.cs +++ b/test/dotnet-watch.Tests/TestUtilities/DotNetWatchTestBase.cs @@ -19,10 +19,10 @@ public DotNetWatchTestBase(ITestOutputHelper logger) TestAssets = new TestAssetsManager(App.Logger); } - public Task InitializeAsync() - => Task.CompletedTask; + public ValueTask InitializeAsync() + => default; - public async Task DisposeAsync() + public async ValueTask DisposeAsync() { Log("Disposing test"); await App.DisposeAsync(); diff --git a/test/dotnet-watch.Tests/TestUtilities/ModuleInitializer.cs b/test/dotnet-watch.Tests/TestUtilities/MSBuildFixture.cs similarity index 84% rename from test/dotnet-watch.Tests/TestUtilities/ModuleInitializer.cs rename to test/dotnet-watch.Tests/TestUtilities/MSBuildFixture.cs index 84836479d581..71a39705de97 100644 --- a/test/dotnet-watch.Tests/TestUtilities/ModuleInitializer.cs +++ b/test/dotnet-watch.Tests/TestUtilities/MSBuildFixture.cs @@ -2,16 +2,20 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.Loader; using Microsoft.Build.Locator; +[assembly: AssemblyFixture(typeof(Microsoft.DotNet.Watch.UnitTests.MSBuildFixture))] + namespace Microsoft.DotNet.Watch.UnitTests; -public static class ModuleInitializer +/// +/// Assembly fixture that registers MSBuild and sets up assembly resolution for dotnet-watch tests. +/// A fixture is preferred over a [ModuleInitializer] because it doesn't get invoked for test discovery. +/// +public class MSBuildFixture { - [ModuleInitializer] - public static void Initialize() + public MSBuildFixture() { // Ensure that we load the msbuild binaries from redist deployment. Otherwise, msbuild might use target files // that do not match the implementations of the core tasks. diff --git a/test/dotnet.Tests/CommandTests/Package/Add/GivenDotnetPackageAdd.cs b/test/dotnet.Tests/CommandTests/Package/Add/GivenDotnetPackageAdd.cs index 744f8bcd9856..102640211fe5 100644 --- a/test/dotnet.Tests/CommandTests/Package/Add/GivenDotnetPackageAdd.cs +++ b/test/dotnet.Tests/CommandTests/Package/Add/GivenDotnetPackageAdd.cs @@ -3,7 +3,6 @@ using System.Runtime.CompilerServices; using Microsoft.DotNet.Cli.Commands; -using Xunit.Runners; namespace Microsoft.DotNet.Cli.Package.Add.Tests { @@ -379,7 +378,7 @@ public void FileBasedApp_NoVersion(string[] inputVersions, string? expectedVersi var file = Path.Join(testInstance.Path, "Program.cs"); var source = $""" - #:property RestoreSources=$(RestoreSources);{restoreSources} + #:property RestoreAdditionalProjectSources={restoreSources} Console.WriteLine(); """; File.WriteAllText(file, source); @@ -416,7 +415,7 @@ public void FileBasedApp_NoVersion_Prerelease(string[] inputVersions, string? _, var file = Path.Join(testInstance.Path, "Program.cs"); var source = $""" - #:property RestoreSources=$(RestoreSources);{restoreSources} + #:property RestoreAdditionalProjectSources={restoreSources} Console.WriteLine(); """; File.WriteAllText(file, source); @@ -637,7 +636,7 @@ public void FileBasedApp_CentralPackageManagement_NoVersionSpecified(bool legacy var file = Path.Join(testInstance.Path, "Program.cs"); var source = $""" - #:property RestoreSources=$(RestoreSources);{restoreSources} + #:property RestoreAdditionalProjectSources={restoreSources} Console.WriteLine(); """; File.WriteAllText(file, source); @@ -687,7 +686,7 @@ public void FileBasedApp_CentralPackageManagement_NoVersionSpecified_KeepExistin var file = Path.Join(testInstance.Path, "Program.cs"); var source = $""" - #:property RestoreSources=$(RestoreSources);{restoreSources} + #:property RestoreAdditionalProjectSources={restoreSources} #:package A Console.WriteLine(); """; diff --git a/test/dotnet.Tests/CommandTests/Run/GivenDotnetRunIsInterrupted.cs b/test/dotnet.Tests/CommandTests/Run/GivenDotnetRunIsInterrupted.cs index a7a188e14648..7ebdcd079eb9 100644 --- a/test/dotnet.Tests/CommandTests/Run/GivenDotnetRunIsInterrupted.cs +++ b/test/dotnet.Tests/CommandTests/Run/GivenDotnetRunIsInterrupted.cs @@ -27,6 +27,10 @@ public void ItTerminatesWinExeAppWithCloseMainWindow() var command = new DotnetCommand(Log, "run") .WithWorkingDirectory(asset.Path); + // Launch dotnet run in a new process group so that GenerateConsoleCtrlEvent + // targets only the child group and does not propagate to the test host. + command.CreateNewProcessGroup = true; + bool signaled = false; bool sawClosingGracefully = false; Process child = null; diff --git a/test/dotnet.Tests/CommandTests/Test/GivenDotnetTestBuildsAndRunsTestfromCsproj.cs b/test/dotnet.Tests/CommandTests/Test/GivenDotnetTestBuildsAndRunsTestfromCsproj.cs index 6bc213d4a4e4..ac056bcb2fbe 100644 --- a/test/dotnet.Tests/CommandTests/Test/GivenDotnetTestBuildsAndRunsTestfromCsproj.cs +++ b/test/dotnet.Tests/CommandTests/Test/GivenDotnetTestBuildsAndRunsTestfromCsproj.cs @@ -716,7 +716,9 @@ public void EnsureOutputPathEscaped(string flag) { var testProjectDirectory = CopyAndRestoreVSTestDotNetCoreTestApp([flag]); - var pathWithComma = Path.Combine(AppContext.BaseDirectory, "a,b"); + // Use a unique subdirectory per flag to avoid conflicts between theory data rows. + // --diag creates a file, while --output and --results-directory create directories. + var pathWithComma = Path.Combine(AppContext.BaseDirectory, "a,b", flag.TrimStart('-')); // Call test CommandResult result = new DotnetTestCommand(Log, disableNewOutput: true) diff --git a/test/dotnet.Tests/dotnet.Tests.csproj b/test/dotnet.Tests/dotnet.Tests.csproj index e5655de02a0f..658735a93ee6 100644 --- a/test/dotnet.Tests/dotnet.Tests.csproj +++ b/test/dotnet.Tests/dotnet.Tests.csproj @@ -89,9 +89,9 @@ - - - + + + diff --git a/test/xunit-runner/XUnitRunner.targets b/test/xunit-runner/XUnitRunner.targets index 03897ea63aab..dce3fb456e8d 100644 --- a/test/xunit-runner/XUnitRunner.targets +++ b/test/xunit-runner/XUnitRunner.targets @@ -4,12 +4,20 @@ $(SdkTargetFramework) $(SdkTargetFramework) - 2.4.1 + $(XUnitV3Version) <_SDKCustomXUnitPublishTargetsPath>$(MSBuildThisFileDirectory)XUnitPublish.targets -nocolor + + <_TestPublishRidProperties Condition="'$(TargetRid)' != ''">RuntimeIdentifier=$(TargetRid);SelfContained=false;ErrorOnDuplicatePublishOutputFiles=false + $(ArtifactsBinDir)HelixTasks\$(Configuration)\HelixTasks.dll @@ -37,7 +45,7 @@ Outputs="%(SDKCustomXUnitProject.Identity)%(SDKCustomXUnitProject.TargetFramework)%(SDKCustomXUnitProject.RuntimeTargetFramework)%(SDKCustomXUnitProject.AdditionalProperties)"> + Properties="CustomAfterMicrosoftCommonTargets=$(_SDKCustomXUnitPublishTargetsPath);RuntimeIdentifiers=$(TargetRid);%(SDKCustomXUnitProject.AdditionalProperties)"> @@ -53,7 +61,16 @@ <_CurrentRuntimeTargetFramework Condition="'$(_CurrentRuntimeTargetFramework)' == ''">$(SDKCustomXUnitRuntimeTargetFramework) <_CurrentAdditionalProperties>%(SDKCustomXUnitProject.AdditionalProperties) - + + + + @@ -77,6 +94,7 @@ diff --git a/test/xunit.runner.json b/test/xunit.runner.json index 1fca20845e33..650eda816f3b 100644 --- a/test/xunit.runner.json +++ b/test/xunit.runner.json @@ -1,5 +1,5 @@ { - "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", + "$schema": "https://xunit.net/schema/v3.1/xunit.runner.schema.json", "diagnosticMessages": true, "longRunningTestSeconds": 20, "showLiveOutput": true, From 1bc1dee753234ce1aef9fcd8f07b2cd19c98986b Mon Sep 17 00:00:00 2001 From: Michael Simons Date: Fri, 10 Apr 2026 14:18:58 +0000 Subject: [PATCH 2/4] Pull in arcade xunitV3 fix --- eng/XUnitV3/XUnitV3.Runner.targets | 93 ++++++++++++++++++++++++++++++ eng/XUnitV3/XUnitV3.targets | 51 ++++++++++++++++ eng/XUnitV3/xunit.runner.json | 4 ++ 3 files changed, 148 insertions(+) create mode 100644 eng/XUnitV3/XUnitV3.Runner.targets create mode 100644 eng/XUnitV3/XUnitV3.targets create mode 100644 eng/XUnitV3/xunit.runner.json diff --git a/eng/XUnitV3/XUnitV3.Runner.targets b/eng/XUnitV3/XUnitV3.Runner.targets new file mode 100644 index 000000000000..f60168b4dfef --- /dev/null +++ b/eng/XUnitV3/XUnitV3.Runner.targets @@ -0,0 +1,93 @@ + + + + + + + + <_TestResultDirectory>$([System.IO.Path]::GetDirectoryName('%(TestToRun.ResultsTrxPath)')) + <_TestResultTrxFileName>$([System.IO.Path]::GetFileName('%(TestToRun.ResultsTrxPath)')) + <_TestResultXmlFileName>$([System.IO.Path]::GetFileName('%(TestToRun.ResultsXmlPath)')) + <_TestResultHtmlFileName>$([System.IO.Path]::GetFileName('%(TestToRun.ResultsHtmlPath)')) + + + + <_TestEnvironment>%(TestToRun.EnvironmentDisplay) + <_TestAssembly>%(TestToRun.Identity) + <_TestRuntime>%(TestToRun.TestRuntime) + <_TestTimeout>%(TestToRun.TestTimeout) + <_TestRunnerAdditionalArguments>%(TestToRun.TestRunnerAdditionalArguments) + + <_TestRunner>%(TestToRun.RunCommand) + <_TestRunnerArgs Condition="'$(UseMicrosoftTestingPlatformRunner)' != 'true'">%(TestToRun.RunArguments) $(_TestRunnerAdditionalArguments) -xml "%(TestToRun.ResultsXmlPath)" -html "%(TestToRun.ResultsHtmlPath)" -trx "%(TestToRun.ResultsTrxPath)" + <_TestRunnerArgs Condition="'$(UseMicrosoftTestingPlatformRunner)' == 'true'">%(TestToRun.RunArguments) $(_TestRunnerAdditionalArguments) --results-directory "$(_TestResultDirectory)" --report-xunit --report-xunit-filename "$(_TestResultXmlFileName)" --report-xunit-html --report-xunit-html-filename "$(_TestResultHtmlFileName)" --report-trx --report-trx-filename "$(_TestResultTrxFileName)" + + $(DotNetRoot) + + + + <_TestRunnerArgs Condition="'$(UseMicrosoftTestingPlatformRunner)' != 'true'">$(_TestRunnerArgs) -noAutoReporters + <_TestRunnerArgs Condition="'$(UseMicrosoftTestingPlatformRunner)' == 'true'">$(_TestRunnerArgs) --auto-reporters off + + + + <_TestRunnerCommand>"$(_TestRunner)" $(_TestRunnerArgs) + + + <_TestRunnerCommand Condition="'$(TestCaptureOutput)' != 'false'">$(_TestRunnerCommand) >> "%(TestToRun.ResultsStdOutPath)" 2>&1 + + + + <_OutputFiles Include="%(TestToRun.ResultsXmlPath)" /> + <_OutputFiles Include="%(TestToRun.ResultsHtmlPath)" /> + <_OutputFiles Include="%(TestToRun.ResultsStdOutPath)" /> + + + + + + + + + + + + + + + + + + + <_ResultsFileToDisplay>%(TestToRun.ResultsHtmlPath) + <_ResultsFileToDisplay Condition="!Exists('$(_ResultsFileToDisplay)')">%(TestToRun.ResultsStdOutPath) + + + + + + + + + + + diff --git a/eng/XUnitV3/XUnitV3.targets b/eng/XUnitV3/XUnitV3.targets new file mode 100644 index 000000000000..c2cf910eaa33 --- /dev/null +++ b/eng/XUnitV3/XUnitV3.targets @@ -0,0 +1,51 @@ + + + + + false + + + true + + + + + + + + + + + + + + + + + + + + + $(MSBuildThisFileDirectory)xunit.runner.json + $(MSBuildThisFileDirectory)xunit.runner.json + + + + + + + + + + + + + diff --git a/eng/XUnitV3/xunit.runner.json b/eng/XUnitV3/xunit.runner.json new file mode 100644 index 000000000000..8465a4543641 --- /dev/null +++ b/eng/XUnitV3/xunit.runner.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", + "shadowCopy": false +} From 36a28af798261e30f607087158326be08c3b60f6 Mon Sep 17 00:00:00 2001 From: Michael Simons Date: Fri, 10 Apr 2026 16:04:25 +0000 Subject: [PATCH 3/4] Revert DefaultRequestDispatcherTest.cs changes from commit 96683a0 Reverts the changes made to DefaultRequestDispatcherTest.cs in commit 96683a0ad81c5694820f7824342a4ca5079bdd64, restoring the original Task.Run, SetResult, and simple await patterns. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../DefaultRequestDispatcherTest.cs | 27 +++---------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/test/Microsoft.NET.Sdk.Razor.Tool.Tests/DefaultRequestDispatcherTest.cs b/test/Microsoft.NET.Sdk.Razor.Tool.Tests/DefaultRequestDispatcherTest.cs index 8d222eca8a9a..758552b1df96 100644 --- a/test/Microsoft.NET.Sdk.Razor.Tool.Tests/DefaultRequestDispatcherTest.cs +++ b/test/Microsoft.NET.Sdk.Razor.Tool.Tests/DefaultRequestDispatcherTest.cs @@ -358,7 +358,7 @@ public async Task Dispatcher_ProcessSimultaneousConnections_HitsKeepAliveTimeout return connectionTask; } - readySource.TrySetResult(true); + readySource.SetResult(true); return new TaskCompletionSource().Task; }); @@ -383,17 +383,11 @@ public async Task Dispatcher_ProcessSimultaneousConnections_HitsKeepAliveTimeout }; var keepAlive = TimeSpan.FromSeconds(1); - // Use Task.Factory.StartNew with LongRunning to run the dispatcher on a dedicated - // OS thread instead of a thread pool thread. The dispatcher's Run() method uses - // blocking Task.WaitAny() which permanently blocks its thread. On Helix CI agents - // running many tests in parallel, blocking a thread pool thread contributes to pool - // starvation, which prevents Task.Delay timer callbacks from firing, causing the - // keep-alive timeout to never complete and the test to hang indefinitely. - var dispatcherTask = Task.Factory.StartNew(() => + var dispatcherTask = Task.Run(() => { var dispatcher = new DefaultRequestDispatcher(connectionHost.Object, compilerHost, CancellationToken.None, eventBus, keepAlive); dispatcher.Run(); - }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); + }); // Wait for all connections to be created. await readySource.Task; @@ -409,20 +403,7 @@ public async Task Dispatcher_ProcessSimultaneousConnections_HitsKeepAliveTimeout // Act // Now dispatcher should be in an idle state with no active connections. - // Use a dedicated thread to enforce the timeout, since under extreme thread pool - // starvation on Helix CI, even WaitAsync's timer continuations can't be scheduled. - // A dedicated OS thread with Thread.Join(timeout) uses a kernel wait that works - // regardless of thread pool state. - var completed = false; - var timeoutThread = new Thread(() => - { - completed = dispatcherTask.Wait(TimeSpan.FromSeconds(60)); - }) { IsBackground = true }; - timeoutThread.Start(); - timeoutThread.Join(TimeSpan.FromSeconds(65)); - Assert.True(completed, - "Dispatcher did not shut down within 60 seconds. This likely indicates " + - "thread pool starvation preventing Task.Delay timer callbacks from firing."); + await dispatcherTask; // Assert Assert.False(eventBus.HasDetectedBadConnection); From 740edd6a2394c867beedc3a676e4582b2b6d4a43 Mon Sep 17 00:00:00 2001 From: Michael Simons Date: Fri, 10 Apr 2026 20:27:26 +0000 Subject: [PATCH 4/4] Fix ReadFromStdin_ProjectReference NuGet resolution on Helix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dotnet run - (stdin) feature creates a virtual .csproj in a platform-specific temp directory (~/.local/share/dotnet/runfile/ on Linux). NuGet walks up from that location to find NuGet.config, but the test's NuGet.config (with internal feeds like dotnet11-transport) was only placed in /tmp/dotnetSdkTests/ — a completely different directory tree. This caused NuGet restore to only find nuget.org, which doesn't have preview SDK packages. Fix by copying NuGet.config into the runfile base directory so NuGet's upward config search finds the test feeds. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../CommandTests/Run/RunFileTestBase.cs | 14 ++++++++++++++ .../CommandTests/Run/RunFileTests_General.cs | 4 ++++ 2 files changed, 18 insertions(+) diff --git a/test/dotnet.Tests/CommandTests/Run/RunFileTestBase.cs b/test/dotnet.Tests/CommandTests/Run/RunFileTestBase.cs index f52905b497a5..4cc89bdd49a3 100644 --- a/test/dotnet.Tests/CommandTests/Run/RunFileTestBase.cs +++ b/test/dotnet.Tests/CommandTests/Run/RunFileTestBase.cs @@ -134,6 +134,20 @@ private static string PrepareOutOfTreeBaseDirectory() return outOfTreeBaseDirectory; } + /// + /// Copies NuGet.config to the runfile base directory so virtual projects created by + /// dotnet run - (stdin) can resolve packages from test feeds. The virtual project + /// is created under this directory, and NuGet walks up from the project location to + /// find config files. + /// + protected static void CopyNuGetConfigToRunfileDirectory() + { + var sourceNuGetConfig = Path.Join(SdkTestContext.Current.TestExecutionDirectory, "NuGet.config"); + var runfileDir = VirtualProjectBuilder.GetTempSubdirectory(); + Directory.CreateDirectory(runfileDir); + File.Copy(sourceNuGetConfig, Path.Join(runfileDir, "NuGet.config"), overwrite: true); + } + internal static string DirectiveError(string path, int line, string messageFormat, params ReadOnlySpan args) { return $"{path}({line}): {FileBasedProgramsResources.DirectiveError}: {string.Format(messageFormat, args)}"; diff --git a/test/dotnet.Tests/CommandTests/Run/RunFileTests_General.cs b/test/dotnet.Tests/CommandTests/Run/RunFileTests_General.cs index fd7819b311ce..901432183be4 100644 --- a/test/dotnet.Tests/CommandTests/Run/RunFileTests_General.cs +++ b/test/dotnet.Tests/CommandTests/Run/RunFileTests_General.cs @@ -397,6 +397,10 @@ public void ReadFromStdin_BuildProps() [Fact] public void ReadFromStdin_ProjectReference() { + // Ensure the runfile directory has a NuGet.config so the virtual project created by + // `dotnet run -` can resolve packages from test feeds during restore. + CopyNuGetConfigToRunfileDirectory(); + var testInstance = TestAssetsManager.CreateTestDirectory(); var libDir = Path.Join(testInstance.Path, "lib");