Files
OpenNest/OpenNest.FrontEnd.Tests/DefaultEngineSelectionTests.cs
T
aj 89356371db feat(mcp): describe each engine in autonest_plate and default fill tools to Fill
The engine argument's description now names every built-in engine with what
it suits, so a model can choose one; omitting it still means Default. The fill
tools resolve an omitted strategy to Fill directly instead of through the
session's whole-job default. Front-end tests cover the MCP and console
defaults and keep the description in step with the registry's built-ins.
2026-10-06 12:26:19 -04:00

107 lines
4.2 KiB
C#

using System.ComponentModel;
using System.Reflection;
using System.Text.RegularExpressions;
using OpenNest.CNC;
using OpenNest.Engine.Jobs;
using OpenNest.Geometry;
using OpenNest.IO;
using OpenNest.Mcp;
using OpenNest.Mcp.Tools;
namespace OpenNest.FrontEnd.Tests;
/// <summary>Front ends that are not told an engine use Default; fill tools use Fill.</summary>
[Collection("FrontEndRegistry")]
public class DefaultEngineSelectionTests : IDisposable
{
private readonly string directory = Path.Combine(Path.GetTempPath(), "opennest-default-engine-" + Guid.NewGuid());
public DefaultEngineSelectionTests() => Directory.CreateDirectory(directory);
public void Dispose() => Directory.Delete(directory, true);
private static NestSession Session(bool occupied = false)
{
var shape = new Shape();
shape.Entities.Add(new Line(new Vector(0, 0), new Vector(2, 0)));
shape.Entities.Add(new Line(new Vector(2, 0), new Vector(2, 2)));
shape.Entities.Add(new Line(new Vector(2, 2), new Vector(0, 2)));
shape.Entities.Add(new Line(new Vector(0, 2), new Vector(0, 0)));
var session = new NestSession { Nest = new Nest("fixture") };
session.Nest.Drawings.Add(new Drawing("square", OpenNest.Converters.ConvertGeometry.ToProgram(shape)));
var plate = session.Nest.CreatePlate();
plate.Size = new Size(20, 30);
plate.PartSpacing = 0.2;
plate.EdgeSpacing = new Spacing(0.5, 0.5, 0.5, 0.5);
if (occupied) // The nest writer keeps only plates that hold parts; autonest replaces them.
plate.Parts.Add(new Part(session.Nest.Drawings.Single()));
return session;
}
[Fact]
public void McpAutonestWithoutAnEngineUsesDefault()
{
var session = Session();
var response = new NestingTools(session).AutoNestPlate(0, "square", "2");
Assert.Contains("(Default engine): success", response);
Assert.Equal(2, session.GetPlate(0).Parts.Count);
}
[Fact]
public void McpFillToolsWithoutAnEngineUseFill()
{
var session = Session();
session.DefaultEngineName = "Rectangles"; // The session default is for autonest only.
var response = new NestingTools(session).FillPlate(0, "square", 2);
Assert.Contains("(Fill): success", response);
Assert.Equal(2, session.GetPlate(0).Parts.Count);
}
[Fact]
public void McpAutonestEngineDescriptionNamesEveryBuiltInEngine()
{
var engine = typeof(NestingTools).GetMethod(nameof(NestingTools.AutoNestPlate))!
.GetParameters().Single(p => p.Name == "engine");
var description = engine.GetCustomAttribute<DescriptionAttribute>()!.Description;
var builtIns = NestingEngineRegistry.AvailableEngines
.Where(e => e.Factory().GetType().Assembly == typeof(NestingEngineRegistry).Assembly)
.Select(e => e.Name)
.ToList();
Assert.Contains("Default", builtIns);
Assert.All(builtIns, name => Assert.Matches(@"\b" + Regex.Escape(name) + @"\b", description));
Assert.StartsWith("Whole-job engine. Default (used when omitted)", description);
}
[Fact]
public void ConsoleAutonestWithoutAnEngineUsesDefault()
{
var input = Path.Combine(directory, "input.nest");
var output = Path.Combine(directory, "output.nest");
Assert.True(new NestWriter(Session(occupied: true).Nest).Write(input));
var method = Assembly.Load("OpenNest.Console").GetType("NestConsole")!
.GetMethod("Run", BindingFlags.Static | BindingFlags.Public)!;
var (originalOut, originalError) = (Console.Out, Console.Error);
using var captured = new StringWriter();
int exit;
try
{
Console.SetOut(captured);
Console.SetError(captured);
exit = (int)method.Invoke(null, new object[] { new[] { input, "--autonest", "--quantity", "2", "--output", output } })!;
}
finally
{
Console.SetOut(originalOut);
Console.SetError(originalError);
}
Assert.True(exit == 0, captured.ToString());
Assert.Contains("Engine: Default", captured.ToString());
Assert.Equal(2, Assert.Single(new NestReader(output).Read().Plates).Parts.Count);
}
}