Skip to content
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

Enable Windows Image device tests #20167

Merged
merged 1 commit into from
Jan 26, 2024
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
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
Loading