/* * Description: Static and helper regression tests for MXPIC EDA frontend/backend integration contracts. * Inside functions: N/A - assertion-based test/module script. * Developer : Qin Yue @ 2026 * Organization : OptiHK Limited */ const assert = require('assert'); const helpers = require('../frontend/canvas-helpers.js'); const handles = helpers.buildPortHandles({ a0: { x: 0, y: 0, a: 180 }, b0: { x: 0, y: 0, a: 0 }, a1: { x: -91.7, y: 4.475, a: 180 }, a2: { x: -91.7, y: -4.475, a: 180 }, b1: { x: 91.7, y: 4.475, a: 0 }, b2: { x: 91.7, y: -4.475, a: 0 }, ep2a: { x: -37.8, y: -20, a: 270 }, ep2b: { x: -37.8, y: 20, a: 90 }, }); assert.deepStrictEqual(handles.map(handle => handle.name), ['a1', 'a2', 'b1', 'b2', 'ep2b', 'ep2a']); assert.deepStrictEqual(handles.filter(handle => handle.position === 'left').map(handle => handle.name), ['a1', 'a2']); assert.deepStrictEqual(handles.filter(handle => handle.position === 'right').map(handle => handle.name), ['b1', 'b2']); assert.deepStrictEqual(handles.find(handle => handle.name === 'ep2b').position, 'top'); assert.deepStrictEqual(handles.find(handle => handle.name === 'ep2a').position, 'bottom'); assert.strictEqual(handles.find(handle => handle.name === 'a1').style.top, '15%'); assert.strictEqual(handles.find(handle => handle.name === 'a1').style.left, 0); assert.strictEqual(handles.find(handle => handle.name === 'a2').style.top, '85%'); assert.strictEqual(handles.find(handle => handle.name === 'b1').style.left, '100%'); assert.strictEqual(handles.find(handle => handle.name === 'ep2b').style.left, '50%'); assert.strictEqual(handles.find(handle => handle.name === 'ep2b').style.top, 0); assert.strictEqual(handles.find(handle => handle.name === 'ep2a').style.top, '100%'); const uniformLeftHandles = helpers.buildPortHandles({ p_top: { x: -10, y: 300, a: 180 }, p_mid: { x: -10, y: 20, a: 180 }, p_bottom: { x: -10, y: -5, a: 180 }, }); assert.deepStrictEqual( uniformLeftHandles.map(handle => handle.style.top), ['15%', '50%', '85%'], 'ports on the same side should be uniformly spaced after sorting' ); const exactCenteredHandles = helpers.buildPortHandles({ a1: { x: -91.7, y: 4.475, a: 180 }, a2: { x: -91.7, y: -4.475, a: 180 }, b1: { x: 91.7, y: 4.475, a: 0 }, b2: { x: 91.7, y: -4.475, a: 0 }, }, { boxSize: { width: 183.4, height: 29.65 } }); assert.strictEqual( exactCenteredHandles.find(handle => handle.name === 'a1').style.top, '34.907%', 'box_size-aware handles should use exact centered PDK y coordinates instead of uniform spacing' ); assert.strictEqual(exactCenteredHandles.find(handle => handle.name === 'a2').style.top, '65.093%'); assert.strictEqual(exactCenteredHandles.find(handle => handle.name === 'b1').style.left, '100%'); const exactPositiveHandles = helpers.buildPortHandles({ a1: { x: 0, y: 10, a: 180 }, b1: { x: 150.4, y: 10, a: 0 }, a2: { x: 0, y: -10, a: 180 }, b2: { x: 150.4, y: -10, a: 0 }, }, { boxSize: { width: 150.4, height: 40 } }); assert.strictEqual(exactPositiveHandles.find(handle => handle.name === 'a1').style.top, '25%'); assert.strictEqual(exactPositiveHandles.find(handle => handle.name === 'a2').style.top, '75%'); const exactPortObjectHandles = helpers.buildPortHandles({ port_1: { x: 0, y: 10, a: 0 }, port_2: { x: 0, y: 0, a: 0 }, port_3: { x: 0, y: -10, a: 0 }, }, { boxSize: { width: 47, height: 58 } }); assert.deepStrictEqual( exactPortObjectHandles.map(handle => handle.style.top), ['32.759%', '50%', '67.241%'], 'standalone Port handles should use their actual y offsets inside the Port body' ); const exactAnchorHandles = helpers.buildPortHandles( helpers.buildElementPorts('anchor', { portNumber: 3, pitch: 10 }), { boxSize: { width: 16, height: 58 } } ); assert.deepStrictEqual( exactAnchorHandles.filter(handle => handle.name.startsWith('a')).map(handle => handle.style.top), ['32.759%', '50%', '67.241%'], 'standalone Anchor handles should be centered around the Anchor body' ); assert.deepStrictEqual( helpers.normalizeBoxSize({ box_size: [946, 75] }), { width: 946, height: 75 }, 'component box size should load from YAML box_size arrays' ); assert.deepStrictEqual( helpers.normalizeBoxSize({ box_size: ['946.0', '75.0'] }), { width: 946, height: 75 }, 'component box size should accept numeric strings from YAML/JSON metadata' ); assert.deepStrictEqual( helpers.normalizeBoxSize({ box_sz: { width: 1200, height: 85 } }), { width: 1200, height: 85 }, 'component box size should also accept box_sz objects' ); assert.strictEqual( helpers.PORT_NODE_SIZE, 30, 'Port and Anchor virtual elements should use the same 30 um canvas footprint' ); assert.deepStrictEqual( helpers.calculateLayoutBounds({ nodes: [{ position: { x: 100, y: 200 }, data: { componentName: 'rotated_component', boxSize: { width: 50, height: 20 }, rotation: 90 } }] }), { minX: 80, minY: 200, maxX: 100, maxY: 250, width: 20, height: 50, bottomLeft: { x: 80, y: 200 }, topRight: { x: 100, y: 250 } }, 'layout preview bounds should use component box_size and rotation to find device corners' ); assert.strictEqual( helpers.chooseCategoryComponent('generate with mxpic_forge', [ 'generate with mxpic_forge', 'EC_SiN400_1310_0p5dB_L935_A0_QY_202604' ], 'edge_couplers'), 'EC_SiN400_1310_0p5dB_L935_A0_QY_202604', 'dropping an EC category should prefer the real PDK component so its YAML box_size is loaded' ); assert.deepStrictEqual( helpers.clampPositionToCanvas({ x: 4990, y: 5010 }, { width: 5000, height: 5000 }, { width: 946, height: 75 }), { x: 4054, y: 4925 }, 'component drag position should keep the full component box inside the canvas boundary' ); const rulerMeasurement = helpers.createRulerMeasurement({ x: 10, y: 20 }, { x: 40, y: 60 }); assert.deepStrictEqual( rulerMeasurement, { start: { x: 10, y: 20 }, end: { x: 40, y: 60 }, dx: 30, dy: 40, distance: 50, midpoint: { x: 25, y: 40 }, label: '50.000 um dx 30.000 dy 40.000' }, 'ruler measurement should calculate point-to-point distance in canvas um coordinates' ); assert.strictEqual( helpers.createRulerMeasurement({ x: 1 }, null), null, 'ruler measurement should wait until both points are available' ); assert.deepStrictEqual( helpers.createComponentSymbolMetrics({ width: 946, height: 75 }), { width: 898.7, height: 51 }, 'large edge-coupler symbols should scale close to the YAML box width instead of being capped near 300 um' ); assert.deepStrictEqual( helpers.createComponentSymbolMetrics({ width: 132, height: 82 }), { width: 118.8, height: 55.76 }, 'default symbols should still scale proportionally inside normal component boxes' ); assert.deepStrictEqual( helpers.calculateCompositeBoxSize({ nodes: [ { type: 'portNode', position: { x: 10, y: 5 }, data: { componentDisplayName: 'input', elementType: 'port', portNumber: 1, pitch: 10, width: 0.5 } }, { type: 'rotatableNode', position: { x: 50, y: 20 }, data: { componentName: 'MMI_1', boxSize: { width: 80, height: 30 } } }, { type: 'rotatableNode', position: { x: 160, y: 70 }, data: { componentName: 'MMI_2', boxSize: { width: 20, height: 10 } } } ] }), { width: 170, height: 75 }, 'composite canvas symbols should use the bounds of exported ports and internal instance boxes' ); assert.deepStrictEqual( helpers.calculateCompositeBoxSize({ nodes: [{ type: 'portNode', position: { x: 10, y: 5 }, data: { componentDisplayName: 'input', elementType: 'port', portNumber: 1, pitch: 10 } }] }), helpers.DEFAULT_COMPONENT_BOX_SIZE, 'single-port empty canvases should keep the default component footprint' ); const rotatedHandles = helpers.buildPortHandles({ left_port: { x: -50, y: 0, a: 180 }, top_port: { x: 0, y: 20, a: 90 }, }, { rotation: 180 }); assert.strictEqual( rotatedHandles.find(handle => handle.name === 'left_port').position, 'right', 'rotating a component should rotate the React Flow handle side' ); assert.strictEqual( rotatedHandles.find(handle => handle.name === 'top_port').position, 'bottom', 'rotating a component should rotate vertical port handle sides' ); const quarterTurnHandles = helpers.buildPortHandles({ out: { x: 50, y: 0, a: 0 }, up: { x: 0, y: 20, a: 90 }, }, { rotation: 90 }); assert.strictEqual( quarterTurnHandles.find(handle => handle.name === 'out').position, 'bottom', '90 degree canvas rotation should move a right-facing port direction to the bottom side' ); assert.strictEqual( quarterTurnHandles.find(handle => handle.name === 'up').position, 'right', '90 degree canvas rotation should move a top-facing port direction to the right side' ); const negativeQuarterTurnHandles = helpers.buildPortHandles({ out: { x: 50, y: 0, a: 0 }, down: { x: 0, y: -20, a: -90 }, }, { rotation: -90 }); assert.strictEqual( negativeQuarterTurnHandles.find(handle => handle.name === 'out').position, 'top', '-90 degree canvas rotation should move a right-facing port direction to the top side' ); assert.strictEqual( negativeQuarterTurnHandles.find(handle => handle.name === 'down').position, 'right', '-90 degree canvas rotation should move a bottom-facing port direction to the right side' ); const args = helpers.createForgeArguments(); assert(Object.keys(args).length >= 10); assert.strictEqual(helpers.isForgeComponent('generate with mxpic_forge'), true); assert.strictEqual(helpers.isBasicComponent('waveguide'), true); assert.strictEqual(helpers.isBasicComponent('circle'), true); assert.strictEqual(helpers.isBasicComponent('cricle'), true); assert.strictEqual( helpers.buildElementPorts('port').port.a, 0, 'Port objects should default to 0 degree angle' ); assert.deepStrictEqual( { a1: helpers.buildElementPorts('anchor').a1.a, b1: helpers.buildElementPorts('anchor').b1.a, }, { a1: 180, b1: 0 }, 'Anchor objects should default to a1 for the left port and b1 for the right port' ); assert.deepStrictEqual( { a1: helpers.buildElementPorts('anchor').a1, b1: helpers.buildElementPorts('anchor').b1, }, { a1: { x: 0, y: -15, a: 180, width: 0.5 }, b1: { x: 0, y: -15, a: 0, width: 0.5 } }, 'Anchor a/b port pairs should share coordinates and keep opposite directions' ); assert.deepStrictEqual( helpers.buildBasicComponentPorts('waveguide', { length: 120, width: 0.6 }).b1, { x: 120, y: 0, a: 0, width: 0.6, xsection: 'strip', description: 'Optical power output' }, 'basic waveguide ports should be generated from editable settings' ); assert.deepStrictEqual( helpers.getBasicComponentMetadata('waveguide', { length: 120, width: 0.5 }).box_size, [120, 20], 'basic waveguide symbol should use a height that is two port-circle diameters' ); assert.deepStrictEqual( helpers.getBasicComponentMetadata('90 bend', { radius: 15 }).box_size, [25, 25], '90 bend symbol should not shrink below the radius-25 canvas size' ); assert.deepStrictEqual( helpers.getBasicComponentMetadata('90 bend', { radius: 30 }).box_size, [30, 30], '90 bend symbol should still scale above radius 25' ); assert.deepStrictEqual( helpers.buildBasicComponentPorts('90 bend', { radius: 15, width: 0.6 }), { a1: { x: 0, y: 12.5, a: 180, width: 0.6, xsection: 'strip', description: 'Optical power input' }, b1: { x: 12.5, y: 0, a: 90, width: 0.6, xsection: 'strip', description: 'Optical power output' } }, '90 bend ports should sit at the middle of the left and top sides of the square' ); const ninetyBendHandles = helpers.buildPortHandles(helpers.buildBasicComponentPorts('90 bend', { radius: 15 })); assert.strictEqual(ninetyBendHandles.find(handle => handle.name === 'a1').position, 'left'); assert.strictEqual(ninetyBendHandles.find(handle => handle.name === 'a1').style.top, '50%'); assert.strictEqual(ninetyBendHandles.find(handle => handle.name === 'a1').style.left, 0); assert.strictEqual(ninetyBendHandles.find(handle => handle.name === 'b1').position, 'top'); assert.strictEqual(ninetyBendHandles.find(handle => handle.name === 'b1').style.left, '50%'); assert.strictEqual(ninetyBendHandles.find(handle => handle.name === 'b1').style.top, 0); assert.deepStrictEqual( helpers.getBasicComponentMetadata('180 bend', { radius: 15 }).box_size, [25, 50], '180 bend symbol should not shrink below the radius-25 canvas size' ); assert.deepStrictEqual( helpers.getBasicComponentMetadata('180 bend', { radius: 30 }).box_size, [30, 60], '180 bend symbol should still scale above radius 25' ); assert.deepStrictEqual( helpers.getBasicComponentMetadata('taper', { length: 80, width1: 0.4, width2: 1.2 }).box_size, [80, 20], 'basic taper symbol should use a height that is two port-circle diameters' ); assert.deepStrictEqual( helpers.getBasicComponentMetadata('taper', { length: 80, width1: 0.4, width2: 1.2 }).ports.a1.description, 'Optical power input', 'basic component metadata should include human-readable port descriptions' ); const yaml = helpers.buildInstanceYaml({ instanceName: 'component_1', componentName: 'generate with mxpic_forge', componentPath: 'ignored/path', position: { x: 12.34, y: -5 }, rotation: 90, flip: true, flop: true, forgeArguments: { function_name: 'mmi1x2', length: 25.5, include_heater: true } }); assert(yaml.includes('component: generate_with_forge')); assert(yaml.includes('flip: 1')); assert(yaml.includes('flop: 1')); assert(yaml.includes('function_name: "mmi1x2"')); assert(yaml.includes('length: 25.5')); assert(yaml.includes('include_heater: true')); const basicYaml = helpers.buildInstanceYaml({ instanceName: 'wg_1', componentName: 'waveguide', componentPath: 'ignored', position: { x: 0, y: 0 }, rotation: 0, flip: false, flop: false, basicArguments: { length: 88, width: 0.7, xsection: 'strip' } }); assert(basicYaml.includes('component: waveguide')); assert(basicYaml.includes('length: 88')); assert(basicYaml.includes('width: 0.7')); const projectInstancesYaml = helpers.buildInstancesYaml({ nodes: [ { id: 'node-1', type: 'rotatableNode', position: { x: 10, y: 20 }, data: { componentDisplayName: 'component_1', componentName: 'PDK_A', rotation: 0 } }, { id: 'node-2', type: 'rotatableNode', position: { x: 30, y: 40 }, data: { componentDisplayName: 'cell_1', componentName: 'canvas_1', type: 'composite', rotation: 90 } } ], resolveComponentPath: name => name === 'PDK_A' ? 'foundry/path/PDK_A' : name }); assert(projectInstancesYaml.includes('component_1:')); assert(projectInstancesYaml.includes('component: foundry/path/PDK_A')); assert(projectInstancesYaml.includes('cell_1:')); assert(projectInstancesYaml.includes('component: canvas_1')); const pagePortsYaml = helpers.buildPortsYaml({ x: 50, y: 150, a: 90 }); assert(pagePortsYaml.includes('pins:')); assert(!pagePortsYaml.includes('ports:')); assert(pagePortsYaml.includes('- name: port_io1')); assert(pagePortsYaml.includes('pin: io1')); assert(pagePortsYaml.includes('x: 50.0')); assert(pagePortsYaml.includes('y: -150.0')); assert(pagePortsYaml.includes('angle: -90.0')); const componentPorts = helpers.buildPageComponentPorts({ x: 12, y: -6, a: 180 }); assert.deepStrictEqual(componentPorts, { port_io1: { element: 'port', pin: 'io1', x: 12, y: -6, a: 0, width: 0.5 } }); const elementNodes = [ { id: 'port-1', type: 'portNode', position: { x: 10, y: 20 }, data: { componentDisplayName: 'in0', elementType: 'port', angle: 180, width: 0.7, layer: 'WG_CORE', description: 'input port' } }, { id: 'anchor-1', type: 'rotatableNode', position: { x: 30, y: 40 }, data: { componentDisplayName: 'anchor_1', componentName: 'Anchor', elementType: 'anchor', rotation: 0 } }, { id: 'mmi-1', type: 'rotatableNode', position: { x: 50, y: 60 }, data: { componentDisplayName: 'component_1', componentName: 'MMI', rotation: 0 } } ]; assert.deepStrictEqual(helpers.buildElementPorts('port', { angle: 90, width: 0.8 }), { port: { x: 0, y: 0, a: 90, width: 0.8 } }); assert.deepStrictEqual(Object.keys(helpers.buildElementPorts('anchor')), ['a1', 'b1']); assert.deepStrictEqual(Object.keys(helpers.buildElementPorts('port', { portNumber: 3, pitch: 10 })), ['port_1', 'port_2', 'port_3']); assert.deepStrictEqual(helpers.buildElementPorts('port', { portNumber: 3, pitch: 10 }).port_1, { x: 0, y: 10, a: 0, width: 0.5 }); assert.deepStrictEqual(helpers.buildElementPorts('port', { portNumber: 3 }).port_1, { x: 0, y: 10, a: 0, width: 0.5 }); assert.deepStrictEqual(Object.keys(helpers.buildElementPorts('anchor', { portNumber: 2, pitch: 12 })), ['a1', 'b1', 'a2', 'b2']); assert.deepStrictEqual(helpers.buildElementPorts('anchor', { portNumber: 2, pitch: 12 }).a1, { x: 0, y: 6, a: 180, width: 0.5 }); assert.deepStrictEqual(helpers.buildElementPorts('anchor', { portNumber: 2, pitch: 12 }).b2, { x: 0, y: -6, a: 0, width: 0.5 }); assert.deepStrictEqual(helpers.buildElementPorts('anchor', { portNumber: 2, pitch: 12 }).a2, { x: 0, y: -6, a: 180, width: 0.5 }); assert.deepStrictEqual( helpers.getNodePortCanvasPoint({ id: 'anchor-rotated', type: 'anchorNode', position: { x: 100, y: 200 }, data: { elementType: 'anchor', rotation: 90, portNumber: 1, pitch: 10, ports: helpers.buildElementPorts('anchor', { portNumber: 1, pitch: 10 }) } }, 'a1'), { x: 115, y: 200 }, 'Anchor port endpoint coordinates should rotate with the anchor body' ); assert.deepStrictEqual( helpers.getNodePortCanvasPoint({ id: 'port-rotated', type: 'portNode', position: { x: 100, y: 200 }, data: { elementType: 'port', angle: 180, portNumber: 3, pitch: 10, ports: helpers.buildElementPorts('port', { angle: 180, portNumber: 3, pitch: 10 }) } }, 'port_1'), { x: 100, y: 210 }, 'Port pin endpoint coordinates should rotate with the Port body' ); assert.deepStrictEqual(helpers.buildElementBoxSize({ portNumber: 1 }), { width: 47, height: 30 }); assert.deepStrictEqual(helpers.buildElementBoxSize({ elementType: 'anchor', portNumber: 1 }), { width: 16, height: 30 }); assert.deepStrictEqual(helpers.buildElementBoxSize({ elementType: 'anchor', portNumber: 4, pitch: 10 }), { width: 16, height: 72 }); assert.deepStrictEqual(helpers.buildElementBoxSize({ portNumber: 4, pitch: 10 }), { width: 47, height: 72 }); assert.deepStrictEqual( helpers.buildElementBoxSize({ elementType: 'port', portName: 'input_port' }), { width: 82, height: 30 }, 'Port object width should grow to fit the displayed port instance name' ); assert.deepStrictEqual( helpers.buildPageComponentPorts(null, [{ id: 'port-array', type: 'portNode', position: { x: 100, y: 200 }, data: { componentDisplayName: 'array', elementType: 'port', portNumber: 3, pitch: 10, width: 0.6 } }]), { array_io1: { element: 'array', pin: 'io1', x: 100, y: 190, a: 180, width: 0.6 }, array_io2: { element: 'array', pin: 'io2', x: 100, y: 200, a: 180, width: 0.6 }, array_io3: { element: 'array', pin: 'io3', x: 100, y: 210, a: 180, width: 0.6 } } ); assert.deepStrictEqual( helpers.buildPageComponentPorts(null, [{ id: 'port-array-rotated-90', type: 'portNode', position: { x: 100, y: 200 }, data: { componentDisplayName: 'array', elementType: 'port', angle: 90, portNumber: 2, pitch: 10, width: 0.6 } }]), { array_io1: { element: 'array', pin: 'io1', x: 95, y: 200, a: -90, width: 0.6 }, array_io2: { element: 'array', pin: 'io2', x: 105, y: 200, a: -90, width: 0.6 } } ); assert.deepStrictEqual( helpers.buildPageComponentPorts(null, [{ id: 'port-array-rotated', type: 'portNode', position: { x: 100, y: 200 }, data: { componentDisplayName: 'array', elementType: 'port', angle: 180, portNumber: 3, pitch: 10, width: 0.6 } }]), { array_io1: { element: 'array', pin: 'io1', x: 100, y: 210, a: 0, width: 0.6 }, array_io2: { element: 'array', pin: 'io2', x: 100, y: 200, a: 0, width: 0.6 }, array_io3: { element: 'array', pin: 'io3', x: 100, y: 190, a: 0, width: 0.6 } }, 'Rotated Port object pins should export rotated coordinates along with their rotated angle' ); const canvasPortsYaml = helpers.buildCanvasPortsYaml(elementNodes); assert(canvasPortsYaml.includes('pins:')); assert(!canvasPortsYaml.includes('ports:')); assert(canvasPortsYaml.includes('name: in0_io1')); assert(canvasPortsYaml.includes('element: in0')); assert(canvasPortsYaml.includes('pin: io1')); assert(canvasPortsYaml.includes('description: "input port"')); assert(canvasPortsYaml.includes('width: 0.7')); assert(canvasPortsYaml.includes('y: -20.0')); assert(canvasPortsYaml.includes('angle: 0.0')); const elementsYaml = helpers.buildElementsYaml(elementNodes); assert(elementsYaml.includes('in0:')); assert(elementsYaml.includes('type: port')); assert(elementsYaml.includes('anchor_1:')); assert(elementsYaml.includes('type: anchor')); assert(elementsYaml.includes('y: -20.0')); assert(elementsYaml.includes('pin_number: 1')); assert(elementsYaml.includes('name: in0_io1')); assert(elementsYaml.includes('role: io1')); assert(elementsYaml.includes('name: anchor_1_a1')); assert(elementsYaml.includes('role: a1')); assert(elementsYaml.includes('pitch: 10')); const instancesWithoutElements = helpers.buildInstancesYaml({ nodes: elementNodes, resolveComponentPath: name => name }); assert(!instancesWithoutElements.includes('anchor_1:')); assert(!instancesWithoutElements.includes('in0:')); assert(instancesWithoutElements.includes('component_1:')); assert(instancesWithoutElements.includes('y: -60.0')); const multiPortComponentPorts = helpers.buildPageComponentPorts(null, elementNodes); assert.deepStrictEqual(multiPortComponentPorts.in0_io1, { element: 'in0', pin: 'io1', x: 10, y: 20, a: 0, width: 0.7 }); const technologyManifest = { defaults: { xsection: 'strip', width: 0.45, radius: 10, routing_type: 'euler_bend' }, xsections: { strip: { family: 'optical', default_width: 0.45 }, rib_low: { family: 'optical', default_width: 0.5 }, metal_1: { family: 'electrical', default_width: 5 }, metal_2: { family: 'electrical', default_width: 6 } }, routing_types: ['euler_bend', 'standard_bend'] }; const routeDefaults = helpers.createRouteSettings(technologyManifest); assert.deepStrictEqual(routeDefaults, { xsection: 'strip', family: 'optical', width: 0.45, radius: 10, routing_type: 'euler_bend', widthEdited: false }); const metalRoute = helpers.updateRouteXsection(routeDefaults, 'metal_1', technologyManifest); assert.strictEqual(metalRoute.family, 'electrical'); assert.strictEqual(metalRoute.width, 5); const manuallyEditedWidth = helpers.updateRouteField(routeDefaults, 'width', 0.62, technologyManifest); const changedXsection = helpers.updateRouteXsection(manuallyEditedWidth, 'rib_low', technologyManifest); assert.strictEqual(changedXsection.width, 0.62); assert.strictEqual(changedXsection.family, 'optical'); const styledStrip = helpers.routeStyleForSettings({ xsection: 'strip', family: 'optical' }, false); const styledMetal = helpers.routeStyleForSettings({ xsection: 'metal_1', family: 'electrical' }, true); assert.notStrictEqual(styledStrip.style.stroke, styledMetal.style.stroke); assert(styledMetal.style.strokeDasharray, 'electrical routes should use a visibly different line treatment'); assert(styledMetal.style.strokeWidth > styledStrip.style.strokeWidth); const routeYaml = helpers.buildBundlesYaml({ nodes: [ { id: 'a', data: { componentDisplayName: 'inst_a' } }, { id: 'b', data: { componentDisplayName: 'inst_b' } } ], edges: [{ id: 'edge-a-b', source: 'a', target: 'b', sourceHandle: 'out', targetHandle: 'in', data: { route: { xsection: 'metal_1', family: 'electrical', width: 5, radius: 20, routing_type: 'standard_bend' }, points: [{ x: 0, y: 0 }, { x: 40, y: 20 }] } }] }, technologyManifest); assert(routeYaml.includes('xsection: metal_1')); assert(routeYaml.includes('family: electrical')); assert(routeYaml.includes('radius: 20')); assert(routeYaml.includes('routing_type: standard_bend')); assert(routeYaml.includes('points:')); assert(routeYaml.includes('x: 40.0')); assert(routeYaml.includes('y: -20.0')); const anchoredRouteYaml = helpers.buildBundlesYaml({ nodes: [ { id: 'src-node', type: 'rotatableNode', position: { x: 10, y: 20 }, data: { componentDisplayName: 'src_inst', boxSize: [100, 40], ports: { out: { x: -10, y: 0, a: 180 } } } }, { id: 'dst-node', type: 'rotatableNode', position: { x: 120, y: 20 }, data: { componentDisplayName: 'dst_inst', boxSize: [100, 40], ports: { in: { x: 10, y: 0, a: 0 } } } } ], edges: [{ id: 'edge-src-dst', source: 'src-node', target: 'dst-node', sourceHandle: 'out', targetHandle: 'in', data: { route: { xsection: 'strip', family: 'optical', width: 0.45, radius: 10, routing_type: 'euler_bend' }, points: [{ x: 0, y: 0 }, { x: 80, y: 0 }, { x: 80, y: 60 }] } }] }, technologyManifest); assert(anchoredRouteYaml.includes('from: src_inst:out')); assert(anchoredRouteYaml.includes('to: dst_inst:in')); assert(anchoredRouteYaml.includes('x: 0.0')); assert(anchoredRouteYaml.includes('y: -20.0')); assert(anchoredRouteYaml.includes('x: 130.0')); const freeRouteYaml = helpers.buildBundlesYaml({ nodes: [], edges: [{ id: 'route-free-1', source: '__free_route_route-free-1_start__', target: '__free_route_route-free-1_end__', data: { freeRoute: true, route: { xsection: 'strip', family: 'optical', width: 0.45, radius: 10, routing_type: 'euler_bend' }, points: [{ x: 10, y: 20 }, { x: 80, y: 20 }, { x: 80, y: 120 }] } }] }, technologyManifest); assert(freeRouteYaml.includes('id: "route-free-1"')); assert(!freeRouteYaml.includes('from:')); assert(!freeRouteYaml.includes('to:')); assert(freeRouteYaml.includes('points:')); assert(freeRouteYaml.includes('x: 80.0')); assert(freeRouteYaml.includes('y: -120.0')); const edgeA = { id: 'edge-a-b', source: 'a', target: 'b', data: { route: { family: 'optical' } } }; const edgeB = { id: 'edge-c-d', source: 'c', target: 'd', data: { route: { family: 'optical' } } }; const edgeC = { id: 'edge-e-f', source: 'e', target: 'f', data: { route: { family: 'electrical' } } }; const crossingNodes = { a: { position: { x: 0, y: 0 } }, b: { position: { x: 100, y: 100 } }, c: { position: { x: 0, y: 100 } }, d: { position: { x: 100, y: 0 } }, e: { position: { x: 0, y: 100 } }, f: { position: { x: 100, y: 0 } } }; edgeA.data.route.xsection = 'strip'; edgeB.data.route.xsection = 'strip'; edgeC.data.route.xsection = 'metal_1'; const edgeD = { id: 'edge-g-h', source: 'e', target: 'f', data: { route: { xsection: 'rib_low', family: 'optical' } } }; const edgeE = { id: 'edge-metal-alias', source: 'e', target: 'f', data: { route: { xsection: 'metal1', family: 'electrical' } } }; const edgeF = { id: 'edge-metal-underscore', source: 'a', target: 'b', data: { route: { xsection: 'metal_1', family: 'electrical' } } }; assert.strictEqual(helpers.findSameTypeRouteCrossing(edgeB, [edgeA], crossingNodes).conflictEdge.id, 'edge-a-b'); assert.strictEqual(helpers.findSameTypeRouteCrossing(edgeC, [edgeA], crossingNodes), null); assert.strictEqual(helpers.findSameTypeRouteCrossing(edgeD, [edgeA], crossingNodes), null); assert.strictEqual(helpers.findSameTypeRouteCrossing(edgeE, [edgeF], crossingNodes).conflictEdge.id, 'edge-metal-underscore'); assert.strictEqual(helpers.findSameFamilyRouteCrossing(edgeB, [edgeA], crossingNodes).conflictEdge.id, 'edge-a-b');