Skip to content

Commit

Permalink
Blank image when Source set to null or on image loading error
Browse files Browse the repository at this point in the history
Fixes #8787
  • Loading branch information
hartez authored and jfversluis committed Mar 16, 2023
1 parent bdcfd7e commit cf0bb65
Show file tree
Hide file tree
Showing 7 changed files with 157 additions and 19 deletions.
5 changes: 5 additions & 0 deletions src/Core/src/Platform/Android/ImageSourcePartExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ internal static class ImageSourcePartExtensions
}
catch (Exception ex)
{
if (setImage is not null)
{
setImage(null);
}

events?.LoadingFailed(ex);
}
finally
Expand Down
32 changes: 17 additions & 15 deletions src/Core/src/Platform/ImageSourcePartLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public ImageSourcePartLoader(
{
Handler = handler;
_imageSourcePart = imageSourcePart;

SetImage = setImage;
}

Expand All @@ -52,27 +53,28 @@ public void Reset()

public async Task UpdateImageSourceAsync()
{
if (PlatformView != null)
if (PlatformView is null)
{
var token = this.SourceManager.BeginLoad();
var imageSource = _imageSourcePart();
return;
}

var token = this.SourceManager.BeginLoad();
var imageSource = _imageSourcePart();

if (imageSource != null)
{
if (imageSource is not null && imageSource.Source is not null)
{
#if __IOS__ || __ANDROID__ || WINDOWS || TIZEN
var result = await imageSource.UpdateSourceAsync(PlatformView, ImageSourceServiceProvider, SetImage!, token)
.ConfigureAwait(false);
var result = await imageSource.UpdateSourceAsync(PlatformView, ImageSourceServiceProvider, SetImage!, token)
.ConfigureAwait(false);

SourceManager.CompleteLoad(result);
SourceManager.CompleteLoad(result);
#else
await Task.CompletedTask;
await Task.CompletedTask;
#endif
}
else
{
SetImage?.Invoke(null);
SourceManager.CompleteLoad(null);
}
}
else
{
SetImage?.Invoke(null);
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/Core/src/Platform/Windows/ImageSourcePartExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ internal static class ImageSourcePartExtensions
}
catch (Exception ex)
{
if (setImage is not null)
{
setImage(null);
}

events?.LoadingFailed(ex);
}
finally
Expand Down
5 changes: 5 additions & 0 deletions src/Core/src/Platform/iOS/ImageSourcePartExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ internal static class ImageSourcePartExtensions
}
catch (Exception ex)
{
if (setImage is not null)
{
setImage(null);
}

events?.LoadingFailed(ex);
}
finally
Expand Down
52 changes: 52 additions & 0 deletions src/Core/tests/DeviceTests/Handlers/Image/ImageHandlerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -460,5 +460,57 @@ protected TCustomHandler CreateHandler<TCustomHandler>(IView view)
static int GetDrawableId(string image) =>
MauiProgram.DefaultContext.Resources.GetDrawableId(MauiProgram.DefaultContext.PackageName, image);
#endif

[Fact]
public async Task UpdatingSourceToNullClearsImage()
{
var image = new TStub
{
Background = new SolidPaintStub(Colors.Black),
Source = new FileImageSourceStub("red.png"),
};

await InvokeOnMainThreadAsync(async () =>
{
var handler = CreateHandler<CountedImageHandler>(image);

await image.Wait();

await handler.PlatformView.AssertContainsColor(Colors.Red);

handler.ImageEvents.Clear();

image.Source = null;
handler.UpdateValue(nameof(IImage.Source));

await image.Wait();

await handler.PlatformView.AssertDoesNotContainColor(Colors.Red);
});
}

[Fact]
public async Task UpdatingSourceToNonexistentSourceClearsImage()
{
var image = new TStub
{
Background = new SolidPaintStub(Colors.Black),
Source = new FileImageSourceStub("red.png"),
};

await InvokeOnMainThreadAsync(async () =>
{
var handler = CreateHandler<ImageHandler>(image);
await image.Wait();
await handler.PlatformView.AssertContainsColor(Colors.Red);

image.Source = new FileImageSourceStub("fail.png");
handler.UpdateValue(nameof(IImage.Source));
await handler.PlatformView.AttachAndRun(() => { });

await image.Wait(5000);
await handler.PlatformView.AssertDoesNotContainColor(Colors.Red);
});
}
}
}
40 changes: 38 additions & 2 deletions src/TestUtils/src/DeviceTests/AssertionExtensions.Android.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Platform;
using Xunit;
using Xunit.Sdk;
using AColor = Android.Graphics.Color;
using AView = Android.Views.View;

Expand Down Expand Up @@ -296,7 +297,13 @@ public static Bitmap AssertColorAtTopRight(this Bitmap bitmap, AColor expectedCo
return bitmap.AssertColorAtPoint(expectedColor, bitmap.Width - 1, bitmap.Height - 1);
}

public static Bitmap AssertContainsColor(this Bitmap bitmap, AColor expectedColor)
public static Task<Bitmap> AssertContainsColor(this Bitmap bitmap, Graphics.Color expectedColor, Func<Maui.Graphics.RectF, Maui.Graphics.RectF>? withinRectModifier = null)
=> Task.FromResult(bitmap.AssertContainsColor(expectedColor.ToPlatform()));

