#if WINDOWS using System.Drawing; using System.Drawing.Imaging; using System.Runtime.InteropServices; using MeetingAssistant.Screenshots; using Microsoft.Extensions.DependencyInjection; using Xunit.Abstractions; namespace MeetingAssistant.Tests; public sealed class WindowsScreenshotImageCropperTests(ITestOutputHelper output) { [WindowsFact("System.Drawing screenshot cropping")] public Task WindowsCropUsesOcrPixelCoordinates() => AssertCropAsync(new ScreenshotCropCoordinates(0, 0, 2, 2)); [WindowsFact("System.Drawing screenshot cropping")] public Task WindowsCropPreservesExpectedPixelsAtNonzeroOrigin() => AssertCropAsync(new ScreenshotCropCoordinates(1, 1, 3, 2)); [WindowsFact("System.Drawing screenshot cropping")] public async Task WindowsCropRejectsInvalidBounds() { using var fixture = new ImageFixture(); await using var factory = new IsolatedApplicationFactory(); using var client = factory.CreateClient(); var cropper = GetWindowsCropper(factory); ScreenshotCropCoordinates[] invalidBounds = [ new(-1, 0, 1, 1), new(0, -1, 1, 1), new(0, 0, 0, 1), new(0, 0, 1, 0), new(0, 0, -1, 1), new(0, 0, 1, -1), new(4, 0, 1, 1), new(0, 3, 1, 1), new(3, 0, 2, 1), new(0, 2, 1, 2) ]; foreach (var bounds in invalidBounds) { var failure = await Assert.ThrowsAsync(() => cropper.SaveCroppedScreenshotAsync(fixture.InputPath, bounds, CancellationToken.None)); Assert.Contains("outside image bounds 4x3", failure.Message); Assert.False(File.Exists(fixture.CroppedPath)); output.WriteLine($"Rejected {bounds.X},{bounds.Y},{bounds.Width},{bounds.Height}; no output created."); } } private async Task AssertCropAsync(ScreenshotCropCoordinates bounds) { using var fixture = new ImageFixture(); await using var factory = new IsolatedApplicationFactory(); using var client = factory.CreateClient(); var cropper = GetWindowsCropper(factory); var path = await cropper.SaveCroppedScreenshotAsync( fixture.InputPath, bounds, CancellationToken.None); Assert.Equal(fixture.CroppedPath, path); using var cropped = new Bitmap(path!); Assert.Equal(bounds.Width, cropped.Width); Assert.Equal(bounds.Height, cropped.Height); for (var y = 0; y < cropped.Height; y++) { for (var x = 0; x < cropped.Width; x++) { Assert.Equal(ImageFixture.PixelAt(bounds.X + x, bounds.Y + y).ToArgb(), cropped.GetPixel(x, y).ToArgb()); } } output.WriteLine($"Verified every pixel of {bounds.Width}x{bounds.Height} at original origin {bounds.X},{bounds.Y}."); } private IScreenshotImageCropper GetWindowsCropper(IsolatedApplicationFactory factory) { var cropper = factory.Services.GetRequiredService(); Assert.IsType(cropper); output.WriteLine($"Registered cropper: {cropper.GetType().Name}; host: {RuntimeInformation.OSDescription}; " + $"architecture: {RuntimeInformation.ProcessArchitecture}; runtime: {RuntimeInformation.FrameworkDescription}; " + "target: net10.0-windows10.0.19041.0."); return cropper; } private sealed class ImageFixture : IDisposable { private readonly string root = Path.Combine(Path.GetTempPath(), $"meeting-assistant-windows-crop-{Guid.NewGuid():N}"); public ImageFixture() { Directory.CreateDirectory(root); using var image = new Bitmap(4, 3, PixelFormat.Format32bppArgb); for (var y = 0; y < image.Height; y++) { for (var x = 0; x < image.Width; x++) { image.SetPixel(x, y, PixelAt(x, y)); } } image.Save(InputPath, ImageFormat.Png); } public string InputPath => Path.Combine(root, "input.png"); public string CroppedPath => Path.Combine(root, "input-cropped.png"); // Each original coordinate has a distinct opaque color; no resampling tolerance hides a shifted origin. public static Color PixelAt(int x, int y) => Color.FromArgb(255, 30 + x * 50, 20 + y * 70, 10 + x * 15 + y * 5); public void Dispose() => Directory.Delete(root, recursive: true); } } #endif