import assert from 'node:assert/strict'; import { BUTTONS, formatBytes, setButton, mergeFirmware } from '../web/app.js'; import { buildFat32Image, FAT32_IMAGE_SIZE } from '../web/fat32.js'; // Merge - app image is placed at 0x10000 in a 0xFF-erased 16 MB flash with bootloader and partitions. const bootloader = new Uint8Array([1, 2, 3]); const partitions = new Uint8Array([4, 5, 6]); const app = new Uint8Array(1024); app[0] = 0xe9; const flash = mergeFirmware(app, bootloader, partitions); assert.equal(flash.length, 16 * 1024 * 1024); assert.deepEqual(flash.slice(0, 3), bootloader); assert.deepEqual(flash.slice(0x8000, 0x8003), partitions); assert.equal(flash[0x10000], 0xe9); assert.equal(flash[0x20000], 0xff); assert.throws(() => mergeFirmware(new Uint8Array([0x00]), bootloader, partitions), /0xE9/); assert.equal(formatBytes(512), '512 B'); assert.equal(formatBytes(64 * 1024 * 1024), '64.0 MiB'); const fatPayload = new TextEncoder().encode('A'.repeat(700)); const sdImage = buildFat32Image([ { path: 'Test/example.txt', bytes: fatPayload }, { path: 'Books/A long filename.txt', bytes: new TextEncoder().encode('book') }, ]); const sdView = new DataView(sdImage.buffer); assert.equal(sdImage.length, FAT32_IMAGE_SIZE); assert.equal(sdView.getUint16(11, true), 512); assert.equal(sdImage[13], 1); assert.equal(sdView.getUint32(32, true), FAT32_IMAGE_SIZE / 512); assert.equal(sdView.getUint32(44, true), 2); assert.deepEqual([...sdImage.slice(510, 512)], [0x55, 0xaa]); const fatSectors = sdView.getUint32(36, true); const fatOffset = 32 * 512; const dataOffset = (32 + 2 * fatSectors) * 512; const root = sdImage.subarray(dataOffset, dataOffset + 512); const testEntryOffset = Array.from({ length: 16 }, (_, i) => i * 32) .find(offset => root[offset + 11] !== 0x0f && new TextDecoder().decode(root.subarray(offset, offset + 8)).trim() === 'TEST'); assert.notEqual(testEntryOffset, undefined); const testCluster = new DataView(root.buffer, root.byteOffset + testEntryOffset, 32).getUint16(26, true); const testDirectory = sdImage.subarray(dataOffset + (testCluster - 2) * 512, dataOffset + (testCluster - 1) * 512); const fileOffset = Array.from({ length: 16 }, (_, i) => i * 32) .find(offset => new TextDecoder().decode(testDirectory.subarray(offset, offset + 8)).trim() === 'EXAMPLE'); const fileEntry = new DataView(testDirectory.buffer, testDirectory.byteOffset + fileOffset, 32); const fileCluster = fileEntry.getUint16(26, true); assert.equal(fileEntry.getUint32(28, true), fatPayload.length); assert.equal(sdView.getUint32(fatOffset + fileCluster * 4, true), fileCluster + 1); assert.equal(sdView.getUint32(fatOffset + (fileCluster + 1) * 4, true), 0x0fffffff); assert.deepEqual(sdImage.slice(dataOffset + (fileCluster - 2) * 512, dataOffset + (fileCluster - 2) * 512 + fatPayload.length), fatPayload); const calls = []; const module = { _xteink_wasm_set_adc: (...args) => calls.push(['adc', ...args]), _xteink_wasm_set_gpio: (...args) => calls.push(['gpio', ...args]), }; setButton(module, 'confirm', true); setButton(module, 'confirm', false); setButton(module, 'power', true); setButton(module, 'power', false); assert.deepEqual(calls, [ ['adc', BUTTONS.confirm.channel, BUTTONS.confirm.value], ['adc', BUTTONS.confirm.channel, 4095], ['gpio', 3, 0], ['gpio', 3, 1], ]);