fix(core): make a plate's removed-clamp set immutable

Plate.RemovedClamps exposed a HashSet (and every untouched plate shared one
static empty HashSet), so a downcast could change one plate's removal state
or every plate's at once without re-resolving zones. It is now an
ImmutableHashSet.

PM 6ded8ac4-84fd-4e8b-9a3b-6f41e494542c item (e)
This commit is contained in:
aj committed 2026-10-13 18:15:50 -04:00
1 parent 7ed40d4bb8
commit 6b995ac40a
2 files changed
+16 -2

No files matched your search

+3 -2
View File
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using OpenNest.Collections;
using OpenNest.Geometry;
@@ -141,7 +142,7 @@ namespace OpenNest
/// </summary>
public ClampRail ClampRail { get; private set; }
private static readonly IReadOnlySet<string> NoRemovedClamps = new HashSet<string>();
private static readonly IReadOnlySet<string> NoRemovedClamps = ImmutableHashSet<string>.Empty;
/// <summary>
/// Rail clamp ids taken off this sheet as a last resort (D8) so a part that could not
@@ -183,7 +184,7 @@ namespace OpenNest
ClampRail = rail;
RemovedClamps = rail == null || removedClamps == null
? NoRemovedClamps
: new HashSet<string>(removedClamps, StringComparer.Ordinal);
: removedClamps.ToImmutableHashSet(StringComparer.Ordinal);
ReplaceClampZones(ResolveRailZones());
}
+13
View File
@@ -382,4 +382,17 @@ public class ClampRailTests
Assert.Equal(ClampRail.DefaultRemovalMinutes, plate.ClampRemovalMinutes);
Assert.Contains("clamp-8", plate.RemovedClamps); // still listed (C13)
}
// PM 6ded8ac4 (e): the removed set is read-only, even through a downcast, and an
// untouched plate does not share a mutable empty set with every other plate.
[Fact]
public void RemovedClampsCannotBeMutatedThroughTheCollection()
{
var removed = Sheet(48, 120);
removed.SetClampRail(Machine(), new[] { "clamp-3" });
var untouched = Sheet(48, 120);
Assert.True(((ICollection<string>)removed.RemovedClamps).IsReadOnly);
Assert.True(((ICollection<string>)untouched.RemovedClamps).IsReadOnly);
}
}