Files
OpenNest/OpenNest.Tests/Diagnostics/OverlapReportStateTests.cs
T

229 lines
8.9 KiB
C#

using OpenNest.CNC;
using OpenNest.Diagnostics;
using OpenNest.Geometry;
namespace OpenNest.Tests.Diagnostics;
public class OverlapReportStateTests
{
[Fact]
public void RestartRejectsOldSuccessAndFailureWithoutDisturbingNewRequest()
{
var plate = PlateWithParts();
var state = new OverlapReportState();
var old = state.Begin(plate);
var current = state.Begin(plate);
Assert.False(state.TryPublish(old, plate, Analyze(plate)));
Assert.False(state.TryFail(old, plate));
Assert.Equal(OverlapCheckStatus.Checking, state.Status);
Assert.True(state.TryPublish(current, plate, Analyze(plate)));
Assert.Equal(OverlapCheckStatus.Current, state.Status);
Assert.Equal("Overlaps: 1 pairs", state.Message);
}
[Theory]
[InlineData("move-x")]
[InlineData("move-y")]
[InlineData("rotate")]
[InlineData("reorder")]
[InlineData("replace-part")]
[InlineData("replace-drawing")]
[InlineData("placed-program")]
[InlineData("clean-program")]
[InlineData("add")]
[InlineData("remove")]
[InlineData("plate")]
public void ExactOrderedStampRejectsEditsBeforePublishAndAfterCompletion(string edit)
{
foreach (var publishFirst in new[] { false, true })
{
var plate = PlateWithParts();
var state = new OverlapReportState();
var request = state.Begin(plate);
var report = Analyze(plate);
if (publishFirst)
Assert.True(state.TryPublish(request, plate, report));
var part = plate.Parts[0];
switch (edit)
{
case "move-x": part.Offset(1e-10, 0); break;
case "move-y": part.Offset(0, 1e-10); break;
case "rotate": part.Rotate(0.01); break;
case "reorder": (plate.Parts[0], plate.Parts[1]) = (plate.Parts[1], plate.Parts[0]); break;
case "replace-part": plate.Parts[0] = part.CloneAtOffset(new Vector()); break;
case "replace-drawing": plate.Parts[0] = Rectangle(); break;
case "placed-program": part.Update(); break;
case "clean-program": part.BaseDrawing.Program = (Program)part.BaseDrawing.Program.Clone(); break;
case "add": plate.Parts.Add(Rectangle()); break;
case "remove": plate.Parts.RemoveAt(0); break;
case "plate":
var replacement = new Plate();
replacement.Parts.AddRange(plate.Parts);
plate = replacement;
break;
}
Assert.False(state.EnsureFresh(plate));
Assert.False(state.TryPublish(request, plate, report));
Assert.Null(state.Report);
Assert.Equal(OverlapCheckStatus.Stale, state.Status);
Assert.Equal("Overlap check out of date — run Check Overlaps again", state.Message);
}
}
[Fact]
public void CancelFailureAndIncompleteAreNeverClear()
{
var plate = new Plate();
var state = new OverlapReportState();
var request = state.Begin(plate);
state.Cancel();
Assert.False(state.TryPublish(request, plate, Analyze(plate)));
Assert.Equal(OverlapCheckStatus.Canceled, state.Status);
Assert.Null(state.Report);
request = state.Begin(plate);
Assert.True(state.TryFail(request, plate));
Assert.Equal(OverlapCheckStatus.Failed, state.Status);
Assert.Contains("failed", state.Message);
Assert.Null(state.Report);
plate.Parts.Add(new Part(new Drawing("open", new Program())));
request = state.Begin(plate);
Assert.True(state.TryPublish(request, plate, Analyze(plate)));
Assert.Equal(OverlapCheckStatus.Incomplete, state.Status);
Assert.Equal("Overlap check incomplete: 0 overlapping pairs; 1 parts could not be checked", state.Message);
state.Invalidate();
Assert.Equal(OverlapCheckStatus.Stale, state.Status);
Assert.Null(state.Report);
}
[Fact]
public void IssueCountIncludesBothPairIdsAndDeduplicatesAcrossAllIssues()
{
var issues = new[]
{
new PlateOverlapIssue(2, null, "input"),
new PlateOverlapIssue(2, 4, "pair"),
new PlateOverlapIssue(4, 7, "pair"),
new PlateOverlapIssue(7, null, "input")
};
Assert.Equal(3, OverlapReportState.CountUncheckedParts(issues));
}
[Fact]
public void DisplayAndNonGeometryChangesRetainReportAndGeneration()
{
var plate = PlateWithParts();
var state = new OverlapReportState();
Assert.Equal(OverlapDisplayMode.Areas, state.DisplayMode);
var request = state.Begin(plate);
var report = Analyze(plate);
Assert.True(state.TryPublish(request, plate, report));
state.DisplayMode = OverlapDisplayMode.Off;
plate.Parts[0].BaseDrawing.Name = "renamed";
plate.Quantity = 2;
plate.PartSpacing = 123;
Assert.True(state.EnsureFresh(plate));
Assert.Same(report, state.Report);
Assert.Equal(request, state.Generation);
state.DisplayMode = OverlapDisplayMode.Areas;
Assert.Same(report, state.Report);
Assert.Equal(request, state.Generation);
state.DisplayMode = OverlapDisplayMode.Off;
state.Begin(plate);
Assert.Equal(OverlapDisplayMode.Areas, state.DisplayMode);
Assert.Null(state.Report);
}
[Theory]
[InlineData(OverlapDisplayMode.Off, OverlapDisplayMode.Areas)]
[InlineData(OverlapDisplayMode.Areas, OverlapDisplayMode.Areas)]
[InlineData(OverlapDisplayMode.Centroids, OverlapDisplayMode.Centroids)]
[InlineData(OverlapDisplayMode.Both, OverlapDisplayMode.Both)]
public void CheckAndRecheckPreserveVisibleMode(OverlapDisplayMode chosen, OverlapDisplayMode expected)
{
var plate = PlateWithParts();
var state = new OverlapReportState { DisplayMode = chosen };
var request = state.Begin(plate);
Assert.Equal(expected, state.DisplayMode);
Assert.True(state.TryPublish(request, plate, Analyze(plate)));
state.Begin(plate);
Assert.Equal(expected, state.DisplayMode);
}
[Fact]
public void NewPlateResetsStateButRetainsDocumentDisplayPreference()
{
var plate = PlateWithParts();
var state = new OverlapReportState();
var request = state.Begin(plate);
state.DisplayMode = OverlapDisplayMode.Off;
state.Reset();
Assert.Equal(OverlapCheckStatus.NotChecked, state.Status);
Assert.Null(state.Report);
Assert.Equal(OverlapDisplayMode.Off, state.DisplayMode);
Assert.False(state.TryPublish(request, plate, Analyze(plate)));
}
[Fact]
public void CutoffSlotsRemainPartOfTheOrderedIdentityStamp()
{
var plate = PlateWithParts();
var cutoff = Rectangle();
cutoff.BaseDrawing.IsCutOff = true;
plate.Parts.Insert(0, cutoff);
var state = new OverlapReportState();
Assert.True(state.TryPublish(state.Begin(plate), plate, Analyze(plate)));
var pair = Assert.Single(state.Report.Pairs);
Assert.Equal((1, 2), (pair.PartAId, pair.PartBId));
plate.Parts.RemoveAt(0);
Assert.False(state.EnsureFresh(plate));
Assert.Null(state.Report);
}
[Fact]
public void InPlaceGeometryEditorsMustExplicitlyInvalidate()
{
var plate = PlateWithParts();
var state = new OverlapReportState();
var request = state.Begin(plate);
var report = Analyze(plate);
// Deliberately not serialized or converted by the stamp.
plate.Parts[0].BaseDrawing.Program.Codes.Clear();
Assert.True(state.EnsureFresh(plate));
state.Invalidate();
Assert.False(state.TryPublish(request, plate, report));
Assert.Equal(OverlapCheckStatus.Stale, state.Status);
}
[Fact]
public void EmptyCompleteReportAloneCanSayClear()
{
var plate = new Plate();
var state = new OverlapReportState();
Assert.Equal(OverlapCheckStatus.NotChecked, state.Status);
Assert.True(state.TryPublish(state.Begin(plate), plate, Analyze(plate)));
Assert.Equal("No material overlaps detected", state.Message);
}
private static PlateOverlapReport Analyze(Plate plate) => PlateOverlapAnalyzer.Analyze(plate.Parts.ToArray());
private static Plate PlateWithParts()
{
var plate = new Plate();
plate.Parts.Add(Rectangle());
plate.Parts.Add(Rectangle());
return plate;
}
private static Part Rectangle()
{
var program = new Program(Mode.Absolute);
program.Codes.Add(new RapidMove(0, 0));
program.Codes.Add(new LinearMove(4, 0));
program.Codes.Add(new LinearMove(4, 4));
program.Codes.Add(new LinearMove(0, 4));
program.Codes.Add(new LinearMove(0, 0));
return new Part(new Drawing("same name", program));
}
}