test(examples): ningun LED de la galeria puede colgar directo de un pin
Guarda de regresion para lo que se acaba de arreglar. Recorre el grafo de cableado (no solo el vecino inmediato del LED, porque la resistencia protege igual desde el catodo o desde el otro lado de un transistor o un rele) y falla si algun LED llega a un pin sin limitar la corriente. Es el peor tipo de ejemplo roto: el sketch imprime "LED ON", el runtime quema el LED y no aparece ningun error, solo un LED oscuro.
This commit is contained in:
parent
27774bb3e1
commit
5a79836fdc
|
|
@ -0,0 +1,93 @@
|
|||
/**
|
||||
* Every gallery LED must have a resistor in its path.
|
||||
*
|
||||
* The electrical model is honest: a LED straight across a 3.3 V or 5 V pin
|
||||
* asks for an absurd current, the runtime burns it out and it stays dark
|
||||
* for the rest of the run — while the sketch happily prints "LED ON". That
|
||||
* is the worst kind of broken example, because nothing looks like an
|
||||
* error. Twenty-four of them shipped that way (mostly Pico, and every RGB
|
||||
* example, which needs one resistor PER CHANNEL) before this guard existed.
|
||||
*
|
||||
* The check walks the wiring graph rather than the LED's immediate
|
||||
* neighbour: a resistor protects from the anode side, from the cathode
|
||||
* side, and from the far side of a transistor or relay contact.
|
||||
*/
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { exampleProjects } from '../data/examples';
|
||||
|
||||
const LED_TYPES = new Set(['wokwi-led', 'wokwi-rgb-led']);
|
||||
/** Anything that limits current well enough to save the LED. */
|
||||
const LIMITER_TYPES = [
|
||||
'wokwi-resistor',
|
||||
'wokwi-resistor-us',
|
||||
'wokwi-potentiometer',
|
||||
'wokwi-slide-potentiometer',
|
||||
'wokwi-ntc-temperature-sensor',
|
||||
'wokwi-photoresistor-sensor',
|
||||
];
|
||||
|
||||
interface Comp {
|
||||
type?: string;
|
||||
id?: string;
|
||||
}
|
||||
interface Wire {
|
||||
start?: { componentId?: string };
|
||||
end?: { componentId?: string };
|
||||
}
|
||||
|
||||
function isLimiter(type: string | undefined): boolean {
|
||||
return !!type && LIMITER_TYPES.some((t) => type.startsWith(t));
|
||||
}
|
||||
|
||||
/** LEDs with no current limiter anywhere on their side of the circuit. */
|
||||
function unprotectedLeds(example: {
|
||||
components?: Comp[];
|
||||
wires?: Wire[];
|
||||
}): string[] {
|
||||
const comps = new Map<string, string>();
|
||||
for (const c of example.components ?? []) {
|
||||
if (c?.id) comps.set(c.id, c.type ?? '');
|
||||
}
|
||||
const leds = [...comps.entries()]
|
||||
.filter(([, type]) => LED_TYPES.has(type))
|
||||
.map(([id]) => id);
|
||||
if (!leds.length) return [];
|
||||
|
||||
const adj = new Map<string, string[]>();
|
||||
for (const w of example.wires ?? []) {
|
||||
const a = w?.start?.componentId;
|
||||
const b = w?.end?.componentId;
|
||||
if (!a || !b) continue;
|
||||
adj.set(a, [...(adj.get(a) ?? []), b]);
|
||||
adj.set(b, [...(adj.get(b) ?? []), a]);
|
||||
}
|
||||
|
||||
return leds.filter((led) => {
|
||||
const seen = new Set([led]);
|
||||
const queue = [...(adj.get(led) ?? [])];
|
||||
while (queue.length) {
|
||||
const node = queue.shift()!;
|
||||
if (seen.has(node)) continue;
|
||||
seen.add(node);
|
||||
const type = comps.get(node);
|
||||
if (isLimiter(type)) return false; // protected
|
||||
// An unknown id is the board itself (a supply rail) and another LED
|
||||
// is not protection either: neither continues the search.
|
||||
if (type === undefined || LED_TYPES.has(type)) continue;
|
||||
queue.push(...(adj.get(node) ?? []));
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
describe('gallery examples — LED current limiting', () => {
|
||||
it('no example drives a LED straight off a pin', () => {
|
||||
const offenders: string[] = [];
|
||||
for (const ex of exampleProjects) {
|
||||
for (const led of unprotectedLeds(ex as never)) {
|
||||
offenders.push(`${ex.id}: ${led}`);
|
||||
}
|
||||
}
|
||||
expect(offenders, 'LEDs without a series resistor').toEqual([]);
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue