Skip to content

Add retries to all tests related to checking for weak references #29957

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 12, 2025
Merged
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
17 changes: 3 additions & 14 deletions src/Controls/tests/Core.UnitTests/BindableLayoutTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -625,17 +625,8 @@ public async Task DoesNotLeak()
proxyRef = new WeakReference(proxy);
}

// First GC
await Task.Yield();
GC.Collect();
GC.WaitForPendingFinalizers();
Assert.False(controllerRef.IsAlive, "BindableLayoutController should not be alive!");

// Second GC
await Task.Yield();
GC.Collect();
GC.WaitForPendingFinalizers();
Assert.False(proxyRef.IsAlive, "WeakCollectionChangedProxy should not be alive!");
Assert.False(await controllerRef.WaitForCollect(), "BindableLayoutController should not be alive!");
Assert.False(await proxyRef.WaitForCollect(), "WeakCollectionChangedProxy should not be alive!");
}

[Fact("BindableLayout Still Updates after a GC")]
Expand All @@ -648,9 +639,7 @@ public async Task UpdatesAfterGC()

Assert.Equal(2, layout.Children.Count);

await Task.Yield();
GC.Collect();
GC.WaitForPendingFinalizers();
await TestHelpers.Collect();

list.Add("Baz");
Assert.Equal(3, layout.Children.Count);
Expand Down
15 changes: 3 additions & 12 deletions src/Controls/tests/Core.UnitTests/BindingUnitTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1245,24 +1245,15 @@ void create()
;
create();

await Task.Yield();
GC.Collect();
GC.WaitForPendingFinalizers();

if (mode == BindingMode.TwoWay || mode == BindingMode.OneWay)
Assert.False(weakViewModel.IsAlive, "ViewModel wasn't collected");
Assert.False(await weakViewModel.WaitForCollect(), "ViewModel wasn't collected");

if (mode == BindingMode.TwoWay || mode == BindingMode.OneWayToSource)
Assert.False(weakBindable.IsAlive, "Bindable wasn't collected");

// WeakPropertyChangedProxy won't go away until the second GC, BindingExpressionPart unsubscribes in its finalizer
await Task.Yield();
GC.Collect();
GC.WaitForPendingFinalizers();
Assert.False(await weakBindable.WaitForCollect(), "Bindable wasn't collected");

foreach (var proxy in proxies)
{
Assert.False(proxy.IsAlive, "WeakPropertyChangedProxy wasn't collected");
Assert.False(await proxy.WaitForCollect(), "WeakPropertyChangedProxy wasn't collected");
}
}

Expand Down
20 changes: 6 additions & 14 deletions src/Controls/tests/Core.UnitTests/SetterSpecificityListTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,42 +36,34 @@ public async Task RemovingValueDoesNotLeak()
var list = new SetterSpecificityList<object>();
list[SetterSpecificity.DefaultValue] = nameof(SetterSpecificity.DefaultValue);
list[SetterSpecificity.FromHandler] = nameof(SetterSpecificity.FromHandler);
WeakReference<object> weakReference;
WeakReference weakReference;

{
var o = new object();
weakReference = new WeakReference<object>(o);
weakReference = new WeakReference(o);
list[SetterSpecificity.FromBinding] = o;
}

list.Remove(SetterSpecificity.FromBinding);

await Task.Yield();
GC.Collect();
GC.WaitForPendingFinalizers();

Assert.False(weakReference.TryGetTarget(out _));
Assert.False(await weakReference.WaitForCollect());
}

[Fact]
public async Task RemovingLastValueDoesNotLeak()
{
var list = new SetterSpecificityList<object>();
WeakReference<object> weakReference;
WeakReference weakReference;

{
var o = new object();
weakReference = new WeakReference<object>(o);
weakReference = new WeakReference(o);
list[SetterSpecificity.ManualValueSetter] = o;
}

list.Remove(SetterSpecificity.ManualValueSetter);

await Task.Yield();
GC.Collect();
GC.WaitForPendingFinalizers();

Assert.False(weakReference.TryGetTarget(out _));
Assert.False(await weakReference.WaitForCollect());
}

[Fact]
Expand Down
10 changes: 6 additions & 4 deletions src/Controls/tests/Core.UnitTests/TestHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,21 @@ public static async Task Collect()
await Task.Yield();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect(2, GCCollectionMode.Forced, true);
GC.WaitForPendingFinalizers();
GC.Collect(2, GCCollectionMode.Forced, true);
await Task.Yield();
}


public static async Task<bool> WaitForCollect(this WeakReference reference)
{
for (int i = 0; i < 40 && reference.IsAlive; i++)
{
await Task.Yield();
GC.Collect();
GC.WaitForPendingFinalizers();
await Collect();
}

return reference.IsAlive;
}
}
}
}
21 changes: 5 additions & 16 deletions src/Controls/tests/Core.UnitTests/TypedBindingUnitTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1437,16 +1437,12 @@ public async Task BindingUnsubscribesForDeadTarget()

