Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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,
};

Expand All @@ -115,13 +117,97 @@ 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,
};

DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"test {testAsset.TargetAssetPath} --list-tests{disableAppDomainCommand}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, workingDirectory: testAsset.TargetAssetPath, cancellationToken: TestContext.CancellationToken);
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; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@
<Compile Include="$(RepoRoot)test\IntegrationTests\Microsoft.Testing.Platform.Acceptance.IntegrationTests\Helpers\AcceptanceFixture.cs" Link="Helpers\AcceptanceFixture.cs" />
<Compile Include="$(RepoRoot)test\IntegrationTests\Microsoft.Testing.Platform.Acceptance.IntegrationTests\Helpers\AcceptanceTestBase.cs" Link="Helpers\AcceptanceTestBase.cs" />
<Compile Include="$(RepoRoot)test\IntegrationTests\Microsoft.Testing.Platform.Acceptance.IntegrationTests\ServerMode\**\*.cs" Link="ServerMode\%(RecursiveDir)%(FileName)%(Extension)" />
<Compile Include="$(RepoRoot)test\Utilities\Automation.CLI\VSTestConsoleLocator.cs" Link="Helpers\VSTestConsoleLocator.cs" />
<Compile Include="$(RepoRoot)\test\Utilities\Microsoft.Testing.TestInfrastructure\RootFinder.cs" Link="Helpers\RootFinder.cs" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="MSBuild.StructuredLogger" />
<PackageReference Include="StreamJsonRpc" />
</ItemGroup>

<!-- Packages needed for the test assets but that we don't want to reference -->
<ItemGroup>
<PackageDownload Include="Aspire.Hosting.Testing" Version="[$(AspireHostingTestingVersion)]" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
<PackageReference Include="StreamJsonRpc" />
</ItemGroup>

<ItemGroup>
<Compile Include="$(RepoRoot)\test\Utilities\Microsoft.Testing.TestInfrastructure\RootFinder.cs" Link="RootFinder.cs" />
</ItemGroup>

<ItemGroup>
<Using Include="Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers" />
<Using Include="Microsoft.Testing.Platform.Builder" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,8 @@
<ItemGroup>
<ProjectReference Include="$(RepoRoot)test\Utilities\Microsoft.Testing.TestInfrastructure\Microsoft.Testing.TestInfrastructure.csproj" />
</ItemGroup>

<ItemGroup>
<Compile Include="$(RepoRoot)\test\Utilities\Microsoft.Testing.TestInfrastructure\RootFinder.cs" Link="RootFinder.cs" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,8 @@
<ProjectReference Include="$(RepoRoot)src\Adapter\MSTest.TestAdapter\MSTest.TestAdapter.csproj" />
</ItemGroup>

<ItemGroup>
<Compile Include="$(RepoRoot)\test\Utilities\Microsoft.Testing.TestInfrastructure\RootFinder.cs" Link="RootFinder.cs" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
<PackageReference Include="AwesomeAssertions" />
</ItemGroup>

<ItemGroup>
<Compile Include="$(RepoRoot)\test\Utilities\Microsoft.Testing.TestInfrastructure\RootFinder.cs" Link="RootFinder.cs" />
</ItemGroup>

<ItemGroup>
<None Update="*.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
Expand Down
4 changes: 4 additions & 0 deletions test/Utilities/Automation.CLI/Automation.CLI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,8 @@
<ProjectReference Include="$(RepoRoot)src\TestFramework\TestFramework\TestFramework.csproj" />
</ItemGroup>

<ItemGroup>
<Compile Include="$(RepoRoot)\test\Utilities\Microsoft.Testing.TestInfrastructure\RootFinder.cs" Link="RootFinder.cs" />
</ItemGroup>

</Project>
8 changes: 0 additions & 8 deletions test/Utilities/Automation.CLI/CLITestBase.common.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
48 changes: 1 addition & 47 deletions test/Utilities/Automation.CLI/CLITestBase.e2e.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public abstract partial class CLITestBase
protected CLITestBase()
{
s_vsTestConsoleWrapper = new(
GetConsoleRunnerPath(),
VSTestConsoleLocator.GetConsoleRunnerPath(),
new()
{
EnvironmentVariables = new()
Expand Down Expand Up @@ -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;
}

/// <summary>
/// Gets the path to <c>vstest.console.exe</c>.
/// </summary>
/// <returns>Full path to <c>vstest.console.exe</c>.</returns>
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;
}

/// <summary>
/// Validate if the discovered tests list contains provided tests.
/// </summary>
Expand Down
89 changes: 89 additions & 0 deletions test/Utilities/Automation.CLI/VSTestConsoleLocator.cs
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// Helper class to locate vstest.console.exe.
/// </summary>
public static class VSTestConsoleLocator
{
private const string TestPlatformPackageName = "Microsoft.TestPlatform";

/// <summary>
/// Gets the path to <c>vstest.console.exe</c>.
/// </summary>
/// <returns>Full path to <c>vstest.console.exe</c>.</returns>
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}");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
<TargetFrameworks>$(SupportedNetFrameworks);netstandard2.0</TargetFrameworks>
<Nullable>enable</Nullable>
<DefineConstants Condition=" '$(FastAcceptanceTest)' == 'true'">$(DefineConstants);SKIP_INTERMEDIATE_TARGET_FRAMEWORKS</DefineConstants>
<DefineConstants>$(DefineConstants);ROOT_FINDER_PUBLIC</DefineConstants>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,7 @@ namespace Microsoft.Testing.TestInfrastructure;
/// <remarks>The <see cref="RootFinder"/> 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.</remarks>
#if ROOT_FINDER_PUBLIC
public
#else
internal
#endif
static class RootFinder
internal static class RootFinder
{
private static string? s_root;

Expand Down
Loading