shibosoftwaredev/f1c100s-linux-nema17-stepper-controller
A four-layer NEMA 17 motor-controller PCB combining an Allwinner F1C100S Linux computer, USB‑PD/buck power system, W5500 Ethernet, STM32 real-time motion control, DRV8825 stepper driver, protection/temperature/current telemetry, USB interfaces, and local LED/buzzer diagnostics.
- Version
- 0.4.56
- License
- unset
- Stars
- 0
tests/software-current-contract.ts
import { expect } from "bun:test"
// Render-order grouping/connectivity identifiers are metadata, not copper or
// electrical connections. Appending a group changes the board's group number.
export const stripGrouping = (record: any) => JSON.parse(JSON.stringify(record,
(key, value) => ["subcircuit_id","subcircuit_connectivity_map_key","source_group_id","pcb_group_id"].includes(key) ? undefined : value))
export const additionNames = new Set(["U_CURRENT_DAC","C_CURRENT_DAC"])
export function originalSourceContract(before: any[], after: any[]) {
const fuseId=before.find(e=>e.type==="source_component"&&e.name==="F_VM")?.source_component_id
const byId = new Map(after.map(e => [e.type+":"+e[e.type+"_id"],e]))
for (const e of before.filter(e => ["source_component","source_port","source_trace","source_net","source_component_internal_connection"].includes(e.type))) {
if (e.type === "source_trace" && e.name === "DRV_REF_FEED") continue
const actual = byId.get(e.type+":"+e[e.type+"_id"])
expect(actual).toBeDefined()
const wanted = stripGrouping(e), value = stripGrouping(actual)
// This annotation was deliberately added by the already-published bulk
// capacitor revision. The 0.4.53 historical test predates the annotation.
if (e.type === "source_port" && e.source_component_id === fuseId && e.pin_number === 2 && value.provides_power && !wanted.provides_power)
delete value.provides_power
expect(value).toEqual(wanted)
}
const oldNames = before.filter(e=>e.type==="source_component").map(e=>e.name)
expect(after.filter(e=>e.type==="source_component"&&!oldNames.includes(e.name)).map(e=>e.name).sort())
.toEqual([...additionNames].sort())
}
export function stablePhysicalContract({before,after,allowedMoves=new Set(["R_REF_TOP","C_U_2V5_IN","C_ETH_1"])}:
{before:any[];after:any[];allowedMoves?:Set<string>}) {
const sourceName = (c: any[]) => new Map(c.filter(e=>e.type==="source_component").map(e=>[e.source_component_id,e.name]))
const oldNames=sourceName(before),newNames=sourceName(after)
const pcbNames = new Map(after.filter(e=>e.type==="pcb_component").map(e=>[e.pcb_component_id,newNames.get(e.source_component_id)]))
const padOwners=new Map(before.filter(e=>e.type==="pcb_smtpad").map(e=>[e.pcb_smtpad_id,e.pcb_component_id]))
const types=new Set(["pcb_board","pcb_component","pcb_port","pcb_smtpad","pcb_plated_hole","pcb_hole","pcb_courtyard_rect","pcb_courtyard_outline","pcb_solder_paste","cad_component"])
const byId=new Map(after.map(e=>[e.type+":"+e[e.type+"_id"],e]))
for(const e of before.filter(e=>types.has(e.type))) {
const name=oldNames.get(e.source_component_id)??pcbNames.get(e.pcb_component_id??padOwners.get(e.pcb_smtpad_id))
if(allowedMoves.has(name))continue
if(["pcb_courtyard_rect","pcb_courtyard_outline"].includes(e.type)) {
// Manual courtyards are emitted after imported footprints, so adding
// a new footprint renumbers those database IDs. Preserve the exact
// owner, geometry, face and multiplicity, not the transient ID.
const normalize=(record:any)=>JSON.stringify(stripGrouping({...record,[e.type+"_id"]:undefined}))
expect(after.filter(z=>z.type===e.type&&z.pcb_component_id===e.pcb_component_id).map(normalize).sort())
.toEqual(before.filter(z=>z.type===e.type&&z.pcb_component_id===e.pcb_component_id).map(normalize).sort())
continue
}
expect(stripGrouping(byId.get(e.type+":"+e[e.type+"_id"]))).toEqual(stripGrouping(e))
}
}