From c6317a8539b4755b901b664feb3e8842ed74abb8 Mon Sep 17 00:00:00 2001 From: Shane Neuville Date: Tue, 17 May 2022 15:39:57 -0500 Subject: [PATCH] Add a Semaphore for AttachAndRun (#7269) --- .../src/DeviceTests/AssertionExtensions.Android.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/TestUtils/src/DeviceTests/AssertionExtensions.Android.cs b/src/TestUtils/src/DeviceTests/AssertionExtensions.Android.cs index a484ebe1f696..b58075ff0664 100644 --- a/src/TestUtils/src/DeviceTests/AssertionExtensions.Android.cs +++ b/src/TestUtils/src/DeviceTests/AssertionExtensions.Android.cs @@ -95,6 +95,10 @@ public static AColor ColorAtPoint(this Bitmap bitmap, int x, int y, bool include return true; }); + // Android doesn't handle adding and removing views in parallel very well + // If a view is removed while a different test triggers a layout then you hit + // a NRE exception + static SemaphoreSlim _attachAndRunSemaphore = new SemaphoreSlim(1); public static async Task AttachAndRun(this AView view, Func> action) { if (view.Parent is WrapperView wrapper) @@ -115,17 +119,21 @@ public static async Task AttachAndRun(this AView view, Func> actio var act = context.GetActivity()!; var rootView = act.FindViewById(Android.Resource.Id.Content)!; - layout.AddView(view); - rootView.AddView(layout); + view.Id = AView.GenerateViewId(); + layout.Id = AView.GenerateViewId(); try { + await _attachAndRunSemaphore.WaitAsync(); + layout.AddView(view); + rootView.AddView(layout); return await Run(view, action); } finally { rootView.RemoveView(layout); layout.RemoveView(view); + _attachAndRunSemaphore.Release(); } } else