Skip to content

Commit

Permalink
Enable Windows Image device tests (#20167)
Browse files Browse the repository at this point in the history
  • Loading branch information
mattleibow authored Jan 26, 2024
1 parent dbf7aad commit 744f951
Show file tree
Hide file tree
Showing 7 changed files with 235 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@
using Microsoft.Maui.DeviceTests.Stubs;
using Xunit;
using Xunit.Sdk;
#if __IOS__ || MACCATALYST
using PlatformView = UIKit.UIView;
#elif __ANDROID__
using PlatformView = Android.Views.View;
#elif WINDOWS
using PlatformView = Microsoft.UI.Xaml.FrameworkElement;
#elif TIZEN
using PlatformView = Tizen.NUI.BaseComponents.View;
#elif (NETSTANDARD || !PLATFORM)
using PlatformView = System.Object;
#endif

namespace Microsoft.Maui.DeviceTests
{
Expand Down Expand Up @@ -45,6 +56,20 @@ public Task<T> AttachAndRun<T>(IView view, Func<THandler, Task<T>> action)
}, MauiContext, async (view) => (IPlatformViewHandler)(await CreateHandlerAsync(view)));
}

public Task AttachAndRun(PlatformView view, Action action) =>
#if WINDOWS
view.AttachAndRun(action, MauiContext);
#else
view.AttachAndRun(action);
#endif

public Task AttachAndRun(PlatformView view, Func<Task> action) =>
#if WINDOWS
view.AttachAndRun(action, MauiContext);
#else
view.AttachAndRun(action);
#endif

