Files
OpenNest-Engines/Engine.Testing/LayoutAssert.cs
T
c691381f30 test(engines): add a shared engine test kit and contract tests
Each engine's tests carried its own copy of the job/shape helpers and
validation, and they had drifted: Gpt6Astra linked NestValidator.cs as
source (which stopped compiling once the host moved its checks into
NestLayoutCheck), the others referenced OpenNest.Benchmark. The kit
provides JobBuilder, Shapes, LayoutAssert (backed by the host's public
NestLayoutCheck) and EngineContractTests<TEngine>: quadrants, overflow,
oversize parts, lower-number priority, etch marks, sequential plate
indices, determinism, cancellation and stop reasons, for every engine.

Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 09:29:26 -04:00

51 lines
2.4 KiB
C#

using OpenNest.Converters;
using OpenNest.Engine.Jobs;
using OpenNest.Engine.Jobs.Adapters;
using Xunit;
namespace OpenNest.Engine.Testing;
public static class LayoutAssert
{
public static void Valid(NestJob job, NestJobResult result)
{
var violations = NestLayoutCheck.Violations(job, result);
Assert.True(violations.Count == 0, string.Join(Environment.NewLine, violations));
Assert.Equal(Enumerable.Range(0, result.Plates.Count), result.Plates.Select(p => p.PlateIndex));
foreach (var f in result.Fulfillment)
Assert.Equal(f.Requested, f.Placed + f.Unplaced);
foreach (var sheet in result.Plates)
{
var s = sheet.Stock;
var work = s.WorkArea;
foreach (var pose in sheet.Placements)
{
var part = job.Parts.Single(p => p.Id == pose.PartId);
Assert.True(part.Rotation.Allows(pose.Rotation));
var geometry = ConvertProgram.ToGeometry(DrawingJobMapper.ToProgram(part.Geometry))
.Where(e => SpecialLayers.IsMaterial(e.Layer)).ToArray();
foreach (var entity in geometry) { entity.Rotate(pose.Rotation); entity.Offset(pose.X, pose.Y); }
var b = (L: geometry.Min(e => e.Left), B: geometry.Min(e => e.Bottom),
R: geometry.Max(e => e.Right), T: geometry.Max(e => e.Top));
Assert.True(b.L >= work.Left - 1e-7 && b.B >= work.Bottom - 1e-7
&& b.R <= work.Right + 1e-7 && b.T <= work.Top + 1e-7);
}
}
foreach (var part in job.Parts)
{
var placed = result.Plates.SelectMany(s => s.Placements).Where(p => p.PartId == part.Id).ToArray();
Assert.Equal(Enumerable.Range(0, placed.Length), placed.Select(p => p.InstanceIndex).Order());
var fulfillment = result.Fulfillment.Single(f => f.PartId == part.Id);
Assert.Equal(placed.Length, fulfillment.Placed);
Assert.Equal(part.Quantity, fulfillment.Placed + fulfillment.Unplaced);
}
foreach (var usage in result.StockUsage)
{
var stock = job.Plates.Single(s => s.Id == usage.StockId);
Assert.Equal(result.Plates.Count(s => s.StockId == stock.Id), usage.Used);
Assert.Equal(stock.Quantity - usage.Used, usage.Remaining);
Assert.True(usage.Remaining is null or >= 0);
}
}
}