Assert.Equal(1, viewmodel.InvocationListSize());

await Task.Yield();
GC.Collect();
GC.WaitForPendingFinalizers();
Assert.False(await bindingRef.WaitForCollect(), "Binding should not be alive!");
Assert.False(await buttonRef.WaitForCollect(), "Button should not be alive!");

viewmodel.OnPropertyChanged("Foo");

Assert.Equal(0, viewmodel.InvocationListSize());

Assert.False(bindingRef.IsAlive, "Binding should not be alive!");
Assert.False(buttonRef.IsAlive, "Button should not be alive!");
}

[Fact]
Expand Down Expand Up @@ -1491,18 +1487,11 @@ public async Task BindingDoesNotStayAliveForDeadTarget()

Assert.Equal(1, viewModel.InvocationListSize());

await Task.Yield();
GC.Collect();
GC.WaitForPendingFinalizers();

Assert.False(bindingRef.IsAlive, "Binding should not be alive!");
Assert.False(buttonRef.IsAlive, "Button should not be alive!");
Assert.False(await bindingRef.WaitForCollect(), "Binding should not be alive!");
Assert.False(await buttonRef.WaitForCollect(), "Button should not be alive!");

// WeakPropertyChangedProxy won't go away until the second GC, PropertyChangedProxy unsubscribes in its finalizer
await Task.Yield();
GC.Collect();
GC.WaitForPendingFinalizers();
Assert.False(proxyRef.IsAlive, "WeakPropertyChangedProxy should not be alive!");
Assert.False(await proxyRef.WaitForCollect(), "WeakPropertyChangedProxy should not be alive!");
}

[Fact]
Expand Down
18 changes: 4 additions & 14 deletions src/Controls/tests/Core.UnitTests/VisualElementTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,7 @@ public async Task GradientBrushSubscribed()
fired = true;
};

await Task.Yield();
GC.Collect();
GC.WaitForPendingFinalizers();
await TestHelpers.Collect();
GC.KeepAlive(visual);

gradient.GradientStops.Add(new GradientStop(Colors.CornflowerBlue, 1));
Expand Down Expand Up @@ -187,9 +185,7 @@ public async Task RectangleGeometrySubscribed()
fired = true;
};

await Task.Yield();
GC.Collect();
GC.WaitForPendingFinalizers();
await TestHelpers.Collect();
GC.KeepAlive(visual);

geometry.Rect = new Rect(1, 2, 3, 4);
Expand All @@ -209,9 +205,7 @@ public async Task ShadowSubscribed()
fired = true;
};

await Task.Yield();
GC.Collect();
GC.WaitForPendingFinalizers();
await TestHelpers.Collect();
GC.KeepAlive(visualElement);

shadow.Brush = new SolidColorBrush(Colors.Green);
Expand All @@ -230,11 +224,7 @@ public async Task ShadowDoesNotLeak()

var reference = new WeakReference(new VisualElement { Shadow = shadow });

await Task.Yield();
GC.Collect();
GC.WaitForPendingFinalizers();

Assert.False(reference.IsAlive, "VisualElement should not be alive!");
Assert.False(await reference.WaitForCollect(), "VisualElement should not be alive!");
}

[Fact]
Expand Down
10 changes: 2 additions & 8 deletions src/Controls/tests/Core.UnitTests/WeakEventProxyTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,7 @@ public async Task DoesNotLeak()
reference = new WeakReference(subscriber);
}

await Task.Yield();
GC.Collect();
GC.WaitForPendingFinalizers();

Assert.False(reference.IsAlive, "Subscriber should not be alive!");
Assert.False(await reference.WaitForCollect(), "Subscriber should not be alive!");
}

[Fact]
Expand All @@ -40,9 +36,7 @@ public async Task EventFires()
NotifyCollectionChangedEventHandler handler = (s, e) => fired = true;
proxy.Subscribe(list, handler);

await Task.Yield();
GC.Collect();
GC.WaitForPendingFinalizers();
await TestHelpers.Collect();
GC.KeepAlive(handler);

list.Add("a");
Expand Down
4 changes: 1 addition & 3 deletions src/Controls/tests/Core.UnitTests/WindowsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -756,9 +756,7 @@ public async Task TwoKeysSameWindow()
}

// GC collect the original key
await Task.Yield();
GC.Collect();
GC.WaitForPendingFinalizers();
await TestHelpers.Collect();

// Same window, doesn't create a new one
var actual = ((IApplication)application).CreateWindow(new ActivationState(new MockMauiContext(), new PersistedState
Expand Down
Loading