mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-11-04 05:51:34 -05:00
Review fixes for ledger-first Make Nests: - The release renest (offer, staged run, codes, note) moves into OpenNest.Desktop.ReleaseRenest, shared by the headless panel, the live main window and the tests. Codes are given inside each commit, so the live tab's Auto Nest undo step records them; before, undo and redo dropped every ledger code. - A bundle repeats one code per sheet: a later nest of the run lost the whole bundle once an earlier nest took one sheet. Taken sheets are now counted per code (LedgerCodeFilter.ExcludeCopies). - A stage whose layout is not kept lists every copy it asked for as not placed (the proposal cannot be kept), and a rejected ledger layout says "MaterialLedger not used: Auto Nest could not keep a layout on its stock" instead of a bare "No MaterialLedger stock used". - "no free stock" is said only when the ledger had none; otherwise the note counts what was left out (on other nests, taken by earlier nests of this release, too small, unknown shape). - The ledger is not read for a nest with nothing to nest, the live read runs under the tab's stop token, a reason's own full stop is not doubled, and the sheet count is the codes given.
168 lines
7.0 KiB
C#
168 lines
7.0 KiB
C#
using System.Windows.Forms;
|
|
using OpenNest.Controls;
|
|
using OpenNest.Data;
|
|
using OpenNest.Data.MaterialLedger;
|
|
using OpenNest.Engine;
|
|
using OpenNest.Engine.Jobs;
|
|
using OpenNest.Forms;
|
|
using OpenNest.Geometry;
|
|
using OpenNest.Shapes;
|
|
|
|
namespace OpenNest.WinForms.Tests.Forms;
|
|
|
|
// Make Nests nesting live in the main window. Reads Application.OpenForms through MainForm,
|
|
// so it must not overlap fill-lifetime tests.
|
|
[Collection("Fill operation lifetime")]
|
|
public class MainFormReleaseNestingTests
|
|
{
|
|
private static readonly TimeSpan TestTimeout = TimeSpan.FromMinutes(1);
|
|
|
|
[Fact]
|
|
public void EachNestNestsInItsOwnTab_ASavedOneIsBoundNotReopened_AndAnUnsavedOneCloses() => StaTestThread.Run(() =>
|
|
{
|
|
using var main = new Host();
|
|
main.SolveReleasedNest = (_, request, cts) =>
|
|
Task.Run(() => NestPipeline.Run(NestingEngineRegistry.Create("Rectangles"), "Rectangles", request, null, cts.Token));
|
|
main.Show();
|
|
IReleaseNestingHost host = main;
|
|
var saved = NestOf("N26-0001", 6);
|
|
var unsaved = NestOf("N26-0002", 2);
|
|
|
|
host.Begin();
|
|
var first = Run(host.RenestAsync(saved, null, CancellationToken.None));
|
|
var tab = Assert.Single(Tabs(main));
|
|
Assert.Same(saved, tab.Nest);
|
|
Assert.True(first.Nested, first.Note);
|
|
Assert.StartsWith("Nested on 1 sheet", first.Note);
|
|
Assert.Equal(6, saved.Plates.Sum(p => p.Parts.Count * p.Quantity));
|
|
Assert.Equal(Guid.Empty, tab.Document.RemoteId);
|
|
// The kept layout is one undoable step in the tab; undo waits for the release.
|
|
Assert.Equal(1, tab.History.UndoCount);
|
|
Assert.Equal("Auto Nest", tab.History.UndoLabel);
|
|
Assert.False(main.TryUndo(tab));
|
|
Run(host.RenestAsync(unsaved, null, CancellationToken.None));
|
|
Assert.Equal(2, Tabs(main).Count);
|
|
|
|
// Make Nests opens what it saved: the tab already showing it is bound instead.
|
|
var record = new NestRecord { Id = Guid.NewGuid(), Revision = 1, Name = saved.Name };
|
|
main.OpenServerNest(saved, record);
|
|
Assert.Equal(2, Tabs(main).Count);
|
|
Assert.Equal((record.Id, 1), (tab.Document.RemoteId, tab.Document.RemoteRevision));
|
|
|
|
host.End(null!);
|
|
Application.DoEvents();
|
|
Assert.Same(tab, Assert.Single(Tabs(main)));
|
|
}, TestTimeout, "The live release nesting test did not complete.");
|
|
|
|
[Fact]
|
|
public void StoppingTheAutoNestStopsTheRelease_AndLeavesNoLayout() => StaTestThread.Run(() =>
|
|
{
|
|
using var main = new Host();
|
|
main.SolveReleasedNest = (_, _, cts) =>
|
|
{
|
|
// The progress window's Stop.
|
|
cts.Cancel();
|
|
return Task.FromException<NestPipelineResult>(new OperationCanceledException(cts.Token));
|
|
};
|
|
main.Show();
|
|
IReleaseNestingHost host = main;
|
|
var nest = NestOf("N26-0003", 4);
|
|
|
|
host.Begin();
|
|
var renest = host.RenestAsync(nest, null, CancellationToken.None);
|
|
PumpUntil(() => renest.IsCompleted);
|
|
|
|
Assert.True(renest.IsCanceled || renest.Exception?.InnerException is OperationCanceledException);
|
|
Assert.DoesNotContain(nest.Plates, p => p.Parts.Count > 0);
|
|
host.End(null!);
|
|
Application.DoEvents();
|
|
Assert.Empty(Tabs(main));
|
|
}, TestTimeout, "The live release stop test did not complete.");
|
|
|
|
[Fact]
|
|
public void LedgerStockIsNestedFirst_InTheTab_ThenTheRestOnTheNestDefaultsSheet() =>
|
|
StaTestThread.Run(() =>
|
|
{
|
|
using var main = new Host();
|
|
main.SolveReleasedNest = (_, request, cts) => Task.Run(() => NestPipeline.Run(
|
|
NestingEngineRegistry.Create("Rectangles"), "Rectangles", request, null, cts.Token));
|
|
main.Show();
|
|
IReleaseNestingHost host = main;
|
|
// One free 10x10 ledger sheet holds one 8x8 part; the second copy tops up on 48x96.
|
|
var nest = NestOf("N26-0004", 2, partSize: 8);
|
|
nest.Material = new Material("Synthetic Steel", "SYN-1");
|
|
nest.Thickness = 0.25;
|
|
var ledger = new ReleaseLedgerStock(
|
|
(_, _, _, _) => Task.FromResult<LedgerStockResult>(new LedgerStockResult.Ok(
|
|
Array.Empty<LedgerStockSpec>(),
|
|
[new LedgerStockItem("S-1", 1, LedgerItemKinds.FullSheet, 10, 10, 1,
|
|
LedgerItemShapes.Rectangular, "B3", "Bay 3", null, null, DateTime.UnixEpoch)])),
|
|
_ => Task.FromResult(LedgerCodesInUse.Known(Array.Empty<NestStockCodeUse>())));
|
|
|
|
host.Begin();
|
|
var renest = Run(host.RenestAsync(nest, ledger, CancellationToken.None));
|
|
|
|
var tab = Assert.Single(Tabs(main));
|
|
Assert.True(renest.Nested, renest.Note);
|
|
Assert.EndsWith(" 1 sheet from MaterialLedger stock.", renest.Note);
|
|
var coded = Assert.Single(nest.Plates, p => p.StockItemCodes.Count > 0);
|
|
Assert.Equal((new Size(10, 10), "S-1"), (coded.Size, Assert.Single(coded.StockItemCodes)));
|
|
Assert.Contains(nest.Plates, p => p.Parts.Count > 0 && p.Size.Equals(new Size(48, 96))
|
|
&& p.StockItemCodes.Count == 0);
|
|
Assert.Equal(2, nest.Plates.Sum(p => p.Parts.Count * p.Quantity));
|
|
Assert.Equal("B3", tab.LedgerLocations["S-1"]);
|
|
// Each stage is its own Auto Nest step in the tab, and its codes are part of that step.
|
|
Assert.Equal(2, tab.History.UndoCount);
|
|
tab.Undo();
|
|
tab.Undo();
|
|
tab.Redo();
|
|
tab.Redo();
|
|
var redone = Assert.Single(tab.Nest.Plates, p => p.StockItemCodes.Count > 0);
|
|
Assert.Equal("S-1", Assert.Single(redone.StockItemCodes));
|
|
host.End(null!);
|
|
Application.DoEvents();
|
|
}, TestTimeout, "The live release ledger test did not complete.");
|
|
|
|
private static Nest NestOf(string name, int quantity, double partSize = 0)
|
|
{
|
|
var nest = new Nest(name) { Units = Units.Inches };
|
|
nest.PlateDefaults.Size = new Size(48, 96);
|
|
var drawing = new RectangleShape
|
|
{
|
|
Name = name + "-A",
|
|
Length = partSize > 0 ? partSize : 4,
|
|
Width = partSize > 0 ? partSize : 2,
|
|
}.GetDrawing();
|
|
drawing.Quantity.Required = quantity;
|
|
nest.Drawings.Add(drawing);
|
|
return nest;
|
|
}
|
|
|
|
private static List<EditNestForm> Tabs(MainForm main) =>
|
|
main.MdiChildren.OfType<EditNestForm>().Where(f => !f.IsDisposed).ToList();
|
|
|
|
private static T Run<T>(Task<T> task)
|
|
{
|
|
PumpUntil(() => task.IsCompleted);
|
|
return task.GetAwaiter().GetResult();
|
|
}
|
|
|
|
private static void PumpUntil(Func<bool> condition)
|
|
{
|
|
var until = DateTime.UtcNow.AddSeconds(30);
|
|
while (!condition() && DateTime.UtcNow < until)
|
|
{
|
|
Application.DoEvents();
|
|
Thread.Sleep(10);
|
|
}
|
|
Assert.True(condition(), "Timed out.");
|
|
}
|
|
|
|
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) { }
|
|
}
|
|
}
|