Browse Source

LayoutTestValidator refactoring

Marcin Ziąbek 2 years ago
parent
commit
fcec56db08

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

@@ -12,8 +12,6 @@ internal class BoundingBox
     public double Width => MaxX - MinX;
     public double Height => MaxY - MinY;
 
-    public override string ToString() => $"BBox(Min: {MinX:N0}x{MinY:N0}, Max: {MaxX:N0}x{MaxY:N0}, W: {Width:N0}, H: {Height:N0})";
-
     public static BoundingBox From(Position position, Size size)
     {
         return new BoundingBox

+ 2 - 77
Source/QuestPDF.LayoutTests/TestEngine/LayoutTest.cs

@@ -136,7 +136,7 @@ internal class LayoutTest
         var container = new Container();
         container.Element(handler);
 
-        TestResult.GeneratedLayout = LayoutTestExecutor.Execute(TestResult.PageSize, container);
+        TestResult.ActualLayout = LayoutTestExecutor.Execute(TestResult.PageSize, container);
         
         return this;
     }
@@ -171,81 +171,6 @@ internal class LayoutTest
 
     public void Validate()
     {
-        if (TestResult.GeneratedLayout.Count != TestResult.ExpectedLayout.Count)
-            throw new Exception($"Generated {TestResult.GeneratedLayout.Count} but expected {TestResult.ExpectedLayout.Count} pages.");
-
-        var numberOfPages = TestResult.GeneratedLayout.Count;
-        
-        foreach (var i in Enumerable.Range(0, numberOfPages))
-        {
-            try
-            {
-                var actual = TestResult.GeneratedLayout.ElementAt(i);
-                var expected = TestResult.ExpectedLayout.ElementAt(i);
-
-                if (Math.Abs(actual.RequiredArea.Width - expected.RequiredArea.Width) > Size.Epsilon)
-                    throw new Exception($"Taken area width is equal to {actual.RequiredArea.Width} but expected {expected.RequiredArea.Width}");
-                
-                if (Math.Abs(actual.RequiredArea.Height - expected.RequiredArea.Height) > Size.Epsilon)
-                    throw new Exception($"Taken area height is equal to {actual.RequiredArea.Height} but expected {expected.RequiredArea.Height}");
-                
-                if (actual.MockPositions.Count != expected.MockPositions.Count)
-                    throw new Exception($"Visible {actual.MockPositions.Count} but expected {expected.MockPositions.Count}");
-
-                foreach (var child in expected.MockPositions)
-                {
-                    var matchingActualElements = actual
-                        .MockPositions
-                        .Where(x => x.MockId == child.MockId)
-                        .Where(x => Position.Equal(x.Position, child.Position))
-                        .Where(x => Size.Equal(x.Size, child.Size))
-                        .Count();
-
-                    if (matchingActualElements == 0)
-                        throw new Exception($"Cannot find actual drawing command for child {child.MockId} on position {child.Position} and size {child.Size}");
-                    
-                    if (matchingActualElements > 1)
-                        throw new Exception($"Found multiple drawing commands for child {child.MockId} on position {child.Position} and size {child.Size}");
-                }
-                
-                // todo: add z-depth testing
-                var actualOverlaps = GetOverlappingItems(actual.MockPositions);
-                var expectedOverlaps = GetOverlappingItems(expected.MockPositions);
-                
-                foreach (var overlap in expectedOverlaps)
-                {
-                    var matchingActualElements = actualOverlaps.Count(x => x.Item1 == overlap.Item1 && x.Item2 == overlap.Item2);
-
-                    if (matchingActualElements != 1)
-                        throw new Exception($"Element {overlap.Item1} should be visible underneath element {overlap.Item2}");
-                }
-                
-                IEnumerable<(string, string)> GetOverlappingItems(ICollection<LayoutTestResult.MockLayoutPosition> items)
-                {
-                    for (var i = 0; i < items.Count; i++)
-                    {
-                        for (var j = i; 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.MockId, afterChild.MockId);
-                        }
-                    }
-                }
-            }
-            catch (Exception e)
-            {
-                throw new Exception($"Error on page {i + 1}: {e.Message}");
-            }
-        }
+        LayoutTestValidator.Validate(TestResult);
     }
 }

+ 4 - 4
Source/QuestPDF.LayoutTests/TestEngine/LayoutTestOutputVisualization.cs

