| 1 | import { describe, expect, it } from "vite-plus/test"; |
| 2 | |
| 3 | import { extractIcnsPng } from "./icns.ts"; |
| 4 | |
| 5 | function pngHeader(size: number): Uint8Array { |
| 6 | const png = new Uint8Array(24); |
| 7 | png.set([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0, 0, 0, 13, 0x49, 0x48, 0x44, 0x52]); |
| 8 | new DataView(png.buffer).setUint32(16, size); |
| 9 | new DataView(png.buffer).setUint32(20, size); |
| 10 | return png; |
| 11 | } |
| 12 | |
| 13 | function icns(...entries: ReadonlyArray<readonly [type: string, data: Uint8Array]>): Uint8Array { |
| 14 | const length = 8 + entries.reduce((total, [, data]) => total + 8 + data.length, 0); |
| 15 | const bytes = new Uint8Array(length); |
| 16 | const view = new DataView(bytes.buffer); |
| 17 | bytes.set(new TextEncoder().encode("icns")); |
| 18 | view.setUint32(4, length); |
| 19 | let offset = 8; |
| 20 | for (const [type, data] of entries) { |
| 21 | bytes.set(new TextEncoder().encode(type), offset); |
| 22 | view.setUint32(offset + 4, 8 + data.length); |
| 23 | bytes.set(data, offset + 8); |
| 24 | offset += 8 + data.length; |
| 25 | } |
| 26 | return bytes; |
| 27 | } |
| 28 | |
| 29 | const widthOf = (png: Uint8Array | null) => |
| 30 | png ? new DataView(png.buffer, png.byteOffset).getUint32(16) : null; |
| 31 | |
| 32 | describe("extractIcnsPng", () => { |
| 33 | it("picks the smallest PNG that covers the requested size", () => { |
| 34 | const file = icns( |
| 35 | ["TOC ", new Uint8Array(16)], |
| 36 | ["ic09", pngHeader(512)], |
| 37 | ["icp5", pngHeader(32)], |
| 38 | ["ic07", pngHeader(128)], |
| 39 | ["ic08", pngHeader(256)], |
| 40 | ); |
| 41 | |
| 42 | expect(widthOf(extractIcnsPng(file, 96))).toBe(128); |
| 43 | }); |
| 44 | |
| 45 | it("falls back to the largest PNG when none covers the requested size", () => { |
| 46 | const file = icns(["icp4", pngHeader(16)], ["icp5", pngHeader(32)]); |
| 47 | |
| 48 | expect(widthOf(extractIcnsPng(file, 96))).toBe(32); |
| 49 | }); |
| 50 | |
| 51 | it("returns null for files without a PNG entry or with a foreign header", () => { |
| 52 | expect(extractIcnsPng(icns(["it32", new Uint8Array(64)]), 96)).toBeNull(); |
| 53 | expect(extractIcnsPng(pngHeader(128), 96)).toBeNull(); |
| 54 | }); |
| 55 | |
| 56 | it("stops at an entry whose length runs past the file", () => { |
| 57 | const file = icns(["ic07", pngHeader(128)], ["ic08", pngHeader(256)]); |
| 58 | new DataView(file.buffer).setUint32(8 + 8 + 24 + 4, 4096); |
| 59 | |
| 60 | expect(widthOf(extractIcnsPng(file, 200))).toBe(128); |
| 61 | }); |
| 62 | }); |