Files
SnowLuma/packages/protocol/tests/highway/utils.test.ts
T
TanTan fa9c73363d
Dev Build / cleanup (push) Waiting to run
Dev Build / typecheck (push) Waiting to run
Publish MCP to npm / Build, test, and publish MCP (push) Waiting to run
Publish SDK to npm / Build, test, and publish SDK (push) Waiting to run
Sync action catalog to docs / sync (push) Waiting to run
Publish UI to npm / Build and publish UI (push) Waiting to run
Build and Release / preflight (push) Waiting to run
SnowLuma v2.4.2 source snapshot
2026-11-13 09:02:00 +08:00

176 lines
6.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, it, expect } from 'vitest';
import { detectImageFormat } from '@snowluma/protocol/highway/utils';
// Regression coverage for detectImageFormat (was previously untested). The
// header-parsing is all hand-rolled byte math, so each format's offset AND
// endianness is pinned here. The VP8X cases exist because that branch read the
// canvas width/height big-endian when the WebP spec stores them 24-bit
// little-endian (issue #112: a 1920×1080 image came back as 8324865×3605505,
// cropping the QQ thumbnail). VP8 / VP8L were already little-endian.
// ── little-endian byte writers (match how the formats store dimensions) ──
function le16(v: number): number[] {
return [v & 0xff, (v >> 8) & 0xff];
}
function le24(v: number): number[] {
return [v & 0xff, (v >> 8) & 0xff, (v >> 16) & 0xff];
}
function le32(v: number): number[] {
return [v & 0xff, (v >> 8) & 0xff, (v >> 16) & 0xff, (v >> 24) & 0xff];
}
function be16(v: number): number[] {
return [(v >> 8) & 0xff, v & 0xff];
}
function be32(v: number): number[] {
return [(v >> 24) & 0xff, (v >> 16) & 0xff, (v >> 8) & 0xff, v & 0xff];
}
function ascii(s: string): number[] {
return [...s].map((c) => c.charCodeAt(0));
}
/** Build a buffer from segments placed at explicit offsets; gaps are 0. */
function buf(len: number, ...segs: Array<[number, number[]]>): Uint8Array {
const b = new Uint8Array(len);
for (const [off, bytes] of segs) {
b.set(bytes, off);
}
return b;
}
describe('detectImageFormat', () => {
it('PNG — IHDR width/height (big-endian @16/@20) → 1001', () => {
const b = buf(24, [0, [0x89, 0x50, 0x4e, 0x47]], [16, be32(800)], [20, be32(600)]);
expect(detectImageFormat(b)).toEqual({ format: 1001, width: 800, height: 600 });
});
it('GIF — logical screen width/height (little-endian @6/@8) → 2000', () => {
const b = buf(10, [0, ascii('GIF89a')], [6, le16(320)], [8, le16(240)]);
expect(detectImageFormat(b)).toEqual({ format: 2000, width: 320, height: 240 });
// GIF87a header variant is also accepted.
const b87 = buf(10, [0, ascii('GIF87a')], [6, le16(48)], [8, le16(16)]);
expect(detectImageFormat(b87)).toEqual({ format: 2000, width: 48, height: 16 });
});
it('BMP — DIB width/height (little-endian @18/@22) → 1005', () => {
const b = buf(26, [0, ascii('BM')], [18, le32(1024)], [22, le32(768)]);
expect(detectImageFormat(b)).toEqual({ format: 1005, width: 1024, height: 768 });
});
it('WebP VP8 (lossy) — little-endian @26/@28 → 1002', () => {
const b = buf(
32,
[0, ascii('RIFF')],
[8, ascii('WEBP')],
[12, ascii('VP8 ')],
[26, le16(640)],
[28, le16(480)],
);
expect(detectImageFormat(b)).toEqual({ format: 1002, width: 640, height: 480 });
});
it('WebP VP8L (lossless) — 14-bit fields packed in LE32 @21 → 1002', () => {
// width-1 in bits[0..13], height-1 in bits[14..27].
const bits = (100 - 1) | ((200 - 1) << 14);
const b = buf(
32,
[0, ascii('RIFF')],
[8, ascii('WEBP')],
[12, ascii('VP8L')],
[21, le32(bits >>> 0)],
);
expect(detectImageFormat(b)).toEqual({ format: 1002, width: 100, height: 200 });
});
it('WebP VP8X (extended) — canvas width/height are 24-bit LITTLE-endian +1 @24/@27 → 1002 (issue #112)', () => {
const b = buf(
34,
[0, ascii('RIFF')],
[8, ascii('WEBP')],
[12, ascii('VP8X')],
[24, le24(1920 - 1)],
[27, le24(1080 - 1)],
);
expect(detectImageFormat(b)).toEqual({ format: 1002, width: 1920, height: 1080 });
});
it('WebP VP8X — asymmetric dims pin byte order (BE misread would give huge/swapped values)', () => {
// 256×1: width-1=255 → LE bytes [ff,00,00]; a BE read of those = 16711680.
// height-1=0 → [00,00,00]. Distinct w≠h also catches an offset swap.
const b = buf(
34,
[0, ascii('RIFF')],
[8, ascii('WEBP')],
[12, ascii('VP8X')],
[24, le24(256 - 1)],
[27, le24(1 - 1)],
);
expect(detectImageFormat(b)).toEqual({ format: 1002, width: 256, height: 1 });
});
it('WebP VP8X — exact bytes from issue #112 decode to 1920×1080, not 8324865×3605505', () => {
// The reported broken values came from these literal canvas bytes.
const b = buf(
34,
[0, ascii('RIFF')],
[8, ascii('WEBP')],
[12, ascii('VP8X')],
[24, [0x7f, 0x07, 0x00]],
[27, [0x37, 0x04, 0x00]],
);
expect(detectImageFormat(b)).toEqual({ format: 1002, width: 1920, height: 1080 });
});
it('JPEG — dimensions from the SOF0 segment (big-endian, height before width) → 1000', () => {
// FFD8 SOI, then SOF0 (FFC0) segment: len(BE16)=17, precision, height@+5, width@+7.
// Buffer must hold the whole declared 17-byte segment (offset 2..20) or the
// parser's bounds-check bails before reading — hence length 24, not 20.
const b = buf(
24,
[0, [0xff, 0xd8]],
[2, [0xff, 0xc0]],
[4, be16(17)],
[6, [8]],
[7, be16(720)],
[9, be16(1280)],
);
expect(detectImageFormat(b)).toEqual({ format: 1000, width: 1280, height: 720 });
});
it('JPEG — skips a DHT (0xFFC4) segment before SOF0 (must not misread Huffman bytes)', () => {
// SOI, DHT(FFC4) len=4 (2 payload bytes), then SOF0 with real 640×480.
const b = buf(
28,
[0, [0xff, 0xd8]],
[2, [0xff, 0xc4]],
[4, be16(4)],
[6, [0xde, 0xad]],
[8, [0xff, 0xc0]],
[10, be16(17)],
[12, [8]],
[13, be16(480)],
[15, be16(640)],
);
expect(detectImageFormat(b)).toEqual({ format: 1000, width: 640, height: 480 });
});
it('unknown bytes fall back to {1000, 0, 0} without throwing', () => {
expect(detectImageFormat(new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]))).toEqual({
format: 1000,
width: 0,
height: 0,
});
});
it('a truncated WebP header (<30 bytes) does not crash and falls through', () => {
const b = buf(20, [0, ascii('RIFF')], [8, ascii('WEBP')], [12, ascii('VP8X')]);
expect(detectImageFormat(b)).toEqual({ format: 1000, width: 0, height: 0 });
});
});