using System; using System.IO; using System.Linq; using Microsoft.Data.Sqlite; using Xunit; using ytLive.Models; using ytLive.ViewModels; namespace ytLive.Tests; /// /// TASK 57, the "vv" feature, Unit 1 — the vertical (9:16) tier's camera /// placement. /// /// The vertical tier is a centred band crop of the SAME 1920×1080 master, so a /// camera placed for 16:9 lands outside the cropped window and is simply not /// in the vertical stream (the Live scene's camera sat at x≈1408 — off the /// 607.5-wide band entirely). Each scene therefore keeps its OWN vertical /// placement, and the tier switch swaps the live geometry. /// /// One test, the whole story (the Good Dog rule): the vertical tier is picked → /// the Live camera comes into the band as a small tile → dragging it to the /// top-right snaps it top-right INSIDE the band → switching back to 16:9 /// restores the desktop layout exactly → and the two placements survive a save /// and reload. /// [Collection("RealApp")] public sealed class VerticalWebcamLayoutTests { private readonly RealAppHost _app; public VerticalWebcamLayoutTests(RealAppHost app) => _app = app; [Fact] public void Vertical_Tier_Puts_The_Live_Camera_In_The_Band_And_16x9_Restores_The_Landscape_Layout() { _app.Run(Run); } private void Run() { var tempDb = Path.Combine(Path.GetTempPath(), $"ytLlive-vv-{Guid.NewGuid():N}.db"); MainViewModel.LayoutPathOverride = tempDb; try { var vm = new MainViewModel(); var live = vm.Scenes.First(s => s.Name == "Live"); var camera = new WebcamSceneConfig { WebcamId = "webcam-1", Name = "Webcam", X = 1408, Y = 778, Width = 480, Height = 270, }; live.Elements.Add(camera); // The 16:9 placement: the camera sits where the desktop layout put it. Assert.Equal(1408, camera.X); Assert.Equal(778, camera.Y); Assert.Equal(480, camera.Width); // ── The vertical tier is picked ─────────────────────────────────── vm.SelectedQuality = vm.QualityOptions[^1]; Assert.True(vm.IsVerticalTier); var band = vm.VerticalBand; // The camera came INTO the band (it used to sit outside it entirely). Assert.True(camera.X >= band.Left, $"camera X {camera.X} is left of the band {band.Left}"); Assert.True(camera.X + camera.Width <= band.Right, "the camera hangs over the band's right edge"); Assert.True(camera.Y >= band.Top, "the camera hangs over the band's top edge"); Assert.True(camera.Y + camera.Height <= band.Bottom, "the camera hangs over the band's bottom edge"); Assert.Equal(VerticalCameraLayout.TileWidth, camera.Width); Assert.Equal(VerticalCameraLayout.TileHeight, camera.Height); // ── Drag it to the top-right ───────────────────────────────────── vm.MoveWebcamInVerticalTier(camera, band.Right - 40, band.Top + 20); Assert.Equal(band.Right - VerticalCameraLayout.Inset - camera.Width, camera.X, 3); Assert.Equal(band.Top + VerticalCameraLayout.Inset, camera.Y, 3); Assert.True(camera.X >= band.Left && camera.X + camera.Width <= band.Right); Assert.True(camera.Y >= band.Top && camera.Y + camera.Height <= band.Bottom); var verticalX = camera.X; var verticalY = camera.Y; // ── Back to 16:9: the desktop layout is restored exactly ───────── vm.SelectedQuality = vm.QualityOptions[0]; Assert.False(vm.IsVerticalTier); Assert.Equal(1408, camera.X); Assert.Equal(778, camera.Y); Assert.Equal(480, camera.Width); Assert.Equal(270, camera.Height); // ── And the vertical tier comes back with the creator's placement ─ vm.SelectedQuality = vm.QualityOptions[^1]; Assert.Equal(verticalX, camera.X, 3); Assert.Equal(verticalY, camera.Y, 3); // ── It survives a save + reload: both placements, side by side ─── // The store writes the webcam rows only when the app has a webcam // identity (the config rows hang off it) — this box has no camera // plugged in, so the identity is supplied directly. using (var store = new Services.LayoutStore(tempDb)) { store.Save(vm.Scenes, new Webcam { Id = "webcam-1", DeviceId = "device-1", Name = "Webcam" }, null); } using var reloaded = new Services.LayoutStore(tempDb); var reloadedCamera = reloaded.Load().First(s => s.Name == "Live").WebcamConfig; Assert.NotNull(reloadedCamera); // The 16:9 layout is the one in the live columns… Assert.Equal(1408, reloadedCamera.X); Assert.Equal(778, reloadedCamera.Y); Assert.Equal(480, reloadedCamera.Width); Assert.Equal(270, reloadedCamera.Height); // …and the vertical placement is the creator's own, stored beside it. Assert.Equal(verticalX, reloadedCamera.VerticalX!.Value, 3); Assert.Equal(verticalY, reloadedCamera.VerticalY!.Value, 3); Assert.Equal(VerticalCameraLayout.TileWidth, reloadedCamera.VerticalWidth!.Value, 3); Assert.Equal(VerticalCameraLayout.TileHeight, reloadedCamera.VerticalHeight!.Value, 3); } finally { MainViewModel.LayoutPathOverride = null; SqliteConnection.ClearAllPools(); try { File.Delete(tempDb); } catch { /* best-effort cleanup */ } } } }