velxio/frontend/src/__tests__/microsd-real-firmware.test.ts

59 lines
2.4 KiB
TypeScript

/**
* microsd-real-firmware.test.ts — end-to-end proof that a REAL Arduino SD
* library (SD.h on avr8js) mounts the FAT16 image our microsd-card part serves
* over SPI, reads a file, writes a new one, and reads it back.
*
* Firmware fixture (`fixtures/microsd-rw/`) was compiled with arduino-cli
* (arduino:avr:uno) from the committed .ino. It exercises:
* - SD.begin() -> the SD-over-SPI init handshake (CMD0/8/55+41/58)
* - SD.open() -> mount FAT16 volume + read directory (CMD17 byte-addressed)
* - read/write -> CMD17 reads from the FAT image; CMD24 writes persist
*
* This caught three real bugs the unit tests (with a synthetic SPI driver)
* could not: the spi.onByte/completeTransfer adapter API, the 1-byte Ncr
* command->response latency, and SDSC byte addressing.
*/
import { describe, it, expect } from 'vitest';
import { readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { AVRSimulator } from '../simulation/AVRSimulator';
import { PinManager } from '../simulation/PinManager';
import { PartSimulationRegistry } from '../simulation/parts/PartSimulationRegistry';
import '../simulation/parts/ProtocolParts';
import { buildFat16Image } from '../utils/fatImage';
const HEX = readFileSync(
fileURLToPath(new URL('./fixtures/microsd-rw/microsd-rw.ino.hex', import.meta.url)),
'utf-8',
);
function runUntil(sim: AVRSimulator, budget: number, pred: () => boolean): void {
for (let i = 0; i < budget; i++) {
sim.step();
if ((i & 0x3ff) === 0 && pred()) return;
}
}
describe('microSD — real AVR SD.h firmware', () => {
it('reads a file from the FAT image, writes a new one, and reads it back', () => {
const sim = new AVRSimulator(new PinManager(), 'uno');
sim.loadHex(HEX);
const img = buildFat16Image([
{ name: 'hello.txt', data: new TextEncoder().encode('SD WORKS 123') },
]);
const el = { sdImageData: img } as unknown as HTMLElement;
PartSimulationRegistry.get('microsd-card')!.attachEvents!(el, sim as any, () => null);
let out = '';
sim.onSerialData = (ch) => {
out += ch;
};
runUntil(sim, 60_000_000, () => out.includes('DONE') || out.includes('FAIL'));
expect(out).toContain('READ:SD WORKS 123'); // read the pre-loaded file
expect(out).toContain('RBACK:written-123'); // wrote + read back a new file
expect(out).toContain('DONE');
expect(out).not.toContain('FAIL');
});
});