diff --git a/src/Core/src/ImageSources/Android/Glide/PassThroughModelLoader.cs b/src/Core/src/ImageSources/Android/Glide/PassThroughModelLoader.cs index 8c3d314aa944..8659cf8b3e7a 100644 --- a/src/Core/src/ImageSources/Android/Glide/PassThroughModelLoader.cs +++ b/src/Core/src/ImageSources/Android/Glide/PassThroughModelLoader.cs @@ -4,6 +4,7 @@ using Bumptech.Glide.Load.Data; using Bumptech.Glide.Load.Model; using Bumptech.Glide.Signature; +using Java.IO; namespace Microsoft.Maui.BumptechGlide { @@ -32,8 +33,17 @@ public DataFetcher(Java.Lang.Object model) public void Cancel() { } - public void Cleanup() => - _model?.Dispose(); + public void Cleanup() + { + if (_model is InputStream inputStream) + { + try + { + inputStream.Close(); + } + catch (IOException) { } + } + } public void LoadData(Priority priority, IDataFetcherDataCallback callback) => callback.OnDataReady(_model); diff --git a/src/Core/tests/DeviceTests/Services/ImageSource/ImageSourceServiceTests.Android.cs b/src/Core/tests/DeviceTests/Services/ImageSource/ImageSourceServiceTests.Android.cs index d7b16754dc47..b04b8c032a0b 100644 --- a/src/Core/tests/DeviceTests/Services/ImageSource/ImageSourceServiceTests.Android.cs +++ b/src/Core/tests/DeviceTests/Services/ImageSource/ImageSourceServiceTests.Android.cs @@ -140,29 +140,34 @@ public async Task CustomReleasingImageSourceReturnsDifferentBitmap() result2.Dispose(); } - static async Task TryCollectFile(string bitmapFile) + async Task TryCollectFile(string bitmapFile) { var collected = false; - for (var i = 0; i < GCCollectRetries; i++) + for (var i = 0; i < GCCollectRetries && !collected; i++) { - GC.Collect(); + await WaitForGC(); try { // the OnlyRetrieveFromCache means that if it is not already loaded, then throw - await Glide - .With(Platform.DefaultContext) - .Load(bitmapFile, Platform.DefaultContext) - .SetOnlyRetrieveFromCache(true) - .SetDiskCacheStrategy(DiskCacheStrategy.None) - .SubmitAsync(Platform.DefaultContext); + _ = await Glide + .With(Platform.DefaultContext) + .Load(bitmapFile, Platform.DefaultContext) + .SetOnlyRetrieveFromCache(true) + .SetDiskCacheStrategy(DiskCacheStrategy.None) + .SubmitAsync(Platform.DefaultContext); } catch (ExecutionException ex) when (ex.Cause is GlideException) { // no-op becasue we are waiting for this collected = true; } + catch(GlideException) + { + // no-op becasue we are waiting for this + collected = true; + } } return collected; diff --git a/src/Core/tests/DeviceTests/TestBase.cs b/src/Core/tests/DeviceTests/TestBase.cs index 4fa5561671ed..c6779213b2d1 100644 --- a/src/Core/tests/DeviceTests/TestBase.cs +++ b/src/Core/tests/DeviceTests/TestBase.cs @@ -18,5 +18,14 @@ protected Task InvokeOnMainThreadAsync(Func action) => public Task InvokeOnMainThreadAsync(Func> func) => TestDispatcher.Current.DispatchAsync(func); + + protected async Task WaitForGC() + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + await Task.Delay(10); + GC.Collect(); + GC.WaitForPendingFinalizers(); + } } } diff --git a/src/TestUtils/src/DeviceTests/RepeatAttribute.cs b/src/TestUtils/src/DeviceTests/RepeatAttribute.cs new file mode 100644 index 000000000000..21728412691b --- /dev/null +++ b/src/TestUtils/src/DeviceTests/RepeatAttribute.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Xunit.Sdk; + +namespace Microsoft.Maui.DeviceTests +{ + /// + /// Usage Example + /// [Theory] + /// [Repeat(100)] + /// public async Task TheSameImageSourceReturnsTheSameBitmap(int _) + /// + public sealed class RepeatAttribute : DataAttribute + { + readonly int _count; + + public RepeatAttribute(int count) + { + this._count = count; + } + + public override IEnumerable GetData(MethodInfo testMethod) + { + foreach (var iterationNumber in Enumerable.Range(start: 1, count: this._count)) + { + yield return new object[] { iterationNumber }; + } + } + } +}