import { assert, assertEquals } from "assert"; import { b, stubbingWarn } from "../helpers.ts"; import { PngNodeType } from "../../public/png/constants.js"; import parsePng from "../../public/png/parsePng.js"; const FIXTURES_URL = new URL("./fixtures/", import.meta.url); const getFixture = (name: string): Promise => Deno.readFile(new URL(name, FIXTURES_URL)); async function* getFixtureNames(): AsyncIterable { for await (const dirEntry of Deno.readDir(FIXTURES_URL)) { if (!dirEntry.isFile) continue; const { name } = dirEntry; if (name.endsWith(".png")) yield name; } } const t = (type: string) => new TextEncoder().encode(type); Deno.test( "parses PNG test suite reasonably", stubbingWarn(async () => { const fixturesToSkip = new Set([ // Incorrect bit depths, which we don't detect. "xd0n2c08.png", "xd3n2c08.png", "xd9n2c08.png", // Incorrect color types, which we don't detect. "xc1n0g08.png", "xc9n2c08.png", // Missing an IDAT chunk, which we don't detect. "xdtn0g01.png", ]); for await (const fixtureName of getFixtureNames()) { const fixture = await getFixture(fixtureName); if (fixturesToSkip.has(fixtureName)) continue; if (fixtureName.startsWith("x")) { assertEquals( parsePng(fixture), null, `${fixtureName} should fail to parse`, ); } else { try { parsePng(fixture); } catch { assert(false, `${fixtureName} should parse`); } } } }), ); Deno.test("parses a basic PNG", async () => { const fixture = await getFixture("basn2c16.png"); assertEquals(parsePng(fixture), { type: PngNodeType.root, bytes: fixture, children: [ { type: PngNodeType.signature, bytes: fixture.subarray(0, 8), }, { type: PngNodeType.ihdr, bytes: fixture.subarray(8, 8 + 25), children: [ { type: PngNodeType.chunkLength, bytes: b(0, 0, 0, 13) }, { type: PngNodeType.chunkType, bytes: t("IHDR") }, { type: PngNodeType.ihdrChunkData, bytes: fixture.subarray(16, 16 + 13), children: [ { type: PngNodeType.ihdrWidth, bytes: b(0, 0, 0, 32) }, { type: PngNodeType.ihdrHeight, bytes: b(0, 0, 0, 32) }, { type: PngNodeType.ihdrBitDepth, bytes: b(16) }, { type: PngNodeType.ihdrColourType, bytes: b(2) }, { type: PngNodeType.ihdrCompressionMethod, bytes: b(0) }, { type: PngNodeType.ihdrFilterMethod, bytes: b(0) }, { type: PngNodeType.ihdrInterlaceMethod, bytes: b(0) }, ], }, { type: PngNodeType.chunkCrc, bytes: b(172, 136, 49, 224) }, ], }, { type: PngNodeType.gama, bytes: fixture.subarray(33, 33 + 16), children: [ { type: PngNodeType.chunkLength, bytes: b(0, 0, 0, 4) }, { type: PngNodeType.chunkType, bytes: t("gAMA") }, { type: PngNodeType.chunkData, bytes: b(0, 1, 134, 160) }, { type: PngNodeType.chunkCrc, bytes: b(49, 232, 150, 95) }, ], }, { type: PngNodeType.idat, bytes: fixture.subarray(49, 49 + 241), children: [ { type: PngNodeType.chunkLength, bytes: b(0, 0, 0, 229) }, { type: PngNodeType.chunkType, bytes: t("IDAT") }, { type: PngNodeType.idatChunkData, bytes: fixture.subarray(57, 57 + 229), }, { type: PngNodeType.chunkCrc, bytes: b(7, 187, 196, 236) }, ], }, { type: PngNodeType.iend, bytes: fixture.subarray(290, 290 + 12), children: [ { type: PngNodeType.iendChunkLength, bytes: b(0, 0, 0, 0) }, { type: PngNodeType.chunkType, bytes: t("IEND") }, { type: PngNodeType.chunkCrc, bytes: b(0xae, 0x42, 0x60, 0x82) }, ], }, ], }); });