konoebml/tests/decoder.spec.ts

136 lines
4.2 KiB
TypeScript

import {
EbmlStreamDecoder as Decoder,
EbmlDataTag,
EbmlElementType,
EbmlTagPosition,
type EbmlTagType,
} from 'konoebml';
import { assert, describe, it } from 'vitest';
const bufFrom = (data: Uint8Array | readonly number[]): ArrayBuffer =>
new Uint8Array(data).buffer;
const getDecoderWithNullSink = () => {
const decoder = new Decoder();
decoder.readable.pipeTo(new WritableStream({}));
return decoder;
};
async function collectTags(decoder: Decoder): Promise<EbmlTagType[]> {
const tags: EbmlTagType[] = [];
await decoder.readable.pipeTo(
new WritableStream({
write: (tag) => {
tags.push(tag);
},
})
);
return tags;
}
describe('EbmlStreamDecoder', () => {
it('should wait for more data if a tag is longer than the buffer', async () => {
const decoder = getDecoderWithNullSink();
const writer = decoder.writable.getWriter();
await writer.write(bufFrom([0x1a, 0x45]));
assert.strictEqual(decoder.transformer.getBuffer().byteLength, 2);
});
it('should clear the buffer after a full tag is written in one chunk', async () => {
const decoder = getDecoderWithNullSink();
const writer = decoder.writable.getWriter();
await writer.write(bufFrom([0x42, 0x86, 0x81, 0x01]));
assert.strictEqual(decoder.transformer.getBuffer().byteLength, 0);
});
it('should clear the buffer after a full tag is written in multiple chunks', async () => {
const decoder = getDecoderWithNullSink();
const writer = decoder.writable.getWriter();
await writer.write(bufFrom([0x42, 0x86]));
await writer.write(bufFrom([0x81, 0x01]));
assert.strictEqual(decoder.transformer.getBuffer().byteLength, 0);
});
it('should increment the cursor on each step', async () => {
const decoder = getDecoderWithNullSink();
const writer = decoder.writable.getWriter();
await writer.write(bufFrom([0x42])); // 4
assert.strictEqual(decoder.transformer.getBuffer().byteLength, 1);
await writer.write(bufFrom([0x86])); // 5
assert.strictEqual(decoder.transformer.getBuffer().byteLength, 2);
await writer.write(bufFrom([0x81])); // 6 & 7
assert.strictEqual(decoder.transformer.getBuffer().byteLength, 0);
await writer.write(bufFrom([0x01])); // 6 & 7
assert.strictEqual(decoder.transformer.getBuffer().byteLength, 0);
});
it('should emit correct tag events for simple data', async () => {
const decoder = new Decoder();
const writer = decoder.writable.getWriter();
const tags = collectTags(decoder);
await writer.write(bufFrom([0x42, 0x86, 0x81, 0x01]));
await writer.close();
const [tag] = await tags;
assert.strictEqual(tag.position, EbmlTagPosition.Content);
assert.strictEqual(tag.id.toString(16), '4286');
assert.strictEqual(tag.contentLength, 0x01);
assert.strictEqual(tag.type, EbmlElementType.UnsignedInt);
assert.ok(tag instanceof EbmlDataTag);
assert.deepStrictEqual(tag.data, 1);
});
it('should emit correct EBML tag events for master tags', async () => {
const decoder = new Decoder();
const writer = decoder.writable.getWriter();
writer.write(bufFrom([0x1a, 0x45, 0xdf, 0xa3, 0x80]));
writer.close();
const [tag] = await collectTags(decoder);
assert.strictEqual(tag.position, EbmlTagPosition.Start);
assert.strictEqual(tag.id.toString(16), '1a45dfa3');
assert.strictEqual(tag.contentLength, 0);
assert.strictEqual(tag.type, EbmlElementType.Master);
assert.ok(!(tag instanceof EbmlDataTag));
assert.ok(!('data' in tag));
});
it('should emit correct EBML:end events for master tags', async () => {
const decoder = new Decoder();
const writer = decoder.writable.getWriter();
writer.write(bufFrom([0x1a, 0x45, 0xdf, 0xa3]));
writer.write(bufFrom([0x84, 0x42, 0x86, 0x81, 0x00]));
writer.close();
const tags = await collectTags(decoder);
assert.strictEqual(tags.length, 3);
const firstEndTag = tags.find((t) => t.position === EbmlTagPosition.End)!;
assert.strictEqual(firstEndTag.id.toString(16), '1a45dfa3');
assert.strictEqual(firstEndTag.contentLength, 4);
assert.strictEqual(firstEndTag.type, EbmlElementType.Master);
assert.ok(!(firstEndTag instanceof EbmlDataTag));
assert.ok(!('data' in firstEndTag));
});
});