From f8da870ae413bfd8151c1c3e096cd94eaa46252a Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Fri, 29 Dec 2023 11:38:48 -0600 Subject: [PATCH] Stabilize ImageList_FinalizerReleasesNativeHandle_ReturnsExpected Closes #6635 --- .../UIIntegrationTests/ImageListTests.cs | 51 ++++++++++++++----- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/src/System.Windows.Forms/tests/IntegrationTests/UIIntegrationTests/ImageListTests.cs b/src/System.Windows.Forms/tests/IntegrationTests/UIIntegrationTests/ImageListTests.cs index 2799a87e59c..b03d00b3ab9 100644 --- a/src/System.Windows.Forms/tests/IntegrationTests/UIIntegrationTests/ImageListTests.cs +++ b/src/System.Windows.Forms/tests/IntegrationTests/UIIntegrationTests/ImageListTests.cs @@ -3,6 +3,7 @@ using System.ComponentModel; using System.Drawing; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Windows.Win32.System.Threading; using Xunit.Abstractions; @@ -20,48 +21,70 @@ public ImageListTests(ITestOutputHelper testOutputHelper) { } - [ActiveIssue("https://github.com/dotnet/winforms/issues/6635")] - [WinFormsFact(Skip = "Flaky tests, see: https://github.com/dotnet/winforms/issues/6635")] + [WinFormsFact] public void ImageList_FinalizerReleasesNativeHandle_ReturnsExpected() { + // Call GetGdiHandles at the start of the test to attempt to clear out any leftovers from previous tests + uint referenceGdiHandleCount = GetGdiHandles(); + TestOutputHelper.WriteLine($"Reference GDI handle count at start of test: {referenceGdiHandleCount}"); + // Warm up to create any GDI handles that are necessary, e.g. fonts, brushes, etc. - using (Form form = CreateForm()) - { - form.Show(); - } + ShowForm(); uint startGdiHandleCount = GetGdiHandles(); TestOutputHelper.WriteLine($"GDI handles before: {startGdiHandleCount}"); // Now test for real - using (Form form = CreateForm()) - { - form.Show(); - } + ShowForm(); uint endGdiHandleCount = GetGdiHandles(); TestOutputHelper.WriteLine($"GDI handles after: {endGdiHandleCount}"); Assert.Equal(startGdiHandleCount, endGdiHandleCount); + [MethodImpl(MethodImplOptions.NoInlining)] static uint GetGdiHandles() { - GC.Collect(); - GC.WaitForPendingFinalizers(); - GC.Collect(0); + uint result = GetGdiHandlesOnce(); + while (true) + { + uint updatedResult = GetGdiHandlesOnce(); + if (updatedResult == result) + { + // When two invocations return the same value, it's assumed to have stabilized + return result; + } + + result = updatedResult; + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static uint GetGdiHandlesOnce() + { + GC.GetTotalMemory(true); uint result = PInvoke.GetGuiResources((HANDLE)Process.GetCurrentProcess().Handle, GET_GUI_RESOURCES_FLAGS.GR_GDIOBJECTS); if (result == 0) { int lastWin32Error = Marshal.GetLastWin32Error(); - throw new Win32Exception(lastWin32Error, "Failed to retrieves the count of GDI handles"); + if (lastWin32Error != 0) + throw new Win32Exception(lastWin32Error, "Failed to retrieves the count of GDI handles"); } return result; } } + [MethodImpl(MethodImplOptions.NoInlining)] + private void ShowForm() + { + using Form form = CreateForm(); + form.Show(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] private Form CreateForm() { ListView listView1 = new()