LayoutTestValidator.cs 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. using QuestPDF.Infrastructure;
  2. namespace QuestPDF.LayoutTests.TestEngine;
  3. internal class LayoutTestValidator
  4. {
  5. public static void Validate(LayoutTestResult result)
  6. {
  7. if (result.ActualLayout.Count != result.ExpectedLayout.Count)
  8. throw new LayoutTestException($"Content return layout with {result.ActualLayout.Count} pages but expected {result.ExpectedLayout.Count} pages");
  9. var numberOfPages = result.ActualLayout.Count;
  10. foreach (var i in Enumerable.Range(0, numberOfPages))
  11. {
  12. try
  13. {
  14. var actualPage = result.ActualLayout.ElementAt(i);
  15. var expectedPage = result.ActualLayout.ElementAt(i);
  16. ValidatePage(actualPage, expectedPage);
  17. }
  18. catch (LayoutTestException exception)
  19. {
  20. throw new LayoutTestException($"Found issue on page number {i + 1}: {exception.Message}");
  21. }
  22. catch (Exception exception)
  23. {
  24. throw new LayoutTestException($"Encountered exception during validating page number {i + 1}", exception);
  25. }
  26. }
  27. static void ValidatePage(LayoutTestResult.PageLayoutSnapshot actualLayout, LayoutTestResult.PageLayoutSnapshot expectedLayout)
  28. {
  29. if (Math.Abs(actualLayout.RequiredArea.Width - expectedLayout.RequiredArea.Width) > Size.Epsilon)
  30. throw new LayoutTestException($"Taken horizontal area is equal to {actualLayout.RequiredArea.Width} but expected {expectedLayout.RequiredArea.Width}");
  31. if (Math.Abs(actualLayout.RequiredArea.Height - expectedLayout.RequiredArea.Height) > Size.Epsilon)
  32. throw new LayoutTestException($"Taken vertical area is equal to {actualLayout.RequiredArea.Height} but expected {expectedLayout.RequiredArea.Height}");
  33. if (actualLayout.MockPositions.Count != expectedLayout.MockPositions.Count)
  34. throw new LayoutTestException($"Visible {actualLayout.MockPositions.Count} mocks but expected {expectedLayout.MockPositions.Count}");
  35. ValidatePositionAndSizeOfMocks(actualLayout, expectedLayout);
  36. ValidateDrawingOrder(actualLayout, expectedLayout);
  37. }
  38. static void ValidatePositionAndSizeOfMocks(LayoutTestResult.PageLayoutSnapshot actualLayout, LayoutTestResult.PageLayoutSnapshot expectedLayout)
  39. {
  40. foreach (var expectedMock in expectedLayout.MockPositions)
  41. {
  42. var matchingActualMock = actualLayout
  43. .MockPositions
  44. .Where(x => x.MockId == expectedMock.MockId)
  45. .Where(x => Position.Equal(x.Position, expectedMock.Position))
  46. .Where(x => Size.Equal(x.Size, expectedMock.Size))
  47. .Count();
  48. if (matchingActualMock == 0)
  49. throw new Exception($"Cannot find '{expectedMock.MockId}' mock on position {expectedMock.Position} and size {expectedMock.Size}");
  50. if (matchingActualMock > 1)
  51. throw new Exception($"Found multiple '{expectedMock.MockId}' mocks on position {expectedMock.Position} and size {expectedMock.Size}");
  52. }
  53. }
  54. static void ValidateDrawingOrder(LayoutTestResult.PageLayoutSnapshot actualLayout, LayoutTestResult.PageLayoutSnapshot expectedLayout)
  55. {
  56. var actualOverlaps = GetOverlappingItems(actualLayout.MockPositions).ToList();
  57. var expectedOverlaps = GetOverlappingItems(expectedLayout.MockPositions).ToList();
  58. foreach (var expectedOverlap in expectedOverlaps)
  59. {
  60. var matchingActualElements = actualOverlaps.Count(actualOverlap => actualOverlap == expectedOverlap);
  61. if (matchingActualElements != 1)
  62. throw new Exception($"Mock '{expectedOverlap.belowMockId}' should be visible below '{expectedOverlap.aboveMockId}' mock");
  63. }
  64. IEnumerable<(string belowMockId, string aboveMockId)> GetOverlappingItems(ICollection<LayoutTestResult.MockLayoutPosition> items)
  65. {
  66. for (var i = 0; i < items.Count; i++)
  67. {
  68. for (var j = i; j < items.Count; j++)
  69. {
  70. var beforeChild = items.ElementAt(i);
  71. var afterChild = items.ElementAt(j);
  72. var beforeBoundingBox = BoundingBox.From(beforeChild.Position, beforeChild.Size);
  73. var afterBoundingBox = BoundingBox.From(afterChild.Position, afterChild.Size);
  74. var intersection = BoundingBoxExtensions.Intersection(beforeBoundingBox, afterBoundingBox);
  75. if (intersection == null)
  76. continue;
  77. yield return (beforeChild.MockId, afterChild.MockId);
  78. }
  79. }
  80. }
  81. }
  82. }
  83. }