diff --git a/lib/shared/autorouter-diagnostics.ts b/lib/shared/autorouter-diagnostics.ts index b077a3c5b..1206b935b 100644 --- a/lib/shared/autorouter-diagnostics.ts +++ b/lib/shared/autorouter-diagnostics.ts @@ -289,8 +289,10 @@ export class AutorouterDiagnostics { if (this.options.enabled && !this.hasWrittenPlacementSnapshot) { const placementCircuitJson = this.getCurrentCircuitJson().filter( (element) => !this.isRouteElement(element), - ) - this.writePngSnapshot("placement-unrouted.png", placementCircuitJson) + ) as AnyCircuitElement[] + this.writePngSnapshot("placement-unrouted.png", placementCircuitJson, { + shouldDrawRatsNest: true, + }) this.hasWrittenPlacementSnapshot = true } @@ -650,9 +652,13 @@ export class AutorouterDiagnostics { return filePath } - private writePngSnapshot(fileName: string, circuitJson: CircuitJson) { + private writePngSnapshot( + fileName: string, + circuitJson: CircuitJson, + options?: { shouldDrawRatsNest?: boolean }, + ) { try { - const pcbSvg = convertCircuitJsonToPcbSvg(circuitJson) + const pcbSvg = convertCircuitJsonToPcbSvg(circuitJson, options) const png = convertSvgToPngBuffer(pcbSvg) const debugDir = path.resolve(this.options.debugDir ?? DEFAULT_DEBUG_DIR) fs.mkdirSync(debugDir, { recursive: true }) diff --git a/tests/shared/__snapshots__/autorouter-start-rats-nest.snap.svg b/tests/shared/__snapshots__/autorouter-start-rats-nest.snap.svg new file mode 100644 index 000000000..628e8392c --- /dev/null +++ b/tests/shared/__snapshots__/autorouter-start-rats-nest.snap.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/shared/autorouter-diagnostics.test.ts b/tests/shared/autorouter-diagnostics.test.ts index f71cdb71f..3ffc6ba73 100644 --- a/tests/shared/autorouter-diagnostics.test.ts +++ b/tests/shared/autorouter-diagnostics.test.ts @@ -4,6 +4,7 @@ import fs from "node:fs" import os from "node:os" import path from "node:path" import type { CircuitJson } from "circuit-json" +import { convertCircuitJsonToPcbSvg } from "circuit-to-svg" import { AutorouterDiagnostics, AutorouterPhaseTimeoutError, @@ -11,6 +12,8 @@ import { parseAutorouterPhaseName, parseAutorouterTimeout, } from "lib/shared/autorouter-diagnostics" +import { convertSvgToPngBuffer } from "lib/shared/convert-svg-to-png" +import "bun-match-svg" class FakeRootCircuit extends EventEmitter { dbToArrayCallCount = 0 @@ -137,7 +140,139 @@ describe("autorouter diagnostics", () => { test("logs phase start/end and writes SRJ and PNG artifacts", async () => { const debugDir = makeTempDir() const logs: string[] = [] - const root = new FakeRootCircuit() + const circuitJson: CircuitJson = [ + { + type: "pcb_board", + pcb_board_id: "board_0", + center: { x: 0, y: 0 }, + width: 10, + height: 10, + thickness: 1.4, + num_layers: 2, + material: "fr4", + }, + { + type: "source_net", + source_net_id: "source_net_0", + name: "N1", + member_source_group_ids: [], + }, + { + type: "source_net", + source_net_id: "source_net_1", + name: "N2", + member_source_group_ids: [], + }, + { + type: "source_port", + source_port_id: "source_port_a", + name: "A", + }, + { + type: "source_port", + source_port_id: "source_port_b", + name: "B", + }, + { + type: "source_port", + source_port_id: "source_port_c", + name: "C", + }, + { + type: "source_port", + source_port_id: "source_port_d", + name: "D", + }, + { + type: "pcb_port", + pcb_port_id: "pcb_port_a", + source_port_id: "source_port_a", + x: -2, + y: -2, + layers: ["top"], + }, + { + type: "pcb_port", + pcb_port_id: "pcb_port_b", + source_port_id: "source_port_b", + x: 2, + y: 2, + layers: ["top"], + }, + { + type: "pcb_port", + pcb_port_id: "pcb_port_c", + source_port_id: "source_port_c", + x: -2, + y: 2, + layers: ["top"], + }, + { + type: "pcb_port", + pcb_port_id: "pcb_port_d", + source_port_id: "source_port_d", + x: 2, + y: -2, + layers: ["top"], + }, + { + type: "pcb_plated_hole", + pcb_plated_hole_id: "pcb_plated_hole_a", + pcb_port_id: "pcb_port_a", + x: -2, + y: -2, + shape: "circle", + outer_diameter: 1, + hole_diameter: 0.5, + layers: ["top", "bottom"], + }, + { + type: "pcb_plated_hole", + pcb_plated_hole_id: "pcb_plated_hole_b", + pcb_port_id: "pcb_port_b", + x: 2, + y: 2, + shape: "circle", + outer_diameter: 1, + hole_diameter: 0.5, + layers: ["top", "bottom"], + }, + { + type: "pcb_plated_hole", + pcb_plated_hole_id: "pcb_plated_hole_c", + pcb_port_id: "pcb_port_c", + x: -2, + y: 2, + shape: "circle", + outer_diameter: 1, + hole_diameter: 0.5, + layers: ["top", "bottom"], + }, + { + type: "pcb_plated_hole", + pcb_plated_hole_id: "pcb_plated_hole_d", + pcb_port_id: "pcb_port_d", + x: 2, + y: -2, + shape: "circle", + outer_diameter: 1, + hole_diameter: 0.5, + layers: ["top", "bottom"], + }, + { + type: "source_trace", + source_trace_id: "source_trace_0", + connected_source_port_ids: ["source_port_a", "source_port_b"], + connected_source_net_ids: ["source_net_0"], + }, + { + type: "source_trace", + source_trace_id: "source_trace_1", + connected_source_port_ids: ["source_port_c", "source_port_d"], + connected_source_net_ids: ["source_net_1"], + }, + ] + const root = new FakeRootCircuit(circuitJson) const diagnostics = new AutorouterDiagnostics({ enabled: true, dumpSrj: "all", @@ -223,6 +358,24 @@ describe("autorouter diagnostics", () => { expect( fs.readFileSync(path.join(debugDir, "phase-0-routed.png")).subarray(0, 8), ).toEqual(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10])) + const startPng = fs.readFileSync( + path.join(debugDir, "placement-unrouted.png"), + ) + const expectedPngWithRatsNest = convertSvgToPngBuffer( + convertCircuitJsonToPcbSvg(circuitJson as any, { + shouldDrawRatsNest: true, + }), + ) + const expectedPngWithoutRatsNest = convertSvgToPngBuffer( + convertCircuitJsonToPcbSvg(circuitJson as any), + ) + expect(startPng).toEqual(Buffer.from(expectedPngWithRatsNest)) + expect(startPng).not.toEqual(Buffer.from(expectedPngWithoutRatsNest)) + expect( + convertCircuitJsonToPcbSvg(circuitJson as any, { + shouldDrawRatsNest: true, + }), + ).toMatchSvgSnapshot(import.meta.path, "autorouter-start-rats-nest") expect( fs.readFileSync(path.join(debugDir, "phase-0-routed.png")), ).not.toEqual(