Răsfoiți Sursa

Simplify layout testing engine

Marcin Ziąbek 3 luni în urmă
părinte
comite
d990861c3d
22 a modificat fișierele cu 312 adăugiri și 792 ștergeri
  1. 22 22
      Source/QuestPDF.LayoutTests/ColumnTests.cs
  2. 0 1
      Source/QuestPDF.LayoutTests/Setup.cs
  3. 3 3
      Source/QuestPDF.LayoutTests/ShowIfTests.cs
  4. 12 12
      Source/QuestPDF.LayoutTests/ShrinkTests.cs
  5. 0 37
      Source/QuestPDF.LayoutTests/TestEngine/AnnotateInvalidAreaHelper.cs
  6. 0 44
      Source/QuestPDF.LayoutTests/TestEngine/BoundingBox.cs
  7. 1 24
      Source/QuestPDF.LayoutTests/TestEngine/ContinuousBlock.cs
  8. 26 0
      Source/QuestPDF.LayoutTests/TestEngine/DrawingRecorder.cs
  9. 29 0
      Source/QuestPDF.LayoutTests/TestEngine/ElementObserver.cs
  10. 19 0
      Source/QuestPDF.LayoutTests/TestEngine/ElementObserverSetter.cs
  11. 48 73
      Source/QuestPDF.LayoutTests/TestEngine/FluentExtensions.cs
  12. 142 37
      Source/QuestPDF.LayoutTests/TestEngine/LayoutTest.cs
  13. 0 14
      Source/QuestPDF.LayoutTests/TestEngine/LayoutTestException.cs
  14. 0 101
      Source/QuestPDF.LayoutTests/TestEngine/LayoutTestExecutor.cs
  15. 0 249
      Source/QuestPDF.LayoutTests/TestEngine/LayoutTestOutputVisualization.cs
  16. 0 55
      Source/QuestPDF.LayoutTests/TestEngine/LayoutTestResult.cs
  17. 0 88
      Source/QuestPDF.LayoutTests/TestEngine/LayoutTestValidator.cs
  18. 0 8
      Source/QuestPDF.LayoutTests/TestEngine/LayoutTestVisualizationStrategy.cs
  19. 0 6
      Source/QuestPDF.LayoutTests/TestEngine/Settings.cs
  20. 0 18
      Source/QuestPDF.LayoutTests/TestEngine/WrapChild.cs
  21. 5 0
      Source/QuestPDF/Drawing/DocumentGenerator.cs
  22. 5 0
      Source/QuestPDF/Fluent/GenerateExtensions.cs

+ 22 - 22
Source/QuestPDF.LayoutTests/ColumnTests.cs

