Files
OpenNest/OpenNest.Tests/PlateDisplayNumberingTests.cs
T
aj b8f319d3e8 fix(ui): exclude the new-plate sentinel from display counts
The editor header and the Plan Cutting dialog counted the trailing empty
new-plate workspace PlateManager.EnsureSentinel maintains, so a one-plate
nest read 'Plate 1 of 2'. PlateDisplayNumbering now derives display-only
numbers that exclude only a trailing empty sentinel; interior empty plates
keep their slot and number, and navigation, storage indexes, exported
names and batch selection are untouched. The sentinel itself is labeled
'New plate (empty)' instead of being numbered beyond the shown total.
2026-10-06 20:58:32 -04:00

139 lines
4.8 KiB
C#

using OpenNest.CNC;
using OpenNest.Geometry;
namespace OpenNest.Tests;
public class PlateDisplayNumberingTests
{
private static Nest CreateNest() => new("test");
private static Part MakePart()
{
var pgm = new Program();
pgm.Codes.Add(new RapidMove(new Vector(0, 0)));
pgm.Codes.Add(new LinearMove(new Vector(10, 0)));
pgm.Codes.Add(new LinearMove(new Vector(10, 10)));
pgm.Codes.Add(new LinearMove(new Vector(0, 0)));
return new Part(new Drawing("test", pgm));
}
[Fact]
public void PopulatedThenSentinel_TotalIsOneAndRealPlateDisplaysAsOne()
{
var nest = CreateNest();
var real = nest.CreatePlate();
real.Parts.Add(MakePart());
nest.CreatePlate(); // The trailing empty sentinel.
Assert.Equal(1, PlateDisplayNumbering.DisplayedPlateCount(nest.Plates));
Assert.Equal(1, PlateDisplayNumbering.DisplayedPlateNumber(nest.Plates, 0));
Assert.Equal("Plate 1 of 1", PlateDisplayNumbering.FormatHeader(nest.Plates, 0, null));
}
[Fact]
public void Sentinel_IsLabeledAsNewPlateNotNumbered()
{
var nest = CreateNest();
var real = nest.CreatePlate();
real.Parts.Add(MakePart());
nest.CreatePlate();
var sentinel = nest.Plates.Count - 1;
Assert.True(PlateDisplayNumbering.IsTrailingSentinel(nest.Plates, sentinel));
Assert.Null(PlateDisplayNumbering.DisplayedPlateNumber(nest.Plates, sentinel));
Assert.Equal("New plate (empty)", PlateDisplayNumbering.FormatHeader(nest.Plates, sentinel, null));
}
[Fact]
public void TwoRealPlatesThenSentinel_TotalIsTwo()
{
var nest = CreateNest();
nest.Plates.Add(WithPart());
nest.Plates.Add(WithPart());
nest.CreatePlate(); // sentinel
Assert.Equal(2, PlateDisplayNumbering.DisplayedPlateCount(nest.Plates));
Assert.Equal(1, PlateDisplayNumbering.DisplayedPlateNumber(nest.Plates, 0));
Assert.Equal(2, PlateDisplayNumbering.DisplayedPlateNumber(nest.Plates, 1));
Assert.Null(PlateDisplayNumbering.DisplayedPlateNumber(nest.Plates, 2));
}
[Fact]
public void NoPlates_TotalIsZeroAndHeaderSaysNoPlates()
{
var nest = CreateNest();
Assert.Equal(0, PlateDisplayNumbering.DisplayedPlateCount(nest.Plates));
Assert.Null(PlateDisplayNumbering.DisplayedPlateNumber(nest.Plates, 0));
Assert.Equal("No plates", PlateDisplayNumbering.FormatHeader(nest.Plates, 0, null));
}
[Fact]
public void SingleEmptyPlate_IsTheNewPlateView_NotNumbered()
{
var nest = CreateNest();
nest.CreatePlate(); // only the sentinel
Assert.Equal(0, PlateDisplayNumbering.DisplayedPlateCount(nest.Plates));
Assert.True(PlateDisplayNumbering.IsTrailingSentinel(nest.Plates, 0));
Assert.Equal("New plate (empty)", PlateDisplayNumbering.FormatHeader(nest.Plates, 0, null));
}
[Fact]
public void InteriorEmptyPlate_KeepsItsSlotAndNumber()
{
var nest = CreateNest();
nest.Plates.Add(WithPart());
nest.CreatePlate(); // An empty interior plate: EnsureSentinel only trims at the tail.
nest.Plates.Add(WithPart());
nest.CreatePlate(); // sentinel
Assert.Equal(3, PlateDisplayNumbering.DisplayedPlateCount(nest.Plates));
Assert.Equal(2, PlateDisplayNumbering.DisplayedPlateNumber(nest.Plates, 1));
Assert.Equal(3, PlateDisplayNumbering.DisplayedPlateNumber(nest.Plates, 2));
Assert.False(PlateDisplayNumbering.IsTrailingSentinel(nest.Plates, 1));
}
[Fact]
public void LastPlateWithParts_IsNotASentinel()
{
var nest = CreateNest();
nest.Plates.Add(WithPart());
Assert.Equal(1, PlateDisplayNumbering.DisplayedPlateCount(nest.Plates));
Assert.False(PlateDisplayNumbering.IsTrailingSentinel(nest.Plates, 0));
}
[Fact]
public void IndexOutsideCollection_HasNoNumberAndIsNotASentinel()
{
var nest = CreateNest();
nest.Plates.Add(WithPart());
Assert.Null(PlateDisplayNumbering.DisplayedPlateNumber(nest.Plates, 5));
Assert.Null(PlateDisplayNumbering.DisplayedPlateNumber(nest.Plates, -1));
Assert.False(PlateDisplayNumbering.IsTrailingSentinel(nest.Plates, 5));
}
[Fact]
public void Helper_DoesNotMutateTheNest()
{
var nest = CreateNest();
nest.Plates.Add(WithPart());
nest.CreatePlate();
var before = nest.Plates.Count;
PlateDisplayNumbering.DisplayedPlateCount(nest.Plates);
PlateDisplayNumbering.FormatHeader(nest.Plates, 1, "100 x 100");
Assert.Equal(before, nest.Plates.Count);
}
private static Plate WithPart()
{
var plate = new Plate();
plate.Parts.Add(MakePart());
return plate;
}
}