test(io): write test archives through NestArchiveJson

Nine persistence test classes turned a nest or a single drawing into
archive bytes with their own Write helpers, and one kept a private copy
of NestJson. They now use NestArchiveJson.ArchiveBytes and NestJson.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ajandClaude Opus 5.5 committed 2026-11-01 11:23:01 -05:00
1 parent a72e9eb900
commit 9d6d5fa4b2
10 files changed
+55 -120

No files matched your search

+4 -13
View File
@@ -27,7 +27,7 @@ public class BreakoffPersistenceTests
var drawing = WithBreakoff();
var original = drawing.Breakoffs[0];
var (restored, warnings) = RoundTrip(Write(drawing));
var (restored, warnings) = RoundTrip(ArchiveBytes(drawing));
Assert.Empty(warnings);
var breakoff = Assert.Single(Assert.Single(restored.Drawings).Breakoffs);
@@ -44,7 +44,7 @@ public class BreakoffPersistenceTests
drawing.Program.Codes.Add(new OpenNest.CNC.LinearMove(1, 0) { Layer = OpenNest.CNC.LayerType.Break });
drawing.Program.Codes.Add(new OpenNest.CNC.ArcMove(1, 1, 0, 1) { Layer = OpenNest.CNC.LayerType.Break });
var (restored, _) = RoundTrip(Write(drawing));
var (restored, _) = RoundTrip(ArchiveBytes(drawing));
var moves = Assert.Single(restored.Drawings).Program.Codes.OfType<OpenNest.CNC.Motion>()
.Where(m => m is OpenNest.CNC.LinearMove { Layer: OpenNest.CNC.LayerType.Break }
@@ -55,7 +55,7 @@ public class BreakoffPersistenceTests
[Fact]
public void NoBreakoffs_AreOmittedFromTheFile()
{
var archive = Write(DrawingPackageFixture.Build());
var archive = ArchiveBytes(DrawingPackageFixture.Build());
Assert.False(NestJson(archive)["drawings"]![0]!.AsObject().ContainsKey("breakoffs"));
Assert.Empty(Assert.Single(RoundTrip(archive).Nest.Drawings).Breakoffs);
@@ -64,7 +64,7 @@ public class BreakoffPersistenceTests
[Fact]
public void UnreadableBreakoff_IsDroppedWithAWarning_AndTheDrawingLoads()
{
var archive = Write(WithBreakoff());
var archive = ArchiveBytes(WithBreakoff());
var json = NestJson(archive);
json["drawings"]![0]!["breakoffs"]![0]!["tabWidth"] = 0;
@@ -73,13 +73,4 @@ public class BreakoffPersistenceTests
Assert.Empty(Assert.Single(restored.Drawings).Breakoffs);
Assert.Contains(warnings, w => w.Contains("breakoff 1 could not be read"));
}
private static byte[] Write(Drawing drawing)
{
var nest = new Nest(drawing.Name) { Units = Units.Inches };
nest.Drawings.Add(drawing);
using var stream = new MemoryStream();
Assert.True(new NestWriter(nest).Write(stream));
return stream.ToArray();
}
}
+5 -13
View File
@@ -31,7 +31,7 @@ public class CutPinPersistenceTests
[Fact]
public void Pins_RoundTripEveryFieldInOrder()
{
var (restored, warnings) = RoundTrip(Write(PinnedDrawing()));
var (restored, warnings) = RoundTrip(ArchiveBytes(PinnedDrawing()));
Assert.Empty(warnings);
var pins = Assert.Single(restored.Drawings).CutPins;
@@ -60,7 +60,7 @@ public class CutPinPersistenceTests
[Fact]
public void NoPins_AreOmittedFromTheFile()
{
var archive = Write(DrawingPackageFixture.Build());
var archive = ArchiveBytes(DrawingPackageFixture.Build());
var (restored, warnings) = RoundTrip(archive);
@@ -86,7 +86,7 @@ public class CutPinPersistenceTests
[MemberData(nameof(MalformedPins))]
public void MalformedPin_IsSkippedWithAWarningAndTheRestLoad(string pinJson)
{
var archive = ReplaceCutPins(Write(DrawingPackageFixture.Build()),
var archive = ReplaceCutPins(ArchiveBytes(DrawingPackageFixture.Build()),
JsonNode.Parse($"[{ValidPin},{pinJson}]"));
var (restored, warnings) = RoundTrip(archive);
@@ -103,7 +103,8 @@ public class CutPinPersistenceTests
[InlineData("\"pins\"")]
public void PinsThatAreNotAList_LoadNoPinsWithAWarning(string value)
{
var archive = ReplaceCutPins(Write(DrawingPackageFixture.Build()), JsonNode.Parse(value));
var archive = ReplaceCutPins(
ArchiveBytes(DrawingPackageFixture.Build()), JsonNode.Parse(value));
var (restored, warnings) = RoundTrip(archive);
@@ -185,15 +186,6 @@ public class CutPinPersistenceTests
return stream.ToArray();
}
private static byte[] Write(Drawing drawing)
{
var nest = new Nest(drawing.Name) { Units = Units.Inches };
nest.Drawings.Add(drawing);
using var stream = new MemoryStream();
Assert.True(new NestWriter(nest).Write(stream));
return stream.ToArray();
}
private static byte[] ReplaceCutPins(byte[] archive, JsonNode? cutPins)
{
var json = NestJson(archive);
+5 -14
View File
@@ -16,7 +16,7 @@ public class DimensionPersistenceTests
[Fact]
public void Dimensions_RoundTripEveryField()
{
var (restored, warnings) = RoundTrip(Write(WithDimensions()));
var (restored, warnings) = RoundTrip(ArchiveBytes(WithDimensions()));
Assert.Empty(warnings);
var dimensions = Assert.Single(restored.Drawings).Dimensions;
@@ -30,7 +30,7 @@ public class DimensionPersistenceTests
[Fact]
public void NoDimensions_AreOmittedFromTheFile()
{
var archive = Write(DrawingPackageFixture.Build());
var archive = ArchiveBytes(DrawingPackageFixture.Build());
Assert.False(NestJson(archive)["drawings"]![0]!.AsObject().ContainsKey("dimensions"));
Assert.Empty(Assert.Single(RoundTrip(archive).Nest.Drawings).Dimensions);
@@ -42,7 +42,7 @@ public class DimensionPersistenceTests
[InlineData("offset", 0.0)]
public void UnreadableDimension_IsDroppedWithAWarning_AndTheRestLoad(string field, object value)
{
var archive = Write(WithDimensions());
var archive = ArchiveBytes(WithDimensions());
var json = NestJson(archive);
json["drawings"]![0]!["dimensions"]![0]![field] = value is string text ? text : (double)value;
@@ -72,7 +72,7 @@ public class DimensionPersistenceTests
[Fact]
public void RadialDimensions_RoundTripTheirCurveAndAngle_AndOverallOnesOmitThem()
{
var archive = Write(WithRadial());
var archive = ArchiveBytes(WithRadial());
var json = NestJson(archive)["drawings"]![0]!["dimensions"]!;
Assert.False(json[0]!.AsObject().ContainsKey("entityId"));
@@ -92,7 +92,7 @@ public class DimensionPersistenceTests
[InlineData("angle")]
public void RadialDimensionWithoutItsCurveOrAngle_IsDroppedWithAWarning(string field)
{
var archive = Write(WithRadial());
var archive = ArchiveBytes(WithRadial());
var json = NestJson(archive);
json["drawings"]![0]!["dimensions"]![1]!.AsObject().Remove(field);
@@ -102,13 +102,4 @@ public class DimensionPersistenceTests
Assert.Equal(DimensionKind.OverallHorizontal, dimension.Kind);
Assert.Contains(warnings, w => w.Contains("dimension 2 could not be read"));
}
private static byte[] Write(Drawing drawing)
{
var nest = new Nest(drawing.Name) { Units = Units.Inches };
nest.Drawings.Add(drawing);
using var stream = new MemoryStream();
Assert.True(new NestWriter(nest).Write(stream));
return stream.ToArray();
}
}
@@ -2,6 +2,7 @@ using System.IO.Compression;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json.Nodes;
using static OpenNest.IO.Tests.NestArchiveJson;
namespace OpenNest.IO.Tests;
@@ -36,7 +37,7 @@ public class DrawingLibraryLinkPersistenceTests
{
var drawing = DrawingPackageFixture.Build();
var json = NestJson(Write(drawing));
var json = NestJson(ArchiveBytes(drawing));
var (restored, warnings) = RoundTrip(drawing);
Assert.False(json["drawings"]![0]!.AsObject().ContainsKey("library"));
@@ -65,7 +66,7 @@ public class DrawingLibraryLinkPersistenceTests
public void MalformedLink_LoadsTheDrawingWithoutItAndWarns(string linkJson)
{
var drawing = DrawingPackageFixture.Build();
var archive = ReplaceLibrary(Write(drawing), JsonNode.Parse(linkJson));
var archive = ReplaceLibrary(ArchiveBytes(drawing), JsonNode.Parse(linkJson));
var reader = new NestReader(new MemoryStream(archive));
var restored = reader.Read();
@@ -81,7 +82,7 @@ public class DrawingLibraryLinkPersistenceTests
[Fact]
public void NullLink_ReadsAsNoLinkWithoutWarning()
{
var archive = ReplaceLibrary(Write(DrawingPackageFixture.Build()), null);
var archive = ReplaceLibrary(ArchiveBytes(DrawingPackageFixture.Build()), null);
var reader = new NestReader(new MemoryStream(archive));
var restored = reader.Read();
@@ -105,28 +106,12 @@ public class DrawingLibraryLinkPersistenceTests
Assert.Same(link, drawing.Library);
}
private static byte[] Write(Drawing drawing)
{
var nest = new Nest(drawing.Name) { Units = Units.Inches };
nest.Drawings.Add(drawing);
using var stream = new MemoryStream();
Assert.True(new NestWriter(nest).Write(stream));
return stream.ToArray();
}
private static (Nest Nest, IReadOnlyList<string> Warnings) RoundTrip(Drawing drawing)
{
var reader = new NestReader(new MemoryStream(Write(drawing)));
var reader = new NestReader(new MemoryStream(ArchiveBytes(drawing)));
return (reader.Read(), reader.Warnings);
}
private static JsonObject NestJson(byte[] archive)
{
using var zip = new ZipArchive(new MemoryStream(archive), ZipArchiveMode.Read);
using var reader = new StreamReader(zip.GetEntry("nest.json")!.Open(), Encoding.UTF8);
return JsonNode.Parse(reader.ReadToEnd())!.AsObject();
}
private static byte[] ReplaceLibrary(byte[] archive, JsonNode? library)
{
var json = NestJson(archive);
@@ -23,13 +23,6 @@ public class ExpectedRemnantPersistenceTests
return nest;
}
private static byte[] Write(Nest nest)
{
using var stream = new MemoryStream();
Assert.True(new NestWriter(nest).Write(stream));
return stream.ToArray();
}
private static JsonNode Remnant(byte[] archive) => NestJson(archive)["plates"]![0]!["expectedRemnant"];
private static (double, double, string) Shape(JsonNode remnant) =>
@@ -38,7 +31,7 @@ public class ExpectedRemnantPersistenceTests
[Fact]
public void Offcut_IsWrittenWithWidthTheShortSide()
{
Assert.Equal((48.0, 86.0, "offcut"), Shape(Remnant(Write(NestWithPart()))));
Assert.Equal((48.0, 86.0, "offcut"), Shape(Remnant(ArchiveBytes(NestWithPart()))));
}
[Fact]
@@ -48,7 +41,7 @@ public class ExpectedRemnantPersistenceTests
nest.Plates[0].CutOffs.Add(new CutOff(new Vector(60, 0), CutOffAxis.Vertical));
nest.Plates[0].RegenerateCutOffs(new CutOffSettings());
Assert.Equal((36.0, 48.0, "cutoff"), Shape(Remnant(Write(nest))));
Assert.Equal((36.0, 48.0, "cutoff"), Shape(Remnant(ArchiveBytes(nest))));
}
[Fact]
@@ -57,7 +50,8 @@ public class ExpectedRemnantPersistenceTests
var nest = NestWithPart();
nest.MinimumRemnantDimension = 50;
Assert.False(NestJson(Write(nest))["plates"]![0]!.AsObject().ContainsKey("expectedRemnant"));
var plate = NestJson(ArchiveBytes(nest))["plates"]![0]!.AsObject();
Assert.False(plate.ContainsKey("expectedRemnant"));
}
[Fact]
@@ -65,7 +59,7 @@ public class ExpectedRemnantPersistenceTests
{
var nest = NestWithPart();
nest.MinimumRemnantDimension = 6;
var archive = Write(nest);
var archive = ArchiveBytes(nest);
Assert.Equal(6, RoundTrip(archive).Nest.MinimumRemnantDimension);
@@ -81,23 +75,23 @@ public class ExpectedRemnantPersistenceTests
[InlineData("[1, 2]")]
public void StoredValue_IsIgnoredOnRead_AndRecomputedOnSave(string stored)
{
var archive = Write(NestWithPart());
var archive = ArchiveBytes(NestWithPart());
var json = NestJson(archive);
json["plates"]![0]!["expectedRemnant"] = JsonNode.Parse(stored);
var (reopened, _) = RoundTrip(ReplaceNestJson(archive, json));
Assert.Single(reopened.Plates);
Assert.Equal((48.0, 86.0, "offcut"), Shape(Remnant(Write(reopened))));
Assert.Equal((48.0, 86.0, "offcut"), Shape(Remnant(ArchiveBytes(reopened))));
}
[Fact]
public void PartMove_IsReflectedOnTheNextSave()
{
var (reopened, _) = RoundTrip(Write(NestWithPart()));
var (reopened, _) = RoundTrip(ArchiveBytes(NestWithPart()));
reopened.Plates[0].Parts[0].Location = new Vector(40, 0);
// Strips: 40 x 48 left of the part, 46 x 48 right of it, 96 x 38 above it.
Assert.Equal((38.0, 96.0, "offcut"), Shape(Remnant(Write(reopened))));
Assert.Equal((38.0, 96.0, "offcut"), Shape(Remnant(ArchiveBytes(reopened))));
}
}
+11
View File
@@ -24,6 +24,17 @@ internal static class NestArchiveJson
return stream;
}
/// <summary>The archive bytes <see cref="NestWriter"/> writes for the nest.</summary>
public static byte[] ArchiveBytes(Nest nest) => Write(nest).ToArray();
/// <summary>The archive bytes of an inch nest that holds only the drawing.</summary>
public static byte[] ArchiveBytes(Drawing drawing)
{
var nest = new Nest(drawing.Name) { Units = Units.Inches };
nest.Drawings.Add(drawing);
return ArchiveBytes(nest);
}
/// <summary>The archive's nest.json, leaving <paramref name="zip"/> ready to read again.</summary>
public static JsonObject NestJson(MemoryStream zip)
{
@@ -6,6 +6,7 @@ using OpenNest.CNC.CuttingStrategy;
using OpenNest.Converters;
using OpenNest.Engine.CuttingPlanning;
using OpenNest.Geometry;
using static OpenNest.IO.Tests.NestArchiveJson;
namespace OpenNest.IO.Tests;
@@ -24,7 +25,7 @@ public class PartCuttingParametersPersistenceTests
plate.Parts.Add(leaded);
plate.Parts.Add(new Part(drawing, new Vector(6, 1)));
var bytes = Write(nest);
var bytes = ArchiveBytes(nest);
var reader = new NestReader(new MemoryStream(bytes));
var parts = Assert.Single(reader.Read().Plates).Parts;
@@ -53,7 +54,7 @@ public class PartCuttingParametersPersistenceTests
Assert.All(plate.Parts, part => Assert.NotNull(part.CuttingParameters));
var expected = plate.Parts.Select(p => CuttingParametersSerializer.Serialize(p.CuttingParameters)).ToArray();
var reader = new NestReader(new MemoryStream(Write(nest)));
var reader = new NestReader(new MemoryStream(ArchiveBytes(nest)));
var parts = Assert.Single(reader.Read().Plates).Parts;
Assert.Empty(reader.Warnings);
@@ -70,7 +71,7 @@ public class PartCuttingParametersPersistenceTests
var part = new Part(drawing, new Vector(1, 1));
part.ApplyLeadIns(Parameters(0.3), Vector.Zero);
nest.CreatePlate().Parts.Add(part);
var legacy = RemovePartSettings(Write(nest));
var legacy = RemovePartSettings(ArchiveBytes(nest));
var reader = new NestReader(new MemoryStream(legacy));
var copy = Assert.Single(Assert.Single(reader.Read().Plates).Parts);
@@ -89,13 +90,6 @@ public class PartCuttingParametersPersistenceTests
PierceClearance = 0.05,
};
private static byte[] Write(Nest nest)
{
using var stream = new MemoryStream();
Assert.True(new NestWriter(nest).Write(stream));
return stream.ToArray();
}
private static JsonArray PartsJson(byte[] archive)
{
using var zip = new ZipArchive(new MemoryStream(archive), ZipArchiveMode.Read);
@@ -16,13 +16,6 @@ public class SheetOutlinePersistenceTests
new[] { new Vector(0, 0), new Vector(96, 0), new Vector(96, 24), new Vector(48, 24), new Vector(48, 48), new Vector(0, 48) },
new[] { new[] { new Vector(10, 10), new Vector(14, 10), new Vector(14, 14), new Vector(10, 14) } });
private static byte[] Write(Nest nest)
{
using var stream = new MemoryStream();
Assert.True(new NestWriter(nest).Write(stream));
return stream.ToArray();
}
private static Nest OutlinedNest()
{
var nest = new Nest("outlined");
@@ -33,7 +26,7 @@ public class SheetOutlinePersistenceTests
[Fact]
public void OutlineRoundTripsWithObstaclesAndKeepsAnEmptyPlate()
{
var (reopened, warnings) = RoundTrip(Write(OutlinedNest()));
var (reopened, warnings) = RoundTrip(ArchiveBytes(OutlinedNest()));
Assert.Empty(warnings);
var plate = Assert.Single(reopened.Plates);
@@ -47,7 +40,7 @@ public class SheetOutlinePersistenceTests
[Fact]
public void FileShapeIsPointsAndObstaclesInPlateCoordinates()
{
var plates = NestJson(Write(OutlinedNest()))["plates"]!.AsArray();
var plates = NestJson(ArchiveBytes(OutlinedNest()))["plates"]!.AsArray();
var outline = Assert.Single(plates)!["outline"]!.AsObject();
var points = outline["points"]!.AsArray();
@@ -66,7 +59,7 @@ public class SheetOutlinePersistenceTests
var nest = new Nest("plain");
nest.Plates.Add(new Plate(48, 96));
nest.Plates[0].CutOffs.Add(new CutOff(new Vector(40, 0), CutOffAxis.Vertical));
var archive = Write(nest);
var archive = ArchiveBytes(nest);
var json = NestJson(archive);
Assert.Null(Assert.Single(json["plates"]!.AsArray())!["outline"]);
@@ -86,7 +79,7 @@ public class SheetOutlinePersistenceTests
[InlineData("{\"points\": [{\"x\": 0, \"y\": 0}, {\"x\": 10, \"y\": 0}, {\"x\": 10, \"y\": 10}], \"obstacles\": 3}", "'obstacles' is not an array")]
public void MalformedOutlineLoadsARectangleWithAWarning(string value, string problem)
{
var archive = Write(OutlinedNest());
var archive = ArchiveBytes(OutlinedNest());
var json = NestJson(archive);
var plate = Assert.Single(json["plates"]!.AsArray())!.AsObject();
plate["outline"] = JsonNode.Parse(value);
@@ -23,17 +23,10 @@ public class StockItemCodesPersistenceTests
return nest;
}
private static byte[] Write(Nest nest)
{
using var stream = new MemoryStream();
Assert.True(new NestWriter(nest).Write(stream));
return stream.ToArray();
}
[Fact]
public void CodesRoundTripInSheetOrder()
{
var (reopened, _) = RoundTrip(Write(NestWithPlate(3, "SHEET-456", "SHEET-456")));
var (reopened, _) = RoundTrip(ArchiveBytes(NestWithPlate(3, "SHEET-456", "SHEET-456")));
var plate = Assert.Single(reopened.Plates);
Assert.Equal(3, plate.Quantity);
@@ -44,7 +37,7 @@ public class StockItemCodesPersistenceTests
[Fact]
public void UncodedPlate_WritesNoKey()
{
var json = NestJson(Write(NestWithPlate(1)));
var json = NestJson(ArchiveBytes(NestWithPlate(1)));
Assert.False(json["plates"]![0]!.AsObject().ContainsKey("stockItemCodes"));
}
@@ -52,7 +45,7 @@ public class StockItemCodesPersistenceTests
[Fact]
public void OldFileWithoutTheKey_LoadsWithNoCodes()
{
var archive = Write(NestWithPlate(1, "DROP-1182"));
var archive = ArchiveBytes(NestWithPlate(1, "DROP-1182"));
var json = NestJson(archive);
Assert.True(json["plates"]![0]!.AsObject().Remove("stockItemCodes"));
@@ -68,7 +61,7 @@ public class StockItemCodesPersistenceTests
[InlineData("[\"A\", \"B\", \"C\", \"D\"]", new[] { "A", "B", "C" })]
public void MalformedCodes_ReadLeniently(string value, string[] expected)
{
var archive = Write(NestWithPlate(3, "X"));
var archive = ArchiveBytes(NestWithPlate(3, "X"));
var json = NestJson(archive);
json["plates"]![0]!["stockItemCodes"] = JsonNode.Parse(value);
@@ -29,7 +29,7 @@ public class TabbedCutoutPersistenceTests
var drawing = WithCutout();
var original = drawing.TabbedCutouts[0];
var (restored, warnings) = RoundTrip(Write(drawing));
var (restored, warnings) = RoundTrip(ArchiveBytes(drawing));
Assert.Empty(warnings);
var cutout = Assert.Single(Assert.Single(restored.Drawings).TabbedCutouts);
@@ -51,7 +51,7 @@ public class TabbedCutoutPersistenceTests
entities, new List<Bend>(), 1, null, null, null,
[new TabbedCutout { EdgeIds = hole.Select(e => e.Id).ToList() }]);
var (restored, warnings) = RoundTrip(Write(drawing));
var (restored, warnings) = RoundTrip(ArchiveBytes(drawing));
Assert.Empty(warnings);
var reread = Assert.Single(restored.Drawings);
@@ -65,7 +65,7 @@ public class TabbedCutoutPersistenceTests
[Fact]
public void NoTabbedCutouts_AreOmittedFromTheFile()
{
var archive = Write(DrawingPackageFixture.Build());
var archive = ArchiveBytes(DrawingPackageFixture.Build());
Assert.False(NestJson(archive)["drawings"]![0]!.AsObject().ContainsKey("tabbedCutouts"));
Assert.Empty(Assert.Single(RoundTrip(archive).Nest.Drawings).TabbedCutouts);
@@ -74,7 +74,7 @@ public class TabbedCutoutPersistenceTests
[Fact]
public void UnreadableTabbedCutout_IsDroppedWithAWarning_AndTheDrawingLoads()
{
var archive = Write(WithCutout());
var archive = ArchiveBytes(WithCutout());
var json = NestJson(archive);
json["drawings"]![0]!["tabbedCutouts"]![0]!["tabWidth"] = 0;
@@ -98,13 +98,4 @@ public class TabbedCutoutPersistenceTests
}
return length;
}
private static byte[] Write(Drawing drawing)
{
var nest = new Nest(drawing.Name) { Units = Units.Inches };
nest.Drawings.Add(drawing);
using var stream = new MemoryStream();
Assert.True(new NestWriter(nest).Write(stream));
return stream.ToArray();
}
}