@@ -7,19 +7,19 @@ public class ColumnTests
     {
         LayoutTest
             .HavingSpaceOfSize(100, 140)
-            .WithContent(content =>
+            .ForContent(content =>
             {
                 content.Shrink().Column(column =>
                 {
                     column.Spacing(10);
                     
-                    column.Item().Mock("a").Size(50, 30);
-                    column.Item().Mock("b").Size(40, 20);
-                    column.Item().Mock("c").Size(70, 40);
-                    column.Item().Mock("d").Size(60, 60);
+                    column.Item().Mock("a").ContinuousBlock(50, 30);
+                    column.Item().Mock("b").ContinuousBlock(40, 20);
+                    column.Item().Mock("c").ContinuousBlock(70, 40);
+                    column.Item().Mock("d").ContinuousBlock(60, 60);
                 });
             })
-            .ExpectedDrawResult(document =>
+            .ExpectDrawResult(document =>
             {
                 document
                     .Page()
@@ -47,15 +47,15 @@ public class ColumnTests
     {
         LayoutTest
             .HavingSpaceOfSize(100, 100)
-            .WithContent(content =>
+            .ForContent(content =>
             {
                 content.Shrink().Column(column =>
                 {
                     column.Spacing(10);
-                    column.Item().Mock("a").Size(50, 30);
+                    column.Item().Mock("a").ContinuousBlock(50, 30);
                 });
             })
-            .ExpectedDrawResult(document =>
+            .ExpectDrawResult(document =>
             {
                 document
                     .Page()
@@ -72,18 +72,18 @@ public class ColumnTests
     {
         LayoutTest
             .HavingSpaceOfSize(100, 100)
-            .WithContent(content =>
+            .ForContent(content =>
             {
                 content.Shrink().Column(column =>
                 {
                     column.Spacing(10);
                     
-                    column.Item().Mock("a").Size(50, 30);
-                    column.Item().Mock("b").Size(50, 0);
-                    column.Item().Mock("c").Size(70, 20);
+                    column.Item().Mock("a").ContinuousBlock(50, 30);
+                    column.Item().Mock("b").ContinuousBlock(50, 0);
+                    column.Item().Mock("c").ContinuousBlock(70, 20);
                 });
             })
-            .ExpectedDrawResult(document =>
+            .ExpectDrawResult(document =>
             {
                 document
                     .Page()
@@ -102,17 +102,17 @@ public class ColumnTests
     {
         LayoutTest
             .HavingSpaceOfSize(100, 100)
-            .WithContent(content =>
+            .ForContent(content =>
             {
                 content.Shrink().Column(column =>
                 {
                     column.Spacing(0);
                     
-                    column.Item().Mock("a").Size(50, 30);
-                    column.Item().Mock("b").Size(40, 20);
+                    column.Item().Mock("a").ContinuousBlock(50, 30);
+                    column.Item().Mock("b").ContinuousBlock(40, 20);
                 });
             })
-            .ExpectedDrawResult(document =>
+            .ExpectDrawResult(document =>
             {
                 document
                     .Page()
@@ -130,17 +130,17 @@ public class ColumnTests
     {
         LayoutTest
             .HavingSpaceOfSize(100, 80)
-            .WithContent(content =>
+            .ForContent(content =>
             {
                 content.Shrink().Column(column =>
                 {
                     column.Spacing(5);
                     
-                    column.Item().Mock("a").Size(50, 40);
-                    column.Item().Mock("b").Size(60, 50);
+                    column.Item().Mock("a").ContinuousBlock(50, 40);
+                    column.Item().Mock("b").ContinuousBlock(60, 50);
                 });
             })
-            .ExpectedDrawResult(document =>
+            .ExpectDrawResult(document =>
             {
                 document
                     .Page()

+ 0 - 1
Source/QuestPDF.LayoutTests/Setup.cs

@@ -7,7 +7,6 @@
         public static void Configure()
         {
             QuestPDF.Settings.License = LicenseType.Community;
-            QuestPDF.LayoutTests.TestEngine.Settings.LayoutTestVisualizationStrategy = LayoutTestVisualizationStrategy.WhenFailure;
         }
     }
 }

+ 3 - 3
Source/QuestPDF.LayoutTests/ShowIfTests.cs

@@ -7,16 +7,16 @@ public class ShowIfTests
     {
         LayoutTest
             .HavingSpaceOfSize(100, 100)
-            .WithContent(content =>
+            .ForContent(content =>
             {
                 content.Decoration(decoration =>
                 {
                     decoration.Before().ShowIf(c => c.PageNumber % 2 == 0).Mock("before").Size(80, 20);
-                    decoration.Content().Mock("content").Size(70, 460);
+                    decoration.Content().Mock("content").ContinuousBlock(70, 460);
                     decoration.After().ShowIf(c => c.PageNumber % 3 == 0).Mock("after").Size(90, 30);
                 });
             })
-            .ExpectedDrawResult(document =>
+            .ExpectDrawResult(document =>
             {
                 document
                     .Page()

+ 12 - 12
Source/QuestPDF.LayoutTests/ShrinkTests.cs

@@ -7,13 +7,13 @@ public class ShrinkTests
     {
         LayoutTest
             .HavingSpaceOfSize(100, 120)
-            .WithContent(content =>
+            .ForContent(content =>
             {
                 content
                     .Shrink()
-                    .Mock().Size(60, 200);
+                    .Mock().ContinuousBlock(60, 200);
             })
-            .ExpectedDrawResult(document =>
+            .ExpectDrawResult(document =>
             {
                 document
                     .Page()
@@ -38,13 +38,13 @@ public class ShrinkTests
     {
         LayoutTest
             .HavingSpaceOfSize(100, 120)
-            .WithContent(content =>
+            .ForContent(content =>
             {
                 content
                     .ShrinkVertical()
-                    .Mock().Size(60, 200);
+                    .Mock().ContinuousBlock(60, 200);
             })
-            .ExpectedDrawResult(document =>
+            .ExpectDrawResult(document =>
             {
                 document
                     .Page()
@@ -69,13 +69,13 @@ public class ShrinkTests
     {
         LayoutTest
             .HavingSpaceOfSize(100, 120)
-            .WithContent(content =>
+            .ForContent(content =>
             {
                 content
                     .ShrinkHorizontal()
-                    .Mock().Size(60, 200);
+                    .Mock().ContinuousBlock(60, 200);
             })
-            .ExpectedDrawResult(document =>
+            .ExpectDrawResult(document =>
             {
                 document
                     .Page()
@@ -100,14 +100,14 @@ public class ShrinkTests
     {
         LayoutTest
             .HavingSpaceOfSize(100, 120)
-            .WithContent(content =>
+            .ForContent(content =>
             {
                 content
                     .ContentFromRightToLeft()
                     .Shrink()
-                    .Mock().Size(60, 200);
+                    .Mock().ContinuousBlock(60, 200);
             })
-            .ExpectedDrawResult(document =>
+            .ExpectDrawResult(document =>
             {
                 document
                     .Page()

+ 0 - 37
Source/QuestPDF.LayoutTests/TestEngine/AnnotateInvalidAreaHelper.cs

@@ -1,37 +0,0 @@
-using QuestPDF.Helpers;
-using SkiaSharp;
-
-namespace QuestPDF.LayoutTests.TestEngine;
-
-internal static class AnnotateInvalidAreaHelper
-{
-    private const float StripeThickness = 1f;
-    private const float StripeScale = 3f;
-    private static readonly Color LineColor = Colors.Red.Medium;
-    
-    public static void Annotate(SKCanvas canvas, SKPath area)
-    {
-        canvas.Save();
-        canvas.ClipPath(area);
-
-        DrawCheckerboardPattern();
-        
-        canvas.Restore();
-
-        void DrawCheckerboardPattern()
-        {
-            var matrix = SKMatrix.CreateScale(StripeScale, StripeScale).PostConcat(SKMatrix.CreateRotation((float)(Math.PI / 4)));
-
-            using var paint = new SKPaint
-            {
-                Color = new SKColor(LineColor),
-                PathEffect = SKPathEffect.Create2DLine(StripeThickness, matrix),
-                IsAntialias = true
-            };
-
-            var stripeArea = area.Bounds;
-            stripeArea.Inflate(StripeScale * 2, StripeScale * 2);
-            canvas.DrawRect(stripeArea, paint);
-        }
-    }
-}

+ 0 - 44
Source/QuestPDF.LayoutTests/TestEngine/BoundingBox.cs

@@ -1,44 +0,0 @@
-using QuestPDF.Infrastructure;
-
-namespace QuestPDF.LayoutTests.TestEngine;
-
-internal class BoundingBox
-{
-    public double MinX { get; init; }
-    public double MinY { get; init; }
-    public double MaxX { get; init; }
-    public double MaxY { get; init; }
-    
-    public double Width => MaxX - MinX;
-    public double Height => MaxY - MinY;
-
-    public static BoundingBox From(Position position, Size size)
-    {
-        return new BoundingBox
-        {
-            MinX = position.X,
-            MinY = position.Y,
-            MaxX = position.X + size.Width,
-            MaxY = position.Y + size.Height
-        };
-    }
-}
-
-internal static class BoundingBoxExtensions
-{
-    public static BoundingBox? Intersection(BoundingBox first, BoundingBox second)
-    {
-        var common = new BoundingBox
-        {
-            MinX = Math.Max(first.MinX, second.MinX),
-            MinY = Math.Max(first.MinY, second.MinY),
-            MaxX = Math.Min(first.MaxX, second.MaxX),
-            MaxY = Math.Min(first.MaxY, second.MaxY),
-        };
-
-        if (common.Width < 0 || common.Height < 0)
-            return null;
-
-        return common;
-    }
-}

+ 1 - 24
Source/QuestPDF.LayoutTests/TestEngine/MockChild.cs → Source/QuestPDF.LayoutTests/TestEngine/ContinuousBlock.cs

@@ -1,28 +1,15 @@
 using QuestPDF.Drawing;
 using QuestPDF.Helpers;
-using QuestPDF.Infrastructure;
 using QuestPDF.Skia;
 
 namespace QuestPDF.LayoutTests.TestEngine;
 
-internal class MockDrawingCommand
+internal class ContinuousBlock : Element
 {
-    public string MockId { get; set; }
-    public int PageNumber { get; set; }
-    public Position Position { get; set; }
-    public Size Size { get; set; }
-}
-
-internal class ElementMock : Element
-{
-    public string MockId { get; set; }
-    
     public float TotalWidth { get; set; }
     public float TotalHeight { get; set; }
     
     private float HeightOffset { get; set; }
-
-    internal List<MockDrawingCommand> DrawingCommands { get; } = new();
     
     internal override SpacePlan Measure(Size availableSpace)
     {
@@ -53,16 +40,6 @@ internal class ElementMock : Element
         using var paint = new SkPaint();
         paint.SetSolidColor(Colors.Grey.Medium);
         Canvas.DrawRectangle(Position.Zero, size, paint);
-        
-        var matrix = Canvas.GetCurrentMatrix();
-        
-        DrawingCommands.Add(new MockDrawingCommand
-        {
-            MockId = MockId,
-            PageNumber = PageContext.CurrentPage,
-            Position = new Position(matrix.TranslateX / matrix.ScaleX, matrix.TranslateY / matrix.ScaleY),
-            Size = availableSpace
-        });
 
         if (HeightOffset > TotalHeight - Size.Epsilon)
             HeightOffset = 0;

+ 26 - 0
Source/QuestPDF.LayoutTests/TestEngine/DrawingRecorder.cs

@@ -0,0 +1,26 @@
+using System.Collections.ObjectModel;
+
+namespace QuestPDF.LayoutTests.TestEngine;
+
+internal class ElementDrawingEvent
+{
+    public string ObserverId { get; set; }
+    public int PageNumber { get; set; }
+    public Position Position { get; set; }
+    public Size Size { get; set; }
+}
+
+internal class DrawingRecorder
+{
+    private List<ElementDrawingEvent> DrawingEvents { get; } = [];
+    
+    public void Record(ElementDrawingEvent elementDrawingEvent)
+    {
+        DrawingEvents.Add(elementDrawingEvent);
+    }
+    
+    public IReadOnlyCollection<ElementDrawingEvent> GetDrawingEvents()
+    {
+        return new ReadOnlyCollection<ElementDrawingEvent>(DrawingEvents);
+    }
+}

+ 29 - 0
Source/QuestPDF.LayoutTests/TestEngine/ElementObserver.cs

@@ -0,0 +1,29 @@
+using System.Diagnostics;
+
+namespace QuestPDF.LayoutTests.TestEngine;
+
+
+
+internal class ElementObserver : ContainerElement
+{
+    public string? ObserverId { get; set; }
+    public DrawingRecorder? DrawingRecorder { get; set; }
+    
+    internal override void Draw(Size availableSpace)
+    {
+        Debug.Assert(ObserverId != null);
+        Debug.Assert(DrawingRecorder != null);
+        
+        var matrix = Canvas.GetCurrentMatrix();
+        
+        DrawingRecorder?.Record(new ElementDrawingEvent
+        {
+            ObserverId = ObserverId,
+            PageNumber = PageContext.CurrentPage,
+            Position = new Position(matrix.TranslateX, matrix.TranslateY),
+            Size = ObserverId == "$document" ? Child.Measure(availableSpace) : availableSpace
+        });
+        
+        base.Draw(availableSpace);
+    }
+}

+ 19 - 0
Source/QuestPDF.LayoutTests/TestEngine/ElementObserverSetter.cs

@@ -0,0 +1,19 @@
+using QuestPDF.Helpers;
+
+namespace QuestPDF.LayoutTests.TestEngine;
+
+internal class ElementObserverSetter : ContainerElement
+{
+    public required DrawingRecorder Recorder { get; init; }
+    
+    internal override void Draw(Size availableSpace)
+    {
+        this.VisitChildren(x =>
+        {
+            if (x is ElementObserver observer)
+                observer.DrawingRecorder = Recorder;
+        });
+        
+        base.Draw(availableSpace);
+    }
+}

+ 48 - 73
Source/QuestPDF.LayoutTests/TestEngine/FluentExtensions.cs

@@ -1,122 +1,97 @@
-using QuestPDF.Fluent;
-using QuestPDF.Infrastructure;
-
 namespace QuestPDF.LayoutTests.TestEngine;
 
-internal class ExpectedDocumentLayoutDescriptor
+internal class ExpectedDocumentLayoutDescriptor(DrawingRecorder DrawingRecorder)
 {
-    public LayoutTestResult.DocumentLayout DocumentLayout { get; } = new(); 
+    private int CurrentPage { get; set; } = 1;
     
     public ExpectedPageLayoutDescriptor Page()
     {
-        var page = new LayoutTestResult.PageLayout();
-        DocumentLayout.Pages.Add(page);
-        return new ExpectedPageLayoutDescriptor(page);
-    }
-    
-    public void ExpectInfiniteLayoutException()
-    {
-        DocumentLayout.GeneratesInfiniteLayout = true;
+        return new ExpectedPageLayoutDescriptor(DrawingRecorder, CurrentPage++);
     }
 }
 
-internal class ExpectedPageLayoutDescriptor
+internal class ExpectedPageLayoutDescriptor(DrawingRecorder DrawingRecorder, int CurrentPageNumber)
 {
-    private LayoutTestResult.PageLayout PageLayout { get; }
-
-    public ExpectedPageLayoutDescriptor(LayoutTestResult.PageLayout pageLayout)
-    {
-        PageLayout = pageLayout;
-    }
-    
     public ExpectedPageLayoutDescriptor RequiredAreaSize(float width, float height)
     {
-        PageLayout.RequiredArea = new Size(width, height);
+        DrawingRecorder.Record(new ElementDrawingEvent
+        {
+            ObserverId = "$document",
+            PageNumber = CurrentPageNumber,
+            Size = new Size(width, height)
+        });
+        
         return this;
     }
     
-    public ExpectedPageLayoutDescriptor Content(Action<ExpectedPageContentDescriptor> content)
+    public void Content(Action<ExpectedPageContentDescriptor> content)
     {
-        var pageContent = new ExpectedPageContentDescriptor();
+        var pageContent = new ExpectedPageContentDescriptor(DrawingRecorder, CurrentPageNumber);
         content(pageContent);
-        
-        PageLayout.Mocks = pageContent.MockPositions;
-        return this;
     }
 }
 
-internal class ExpectedPageContentDescriptor
+internal class ExpectedPageContentDescriptor(DrawingRecorder drawingRecorder, int CurrentPageNumber)
 {
-    public List<LayoutTestResult.MockLayoutPosition> MockPositions { get;} = new();
-    
-    public ExpectedMockPositionDescriptor Mock(string mockId = MockFluent.DefaultMockId)
+    public ExpectedMockPositionDescriptor Mock(string mockId = FluentExtensions.DefaultMockId)
     {
-        var child = new LayoutTestResult.MockLayoutPosition { MockId = mockId };
-        MockPositions.Add(child);
-        return new ExpectedMockPositionDescriptor(child);
+        var elementDrawingEvent = new ElementDrawingEvent
+        {
+            ObserverId = mockId,
+            PageNumber = CurrentPageNumber,
+        };
+        
+        drawingRecorder.Record(elementDrawingEvent);
+        return new ExpectedMockPositionDescriptor(elementDrawingEvent);
     }
 }
 
-internal class ExpectedMockPositionDescriptor
+internal class ExpectedMockPositionDescriptor(ElementDrawingEvent drawingEvent)
 {
-    private LayoutTestResult.MockLayoutPosition MockLayoutPosition { get; }
-
-    public ExpectedMockPositionDescriptor(LayoutTestResult.MockLayoutPosition mockLayoutPosition)
-    {
-        MockLayoutPosition = mockLayoutPosition;
-    }
-
     public ExpectedMockPositionDescriptor Position(float x, float y)
     {
-        MockLayoutPosition.Position = new Position(x, y);
+        drawingEvent.Position = new Position(x, y);
         return this;
     }
     
     public ExpectedMockPositionDescriptor Size(float width, float height)
     {
-        MockLayoutPosition.Size = new Size(width, height);
+        drawingEvent.Size = new Size(width, height);
         return this;
     }
 }
 
-internal static class MockFluent
+internal static class FluentExtensions
 {
     public const string DefaultMockId = "$mock";
     
-    public static MockDescriptor Mock(this IContainer element, string id = DefaultMockId)
+    public static IContainer Mock(this IContainer element, string id = DefaultMockId)
     {
-        var mock = new ElementMock
+        return element.Element(new ElementObserver
         {
-            MockId = id
-        };
-        
-        element.Element(mock);
-        return new MockDescriptor(mock);
+            ObserverId = id
+        });
     } 
-}
-
-internal class MockDescriptor
-{
-    private ElementMock Mock { get; }
 
-    public MockDescriptor(ElementMock mock)
+    public static IContainer ElementObserverSetter(this IContainer element, DrawingRecorder recorder)
     {
-        Mock = mock;
-    }
-
-    public MockDescriptor Size(float width, float height)
+        return element.Element(new ElementObserverSetter
+        {
+            Recorder = recorder
+        });
+    } 
+    
+    public static IContainer Size(this IContainer element, float width, float height)
     {
-        Mock.TotalWidth = width;
-        Mock.TotalHeight = height;
-
-        return this;
-    }
-}
-
-internal static class WrapFluent
-{
-    public static void Wrap(this IContainer element)
+        return element.Width(width).Height(height);
+    }     
+    
+    public static void ContinuousBlock(this IContainer element, float width, float height)
     {
-        element.Element(new WrapChild());
+        element.Element(new ContinuousBlock
+        {
+            TotalWidth = width, 
+            TotalHeight = height
+        });
     } 
 }

+ 142 - 37
Source/QuestPDF.LayoutTests/TestEngine/LayoutTest.cs

@@ -1,77 +1,182 @@
-using System.Diagnostics;
 using System.Runtime.CompilerServices;
+using System.Text;
 using QuestPDF.Elements;
-using QuestPDF.Fluent;
-using QuestPDF.Infrastructure;
+using QuestPDF.Helpers;
 
 namespace QuestPDF.LayoutTests.TestEngine;
 
-internal sealed class LayoutTest
+internal class LayoutTest
 {
     private string TestIdentifier { get; set; }
-    private LayoutTestResult TestResult { get; } = new LayoutTestResult();
+    private Size AvailableSpace { get; set; }
+    private DrawingRecorder ActualDrawingRecorder { get; } = new();
+    private DrawingRecorder ExpectedDrawingRecorder { get; } = new();
+    private IContainer? Content { get; set; }
   
     public static LayoutTest HavingSpaceOfSize(float width, float height, [CallerMemberName] string testIdentifier = "test")
     {
         var layoutTest = new LayoutTest
         {
             TestIdentifier = testIdentifier,
-            
-            TestResult =
-            {
-                PageSize = new Size(width, height)
-            }
+            AvailableSpace = new Size(width, height)
         };
 
         return layoutTest;
     }
 
-    public LayoutTest WithContent(Action<IContainer> handler)
+    public LayoutTest ForContent(Action<IContainer> handler)
     {
-        var container = new Container();
-        container.Element(handler);
-
-        TestResult.ActualLayout = LayoutTestExecutor.Execute(TestResult.PageSize, container);
+        if (Content != null)
+            throw new InvalidOperationException("Content has already been defined.");
+        
+        Content = new Container();
+        
+        Content
+            .Width(AvailableSpace.Width)
+            .Height(AvailableSpace.Height)
+            .ElementObserverSetter(ActualDrawingRecorder)
+            .Mock("$document")
+            .Element(handler);
         
         return this;
     }
 
-    public void ExpectedDrawResult(Action<ExpectedDocumentLayoutDescriptor> handler)
+    public void ExpectDrawResult(Action<ExpectedDocumentLayoutDescriptor> handler)
     {
-        var builder = new ExpectedDocumentLayoutDescriptor();
+        if (!ActualDrawingRecorder.GetDrawingEvents().Any())
+            PerformTest();
+        
+        var builder = new ExpectedDocumentLayoutDescriptor(ExpectedDrawingRecorder);
         handler(builder);
 
-        TestResult.ExpectedLayout = builder.DocumentLayout;
+        var actualDrawingEvents = ActualDrawingRecorder.GetDrawingEvents();
+        var expectedDrawingEvents = ExpectedDrawingRecorder.GetDrawingEvents();
 
-        try
+        if (CheckIfIdentical(actualDrawingEvents, expectedDrawingEvents))
+        {
+            Assert.Pass();
+        }
+        else
         {
-            LayoutTestValidator.Validate(TestResult);
+            DrawLog(actualDrawingEvents, expectedDrawingEvents);
+            Assert.Fail($"The drawing operations do not match the expected result. See the log above for details. Test identifier: '{TestIdentifier}'.");
         }
-        catch
+
+        static bool CheckIfIdentical(IReadOnlyCollection<ElementDrawingEvent> actual, IReadOnlyCollection<ElementDrawingEvent> expected)
         {
-            if (Settings.LayoutTestVisualizationStrategy != LayoutTestVisualizationStrategy.Never)
-                GenerateTestPreview();
+            if (actual.Count != expected.Count)
+                return false;
+
+            return actual.Zip(expected, Compare).All(x => x);
+        }
+
+        static bool Compare(ElementDrawingEvent? actual, ElementDrawingEvent? expected)
+        {
+            if (actual == null && expected == null)
+                return true;
+            
+            if (actual == null || expected == null)
+                return false;
+            
+            return actual.ObserverId == expected.ObserverId &&
+                   actual.PageNumber == expected.PageNumber &&
+                   Position.Equal(actual.Position, expected.Position) &&
+                   Size.Equal(actual.Size, expected.Size);
+        }
+
+        static void DrawLog(IReadOnlyCollection<ElementDrawingEvent> actualEvents, IReadOnlyCollection<ElementDrawingEvent> expectedEvents)
+        {
+            var identicalLines = actualEvents.Zip(expectedEvents, Compare).TakeWhile(x => x).Count();
+
+            if (identicalLines > 0)
+            {
+                TestContext.Out.WriteLine("IDENTICAL");
+                TestContext.Out.WriteLine(DrawHeader());
                 
-            throw;
+                foreach (var actualEvent in actualEvents.Take(identicalLines))
+                    TestContext.Out.WriteLine($"🟩\t{GetEventAsText(actualEvent)}");
+            }
+
+            if (expectedEvents.Count > identicalLines)
+            {
+                TestContext.Out.WriteLine();
+                TestContext.Out.WriteLine("EXPECTED");
+                TestContext.Out.WriteLine(DrawHeader());
+                
+                foreach (var expectedEvent in expectedEvents.Skip(identicalLines))
+                    TestContext.Out.WriteLine($"🟧\t{GetEventAsText(expectedEvent)}");   
+            }
+
+            if (actualEvents.Count > identicalLines)
+            {
+                TestContext.Out.WriteLine();
+                TestContext.Out.WriteLine("ACTUAL");
+                TestContext.Out.WriteLine(DrawHeader());
+                
+                foreach (var actualEvent in actualEvents.Skip(identicalLines))
+                    TestContext.Out.WriteLine($"🟥\t{GetEventAsText(actualEvent)}");    
+            }
+        }
+
+        static string DrawHeader()
+        {
+            var mock = "Mock".PadRight(12);
+            var page = "Page".PadRight(6);
+            var x = "X".PadRight(8);
+            var y = "Y".PadRight(8);
+            var width = "W".PadRight(10);
+            var height = "H";
+            
+            return $"\t{mock} {page} {x} {y} {width} {height}";      
         }
-        finally
+        
+        static string GetEventAsText(ElementDrawingEvent drawingEvent)
         {
-            if (Settings.LayoutTestVisualizationStrategy == LayoutTestVisualizationStrategy.Always)
-                GenerateTestPreview();
+            var observerId = drawingEvent.ObserverId.PadRight(12);
+            var pageNumber = $"{drawingEvent.PageNumber}".PadRight(6);
+            
+            var positionX = $"{drawingEvent.Position.X}".PadRight(8);
+            var positionY = $"{drawingEvent.Position.Y}".PadRight(8);
+            
+            var sizeWidth = $"{drawingEvent.Size.Width}".PadRight(10);
+            var sizeHeight = $"{drawingEvent.Size.Height}";
+            
+            return $"{observerId} {pageNumber} {positionX} {positionY} {sizeWidth} {sizeHeight}";       
         }
     }
 
-    private void GenerateTestPreview()
+    private void PerformTest()
     {
-        var path = Path.Combine(Path.GetTempPath(), $"{TestIdentifier}.pdf");
-        
-        if (File.Exists(path))
-            File.Delete(path);
+        Document
+            .Create(document =>
+            {
+                document.Page(page =>
+                {
+                    page.MinSize(new PageSize(0, 0));
+                    page.MaxSize(new PageSize(Size.Infinity, Size.Infinity));
+                    page.Content().Element(Content);
+                });
+            })
+            .GenerateAndDiscard();
+    }
+
+    public LayoutTest VisualizeOutput()
+    {
+        if (Content == null)
+            throw new InvalidOperationException("Content has not been defined.");
         
-        var stream = new FileStream(path, FileMode.CreateNew);
-        LayoutTestResultVisualization.Visualize(TestResult, stream);
-        stream.Dispose();
+        Document
+            .Create(document =>
+            {
+                document.Page(page =>
+                {
+                    page.MinSize(new PageSize(0, 0));
+                    page.MaxSize(new PageSize(Size.Infinity, Size.Infinity));
+                    page.Content().Element(Content);
+                });
+            })
+            .GeneratePdfAndShow();
         
-        Helpers.Helpers.OpenFileUsingDefaultProgram(path);
+        return this;
     }
-}
+}

+ 0 - 14
Source/QuestPDF.LayoutTests/TestEngine/LayoutTestException.cs

@@ -1,14 +0,0 @@
-namespace QuestPDF.LayoutTests.TestEngine;
-
-public sealed class LayoutTestException : Exception
-{
-    internal LayoutTestException(string message) : base(message)
-    {
-            
-    }
-    
-    internal LayoutTestException(string message, Exception innerException) : base(message, innerException)
-    {
-            
-    }
-}

+ 0 - 101
Source/QuestPDF.LayoutTests/TestEngine/LayoutTestExecutor.cs

@@ -1,101 +0,0 @@
-using QuestPDF.Drawing;
-using QuestPDF.Drawing.DocumentCanvases;
-using QuestPDF.Drawing.Proxy;
-using QuestPDF.Elements;
-using QuestPDF.Helpers;
-using QuestPDF.Infrastructure;
-
-namespace QuestPDF.LayoutTests.TestEngine;
-
-internal static class LayoutTestExecutor
-{
-    public static LayoutTestResult.DocumentLayout Execute(Size pageSize, Container container)
-    {
-        var (pageSizes, generatesInfiniteLayout) = GenerateDocument();
-        container.ReleaseDisposableChildren();
-
-        return new LayoutTestResult.DocumentLayout
-        {
-            Pages = CollectMockInformation(pageSizes),
-            GeneratesInfiniteLayout = generatesInfiniteLayout
-        };
-
-        (List<Size> pageSizes, bool generatesInfiniteLayout) GenerateDocument()
-        {
-            // inject dependencies
-            var pageContext = new PageContext();
-            pageContext.ProceedToNextRenderingPhase();
-
-            using var canvas = new CompanionDocumentCanvas();
-        
-            container.InjectDependencies(pageContext, canvas.GetDrawingCanvas());
-        
-            // distribute global state
-            container.ApplyInheritedAndGlobalTexStyle(TextStyle.Default);
-            container.ApplyContentDirection(ContentDirection.LeftToRight);
-            container.ApplyDefaultImageConfiguration(DocumentSettings.Default.ImageRasterDpi, DocumentSettings.Default.ImageCompressionQuality, true);
-        
-            // render
-            container.VisitChildren(x => (x as IStateful)?.ResetState());
-            
-            var pageSizes = new List<Size>();
-        
-            while(true)
-            {
-                pageContext.IncrementPageNumber();
-              
-                var spacePlan = container.Measure(pageSize);
-                pageSizes.Add(spacePlan);
-
-                if (spacePlan.Type == SpacePlanType.Wrap)
-                {
-                    pageContext.DecrementPageNumber();
-                    canvas.EndDocument();
-                    return (pageSizes, true);
-                }
-
-                try
-                {
-                    canvas.BeginPage(pageSize);
-                    container.Draw(pageSize);
-                }
-                catch (Exception exception)
-                {
-                    canvas.EndDocument();
-                    throw new LayoutTestException("Exception occured during layout execution", exception);
-                }
-
-                canvas.EndPage();
-
-                if (spacePlan.Type == SpacePlanType.FullRender)
-                    break;
-            }
-
-            return (pageSizes, false);
-        }
-
-        ICollection<LayoutTestResult.PageLayout> CollectMockInformation(ICollection<Size> pageSizes)
-        {
-            // mock cannot contain another mock, flat structure
-            var mocks = container.ExtractElementsOfType<ElementMock>().Select(x => x.Value); 
-
-            return mocks
-                .SelectMany(x => x.DrawingCommands)
-                .GroupBy(x => x.PageNumber)
-                .OrderBy(x => x.Key)
-                .Select(x => new LayoutTestResult.PageLayout
-                {
-                    RequiredArea = pageSizes.ElementAt(x.Key - 1),
-                    Mocks = x
-                        .Select(y => new LayoutTestResult.MockLayoutPosition
-                        {
-                            MockId = y.MockId,
-                            Size = y.Size,
-                            Position = y.Position
-                        })
-                        .ToList()
-                })
-                .ToList();
-        }
-    }
-}

+ 0 - 249
Source/QuestPDF.LayoutTests/TestEngine/LayoutTestOutputVisualization.cs

@@ -1,249 +0,0 @@
-using QuestPDF.Drawing;
-using QuestPDF.Fluent;
-using QuestPDF.Helpers;
-using QuestPDF.Infrastructure;
-using SkiaSharp;
-
-namespace QuestPDF.LayoutTests.TestEngine;
-
-internal static class LayoutTestResultVisualization
-{
-    // output settings
-    private const int OutputImageScale = 2;
-    private const int Padding = 10;
-    
-    // document colors
-    private static readonly Color DocumentBackgroundColor = Colors.Grey.Darken2;
-    private static readonly Color PageBackgroundColor = Colors.Grey.Lighten1;
-    private static readonly Color RequiredAreaBackgroundColor = Colors.White;
-    
-    // grid configuration
-    private const float GridSize = 10;
-    private const float GridLineThickness = 0.25f;
-    private const byte GridLineTransparency = 48;
-    
-    // mock drawing settings
-    private const byte OccludedMockBorderThickness = 5;
-
-    private static readonly Color[] DefaultElementColors =
-    {
-        Colors.DeepPurple.Lighten2,
-        Colors.Blue.Lighten2,
-        Colors.Cyan.Lighten2,
-        Colors.Green.Lighten2,
-        Colors.Lime.Lighten2,
-        Colors.Amber.Lighten2,
-        Colors.Brown.Lighten2,
-        
-        Colors.DeepPurple.Medium,
-        Colors.Blue.Medium,
-        Colors.Cyan.Medium,
-        Colors.Green.Medium,
-        Colors.Lime.Medium,
-        Colors.Amber.Medium,
-        Colors.Brown.Medium,
-    };
-    
-    // implementations
-    public static void Visualize(LayoutTestResult result, Stream stream)
-    {
-        // determine output dimenstions
-        var numberOfPages = Math.Max(result.ActualLayout.Pages.Count, result.ExpectedLayout.Pages.Count);
-
-        var canvasWidth = result.PageSize.Width * 2 + Padding * 4;
-        var canvasHeight = result.PageSize.Height * numberOfPages + Padding * (numberOfPages + 2);
-
-        // create PDF
-        using var pdf = SKDocument.CreatePdf(stream);
-        using var canvas = pdf.BeginPage(canvasWidth * OutputImageScale, canvasHeight * OutputImageScale);
-        
-        canvas.Scale(OutputImageScale, OutputImageScale);
-        canvas.Clear(new SKColor(DocumentBackgroundColor));
-
-        // draw content
-        var mockColors = AssignColorsToMocks();
-        DrawDocument();
-
-        // finish generation
-        pdf.EndPage();
-        pdf.Close();
-
-        IDictionary<string, Color> AssignColorsToMocks()
-        {
-            var mocks = Enumerable
-                .Concat(result.ActualLayout.Pages, result.ExpectedLayout.Pages)
-                .SelectMany(x => x.Mocks)
-                .Select(x => x.MockId)
-                .Distinct()
-                .ToList();
-
-            return Enumerable
-                .Range(0, mocks.Count)
-                .ToDictionary(i => mocks[i], i => DefaultElementColors[i]);
-        }
-
-        void DrawDocument()
-        {
-            canvas.Translate(Padding, Padding);
-            
-            // draw title
-            using var textPaint = new SKPaint
-            {
-                TextSize = 8,
-                Color = SKColors.White,
-                Typeface = SKTypeface.FromFamilyName("Calibri", SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright),
-                TextAlign = SKTextAlign.Center
-            };
-            
-            var actualHeaderPosition = new SKPoint(result.PageSize.Width / 2, textPaint.TextSize / 2);
-            canvas.DrawText("ACTUAL", actualHeaderPosition, textPaint);
-            
-            var expectedHeaderPosition = new SKPoint(Padding * 2 + result.PageSize.Width * 1.5f, textPaint.TextSize / 2);
-            canvas.DrawText("EXPECTED", expectedHeaderPosition, textPaint);
-            
-            // draw pages
-            canvas.Save();
-            canvas.Translate(0, Padding);
-            
-            foreach (var pageIndex in Enumerable.Range(0, numberOfPages))
-            {
-                var actualPage = result.ActualLayout.Pages.ElementAtOrDefault(pageIndex);
-                var expectedPage = result.ExpectedLayout.Pages.ElementAtOrDefault(pageIndex);
-                
-                DrawPage(actualPage);
-                DrawLayoutDifferences(actualPage, expectedPage);
-                
-                canvas.Translate(result.PageSize.Width + Padding, 0);
-                canvas.DrawText((pageIndex + 1).ToString(), 0, textPaint.TextSize, textPaint);
-                
-                canvas.Translate(Padding, 0);
-                DrawPage(expectedPage);
-                DrawLayoutDifferences(expectedPage, actualPage);
-                
-                canvas.Translate(-result.PageSize.Width - Padding * 2, result.PageSize.Height + Padding);
-            }
-
-            canvas.Restore();
-        }
-        
-        void DrawPage(LayoutTestResult.PageLayout? pageLayout)
-        {
-            // draw page
-            using var availableAreaPaint = new SKPaint
-            {
-                Color = new SKColor(PageBackgroundColor)
-            };
-            
-            canvas.DrawRect(0, 0, result.PageSize.Width, result.PageSize.Height, availableAreaPaint);
-            
-            if (pageLayout == null)
-            {
-                DrawGridLines();
-                return;
-            }
-            
-            // draw required area
-            using var requiredAreaPaint = new SKPaint
-            {
-                Color = new SKColor(RequiredAreaBackgroundColor)
-            };
-            
-            canvas.DrawRect(0, 0, pageLayout.RequiredArea.Width, pageLayout.RequiredArea.Height, requiredAreaPaint);
-            
-            // draw mocks
-            foreach (var mock in pageLayout.Mocks)
-                DrawMock(mock);
-            
-            foreach (var mock in pageLayout.Mocks.GetOverlappingItems())
-                DrawOccludedMock(mock.Below);
-            
-            DrawGridLines();
-        }
-
-        void DrawMock(LayoutTestResult.MockLayoutPosition mock)
-        {
-            var color = mockColors[mock.MockId];
-                
-            using var mockAreaPaint = new SKPaint
-            {
-                Color = new SKColor(color)
-            };
-            
-            canvas.Save();
-            
-            canvas.Translate(mock.Position.X, mock.Position.Y);
-            canvas.DrawRect(0, 0, mock.Size.Width, mock.Size.Height, mockAreaPaint);
-
-            canvas.Restore();
-        }
-        
-        void DrawOccludedMock(LayoutTestResult.MockLayoutPosition mock)
-        {
-            var color = mockColors[mock.MockId];
-                
-            using var mockBorderPaint = new SKPaint
-            {
-                Color = new SKColor(color),
-                IsStroke = true,
-                StrokeWidth = OccludedMockBorderThickness
-            };
-
-            var borderPosition = new SKRect(0, 0, mock.Size.Width, mock.Size.Height);
-            borderPosition.Inflate(-OccludedMockBorderThickness / 2f, -OccludedMockBorderThickness / 2f);
-            
-            canvas.Save();
-            canvas.Translate(mock.Position.X, mock.Position.Y);
-            canvas.DrawRect(borderPosition, mockBorderPaint);
-            canvas.Restore();
-        }
-        
-        void DrawGridLines()
-        {
-            using var paint = new SKPaint
-            {
-                Color = SKColors.Black.WithAlpha(GridLineTransparency),
-                StrokeWidth = GridLineThickness
-            };
-
-            var verticalLineCount = (int)Math.Floor(result.PageSize.Width / GridSize);
-            var horizontalLineCount = (int)Math.Floor(result.PageSize.Height / GridSize);
-            
-            foreach (var i in Enumerable.Range(1, verticalLineCount))
-                canvas.DrawLine(new SKPoint(i * GridSize, 0), new SKPoint(i * GridSize, result.PageSize.Height), paint);
-            
-            foreach (var i in Enumerable.Range(1, horizontalLineCount))
-                canvas.DrawLine(new SKPoint(0, i * GridSize), new SKPoint(result.PageSize.Width, i * GridSize), paint);
-        }
-
-        void DrawLayoutDifferences(LayoutTestResult.PageLayout? target, LayoutTestResult.PageLayout? compareWith)
-        {
-            using var targetPath = BuildPathFromLayout(target);
-            using var compareWithPath = BuildPathFromLayout(compareWith);
-
-            using var differencePath = targetPath.Op(compareWithPath, SKPathOp.Difference);
-            
-            AnnotateInvalidAreaHelper.Annotate(canvas, differencePath);
-            
-            SKPath BuildPathFromLayout(LayoutTestResult.PageLayout? layout)
-            {
-                var resultPath = new SKPath();
-
-                if (layout == null)
-                    return resultPath;
-                
-                foreach (var mock in layout.Mocks)
-                {
-                    var position = new SKRect(
-                        mock.Position.X, 
-                        mock.Position.Y, 
-                        mock.Position.X + mock.Size.Width, 
-                        mock.Position.Y + mock.Size.Height);
-                    
-                    resultPath.AddRect(position);
-                }
-
-                return resultPath;
-            }
-        }
-    }
-}

+ 0 - 55
Source/QuestPDF.LayoutTests/TestEngine/LayoutTestResult.cs

@@ -1,55 +0,0 @@
-using QuestPDF.Infrastructure;
-
-namespace QuestPDF.LayoutTests.TestEngine;
-
-internal sealed class LayoutTestResult
-{
-    public Size PageSize { get; set; }
-    
-    public DocumentLayout ActualLayout { get; set; }
-    public DocumentLayout ExpectedLayout { get; set; }
-
-    public sealed class DocumentLayout
-    {
-        public ICollection<PageLayout> Pages { get; set; } = new List<PageLayout>();
-        public bool GeneratesInfiniteLayout { get; set; }
-    }
-    
-    public sealed class PageLayout
-    {
-        public Size RequiredArea { get; set; }
-        public ICollection<MockLayoutPosition> Mocks { get; set; }
-    }
-
-    public sealed class MockLayoutPosition
-    {
-        public string MockId { get; set; }
-        public Position Position { get; set; }
-        public Size Size { get; set; }
-    }
-}
-
-internal static class LayoutTestResultHelpers
-{
-    public static IEnumerable<(LayoutTestResult.MockLayoutPosition Below, LayoutTestResult.MockLayoutPosition Above)> GetOverlappingItems(this ICollection<LayoutTestResult.MockLayoutPosition> items)
-    {
-        for (var i = 0; i < items.Count; i++)
-        {
-            for (var j = i + 1; j < items.Count; j++)
-            {
-                var beforeChild = items.ElementAt(i);
-                var afterChild = items.ElementAt(j);
-
-                var beforeBoundingBox = BoundingBox.From(beforeChild.Position, beforeChild.Size);
-                var afterBoundingBox = BoundingBox.From(afterChild.Position, afterChild.Size);
-
-                var intersection = BoundingBoxExtensions.Intersection(beforeBoundingBox, afterBoundingBox);
-                        
-                if (intersection == null)
-                    continue;
-
-                yield return (beforeChild, afterChild);
-            }
-        }
-    }
-}

+ 0 - 88
Source/QuestPDF.LayoutTests/TestEngine/LayoutTestValidator.cs

@@ -1,88 +0,0 @@
-using QuestPDF.Infrastructure;
-
-namespace QuestPDF.LayoutTests.TestEngine;
-
-internal static class LayoutTestValidator
-{
-    public static void Validate(LayoutTestResult result)
-    {
-        if (result.ActualLayout.GeneratesInfiniteLayout && !result.ExpectedLayout.GeneratesInfiniteLayout)
-            throw new LayoutTestException("Provided content generates unexpected infinite layout");
-        
-        if (!result.ActualLayout.GeneratesInfiniteLayout && result.ExpectedLayout.GeneratesInfiniteLayout)
-            throw new LayoutTestException("Provided content is expected to generate infinite layout but it does not");
-
-        var actualPages = result.ActualLayout.Pages;
-        var expectedPages = result.ExpectedLayout.Pages;
-        
-        if (actualPages.Count != expectedPages.Count)
-            throw new LayoutTestException($"Content return layout with {actualPages.Count} pages but expected {expectedPages.Count} pages");
-        
-        foreach (var i in Enumerable.Range(0, actualPages.Count))
-        {
-            try
-            {
-                var actualPage = actualPages.ElementAt(i);
-                var expectedPage = expectedPages.ElementAt(i);
-
-                ValidatePage(actualPage, expectedPage);
-            }
-            catch (LayoutTestException exception)
-            {
-                throw new LayoutTestException($"Found issue on page number {i + 1}: {exception.Message}");
-            }
-            catch (Exception exception)
-            {
-                throw new LayoutTestException($"Encountered exception during validating page number {i + 1}", exception);
-            }
-        }
-
-        static void ValidatePage(LayoutTestResult.PageLayout actualLayout, LayoutTestResult.PageLayout expectedLayout)
-        {
-            if (Math.Abs(actualLayout.RequiredArea.Width - expectedLayout.RequiredArea.Width) > Size.Epsilon)
-                throw new LayoutTestException($"Required horizontal area is equal to {actualLayout.RequiredArea.Width} but expected {expectedLayout.RequiredArea.Width}");
-            
-            if (Math.Abs(actualLayout.RequiredArea.Height - expectedLayout.RequiredArea.Height) > Size.Epsilon)
-                throw new LayoutTestException($"Required vertical area is equal to {actualLayout.RequiredArea.Height} but expected {expectedLayout.RequiredArea.Height}");
-            
-            if (actualLayout.Mocks.Count != expectedLayout.Mocks.Count)
-                throw new LayoutTestException($"Visible {actualLayout.Mocks.Count} mocks but expected {expectedLayout.Mocks.Count}");
-
-            ValidatePositionAndSizeOfMocks(actualLayout, expectedLayout);
-            ValidateDrawingOrder(actualLayout, expectedLayout);
-        }
-
-        static void ValidatePositionAndSizeOfMocks(LayoutTestResult.PageLayout actualLayout, LayoutTestResult.PageLayout expectedLayout)
-        {
-            foreach (var expectedMock in expectedLayout.Mocks)
-            {
-                var matchingActualMock = actualLayout
-                    .Mocks
-                    .Where(x => x.MockId == expectedMock.MockId)
-                    .Where(x => Position.Equal(x.Position, expectedMock.Position))
-                    .Where(x => Size.Equal(x.Size, expectedMock.Size))
-                    .Count();
-
-                if (matchingActualMock == 0)
-                    throw new Exception($"Cannot find '{expectedMock.MockId}' mock on position {expectedMock.Position.ToString()} and size {expectedMock.Size}");
-                
-                if (matchingActualMock > 1)
-                    throw new Exception($"Found multiple '{expectedMock.MockId}' mocks on position {expectedMock.Position} and size {expectedMock.Size}");
-            }
-        }
-
-        static void ValidateDrawingOrder(LayoutTestResult.PageLayout actualLayout, LayoutTestResult.PageLayout expectedLayout)
-        {
-            var actualOverlaps = actualLayout.Mocks.GetOverlappingItems().ToList();
-            var expectedOverlaps = expectedLayout.Mocks.GetOverlappingItems().ToList();
-            
-            foreach (var expectedOverlap in expectedOverlaps)
-            {
-                var matchingActualElements = actualOverlaps.Count(actualOverlap => actualOverlap.Below.MockId == expectedOverlap.Below.MockId && actualOverlap.Above.MockId == expectedOverlap.Above.MockId);
-
-                if (matchingActualElements != 1)
-                    throw new Exception($"Mock '{expectedOverlap.Below.MockId}' should be visible below '{expectedOverlap.Above.MockId}' mock");
-            }
-        }
-    }
-}

+ 0 - 8
Source/QuestPDF.LayoutTests/TestEngine/LayoutTestVisualizationStrategy.cs

@@ -1,8 +0,0 @@
-namespace QuestPDF.LayoutTests.TestEngine;
-
-public enum LayoutTestVisualizationStrategy
-{
-    Never,
-    WhenFailure,
-    Always
-}

+ 0 - 6
Source/QuestPDF.LayoutTests/TestEngine/Settings.cs

@@ -1,6 +0,0 @@
-namespace QuestPDF.LayoutTests.TestEngine;
-
-public static class Settings
-{
-    public static LayoutTestVisualizationStrategy LayoutTestVisualizationStrategy { get; set; } = LayoutTestVisualizationStrategy.WhenFailure;
-}

+ 0 - 18
Source/QuestPDF.LayoutTests/TestEngine/WrapChild.cs

@@ -1,18 +0,0 @@
-using QuestPDF.Drawing;
-using QuestPDF.Helpers;
-using QuestPDF.Infrastructure;
-
-namespace QuestPDF.LayoutTests.TestEngine;
-
-internal class WrapChild : Element
-{
-    internal override SpacePlan Measure(Size availableSpace)
-    {
-        return SpacePlan.Wrap("This element is used only for testing purposes and does not fit on any space by design.");
-    }
-
-    internal override void Draw(Size availableSpace)
-    {
-        
-    }
-}

+ 5 - 0
Source/QuestPDF/Drawing/DocumentGenerator.cs

@@ -63,6 +63,11 @@ namespace QuestPDF.Drawing
 
             return canvas.Images;
         }
+        
+        internal static void GenerateAndDiscard(IDocument document)
+        {
+            RenderDocument(new FreeDocumentCanvas(), document, DocumentSettings.Default);
+        }
 
         internal static void ValidateLicense()
         {

+ 5 - 0
Source/QuestPDF/Fluent/GenerateExtensions.cs

@@ -16,6 +16,11 @@ namespace QuestPDF.Fluent
             ClearGenerateAndShowFiles();
         }
         
+        internal static void GenerateAndDiscard(this IDocument document)
+        {
+            DocumentGenerator.GenerateAndDiscard(document);
+        }
+        
         #region Genearate And Show Configuration
 
         private static readonly Random Random = new();