protected Task<THandler> CreateHandlerAsync(IView view)
{
return InvokeOnMainThreadAsync(() =>
Expand Down
1 change: 0 additions & 1 deletion src/Core/tests/DeviceTests/Core.DeviceTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@
<None Include="@(Compile)" />
</ItemGroup>
<ItemGroup Condition="$(TargetFramework.Contains('-windows'))">
<Compile Remove="Handlers\Image\*.cs" />
<Compile Remove="Handlers\ImageButton\*.cs" />
<Compile Remove="Handlers\ShapeView\*.cs" />
<Content Include="Platforms\Windows\Assets\**" Link="Assets\%(RecursiveDir)%(Filename)%(Extension)" CopyToOutputDirectory="PreserveNewest" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System;
using Microsoft.UI.Xaml.Media.Imaging;
using WImage = Microsoft.UI.Xaml.Controls.Image;
using WStretch = Microsoft.UI.Xaml.Media.Stretch;

namespace Microsoft.Maui.DeviceTests
{
public partial class ImageHandlerTests<TImageHandler, TStub>
{
WImage GetPlatformImageView(IImageHandler imageHandler) =>
imageHandler.PlatformView;

bool GetNativeIsAnimationPlaying(IImageHandler imageHandler) =>
GetPlatformImageView(imageHandler).Source is BitmapImage bitmapImage && bitmapImage.IsPlaying;

Aspect GetNativeAspect(IImageHandler imageHandler) =>
GetPlatformImageView(imageHandler).Stretch switch
{
WStretch.Uniform => Aspect.AspectFit,
WStretch.UniformToFill => Aspect.AspectFill,
WStretch.Fill => Aspect.Fill,
WStretch.None => Aspect.Center,
_ => throw new ArgumentOutOfRangeException("Stretch")
};
}
}
80 changes: 61 additions & 19 deletions src/Core/tests/DeviceTests/Handlers/Image/ImageHandlerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#elif IOS || MACCATALYST
using UIKit;
using PlatformImageType = UIKit.UIImage;
#elif WINDOWS
using PlatformImageType = Microsoft.UI.Xaml.Controls.Image;
#endif

namespace Microsoft.Maui.DeviceTests
Expand All @@ -32,11 +34,14 @@ public abstract partial class ImageHandlerTests<TImageHandler, TStub> : CoreHand
#elif IOS || MACCATALYST
const string ImageEventAppResourceMemberName = "Image";
const string ImageEventCustomMemberName = "Image";
#elif WINDOWS
const string ImageEventAppResourceMemberName = "Source";
const string ImageEventCustomMemberName = "Source";
#endif

[Theory(
#if IOS || MACCATALYST
Skip = "Test failing on iOS"
#if IOS || MACCATALYST || WINDOWS
Skip = "Test failing on iOS and WINDOWS"
#endif
)]
[InlineData("#FF0000")]
Expand All @@ -55,28 +60,28 @@ await InvokeOnMainThreadAsync(async () =>
var handler = CreateHandler(image);
var platformView = GetPlatformImageView(handler);

await platformView.AttachAndRun(async () =>
await AttachAndRun(platformView, async () =>
{
// the first one works
image.Source = new FileImageSourceStub(firstPath);
handler.UpdateValue(nameof(IImage.Source));
await image.WaitUntilLoaded();

await platformView.AssertContainsColor(Colors.Blue.ToPlatform(), MauiContext);
await platformView.AssertContainsColor(Colors.Blue, MauiContext);

// the second one does not
image.Source = new FileImageSourceStub(secondPath);
handler.UpdateValue(nameof(IImage.Source));
await image.WaitUntilLoaded();

await platformView.AssertContainsColor(expectedColor.ToPlatform(), MauiContext);
await platformView.AssertContainsColor(expectedColor, MauiContext);
});
});
}

[Theory(
#if _ANDROID__
Skip = "Test failing on ANDROID"
#if ANDROID || WINDOWS
Skip = "Test failing on ANDROID and WINDOWS"
#endif
)]
[InlineData("red.png", "#FF0000")]
Expand Down Expand Up @@ -116,7 +121,9 @@ await InvokeOnMainThreadAsync(async () =>
#endif
)]
[InlineData("animated_heart.gif", true)]
#if !WINDOWS
[InlineData("animated_heart.gif", false)]
#endif
public async virtual Task AnimatedSourceInitializesCorrectly(string filename, bool isAnimating)
{
var image = new TStub
Expand All @@ -131,7 +138,7 @@ await InvokeOnMainThreadAsync(async () =>

await image.WaitUntilLoaded();

await GetPlatformImageView(handler).AttachAndRun(() =>
await AttachAndRun(GetPlatformImageView(handler), () =>
{
Assert.Equal(isAnimating, GetNativeIsAnimationPlaying(handler));
});
Expand Down Expand Up @@ -196,7 +203,11 @@ await InvokeOnMainThreadAsync(async () =>
Assert.NotNull(exception);
}

[Fact]
[Fact(
#if WINDOWS
Skip = "Hanging on Windows."
#endif
)]
public async Task ImageLoadSequenceIsCorrect()
{
await ImageLoadSequenceIsCorrectImplementation();
Expand Down Expand Up @@ -259,12 +270,15 @@ public async Task ImageLoadSequenceIsCorrect()
});
}

[Fact]
[Fact(
#if WINDOWS
Skip = "Hanging on Windows."
#endif
)]
public async Task InterruptingLoadCancelsAndStartsOver()
{
await InterruptingLoadCancelsAndStartsOverImplementation();
}

async Task<List<(string Member, object Value)>> InterruptingLoadCancelsAndStartsOverImplementation()
{
var image = new TStub
Expand Down Expand Up @@ -325,7 +339,11 @@ await InvokeOnMainThreadAsync(async () =>
return events;
}

[Theory]
[Theory(
#if WINDOWS
Skip = "To be implemented on Windows."
#endif
)]
[InlineData("#FF0000")]
[InlineData("#00FF00")]
[InlineData("#000000")]
Expand Down Expand Up @@ -353,7 +371,11 @@ await InvokeOnMainThreadAsync(async () =>
});
}

[Fact]
[Fact(
#if WINDOWS
Skip = "To be implemented on Windows."
#endif
)]
public async Task InitializingSourceOnlyUpdatesImageOnce()
{
var image = new TStub
Expand Down Expand Up @@ -382,7 +404,11 @@ await InvokeOnMainThreadAsync(async () =>
});
}

[Fact]
[Fact(
#if WINDOWS
Skip = "To be implemented on Windows."
#endif
)]
public async Task UpdatingSourceOnlyUpdatesImageOnce()
{
var image = new TStub
Expand Down Expand Up @@ -420,7 +446,11 @@ await InvokeOnMainThreadAsync(async () =>
});
}

