Files
OpenNest/OpenNest.Tests/Reporting/NestReportBuilderTests.cs
T

168 lines
7.4 KiB
C#

using System.Text.Json;
using OpenNest.CNC;
using OpenNest.Geometry;
using OpenNest.IO;
using OpenNest.Reporting;
namespace OpenNest.Tests.Reporting;
public class NestReportBuilderTests
{
[Fact]
public void Capture_ReconcilesOrderedReferenceUnionWithoutCountingCutoffs()
{
var nest = NestReportTestData.CreateNest();
var snapshot = NestReportBuilder.Capture(nest, NestReportTestData.GeneratedAt);
Assert.Equal("Report test job", snapshot.Name);
Assert.Equal("Test customer", snapshot.Customer);
Assert.Equal("Synthetic report test data", snapshot.Notes);
Assert.Equal("Steel", snapshot.Material);
Assert.Equal("A36", snapshot.Grade);
Assert.Equal(0.125, snapshot.Thickness);
Assert.Equal("in", snapshot.Units);
Assert.Equal(NestReportTestData.GeneratedAt, snapshot.GeneratedAt);
Assert.Equal(new[] { "1", "2", "3", "4" }, snapshot.Drawings.Select(d => d.Id));
Assert.Equal(new[] { "Bracket", "Rotated", "Bracket", "Unplaced" }, snapshot.Drawings.Select(d => d.Name));
Assert.Equal(new long[] { 5, 1, 7, 3 }, snapshot.Drawings.Select(d => d.Required));
Assert.Equal(new long[] { 4, 2, 2, 0 }, snapshot.Drawings.Select(d => d.Nested));
Assert.Equal(new long[] { 1, 0, 5, 3 }, snapshot.Drawings.Select(d => d.Shortage));
Assert.Equal(new long[] { 0, 1, 0, 0 }, snapshot.Drawings.Select(d => d.Extra));
Assert.All(snapshot.Drawings.Take(3), d => Assert.Equal(new[] { 1 }, d.Plates));
Assert.Empty(snapshot.Drawings[3].Plates);
var plate = Assert.Single(snapshot.Plates);
Assert.Equal(new[] { "1", "1", "2", "3" }, plate.Parts.Select(p => p.ReportId));
Assert.Single(plate.Cutoffs);
Assert.Equal(2, snapshot.TotalSheets);
Assert.Equal(new ReportBounds(0, 0, 48, 24), plate.Bounds);
Assert.Equal(0.125, plate.PartSpacing);
Assert.Equal(nest.Plates[0].Utilization(), plate.Utilization);
}
[Fact]
public void Capture_RecountsAcrossPlateCopiesWithWideIntegersAndUniquePlateNumbers()
{
var nest = NestReportTestData.CreateNest();
var drawing = nest.Plates[0].Parts[0].BaseDrawing;
// Change copies after adding: the legacy cached Nested count stays stale.
nest.Plates[0].Quantity = int.MaxValue;
var second = new Plate(24, 48) { Quantity = 3 };
second.Parts.Add(new Part(drawing));
second.Parts.Add(new Part(drawing));
nest.Plates.Add(second);
var cached = drawing.Quantity.Nested;
var snapshot = NestReportBuilder.Capture(nest, NestReportTestData.GeneratedAt);
Assert.Equal(checked(2L * int.MaxValue + 6), snapshot.Drawings[0].Nested);
Assert.Equal(new[] { 1, 2 }, snapshot.Drawings[0].Plates);
Assert.Equal(checked((long)int.MaxValue + 3), snapshot.TotalSheets);
Assert.Equal(cached, drawing.Quantity.Nested);
}
[Fact]
public void Capture_IsRepeatableDoesNotMutateSourcesAndRetainsNoMutableReferences()
{
var nest = NestReportTestData.CreateNest();
var part = nest.Plates[0].Parts[0];
var drawing = part.BaseDrawing;
var source = Fingerprint(nest);
var snapshot = NestReportBuilder.Capture(nest, NestReportTestData.GeneratedAt);
var serialized = JsonSerializer.Serialize(snapshot);
Assert.Equal(source, Fingerprint(nest));
Assert.Equal(serialized, JsonSerializer.Serialize(NestReportBuilder.Capture(nest, NestReportTestData.GeneratedAt)));
Assert.NotEmpty(snapshot.Plates);
nest.Name = "Edited";
nest.Material.Name = "Edited material";
nest.Plates[0].Size = new Size(100, 200);
nest.Plates[0].Quantity = 42;
part.Location = new Vector(500, 600);
((LinearMove)part.Program.Codes[1]).EndPoint = new Vector(99, 99);
((LinearMove)drawing.Program.Codes[1]).EndPoint = new Vector(88, 88);
drawing.Name = "Edited drawing";
drawing.Quantity.Required = 123;
nest.Drawings.Clear();
nest.Plates.Clear();
Assert.Equal(serialized, JsonSerializer.Serialize(snapshot));
}
[Fact]
public void Capture_EmptyAndDemandOnlyJobsKeepExplicitEmptyCollectionsAndOrdinalOrder()
{
var nest = new Nest("Empty test");
var empty = NestReportBuilder.Capture(nest, NestReportTestData.GeneratedAt);
Assert.Equal("Empty test", empty.Name);
Assert.Empty(empty.Drawings);
Assert.Empty(empty.Plates);
Assert.Equal(0, empty.TotalSheets);
nest.Drawings.Add(NestReportTestData.Rectangle("zeta", 1, 1, 5));
nest.Drawings.Add(NestReportTestData.Rectangle("Alpha", 1, 1, 2));
var demand = NestReportBuilder.Capture(nest, NestReportTestData.GeneratedAt);
Assert.Equal(new[] { "Alpha", "zeta" }, demand.Drawings.Select(d => d.Name));
Assert.All(demand.Drawings, d => { Assert.Equal(0, d.Nested); Assert.Equal(d.Required, d.Shortage); });
Assert.Empty(demand.Plates);
}
[Theory]
[InlineData(0, 48, 1)]
[InlineData(24, -1, 1)]
[InlineData(double.NaN, 48, 1)]
[InlineData(24, double.PositiveInfinity, 1)]
[InlineData(24, 48, 0)]
[InlineData(24, 48, -1)]
public void Capture_RejectsInvalidPlateBeforeProducingSnapshot(double width, double length, int copies)
{
var nest = NestReportTestData.CreateNest();
nest.Plates[0].Size = new Size(width, length);
nest.Plates[0].Quantity = copies;
var before = Fingerprint(nest);
var error = Assert.Throws<InvalidOperationException>(() => NestReportBuilder.Capture(nest, NestReportTestData.GeneratedAt));
Assert.Contains("Plate 1", error.Message);
Assert.Equal(before, Fingerprint(nest));
}
[Theory]
[InlineData(1, 0, 0, 48, 24)]
[InlineData(2, -48, 0, 0, 24)]
[InlineData(3, -48, -24, 0, 0)]
[InlineData(4, 0, -24, 48, 0)]
public void Capture_UsesQuadrantAwareStockBounds(int quadrant, double left, double bottom, double right, double top)
{
var nest = NestReportTestData.CreateNest();
nest.Plates[0].Quadrant = quadrant;
var snapshot = NestReportBuilder.Capture(nest, NestReportTestData.GeneratedAt);
Assert.Equal(new ReportBounds(left, bottom, right, top), Assert.Single(snapshot.Plates).Bounds);
}
internal static string Fingerprint(Nest nest) => JsonSerializer.Serialize(new
{
nest.Name,
nest.Customer,
nest.Notes,
nest.Material,
nest.Thickness,
nest.Units,
nest.DateCreated,
nest.DateLastModified,
Drawings = nest.Drawings.Select(d => new { d.Id, d.Name, d.Quantity, Main = NestWriter.GetProgramText(d.Program), Subs = NestWriter.GetSubProgramsText(d.Program) }),
Plates = nest.Plates.Select(p => new
{
p.Size,
p.Quantity,
p.PartSpacing,
p.Quadrant,
Parts = p.Parts.Select(part => new
{
part.BaseDrawing.Id,
part.Location,
part.Rotation,
part.HasManualLeadIns,
part.LeadInsLocked,
Main = NestWriter.GetProgramText(part.Program),
Subs = NestWriter.GetSubProgramsText(part.Program),
}),
}),
}, new JsonSerializerOptions { IncludeFields = true, NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.AllowNamedFloatingPointLiterals });
}