public static Task<Bitmap> AssertDoesNotContainColor(this Bitmap bitmap, Graphics.Color unexpectedColor, Func<Maui.Graphics.RectF, Maui.Graphics.RectF>? withinRectModifier = null)
=> Task.FromResult(bitmap.AssertDoesNotContainColor(unexpectedColor.ToPlatform()));

public static Bitmap AssertContainsColor(this Bitmap bitmap, AColor expectedColor, Func<Maui.Graphics.RectF, Maui.Graphics.RectF>? withinRectModifier = null)
{
for (int x = 0; x < bitmap.Width; x++)
{
Expand All @@ -309,19 +316,48 @@ public static Bitmap AssertContainsColor(this Bitmap bitmap, AColor expectedColo
}
}

Assert.True(false, CreateColorError(bitmap, $"Color {expectedColor} not found."));
throw new XunitException($"Color {expectedColor} not found.");
}

public static Bitmap AssertDoesNotContainColor(this Bitmap bitmap, AColor unexpectedColor, Func<Maui.Graphics.RectF, Maui.Graphics.RectF>? withinRectModifier = null)
{
var imageRect = new Graphics.RectF(0, 0, bitmap.Width, bitmap.Height);

if (withinRectModifier is not null)
imageRect = withinRectModifier.Invoke(imageRect);

for (int x = (int)imageRect.X; x < (int)imageRect.Width; x++)
{
for (int y = (int)imageRect.Y; y < (int)imageRect.Height; y++)
{
if (bitmap.ColorAtPoint(x, y, true).IsEquivalent(unexpectedColor))
{
throw new XunitException($"Color {unexpectedColor} was found at point {x}, {y}.");
}
}
}

return bitmap;
}

public static Task<Bitmap> AssertContainsColor(this AView view, Graphics.Color expectedColor) =>
AssertContainsColor(view, expectedColor.ToPlatform());

public static Task<Bitmap> AssertDoesNotContainColor(this AView view, Graphics.Color unexpectedColor) =>
AssertDoesNotContainColor(view, unexpectedColor.ToPlatform());

public static async Task<Bitmap> AssertContainsColor(this AView view, AColor expectedColor)
{
var bitmap = await view.ToBitmap();
return AssertContainsColor(bitmap, expectedColor);
}

public static async Task<Bitmap> AssertDoesNotContainColor(this AView view, AColor unexpectedColor)
{
var bitmap = await view.ToBitmap();
return AssertDoesNotContainColor(bitmap, unexpectedColor);
}

public static async Task<Bitmap> AssertColorAtPointAsync(this AView view, AColor expectedColor, int x, int y)
{
var bitmap = await view.ToBitmap();
Expand Down
37 changes: 35 additions & 2 deletions src/TestUtils/src/DeviceTests/AssertionExtensions.iOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -300,10 +300,22 @@ public static async Task<UIImage> AssertContainsColor(this UIView view, UIColor
return bitmap.AssertContainsColor(expectedColor);
}

public static async Task<UIImage> AssertDoesNotContainColor(this UIView view, UIColor unexpectedColor)
{
var bitmap = await view.ToBitmap();
return bitmap.AssertDoesNotContainColor(unexpectedColor);
}

public static Task<UIImage> AssertContainsColor(this UIView view, Microsoft.Maui.Graphics.Color expectedColor) =>
AssertContainsColor(view, expectedColor.ToPlatform());

public static UIImage AssertContainsColor(this UIImage bitmap, UIColor expectedColor)
public static Task<UIImage> AssertDoesNotContainColor(this UIView view, Microsoft.Maui.Graphics.Color unexpectedColor) =>
AssertDoesNotContainColor(view, unexpectedColor.ToPlatform());

public static Task<UIImage> AssertContainsColor(this UIImage image, Graphics.Color expectedColor, Func<Graphics.RectF, Graphics.RectF>? withinRectModifier = null)
=> Task.FromResult(image.AssertContainsColor(expectedColor.ToPlatform(), withinRectModifier));

public static UIImage AssertContainsColor(this UIImage bitmap, UIColor expectedColor, Func<Graphics.RectF, Graphics.RectF>? withinRectModifier = null)
{
for (int x = 0; x < bitmap.Size.Width; x++)
{
Expand All @@ -316,10 +328,31 @@ public static UIImage AssertContainsColor(this UIImage bitmap, UIColor expectedC
}
}

Assert.True(false, CreateColorError(bitmap, $"Color {expectedColor} not found."));
throw new XunitException($"Color {expectedColor} not found.");
}

public static UIImage AssertDoesNotContainColor(this UIImage bitmap, UIColor unexpectedColor, Func<Graphics.RectF, Graphics.RectF>? withinRectModifier = null)
{
var imageRect = new Graphics.RectF(0, 0, (float)bitmap.Size.Width.Value, (float)bitmap.Size.Height.Value);

if (withinRectModifier is not null)
imageRect = withinRectModifier.Invoke(imageRect);

for (int x = (int)imageRect.X; x < (int)imageRect.Width; x++)
{
for (int y = (int)imageRect.Y; y < (int)imageRect.Height; y++)
{
if (ColorComparison.ARGBEquivalent(bitmap.ColorAtPoint(x, y), unexpectedColor))
{
throw new XunitException($"Color {unexpectedColor} was found at point {x}, {y}.");
}
}
}

return bitmap;
}


public static Task AssertEqualAsync(this UIImage bitmap, UIImage other)
{
Assert.NotNull(bitmap);
Expand Down

0 comments on commit cf0bb65

Please sign in to comment.