diff --git a/Directory.Build.targets b/Directory.Build.targets index f0c700ee2e..6d96423635 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -4,7 +4,18 @@ <_NetFrameworkHostedCompilersVersion Condition="'$(_NetFrameworkHostedCompilersVersion)' == ''">4.11.0-3.24280.3 - + + + $(DefineConstants);IS_VSTEST_REPO + + $(MSBuildWarningsAsMessages);MSB3277 + + diff --git a/eng/Versions.props b/eng/Versions.props index 65d997fd1f..f3ef3e1262 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -70,7 +70,7 @@ <_MicrosoftVSSDKBuildToolsVersion_>17.14.2119 5.0.0 13.0.3 - 9.0.11 + 10.0.0 4.5.5 8.0.0 18.0.0-preview-1-10911-061 diff --git a/src/Microsoft.TestPlatform.CoreUtilities/Microsoft.TestPlatform.CoreUtilities.csproj b/src/Microsoft.TestPlatform.CoreUtilities/Microsoft.TestPlatform.CoreUtilities.csproj index 866225a77c..6776a4de05 100644 --- a/src/Microsoft.TestPlatform.CoreUtilities/Microsoft.TestPlatform.CoreUtilities.csproj +++ b/src/Microsoft.TestPlatform.CoreUtilities/Microsoft.TestPlatform.CoreUtilities.csproj @@ -19,6 +19,7 @@ + diff --git a/src/Microsoft.TestPlatform.ObjectModel/Microsoft.TestPlatform.ObjectModel.csproj b/src/Microsoft.TestPlatform.ObjectModel/Microsoft.TestPlatform.ObjectModel.csproj index 1675d4cb00..0d20b23a19 100644 --- a/src/Microsoft.TestPlatform.ObjectModel/Microsoft.TestPlatform.ObjectModel.csproj +++ b/src/Microsoft.TestPlatform.ObjectModel/Microsoft.TestPlatform.ObjectModel.csproj @@ -37,6 +37,7 @@ + diff --git a/src/datacollector/app.config b/src/datacollector/app.config index d464fe4c5d..3b5c8143b9 100644 --- a/src/datacollector/app.config +++ b/src/datacollector/app.config @@ -22,11 +22,21 @@ - + + + + + + + + + + + - + diff --git a/src/package/Microsoft.TestPlatform.CLI/Microsoft.TestPlatform.CLI.csproj b/src/package/Microsoft.TestPlatform.CLI/Microsoft.TestPlatform.CLI.csproj index 696cb68cbf..f3ad7825d0 100644 --- a/src/package/Microsoft.TestPlatform.CLI/Microsoft.TestPlatform.CLI.csproj +++ b/src/package/Microsoft.TestPlatform.CLI/Microsoft.TestPlatform.CLI.csproj @@ -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 --> - NU1702 + $(MSBuildWarningsAsMessages);NU1702 diff --git a/src/package/Microsoft.TestPlatform.Portable/Microsoft.TestPlatform.Portable.csproj b/src/package/Microsoft.TestPlatform.Portable/Microsoft.TestPlatform.Portable.csproj index 33fde7df1b..f17f680575 100644 --- a/src/package/Microsoft.TestPlatform.Portable/Microsoft.TestPlatform.Portable.csproj +++ b/src/package/Microsoft.TestPlatform.Portable/Microsoft.TestPlatform.Portable.csproj @@ -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 --> - NU1702 + $(MSBuildWarningsAsMessages);NU1702 diff --git a/src/package/Microsoft.TestPlatform/Microsoft.TestPlatform.csproj b/src/package/Microsoft.TestPlatform/Microsoft.TestPlatform.csproj index 540e0910ea..6739984bbd 100644 --- a/src/package/Microsoft.TestPlatform/Microsoft.TestPlatform.csproj +++ b/src/package/Microsoft.TestPlatform/Microsoft.TestPlatform.csproj @@ -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 --> - NU1702;NETSDK1023 + $(MSBuildWarningsAsMessages);NU1702;NETSDK1023 diff --git a/src/testhost.x86/app.config b/src/testhost.x86/app.config index ff5d13c675..84cb676f80 100644 --- a/src/testhost.x86/app.config +++ b/src/testhost.x86/app.config @@ -35,11 +35,21 @@ - + + + + + + + + + + + - + diff --git a/src/vstest.console/app.config b/src/vstest.console/app.config index 115518cc51..de2fe51c34 100644 --- a/src/vstest.console/app.config +++ b/src/vstest.console/app.config @@ -27,11 +27,11 @@ - + - + @@ -40,7 +40,12 @@ - + + + + + + @@ -55,7 +60,7 @@ - + diff --git a/test/Microsoft.TestPlatform.Acceptance.IntegrationTests/DiscoveryTests.cs b/test/Microsoft.TestPlatform.Acceptance.IntegrationTests/DiscoveryTests.cs index 3680b3c9bc..ef1817aeb7 100644 --- a/test/Microsoft.TestPlatform.Acceptance.IntegrationTests/DiscoveryTests.cs +++ b/test/Microsoft.TestPlatform.Acceptance.IntegrationTests/DiscoveryTests.cs @@ -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; @@ -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(); } } diff --git a/test/Microsoft.TestPlatform.Acceptance.IntegrationTests/DistributedTestAgentScenarioTests.cs b/test/Microsoft.TestPlatform.Acceptance.IntegrationTests/DistributedTestAgentScenarioTests.cs new file mode 100644 index 0000000000..972a6d6f2c --- /dev/null +++ b/test/Microsoft.TestPlatform.Acceptance.IntegrationTests/DistributedTestAgentScenarioTests.cs @@ -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; + +/// +/// 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. +/// +[TestClass] +public class DistributedTestAgentScenarioTests : AcceptanceTestBase +{ + /// + /// 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. + /// + [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"); + } + + /// + /// Validates that the V2.CLI VSIX layout also has matching SCI assembly versions. + /// + [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; + } +} + diff --git a/test/TestAssets/DtaLikeHost/DtaLikeHost.csproj b/test/TestAssets/DtaLikeHost/DtaLikeHost.csproj new file mode 100644 index 0000000000..cf11b4e281 --- /dev/null +++ b/test/TestAssets/DtaLikeHost/DtaLikeHost.csproj @@ -0,0 +1,71 @@ + + + + + + DtaLikeHost + net472 + Exe + + false + false + true + + false + false + + + + + + + + + <_TestPlatformToolsDir>$(PkgMicrosoft_TestPlatform)\tools\net462\Common7\IDE\Extensions\TestPlatform + + + + + <_TestPlatformToolsDir>$(TestPlatformToolsDirOverride) + + + + + + $(_TestPlatformToolsDir)\Microsoft.VisualStudio.TestPlatform.Common.dll + true + + + $(_TestPlatformToolsDir)\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll + true + + + + + + + + diff --git a/test/TestAssets/DtaLikeHost/Program.cs b/test/TestAssets/DtaLikeHost/Program.cs new file mode 100644 index 0000000000..7999bed8a5 --- /dev/null +++ b/test/TestAssets/DtaLikeHost/Program.cs @@ -0,0 +1,53 @@ +// 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.Reflection; + +using Microsoft.VisualStudio.TestPlatform.Common.Filtering; + +namespace DtaLikeHost; + +internal static class Program +{ + private static int Main() + { + // Report what Common.dll expects and what we ship next to it, so the mismatch + // (or agreement) is visible in the console output regardless of whether the + // CLR actually fails to bind. + var commonAsm = typeof(FilterExpressionWrapper).Assembly; + Console.WriteLine($"Common.dll path: {commonAsm.Location}"); + Console.WriteLine($"Common.dll version: {commonAsm.GetName().Version}"); + foreach (var r in commonAsm.GetReferencedAssemblies()) + { + if (r.Name == "System.Collections.Immutable" || r.Name == "System.Reflection.Metadata") + { + Console.WriteLine($" Common.dll references {r.Name}, Version={r.Version}"); + } + } + + try + { + // A simple equality filter produces a FastFilter, which triggers + // FastFilter.Builder.ctor -> ImmutableDictionary.CreateBuilder(...) + // -> forces the CLR to resolve System.Collections.Immutable at the version + // baked into Common.dll's metadata. + var wrapper = new FilterExpressionWrapper("TestCategory=Foo"); + Console.WriteLine($"FilterExpressionWrapper constructed: FilterString='{wrapper.FilterString}', ParseError='{wrapper.ParseError}'"); + + // Reflect on the private FastFilter field to prove it was actually built. + var fastFilterField = typeof(FilterExpressionWrapper).GetField("FastFilter", BindingFlags.Instance | BindingFlags.NonPublic); + var fastFilter = fastFilterField?.GetValue(wrapper); + Console.WriteLine($"FastFilter built: {fastFilter is not null}"); + } + catch (Exception ex) + { + Console.Error.WriteLine("REPRO HIT: exception constructing FilterExpressionWrapper:"); + Console.Error.WriteLine(ex); + return 1; + } + + Console.WriteLine("OK - no binding exception."); + return 0; + } +}