// 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 readJson(String path) => jsonDecode(File(path).readAsStringSync()) as Map; 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>(); 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>(); 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(); 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>(); 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; final planes = (vectors['planes']! as List).cast>(); expect(planes, hasLength(17)); for (final p in planes) { final plane = p['plane']! as int; if (!sharedPlanes.contains(plane)) checkPlane(plane, p); } }); }