@@ -5,16 +5,21 @@ package dev.firechip.cobs
55
66import java.io.File
77import org.junit.Assert.assertArrayEquals
8+ import org.junit.Assert.assertFalse
89import org.junit.Assert.assertTrue
10+ import org.junit.Assert.fail
911import org.junit.Assume.assumeTrue
1012import org.junit.Test
1113
1214/* *
1315 * Verifies this library against the shared conformance vectors from
1416 * https://github.com/firechip/cobs-conformance
1517 *
16- * Skipped unless the `COBS_CONFORMANCE_VECTORS` environment variable points to a
17- * downloaded `vectors.jsonl` (CI sets this; local `./gradlew test` skips it).
18+ * Each check is skipped unless the corresponding environment variable points to a
19+ * downloaded JSONL file (CI sets these; local `./gradlew test` skips them):
20+ * - `COBS_CONFORMANCE_VECTORS` -> `vectors.jsonl` {decoded, cobs, cobsr}
21+ * - `COBS_CONFORMANCE_SENTINEL` -> `sentinel.jsonl` {decoded, sentinel, cobs, cobsr}
22+ * - `COBS_CONFORMANCE_ERRORS` -> `errors.jsonl` {encoded, cobs, cobsr}
1823 */
1924class ConformanceTest {
2025
@@ -52,4 +57,78 @@ class ConformanceTest {
5257 }
5358 assertTrue(" no vectors checked" , count > 0 )
5459 }
60+
61+ @Test
62+ fun conformsToSentinelVectors () {
63+ val path = System .getenv(" COBS_CONFORMANCE_SENTINEL" )
64+ assumeTrue(" set COBS_CONFORMANCE_SENTINEL to run" , ! path.isNullOrBlank())
65+
66+ val regex = Regex (
67+ " \" decoded\" :\" ([0-9a-f]*)\" ,\" sentinel\" :\" ([0-9a-f]*)\" ," +
68+ " \" cobs\" :\" ([0-9a-f]*)\" ,\" cobsr\" :\" ([0-9a-f]*)\" " ,
69+ )
70+ var count = 0
71+ File (path!! ).forEachLine { line ->
72+ if (line.isBlank()) return @forEachLine
73+ val match = regex.find(line) ? : error(" malformed sentinel line: $line " )
74+ val (d, s, c, cr) = match.destructured
75+ val data = hex(d)
76+ val sentinel = hex(s)[0 ]
77+ val cobs = hex(c)
78+ val cobsr = hex(cr)
79+ assertArrayEquals(" cobs sentinel $s encode $d " , cobs, Cobs .encodeWithSentinel(data, sentinel))
80+ assertArrayEquals(" cobsr sentinel $s encode $d " , cobsr, Cobsr .encodeWithSentinel(data, sentinel))
81+ assertArrayEquals(" cobs sentinel $s decode $c " , data, Cobs .decodeWithSentinel(cobs, sentinel))
82+ assertArrayEquals(" cobsr sentinel $s decode $cr " , data, Cobsr .decodeWithSentinel(cobsr, sentinel))
83+ assertFalse(" sentinel $s must not appear in cobs $c " , cobs.contains(sentinel))
84+ assertFalse(" sentinel $s must not appear in cobsr $cr " , cobsr.contains(sentinel))
85+ count++
86+ }
87+ assertTrue(" no sentinel vectors checked" , count > 0 )
88+ }
89+
90+ @Test
91+ fun conformsToErrorVectors () {
92+ val path = System .getenv(" COBS_CONFORMANCE_ERRORS" )
93+ assumeTrue(" set COBS_CONFORMANCE_ERRORS to run" , ! path.isNullOrBlank())
94+
95+ val regex = Regex (
96+ " \" encoded\" :\" ([0-9a-f]*)\" ,\" cobs\" :(null|\" [0-9a-f]*\" )," +
97+ " \" cobsr\" :(null|\" [0-9a-f]*\" )" ,
98+ )
99+ var count = 0
100+ File (path!! ).forEachLine { line ->
101+ if (line.isBlank()) return @forEachLine
102+ val match = regex.find(line) ? : error(" malformed error line: $line " )
103+ val (e, c, cr) = match.destructured
104+ val encoded = hex(e)
105+ checkDecode(" cobs decode $e " , c, encoded) { Cobs .decode(it) }
106+ checkDecode(" cobsr decode $e " , cr, encoded) { Cobsr .decode(it) }
107+ count++
108+ }
109+ assertTrue(" no error vectors checked" , count > 0 )
110+ }
111+
112+ /* *
113+ * Asserts that [decode] applied to [encoded] either fails (when [field] is the
114+ * JSON literal `null`) or produces the hex payload quoted in [field].
115+ */
116+ private fun checkDecode (
117+ label : String ,
118+ field : String ,
119+ encoded : ByteArray ,
120+ decode : (ByteArray ) -> ByteArray ,
121+ ) {
122+ if (field == " null" ) {
123+ try {
124+ decode(encoded)
125+ fail(" $label : expected decode to fail but it succeeded" )
126+ } catch (_: CobsDecodeException ) {
127+ // expected: this input MUST fail to decode
128+ }
129+ } else {
130+ val expected = hex(field.substring(1 , field.length - 1 ))
131+ assertArrayEquals(label, expected, decode(encoded))
132+ }
133+ }
55134}
0 commit comments