style: apply CSharpier formatting to all C# sources

Repo-wide sweep with the pinned CSharpier 1.3.0 tool. Whitespace and
line-wrapping only; OpenNest.Engine.Tests (109) and OpenNest.IO.Tests
pass after reformat, full solution builds 0 errors.

Added .csharpierignore so csproj/config XML keeps its existing layout
(CSharpier's XML wrapping churns attributes with zero benefit).

Formatting is now enforceable: dotnet csharpier check . passes.
This commit is contained in:
aj
2026-09-20 16:41:50 -04:00
parent 8e6fa677fb
commit aec0523062
476 changed files with 16592 additions and 7800 deletions
+160 -61
View File
@@ -4,19 +4,31 @@ namespace OpenNest.Engine.Tests.Jobs;
public class StockLadderTests
{
private static NestJobPart Rectangle(string id, int quantity, double x = 4, double y = 4,
RotationPolicy? rotation = null) => new(id,
PartGeometrySnapshot.FromProgram(TestDrawingFactory.Rectangle(x, y)), quantity,
rotation: rotation ?? RotationPolicy.Fixed(0));
private static NestJobPart Rectangle(
string id,
int quantity,
double x = 4,
double y = 4,
RotationPolicy? rotation = null
) =>
new(
id,
PartGeometrySnapshot.FromProgram(TestDrawingFactory.Rectangle(x, y)),
quantity,
rotation: rotation ?? RotationPolicy.Fixed(0)
);
[Fact]
public void MergesEquivalentDemandOntoLargerSheetAndReturnsFiniteStock()
{
var job = new NestJob(new[] { Rectangle("a", 5) }, new[]
{
new NestPlateStock("small", new Size(10, 10), 2),
new NestPlateStock("large", new Size(10, 18), 1)
});
var job = new NestJob(
new[] { Rectangle("a", 5) },
new[]
{
new NestPlateStock("small", new Size(10, 10), 2),
new NestPlateStock("large", new Size(10, 18), 1),
}
);
var result = new StockLadderNestingEngine().Solve(job);
Assert.Equal(NestJobStatus.Complete, result.Status);
Assert.Equal("large", Assert.Single(result.Plates).StockId);
@@ -36,7 +48,11 @@ public class StockLadderTests
Assert.Equal(NestJobStopReason.StockExhausted, result.StopReason);
Assert.Equal(4, result.Fulfillment[0].Placed);
Verify(job, result);
job = new NestJob(parts, new[] { new NestPlateStock("only", new Size(10, 10)) }, new NestJobOptions(maxPlates: 1));
job = new NestJob(
parts,
new[] { new NestPlateStock("only", new Size(10, 10)) },
new NestJobOptions(maxPlates: 1)
);
result = new StockLadderNestingEngine().Solve(job);
Assert.Equal(NestJobStopReason.PlateLimitReached, result.StopReason);
Assert.Single(result.Plates);
@@ -46,11 +62,14 @@ public class StockLadderTests
[Fact]
public void ConstrainedLargeSinglePrecedesSmallFillers()
{
var job = new NestJob(new[] { Rectangle("small", 12, 2, 2), Rectangle("large", 1, 12, 6) }, new[]
{
new NestPlateStock("small-sheet", new Size(10, 10)),
new NestPlateStock("large-sheet", new Size(10, 18))
});
var job = new NestJob(
new[] { Rectangle("small", 12, 2, 2), Rectangle("large", 1, 12, 6) },
new[]
{
new NestPlateStock("small-sheet", new Size(10, 10)),
new NestPlateStock("large-sheet", new Size(10, 18)),
}
);
var result = new StockLadderNestingEngine().Solve(job);
Assert.Equal("large", result.Plates[0].Placements[0].PartId);
Assert.Contains(result.Plates[0].Placements, p => p.PartId == "small");
@@ -59,12 +78,31 @@ public class StockLadderTests
}
[Theory]
[InlineData(1)] [InlineData(2)] [InlineData(3)] [InlineData(4)]
[InlineData(1)]
[InlineData(2)]
[InlineData(3)]
[InlineData(4)]
public void GeometrySpacingRotationsAndQuadrantsAreValidated(int quadrant)
{
var job = new NestJob(new[] { Rectangle("a", 6, 3, 5, RotationPolicy.Fixed(System.Math.PI / 2)) },
new[] { new NestPlateStock("sheet", new Size(12, 18), partSpacing: 0.25,
edgeSpacing: new Spacing { Left = 0.5, Right = 0.5, Top = 0.5, Bottom = 0.5 }, quadrant: quadrant) });
var job = new NestJob(
new[] { Rectangle("a", 6, 3, 5, RotationPolicy.Fixed(System.Math.PI / 2)) },
new[]
{
new NestPlateStock(
"sheet",
new Size(12, 18),
partSpacing: 0.25,
edgeSpacing: new Spacing
{
Left = 0.5,
Right = 0.5,
Top = 0.5,
Bottom = 0.5,
},
quadrant: quadrant
),
}
);
var result = new StockLadderNestingEngine().Solve(job);
Assert.Equal(NestJobStatus.Complete, result.Status);
Verify(job, result);
@@ -73,8 +111,10 @@ public class StockLadderTests
[Fact]
public void ImpossibleDemandTerminatesWithoutUsingUnlimitedStock()
{
var job = new NestJob(new[] { Rectangle("a", 1, 100, 100) },
new[] { new NestPlateStock("sheet", new Size(10, 10)) });
var job = new NestJob(
new[] { Rectangle("a", 1, 100, 100) },
new[] { new NestPlateStock("sheet", new Size(10, 10)) }
);
var result = new StockLadderNestingEngine().Solve(job);
Assert.Equal(NestJobStopReason.NoPlacementFound, result.StopReason);
Assert.Empty(result.Plates);
@@ -83,31 +123,56 @@ public class StockLadderTests
[Fact]
public void CancellationBeforeAndDuringTrialNeverReturnsPartialSuccess()
{
var job = new NestJob(new[] { Rectangle("a", 1) }, new[] { new NestPlateStock("s", new Size(10, 10)) });
var job = new NestJob(
new[] { Rectangle("a", 1) },
new[] { new NestPlateStock("s", new Size(10, 10)) }
);
using var cts = new CancellationTokenSource();
var engine = new StockLadderNestingEngine(() => new CallbackNester(request =>
{
cts.Cancel();
return new PlateCandidate(Array.Empty<NestJobPlacement>());
}));
var engine = new StockLadderNestingEngine(() =>
new CallbackNester(request =>
{
cts.Cancel();
return new PlateCandidate(Array.Empty<NestJobPlacement>());
})
);
Assert.Throws<OperationCanceledException>(() => engine.Solve(job, token: cts.Token));
Assert.Throws<OperationCanceledException>(() => new StockLadderNestingEngine().Solve(job, token: cts.Token));
Assert.Throws<OperationCanceledException>(() =>
new StockLadderNestingEngine().Solve(job, token: cts.Token)
);
}
[Theory]
[InlineData(false)] [InlineData(true)]
[InlineData(false)]
[InlineData(true)]
public void RejectsOverlappingOrOverproducingNester(bool overproduce)
{
var job = new NestJob(new[] { Rectangle("a", 2) }, new[] { new NestPlateStock("s", new Size(10, 10)) });
var engine = new StockLadderNestingEngine(() => new CallbackNester(request =>
new PlateCandidate(overproduce
? Enumerable.Repeat(new NestJobPlacement("a", 0, 0, 0, 0), 3)
: new[] { new NestJobPlacement("a", 0, 50, 0, 0) })));
var job = new NestJob(
new[] { Rectangle("a", 2) },
new[] { new NestPlateStock("s", new Size(10, 10)) }
);
var engine = new StockLadderNestingEngine(() =>
new CallbackNester(request => new PlateCandidate(
overproduce
? Enumerable.Repeat(new NestJobPlacement("a", 0, 0, 0, 0), 3)
: new[] { new NestJobPlacement("a", 0, 50, 0, 0) }
))
);
Assert.Throws<InvalidOperationException>(() => engine.Solve(job));
// Direct full-demand overlap check, not masked by the single-part feasibility probe limit.
Assert.Throws<InvalidOperationException>(() => NestJobValidator.ValidateCandidate(
new PlateCandidate(new[] { new NestJobPlacement("a", 0, 0, 0, 0), new NestJobPlacement("a", 1, 1, 1, 0) }),
job.Plates[0], new Dictionary<string, int> { ["a"] = 2 }, job.Parts.ToDictionary(p => p.Id)));
Assert.Throws<InvalidOperationException>(() =>
NestJobValidator.ValidateCandidate(
new PlateCandidate(
new[]
{
new NestJobPlacement("a", 0, 0, 0, 0),
new NestJobPlacement("a", 1, 1, 1, 0),
}
),
job.Plates[0],
new Dictionary<string, int> { ["a"] = 2 },
job.Parts.ToDictionary(p => p.Id)
)
);
}
[Fact]
@@ -115,33 +180,52 @@ public class StockLadderTests
{
var part = Rectangle("a", 1);
var stock = new NestPlateStock("s", new Size(10, 10));
var sheet = new NestJobPlateResult(0, stock, new[] { new NestJobPlacement("a", 0, 0, 0, 0) });
NestJob Job(double rate, double min) => new(new[] { part }, new[] { stock },
new NestJobOptions(salvageRate: rate, minimumSalvageDimension: min));
var sheet = new NestJobPlateResult(
0,
stock,
new[] { new NestJobPlacement("a", 0, 0, 0, 0) }
);
NestJob Job(double rate, double min) =>
new(
new[] { part },
new[] { stock },
new NestJobOptions(salvageRate: rate, minimumSalvageDimension: min)
);
Assert.Equal(100, StockLadderNestingEngine.EstimateNetArea(Job(0.5, 0), sheet));
Assert.Equal(100, StockLadderNestingEngine.EstimateNetArea(Job(0.5, 7), sheet));
Assert.Equal(70, StockLadderNestingEngine.EstimateNetArea(Job(0.5, 5), sheet), 6);
Assert.Throws<ArgumentOutOfRangeException>(() => new NestJobOptions(salvageRate: double.NaN));
Assert.Throws<ArgumentOutOfRangeException>(() =>
new NestJobOptions(salvageRate: double.NaN)
);
Assert.Throws<ArgumentOutOfRangeException>(() => new NestJobOptions(salvageRate: 1.1));
}
[Fact]
public void FailedRepackRetainsAllDemandAndFiniteStockAccounting()
{
var job = new NestJob(new[] { Rectangle("a", 5) }, new[]
{
new NestPlateStock("small", new Size(10, 10), 2),
new NestPlateStock("large", new Size(10, 18), 1)
});
var job = new NestJob(
new[] { Rectangle("a", 5) },
new[]
{
new NestPlateStock("small", new Size(10, 10), 2),
new NestPlateStock("large", new Size(10, 18), 1),
}
);
var fullDemandLargeTrials = 0;
var engine = new StockLadderNestingEngine(() => new CallbackNester(request =>
{
var quantity = Assert.Single(request.Parts).Quantity;
if (request.Stock.Id == "large" && quantity == 5) fullDemandLargeTrials++;
// Deliberately fail to reproduce the fifth piece on the cheaper merged sheet.
return new PlateCandidate(Enumerable.Range(0, System.Math.Min(quantity, 4))
.Select(i => new NestJobPlacement("a", i, i % 2 * 4, i / 2 * 4, 0)));
}));
var engine = new StockLadderNestingEngine(() =>
new CallbackNester(request =>
{
var quantity = Assert.Single(request.Parts).Quantity;
if (request.Stock.Id == "large" && quantity == 5)
fullDemandLargeTrials++;
// Deliberately fail to reproduce the fifth piece on the cheaper merged sheet.
return new PlateCandidate(
Enumerable
.Range(0, System.Math.Min(quantity, 4))
.Select(i => new NestJobPlacement("a", i, i % 2 * 4, i / 2 * 4, 0))
);
})
);
var result = engine.Solve(job);
Assert.True(fullDemandLargeTrials >= 2); // Construction AND equivalent-demand repack ran.
Assert.Equal(NestJobStatus.Complete, result.Status);
@@ -166,7 +250,9 @@ public class StockLadderTests
var job = new NestJob(new[] { part }, new[] { new NestPlateStock("s", new Size(10, 10)) });
if (reject)
{
var error = Assert.Throws<ArgumentException>(() => new StockLadderNestingEngine().Solve(job));
var error = Assert.Throws<ArgumentException>(() =>
new StockLadderNestingEngine().Solve(job)
);
Assert.Contains("Open geometry leaves the closed material region", error.Message);
}
else
@@ -183,12 +269,21 @@ public class StockLadderTests
var remaining = job.Parts.ToDictionary(p => p.Id, p => p.Quantity);
foreach (var sheet in result.Plates)
{
NestJobValidator.ValidateCandidate(new PlateCandidate(sheet.Placements), sheet.Stock, remaining, parts);
foreach (var pose in sheet.Placements) remaining[pose.PartId]--;
NestJobValidator.ValidateCandidate(
new PlateCandidate(sheet.Placements),
sheet.Stock,
remaining,
parts
);
foreach (var pose in sheet.Placements)
remaining[pose.PartId]--;
}
foreach (var part in job.Parts)
{
var poses = result.Plates.SelectMany(p => p.Placements).Where(p => p.PartId == part.Id).ToList();
var poses = result
.Plates.SelectMany(p => p.Placements)
.Where(p => p.PartId == part.Id)
.ToList();
Assert.Equal(Enumerable.Range(0, poses.Count), poses.Select(p => p.InstanceIndex));
var fulfillment = result.Fulfillment.Single(p => p.PartId == part.Id);
Assert.Equal(part.Quantity, fulfillment.Placed + fulfillment.Unplaced);
@@ -204,9 +299,13 @@ public class StockLadderTests
}
}
private sealed class CallbackNester(Func<PlatePlacementRequest, PlateCandidate> callback) : IPlateNester
private sealed class CallbackNester(Func<PlatePlacementRequest, PlateCandidate> callback)
: IPlateNester
{
public PlateCandidate Place(PlatePlacementRequest request, IProgress<NestJobProgress>? progress = null,
CancellationToken token = default) => callback(request);
public PlateCandidate Place(
PlatePlacementRequest request,
IProgress<NestJobProgress>? progress = null,
CancellationToken token = default
) => callback(request);
}
}