clone

an offline map explorer where each world, from a game map to a city's open data, ships as one .atlas file

feat/atlas-next-horizon
atlas / render / test / atlasloc.test.ts
atlasloc.test.ts 4.4K
1// The packed locations, judged against the corpus.2//3// The corpus carries three things that make this a real test rather than a4// round trip against my own opinion: the record list, the packed byte length,5// and the SHA-256 of the packed bytes as the reference implementation wrote6// them. So the test PACKS the corpus records per `docs/format.md` §7, checks7// the bytes it produced against the recorded digest — which pins the layout,8// the alignment, the offsets and the encoding all at once — and only then9// reads them back with the seam's reader and holds every column to the10// record.11//12// If the layout were misread in any way that mattered, the digest would move13// before the reader ever ran.1415import { createHash } from "node:crypto";16import test from "node:test";17import { strict as assert } from "node:assert";18import { LocationTable } from "../data/atlasloc.ts";19import { locations, payloads, volumes } from "./fixtures.ts";20import type { LocationsFixture } from "./fixtures.ts";2122/** Pack corpus records exactly as format.md §7 lays the bytes out. */23function pack(fixture: LocationsFixture): ArrayBuffer {24 const n = fixture.locations.length;25 const encoder = new TextEncoder();26 const titles = fixture.locations.map((record) => encoder.encode(record.title));27 const titleBytes = titles.reduce((held, run) => held + run.length, 0);28 const buffer = new ArrayBuffer(20 + 26 * n + titleBytes);29 const bytes = new Uint8Array(buffer);30 bytes.set(encoder.encode("ATLASLOC"), 0);31 const header = new DataView(buffer);32 header.setUint16(8, 3, true);33 header.setUint32(10, n, true);3435 const id = new Int32Array(buffer, 16, n);36 const lat = new Float32Array(buffer, 16 + 4 * n, n);37 const lng = new Float32Array(buffer, 16 + 8 * n, n);38 const member = new Int32Array(buffer, 16 + 12 * n, n);39 const shard = new Int32Array(buffer, 16 + 16 * n, n);40 const offsets = new Uint32Array(buffer, 16 + 20 * n, n + 1);41 const owner = new Uint16Array(buffer, 20 + 24 * n, n);4243 let at = 0;44 fixture.locations.forEach((record, i) => {45 id[i] = record.id;46 lat[i] = record.lat;47 lng[i] = record.lng;48 member[i] = record.member;49 shard[i] = record.shard;50 owner[i] = record.owner;51 offsets[i] = at;52 bytes.set(titles[i] ?? new Uint8Array(), 20 + 26 * n + at);53 at += (titles[i] ?? new Uint8Array()).length;54 });55 offsets[n] = at;56 return buffer;57}5859test("the packed layout is the one the corpus was written with", () => {60 let checked = 0;61 for (const slug of volumes()) {62 for (const world of payloads(slug).keys()) {63 const fixture = locations(slug, world);64 const packed = pack(fixture);65 assert.equal(packed.byteLength, fixture.packedBytes,66 `${slug}/${world}: packed length`);67 const digest = createHash("sha256").update(new Uint8Array(packed)).digest("hex");68 assert.equal(digest, fixture.packedSha256, `${slug}/${world}: packed digest`);69 checked++;70 }71 }72 assert.equal(checked, 2, "the city and the planet: every corpus world was checked");73});7475test("the reader answers every column the corpus records", () => {76 for (const slug of volumes()) {77 for (const world of payloads(slug).keys()) {78 const fixture = locations(slug, world);79 const table = LocationTable.over(pack(fixture));80 assert.equal(table.count, fixture.count, `${slug}/${world}: count`);81 for (let i = 0; i < table.count; i++) {82 assert.deepEqual(table.at(i), fixture.locations[i], `${slug}/${world}: location ${i}`);83 }84 }85 }86});8788test("a payload that is not this format is refused rather than guessed at", () => {89 const stranger = new ArrayBuffer(64);90 assert.throws(() => LocationTable.over(stranger), /magic/);9192 const wrongVersion = new ArrayBuffer(64);93 new Uint8Array(wrongVersion).set(new TextEncoder().encode("ATLASLOC"), 0);94 new DataView(wrongVersion).setUint16(8, 2, true);95 assert.throws(() => LocationTable.over(wrongVersion), /version 2/);96});9798test("titles are decoded once and are UTF-8, not NUL-terminated", () => {99 const fixture: LocationsFixture = {100 count: 2, packedBytes: 0, packedSha256: "", world: "w",101 locations: [102 { id: 1, owner: 0, lat: 0, lng: 0, member: 0, shard: 0, title: "Cañón — 峰" },103 { id: 2, owner: 1, lat: 0, lng: 0, member: 0, shard: 0, title: "" },104 ],105 };106 const table = LocationTable.over(pack(fixture));107 assert.equal(table.title(0), "Cañón — 峰");108 assert.equal(table.title(0), "Cañón — 峰");109 assert.equal(table.title(1), "");110});