diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..60edcfb --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,15 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/contentModel.xml +/projectSettingsUpdater.xml +/modules.xml +/.idea.resPlan.NET.iml +# Ignored default folder with query files +/queries/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/.idea.ResPlan/.idea/.gitignore b/.idea/.idea.ResPlan/.idea/.gitignore new file mode 100644 index 0000000..b14c902 --- /dev/null +++ b/.idea/.idea.ResPlan/.idea/.gitignore @@ -0,0 +1,15 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/projectSettingsUpdater.xml +/.idea.ResPlan.iml +/contentModel.xml +/modules.xml +# Ignored default folder with query files +/queries/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/ResPlan.Tests/GeometryVerificationTests.cs b/ResPlan.Tests/GeometryVerificationTests.cs new file mode 100644 index 0000000..08bced1 --- /dev/null +++ b/ResPlan.Tests/GeometryVerificationTests.cs @@ -0,0 +1,63 @@ +using System.Linq; +using NetTopologySuite.Geometries; +using Xunit; +using ResPlan.Library; + +namespace ResPlan.Tests +{ + public class GeometryVerificationTests + { + [Fact] + public void VerifyAllGeometriesAreValidAndClosed() + { + string jsonPath = "resplan_samples.json"; + Assert.True(System.IO.File.Exists(jsonPath), $"Test data {jsonPath} not found."); + + var plans = PlanLoader.LoadPlans(jsonPath); + Assert.NotEmpty(plans); + + foreach (var plan in plans) + { + foreach (var kvp in plan.Geometries) + { + var layerName = kvp.Key; + var geometries = kvp.Value; + + foreach (var geometry in geometries) + { + // 1. Check validity + Assert.True(geometry.IsValid, $"Invalid geometry found in plan {plan.Id}, layer {layerName}. Reason: {geometry.IsValid}"); + + // 2. Check for closed rings if it's a polygon or multipolygon + if (geometry is Polygon polygon) + { + ValidatePolygon(polygon, plan.Id, layerName); + } + else if (geometry is MultiPolygon multiPolygon) + { + foreach (var g in multiPolygon.Geometries) + { + if (g is Polygon p) + ValidatePolygon(p, plan.Id, layerName); + } + } + + // 3. Ensure non-empty + Assert.False(geometry.IsEmpty, $"Empty geometry found in plan {plan.Id}, layer {layerName}"); + } + } + } + } + + private void ValidatePolygon(Polygon polygon, int planId, string layerName) + { + // shell + Assert.True(polygon.ExteriorRing.IsClosed, $"Polygon exterior ring not closed in plan {planId}, layer {layerName}"); + // holes + foreach (var hole in polygon.InteriorRings) + { + Assert.True(hole.IsClosed, $"Polygon interior ring not closed in plan {planId}, layer {layerName}"); + } + } + } +} diff --git a/ResPlan.Tests/ImageComparisonTests.cs b/ResPlan.Tests/ImageComparisonTests.cs index b0718e2..65613cb 100644 --- a/ResPlan.Tests/ImageComparisonTests.cs +++ b/ResPlan.Tests/ImageComparisonTests.cs @@ -3,12 +3,20 @@ using System.Linq; using SkiaSharp; using Xunit; +using Xunit.Abstractions; using ResPlan.Library; namespace ResPlan.Tests { public class ImageComparisonTests { + private readonly ITestOutputHelper _output; + + public ImageComparisonTests(ITestOutputHelper output) + { + _output = output; + } + [Fact] public void VerifyImageGenerationAgainstReference() { @@ -27,11 +35,11 @@ public void VerifyImageGenerationAgainstReference() Assert.True(File.Exists(refPath), $"Reference image {refPath} not found."); // Compare - CompareImages(refPath, actualPath); + CompareImages(refPath, actualPath, plan.Id); } } - private void CompareImages(string refPath, string actualPath) + private void CompareImages(string refPath, string actualPath, int planId) { using var refImg = SKBitmap.Decode(refPath); using var actImg = SKBitmap.Decode(actualPath); @@ -62,6 +70,8 @@ private void CompareImages(string refPath, string actualPath) double mse = diffSum / (double)(pixelCount * 3); double rmse = Math.Sqrt(mse); + _output.WriteLine($"Plan {planId} Comparison: RMSE = {rmse:F2}"); + // Define a tolerance. 0 is perfect. // Given font rendering, anti-aliasing differences, etc. // 255 is max difference. @@ -86,7 +96,7 @@ private void CompareImages(string refPath, string actualPath) // So axes are off. Background should be white. // Let's assert RMSE < 20. - Assert.True(rmse < 50, $"Image mismatch for {refPath}. RMSE: {rmse}"); + Assert.True(rmse < 20, $"Image mismatch for {refPath}. RMSE: {rmse}"); } private long Sq(int x) => x * x;