pure-zstd/test/corpus_test.dart

125 lines
4.4 KiB
Dart
Raw Normal View History

import 'dart:typed_data';
import 'package:pure_zstd/pure_zstd.dart';
import 'package:test/test.dart';
import 'corpus.dart';
/// The committed corpus, which exists because `hgtz_block_test.dart` decodes
/// one container from one producer at one level and so proves the sequence
/// loop and Huffman literals on level-19 terrain and nothing else.
///
/// The frames here reach the parts of RFC 8878 that 16,490 real `.hgtz`
/// blocks never touch: raw blocks, RLE blocks, RLE literals, a frame with no
/// content size, a frame with a checksum, the eight-byte content size, and
/// the direct-nibble Huffman weight form.
///
/// Nothing below asserts what this decoder happens to do. A legal frame is
/// compared against what pyzstd decompressed it to; a refused frame is one
/// pyzstd also refuses. `tool/make_corpus.py` records both, along with the
/// exact command behind each frame, in `test/fixtures/corpus/manifest.json`.
void main() {
final corpus = loadCorpus();
group('a legal frame decodes to the bytes pyzstd gives', () {
for (final entry in corpus.where(
(entry) => entry.legal && entry.trailing == null,
)) {
test('${entry.name}: ${entry.command}', () {
final out = ZstdDecoder().decode(
entry.frame,
expectedSize: entry.expectedSize,
);
expect(out.length, entry.size);
expect(out, orderedEquals(entry.plain));
});
}
});
group('a malformed frame is refused, never crashed on', () {
for (final entry in corpus.where((entry) => !entry.legal)) {
// A `ZstdException` and nothing else. `RangeError`, `OutOfMemoryError`
// and `IndexError` are all `Error` rather than `Exception`, so a caller
// catching `ZstdException` does not catch them and the isolate dies.
test('${entry.name}: ${entry.why}', () {
expect(
() => ZstdDecoder().decode(entry.frame),
throwsA(isA<ZstdException>()),
);
});
}
});
group('two frames in one buffer', () {
final entry = corpus.singleWhere((entry) => entry.name == 'twoframes');
final first = 5000;
test('${entry.command}: decodes to both or refuses, never to the first '
'alone', () {
Uint8List? out;
try {
out = ZstdDecoder().decode(entry.frame);
} on ZstdException {
return; // Refusing is the other acceptable answer.
}
expect(
out.length,
isNot(first),
reason: 'returned the first frame and stopped, raising nothing',
);
expect(out, orderedEquals(entry.plain));
});
});
group('a frame whose declared size is not the one asked for', () {
// A guard callers rest on, and it had no test:
// deleting the whole check left every other case green. A caller that
// knows the size, as the .hgtz reader does, gets an exact comparison
// against the frame's own figure rather than only the ceiling. Left to
// the end, a frame declaring less decoded its own content happily and a
// frame declaring more allocated whatever it asked for first.
for (final entry in corpus.where(
// Not a buffer holding more than one frame: this decoder refuses
// those whatever size it is told to expect.
(entry) =>
entry.legal &&
entry.expectedSize == null &&
entry.trailing == null &&
entry.size! > 1,
)) {
test('${entry.name} is refused when asked for one byte more', () {
expect(
() =>
ZstdDecoder().decode(entry.frame, expectedSize: entry.size! + 1),
throwsA(isA<ZstdException>()),
);
});
test('${entry.name} still decodes when asked for the size it has', () {
expect(
ZstdDecoder().decode(entry.frame, expectedSize: entry.size),
orderedEquals(entry.plain),
);
});
}
});
group('a frame declaring no content size', () {
final entry = corpus.singleWhere((entry) => entry.name == 'nofcs_windowed');
final size = entry.expectedSize!;
test('decodes when given the size it really has', () {
final out = ZstdDecoder().decode(entry.frame, expectedSize: size);
expect(out, orderedEquals(entry.plain));
});
for (final wrong in <int>[size - 1, size + 1, 0]) {
test('is refused when given $wrong instead of $size', () {
expect(
() => ZstdDecoder().decode(entry.frame, expectedSize: wrong),
throwsA(isA<ZstdException>()),
);
});
}
});
}