@@ -154,6 +154,9 @@ mod tests {
154
154
] )
155
155
}
156
156
157
+ /// This test makes sure we sensibly handle config schemas that would normally be banned by cmc.
158
+ /// It's like a golden test but the golden infrastructure relies on being able to use real
159
+ /// component manifests so this operates directly on TokenStreams.
157
160
#[ test]
158
161
fn bad_field_names ( ) {
159
162
let decl = config_decl ! {
@@ -197,6 +200,16 @@ type Config = struct {
197
200
use fidl:: unpersist;
198
201
use fuchsia_inspect:: { Node } ;
199
202
use fuchsia_runtime:: { take_startup_handle, HandleInfo , HandleType } ;
203
+ use std:: convert:: TryInto ;
204
+
205
+ // This is generated from the config schema for the component. Component Manager also
206
+ // computes this in parallel to allow config libraries that its config VMO's ABI matches
207
+ // expectations.
208
+ const EXPECTED_CHECKSUM : & [ u8 ] = & [
209
+ 0xb5 , 0xf9 , 0x33 , 0xe8 , 0x94 , 0x56 , 0x3a , 0xf9 , 0x61 , 0x39 , 0xe5 , 0x05 , 0x79 , 0x4b ,
210
+ 0x88 , 0xa5 , 0x3e , 0xd4 , 0xd1 , 0x5c , 0x32 , 0xe2 , 0xb4 , 0x49 , 0x9e , 0x42 , 0xeb , 0xa3 ,
211
+ 0x32 , 0xb1 , 0xf5 , 0xbb
212
+ ] ;
200
213
201
214
#[ derive( Debug ) ]
202
215
pub struct Config {
@@ -213,55 +226,144 @@ type Config = struct {
213
226
}
214
227
215
228
impl Config {
229
+ /// Take the config startup handle and parse its contents.
230
+ ///
231
+ /// # Panics
232
+ ///
233
+ /// If the config startup handle was already taken or if it is not valid.
216
234
pub fn take_from_startup_handle( ) -> Self {
217
- let config_vmo: zx:: Vmo = take_startup_handle( HandleInfo :: new( HandleType :: ComponentConfigVmo , 0 ) )
218
- . expect( "Config VMO handle must be provided and cannot already have been taken." )
235
+ let handle_info = HandleInfo :: new( HandleType :: ComponentConfigVmo , 0 ) ;
236
+ let config_vmo: zx:: Vmo = take_startup_handle( handle_info)
237
+ . expect( "Config VMO handle must be present." )
219
238
. into( ) ;
220
- let config_size = config_vmo. get_content_size( ) . expect( "must be able to read config vmo content size" ) ;
221
- assert_ne!( config_size, 0 , "config vmo must be non-empty" ) ;
222
-
223
- let config_bytes = config_vmo. read_to_vec( 0 , config_size) . expect( "must be able to read config vmo" ) ;
224
-
225
- let checksum_length = u16 :: from_le_bytes( [ config_bytes[ 0 ] , config_bytes[ 1 ] ] ) as usize ;
226
- let fidl_start = 2 + checksum_length;
227
- let observed_checksum = & config_bytes[ 2 ..fidl_start] ;
228
- let expected_checksum = vec![
229
- 0xb5 , 0xf9 , 0x33 , 0xe8 , 0x94 , 0x56 , 0x3a , 0xf9 , 0x61 , 0x39 , 0xe5 , 0x05 , 0x79 ,
230
- 0x4b , 0x88 , 0xa5 , 0x3e , 0xd4 , 0xd1 , 0x5c , 0x32 , 0xe2 , 0xb4 , 0x49 , 0x9e , 0x42 ,
231
- 0xeb , 0xa3 , 0x32 , 0xb1 , 0xf5 , 0xbb
232
- ] ;
233
-
234
- assert_eq!( observed_checksum, expected_checksum, "checksum from config VMO does not match expected checksum" ) ;
239
+ Self :: from_vmo( & config_vmo) . expect( "Config VMO handle must be valid." )
240
+ }
235
241
236
- let fidl_config: FidlConfig = unpersist( & config_bytes[ fidl_start..] ) . expect( "must be able to parse bytes as config FIDL" ) ;
242
+ /// Parse `Self` from `vmo`.
243
+ pub fn from_vmo( vmo: & zx:: Vmo ) -> Result <Self , Error > {
244
+ let config_size = vmo. get_content_size( ) . map_err( Error :: GettingContentSize ) ?;
245
+ let config_bytes =
246
+ vmo. read_to_vec( 0 , config_size) . map_err( Error :: ReadingConfigBytes ) ?;
247
+ Self :: from_bytes( & config_bytes)
248
+ }
237
249
238
- Self {
250
+ /// Parse `Self` from `bytes`.
251
+ pub fn from_bytes( bytes: & [ u8 ] ) -> Result <Self , Error > {
252
+ let ( checksum_len_bytes, bytes) =
253
+ bytes. split_at_checked( 2 ) . ok_or( Error :: TooFewBytes ) ?;
254
+ let checksum_len_bytes: [ u8 ; 2 ] = checksum_len_bytes
255
+ . try_into( )
256
+ . expect( "previous call guaranteed 2 element slice" ) ;
257
+ let checksum_length = u16 :: from_le_bytes( checksum_len_bytes) as usize ;
258
+ let ( observed_checksum, bytes) =
259
+ bytes. split_at_checked( checksum_length) . ok_or( Error :: TooFewBytes ) ?;
260
+ if observed_checksum != EXPECTED_CHECKSUM {
261
+ return Err ( Error :: ChecksumMismatch {
262
+ observed_checksum: observed_checksum. to_vec( ) ,
263
+ } ) ;
264
+ }
265
+ let fidl_config: FidlConfig = unpersist( bytes) . map_err( Error :: Unpersist ) ?;
266
+ Ok ( Self {
239
267
snake_case_string: fidl_config. snake_case_string,
240
268
lower_camel_case_string: fidl_config. lower_camel_case_string,
241
269
upper_camel_case_string: fidl_config. upper_camel_case_string,
242
270
const_case: fidl_config. const_case,
243
271
string_that_has02_digits: fidl_config. string_that_has02_digits,
244
- mixed_lower_camel_snake_case_string: fidl_config. mixed_lower_camel_snake_case_string,
245
- mixed_upper_camel_snake_case_string: fidl_config. mixed_upper_camel_snake_case_string,
272
+ mixed_lower_camel_snake_case_string: fidl_config
273
+ . mixed_lower_camel_snake_case_string,
274
+ mixed_upper_camel_snake_case_string: fidl_config
275
+ . mixed_upper_camel_snake_case_string,
246
276
multiple__underscores: fidl_config. multiple__underscores,
247
277
unsafe_: fidl_config. unsafe_,
248
278
server_mode_: fidl_config. server_mode_
249
- }
279
+ } )
250
280
}
251
- pub fn record_inspect( & self , inspector_node : & Node ) {
281
+ pub fn record_inspect( & self , inspector_node: & Node ) {
252
282
inspector_node. record_bool( "snake_case_string" , self . snake_case_string) ;
253
- inspector_node. record_bool( "lowerCamelCaseString" , self . lower_camel_case_string) ;
254
- inspector_node. record_bool( "UpperCamelCaseString" , self . upper_camel_case_string) ;
283
+ inspector_node
284
+ . record_bool( "lowerCamelCaseString" , self . lower_camel_case_string) ;
285
+ inspector_node
286
+ . record_bool( "UpperCamelCaseString" , self . upper_camel_case_string) ;
255
287
inspector_node. record_bool( "CONST_CASE" , self . const_case) ;
256
- inspector_node. record_bool( "stringThatHas02Digits" , self . string_that_has02_digits) ;
257
- inspector_node. record_bool( "mixedLowerCamel_snakeCaseString" , self . mixed_lower_camel_snake_case_string) ;
258
- inspector_node. record_bool( "MixedUpperCamel_SnakeCaseString" , self . mixed_upper_camel_snake_case_string) ;
288
+ inspector_node
289
+ . record_bool( "stringThatHas02Digits" , self . string_that_has02_digits) ;
290
+ inspector_node. record_bool(
291
+ "mixedLowerCamel_snakeCaseString" ,
292
+ self . mixed_lower_camel_snake_case_string
293
+ ) ;
294
+ inspector_node. record_bool(
295
+ "MixedUpperCamel_SnakeCaseString" ,
296
+ self . mixed_upper_camel_snake_case_string
297
+ ) ;
259
298
inspector_node. record_bool( "multiple__underscores" , self . multiple__underscores) ;
260
299
inspector_node. record_bool( "unsafe" , self . unsafe_) ;
261
300
inspector_node. record_bool( "ServerMode" , self . server_mode_) ;
262
301
}
263
302
}
264
- } . to_string ( ) ;
303
+
304
+ #[ derive( Debug ) ]
305
+ pub enum Error {
306
+ /// Failed to read the content size of the VMO.
307
+ GettingContentSize ( zx:: Status ) ,
308
+ /// Failed to read the content of the VMO.
309
+ ReadingConfigBytes ( zx:: Status ) ,
310
+ /// The VMO was too small for this config library.
311
+ TooFewBytes ,
312
+ /// The VMO's config ABI checksum did not match this library's.
313
+ ChecksumMismatch { observed_checksum: Vec <u8 > } ,
314
+ /// Failed to parse the non-checksum bytes of the VMO as this library's FIDL type.
315
+ Unpersist ( fidl:: Error ) ,
316
+ }
317
+
318
+ impl std:: fmt:: Display for Error {
319
+ fn fmt( & self , f: & mut std:: fmt:: Formatter <' _>) -> std:: fmt:: Result {
320
+ match self {
321
+ Self :: GettingContentSize ( status) => {
322
+ write!( f, "Failed to get content size: {status}" )
323
+ }
324
+ Self :: ReadingConfigBytes ( status) => {
325
+ write!( f, "Failed to read VMO content: {status}" )
326
+ }
327
+ Self :: TooFewBytes => {
328
+ write!( f, "VMO content is not large enough for this config library." )
329
+ }
330
+ Self :: ChecksumMismatch { observed_checksum } => {
331
+ write!(
332
+ f,
333
+ "ABI checksum mismatch, expected {:?}, got {:?}" ,
334
+ EXPECTED_CHECKSUM , observed_checksum,
335
+ )
336
+ }
337
+ Self :: Unpersist ( fidl_error) => {
338
+ write!( f, "Failed to parse contents of config VMO: {fidl_error}" )
339
+ }
340
+ }
341
+ }
342
+ }
343
+
344
+ impl std:: error:: Error for Error {
345
+ #[ allow( unused_parens, reason = "rustfmt errors without parens here" ) ]
346
+ fn source( & self ) -> Option <( & ' _ ( dyn std:: error:: Error + ' static ) ) > {
347
+ match self {
348
+ Self :: GettingContentSize ( ref status)
349
+ | Self :: ReadingConfigBytes ( ref status) => Some ( status) ,
350
+ Self :: TooFewBytes => None ,
351
+ Self :: ChecksumMismatch { .. } => None ,
352
+ Self :: Unpersist ( ref fidl_error) => Some ( fidl_error) ,
353
+ }
354
+ }
355
+ fn description( & self ) -> & str {
356
+ match self {
357
+ Self :: GettingContentSize ( _) => "getting content size" ,
358
+ Self :: ReadingConfigBytes ( _) => "reading VMO contents" ,
359
+ Self :: TooFewBytes => "VMO contents too small" ,
360
+ Self :: ChecksumMismatch { .. } => "ABI checksum mismatch" ,
361
+ Self :: Unpersist ( _) => "FIDL parsing error" ,
362
+ }
363
+ }
364
+ }
365
+ }
366
+ . to_string ( ) ;
265
367
266
368
assert_eq ! ( actual_rust_src, expected_rust_src) ;
267
369
}
0 commit comments