#nullable enable using UnitTests; using Xunit.Abstractions; namespace UnitTests_Parallelizable.ViewTests; public class ViewDrawingClippingTests () : FakeDriverBase { #region GetClip / SetClip Tests [Fact] public void GetClip_ReturnsDriverClip () { IDriver driver = CreateFakeDriver (80, 25); var region = new Region (new Rectangle (10, 10, 20, 20)); driver.Clip = region; View view = new () { Driver = driver }; Region? result = view.GetClip (); Assert.NotNull (result); Assert.Equal (region, result); } [Fact] public void SetClip_NullRegion_DoesNothing () { IDriver driver = CreateFakeDriver (80, 25); var original = new Region (new Rectangle (5, 5, 10, 10)); driver.Clip = original; View view = new () { Driver = driver }; view.SetClip (null); Assert.Equal (original, driver.Clip); } [Fact] public void SetClip_ValidRegion_SetsDriverClip () { IDriver driver = CreateFakeDriver (80, 25); var region = new Region (new Rectangle (10, 10, 30, 30)); View view = new () { Driver = driver }; view.SetClip (region); Assert.Equal (region, driver.Clip); } #endregion #region SetClipToScreen Tests [Fact] public void SetClipToScreen_ReturnsPreviousClip () { IDriver driver = CreateFakeDriver (80, 25); var original = new Region (new Rectangle (5, 5, 10, 10)); driver.Clip = original; View view = new () { Driver = driver }; Region? previous = view.SetClipToScreen (); Assert.Equal (original, previous); Assert.NotEqual (original, driver.Clip); } [Fact] public void SetClipToScreen_SetsClipToScreen () { IDriver driver = CreateFakeDriver (80, 25); View view = new () { Driver = driver }; view.SetClipToScreen (); Assert.NotNull (driver.Clip); Assert.Equal (driver.Screen, driver.Clip.GetBounds ()); } #endregion #region ExcludeFromClip Tests [Fact] public void ExcludeFromClip_Rectangle_NullDriver_DoesNotThrow () { View view = new () { Driver = null }; var exception = Record.Exception (() => view.ExcludeFromClip (new Rectangle (5, 5, 10, 10))); Assert.Null (exception); } [Fact] public void ExcludeFromClip_Rectangle_ExcludesArea () { IDriver driver = CreateFakeDriver (80, 25); driver.Clip = new Region (new Rectangle (0, 0, 80, 25)); View view = new () { Driver = driver }; var toExclude = new Rectangle (10, 10, 20, 20); view.ExcludeFromClip (toExclude); // Verify the region was excluded Assert.NotNull (driver.Clip); Assert.False (driver.Clip.Contains (15, 15)); } [Fact] public void ExcludeFromClip_Region_NullDriver_DoesNotThrow () { View view = new () { Driver = null }; var exception = Record.Exception (() => view.ExcludeFromClip (new Region (new Rectangle (5, 5, 10, 10)))); Assert.Null (exception); } [Fact] public void ExcludeFromClip_Region_ExcludesArea () { IDriver driver = CreateFakeDriver (80, 25); driver.Clip = new Region (new Rectangle (0, 0, 80, 25)); View view = new () { Driver = driver }; var toExclude = new Region (new Rectangle (10, 10, 20, 20)); view.ExcludeFromClip (toExclude); // Verify the region was excluded Assert.NotNull (driver.Clip); Assert.False (driver.Clip.Contains (15, 15)); } #endregion #region AddFrameToClip Tests [Fact] public void AddFrameToClip_NullDriver_ReturnsNull () { var view = new View { X = 0, Y = 0, Width = 10, Height = 10 }; view.BeginInit (); view.EndInit (); Region? result = view.AddFrameToClip (); Assert.Null (result); } [Fact] public void AddFrameToClip_IntersectsWithFrame () { IDriver driver = CreateFakeDriver (80, 25); driver.Clip = new Region (driver.Screen); var view = new View { X = 1, Y = 1, Width = 20, Height = 20, Driver = driver }; view.BeginInit (); view.EndInit (); view.LayoutSubViews (); Region? previous = view.AddFrameToClip (); Assert.NotNull (previous); Assert.NotNull (driver.Clip); // The clip should now be the intersection of the screen and the view's frame Rectangle expectedBounds = new Rectangle (1, 1, 20, 20); Assert.Equal (expectedBounds, driver.Clip.GetBounds ()); } #endregion #region AddViewportToClip Tests [Fact] public void AddViewportToClip_NullDriver_ReturnsNull () { var view = new View { X = 0, Y = 0, Width = 10, Height = 10 }; view.BeginInit (); view.EndInit (); Region? result = view.AddViewportToClip (); Assert.Null (result); } [Fact] public void AddViewportToClip_IntersectsWithViewport () { IDriver driver = CreateFakeDriver (80, 25); driver.Clip = new Region (driver.Screen); var view = new View { X = 1, Y = 1, Width = 20, Height = 20, Driver = driver }; view.BeginInit (); view.EndInit (); view.LayoutSubViews (); Region? previous = view.AddViewportToClip (); Assert.NotNull (previous); Assert.NotNull (driver.Clip); // The clip should be the viewport area Rectangle viewportScreen = view.ViewportToScreen (new Rectangle (Point.Empty, view.Viewport.Size)); Assert.Equal (viewportScreen, driver.Clip.GetBounds ()); } [Fact] public void AddViewportToClip_WithClipContentOnly_LimitsToVisibleContent () { IDriver driver = CreateFakeDriver (80, 25); driver.Clip = new Region (driver.Screen); var view = new View { X = 1, Y = 1, Width = 20, Height = 20, Driver = driver }; view.SetContentSize (new Size (100, 100)); view.ViewportSettings = ViewportSettingsFlags.ClipContentOnly; view.BeginInit (); view.EndInit (); view.LayoutSubViews (); Region? previous = view.AddViewportToClip (); Assert.NotNull (previous); Assert.NotNull (driver.Clip); // The clip should be limited to visible content Rectangle visibleContent = view.ViewportToScreen (new Rectangle (new (-view.Viewport.X, -view.Viewport.Y), view.GetContentSize ())); Rectangle viewport = view.ViewportToScreen (new Rectangle (Point.Empty, view.Viewport.Size)); Rectangle expected = Rectangle.Intersect (viewport, visibleContent); Assert.Equal (expected, driver.Clip.GetBounds ()); } #endregion #region Clip Interaction Tests [Fact] public void ClipRegions_StackCorrectly_WithNestedViews () { IDriver driver = CreateFakeDriver (100, 100); driver.Clip = new Region (driver.Screen); var superView = new View { X = 1, Y = 1, Width = 50, Height = 50, Driver = driver }; superView.BeginInit (); superView.EndInit (); var view = new View { X = 5, Y = 5, Width = 30, Height = 30, }; superView.Add (view); superView.LayoutSubViews (); // Set clip to superView's frame Region? superViewClip = superView.AddFrameToClip (); Rectangle superViewBounds = driver.Clip.GetBounds (); // Now set clip to view's frame Region? viewClip = view.AddFrameToClip (); Rectangle viewBounds = driver.Clip.GetBounds (); // Child clip should be within superView clip Assert.True (superViewBounds.Contains (viewBounds.Location)); // Restore superView clip view.SetClip (superViewClip); // Assert.Equal (superViewBounds, driver.Clip.GetBounds ()); } [Fact] public void ClipRegions_RespectPreviousClip () { IDriver driver = CreateFakeDriver (80, 25); var initialClip = new Region (new Rectangle (20, 20, 40, 40)); driver.Clip = initialClip; var view = new View { X = 1, Y = 1, Width = 60, Height = 60, Driver = driver }; view.BeginInit (); view.EndInit (); view.LayoutSubViews (); Region? previous = view.AddFrameToClip (); // The new clip should be the intersection of the initial clip and the view's frame Rectangle expected = Rectangle.Intersect ( initialClip.GetBounds (), view.FrameToScreen () ); Assert.Equal (expected, driver.Clip.GetBounds ()); // Restore should give us back the original view.SetClip (previous); Assert.Equal (initialClip.GetBounds (), driver.Clip.GetBounds ()); } #endregion #region Edge Cases [Fact] public void AddFrameToClip_EmptyFrame_WorksCorrectly () { IDriver driver = CreateFakeDriver (80, 25); driver.Clip = new Region (driver.Screen); var view = new View { X = 1, Y = 1, Width = 0, Height = 0, Driver = driver }; view.BeginInit (); view.EndInit (); view.LayoutSubViews (); Region? previous = view.AddFrameToClip (); Assert.NotNull (previous); Assert.NotNull (driver.Clip); } [Fact] public void AddViewportToClip_EmptyViewport_WorksCorrectly () { IDriver driver = CreateFakeDriver (80, 25); driver.Clip = new Region (driver.Screen); var view = new View { X = 1, Y = 1, Width = 1, // Minimal size to have adornments Height = 1, Driver = driver }; view.Border!.Thickness = new Thickness (1); view.BeginInit (); view.EndInit (); view.LayoutSubViews (); // With border thickness of 1, the viewport should be empty Assert.True (view.Viewport.Size.Width == 0 || view.Viewport.Size.Height == 0); Region? previous = view.AddViewportToClip (); Assert.NotNull (previous); } [Fact] public void ClipRegions_OutOfBounds_HandledCorrectly () { IDriver driver = CreateFakeDriver (80, 25); driver.Clip = new Region (driver.Screen); var view = new View { X = 100, // Outside screen bounds Y = 100, Width = 20, Height = 20, Driver = driver }; view.BeginInit (); view.EndInit (); view.LayoutSubViews (); Region? previous = view.AddFrameToClip (); Assert.NotNull (previous); // The clip should be empty since the view is outside the screen Assert.True (driver.Clip.IsEmpty () || !driver.Clip.Contains (100, 100)); } #endregion #region Drawing Tests [Fact] public void Clip_Set_BeforeDraw_ClipsDrawing () { IDriver driver = CreateFakeDriver (80, 25); var clip = new Region (new Rectangle (10, 10, 10, 10)); driver.Clip = clip; var view = new View { X = 0, Y = 0, Width = 50, Height = 50, Driver = driver }; view.BeginInit (); view.EndInit (); view.LayoutSubViews (); view.Draw (); // Verify clip was used Assert.NotNull (driver.Clip); } [Fact] public void Draw_UpdatesDriverClip () { IDriver driver = CreateFakeDriver (80, 25); driver.Clip = new Region (driver.Screen); var view = new View { X = 1, Y = 1, Width = 20, Height = 20, Driver = driver }; view.BeginInit (); view.EndInit (); view.LayoutSubViews (); view.Draw (); // Clip should be updated to exclude the drawn view Assert.NotNull (driver.Clip); // Assert.False (driver.Clip.Contains (15, 15)); // Point inside the view should be excluded } [Fact] public void Draw_WithSubViews_ClipsCorrectly () { IDriver driver = CreateFakeDriver (80, 25); driver.Clip = new Region (driver.Screen); var superView = new View { X = 1, Y = 1, Width = 50, Height = 50, Driver = driver }; var view = new View { X = 5, Y = 5, Width = 20, Height = 20 }; superView.Add (view); superView.BeginInit (); superView.EndInit (); superView.LayoutSubViews (); superView.Draw (); // Both superView and view should be excluded from clip Assert.NotNull (driver.Clip); // Assert.False (driver.Clip.Contains (15, 15)); // Point in superView should be excluded } [Fact] public void Draw_NonVisibleView_DoesNotUpdateClip () { IDriver driver = CreateFakeDriver (80, 25); var originalClip = new Region (driver.Screen); driver.Clip = originalClip.Clone (); var view = new View { X = 10, Y = 10, Width = 20, Height = 20, Visible = false, Driver = driver }; view.BeginInit (); view.EndInit (); view.Draw (); // Clip should not be modified for invisible views Assert.True (driver.Clip.Equals (originalClip)); } [Fact] public void ExcludeFromClip_ExcludesRegion () { IDriver driver = CreateFakeDriver (80, 25); driver.Clip = new Region (driver.Screen); var view = new View { X = 10, Y = 10, Width = 20, Height = 20, Driver = driver }; view.BeginInit (); view.EndInit (); view.LayoutSubViews (); var excludeRect = new Rectangle (15, 15, 10, 10); view.ExcludeFromClip (excludeRect); Assert.NotNull (driver.Clip); Assert.False (driver.Clip.Contains (20, 20)); // Point inside excluded rect should not be in clip } [Fact] public void ExcludeFromClip_WithNullClip_DoesNotThrow () { IDriver driver = CreateFakeDriver (80, 25); driver.Clip = null!; var view = new View { X = 10, Y = 10, Width = 20, Height = 20, Driver = driver }; var exception = Record.Exception (() => view.ExcludeFromClip (new Rectangle (15, 15, 10, 10))); Assert.Null (exception); } #endregion #region Misc Tests [Fact] public void SetClip_SetsDriverClip () { IDriver driver = CreateFakeDriver (80, 25); var view = new View { X = 10, Y = 10, Width = 20, Height = 20, Driver = driver }; var newClip = new Region (new Rectangle (5, 5, 30, 30)); view.SetClip (newClip); Assert.Equal (newClip, driver.Clip); } [Fact (Skip = "See BUGBUG in SetClip")] public void SetClip_WithNullClip_ClearsClip () { IDriver driver = CreateFakeDriver (80, 25); driver.Clip = new Region (new Rectangle (10, 10, 20, 20)); var view = new View { X = 10, Y = 10, Width = 20, Height = 20, Driver = driver }; view.SetClip (null); Assert.Null (driver.Clip); } [Fact] public void Draw_Excludes_View_From_Clip () { IDriver driver = CreateFakeDriver (80, 25); var originalClip = new Region (driver.Screen); driver.Clip = originalClip.Clone (); var view = new View { X = 10, Y = 10, Width = 20, Height = 20, Driver = driver }; view.BeginInit (); view.EndInit (); view.LayoutSubViews (); Region clipWithViewExcluded = originalClip.Clone (); clipWithViewExcluded.Exclude (view.Frame); view.Draw (); Assert.Equal (clipWithViewExcluded, driver.Clip); Assert.NotNull (driver.Clip); } [Fact] public void Draw_EmptyViewport_DoesNotCrash () { IDriver driver = CreateFakeDriver (80, 25); driver.Clip = new Region (driver.Screen); var view = new View { X = 10, Y = 10, Width = 1, Height = 1, Driver = driver }; view.Border!.Thickness = new Thickness (1); view.BeginInit (); view.EndInit (); view.LayoutSubViews (); // With border of 1, viewport should be empty (0x0 or negative) var exception = Record.Exception (() => view.Draw ()); Assert.Null (exception); } [Fact] public void Draw_VeryLargeView_HandlesClippingCorrectly () { IDriver driver = CreateFakeDriver (80, 25); driver.Clip = new Region (driver.Screen); var view = new View { X = 0, Y = 0, Width = 1000, Height = 1000, Driver = driver }; view.BeginInit (); view.EndInit (); view.LayoutSubViews (); var exception = Record.Exception (() => view.Draw ()); Assert.Null (exception); } [Fact] public void Draw_NegativeCoordinates_HandlesClippingCorrectly () { IDriver driver = CreateFakeDriver (80, 25); driver.Clip = new Region (driver.Screen); var view = new View { X = -10, Y = -10, Width = 50, Height = 50, Driver = driver }; view.BeginInit (); view.EndInit (); view.LayoutSubViews (); var exception = Record.Exception (() => view.Draw ()); Assert.Null (exception); } [Fact] public void Draw_OutOfScreenBounds_HandlesClippingCorrectly () { IDriver driver = CreateFakeDriver (80, 25); driver.Clip = new Region (driver.Screen); var view = new View { X = 100, Y = 100, Width = 50, Height = 50, Driver = driver }; view.BeginInit (); view.EndInit (); view.LayoutSubViews (); var exception = Record.Exception (() => view.Draw ()); Assert.Null (exception); } #endregion }