From f00146c18659fe0e3e4fe78d6aeed0ac90d770e4 Mon Sep 17 00:00:00 2001 From: Victor Giers Date: Mon, 13 Jul 2026 04:41:54 +0200 Subject: [PATCH] Implement comprehensive tests for pattern event parsing and normalization. --- src/lib/parsers.ts | 1 + tests/pattern-events.test.ts | 101 +++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 tests/pattern-events.test.ts diff --git a/src/lib/parsers.ts b/src/lib/parsers.ts index 4441d2a..42af8e9 100644 --- a/src/lib/parsers.ts +++ b/src/lib/parsers.ts @@ -1,5 +1,6 @@ import JSZip from 'jszip'; import { + Group, GroupFaderParam, Note, Pad, diff --git a/tests/pattern-events.test.ts b/tests/pattern-events.test.ts new file mode 100644 index 0000000..db0f343 --- /dev/null +++ b/tests/pattern-events.test.ts @@ -0,0 +1,101 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { parsePatternEvents } from '../src/lib/parsers'; + +function buildPattern(records: number[][], bars: number = 4, declaredCount = records.length) { + const data = new Uint8Array(4 + records.length * 8); + data[1] = bars; + data[2] = declaredCount & 0xff; + data[3] = declaredCount >> 8; + + records.forEach((record, index) => { + data.set(record, 4 + index * 8); + }); + + return data; +} + +test('parses note records without changing the derived note shape', () => { + const result = parsePatternEvents(buildPattern([[0x34, 0x12, 3 << 3, 64, 99, 0x78, 0x56, 7]])); + + assert.equal(result.bars, 4); + assert.equal(result.recordCount, 1); + assert.deepEqual(result.notes, { + 3: [{ note: 64, position: 0x1234, duration: 0x5678, velocity: 99 }], + }); + assert.deepEqual(result.events[0], { + type: 'note', + kind: 0, + position: 0x1234, + source: 3, + flags: 7, + rawData: new Uint8Array([0x34, 0x12, 3 << 3, 64, 99, 0x78, 0x56, 7]), + pad: 3, + note: 64, + velocity: 99, + duration: 0x5678, + }); +}); + +test('parses and normalizes fader records', () => { + const result = parsePatternEvents(buildPattern([[0x60, 0, 1, 7, 9, 0xff, 0x7f, 128]])); + + assert.deepEqual(result.notes, {}); + assert.deepEqual(result.events[0], { + type: 'fader', + kind: 1, + position: 96, + source: 0, + flags: 128, + rawData: new Uint8Array([0x60, 0, 1, 7, 9, 0xff, 0x7f, 128]), + parameter: 7, + reserved: 9, + rawValue: 32767, + value: 1, + }); +}); + +test('preserves MIDI and unknown records', () => { + const midi = [1, 0, (4 << 3) | 2, 0xd0, 75, 3, 4, 5]; + const unknown = [2, 0, (5 << 3) | 6, 10, 11, 12, 13, 14]; + const result = parsePatternEvents(buildPattern([midi, unknown])); + + assert.deepEqual(result.events[0], { + type: 'midi', + kind: 2, + position: 1, + source: 4, + flags: 5, + rawData: new Uint8Array(midi), + data: new Uint8Array([0xd0, 75, 3, 4]), + }); + assert.deepEqual(result.events[1], { + type: 'unknown', + kind: 6, + position: 2, + source: 5, + flags: 14, + rawData: new Uint8Array(unknown), + data: new Uint8Array([10, 11, 12, 13]), + }); +}); + +test('uses the uint16 header count and ignores undeclared or partial records', () => { + const records = Array.from({ length: 300 }, (_, index) => [ + index & 0xff, + index >> 8, + 0, + 60, + 100, + 1, + 0, + 0, + ]); + const data = buildPattern([...records, [0, 0, 1, 0, 0, 0, 0, 0]], 8, 300); + const truncated = data.slice(0, data.length - 3); + const result = parsePatternEvents(truncated); + + assert.equal(result.recordCount, 300); + assert.equal(result.events.length, 300); + assert.equal(result.events[299].position, 299); +});