Files
OpenNest/OpenNest.Tests/IO/CadImporterTests.cs
T
aj bc21382c5d fix(io): give re-imported unchanged DXFs the same entity ids
Entity ids came from a process-random sequence, so importing the same
DXF twice produced different source-entity ids and therefore a
different drawing library package (different SHA-256), and an importer
could not tell an unchanged file from a changed one.

CadImporter.Import now derives each entity id from the file's SHA-256
and the entity's position in the import. An unchanged file gives the
same ids and the same package bytes; any change to the file gives
entirely new ids, so suppression, roles and edge selections keyed by id
never move to a different entity.
2026-10-24 09:12:48 -04:00

207 lines
6.9 KiB
C#

using System.IO;
using System.Linq;
using OpenNest.IO;
using Xunit;
namespace OpenNest.Tests.IO
{
public class CadImporterTests
{
private static string TestDxf => Path.Combine("Bending", "TestData", "4526 A14 PT11.dxf");
[Fact]
public void Import_LoadsEntitiesAndDetectsBends()
{
var result = CadImporter.Import(TestDxf);
Assert.NotNull(result);
Assert.NotEmpty(result.Entities);
Assert.NotNull(result.Bends);
Assert.NotNull(result.Bounds);
Assert.Equal(TestDxf, result.SourcePath);
Assert.Equal("4526 A14 PT11", result.Name);
}
[Fact]
public void Import_WhenDetectBendsFalse_ReturnsEmptyBends()
{
var result = CadImporter.Import(TestDxf, new CadImportOptions { DetectBends = false });
Assert.Empty(result.Bends);
}
[Fact]
public void Import_WhenNameOverrideProvided_UsesOverride()
{
var result = CadImporter.Import(TestDxf, new CadImportOptions { Name = "custom" });
Assert.Equal("custom", result.Name);
}
[Fact]
public void Import_WhenNamedDetectorDoesNotExist_ReturnsEmptyBends()
{
// Exercises the named-detector branch: when BendDetectorName doesn't
// match any registered detector, bends should be an empty list
// (not a crash, and no fall-through to auto-detect).
var result = CadImporter.Import(
TestDxf,
new CadImportOptions { BendDetectorName = "__nonexistent__" }
);
Assert.Empty(result.Bends);
}
[Fact]
public void BuildDrawing_ProducesDrawingWithProgramAndMetadata()
{
var result = CadImporter.Import(TestDxf);
var drawing = CadImporter.BuildDrawing(
result,
result.Entities,
result.Bends,
quantity: 5,
customer: "ACME",
editedProgram: null
);
Assert.NotNull(drawing);
Assert.Equal("4526 A14 PT11", drawing.Name);
Assert.Equal("ACME", drawing.Customer);
Assert.Equal(5, drawing.Quantity.Required);
Assert.Equal(TestDxf, drawing.Source.Path);
Assert.NotNull(drawing.Program);
Assert.NotEmpty(drawing.Program.Codes);
Assert.NotNull(drawing.SourceEntities);
Assert.NotEmpty(drawing.SourceEntities);
}
[Fact]
public void BuildDrawing_ExtractsFirstRapidAsSourceOffset()
{
var result = CadImporter.Import(TestDxf);
var drawing = CadImporter.BuildDrawing(
result,
result.Entities,
result.Bends,
quantity: 1,
customer: null,
editedProgram: null
);
Assert.NotNull(drawing.Source.Offset);
// After offset extraction, the program's first rapid must start at origin.
var firstRapid = (OpenNest.CNC.RapidMove)drawing.Program.Codes[0];
Assert.Equal(0, firstRapid.EndPoint.X, 6);
Assert.Equal(0, firstRapid.EndPoint.Y, 6);
}
[Fact]
public void BuildDrawing_WhenEntityHidden_TracksSuppressedId()
{
var result = CadImporter.Import(TestDxf);
// Suppress the first non-bend-source entity
var bendSources = result
.Bends.Where(b => b.SourceEntity != null)
.Select(b => b.SourceEntity)
.ToHashSet();
var hidden = result.Entities.First(e => !bendSources.Contains(e));
hidden.IsVisible = false;
var drawing = CadImporter.BuildDrawing(
result,
result.Entities,
result.Bends,
quantity: 1,
customer: null,
editedProgram: null
);
Assert.Contains(hidden.Id, drawing.SuppressedEntityIds);
}
[Fact]
public void BuildDrawing_WhenEditedProgramProvided_UsesEditedProgram()
{
var result = CadImporter.Import(TestDxf);
var edited = new OpenNest.CNC.Program();
edited.MoveTo(new OpenNest.Geometry.Vector(0, 0));
var drawing = CadImporter.BuildDrawing(
result,
result.Entities,
result.Bends,
quantity: 1,
customer: null,
editedProgram: edited
);
Assert.Same(edited, drawing.Program);
}
[Fact]
public void ImportDrawing_ComposesImportAndBuild()
{
var drawing = CadImporter.ImportDrawing(
TestDxf,
new CadImportOptions { Quantity = 3, Customer = "ACME" }
);
Assert.NotNull(drawing);
Assert.Equal("4526 A14 PT11", drawing.Name);
Assert.Equal(3, drawing.Quantity.Required);
Assert.Equal("ACME", drawing.Customer);
Assert.NotNull(drawing.Program);
Assert.NotNull(drawing.SourceEntities);
}
[Fact]
public void ImportDrawing_UnchangedFile_GivesTheSameLibraryPackage()
{
var first = CadImporter.ImportDrawing(TestDxf);
var second = CadImporter.ImportDrawing(TestDxf);
Assert.Equal(
first.SourceEntities.Select(e => e.Id),
second.SourceEntities.Select(e => e.Id)
);
Assert.Equal(
DrawingPackage.Write(first, Units.Inches),
DrawingPackage.Write(second, Units.Inches)
);
}
[Fact]
public void Import_EntityIdsAreUniqueAndNonempty()
{
var ids = CadImporter.Import(TestDxf).Entities.Select(e => e.Id).ToList();
Assert.DoesNotContain(System.Guid.Empty, ids);
Assert.Equal(ids.Count, ids.Distinct().Count());
}
[Fact]
public void Import_ChangedFile_ReusesNoEntityIds()
{
// Ids key suppression, roles and edge selections, so a changed file must not
// hand an old id to whatever entity now sits at that position.
var changed = Path.Combine(Path.GetTempPath(), $"cadimporter-changed-{System.Guid.NewGuid():N}.dxf");
try
{
File.WriteAllText(changed, "999\nchanged\n" + File.ReadAllText(TestDxf));
var original = CadImporter.Import(TestDxf).Entities.Select(e => e.Id).ToHashSet();
var edited = CadImporter.Import(changed).Entities.Select(e => e.Id).ToList();
Assert.NotEmpty(edited);
Assert.DoesNotContain(edited, original.Contains);
}
finally
{
File.Delete(changed);
}
}
}
}