Files
mxpic_EDA/tests/canvas-helpers.test.js
T
2026-05-28 17:53:41 +08:00

232 lines
7.8 KiB
JavaScript

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 === 'a2').style.top, '85%');
assert.strictEqual(handles.find(handle => handle.name === 'ep2b').style.left, '50%');
const args = helpers.createForgeArguments();
assert(Object.keys(args).length >= 10);
assert.strictEqual(helpers.isForgeComponent('generate with mxpic_forge'), true);
const yaml = helpers.buildInstanceYaml({
instanceName: 'component_1',
componentName: 'generate with mxpic_forge',
componentPath: 'ignored/path',
position: { x: 12.34, y: -5 },
rotation: 90,
forgeArguments: { function_name: 'mmi1x2', length: 25.5, include_heater: true }
});
assert(yaml.includes('component: generate_with_forge'));
assert(yaml.includes('function_name: "mmi1x2"'));
assert(yaml.includes('length: 25.5'));
assert(yaml.includes('include_heater: true'));
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('- name: port'));
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: { x: 12, y: -6, a: 180, 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')), ['left', 'right']);
const canvasPortsYaml = helpers.buildCanvasPortsYaml(elementNodes);
assert(canvasPortsYaml.includes('name: in0'));
assert(canvasPortsYaml.includes('description: "input port"'));
assert(canvasPortsYaml.includes('width: 0.7'));
const elementsYaml = helpers.buildElementsYaml(elementNodes);
assert(elementsYaml.includes('in0:'));
assert(elementsYaml.includes('type: port'));
assert(elementsYaml.includes('anchor_1:'));
assert(elementsYaml.includes('type: anchor'));
const instancesWithoutElements = helpers.buildInstancesYaml({
nodes: elementNodes,
resolveComponentPath: name => name
});
assert(!instancesWithoutElements.includes('anchor_1:'));
assert(!instancesWithoutElements.includes('in0:'));
assert(instancesWithoutElements.includes('component_1:'));
const multiPortComponentPorts = helpers.buildPageComponentPorts(null, elementNodes);
assert.deepStrictEqual(multiPortComponentPorts.in0, { x: 10, y: 20, a: 180, 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' } }
}]
}, technologyManifest);
assert(routeYaml.includes('xsection: metal_1'));
assert(routeYaml.includes('family: electrical'));
assert(routeYaml.includes('radius: 20'));
assert(routeYaml.includes('routing_type: standard_bend'));
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 } }
};
assert.strictEqual(helpers.findSameFamilyRouteCrossing(edgeB, [edgeA], crossingNodes).conflictEdge.id, 'edge-a-b');
assert.strictEqual(helpers.findSameFamilyRouteCrossing(edgeC, [edgeA], crossingNodes), null);