Skip to content

Commit a58cfe5

Browse files
committed
test: verify sentinel + decode-error conformance vectors
Harness + CI now check sentinel.jsonl and errors.jsonl at vectors v1.1.0.
1 parent f7951f4 commit a58cfe5

2 files changed

Lines changed: 76 additions & 3 deletions

File tree

.github/workflows/ci.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ jobs:
6868
- name: Install dependencies
6969
run: dart pub get
7070
- name: Download shared conformance vectors
71-
run: curl -sSfL -o vectors.jsonl https://raw.githubusercontent.com/firechip/cobs-conformance/v1.0.0/vectors/vectors.jsonl
71+
run: |
72+
base=https://raw.githubusercontent.com/firechip/cobs-conformance/v1.1.0/vectors
73+
curl -sSfL -o vectors.jsonl "$base/vectors.jsonl"
74+
curl -sSfL -o sentinel.jsonl "$base/sentinel.jsonl"
75+
curl -sSfL -o errors.jsonl "$base/errors.jsonl"
7276
- name: Check conformance
73-
run: dart run tool/conformance.dart vectors.jsonl
77+
run: dart run tool/conformance.dart vectors.jsonl sentinel.jsonl errors.jsonl

tool/conformance.dart

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
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]
55
import 'dart:convert';
66
import 'dart:io';
77
import 'dart:typed_data';
@@ -21,6 +21,8 @@ String _toHex(List<int> b) =>
2121

2222
void 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

Comments
 (0)