@@ -23,6 +23,22 @@ fn field<'a>(line: &'a str, key: &str) -> &'a str {
2323 & rest[ ..end]
2424}
2525
26+ /// Extracts `"<key>":<value>` where the value is either a quoted hex string or
27+ /// the JSON literal `null`. Returns `None` for `null` (i.e. decode must fail).
28+ fn field_opt < ' a > ( line : & ' a str , key : & str ) -> Option < & ' a str > {
29+ let pat = format ! ( "\" {key}\" :" ) ;
30+ let start = line. find ( & pat) . expect ( "missing key" ) + pat. len ( ) ;
31+ let rest = & line[ start..] ;
32+ if rest. starts_with ( "null" ) {
33+ return None ;
34+ }
35+ let rest = rest
36+ . strip_prefix ( '"' )
37+ . expect ( "expected quoted value or null" ) ;
38+ let end = rest. find ( '"' ) . expect ( "unterminated value" ) ;
39+ Some ( & rest[ ..end] )
40+ }
41+
2642#[ test]
2743fn conforms_to_shared_vectors ( ) {
2844 let path = match std:: env:: var ( "COBS_CONFORMANCE_VECTORS" ) {
@@ -61,3 +77,108 @@ fn conforms_to_shared_vectors() {
6177 assert ! ( count > 0 , "no vectors found in {path}" ) ;
6278 eprintln ! ( "conformance: {count} vectors verified" ) ;
6379}
80+
81+ #[ test]
82+ fn conforms_to_sentinel_vectors ( ) {
83+ let path = match std:: env:: var ( "COBS_CONFORMANCE_SENTINEL" ) {
84+ Ok ( p) if !p. is_empty ( ) => p,
85+ _ => {
86+ eprintln ! ( "skipping: set COBS_CONFORMANCE_SENTINEL to run" ) ;
87+ return ;
88+ }
89+ } ;
90+
91+ let file = std:: fs:: File :: open ( & path) . expect ( "open sentinel file" ) ;
92+ let mut count = 0usize ;
93+ for line in BufReader :: new ( file) . lines ( ) {
94+ let line = line. expect ( "read line" ) ;
95+ if line. trim ( ) . is_empty ( ) {
96+ continue ;
97+ }
98+ let decoded = from_hex ( field ( & line, "decoded" ) ) ;
99+ let sentinel = from_hex ( field ( & line, "sentinel" ) ) ;
100+ assert_eq ! ( sentinel. len( ) , 1 , "sentinel must be a single byte" ) ;
101+ let sentinel = sentinel[ 0 ] ;
102+ let e_cobs = from_hex ( field ( & line, "cobs" ) ) ;
103+ let e_cobsr = from_hex ( field ( & line, "cobsr" ) ) ;
104+
105+ assert_eq ! (
106+ cobs:: encode_to_vec_with_sentinel( & decoded, sentinel) ,
107+ e_cobs,
108+ "cobs encode_with_sentinel"
109+ ) ;
110+ assert_eq ! (
111+ cobsr:: encode_to_vec_with_sentinel( & decoded, sentinel) ,
112+ e_cobsr,
113+ "cobsr encode_with_sentinel"
114+ ) ;
115+ assert_eq ! (
116+ cobs:: decode_to_vec_with_sentinel( & e_cobs, sentinel) . unwrap( ) ,
117+ decoded,
118+ "cobs decode_with_sentinel"
119+ ) ;
120+ assert_eq ! (
121+ cobsr:: decode_to_vec_with_sentinel( & e_cobsr, sentinel) . unwrap( ) ,
122+ decoded,
123+ "cobsr decode_with_sentinel"
124+ ) ;
125+ assert ! (
126+ !e_cobs. contains( & sentinel) ,
127+ "sentinel byte must not appear in cobs output"
128+ ) ;
129+ assert ! (
130+ !e_cobsr. contains( & sentinel) ,
131+ "sentinel byte must not appear in cobsr output"
132+ ) ;
133+ count += 1 ;
134+ }
135+ assert ! ( count > 0 , "no sentinel vectors found in {path}" ) ;
136+ eprintln ! ( "conformance: {count} sentinel vectors verified" ) ;
137+ }
138+
139+ #[ test]
140+ fn conforms_to_error_vectors ( ) {
141+ let path = match std:: env:: var ( "COBS_CONFORMANCE_ERRORS" ) {
142+ Ok ( p) if !p. is_empty ( ) => p,
143+ _ => {
144+ eprintln ! ( "skipping: set COBS_CONFORMANCE_ERRORS to run" ) ;
145+ return ;
146+ }
147+ } ;
148+
149+ let file = std:: fs:: File :: open ( & path) . expect ( "open errors file" ) ;
150+ let mut count = 0usize ;
151+ for line in BufReader :: new ( file) . lines ( ) {
152+ let line = line. expect ( "read line" ) ;
153+ if line. trim ( ) . is_empty ( ) {
154+ continue ;
155+ }
156+ let encoded = from_hex ( field ( & line, "encoded" ) ) ;
157+
158+ match field_opt ( & line, "cobs" ) {
159+ Some ( hex) => assert_eq ! (
160+ cobs:: decode_to_vec( & encoded) . unwrap( ) ,
161+ from_hex( hex) ,
162+ "cobs decode"
163+ ) ,
164+ None => assert ! (
165+ cobs:: decode_to_vec( & encoded) . is_err( ) ,
166+ "cobs decode must fail"
167+ ) ,
168+ }
169+ match field_opt ( & line, "cobsr" ) {
170+ Some ( hex) => assert_eq ! (
171+ cobsr:: decode_to_vec( & encoded) . unwrap( ) ,
172+ from_hex( hex) ,
173+ "cobsr decode"
174+ ) ,
175+ None => assert ! (
176+ cobsr:: decode_to_vec( & encoded) . is_err( ) ,
177+ "cobsr decode must fail"
178+ ) ,
179+ }
180+ count += 1 ;
181+ }
182+ assert ! ( count > 0 , "no error vectors found in {path}" ) ;
183+ eprintln ! ( "conformance: {count} error vectors verified" ) ;
184+ }
0 commit comments