pure-zstd/test/hgtz_block_test.dart
Tag d1caf353b3 Bring the decoder over from the project it grew in
Eighteen commits of history, grafted onto the scaffold rather than over
it. It leaves behind everything that only meant something inside that
project: paths under one machine's mount, references to a transcoder
that is not here, and a benchmark defaulting to a tile nobody else has.

One fixture is real terrain and stays, because it is the only small
input that exercises the direct-nibble Huffman weight form. Its licence
requires attribution to travel with it, so the README's
acknowledgements and the corpus manifest both carry the Copernicus
notice now. Everything else committed here is synthetic or was written
by the zstd CLI, including the .hgtz container, whose name looks like a
tile and whose samples are a function of their position.
2026-09-22 19:25:08 -06:00

65 lines
2.5 KiB
Dart

import 'dart:io';
import 'dart:typed_data';
import 'package:pure_zstd/pure_zstd.dart';
import 'package:test/test.dart';
/// The committed container was written by the Python transcoder these frames
/// come from, so every block is a real level-19 pyzstd frame rather than
/// anything this package produced. Its samples are synthetic, though: this
/// file carries no terrain.
///
/// Every sample in it is `(northRow * 1000 + column) % 30000`, which makes the
/// expected bytes of each block computable without a reference decompressor.
void main() {
group('a real .hgtz container', () {
final bytes = File('test/fixtures/N51E000.hgtz').readAsBytesSync();
final header = ByteData.sublistView(bytes);
final rows = header.getUint16(6);
final columns = header.getUint16(8);
final block = header.getUint16(10);
final blockColumns = (columns + block - 1) ~/ block;
final blockRows = (rows + block - 1) ~/ block;
test('has the shape its README documents', () {
expect(rows, 1201);
expect(columns, 121);
expect(block, 256);
expect(blockRows * blockColumns, 5);
});
test('decodes every block to the samples the generator wrote', () {
final decoder = ZstdDecoder();
for (var index = 0; index < blockRows * blockColumns; index++) {
final entry = 12 + 8 * index;
final offset = header.getUint32(entry);
final length = header.getUint32(entry + 4);
final blockRow = index ~/ blockColumns;
final blockColumn = index % blockColumns;
final top = blockRow * block;
final left = blockColumn * block;
final height = block < rows - top ? block : rows - top;
final width = block < columns - left ? block : columns - left;
final frame = Uint8List.sublistView(bytes, offset, offset + length);
final out = decoder.decode(frame);
expect(out.length, height * width * 2, reason: 'block $index size');
final samples = ByteData.sublistView(out);
for (var row = 0; row < height; row++) {
for (var column = 0; column < width; column++) {
// Row 0 of the container is the north edge, which is what the
// generator counted from.
final northRow = top + row;
final expected = (northRow * 1000 + left + column) % 30000;
expect(
samples.getInt16((row * width + column) * 2),
expected,
reason: 'block $index at $row,$column',
);
}
}
}
});
});
}