You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
dateKeys-dart/test/pathrule_vm_test.dart

135 lines
4.8 KiB

Stage 4a: the rules of paths and texts on the Unicode tables lib/src/pathrule.dart ports internal/pathrule of datekeys-go at c531e93 (spec §29.5, §29.5.1, §29.6): NFD with the canonical ordering and Hangul, the case folding, the simple lowercase, Default_Ignorable with the whitelist of R4, the best-fit projections of R6c, every rule of a path (R2 to R6c and R10), the tree (R7 with its key and the two paths it names, and R9), and the texts of the comment and the declared author, with the texts of Go. canonicalTables is pathrule.Canonical, and a test recomputes tablesDigest from the lists. Go reads a string as bytes, and so does this port: the functions whose name ends in Utf8 take the bytes of a Go string, where a byte that is not valid UTF-8 is the rune U+FFFD, and the limits count bytes; the others take a String as utf8Bytes writes it. Each rule returns its violation, as in Go, and only the public functions throw. tool/pathrule_go_vectors.go runs in an export of datekeys-go, since internal/pathrule cannot be imported from outside its tree, and writes test/vectors/pathrule_vectors.json and its Dart copy: the cases of the tests of Go and of datekeys-ts, 1300 strings and 350 trees drawn from a fixed seed (marks, Hangul, ignorables, emoji, best-fit look-alikes, device names, 8.3 aliases, limits, texts and invalid UTF-8), the cases of R9, code points, and for each plane the SHA-256 of one line per code point of each function. Every code point of every plane gives the results of Go: planes 0, 1 and 14 also on Node.js. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 days ago
// The rules of paths and texts against files: the copy of
// test/vectors/pathrule_vectors.json that pathrule_test.dart reads, a Dart
// constant for the tests compiled to JavaScript, is the JSON file byte for
// byte; the shared vectors of testdata/, paths.json and path_fold.json of
// datekeys-go, give their results; and every code point of every plane
// gives the results of Go, compared through the SHA-256 of one line per
// code point that tool/pathrule_go_vectors.go computes with the reference:
// here the planes that pathrule_test.dart leaves out.
@TestOn('vm')
library;
import 'dart:convert';
import 'dart:io';
import 'package:datekeys/src/bytes.dart';
import 'package:datekeys/src/pathrule.dart';
import 'package:datekeys/src/pathrule_tables.dart';
import 'package:test/test.dart';
import 'pathrule_planes.dart';
import 'vectors/pathrule_vectors.g.dart';
Map<String, Object?> readJson(String path) =>
jsonDecode(File(path).readAsStringSync()) as Map<String, Object?>;
String outcome(void Function() f) {
try {
f();
return 'ok';
} on PathRuleException catch (e) {
return e.message;
}
}
void main() {
test('pathrule_vectors.g.dart holds pathrule_vectors.json', () {
final file = File('test/vectors/pathrule_vectors.json').readAsStringSync();
expect(pathruleVectorsJson, file);
});
group('testdata', () {
final paths = readJson('testdata/vectors/paths.json');
final fold = readJson('testdata/vectors/path_fold.json');
test('name the tables of this library', () {
for (final f in [paths, fold]) {
expect(f['unicode_version'], unicodeVersion);
expect(f['tables_digest'], tablesDigest);
}
});
test('paths.json: the rules of one entry', () {
final cases = (paths['paths']! as List).cast<Map<String, Object?>>();
expect(cases, hasLength(greaterThan(80)));
for (final c in cases) {
expect(
outcome(() => checkPath(c['path']! as String)),
c['result'],
reason: c['name'] as String?,
);
}
});
test('paths.json: the paths of a head, from layer 3 to layer 4', () {
// A head checks R1 and R8 in its third layer (ERR_NON_CANONICAL_CBOR),
// comparing UTF-8 bytes, never UTF-16 code units; then each entry,
// with R2 to R10, and R7 and R9 over the tree (ERR_HEAD_INVALID).
// The decoder of the head, in stage 4c, does this with the CBOR; here
// it is done with the rules of pathrule.dart.
final trees = (paths['trees']! as List).cast<Map<String, Object?>>();
expect(trees, hasLength(greaterThan(10)));
var utf16Differs = false;
for (final c in trees) {
final name = c['name']! as String;
final list = (c['paths']! as List).cast<String>();
var result = 'ok';
var detail = '';
for (var i = 0; i < list.length && result == 'ok'; i++) {
final n = utf8Bytes(list[i]).length;
if (n < 1 ||
n > maxPathLen ||
(i > 0 &&
compareBytes(utf8Bytes(list[i - 1]), utf8Bytes(list[i])) >=
0)) {
result = 'ERR_NON_CANONICAL_CBOR';
}
if (i > 0 && list[i - 1].compareTo(list[i]) >= 0 && result == 'ok') {
utf16Differs = true;
}
}
for (var i = 0; i < list.length && result == 'ok'; i++) {
final o = outcome(() => checkPath(list[i]));
if (o != 'ok') {
result = 'ERR_HEAD_INVALID';
detail = 'file ${i + 1}: $o';
}
}
if (result == 'ok') {
final o = outcome(() => checkTree(list));
if (o != 'ok') {
result = 'ERR_HEAD_INVALID';
detail = o;
}
}
expect(result, c['result'], reason: name);
expect(detail, c['detail'] ?? '', reason: name);
}
// U+FF5E before U+1F600 is in UTF-8 order, not in UTF-16 order.
expect(utf16Differs, isTrue);
});
test('path_fold.json: the NFD and the key of R7', () {
final keys = (fold['keys']! as List).cast<Map<String, Object?>>();
expect(keys, hasLength(greaterThan(20)));
for (final c in keys) {
final s = c['segment']! as String;
expect(nfd(s), c['nfd'], reason: c['name'] as String?);
expect(pathKey(s), c['key'], reason: c['name'] as String?);
}
});
});
test('every code point of the other planes gives the results of Go', () {
// Planes 0, 1 and 14 run in pathrule_test.dart, also compiled to
// JavaScript.
final vectors = jsonDecode(pathruleVectorsJson) as Map<String, Object?>;
final planes = (vectors['planes']! as List).cast<Map<String, Object?>>();
expect(planes, hasLength(17));
for (final p in planes) {
final plane = p['plane']! as int;
if (!sharedPlanes.contains(plane)) checkPlane(plane, p);
}
});
}

Powered by TurnKey Linux.