11// Verifies this package against the shared conformance vectors from
22// https://github.com/firechip/cobs-conformance
33//
4- // Usage: dart run tool/conformance.dart <vectors.jsonl>
4+ // Usage: dart run tool/conformance.dart <vectors.jsonl> [sentinel.jsonl] [errors.jsonl]
55import 'dart:convert' ;
66import 'dart:io' ;
77import 'dart:typed_data' ;
@@ -21,6 +21,8 @@ String _toHex(List<int> b) =>
2121
2222void main (List <String > args) {
2323 final path = args.isNotEmpty ? args[0 ] : 'vectors.jsonl' ;
24+ final sentinelPath = args.length > 1 ? args[1 ] : null ;
25+ final errorsPath = args.length > 2 ? args[2 ] : null ;
2426 var checked = 0 ;
2527 var failures = 0 ;
2628
@@ -48,6 +50,73 @@ void main(List<String> args) {
4850 checked++ ;
4951 }
5052
53+ // Configurable-sentinel vectors: encode/decode round-trips for an arbitrary
54+ // sentinel byte, plus the invariant that the sentinel never appears in output.
55+ if (sentinelPath != null ) {
56+ for (final line in File (sentinelPath).readAsLinesSync ()) {
57+ if (line.trim ().isEmpty) continue ;
58+ final v = json.decode (line) as Map <String , dynamic >;
59+ final decodedHex = v['decoded' ] as String ;
60+ final sentinelHex = v['sentinel' ] as String ;
61+ final cobsHex = v['cobs' ] as String ;
62+ final cobsrHex = v['cobsr' ] as String ;
63+ final decoded = _hex (decodedHex);
64+ final sentinel = _hex (sentinelHex)[0 ];
65+
66+ check (_toHex (cobsEncodeWithSentinel (decoded, sentinel)) == cobsHex,
67+ 'sentinel $sentinelHex cobs encode $decodedHex ' );
68+ check (_toHex (cobsrEncodeWithSentinel (decoded, sentinel)) == cobsrHex,
69+ 'sentinel $sentinelHex cobsr encode $decodedHex ' );
70+ check (
71+ _toHex (cobsDecodeWithSentinel (_hex (cobsHex), sentinel)) == decodedHex,
72+ 'sentinel $sentinelHex cobs decode $cobsHex ' );
73+ check (
74+ _toHex (cobsrDecodeWithSentinel (_hex (cobsrHex), sentinel)) ==
75+ decodedHex,
76+ 'sentinel $sentinelHex cobsr decode $cobsrHex ' );
77+ check (! _hex (cobsHex).contains (sentinel),
78+ 'sentinel $sentinelHex present in cobs $cobsHex ' );
79+ check (! _hex (cobsrHex).contains (sentinel),
80+ 'sentinel $sentinelHex present in cobsr $cobsrHex ' );
81+ checked++ ;
82+ }
83+ }
84+
85+ // Error/decode-outcome vectors: decoding `encoded` must yield the expected
86+ // hex, or throw a CobsDecodeException when the expected value is JSON null.
87+ if (errorsPath != null ) {
88+ void checkOutcome (String label, Uint8List input, Object ? expected,
89+ Uint8List Function (List <int >) decode, String encodedHex) {
90+ if (expected == null ) {
91+ var threw = false ;
92+ try {
93+ decode (input);
94+ } on CobsDecodeException {
95+ threw = true ;
96+ }
97+ check (threw, '$label decode should fail $encodedHex ' );
98+ } else {
99+ try {
100+ check (_toHex (decode (input)) == expected,
101+ '$label decode $encodedHex -> $expected ' );
102+ } on CobsDecodeException {
103+ check (false , '$label decode unexpectedly failed $encodedHex ' );
104+ }
105+ }
106+ }
107+
108+ for (final line in File (errorsPath).readAsLinesSync ()) {
109+ if (line.trim ().isEmpty) continue ;
110+ final v = json.decode (line) as Map <String , dynamic >;
111+ final encodedHex = v['encoded' ] as String ;
112+ final encoded = _hex (encodedHex);
113+
114+ checkOutcome ('cobs' , encoded, v['cobs' ], cobsDecode, encodedHex);
115+ checkOutcome ('cobsr' , encoded, v['cobsr' ], cobsrDecode, encodedHex);
116+ checked++ ;
117+ }
118+ }
119+
51120 stdout.writeln ('Conformance: checked $checked vectors, $failures failures.' );
52121 if (checked == 0 ) {
53122 stderr.writeln ('ERROR: no vectors found in $path ' );
0 commit comments