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
13 changes: 12 additions & 1 deletion Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,18 @@
<!-- Temporary workaround for Arcade issue in net9-preview5 -->
<_NetFrameworkHostedCompilersVersion Condition="'$(_NetFrameworkHostedCompilersVersion)' == ''">4.11.0-3.24280.3</_NetFrameworkHostedCompilersVersion>
</PropertyGroup>


<PropertyGroup>
<DefineConstants Condition="'$(IsFilterSourcePackage)' != 'true'">$(DefineConstants);IS_VSTEST_REPO</DefineConstants>
<!--
MSB3277: netstandard2.0 product assemblies reference SCI 9.0.0.0 (from the explicit
PackageReference needed for the DTA scenario — see issue #15718), but .NETCoreApp
targets have inbox SCI 8.0.0.0. This is harmless: .NET Core assembly loading is
version-tolerant and will load the available version regardless of the reference.
-->
<MSBuildWarningsAsMessages Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp'">$(MSBuildWarningsAsMessages);MSB3277</MSBuildWarningsAsMessages>
</PropertyGroup>

<Import Project="Sdk.targets" Sdk="Microsoft.DotNet.Arcade.Sdk" />
<!-- Override the version of imported package to avoid infinite restore loop in VisualStudio, https://github.com/dotnet/arcade/issues/16228 -->
<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
<_MicrosoftVSSDKBuildToolsVersion_>17.14.2119</_MicrosoftVSSDKBuildToolsVersion_>
<MicrosoftWin32RegistryVersion>5.0.0</MicrosoftWin32RegistryVersion>
<NewtonsoftJsonVersion>13.0.3</NewtonsoftJsonVersion>
<SystemCollectionsImmutableVersion>9.0.11</SystemCollectionsImmutableVersion>
<SystemCollectionsImmutableVersion>10.0.0</SystemCollectionsImmutableVersion>
<SystemMemoryVersion>4.5.5</SystemMemoryVersion>
<SystemReflectionMetadataVersion>8.0.0</SystemReflectionMetadataVersion>
<TestPlatformExternalsVersion>18.0.0-preview-1-10911-061</TestPlatformExternalsVersion>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.Win32.Registry" Version="$(MicrosoftWin32RegistryVersion)" Condition="'$(TargetFramework)' == 'netstandard2.0'" />
<PackageReference Include="System.Reflection.Metadata" Version="$(SystemReflectionMetadataVersion)" Condition="'$(TargetFrameworkIdentifier)' != '.NETCoreApp'" />
<PackageReference Include="System.Collections.Immutable" Version="$(SystemCollectionsImmutableVersion)" Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
<PackageReference Include="System.ValueTuple" Version="4.5.0"
Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net48')) != 'true' and '$(TargetFrameworkIdentifier)' == '.NETFramework'" />
<PackageReference Include="System.Reflection.Metadata" Version="$(SystemReflectionMetadataVersion)" Condition="'$(TargetFrameworkIdentifier)' != '.NETCoreApp'" />
<PackageReference Include="System.Collections.Immutable" Version="$(SystemCollectionsImmutableVersion)" Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'" />
</ItemGroup>

<ItemGroup>
Expand Down
14 changes: 12 additions & 2 deletions src/datacollector/app.config
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,21 @@
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
<bindingRedirect oldVersion="0.0.0.0-6.0.3.0" newVersion="6.0.3.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
</dependentAssembly>

<dependentAssembly>
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
</dependentAssembly>

<dependentAssembly>
<assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="1.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
<bindingRedirect oldVersion="1.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Reflection.Metadata" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
Sometimes NU1702 is not suppressed correctly, so force reducing severity of the warning.
See https://github.com/NuGet/Home/issues/9147
-->
<MSBuildWarningsAsMessages>NU1702</MSBuildWarningsAsMessages>
<MSBuildWarningsAsMessages>$(MSBuildWarningsAsMessages);NU1702</MSBuildWarningsAsMessages>
</PropertyGroup>

<!-- runner and runner dependencies -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
Sometimes NU1702 is not suppressed correctly, so force reducing severity of the warning.
See https://github.com/NuGet/Home/issues/9147
-->
<MSBuildWarningsAsMessages>NU1702</MSBuildWarningsAsMessages>
<MSBuildWarningsAsMessages>$(MSBuildWarningsAsMessages);NU1702</MSBuildWarningsAsMessages>
</PropertyGroup>

<PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
Sometimes NU1702 is not suppressed correctly, so force reducing severity of the warning.
See https://github.com/NuGet/Home/issues/9147
-->
<MSBuildWarningsAsMessages>NU1702;NETSDK1023</MSBuildWarningsAsMessages>
<MSBuildWarningsAsMessages>$(MSBuildWarningsAsMessages);NU1702;NETSDK1023</MSBuildWarningsAsMessages>
</PropertyGroup>

<PropertyGroup>
Expand Down
14 changes: 12 additions & 2 deletions src/testhost.x86/app.config
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,21 @@
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
<bindingRedirect oldVersion="0.0.0.0-6.0.3.0" newVersion="6.0.3.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
</dependentAssembly>

<dependentAssembly>
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
</dependentAssembly>

<dependentAssembly>
<assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="1.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
<bindingRedirect oldVersion="1.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Reflection.Metadata" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
Expand Down
13 changes: 9 additions & 4 deletions src/vstest.console/app.config
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
<bindingRedirect oldVersion="0.0.0.0-6.0.3.0" newVersion="6.0.3.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="1.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
<bindingRedirect oldVersion="1.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Reflection.Metadata" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
Expand All @@ -40,7 +40,12 @@

<dependentAssembly>
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.2" newVersion="4.0.1.2" />
<bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
</dependentAssembly>

<dependentAssembly>
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
</dependentAssembly>

<dependentAssembly>
Expand All @@ -55,7 +60,7 @@

<dependentAssembly>
<assemblyIdentity name="System.Text.Json" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.11" newVersion="6.0.0.11" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.11" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ public void DiscoverTestsShouldShowProperWarningIfNoTestsOnTestCaseFilter(Runner
}

[TestMethod]
[Ignore("SCI 10.0.0.0 cannot be loaded in .NET 9 test host — covered by DtaLikeHost acceptance test instead")]
public void TypesToLoadAttributeTests()
{
var extensionsDirectory = IntegrationTestEnvironment.ExtensionsDirectory;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.IO;

using Microsoft.TestPlatform.TestUtilities;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Microsoft.TestPlatform.AcceptanceTests;

/// <summary>
/// Reproduces the binding-redirect scenario experienced by Azure DevOps' Distributed
/// Test Agent (DTAExecutionHost) and any Visual Studio host that picks up
/// <c>Microsoft.VisualStudio.TestPlatform.Common.dll</c> without the in-box
/// <c>vstest.console.exe.config</c> binding redirects.
///
/// The test loads <c>Common.dll</c> inside a net472 host that has no binding
/// redirects in its app.config and calls
/// <see cref="Microsoft.VisualStudio.TestPlatform.Common.Filtering.FilterExpressionWrapper"/>,
/// which triggers <c>FastFilter.Builder</c> and forces
/// <c>System.Collections.Immutable</c> / <c>System.Reflection.Metadata</c> to load.
///
/// It runs the scenario twice:
/// 1. Against the <c>Microsoft.TestPlatform</c> nupkg's
/// <c>tools/net462/Common7/IDE/Extensions/TestPlatform/</c> layout (as DTA consumes it).
/// 2. Against the flat layout of the <c>Microsoft.VisualStudio.TestTools.TestPlatform.V2.CLI</c>
/// VSIX (as Visual Studio consumes it).
///
/// Regression guard: if <c>Common.dll</c>'s compiled metadata references for SCI/SRM drift
/// away from the versions we ship next to it, the test fails with the same
/// <c>FileLoadException</c> customers see. Both layouts must stay self-consistent.
/// </summary>
[TestClass]
public class DistributedTestAgentScenarioTests : AcceptanceTestBase
{
[TestMethod]
[TestCategory("Windows-Review")]
public void LoadingCommonDllFromMicrosoftTestPlatformPackageWithoutBindingRedirectsDoesNotThrow()
{
// Nupkg layout: DTA-style consumption of the Microsoft.TestPlatform nupkg.
RunDtaLikeHost(toolsDirOverride: null);
}

[TestMethod]
[TestCategory("Windows-Review")]
public void LoadingCommonDllFromCliV2VsixLayoutWithoutBindingRedirectsDoesNotThrow()
{
// VSIX layout: flat folder with Common.dll + SCI + SRM at the root, as shipped
// in Microsoft.VisualStudio.TestTools.TestPlatform.V2.CLI.vsix and consumed by
// Visual Studio. The VSIX is unzipped into PublishDirectory by Build.cs.
var extractedVsixDir = Path.Combine(
IntegrationTestEnvironment.PublishDirectory,
Path.GetFileName(IntegrationTestEnvironment.LocalVsixInsertion));

Assert.IsTrue(
Directory.Exists(extractedVsixDir),
$"Extracted VSIX directory not found at '{extractedVsixDir}'. " +
"Build.cs is expected to unzip the V2.CLI VSIX before acceptance tests run.");

Assert.IsTrue(
File.Exists(Path.Combine(extractedVsixDir, "Microsoft.VisualStudio.TestPlatform.Common.dll")),
$"Expected Common.dll at the root of the extracted VSIX ('{extractedVsixDir}').");

RunDtaLikeHost(toolsDirOverride: extractedVsixDir);
}

private void RunDtaLikeHost(string? toolsDirOverride)
{
var projectPath = GetIsolatedTestAsset("DtaLikeHost.csproj", Net472TargetFramework);
var workingDir = Path.GetDirectoryName(projectPath)!;

var dotnetPath = GetPatchedDotnetPath();

var buildArgs =
$@"build ""{projectPath}"" -c {IntegrationTestEnvironment.BuildConfiguration} " +
$@"/p:PackageVersion={IntegrationTestEnvironment.LatestLocallyBuiltNugetVersion} " +
@"/nodeReuse:false";

if (toolsDirOverride is not null)
{
buildArgs += $@" /p:TestPlatformToolsDirOverride=""{toolsDirOverride}""";
}

ExecuteApplication(dotnetPath, buildArgs, out var buildOut, out var buildErr, out var buildExit, workingDirectory: workingDir);

Assert.AreEqual(
0,
buildExit,
$"dotnet build of DtaLikeHost failed (exit {buildExit}).\nSTDOUT:\n{buildOut}\nSTDERR:\n{buildErr}");

var exePath = Path.Combine(
workingDir,
"artifacts", "bin", "TestAssets", "DtaLikeHost",
IntegrationTestEnvironment.BuildConfiguration,
Net472TargetFramework,
"DtaLikeHost.exe");

Assert.IsTrue(File.Exists(exePath), $"Expected DtaLikeHost.exe at '{exePath}'.");

// With the fix in place, Common.dll's compiled metadata references for
// System.Collections.Immutable and System.Reflection.Metadata match the DLLs
// shipped next to it, so the host exe completes normally even without any
// binding redirects in its app.config.
ExecuteApplication(exePath, args: null, out var runOut, out var runErr, out var runExit);

Assert.AreEqual(
0,
runExit,
"DtaLikeHost.exe exited non-zero, which means Common.dll's compiled metadata " +
"references for System.Collections.Immutable / System.Reflection.Metadata do " +
"not match the versions shipped next to it. DTA-style hosts (no binding " +
"redirects) will FileLoadException on FastFilter.Builder.\n" +
$"Tools dir: {toolsDirOverride ?? "<nupkg default>"}\n" +
$"STDOUT:\n{runOut}\nSTDERR:\n{runErr}");

Assert.Contains("OK - no binding exception.", runOut);
}

private static string GetPatchedDotnetPath()
{
var executable = OSUtils.IsWindows ? "dotnet.exe" : "dotnet";
return Path.GetFullPath(Path.Combine(IntegrationTestEnvironment.RepoRootDirectory, "artifacts", "tmp", ".dotnet", executable));
}
}
74 changes: 74 additions & 0 deletions test/TestAssets/DtaLikeHost/DtaLikeHost.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<Project Sdk="Microsoft.NET.Sdk">

<!--
Minimal repro for System.Collections.Immutable binding mismatch.

Simulates how Azure DevOps' Distributed Test Agent (DTAExecutionHost) consumes
the Microsoft.TestPlatform nupkg:
- net472 host process
- No app.config / no binding redirects (DTA has its own host exe that never
picked up vstest.console.exe.config).
- References Common.dll + ObjectModel.dll from the package's
tools/net462/Common7/IDE/Extensions/TestPlatform/ layout, which is the
same layout the VsTest task lays out on the agent.

FilterExpressionWrapper's ctor calls FilterExpression.Parse which, for simple
filters, builds a FastFilter via FastFilter.Builder. FastFilter.Builder uses
ImmutableDictionary / ImmutableHashSet, which forces System.Collections.Immutable
to load.

With the fix, Common.dll's metadata references System.Collections.Immutable v10.0.0.0
and the package ships v10.0.0.0 next to it, so hosts without binding redirects
can load SCI without FileLoadException.
-->

<PropertyGroup>
<AssemblyName>DtaLikeHost</AssemblyName>
<TargetFramework>net472</TargetFramework>
<OutputType>Exe</OutputType>
<!-- Emulate DTA: no auto-generated binding redirects, no app.config. -->
<AutoGenerateBindingRedirects>false</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>false</GenerateBindingRedirectsOutputType>
<ImplicitlyExpandNETStandardFacades>true</ImplicitlyExpandNETStandardFacades>
<!-- Avoid warnings-as-errors on NuGet audit for a repro asset. -->
<NuGetAudit>false</NuGetAudit>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
</PropertyGroup>

<ItemGroup Condition="'$(TestPlatformToolsDirOverride)' == ''">
<!-- Pull the package onto disk; we don't use its compile-time assets (there
are none for net472 in lib/), we consume its tools/ layout below. -->
<PackageReference Include="Microsoft.TestPlatform" Version="$(PackageVersion)" GeneratePathProperty="true" ExcludeAssets="all" PrivateAssets="all" />
</ItemGroup>

<PropertyGroup Condition="'$(TestPlatformToolsDirOverride)' == ''">
<_TestPlatformToolsDir>$(PkgMicrosoft_TestPlatform)\tools\net462\Common7\IDE\Extensions\TestPlatform</_TestPlatformToolsDir>
</PropertyGroup>

<PropertyGroup Condition="'$(TestPlatformToolsDirOverride)' != ''">
<!-- VSIX flat layout: all DLLs + .exe.configs at the root of the extracted VSIX. -->
<_TestPlatformToolsDir>$(TestPlatformToolsDirOverride)</_TestPlatformToolsDir>
</PropertyGroup>

<Target Name="AddTestPlatformToolsReferences" BeforeTargets="ResolveAssemblyReferences" Condition="Exists('$(_TestPlatformToolsDir)')">
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.TestPlatform.Common">
<HintPath>$(_TestPlatformToolsDir)\Microsoft.VisualStudio.TestPlatform.Common.dll</HintPath>
<Private>true</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestPlatform.ObjectModel">
<HintPath>$(_TestPlatformToolsDir)\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll</HintPath>
<Private>true</Private>
</Reference>
<!-- Copy the assemblies that actually ship next to Common.dll so the runtime
probing sees exactly what DTA would see on the build agent. -->
<Content Include="$(_TestPlatformToolsDir)\System.Collections.Immutable.dll" CopyToOutputDirectory="PreserveNewest" Visible="false" />
<Content Include="$(_TestPlatformToolsDir)\System.Reflection.Metadata.dll" CopyToOutputDirectory="PreserveNewest" Visible="false" />
<!-- SCI 10.0.0 for net462 has transitive dependencies that also ship in the layout. -->
<Content Include="$(_TestPlatformToolsDir)\System.Memory.dll" CopyToOutputDirectory="PreserveNewest" Visible="false" />
<Content Include="$(_TestPlatformToolsDir)\System.Buffers.dll" CopyToOutputDirectory="PreserveNewest" Visible="false" />
<Content Include="$(_TestPlatformToolsDir)\System.Runtime.CompilerServices.Unsafe.dll" CopyToOutputDirectory="PreserveNewest" Visible="false" />
</ItemGroup>
</Target>

</Project>
Loading