[Fact]
[Fact(
#if WINDOWS
Skip = "Hanging on Windows."
#endif
)]
public async Task ImageLoadSequenceIsCorrectWithChecks()
{
var events = await ImageLoadSequenceIsCorrectImplementation();
Expand All @@ -437,7 +467,11 @@ public async Task ImageLoadSequenceIsCorrectWithChecks()
#endif
}

[Fact]
[Fact(
#if WINDOWS
Skip = "Hanging on Windows."
#endif
)]
public async Task InterruptingLoadCancelsAndStartsOverWithChecks()
{
var events = await InterruptingLoadCancelsAndStartsOverImplementation();
Expand Down Expand Up @@ -475,7 +509,11 @@ static int GetDrawableId(string image) =>
MauiProgram.DefaultContext.Resources.GetDrawableId(MauiProgram.DefaultContext.PackageName, image);
#endif

[Fact]
[Fact(
#if WINDOWS
Skip = "To be implemented on Windows."
#endif
)]
public async Task UpdatingSourceToNullClearsImage()
{
var image = new TStub
Expand Down Expand Up @@ -503,7 +541,11 @@ await InvokeOnMainThreadAsync(async () =>
});
}

[Fact]
[Fact(
#if WINDOWS
Skip = "To be implemented on Windows."
#endif
)]
public async Task UpdatingSourceToNonexistentSourceClearsImage()
{
var image = new TStub
Expand All @@ -520,7 +562,7 @@ await InvokeOnMainThreadAsync(async () =>

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

await image.WaitUntilLoaded(5000);
await handler.PlatformView.AssertDoesNotContainColor(Colors.Red, MauiContext);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
using System;
using System.IO;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Text;
using Microsoft.Maui.Devices;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Storage;
using Microsoft.UI.Xaml.Media.Imaging;
using Windows.Graphics.Imaging;
using Xunit;
using WColor = Windows.UI.Color;

namespace Microsoft.Maui.DeviceTests
{
public abstract partial class BaseImageSourceServiceTests
{
public static string CreateBitmapFile(int width, int height, Color color, string filename = null) =>
CreateBitmapFile(width, height, color.ToWindowsColor(), filename);

public static string CreateBitmapFile(int width, int height, WColor color, string filename = null)
{
filename ??= Guid.NewGuid().ToString("N") + ".png";
if (!Path.IsPathRooted(filename))
{
filename = Path.Combine(FileSystem.CacheDirectory, Guid.NewGuid().ToString("N"), filename);
}
var dir = Path.GetDirectoryName(filename);
Directory.CreateDirectory(dir);

using var src = CreateBitmapStream(width, height, color);
using var dst = File.Create(filename);
src.CopyTo(dst);

return filename;
}

public static Stream CreateBitmapStream(int width, int height, Color color) =>
CreateBitmapStream(width, height, color.ToWindowsColor());

public static Stream CreateBitmapStream(int width, int height, WColor color)
{
var bitmap = CreateBitmap(width, height, color);

var stream = new MemoryStream();

var encoder = BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream.AsRandomAccessStream()).GetAwaiter().GetResult();

encoder.SetPixelData(
BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Ignore,
(uint)bitmap.PixelWidth,
(uint)bitmap.PixelHeight,
96,
96,
bitmap.PixelBuffer.ToArray());

stream.Position = 0;

return stream;
}

public static WriteableBitmap CreateBitmap(int width, int height, Color color) =>
CreateBitmap(width, height, color.ToWindowsColor());

public static WriteableBitmap CreateBitmap(int width, int height, WColor color)
{
var bitmap = new WriteableBitmap(width, height);

using (var stream = bitmap.PixelBuffer.AsStream())
{
var pixels = new byte[width * height * 4];

for (var y = 0; y < height; y++)
{
for (var x = 0; x < width; x++)
{
var index = (y * width + x) * 4;

pixels[index + 0] = color.B;
pixels[index + 1] = color.G;
pixels[index + 2] = color.R;
pixels[index + 3] = color.A;
}
}

stream.Write(pixels, 0, pixels.Length);
}

bitmap.Invalidate();

return bitmap;
}
}
}
Loading

0 comments on commit 744f951

Please sign in to comment.