|
| 1 | +// Copyright (c) Microsoft Corporation. All rights reserved. |
| 2 | +// Licensed under the MIT license. See LICENSE file in the project root for full license information. |
| 3 | + |
| 4 | +using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; |
| 5 | +using Microsoft.Testing.Platform.Helpers; |
| 6 | + |
| 7 | +namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; |
| 8 | + |
| 9 | +[TestGroup] |
| 10 | +public sealed class HangDumpOutputTests : AcceptanceTestBase |
| 11 | +{ |
| 12 | + private readonly TestAssetFixture _testAssetFixture; |
| 13 | + |
| 14 | + public HangDumpOutputTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) |
| 15 | + : base(testExecutionContext) => _testAssetFixture = testAssetFixture; |
| 16 | + |
| 17 | + [Arguments("Mini")] |
| 18 | + public async Task HangDump_Outputs_HangingTests_EvenWhenHangingTestsHaveTheSameDisplayName(string format) |
| 19 | + { |
| 20 | + // This test makes sure that when tests have the same display name (e.g. like Test1 from both Class1 and Class2) |
| 21 | + // they will still show up in the hanging tests. This was not the case before when we were just putting them into |
| 22 | + // a dictionary based on DisplayName. In that case both tests were started at the same time, and only 1 entry was added |
| 23 | + // to currently executing tests. When first test with name Test1 completed we removed that entry, but Class2.Test1 was still |
| 24 | + // running. Solution is to use a more unique identifier. |
| 25 | + string resultDirectory = Path.Combine(_testAssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N"), format); |
| 26 | + var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, "HangDump", TargetFrameworks.NetCurrent.Arguments); |
| 27 | + TestHostResult testHostResult = await testHost.ExecuteAsync( |
| 28 | + $"--hangdump --hangdump-timeout 8s --hangdump-type {format} --results-directory {resultDirectory} --no-progress", |
| 29 | + new Dictionary<string, string> |
| 30 | + { |
| 31 | + { "SLEEPTIMEMS1", "100" }, |
| 32 | + { "SLEEPTIMEMS2", "600000" }, |
| 33 | + }); |
| 34 | + testHostResult.AssertExitCodeIs(ExitCodes.TestHostProcessExitedNonGracefully); |
| 35 | + testHostResult.AssertOutputContains("Test1"); |
| 36 | + } |
| 37 | + |
| 38 | + [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] |
| 39 | + public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) |
| 40 | + { |
| 41 | + private const string AssetName = "TestAssetFixture"; |
| 42 | + |
| 43 | + public string TargetAssetPath => GetAssetPath(AssetName); |
| 44 | + |
| 45 | + public override IEnumerable<(string ID, string Name, string Code)> GetAssetsToGenerate() |
| 46 | + { |
| 47 | + yield return (AssetName, AssetName, |
| 48 | + Sources |
| 49 | + .PatchTargetFrameworks(TargetFrameworks.All) |
| 50 | + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); |
| 51 | + } |
| 52 | + |
| 53 | + private const string Sources = """ |
| 54 | +#file HangDump.csproj |
| 55 | +
|
| 56 | +<Project Sdk="Microsoft.NET.Sdk"> |
| 57 | + <PropertyGroup> |
| 58 | + <TargetFrameworks>$TargetFrameworks$</TargetFrameworks> |
| 59 | + <OutputType>Exe</OutputType> |
| 60 | + <UseAppHost>true</UseAppHost> |
| 61 | + <Nullable>enable</Nullable> |
| 62 | + <LangVersion>preview</LangVersion> |
| 63 | + </PropertyGroup> |
| 64 | +
|
| 65 | + <ItemGroup> |
| 66 | + <PackageReference Include="Microsoft.Testing.Extensions.HangDump" Version="$MicrosoftTestingPlatformVersion$" /> |
| 67 | + </ItemGroup> |
| 68 | +</Project> |
| 69 | +
|
| 70 | +#file Program.cs |
| 71 | +
|
| 72 | +using System; |
| 73 | +using System.Threading; |
| 74 | +using System.Threading.Tasks; |
| 75 | +using System.Globalization; |
| 76 | +
|
| 77 | +using Microsoft.Testing.Platform; |
| 78 | +using Microsoft.Testing.Platform.Extensions.TestFramework; |
| 79 | +using Microsoft.Testing.Platform.Builder; |
| 80 | +using Microsoft.Testing.Platform.Capabilities.TestFramework; |
| 81 | +using Microsoft.Testing.Extensions; |
| 82 | +using Microsoft.Testing.Platform.Extensions.Messages; |
| 83 | +using Microsoft.Testing.Platform.Requests; |
| 84 | +using Microsoft.Testing.Platform.Services; |
| 85 | +
|
| 86 | +public class Startup |
| 87 | +{ |
| 88 | + public static async Task<int> Main(string[] args) |
| 89 | + { |
| 90 | + ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); |
| 91 | + builder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_,__) => new DummyTestAdapter()); |
| 92 | + builder.AddHangDumpProvider(); |
| 93 | + using ITestApplication app = await builder.BuildAsync(); |
| 94 | + return await app.RunAsync(); |
| 95 | + } |
| 96 | +} |
| 97 | +
|
| 98 | +public class DummyTestAdapter : ITestFramework, IDataProducer |
| 99 | +{ |
| 100 | + public string Uid => nameof(DummyTestAdapter); |
| 101 | +
|
| 102 | + public string Version => "2.0.0"; |
| 103 | +
|
| 104 | + public string DisplayName => nameof(DummyTestAdapter); |
| 105 | +
|
| 106 | + public string Description => nameof(DummyTestAdapter); |
| 107 | +
|
| 108 | + public Task<bool> IsEnabledAsync() => Task.FromResult(true); |
| 109 | +
|
| 110 | + public Type[] DataTypesProduced => new[] { typeof(TestNodeUpdateMessage) }; |
| 111 | +
|
| 112 | + public Task<CreateTestSessionResult> CreateTestSessionAsync(CreateTestSessionContext context) |
| 113 | + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); |
| 114 | +
|
| 115 | + public Task<CloseTestSessionResult> CloseTestSessionAsync(CloseTestSessionContext context) |
| 116 | + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); |
| 117 | +
|
| 118 | + public async Task ExecuteRequestAsync(ExecuteRequestContext context) |
| 119 | + { |
| 120 | + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() |
| 121 | + { |
| 122 | + Uid = "Class1.Test1", |
| 123 | + DisplayName = "Test1", |
| 124 | + Properties = new PropertyBag(new InProgressTestNodeStateProperty()), |
| 125 | + })); |
| 126 | +
|
| 127 | + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() |
| 128 | + { |
| 129 | + Uid = "Class2.Test1", |
| 130 | + DisplayName = "Test1", |
| 131 | + Properties = new PropertyBag(new InProgressTestNodeStateProperty()), |
| 132 | + })); |
| 133 | +
|
| 134 | + Thread.Sleep(int.Parse(Environment.GetEnvironmentVariable("SLEEPTIMEMS1")!, CultureInfo.InvariantCulture)); |
| 135 | +
|
| 136 | + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() |
| 137 | + { |
| 138 | + Uid = "Class1.Test1", |
| 139 | + DisplayName = "Test1", |
| 140 | + Properties = new PropertyBag(new PassedTestNodeStateProperty()), |
| 141 | + })); |
| 142 | +
|
| 143 | + Thread.Sleep(int.Parse(Environment.GetEnvironmentVariable("SLEEPTIMEMS2")!, CultureInfo.InvariantCulture)); |
| 144 | +
|
| 145 | + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() |
| 146 | + { |
| 147 | + Uid = "Class2.Test1", |
| 148 | + DisplayName = "Test1", |
| 149 | + Properties = new PropertyBag(new PassedTestNodeStateProperty()), |
| 150 | + })); |
| 151 | +
|
| 152 | + context.Complete(); |
| 153 | + } |
| 154 | +} |
| 155 | +"""; |
| 156 | + } |
| 157 | +} |
0 commit comments