Skip to content
Open
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 @@ -6,6 +6,7 @@
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;

using Microsoft.TestPlatform.TestUtilities;
using Microsoft.VisualStudio.TestPlatform.Common.Utilities;
Expand Down Expand Up @@ -106,15 +107,37 @@ public void TypesToLoadAttributeTests()
}
};

foreach (var extension in extensionsToVerify.Keys)
// Use a custom AssemblyLoadContext so that dependencies (e.g. SCI 10.0.0)
// resolve from the extensions directory rather than the test host's runtime.
var parentDirectory = Path.GetDirectoryName(extensionsDirectory)!;
var alc = new AssemblyLoadContext("ExtensionTest", isCollectible: true);
alc.Resolving += (context, name) =>
{
var assemblyFile = Path.Combine(extensionsDirectory, extension);
var assembly = Assembly.LoadFrom(assemblyFile);
var path = Path.Combine(extensionsDirectory, name.Name + ".dll");
if (File.Exists(path))
return context.LoadFromAssemblyPath(path);
path = Path.Combine(parentDirectory, name.Name + ".dll");
if (File.Exists(path))
return context.LoadFromAssemblyPath(path);
return null;
};

try
{
foreach (var extension in extensionsToVerify.Keys)
{
var assemblyFile = Path.Combine(extensionsDirectory, extension);
var assembly = alc.LoadFromAssemblyPath(assemblyFile);

var expected = extensionsToVerify[extension];
var actual = TypesToLoadUtilities.GetTypesToLoad(assembly).Select(i => i.FullName).ToArray();
var expected = extensionsToVerify[extension];
var actual = TypesToLoadUtilities.GetTypesToLoad(assembly).Select(i => i.FullName).ToArray();

CollectionAssert.AreEquivalent(expected, actual, $"Specified types using TypesToLoadAttribute in \"{extension}\" assembly doesn't match the expected.");
CollectionAssert.AreEquivalent(expected, actual, $"Specified types using TypesToLoadAttribute in \"{extension}\" assembly doesn't match the expected.");
}
}
finally
{
alc.Unload();
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// 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;
using System.IO;
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;

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

namespace Microsoft.TestPlatform.AcceptanceTests;

/// <summary>
/// Tests that the DTA (Distributed Test Agent) scenario works: a .NET Framework host
/// loads Microsoft.VisualStudio.TestPlatform.Common.dll without binding redirects. The shipped
/// System.Collections.Immutable DLL must match the assembly version referenced by
/// the compiled product assemblies.
/// </summary>
[TestClass]
public class DistributedTestAgentScenarioTests : AcceptanceTestBase
{
/// <summary>
/// Validates that the Microsoft.TestPlatform nupkg layout (used by DTA/VSTest task)
/// contains a System.Collections.Immutable whose assembly version matches what
/// Common.dll was compiled against.
/// </summary>
[TestMethod]
public void NupkgLayout_CommonDll_CanLoadSciWithoutBindingRedirects()
{
var testPlatformDir = Path.Combine(
IntegrationTestEnvironment.PublishDirectory,
$"Microsoft.TestPlatform.{IntegrationTestEnvironment.LatestLocallyBuiltNugetVersion}.nupkg",
"tools", "net462", "Common7", "IDE", "Extensions", "TestPlatform");

ValidateDtaScenario(testPlatformDir, "nupkg");
}

/// <summary>
/// Validates that the V2.CLI VSIX layout also has matching SCI assembly versions.
/// </summary>
[TestMethod]
public void VsixLayout_CommonDll_CanLoadSciWithoutBindingRedirects()
{
var vsixDir = Path.GetDirectoryName(IntegrationTestEnvironment.LocalVsixInsertion);
if (vsixDir is null || !Directory.Exists(vsixDir))
{
Assert.Inconclusive("VSIX directory not found. Build with -pack to produce it.");
}

var vsixExtractDir = Path.Combine(IntegrationTestEnvironment.ArtifactsTempDirectory, "vsix-extracted");
if (!Directory.Exists(vsixExtractDir))
{
System.IO.Compression.ZipFile.ExtractToDirectory(IntegrationTestEnvironment.LocalVsixInsertion, vsixExtractDir);
}

ValidateDtaScenario(vsixExtractDir, "VSIX");
}

private static void ValidateDtaScenario(string testPlatformDir, string layoutName)
{
Assert.IsTrue(Directory.Exists(testPlatformDir), $"{layoutName} directory not found: {testPlatformDir}");

var commonDll = Path.Combine(testPlatformDir, "Microsoft.VisualStudio.TestPlatform.Common.dll");
var sciDll = Path.Combine(testPlatformDir, "System.Collections.Immutable.dll");

Assert.IsTrue(File.Exists(commonDll), $"Common.dll not found in {layoutName} layout: {commonDll}");
Assert.IsTrue(File.Exists(sciDll), $"System.Collections.Immutable.dll not found in {layoutName} layout: {sciDll}");

var sciVersion = System.Reflection.AssemblyName.GetAssemblyName(sciDll).Version;
var sciRefVersion = GetReferencedAssemblyVersion(commonDll, "System.Collections.Immutable");

Assert.IsNotNull(sciRefVersion, $"Common.dll in {layoutName} layout does not reference System.Collections.Immutable");

// For DTA scenario (no binding redirects), these MUST match exactly.
Assert.AreEqual(
sciRefVersion,
sciVersion,
$"{layoutName} layout: Common.dll references SCI {sciRefVersion} but shipped DLL is {sciVersion}. " +
$"DTA hosts without binding redirects will fail with FileLoadException.");
}

private static Version? GetReferencedAssemblyVersion(string assemblyPath, string referenceName)
{
using var stream = File.OpenRead(assemblyPath);
using var peReader = new PEReader(stream);
var mdReader = peReader.GetMetadataReader();
foreach (var handle in mdReader.AssemblyReferences)
{
var asmRef = mdReader.GetAssemblyReference(handle);
if (mdReader.GetString(asmRef.Name) == referenceName)
{
return asmRef.Version;
}
}

return null;
}
}

71 changes: 71 additions & 0 deletions test/TestAssets/DtaLikeHost/DtaLikeHost.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<Project Sdk="Microsoft.NET.Sdk">

<!--
Minimal repro for System.Collections.Immutable 8.0.0 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.

In 18.5.0 Common.dll's metadata references System.Collections.Immutable v8.0.0.0
and System.Reflection.Metadata v8.0.0.0, while the package ships v9.0.0.0 of both
next to it, so any host without a matching binding redirect is expected to
FileLoadException when SCI / SRM is loaded.
-->

<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 SCI and SRM 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" />
</ItemGroup>
</Target>

</Project>
Loading