test(ui): characterize MainForm nest naming and busy guards

Pins the nest-name format and which operations each MainForm busy
guard counts, ahead of splitting and consolidating MainForm.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ajandClaude Opus 5.5 committed 2026-10-17 20:57:26 -04:00
1 parent 9d6c10c563
commit 686bb4d749
1 file changed
+130
@@ -0,0 +1,130 @@
using System.Reflection;
using OpenNest.Controls;
using OpenNest.Forms;
using OpenNest.Geometry;
namespace OpenNest.WinForms.Tests.Forms;
// Pins MainForm behaviour that its refactor must preserve: nest naming and the deliberately
// different busy guards. Reads Application.OpenForms, so it must not overlap fill-lifetime tests.
[Collection("Fill operation lifetime")]
public class MainFormCharacterizationTests
{
[Theory]
[InlineData(0, "N26-222")]
[InlineData(1, "N26-223")]
[InlineData(29, "N26-22Z")]
[InlineData(30, "N26-232")]
[InlineData(900, "N26-322")]
[InlineData(27000, "N26-3222")]
public void NestNamesUseTwoDigitYearAndPaddedBase30Sequence(int id, string expected) =>
StaTestThread.Run(() =>
{
using var host = new Host();
var name = (string)typeof(MainForm)
.GetMethod("GetNestName", BindingFlags.Instance | BindingFlags.NonPublic)!
.Invoke(host, [new DateTime(2026, 10, 17), id])!;
Assert.Equal(expected, name);
}, TimeSpan.FromMinutes(1), "The STA test did not complete.");
[Fact]
public void BusyGuardsDifferInWhichOperationsTheyCount() => StaTestThread.Run(() =>
{
using var host = new Host();
host.Show();
var nest = new Nest("guards");
var plate = nest.CreatePlate();
plate.Size = new Size(100, 100);
plate.Parts.Add(new Part(new Drawing("left", Square()), new Vector(1, 1)));
plate.Parts.Add(new Part(new Drawing("right", Square()), new Vector(12, 1)));
using var editor = new EditNestForm(nest) { MdiParent = host };
editor.PlateView.SetOverlapAutoCheck(null);
editor.Show();
editor.Activate();
editor.PlateManager.LoadAt(0);
var other = new Nest("other");
other.CreatePlate().Size = new Size(100, 100);
using var otherEditor = new EditNestForm(other) { MdiParent = host };
otherEditor.PlateView.SetOverlapAutoCheck(null);
otherEditor.Show();
otherEditor.PlateManager.LoadAt(0);
var views = new[] { editor.PlateView };
Assert.Equal(new Guards(false, false, false, false), Read(host, editor, views));
// Whole-job nesting blocks every guard.
SetField(host, "nestingInProgress", true);
Assert.Equal(new Guards(true, true, true, true), Read(host, editor, views));
SetField(host, "nestingInProgress", false);
// A database save only blocks the report snapshot.
SetField(host, "databaseSaveInProgress", true);
Assert.Equal(new Guards(false, false, false, true), Read(host, editor, views));
SetField(host, "databaseSaveInProgress", false);
// A fill on a view of the nest blocks the plate guards but not the report.
SetBackingField(editor.PlateView, "IsFillInProgress", true);
try
{
Assert.Equal(new Guards(true, true, true, false), Read(host, editor, views));
}
finally
{
SetBackingField(editor.PlateView, "IsFillInProgress", false);
}
// A fill on another nest's view blocks nothing for this nest.
SetBackingField(otherEditor.PlateView, "IsFillInProgress", true);
try
{
Assert.Equal(new Guards(false, false, false, false), Read(host, editor, views));
}
finally
{
SetBackingField(otherEditor.PlateView, "IsFillInProgress", false);
}
// A busy plate action (selection) blocks only the cutting-plan guard.
editor.PlateView.SelectAll();
Assert.True(editor.PlateView.Actions.CurrentAction?.IsBusy() == true);
Assert.Equal(new Guards(false, true, false, false), Read(host, editor, views));
}, TimeSpan.FromMinutes(1), "The STA test did not complete.");
private sealed record Guards(bool ExpandSpacing, bool CuttingPlan, bool Clamps, bool NestJob);
private static Guards Read(MainForm host, EditNestForm editor, PlateView[] views) => new(
host.IsExpandSpacingBusy(editor),
host.IsCuttingPlanBusy(views),
(bool)Invoke(host, "ClampsInUse", editor.Nest),
(bool)Invoke(host, "IsNestJobBusy"));
private static object Invoke(MainForm host, string name, params object[] args) =>
typeof(MainForm).GetMethod(name, BindingFlags.Instance | BindingFlags.NonPublic)!.Invoke(host, args)!;
private static void SetField(MainForm host, string name, object value) =>
typeof(MainForm).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic)!.SetValue(host, value);
private static void SetBackingField(object target, string property, object value) =>
target.GetType()
.GetField($"<{property}>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic)!
.SetValue(target, value);
private static CNC.Program Square()
{
var program = new CNC.Program();
program.MoveTo(0, 0);
program.LineTo(0, 10);
program.LineTo(10, 10);
program.LineTo(10, 0);
program.LineTo(0, 0);
return program;
}
private sealed class Host : MainForm
{
// No startup migrations/automatic new document, and no settings save at close.
protected override void OnLoad(EventArgs e) { }
protected override void OnClosing(System.ComponentModel.CancelEventArgs e) { }
}
}