diff --git a/OpenNest.Tests/Strategies/FillPipelineTests.cs b/OpenNest.Tests/Strategies/FillPipelineTests.cs new file mode 100644 index 0000000..69c9ffc --- /dev/null +++ b/OpenNest.Tests/Strategies/FillPipelineTests.cs @@ -0,0 +1,62 @@ +using OpenNest.Geometry; + +namespace OpenNest.Tests.Strategies; + +public class FillPipelineTests +{ + private static Drawing MakeRectDrawing(double w, double h, string name = "rect") + { + var pgm = new OpenNest.CNC.Program(); + pgm.Codes.Add(new OpenNest.CNC.RapidMove(new Vector(0, 0))); + pgm.Codes.Add(new OpenNest.CNC.LinearMove(new Vector(w, 0))); + pgm.Codes.Add(new OpenNest.CNC.LinearMove(new Vector(w, h))); + pgm.Codes.Add(new OpenNest.CNC.LinearMove(new Vector(0, h))); + pgm.Codes.Add(new OpenNest.CNC.LinearMove(new Vector(0, 0))); + return new Drawing(name, pgm); + } + + [Fact] + public void Pipeline_PopulatesPhaseResults() + { + var plate = new Plate(120, 60); + var engine = new DefaultNestEngine(plate); + var item = new NestItem { Drawing = MakeRectDrawing(20, 10) }; + + engine.Fill(item, plate.WorkArea(), null, System.Threading.CancellationToken.None); + + Assert.True(engine.PhaseResults.Count >= 4, + $"Expected phase results from all strategies, got {engine.PhaseResults.Count}"); + } + + [Fact] + public void Pipeline_SetsWinnerPhase() + { + var plate = new Plate(120, 60); + var engine = new DefaultNestEngine(plate); + var item = new NestItem { Drawing = MakeRectDrawing(20, 10) }; + + var parts = engine.Fill(item, plate.WorkArea(), null, System.Threading.CancellationToken.None); + + Assert.True(parts.Count > 0); + Assert.True(engine.WinnerPhase == NestPhase.Pairs || + engine.WinnerPhase == NestPhase.Linear || + engine.WinnerPhase == NestPhase.RectBestFit || + engine.WinnerPhase == NestPhase.Extents); + } + + [Fact] + public void Pipeline_RespectsCancellation() + { + var plate = new Plate(120, 60); + var engine = new DefaultNestEngine(plate); + var item = new NestItem { Drawing = MakeRectDrawing(20, 10) }; + var cts = new System.Threading.CancellationTokenSource(); + cts.Cancel(); + + // Pre-cancelled token should return empty or partial results without throwing + var parts = engine.Fill(item, plate.WorkArea(), null, cts.Token); + + // Should not throw — graceful degradation + Assert.NotNull(parts); + } +} diff --git a/OpenNest.Tests/Strategies/FillStrategyRegistryTests.cs b/OpenNest.Tests/Strategies/FillStrategyRegistryTests.cs new file mode 100644 index 0000000..497b533 --- /dev/null +++ b/OpenNest.Tests/Strategies/FillStrategyRegistryTests.cs @@ -0,0 +1,35 @@ +namespace OpenNest.Tests.Strategies; + +public class FillStrategyRegistryTests +{ + [Fact] + public void Registry_DiscoversBuiltInStrategies() + { + var strategies = FillStrategyRegistry.Strategies; + + Assert.True(strategies.Count >= 4, $"Expected at least 4 built-in strategies, got {strategies.Count}"); + Assert.Contains(strategies, s => s.Name == "Pairs"); + Assert.Contains(strategies, s => s.Name == "RectBestFit"); + Assert.Contains(strategies, s => s.Name == "Extents"); + Assert.Contains(strategies, s => s.Name == "Linear"); + } + + [Fact] + public void Registry_StrategiesAreOrderedByOrder() + { + var strategies = FillStrategyRegistry.Strategies; + + for (var i = 1; i < strategies.Count; i++) + Assert.True(strategies[i].Order >= strategies[i - 1].Order, + $"Strategy '{strategies[i].Name}' (Order={strategies[i].Order}) should not precede '{strategies[i - 1].Name}' (Order={strategies[i - 1].Order})"); + } + + [Fact] + public void Registry_LinearIsLast() + { + var strategies = FillStrategyRegistry.Strategies; + var last = strategies[strategies.Count - 1]; + + Assert.Equal("Linear", last.Name); + } +}