formats.exposed/test/png/parsePng.test.ts

125 lines
3.9 KiB
TypeScript

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<Uint8Array> =>
Deno.readFile(new URL(name, FIXTURES_URL));
async function* getFixtureNames(): AsyncIterable<string> {
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) },
],
},
],
});
});