diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyResolverTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyResolverTests.cs new file mode 100644 index 0000000000..01fab28cc0 --- /dev/null +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyResolverTests.cs @@ -0,0 +1,115 @@ +// 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.Platform.Acceptance.IntegrationTests; +using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; +using Microsoft.Testing.Platform.Helpers; + +namespace MSTest.Acceptance.IntegrationTests; + +[TestGroup] +public class AssemblyResolverTests : AcceptanceTestBase +{ + private readonly TestAssetFixture _testAssetFixture; + private const string AssetName = "AssemblyResolverCrash"; + + // There's a bug in TAFX where we need to use it at least one time somewhere to use it inside the fixture self (AcceptanceFixture). + public AssemblyResolverTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture, + AcceptanceFixture globalFixture) + : base(testExecutionContext) + { + _testAssetFixture = testAssetFixture; + } + + public async Task RunningTests_DoesNotHitResourceRecursionIssueAndDoesNotCrashTheRunner() + { + if (!OperatingSystem.IsWindows()) + { + // This test is for .NET Framework only. + return; + } + + var testHost = TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, TargetFrameworks.NetFramework[0].Arguments); + + var testHostResult = await testHost.ExecuteAsync(); + + testHostResult.AssertExitCodeIs(ExitCodes.Success); + } + + [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] + public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + { + public string TargetAssetPath => GetAssetPath(AssetName); + + public override IEnumerable<(string ID, string Name, string Code)> GetAssetsToGenerate() + { + yield return (AssetName, AssetName, + SourceCode + .PatchTargetFrameworks(TargetFrameworks.NetFramework) + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) + .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); + } + + private const string SourceCode = """ +#file AssemblyResolverCrash.csproj + + + + Exe + true + $TargetFrameworks$ + + + + + + + + + + +#file UnitTest1.cs +using System; +using System.Globalization; +using System.IO; +using System.IO.IsolatedStorage; +using System.Threading; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace RecursiveResourceLookupCrash +{ + [TestClass] + public class RecursiveResourceLookupCrashTests + { + [TestInitialize] + public void TestInitialize() + { + + } + + [TestMethod] + public void CrashesOnResourcesLookupWhenNotHandledByAssemblyResolver() + { + // You need to set non-English culture explicitly to reproduce recursive resource + // lookup bug in English environment. + Thread.CurrentThread.CurrentUICulture = new CultureInfo("ja-JP"); + + try + { + // This will internally trigger file not found exception, and try to find ja-JP resources + // for the string, which will trigger Resolve in AssemblyResolver, which will + // use File.Exists call and that will trigger another round of looking up ja-JP + // resources, until this is detected by .NET Framework, and Environment.FailFast + // is called to crash the testhost. + var stream = new IsolatedStorageFileStream("non-existent-filename", FileMode.Open); + } + catch (Exception) + { + } + } + } +} +"""; + } +} diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/testsbaseline.txt b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/testsbaseline.txt index 13e281e98e..d3e8ca84ec 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/testsbaseline.txt +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/testsbaseline.txt @@ -207,3 +207,4 @@ MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InfoTests. MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InfoTests.UsingInfoArgument_OutputCorrectVersionForMSTest(string) (net6.0) MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InfoTests.UsingInfoArgument_OutputCorrectVersionForMSTest(string) (net7.0) MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InfoTests.UsingInfoArgument_OutputCorrectVersionForMSTest(string) (net8.0) +MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.AssemblyResolverTests.RunningTests_DoesNotHitResourceRecursionIssueAndDoesNotCrashTheRunner()