diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/MSTestAdapterSettings.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/MSTestAdapterSettings.cs
index a9c9bf0192..4e2a109300 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/Services/MSTestAdapterSettings.cs
+++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/MSTestAdapterSettings.cs
@@ -189,9 +189,11 @@ public static bool IsAppDomainCreationDisabled(string? settingsXml)
bool disableAppDomain = true;
// HACK: When running VSTest, and VSTest didn't create TestHostAppDomain (default behavior), we must be enabling appdomain in MSTest.
// Otherwise, we will not merge app.config properly, nor we will have correct BaseDirectory of current domain.
+ // This detects if we run in testhost.*.exe or in vstest.console.exe.This covers all: running with vstest.console.exe because there we can run in both modes, running with dotnet test or VS, because there we can run only in testhost(in isolation).
#if NETFRAMEWORK
if (AppDomain.CurrentDomain.Id == 1 &&
- AppDomain.CurrentDomain.FriendlyName.StartsWith("testhost.", StringComparison.Ordinal) &&
+ (AppDomain.CurrentDomain.FriendlyName.StartsWith("testhost.", StringComparison.Ordinal) ||
+ AppDomain.CurrentDomain.FriendlyName.StartsWith("vstest.console.", StringComparison.Ordinal)) &&
AppDomain.CurrentDomain.FriendlyName.EndsWith(".exe", StringComparison.Ordinal))
{
disableAppDomain = false;
diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AppDomainTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AppDomainTests.cs
index 14ed18e665..cac5b3ed9d 100644
--- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AppDomainTests.cs
+++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AppDomainTests.cs
@@ -1,8 +1,10 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using Microsoft.MSTestV2.CLIAutomation;
using Microsoft.Testing.Platform.Acceptance.IntegrationTests;
using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers;
+using Microsoft.Testing.TestInfrastructure;
namespace MSTest.Acceptance.IntegrationTests;
@@ -90,7 +92,7 @@ public async Task RunTests_With_VSTest(bool? disableAppDomain)
string disableAppDomainCommand = disableAppDomain switch
{
true => " -- RunConfiguration.DisableAppDomain=true",
- false => " -- RunConfiguration.EnableAppDomain=false",
+ false => " -- RunConfiguration.DisableAppDomain=false",
null => string.Empty,
};
@@ -115,7 +117,7 @@ public async Task DiscoverTests_With_VSTest(bool? disableAppDomain)
string disableAppDomainCommand = disableAppDomain switch
{
true => " -- RunConfiguration.DisableAppDomain=true",
- false => " -- RunConfiguration.EnableAppDomain=false",
+ false => " -- RunConfiguration.DisableAppDomain=false",
null => string.Empty,
};
@@ -123,5 +125,89 @@ public async Task DiscoverTests_With_VSTest(bool? disableAppDomain)
Assert.AreEqual(0, compilationResult.ExitCode);
}
+ [TestMethod]
+ [DataRow(true)]
+ [DataRow(false)]
+ [DataRow(null)]
+ public async Task RunTests_With_VSTestConsole_Directly(bool? disableAppDomain)
+ {
+ using TestAsset testAsset = await TestAsset.GenerateAssetAsync(
+ AssetName,
+ SingleTestSourceCode
+ .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)
+ .PatchCodeWithReplace("$TargetFramework$", TargetFrameworks.NetFramework[0]));
+
+ // Build the test project
+ DotnetMuxerResult buildResult = await DotnetCli.RunAsync(
+ $"build {testAsset.TargetAssetPath} -c Debug",
+ AcceptanceFixture.NuGetGlobalPackagesFolder.Path,
+ workingDirectory: testAsset.TargetAssetPath,
+ cancellationToken: TestContext.CancellationToken);
+ Assert.AreEqual(0, buildResult.ExitCode, $"Build failed: {buildResult.StandardOutput}");
+
+ // Get the DLL path
+ string dllPath = GetTestDllPath(testAsset.TargetAssetPath, TargetFrameworks.NetFramework[0]);
+ Assert.IsTrue(File.Exists(dllPath), $"Test DLL not found at {dllPath}");
+
+ // Run tests using vstest.console.exe directly
+ string vstestConsolePath = VSTestConsoleLocator.GetConsoleRunnerPath();
+ string disableAppDomainCommand = disableAppDomain switch
+ {
+ true => " -- RunConfiguration.DisableAppDomain=true",
+ false => " -- RunConfiguration.DisableAppDomain=false",
+ null => string.Empty,
+ };
+
+ string arguments = $"\"{dllPath}\"{disableAppDomainCommand}";
+
+ using var commandLine = new CommandLine();
+ await commandLine.RunAsync(
+ $"\"{vstestConsolePath}\" {arguments}",
+ cancellationToken: TestContext.CancellationToken);
+ }
+
+ [TestMethod]
+ [DataRow(true)]
+ [DataRow(false)]
+ [DataRow(null)]
+ public async Task DiscoverTests_With_VSTestConsole_Directly(bool? disableAppDomain)
+ {
+ using TestAsset testAsset = await TestAsset.GenerateAssetAsync(
+ AssetName,
+ SingleTestSourceCode
+ .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)
+ .PatchCodeWithReplace("$TargetFramework$", TargetFrameworks.NetFramework[0]));
+
+ // Build the test project
+ DotnetMuxerResult buildResult = await DotnetCli.RunAsync(
+ $"build {testAsset.TargetAssetPath} -c Debug",
+ AcceptanceFixture.NuGetGlobalPackagesFolder.Path,
+ workingDirectory: testAsset.TargetAssetPath,
+ cancellationToken: TestContext.CancellationToken);
+ Assert.AreEqual(0, buildResult.ExitCode, $"Build failed: {buildResult.StandardOutput}");
+
+ // Get the DLL path
+ string dllPath = GetTestDllPath(testAsset.TargetAssetPath, TargetFrameworks.NetFramework[0]);
+ Assert.IsTrue(File.Exists(dllPath), $"Test DLL not found at {dllPath}");
+
+ // Run discovery using vstest.console.exe directly
+ string vstestConsolePath = VSTestConsoleLocator.GetConsoleRunnerPath();
+ string disableAppDomainCommand = disableAppDomain switch
+ {
+ true => " -- RunConfiguration.DisableAppDomain=true",
+ false => " -- RunConfiguration.DisableAppDomain=false",
+ null => string.Empty,
+ };
+ string arguments = $"\"{dllPath}\" /ListTests{disableAppDomainCommand}";
+
+ using var commandLine = new CommandLine();
+ await commandLine.RunAsync(
+ $"\"{vstestConsolePath}\" {arguments}",
+ cancellationToken: TestContext.CancellationToken);
+ }
+
+ private static string GetTestDllPath(string assetPath, string targetFramework) =>
+ Path.Combine(assetPath, "bin", "Debug", targetFramework, $"{AssetName}.dll");
+
public TestContext TestContext { get; set; }
}
diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/MSTest.Acceptance.IntegrationTests.csproj b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/MSTest.Acceptance.IntegrationTests.csproj
index 7fb66b0acc..c086fd8b9b 100644
--- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/MSTest.Acceptance.IntegrationTests.csproj
+++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/MSTest.Acceptance.IntegrationTests.csproj
@@ -13,13 +13,15 @@
+
+
-
+
diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests.csproj b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests.csproj
index 8da7dd00bb..b6ba49f6aa 100644
--- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests.csproj
+++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests.csproj
@@ -13,6 +13,10 @@
+
+
+
+
diff --git a/test/Performance/MSTest.Performance.Runner/MSTest.Performance.Runner.csproj b/test/Performance/MSTest.Performance.Runner/MSTest.Performance.Runner.csproj
index eb2306c412..686b71999d 100644
--- a/test/Performance/MSTest.Performance.Runner/MSTest.Performance.Runner.csproj
+++ b/test/Performance/MSTest.Performance.Runner/MSTest.Performance.Runner.csproj
@@ -15,4 +15,8 @@
+
+
+
+
diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/MSTest.Analyzers.UnitTests.csproj b/test/UnitTests/MSTest.Analyzers.UnitTests/MSTest.Analyzers.UnitTests.csproj
index 069388fa30..4b32407f1f 100644
--- a/test/UnitTests/MSTest.Analyzers.UnitTests/MSTest.Analyzers.UnitTests.csproj
+++ b/test/UnitTests/MSTest.Analyzers.UnitTests/MSTest.Analyzers.UnitTests.csproj
@@ -44,4 +44,8 @@
+
+
+
+
diff --git a/test/UnitTests/MSTest.SourceGeneration.UnitTests/MSTest.SourceGeneration.UnitTests.csproj b/test/UnitTests/MSTest.SourceGeneration.UnitTests/MSTest.SourceGeneration.UnitTests.csproj
index fcdc1a7ebe..014e756668 100644
--- a/test/UnitTests/MSTest.SourceGeneration.UnitTests/MSTest.SourceGeneration.UnitTests.csproj
+++ b/test/UnitTests/MSTest.SourceGeneration.UnitTests/MSTest.SourceGeneration.UnitTests.csproj
@@ -14,6 +14,10 @@
+
+
+
+
PreserveNewest
diff --git a/test/Utilities/Automation.CLI/Automation.CLI.csproj b/test/Utilities/Automation.CLI/Automation.CLI.csproj
index 6b62895628..cbb4fe349f 100644
--- a/test/Utilities/Automation.CLI/Automation.CLI.csproj
+++ b/test/Utilities/Automation.CLI/Automation.CLI.csproj
@@ -14,4 +14,8 @@
+
+
+
+
diff --git a/test/Utilities/Automation.CLI/CLITestBase.common.cs b/test/Utilities/Automation.CLI/CLITestBase.common.cs
index 8a59fa515c..9b6fccddb4 100644
--- a/test/Utilities/Automation.CLI/CLITestBase.common.cs
+++ b/test/Utilities/Automation.CLI/CLITestBase.common.cs
@@ -30,14 +30,6 @@ protected static XmlDocument ReadCPMFile()
return versionPropsXml;
}
- protected static string GetTestPlatformVersion()
- {
- XmlDocument cpmXml = ReadCPMFile();
- XmlNode testSdkVersion = cpmXml.DocumentElement.SelectSingleNode("PropertyGroup/MicrosoftNETTestSdkVersion");
-
- return testSdkVersion.InnerText;
- }
-
protected static string GetArtifactsBinFolderPath()
{
string assemblyLocation = Assembly.GetExecutingAssembly().Location;
diff --git a/test/Utilities/Automation.CLI/CLITestBase.e2e.cs b/test/Utilities/Automation.CLI/CLITestBase.e2e.cs
index 76d01fb92b..8e3725549c 100644
--- a/test/Utilities/Automation.CLI/CLITestBase.e2e.cs
+++ b/test/Utilities/Automation.CLI/CLITestBase.e2e.cs
@@ -15,7 +15,7 @@ public abstract partial class CLITestBase
protected CLITestBase()
{
s_vsTestConsoleWrapper = new(
- GetConsoleRunnerPath(),
+ VSTestConsoleLocator.GetConsoleRunnerPath(),
new()
{
EnvironmentVariables = new()
@@ -65,52 +65,6 @@ public void InvokeVsTestForExecution(string[] sources, string runSettings = "",
}
}
- public static string GetNugetPackageFolder()
- {
- string nugetPackagesFolderPath = Environment.GetEnvironmentVariable("NUGET_PACKAGES");
- if (!string.IsNullOrEmpty(nugetPackagesFolderPath))
- {
- Assert.IsTrue(Directory.Exists(nugetPackagesFolderPath), $"Found environment variable 'NUGET_PACKAGES' and NuGet package folder '{nugetPackagesFolderPath}' should exist");
-
- return nugetPackagesFolderPath;
- }
-
- string userProfile = Environment.GetEnvironmentVariable("USERPROFILE");
- nugetPackagesFolderPath = Path.Combine(userProfile, ".nuget", "packages");
- Assert.IsTrue(Directory.Exists(nugetPackagesFolderPath), $"NuGet package folder '{nugetPackagesFolderPath}' should exist");
-
- return nugetPackagesFolderPath;
- }
-
- ///
- /// Gets the path to vstest.console.exe.
- ///
- /// Full path to vstest.console.exe.
- public static string GetConsoleRunnerPath()
- {
- string testPlatformNuGetPackageFolder = Path.Combine(
- GetNugetPackageFolder(),
- TestPlatformCLIPackageName,
- GetTestPlatformVersion());
- if (!Directory.Exists(testPlatformNuGetPackageFolder))
- {
- throw new DirectoryNotFoundException($"Test platform NuGet package folder '{testPlatformNuGetPackageFolder}' does not exist");
- }
-
- string vstestConsolePath = Path.Combine(
- testPlatformNuGetPackageFolder,
- "tools",
- "net462",
- "Common7",
- "IDE",
- "Extensions",
- "TestPlatform",
- "vstest.console.exe");
- return !File.Exists(vstestConsolePath)
- ? throw new InvalidOperationException($"Could not find vstest.console.exe in {vstestConsolePath}")
- : vstestConsolePath;
- }
-
///
/// Validate if the discovered tests list contains provided tests.
///
diff --git a/test/Utilities/Automation.CLI/VSTestConsoleLocator.cs b/test/Utilities/Automation.CLI/VSTestConsoleLocator.cs
new file mode 100644
index 0000000000..3966fa7f8e
--- /dev/null
+++ b/test/Utilities/Automation.CLI/VSTestConsoleLocator.cs
@@ -0,0 +1,89 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Microsoft.Testing.TestInfrastructure;
+
+using Assert = Microsoft.VisualStudio.TestTools.UnitTesting.Assert;
+
+namespace Microsoft.MSTestV2.CLIAutomation;
+
+///
+/// Helper class to locate vstest.console.exe.
+///
+public static class VSTestConsoleLocator
+{
+ private const string TestPlatformPackageName = "Microsoft.TestPlatform";
+
+ ///
+ /// Gets the path to vstest.console.exe.
+ ///
+ /// Full path to vstest.console.exe.
+ public static string GetConsoleRunnerPath()
+ {
+ string testPlatformNuGetPackageFolder = Path.Combine(
+ GetNugetPackageFolder(),
+ TestPlatformPackageName,
+ GetTestPlatformVersion());
+ if (!Directory.Exists(testPlatformNuGetPackageFolder))
+ {
+ throw new DirectoryNotFoundException($"Test platform NuGet package folder '{testPlatformNuGetPackageFolder}' does not exist");
+ }
+
+ string vstestConsolePath = Path.Combine(
+ testPlatformNuGetPackageFolder,
+ "tools",
+ "net462",
+ "Common7",
+ "IDE",
+ "Extensions",
+ "TestPlatform",
+ "vstest.console.exe");
+ return !File.Exists(vstestConsolePath)
+ ? throw GetExceptionForVSTestConsoleNotFound(vstestConsolePath)
+ : vstestConsolePath;
+
+ InvalidOperationException GetExceptionForVSTestConsoleNotFound(string expectedPath)
+ {
+ string[] files = Directory.GetFiles(testPlatformNuGetPackageFolder, "vstest.console.exe", SearchOption.AllDirectories);
+ return files.Length == 0
+ ? new InvalidOperationException($"Could not find vstest.console.exe in {vstestConsolePath}")
+ : new InvalidOperationException($"Could not find vstest.console.exe in {vstestConsolePath}. Found in:{Environment.NewLine}{string.Join(Environment.NewLine, files)}");
+ }
+ }
+
+ private static string GetNugetPackageFolder()
+ {
+ string? nugetPackagesFolderPath = Environment.GetEnvironmentVariable("NUGET_PACKAGES");
+ if (!string.IsNullOrEmpty(nugetPackagesFolderPath))
+ {
+ Assert.IsTrue(Directory.Exists(nugetPackagesFolderPath), $"Found environment variable 'NUGET_PACKAGES' and NuGet package folder '{nugetPackagesFolderPath}' should exist");
+
+ return nugetPackagesFolderPath;
+ }
+
+ string? userProfile = Environment.GetEnvironmentVariable("USERPROFILE");
+ if (string.IsNullOrEmpty(userProfile))
+ {
+ throw new InvalidOperationException("USERPROFILE environment variable is not set");
+ }
+
+ nugetPackagesFolderPath = Path.Combine(userProfile, ".nuget", "packages");
+ Assert.IsTrue(Directory.Exists(nugetPackagesFolderPath), $"NuGet package folder '{nugetPackagesFolderPath}' should exist");
+
+ return nugetPackagesFolderPath;
+ }
+
+ private static string GetTestPlatformVersion()
+ {
+ string cpmFilePath = Path.Combine(RootFinder.Find(), "Directory.Packages.props");
+ using FileStream fileStream = File.OpenRead(cpmFilePath);
+#pragma warning disable CA3075 // Insecure DTD processing in XML
+ using var xmlTextReader = new XmlTextReader(fileStream) { Namespaces = false };
+#pragma warning restore CA3075 // Insecure DTD processing in XML
+ var cpmXml = new XmlDocument();
+ cpmXml.Load(xmlTextReader);
+
+ return cpmXml.DocumentElement?.SelectSingleNode("PropertyGroup/MicrosoftNETTestSdkVersion")?.InnerText
+ ?? throw new InvalidOperationException($"Could not find MicrosoftNETTestSdkVersion in {cpmFilePath}");
+ }
+}
diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/Microsoft.Testing.TestInfrastructure.csproj b/test/Utilities/Microsoft.Testing.TestInfrastructure/Microsoft.Testing.TestInfrastructure.csproj
index 6a8963eaa5..97bb9f850f 100644
--- a/test/Utilities/Microsoft.Testing.TestInfrastructure/Microsoft.Testing.TestInfrastructure.csproj
+++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/Microsoft.Testing.TestInfrastructure.csproj
@@ -4,7 +4,6 @@
$(SupportedNetFrameworks);netstandard2.0
enable
$(DefineConstants);SKIP_INTERMEDIATE_TARGET_FRAMEWORKS
- $(DefineConstants);ROOT_FINDER_PUBLIC
diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/RootFinder.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/RootFinder.cs
index 404f930f6a..53d2ac7be5 100644
--- a/test/Utilities/Microsoft.Testing.TestInfrastructure/RootFinder.cs
+++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/RootFinder.cs
@@ -9,12 +9,7 @@ namespace Microsoft.Testing.TestInfrastructure;
/// The class is used to find the root directory of a Git repository by
/// searching for a ".git" directory or file starting from the application's base directory and moving up the directory
/// hierarchy. This is useful for applications that need to determine the root of a project or repository.
-#if ROOT_FINDER_PUBLIC
-public
-#else
-internal
-#endif
- static class RootFinder
+internal static class RootFinder
{
private static string? s_root;