@@ -38,7 +38,7 @@ internal class LayoutTestResultVisualization
     public static void Visualize(LayoutTestResult result, Stream stream)
     {
         // determine output dimenstions
-        var numberOfPages = Math.Max(result.GeneratedLayout.Count, result.ExpectedLayout.Count);
+        var numberOfPages = Math.Max(result.ActualLayout.Count, result.ExpectedLayout.Count);
 
         var canvasWidth = result.PageSize.Width * 2 + Padding * 4;
         var canvasHeight = result.PageSize.Height * numberOfPages + Padding * (numberOfPages + 2);
@@ -54,13 +54,13 @@ internal class LayoutTestResultVisualization
         var mockColors = AssignColorsToMocks();
         
         canvas.Translate(Padding, Padding);
-        DrawLayout("GENERATED", result.GeneratedLayout);
+        DrawLayout("ACTUAL", result.ActualLayout);
         
         canvas.Translate(result.PageSize.Width + Padding, 0);
         DrawPageNumbers();
         
         canvas.Translate(Padding, 0);
-        DrawLayout("EXPECTED", result.GeneratedLayout);
+        DrawLayout("EXPECTED", result.ActualLayout);
 
         // finish generation
         pdf.EndPage();
@@ -69,7 +69,7 @@ internal class LayoutTestResultVisualization
         IDictionary<string, string> AssignColorsToMocks()
         {
             var mocks = Enumerable
-                .Concat(result.GeneratedLayout, result.ExpectedLayout)
+                .Concat(result.ActualLayout, result.ExpectedLayout)
                 .SelectMany(x => x.MockPositions)
                 .Select(x => x.MockId)
                 .Distinct()

+ 1 - 1
Source/QuestPDF.LayoutTests/TestEngine/LayoutTestResult.cs

@@ -6,7 +6,7 @@ internal class LayoutTestResult
 {
     public Size PageSize { get; set; }
     
-    public ICollection<PageLayoutSnapshot> GeneratedLayout { get; set; }
+    public ICollection<PageLayoutSnapshot> ActualLayout { get; set; }
     public ICollection<PageLayoutSnapshot> ExpectedLayout { get; set; }
 
     public class PageLayoutSnapshot

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

@@ -0,0 +1,103 @@
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.LayoutTests.TestEngine;
+
+internal class LayoutTestValidator
+{
+    public static void Validate(LayoutTestResult result)
+    {
+        if (result.ActualLayout.Count != result.ExpectedLayout.Count)
+            throw new LayoutTestException($"Content return layout with {result.ActualLayout.Count} pages but expected {result.ExpectedLayout.Count} pages");
+
+        var numberOfPages = result.ActualLayout.Count;
+        
+        foreach (var i in Enumerable.Range(0, numberOfPages))
+        {
+            try
+            {
+                var actualPage = result.ActualLayout.ElementAt(i);
+                var expectedPage = result.ActualLayout.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.PageLayoutSnapshot actualLayout, LayoutTestResult.PageLayoutSnapshot expectedLayout)
+        {
+            if (Math.Abs(actualLayout.RequiredArea.Width - expectedLayout.RequiredArea.Width) > Size.Epsilon)
+                throw new LayoutTestException($"Taken 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($"Taken vertical area is equal to {actualLayout.RequiredArea.Height} but expected {expectedLayout.RequiredArea.Height}");
+            
+            if (actualLayout.MockPositions.Count != expectedLayout.MockPositions.Count)
+                throw new LayoutTestException($"Visible {actualLayout.MockPositions.Count} mocks but expected {expectedLayout.MockPositions.Count}");
+
+            ValidatePositionAndSizeOfMocks(actualLayout, expectedLayout);
+            ValidateDrawingOrder(actualLayout, expectedLayout);
+        }
+
+        static void ValidatePositionAndSizeOfMocks(LayoutTestResult.PageLayoutSnapshot actualLayout, LayoutTestResult.PageLayoutSnapshot expectedLayout)
+        {
+            foreach (var expectedMock in expectedLayout.MockPositions)
+            {
+                var matchingActualMock = actualLayout
+                    .MockPositions
+                    .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} 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.PageLayoutSnapshot actualLayout, LayoutTestResult.PageLayoutSnapshot expectedLayout)
+        {
+            var actualOverlaps = GetOverlappingItems(actualLayout.MockPositions).ToList();
+            var expectedOverlaps = GetOverlappingItems(expectedLayout.MockPositions).ToList();
+            
+            foreach (var expectedOverlap in expectedOverlaps)
+            {
+                var matchingActualElements = actualOverlaps.Count(actualOverlap => actualOverlap == expectedOverlap);
+
+                if (matchingActualElements != 1)
+                    throw new Exception($"Mock '{expectedOverlap.belowMockId}' should be visible below '{expectedOverlap.aboveMockId}' mock");
+            }
+            
+            IEnumerable<(string belowMockId, string aboveMockId)> GetOverlappingItems(ICollection<LayoutTestResult.MockLayoutPosition> items)
+            {
+                for (var i = 0; i < items.Count; i++)
+                {
+                    for (var j = i; 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.MockId, afterChild.MockId);
+                    }
+                }
+            }
+        }
+    }
+}