fix: enforce sentinel reactively in OnPlateAdded/OnPlateRemoved

Without this, RemoveEmptyPlates would destroy the sentinel with no
recovery, and tail-plate subscriptions would go stale after plate list
mutations. Added tests for both scenarios.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-06 00:06:35 -04:00
parent 51eea6d1e6
commit ede06b1bf6
2 changed files with 50 additions and 1 deletions

View File

@@ -198,6 +198,9 @@ namespace OpenNest
private void OnPlateAdded(object sender, ItemAddedEventArgs<Plate> e)
{
if (!suppressNavigation && !batching)
EnsureSentinel();
PlateListChanged?.Invoke(this, EventArgs.Empty);
if (!suppressNavigation)
@@ -212,6 +215,9 @@ namespace OpenNest
if (CurrentIndex >= Count && Count > 0)
CurrentIndex = Count - 1;
if (!suppressNavigation && !batching)
EnsureSentinel();
PlateListChanged?.Invoke(this, EventArgs.Empty);
if (!suppressNavigation)

View File

@@ -1,4 +1,5 @@
using OpenNest.CNC;
using OpenNest.Collections;
using OpenNest.Geometry;
namespace OpenNest.Tests;
@@ -373,7 +374,10 @@ public class PlateManagerTests
mgr.RemoveCurrent();
Assert.Equal(1, nest.Plates.Count);
// plate1 + sentinel (created by reactive sentinel enforcement)
Assert.Equal(2, nest.Plates.Count);
Assert.Same(plate1, nest.Plates[0]);
Assert.Equal(0, nest.Plates[^1].Parts.Count);
Assert.Equal(0, mgr.CurrentIndex);
}
@@ -394,4 +398,43 @@ public class PlateManagerTests
mgr.LoadFirst();
Assert.True(mgr.CanRemoveCurrent);
}
[Fact]
public void RemoveEmptyPlates_SentinelIsRestored()
{
var nest = CreateNest();
var plate = nest.CreatePlate();
plate.Parts.Add(MakePart());
using var mgr = new PlateManager(nest);
mgr.EnsureSentinel();
Assert.Equal(2, nest.Plates.Count);
// RemoveEmptyPlates removes the sentinel
nest.Plates.RemoveEmptyPlates();
// Sentinel should be restored reactively by OnPlateRemoved
Assert.Equal(2, nest.Plates.Count);
Assert.Equal(0, nest.Plates[^1].Parts.Count);
}
[Fact]
public void PlateRemoval_RefreshesTailSubscriptions()
{
var nest = CreateNest();
var plate1 = nest.CreatePlate();
plate1.Parts.Add(MakePart());
var plate2 = nest.CreatePlate();
plate2.Parts.Add(MakePart());
using var mgr = new PlateManager(nest);
mgr.EnsureSentinel();
// plates: [plate1(parts), plate2(parts), sentinel(empty)]
// Remove plate2 — tail subscriptions should refresh
nest.Plates.Remove(plate2);
// plates: [plate1(parts), sentinel(empty)]
// Verify reactive subscriptions still work on the new tail
nest.Plates[^1].Parts.Add(MakePart());
Assert.Equal(0, nest.Plates[^1].Parts.Count);
}
}