1pub(crate) mod util;
6
7pub mod error;
8
9pub use crate::util::check_url;
10
11use crate::error::*;
12use crate::util::*;
13use cm_graph::DependencyNode;
14use cm_types::IterablePath;
15use directed_graph::DirectedGraph;
16use fidl_fuchsia_component_decl_common as fdecl;
17use itertools::Itertools;
18use std::collections::{BTreeSet, HashMap, HashSet};
19use std::path::Path;
20
21trait HasAvailability {
22 fn availability(&self) -> fdecl::Availability;
23}
24
25impl HasAvailability for fdecl::ExposeService {
26 fn availability(&self) -> fdecl::Availability {
27 return self.availability.unwrap_or(fdecl::Availability::Required);
28 }
29}
30
31impl HasAvailability for fdecl::OfferService {
32 fn availability(&self) -> fdecl::Availability {
33 return self.availability.unwrap_or(fdecl::Availability::Required);
34 }
35}
36
37pub fn validate_value_spec(spec: &fdecl::ConfigValueSpec) -> Result<(), ErrorList> {
41 let mut errors = vec![];
42 if let Some(value) = &spec.value {
43 match value {
44 fdecl::ConfigValue::Single(s) => match s {
45 fdecl::ConfigSingleValue::Bool(_)
46 | fdecl::ConfigSingleValue::Uint8(_)
47 | fdecl::ConfigSingleValue::Uint16(_)
48 | fdecl::ConfigSingleValue::Uint32(_)
49 | fdecl::ConfigSingleValue::Uint64(_)
50 | fdecl::ConfigSingleValue::Int8(_)
51 | fdecl::ConfigSingleValue::Int16(_)
52 | fdecl::ConfigSingleValue::Int32(_)
53 | fdecl::ConfigSingleValue::Int64(_)
54 | fdecl::ConfigSingleValue::String(_) => {}
55 fdecl::ConfigSingleValueUnknown!() => {
56 errors.push(Error::invalid_field(DeclType::ConfigValueSpec, "value"));
57 }
58 },
59 fdecl::ConfigValue::Vector(l) => match l {
60 fdecl::ConfigVectorValue::BoolVector(_)
61 | fdecl::ConfigVectorValue::Uint8Vector(_)
62 | fdecl::ConfigVectorValue::Uint16Vector(_)
63 | fdecl::ConfigVectorValue::Uint32Vector(_)
64 | fdecl::ConfigVectorValue::Uint64Vector(_)
65 | fdecl::ConfigVectorValue::Int8Vector(_)
66 | fdecl::ConfigVectorValue::Int16Vector(_)
67 | fdecl::ConfigVectorValue::Int32Vector(_)
68 | fdecl::ConfigVectorValue::Int64Vector(_)
69 | fdecl::ConfigVectorValue::StringVector(_) => {}
70 fdecl::ConfigVectorValueUnknown!() => {
71 errors.push(Error::invalid_field(DeclType::ConfigValueSpec, "value"));
72 }
73 },
74 fdecl::ConfigValueUnknown!() => {
75 errors.push(Error::invalid_field(DeclType::ConfigValueSpec, "value"));
76 }
77 }
78 } else {
79 errors.push(Error::missing_field(DeclType::ConfigValueSpec, "value"));
80 }
81
82 if errors.is_empty() { Ok(()) } else { Err(ErrorList::new(errors)) }
83}
84
85pub fn validate_values_data(data: &fdecl::ConfigValuesData) -> Result<(), ErrorList> {
93 let mut errors = vec![];
94 if let Some(values) = &data.values {
95 for spec in values {
96 if let Err(mut e) = validate_value_spec(spec) {
97 errors.append(&mut e.errs);
98 }
99 }
100 } else {
101 errors.push(Error::missing_field(DeclType::ConfigValuesData, "values"));
102 }
103
104 if let Some(checksum) = &data.checksum {
105 match checksum {
106 fdecl::ConfigChecksum::Sha256(_) => {}
107 fdecl::ConfigChecksumUnknown!() => {
108 errors.push(Error::invalid_field(DeclType::ConfigValuesData, "checksum"));
109 }
110 }
111 } else {
112 errors.push(Error::missing_field(DeclType::ConfigValuesData, "checksum"));
113 }
114
115 if errors.is_empty() { Ok(()) } else { Err(ErrorList::new(errors)) }
116}
117
118#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
120enum RefKey<'a> {
121 Parent,
122 Self_,
123 Child(&'a str),
124 Collection(&'a str),
125 Framework,
126 Capability,
127 Debug,
128}
129
130pub fn validate<'a>(
147 decl: &'a fdecl::Component,
148 dependencies: &'a mut DirectedGraph<DependencyNode>,
149) -> Result<(), ErrorList> {
150 let ctx = ValidationContext::new(dependencies);
151 ctx.validate(decl).map_err(|errs| ErrorList::new(errs))
152}
153
154fn validate_capabilities(
156 capabilities: &[fdecl::Capability],
157 as_builtin: bool,
158) -> Result<(), ErrorList> {
159 let mut deps = DirectedGraph::new();
160 let mut ctx = ValidationContext::new(&mut deps);
161
162 ctx.load_dictionary_names(capabilities.iter().filter_map(|capability| match capability {
163 fdecl::Capability::Dictionary(dictionary_decl) => Some(dictionary_decl),
164 _ => None,
165 }));
166
167 ctx.validate_capability_decls(capabilities, as_builtin);
168 if ctx.errors.is_empty() { Ok(()) } else { Err(ErrorList::new(ctx.errors)) }
169}
170
171pub fn validate_builtin_capabilities(
173 capabilities: &Vec<fdecl::Capability>,
174) -> Result<(), ErrorList> {
175 validate_capabilities(capabilities, true)
176}
177
178pub fn validate_namespace_capabilities(
180 capabilities: &Vec<fdecl::Capability>,
181) -> Result<(), ErrorList> {
182 validate_capabilities(capabilities, false)
183}
184
185type CheckChildNameFn = fn(Option<&String>, DeclType, &str, &mut Vec<Error>) -> bool;
188
189pub fn validate_dynamic_child(child: &fdecl::Child) -> Result<(), ErrorList> {
190 let mut errors = vec![];
191
192 if let Err(mut error_list) = validate_child(child, check_dynamic_name) {
193 errors.append(&mut error_list.errs);
194 }
195
196 if child.environment.is_some() {
197 errors.push(Error::DynamicChildWithEnvironment);
198 }
199
200 if errors.is_empty() { Ok(()) } else { Err(ErrorList { errs: errors }) }
201}
202
203fn validate_child(
206 child: &fdecl::Child,
207 check_child_name: CheckChildNameFn,
208) -> Result<(), ErrorList> {
209 let mut errors = vec![];
210 check_child_name(child.name.as_ref(), DeclType::Child, "name", &mut errors);
211 check_url(child.url.as_ref(), DeclType::Child, "url", &mut errors);
212 if child.startup.is_none() {
213 errors.push(Error::missing_field(DeclType::Child, "startup"));
214 }
215 if child.environment.is_some() {
217 check_name(child.environment.as_ref(), DeclType::Child, "environment", &mut errors);
218 }
219 if errors.is_empty() { Ok(()) } else { Err(ErrorList { errs: errors }) }
220}
221
222pub fn validate_dynamic_offers<'a>(
235 dynamic_children: Vec<(&'a str, &'a str)>,
236 static_children: impl IntoIterator<Item = &'a str>,
237 collections: impl IntoIterator<Item = &'a str>,
238 capabilities: &'a [fdecl::Capability],
239 static_offers: &'a [fdecl::Offer],
240 dependencies: &'a mut DirectedGraph<DependencyNode>,
241 new_dynamic_offers: &'a [fdecl::Offer],
242) -> Result<(), ErrorList> {
243 let mut ctx = ValidationContext::new(dependencies);
244 ctx.dynamic_children = dynamic_children;
245 ctx.validate_dynamic_offers(
246 static_children,
247 collections,
248 capabilities,
249 static_offers,
250 new_dynamic_offers,
251 )
252 .map_err(|errs| ErrorList::new(errs))
253}
254
255fn check_offer_name(
256 prop: Option<&String>,
257 decl: DeclType,
258 keyword: &str,
259 offer_type: OfferType,
260 errors: &mut Vec<Error>,
261) -> bool {
262 if offer_type == OfferType::Dynamic {
263 check_dynamic_name(prop, decl, keyword, errors)
264 } else {
265 check_name(prop, decl, keyword, errors)
266 }
267}
268
269struct ValidationContext<'a> {
270 all_children: HashSet<&'a str>,
271 all_collections: HashSet<&'a str>,
272 all_capability_ids: HashSet<&'a str>,
273 all_storages: HashMap<&'a str, Option<&'a fdecl::Ref>>,
274 all_services: HashSet<&'a str>,
275 all_protocols: HashSet<&'a str>,
276 all_directories: HashSet<&'a str>,
277 all_runners: HashSet<&'a str>,
278 all_resolvers: HashSet<&'a str>,
279 all_dictionaries: HashMap<&'a str, &'a fdecl::Dictionary>,
280
281 #[cfg(fuchsia_api_level_at_least = "HEAD")]
282 all_configs: HashSet<&'a str>,
283
284 all_environment_names: HashSet<&'a str>,
285 dynamic_children: Vec<(&'a str, &'a str)>,
286 strong_dependencies: &'a mut DirectedGraph<DependencyNode>,
287 target_ids: IdMap<'a>,
288 errors: Vec<Error>,
289}
290
291trait Container {
295 fn contains(&self, key: &str) -> bool;
296}
297
298impl<'a> Container for HashSet<&'a str> {
299 fn contains(&self, key: &str) -> bool {
300 self.contains(key)
301 }
302}
303
304impl<'a, T> Container for HashMap<&'a str, T> {
305 fn contains(&self, key: &str) -> bool {
306 self.contains_key(key)
307 }
308}
309
310impl<'a> ValidationContext<'a> {
311 fn new(strong_dependencies: &'a mut DirectedGraph<DependencyNode>) -> Self {
312 Self {
313 strong_dependencies,
314 all_children: Default::default(),
315 all_collections: Default::default(),
316 all_capability_ids: Default::default(),
317 all_storages: Default::default(),
318 all_services: Default::default(),
319 all_protocols: Default::default(),
320 all_directories: Default::default(),
321 all_runners: Default::default(),
322 all_resolvers: Default::default(),
323 all_dictionaries: Default::default(),
324
325 #[cfg(fuchsia_api_level_at_least = "HEAD")]
326 all_configs: Default::default(),
327
328 all_environment_names: Default::default(),
329 dynamic_children: Default::default(),
330 target_ids: Default::default(),
331 errors: Default::default(),
332 }
333 }
334
335 fn validate(mut self, decl: &'a fdecl::Component) -> Result<(), Vec<Error>> {
336 if let Some(envs) = &decl.environments {
338 self.collect_environment_names(&envs);
339 }
340
341 if let Some(children) = decl.children.as_ref() {
343 for child in children {
344 self.validate_child_decl(&child);
345 }
346 }
347
348 if let Some(collections) = decl.collections.as_ref() {
350 for collection in collections {
351 self.validate_collection_decl(&collection);
352 }
353 }
354
355 if let Some(capabilities) = decl.capabilities.as_ref() {
357 self.load_dictionary_names(capabilities.iter().filter_map(
358 |capability| match capability {
359 fdecl::Capability::Dictionary(dictionary_decl) => Some(dictionary_decl),
360 _ => None,
361 },
362 ));
363 self.validate_capability_decls(capabilities, false);
364 }
365
366 let mut use_runner_name = None;
368 let mut use_runner_source = None;
369 if let Some(uses) = decl.uses.as_ref() {
370 (use_runner_name, use_runner_source) = self.validate_use_decls(uses);
371 }
372
373 if let Some(program) = decl.program.as_ref() {
375 self.validate_program(program, use_runner_name, use_runner_source);
376 }
377
378 if let Some(exposes) = decl.exposes.as_ref() {
380 let mut expose_to_parent_ids = HashMap::new();
381 let mut expose_to_framework_ids = HashMap::new();
382 for expose in exposes.iter() {
383 self.validate_expose_decl(
384 &expose,
385 &mut expose_to_parent_ids,
386 &mut expose_to_framework_ids,
387 );
388 }
389 self.validate_expose_group(&exposes);
390 }
391
392 if let Some(offers) = decl.offers.as_ref() {
394 for offer in offers.iter() {
395 self.validate_offer_decl(&offer, OfferType::Static);
396 }
397 self.validate_offer_group(&offers, OfferType::Static);
398 }
399
400 if let Some(environment) = decl.environments.as_ref() {
402 for environment in environment {
403 self.validate_environment_decl(&environment);
404 }
405 }
406
407 self.validate_config(decl.config.as_ref(), decl.uses.as_ref());
409
410 cm_graph::generate_dependency_graph(
412 &mut self.strong_dependencies,
413 &decl,
414 &self.dynamic_children,
415 );
416 if let Err(e) = self.strong_dependencies.topological_sort() {
417 self.errors.push(Error::dependency_cycle(e.format_cycle()));
418 }
419
420 if self.errors.is_empty() { Ok(()) } else { Err(self.errors) }
421 }
422
423 fn validate_dynamic_offers(
424 mut self,
425 static_children: impl IntoIterator<Item = &'a str>,
426 collections: impl IntoIterator<Item = &'a str>,
427 capabilities: &'a [fdecl::Capability],
428 static_offers: &'a [fdecl::Offer],
429 new_dynamic_offers: &'a [fdecl::Offer],
430 ) -> Result<(), Vec<Error>> {
431 self.all_children.extend(static_children);
432 self.all_collections.extend(collections);
433
434 self.load_dictionary_names(capabilities.iter().filter_map(|capability| match capability {
435 fdecl::Capability::Dictionary(dictionary_decl) => Some(dictionary_decl),
436 _ => None,
437 }));
438 self.validate_capability_decls(capabilities, false);
439
440 for offer in static_offers.iter() {
441 self.validate_offer_decl(offer, OfferType::Static);
442 cm_graph::add_dependencies_from_offer(
443 &mut self.strong_dependencies,
444 offer,
445 &self.dynamic_children,
446 );
447 }
448 self.validate_offer_group(static_offers, OfferType::Static);
449
450 for dynamic_offer in new_dynamic_offers {
451 self.validate_offer_decl(dynamic_offer, OfferType::Dynamic);
452 cm_graph::add_dependencies_from_offer(
453 &mut self.strong_dependencies,
454 dynamic_offer,
455 &self.dynamic_children,
456 );
457 }
458 self.validate_offer_group(new_dynamic_offers, OfferType::Dynamic);
459
460 if let Err(e) = self.strong_dependencies.topological_sort() {
461 self.errors.push(Error::dependency_cycle(e.format_cycle()));
462 }
463
464 if self.errors.is_empty() { Ok(()) } else { Err(self.errors) }
465 }
466
467 fn collect_environment_names(&mut self, envs: &'a [fdecl::Environment]) {
469 for env in envs {
470 if let Some(name) = env.name.as_ref() {
471 if !self.all_environment_names.insert(name) {
472 self.errors.push(Error::duplicate_field(DeclType::Environment, "name", name));
473 }
474 }
475 }
476 }
477
478 fn validate_config(
481 &mut self,
482 config: Option<&fdecl::ConfigSchema>,
483 uses: Option<&Vec<fdecl::Use>>,
484 ) {
485 use std::collections::BTreeMap;
486
487 let optional_use_keys: BTreeMap<String, fdecl::ConfigType> =
489 uses.map_or(BTreeMap::new(), |u| {
490 u.iter()
491 .map(|u| {
492 let fdecl::Use::Config(config) = u else {
493 return None;
494 };
495 if config.availability == Some(fdecl::Availability::Required)
496 || config.availability == None
497 {
498 return None;
499 }
500 if let Some(_) = config.default.as_ref() {
501 return None;
502 }
503 let Some(key) = config.target_name.clone() else {
504 return None;
505 };
506 let Some(value) = config.type_.clone() else {
507 return None;
508 };
509 Some((key, value))
510 })
511 .flatten()
512 .collect()
513 });
514
515 for u in uses.iter().flat_map(|x| x.iter()) {
517 let fdecl::Use::Config(config) = u else { continue };
518 let Some(default) = config.default.as_ref() else { continue };
519 validate_value_spec(&fdecl::ConfigValueSpec {
520 value: Some(default.clone()),
521 ..Default::default()
522 })
523 .map_err(|mut e| self.errors.append(&mut e.errs))
524 .ok();
525 }
526
527 let Some(config) = config else {
528 if !optional_use_keys.is_empty() {
529 self.errors.push(Error::missing_field(DeclType::ConfigField, "config"))
530 }
531 return;
532 };
533
534 if let Some(fields) = &config.fields {
535 for field in fields {
536 if field.key.is_none() {
537 self.errors.push(Error::missing_field(DeclType::ConfigField, "key"));
538 }
539 if let Some(type_) = &field.type_ {
540 self.validate_config_type(type_, true);
541 } else {
542 self.errors.push(Error::missing_field(DeclType::ConfigField, "value_type"));
543 }
544 }
545 } else {
546 self.errors.push(Error::missing_field(DeclType::ConfigSchema, "fields"));
547 }
548
549 if let Some(checksum) = &config.checksum {
550 match checksum {
551 fdecl::ConfigChecksum::Sha256(_) => {}
552 fdecl::ConfigChecksumUnknown!() => {
553 self.errors.push(Error::invalid_field(DeclType::ConfigSchema, "checksum"));
554 }
555 }
556 } else {
557 self.errors.push(Error::missing_field(DeclType::ConfigSchema, "checksum"));
558 }
559
560 'outer: for (key, value) in optional_use_keys.iter() {
561 for field in config.fields.iter().flatten() {
562 if field.key.as_ref() == Some(key) {
563 if field.type_.as_ref() != Some(value) {
564 self.errors.push(Error::invalid_field(DeclType::ConfigField, key.clone()));
565 }
566 continue 'outer;
567 }
568 }
569 self.errors.push(Error::missing_field(DeclType::ConfigField, key.clone()));
570 }
571
572 match config.value_source {
573 None => self.errors.push(Error::missing_field(DeclType::ConfigSchema, "value_source")),
574 #[cfg(fuchsia_api_level_at_least = "HEAD")]
575 Some(fdecl::ConfigValueSource::Capabilities(_)) => {
576 if !optional_use_keys.is_empty() {
577 self.errors
578 .push(Error::invalid_field(DeclType::ConfigValueSource, "ValueSource"))
579 }
580 }
581 Some(fdecl::ConfigValueSource::__SourceBreaking { .. }) => {
582 self.errors.push(Error::invalid_field(DeclType::ConfigValueSource, "ValueSource"))
583 }
584 _ => (),
585 };
586 }
587
588 fn validate_config_type(&mut self, type_: &fdecl::ConfigType, accept_vectors: bool) {
589 match &type_.layout {
590 fdecl::ConfigTypeLayout::Bool
591 | fdecl::ConfigTypeLayout::Uint8
592 | fdecl::ConfigTypeLayout::Uint16
593 | fdecl::ConfigTypeLayout::Uint32
594 | fdecl::ConfigTypeLayout::Uint64
595 | fdecl::ConfigTypeLayout::Int8
596 | fdecl::ConfigTypeLayout::Int16
597 | fdecl::ConfigTypeLayout::Int32
598 | fdecl::ConfigTypeLayout::Int64 => {
599 if let Some(parameters) = &type_.parameters {
601 if !parameters.is_empty() {
602 self.errors
603 .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
604 }
605 } else {
606 self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"));
607 }
608
609 if !type_.constraints.is_empty() {
610 self.errors.push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
611 }
612 }
613 fdecl::ConfigTypeLayout::String => {
614 if let Some(parameters) = &type_.parameters {
616 if !parameters.is_empty() {
617 self.errors
618 .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
619 }
620 } else {
621 self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"));
622 }
623
624 if type_.constraints.is_empty() {
625 self.errors.push(Error::missing_field(DeclType::ConfigType, "constraints"));
626 } else if type_.constraints.len() > 1 {
627 self.errors.push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
628 } else if let fdecl::LayoutConstraint::MaxSize(_) = &type_.constraints[0] {
629 } else {
630 self.errors.push(Error::invalid_field(DeclType::ConfigType, "constraints"));
631 }
632 }
633 fdecl::ConfigTypeLayout::Vector => {
634 if accept_vectors {
635 if let Some(parameters) = &type_.parameters {
637 if parameters.is_empty() {
638 self.errors
639 .push(Error::missing_field(DeclType::ConfigType, "parameters"));
640 } else if parameters.len() > 1 {
641 self.errors
642 .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
643 } else if let fdecl::LayoutParameter::NestedType(nested_type) =
644 ¶meters[0]
645 {
646 self.validate_config_type(nested_type, false);
647 } else {
648 self.errors
649 .push(Error::invalid_field(DeclType::ConfigType, "parameters"));
650 }
651 } else {
652 self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"))
653 }
654
655 if type_.constraints.is_empty() {
656 self.errors.push(Error::missing_field(DeclType::ConfigType, "constraints"));
657 } else if type_.constraints.len() > 1 {
658 self.errors
659 .push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
660 } else if let fdecl::LayoutConstraint::MaxSize(_) = &type_.constraints[0] {
661 } else {
662 self.errors.push(Error::invalid_field(DeclType::ConfigType, "constraints"));
663 }
664 } else {
665 self.errors.push(Error::nested_vector());
666 }
667 }
668 _ => self.errors.push(Error::invalid_field(DeclType::ConfigType, "layout")),
669 }
670 }
671
672 fn validate_capability_decls(
673 &mut self,
674 capabilities: &'a [fdecl::Capability],
675 as_builtin: bool,
676 ) {
677 for capability in capabilities {
678 self.validate_capability_decl(capability, as_builtin);
679 }
680 }
681
682 fn validate_capability_decl(&mut self, capability: &'a fdecl::Capability, as_builtin: bool) {
687 match capability {
688 fdecl::Capability::Service(service) => self.validate_service_decl(&service, as_builtin),
689 fdecl::Capability::Protocol(protocol) => {
690 self.validate_protocol_decl(&protocol, as_builtin)
691 }
692 fdecl::Capability::Directory(directory) => {
693 self.validate_directory_decl(&directory, as_builtin)
694 }
695 fdecl::Capability::Storage(storage) => {
696 if as_builtin {
697 self.errors.push(Error::CapabilityCannotBeBuiltin(DeclType::Storage))
698 } else {
699 self.validate_storage_decl(&storage)
700 }
701 }
702 fdecl::Capability::Runner(runner) => self.validate_runner_decl(&runner, as_builtin),
703 fdecl::Capability::Resolver(resolver) => {
704 self.validate_resolver_decl(&resolver, as_builtin)
705 }
706 fdecl::Capability::EventStream(event) => {
707 if as_builtin {
708 self.validate_event_stream_decl(&event)
709 } else {
710 self.errors.push(Error::CapabilityMustBeBuiltin(DeclType::EventStream))
711 }
712 }
713 fdecl::Capability::Dictionary(dictionary) => {
714 self.validate_dictionary_decl(&dictionary);
715 }
716 #[cfg(fuchsia_api_level_at_least = "HEAD")]
717 fdecl::Capability::Config(config) => {
718 self.validate_configuration_decl(&config);
719 }
720 fdecl::CapabilityUnknown!() => self.errors.push(Error::UnknownCapability),
721 }
722 }
723
724 fn validate_use_decls(
726 &mut self,
727 uses: &'a [fdecl::Use],
728 ) -> (Option<&'a String>, Option<&'a fdecl::Ref>) {
729 for use_ in uses.iter() {
731 self.validate_use_decl(&use_);
732 }
733 self.validate_use_paths(&uses);
734
735 #[cfg(fuchsia_api_level_at_least = "HEAD")]
736 {
737 let mut use_runner_name = None;
738 let mut use_runner_source = None;
739 for use_ in uses.iter() {
740 if let fdecl::Use::Runner(use_runner) = use_ {
741 if use_runner_name.is_some() {
742 self.errors.push(Error::MultipleRunnersUsed);
743 }
744
745 use_runner_name = use_runner.source_name.as_ref();
746 use_runner_source = use_runner.source.as_ref();
747 }
748 }
749 return (use_runner_name, use_runner_source);
750 }
751 #[cfg(fuchsia_api_level_less_than = "HEAD")]
752 return (None, None);
753 }
754
755 fn validate_use_decl(&mut self, use_: &'a fdecl::Use) {
756 match use_ {
757 fdecl::Use::Service(u) => {
758 let decl = DeclType::UseService;
759 self.validate_use_fields(
760 decl,
761 Self::service_checker,
762 u.source.as_ref(),
763 u.source_name.as_ref(),
764 u.source_dictionary.as_ref(),
765 u.target_path.as_ref(),
766 u.dependency_type.as_ref(),
767 u.availability.as_ref(),
768 );
769 if u.dependency_type.is_none() {
770 self.errors.push(Error::missing_field(decl, "dependency_type"));
771 }
772 }
773 fdecl::Use::Protocol(u) => {
774 let decl = DeclType::UseProtocol;
775 self.validate_use_fields(
776 decl,
777 Self::protocol_checker,
778 u.source.as_ref(),
779 u.source_name.as_ref(),
780 u.source_dictionary.as_ref(),
781 u.target_path.as_ref(),
782 u.dependency_type.as_ref(),
783 u.availability.as_ref(),
784 );
785 #[cfg(not(fuchsia_api_level_at_least = "29"))]
786 let has_numbered_handle = false;
787 #[cfg(fuchsia_api_level_at_least = "29")]
788 let has_numbered_handle = u.numbered_handle.is_some();
789 if u.target_path.is_none() && !has_numbered_handle {
790 self.errors.push(Error::missing_field(decl, "target_path"));
791 }
792 if has_numbered_handle && u.target_path.is_some() {
793 self.errors.push(Error::extraneous_field(decl, "numbered_handle"));
794 }
795 if u.dependency_type.is_none() {
796 self.errors.push(Error::missing_field(decl, "dependency_type"));
797 }
798 }
799 fdecl::Use::Directory(u) => {
800 let decl = DeclType::UseDirectory;
801 self.validate_use_fields(
802 decl,
803 Self::directory_checker,
804 u.source.as_ref(),
805 u.source_name.as_ref(),
806 u.source_dictionary.as_ref(),
807 u.target_path.as_ref(),
808 u.dependency_type.as_ref(),
809 u.availability.as_ref(),
810 );
811 if u.dependency_type.is_none() {
812 self.errors.push(Error::missing_field(decl, "dependency_type"));
813 }
814 if u.rights.is_none() {
815 self.errors.push(Error::missing_field(DeclType::UseDirectory, "rights"));
816 }
817 if let Some(subdir) = u.subdir.as_ref() {
818 check_relative_path(
819 Some(subdir),
820 DeclType::UseDirectory,
821 "subdir",
822 &mut self.errors,
823 );
824 }
825 }
826 fdecl::Use::Storage(u) => {
827 const SOURCE: Option<fdecl::Ref> = Some(fdecl::Ref::Parent(fdecl::ParentRef {}));
828 const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
829 Some(fdecl::DependencyType::Strong);
830 self.validate_use_fields(
831 DeclType::UseStorage,
832 Self::storage_checker,
833 SOURCE.as_ref(),
834 u.source_name.as_ref(),
835 None,
836 u.target_path.as_ref(),
837 DEPENDENCY_TYPE.as_ref(),
838 u.availability.as_ref(),
839 );
840 }
841 fdecl::Use::EventStream(u) => {
842 const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
843 Some(fdecl::DependencyType::Strong);
844 let decl = DeclType::UseEventStream;
845 self.validate_use_fields(
846 decl,
847 Self::event_stream_checker,
848 u.source.as_ref(),
849 u.source_name.as_ref(),
850 None,
851 u.target_path.as_ref(),
852 DEPENDENCY_TYPE.as_ref(),
853 u.availability.as_ref(),
854 );
855 match u.source {
857 Some(fdecl::Ref::Child(_)) | Some(fdecl::Ref::Parent(_)) => {
858 }
860 Some(fdecl::Ref::Framework(_))
861 | Some(fdecl::Ref::Self_(_))
862 | Some(fdecl::Ref::Debug(_)) => {
863 self.errors.push(Error::invalid_field(decl, "source"));
865 }
866 Some(fdecl::Ref::Collection(_)) | Some(fdecl::RefUnknown!()) | None => {
867 }
869 }
870 if let Some(scope) = &u.scope {
871 for reference in scope {
872 if !matches!(reference, fdecl::Ref::Child(_) | fdecl::Ref::Collection(_)) {
873 self.errors.push(Error::invalid_field(decl, "scope"));
874 }
875 }
876 }
877 }
878 #[cfg(fuchsia_api_level_at_least = "HEAD")]
879 fdecl::Use::Runner(u) => {
880 const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
881 Some(fdecl::DependencyType::Strong);
882 const AVAILABILITY: Option<fdecl::Availability> =
883 Some(fdecl::Availability::Required);
884 let decl = DeclType::UseRunner;
885 self.validate_use_fields(
886 decl,
887 Self::runner_checker,
888 u.source.as_ref(),
889 u.source_name.as_ref(),
890 u.source_dictionary.as_ref(),
891 None,
892 DEPENDENCY_TYPE.as_ref(),
893 AVAILABILITY.as_ref(),
894 );
895 }
896 #[cfg(fuchsia_api_level_at_least = "HEAD")]
897 fdecl::Use::Config(u) => {
898 const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
899 Some(fdecl::DependencyType::Strong);
900 let decl = DeclType::UseConfiguration;
901 self.validate_use_fields(
902 decl,
903 Self::config_checker,
904 u.source.as_ref(),
905 u.source_name.as_ref(),
906 None,
907 None,
908 DEPENDENCY_TYPE.as_ref(),
909 u.availability.as_ref(),
910 );
911 }
912 #[cfg(fuchsia_api_level_at_least = "29")]
913 fdecl::Use::Dictionary(u) => {
914 let decl = DeclType::UseDictionary;
915 self.validate_use_fields(
916 decl,
917 Self::dictionary_checker,
918 u.source.as_ref(),
919 u.source_name.as_ref(),
920 u.source_dictionary.as_ref(),
921 u.target_path.as_ref(),
922 u.dependency_type.as_ref(),
923 u.availability.as_ref(),
924 );
925 if u.dependency_type.is_none() {
926 self.errors.push(Error::missing_field(decl, "dependency_type"));
927 }
928 }
929 fdecl::UseUnknown!() => {
930 self.errors.push(Error::invalid_field(DeclType::Component, "use"));
931 }
932 }
933 }
934
935 fn validate_program(
938 &mut self,
939 program: &fdecl::Program,
940 use_runner_name: Option<&String>,
941 _use_runner_source: Option<&fdecl::Ref>,
942 ) {
943 match &program.runner {
944 Some(_) =>
945 {
946 #[cfg(fuchsia_api_level_at_least = "HEAD")]
947 if use_runner_name.is_some() {
948 if use_runner_name != program.runner.as_ref()
949 || _use_runner_source
950 != Some(&fdecl::Ref::Environment(fdecl::EnvironmentRef))
951 {
952 self.errors.push(Error::ConflictingRunners);
953 }
954 }
955 }
956 None => {
957 if use_runner_name.is_none() {
958 self.errors.push(Error::MissingRunner);
959 }
960 }
961 }
962
963 if program.info.is_none() {
964 self.errors.push(Error::missing_field(DeclType::Program, "info"));
965 }
966 }
967
968 fn validate_use_paths(&mut self, uses: &[fdecl::Use]) {
971 fn use_to_path<'a>(use_: &'a fdecl::Use) -> Option<&'a Path> {
972 match use_ {
973 #[cfg(fuchsia_api_level_at_least = "29")]
974 fdecl::Use::Dictionary(fdecl::UseDictionary {
975 target_path: Some(path), ..
976 }) => Some(Path::new(path)),
977 fdecl::Use::Directory(fdecl::UseDirectory { target_path: Some(path), .. }) => {
978 Some(Path::new(path))
979 }
980 fdecl::Use::Protocol(fdecl::UseProtocol { target_path: Some(path), .. }) => {
981 Some(Path::new(path))
982 }
983 fdecl::Use::Service(fdecl::UseService { target_path: Some(path), .. }) => {
984 Some(Path::new(path))
985 }
986 fdecl::Use::Storage(fdecl::UseStorage { target_path: Some(path), .. }) => {
987 Some(Path::new(path))
988 }
989 _ => None,
990 }
991 }
992 enum NodeType {
993 Service,
994 Directory,
995 #[allow(unused)]
998 Dictionary,
999 }
1000 fn use_to_type(use_: &fdecl::Use) -> Option<NodeType> {
1001 match use_ {
1002 fdecl::Use::Config(_) => None,
1003 #[cfg(fuchsia_api_level_at_least = "29")]
1004 fdecl::Use::Dictionary(_) => Some(NodeType::Dictionary),
1005 fdecl::Use::Directory(_) => Some(NodeType::Directory),
1006 fdecl::Use::EventStream(_) => Some(NodeType::Service),
1007 fdecl::Use::Protocol(_) => Some(NodeType::Service),
1008 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1009 fdecl::Use::Runner(_) => None,
1010 fdecl::Use::Service(_) => Some(NodeType::Directory),
1011 fdecl::Use::Storage(_) => Some(NodeType::Directory),
1012 _ => None,
1013 }
1014 }
1015 let mut paths_and_uses =
1016 uses.iter().filter_map(|use_| use_to_path(use_).map(|x| (x, use_))).collect::<Vec<_>>();
1017 let mut duplicate_fields = false;
1018 while let Some((path, use_)) = paths_and_uses.pop() {
1019 if paths_and_uses.iter().any(|(path_from_iter, use_from_iter)| {
1020 path == *path_from_iter
1021 && !(DeclType::from(*use_from_iter) == DeclType::UseDictionary
1022 && DeclType::from(use_) == DeclType::UseDictionary)
1023 }) {
1024 self.errors.push(Error::duplicate_field(
1025 use_.into(),
1026 "target_path",
1027 path.to_str().unwrap(),
1028 ));
1029 duplicate_fields = true;
1030 }
1031 }
1032 if duplicate_fields {
1033 return;
1036 }
1037 for (use_a, use_b) in uses.iter().tuple_combinations() {
1038 let Some(path_a) = use_to_path(use_a) else {
1039 continue;
1040 };
1041 let Some(path_b) = use_to_path(use_b) else {
1042 continue;
1043 };
1044 let Some(type_a) = use_to_type(use_a) else {
1045 continue;
1046 };
1047 let Some(type_b) = use_to_type(use_b) else {
1048 continue;
1049 };
1050 let mut conflicts = false;
1051 match (type_a, type_b) {
1052 (NodeType::Service, NodeType::Service)
1053 | (NodeType::Directory, NodeType::Service)
1054 | (NodeType::Service, NodeType::Directory)
1055 | (NodeType::Directory, NodeType::Directory) => {
1056 if path_a.starts_with(path_b) || path_b.starts_with(path_a) {
1057 conflicts = true;
1058 }
1059 }
1060 (NodeType::Dictionary, NodeType::Service)
1061 | (NodeType::Dictionary, NodeType::Directory) => {
1062 if path_a.starts_with(path_b) {
1063 conflicts = true;
1064 }
1065 }
1066 (NodeType::Service, NodeType::Dictionary)
1067 | (NodeType::Directory, NodeType::Dictionary) => {
1068 if path_b.starts_with(path_a) {
1069 conflicts = true;
1070 }
1071 }
1072 (NodeType::Dictionary, NodeType::Dictionary) => {
1073 }
1075 }
1076 if conflicts {
1077 self.errors.push(Error::invalid_path_overlap(
1078 use_a.into(),
1079 path_a.to_str().unwrap(),
1080 use_b.into(),
1081 path_b.to_str().unwrap(),
1082 ));
1083 }
1084 }
1085 for use_ in uses {
1086 let Some(used_path) = use_to_path(use_) else {
1087 continue;
1088 };
1089 if used_path.starts_with(Path::new("/pkg")) {
1090 self.errors.push(Error::pkg_path_overlap(use_.into(), used_path.to_str().unwrap()));
1091 }
1092 }
1093 }
1094
1095 fn validate_use_fields(
1096 &mut self,
1097 decl: DeclType,
1098 capability_checker: impl Fn(&Self) -> &dyn Container,
1102 source: Option<&'a fdecl::Ref>,
1103 source_name: Option<&'a String>,
1104 source_dictionary: Option<&'a String>,
1105 target_path: Option<&'a String>,
1106 dependency_type: Option<&fdecl::DependencyType>,
1107 availability: Option<&'a fdecl::Availability>,
1108 ) {
1109 self.validate_use_source(decl, source, source_dictionary);
1110
1111 check_name(source_name, decl, "source_name", &mut self.errors);
1112 if source_dictionary.is_some() {
1113 check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
1114 }
1115 match decl {
1116 DeclType::UseRunner | DeclType::UseConfiguration => {}
1117 #[cfg(fuchsia_api_level_at_least = "29")]
1118 DeclType::UseProtocol => {
1119 if target_path.is_some() {
1120 check_path(target_path, decl, "target_path", &mut self.errors);
1121 } else {
1122 }
1124 }
1125 _ => {
1126 check_path(target_path, decl, "target_path", &mut self.errors);
1127 }
1128 }
1129 check_use_availability(decl, availability, &mut self.errors);
1130
1131 let is_use_from_child = match source {
1133 Some(fdecl::Ref::Child(_)) => true,
1134 _ => false,
1135 };
1136 match (is_use_from_child, dependency_type) {
1137 (false, Some(fdecl::DependencyType::Weak)) => {
1138 self.errors.push(Error::invalid_field(decl, "dependency_type"));
1139 }
1140 _ => {}
1141 }
1142
1143 self.validate_route_from_self(
1144 decl,
1145 source,
1146 source_name,
1147 source_dictionary,
1148 capability_checker,
1149 );
1150 }
1151
1152 fn validate_use_source(
1153 &mut self,
1154 decl: DeclType,
1155 source: Option<&'a fdecl::Ref>,
1156 source_dictionary: Option<&'a String>,
1157 ) {
1158 match (source, source_dictionary) {
1159 (Some(fdecl::Ref::Parent(_)), _) => {}
1161 (Some(fdecl::Ref::Self_(_)), _) => {}
1162 (Some(fdecl::Ref::Child(child)), _) => {
1163 self.validate_child_ref(decl, "source", &child, OfferType::Static);
1164 return;
1165 }
1166 (Some(fdecl::Ref::Framework(_)), None) => {}
1168 (Some(fdecl::Ref::Debug(_)), None) => {}
1169 (Some(fdecl::Ref::Capability(c)), None) => {
1170 self.validate_source_capability(&c, decl, "source");
1171 return;
1172 }
1173 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1174 (Some(fdecl::Ref::Environment(_)), None) => {}
1175 (Some(fdecl::Ref::Collection(collection)), None) if decl == DeclType::UseService => {
1176 self.validate_collection_ref(decl, "source", &collection);
1177 return;
1178 }
1179 (None, _) => self.errors.push(Error::missing_field(decl, "source")),
1181 (_, _) => self.errors.push(Error::invalid_field(decl, "source")),
1183 }
1184 }
1185
1186 fn validate_child_decl(&mut self, child: &'a fdecl::Child) {
1187 if let Err(mut e) = validate_child(child, check_name) {
1188 self.errors.append(&mut e.errs);
1189 }
1190 if let Some(name) = child.name.as_ref() {
1191 let name: &str = name;
1192 if !self.all_children.insert(name) {
1193 self.errors.push(Error::duplicate_field(DeclType::Child, "name", name));
1194 }
1195 }
1196 if let Some(environment) = child.environment.as_ref() {
1197 if !self.all_environment_names.contains(environment.as_str()) {
1198 self.errors.push(Error::invalid_environment(
1199 DeclType::Child,
1200 "environment",
1201 environment,
1202 ));
1203 }
1204 }
1205 }
1206
1207 fn validate_collection_decl(&mut self, collection: &'a fdecl::Collection) {
1208 let name = collection.name.as_ref();
1209 if check_name(name, DeclType::Collection, "name", &mut self.errors) {
1210 let name: &str = name.unwrap();
1211 if !self.all_collections.insert(name) {
1212 self.errors.push(Error::duplicate_field(DeclType::Collection, "name", name));
1213 }
1214 }
1215 if collection.durability.is_none() {
1216 self.errors.push(Error::missing_field(DeclType::Collection, "durability"));
1217 }
1218 if let Some(environment) = collection.environment.as_ref() {
1219 if !self.all_environment_names.contains(environment.as_str()) {
1220 self.errors.push(Error::invalid_environment(
1221 DeclType::Collection,
1222 "environment",
1223 environment,
1224 ));
1225 }
1226 }
1227 }
1229
1230 fn validate_environment_decl(&mut self, environment: &'a fdecl::Environment) {
1231 let name = environment.name.as_ref();
1232 check_name(name, DeclType::Environment, "name", &mut self.errors);
1233 if environment.extends.is_none() {
1234 self.errors.push(Error::missing_field(DeclType::Environment, "extends"));
1235 }
1236 if let Some(runners) = environment.runners.as_ref() {
1237 let mut registered_runners = HashSet::new();
1238 for runner in runners {
1239 self.validate_runner_registration(runner, &mut registered_runners);
1240 }
1241 }
1242 if let Some(resolvers) = environment.resolvers.as_ref() {
1243 let mut registered_schemes = HashSet::new();
1244 for resolver in resolvers {
1245 self.validate_resolver_registration(resolver, &mut registered_schemes);
1246 }
1247 }
1248
1249 match environment.extends.as_ref() {
1250 Some(fdecl::EnvironmentExtends::None) => {
1251 if environment.stop_timeout_ms.is_none() {
1252 self.errors
1253 .push(Error::missing_field(DeclType::Environment, "stop_timeout_ms"));
1254 }
1255 }
1256 None | Some(fdecl::EnvironmentExtends::Realm) => {}
1257 }
1258
1259 if let Some(debugs) = environment.debug_capabilities.as_ref() {
1260 for debug in debugs {
1261 self.validate_environment_debug_registration(debug);
1262 }
1263 }
1264 }
1265
1266 fn validate_runner_registration(
1267 &mut self,
1268 runner_registration: &'a fdecl::RunnerRegistration,
1269 runner_names: &mut HashSet<&'a str>,
1270 ) {
1271 check_name(
1272 runner_registration.source_name.as_ref(),
1273 DeclType::RunnerRegistration,
1274 "source_name",
1275 &mut self.errors,
1276 );
1277 self.validate_registration_source(
1278 runner_registration.source.as_ref(),
1279 DeclType::RunnerRegistration,
1280 );
1281 if let (Some(fdecl::Ref::Self_(_)), Some(name)) =
1283 (&runner_registration.source, runner_registration.source_name.as_ref())
1284 {
1285 if !self.all_runners.contains(name as &str) {
1286 self.errors.push(Error::invalid_runner(
1287 DeclType::RunnerRegistration,
1288 "source_name",
1289 name,
1290 ));
1291 }
1292 }
1293
1294 check_name(
1295 runner_registration.target_name.as_ref(),
1296 DeclType::RunnerRegistration,
1297 "target_name",
1298 &mut self.errors,
1299 );
1300 if let Some(name) = runner_registration.target_name.as_ref() {
1301 if !runner_names.insert(name.as_str()) {
1302 self.errors.push(Error::duplicate_field(
1303 DeclType::RunnerRegistration,
1304 "target_name",
1305 name,
1306 ));
1307 }
1308 }
1309 }
1310
1311 fn validate_resolver_registration(
1312 &mut self,
1313 resolver_registration: &'a fdecl::ResolverRegistration,
1314 schemes: &mut HashSet<&'a str>,
1315 ) {
1316 check_name(
1317 resolver_registration.resolver.as_ref(),
1318 DeclType::ResolverRegistration,
1319 "resolver",
1320 &mut self.errors,
1321 );
1322 self.validate_registration_source(
1323 resolver_registration.source.as_ref(),
1324 DeclType::ResolverRegistration,
1325 );
1326 check_url_scheme(
1327 resolver_registration.scheme.as_ref(),
1328 DeclType::ResolverRegistration,
1329 "scheme",
1330 &mut self.errors,
1331 );
1332 if let Some(scheme) = resolver_registration.scheme.as_ref() {
1333 if !schemes.insert(scheme.as_str()) {
1334 self.errors.push(Error::duplicate_field(
1335 DeclType::ResolverRegistration,
1336 "scheme",
1337 scheme,
1338 ));
1339 }
1340 }
1341 }
1342
1343 fn validate_registration_source(&mut self, source: Option<&'a fdecl::Ref>, ty: DeclType) {
1344 match source {
1345 Some(fdecl::Ref::Parent(_)) => {}
1346 Some(fdecl::Ref::Self_(_)) => {}
1347 Some(fdecl::Ref::Child(child_ref)) => {
1348 self.validate_child_ref(ty, "source", &child_ref, OfferType::Static);
1350 }
1351 Some(_) => {
1352 self.errors.push(Error::invalid_field(ty, "source"));
1353 }
1354 None => {
1355 self.errors.push(Error::missing_field(ty, "source"));
1356 }
1357 }
1358 }
1359
1360 fn validate_service_decl(&mut self, service: &'a fdecl::Service, as_builtin: bool) {
1361 if check_name(service.name.as_ref(), DeclType::Service, "name", &mut self.errors) {
1362 let name = service.name.as_ref().unwrap();
1363 if !self.all_capability_ids.insert(name) {
1364 self.errors.push(Error::duplicate_field(DeclType::Service, "name", name.as_str()));
1365 }
1366 self.all_services.insert(name);
1367 }
1368 match as_builtin {
1369 true => {
1370 if let Some(path) = service.source_path.as_ref() {
1371 self.errors.push(Error::extraneous_source_path(DeclType::Service, path))
1372 }
1373 }
1374 false => {
1375 check_path(
1376 service.source_path.as_ref(),
1377 DeclType::Service,
1378 "source_path",
1379 &mut self.errors,
1380 );
1381 }
1382 }
1383 }
1384
1385 fn validate_protocol_decl(&mut self, protocol: &'a fdecl::Protocol, as_builtin: bool) {
1386 if check_name(protocol.name.as_ref(), DeclType::Protocol, "name", &mut self.errors) {
1387 let name = protocol.name.as_ref().unwrap();
1388 if !self.all_capability_ids.insert(name) {
1389 self.errors.push(Error::duplicate_field(DeclType::Protocol, "name", name.as_str()));
1390 }
1391 self.all_protocols.insert(name);
1392 }
1393 match as_builtin {
1394 true => {
1395 if let Some(path) = protocol.source_path.as_ref() {
1396 self.errors.push(Error::extraneous_source_path(DeclType::Protocol, path))
1397 }
1398 }
1399 false => {
1400 check_path(
1401 protocol.source_path.as_ref(),
1402 DeclType::Protocol,
1403 "source_path",
1404 &mut self.errors,
1405 );
1406 }
1407 }
1408
1409 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1410 match protocol.delivery {
1411 Some(delivery) => match cm_types::DeliveryType::try_from(delivery) {
1412 Ok(_) => {}
1413 Err(_) => self.errors.push(Error::invalid_field(DeclType::Protocol, "delivery")),
1414 },
1415 None => {}
1416 }
1417 }
1418
1419 fn validate_directory_decl(&mut self, directory: &'a fdecl::Directory, as_builtin: bool) {
1420 if check_name(directory.name.as_ref(), DeclType::Directory, "name", &mut self.errors) {
1421 let name = directory.name.as_ref().unwrap();
1422 if !self.all_capability_ids.insert(name) {
1423 self.errors.push(Error::duplicate_field(
1424 DeclType::Directory,
1425 "name",
1426 name.as_str(),
1427 ));
1428 }
1429 self.all_directories.insert(name);
1430 }
1431 match as_builtin {
1432 true => {
1433 if let Some(path) = directory.source_path.as_ref() {
1434 self.errors.push(Error::extraneous_source_path(DeclType::Directory, path))
1435 }
1436 }
1437 false => {
1438 check_path(
1439 directory.source_path.as_ref(),
1440 DeclType::Directory,
1441 "source_path",
1442 &mut self.errors,
1443 );
1444 }
1445 }
1446 if directory.rights.is_none() {
1447 self.errors.push(Error::missing_field(DeclType::Directory, "rights"));
1448 }
1449 }
1450
1451 fn validate_storage_decl(&mut self, storage: &'a fdecl::Storage) {
1452 match storage.source.as_ref() {
1453 Some(fdecl::Ref::Parent(_)) => {}
1454 Some(fdecl::Ref::Self_(_)) => {}
1455 Some(fdecl::Ref::Child(child)) => {
1456 let _ =
1457 self.validate_child_ref(DeclType::Storage, "source", &child, OfferType::Static);
1458 }
1459 Some(_) => {
1460 self.errors.push(Error::invalid_field(DeclType::Storage, "source"));
1461 }
1462 None => {
1463 self.errors.push(Error::missing_field(DeclType::Storage, "source"));
1464 }
1465 };
1466 if check_name(storage.name.as_ref(), DeclType::Storage, "name", &mut self.errors) {
1467 let name = storage.name.as_ref().unwrap();
1468 if !self.all_capability_ids.insert(name) {
1469 self.errors.push(Error::duplicate_field(DeclType::Storage, "name", name.as_str()));
1470 }
1471 self.all_storages.insert(name, storage.source.as_ref());
1472 }
1473 if storage.storage_id.is_none() {
1474 self.errors.push(Error::missing_field(DeclType::Storage, "storage_id"));
1475 }
1476 check_name(
1477 storage.backing_dir.as_ref(),
1478 DeclType::Storage,
1479 "backing_dir",
1480 &mut self.errors,
1481 );
1482 }
1483
1484 fn validate_runner_decl(&mut self, runner: &'a fdecl::Runner, as_builtin: bool) {
1485 if check_name(runner.name.as_ref(), DeclType::Runner, "name", &mut self.errors) {
1486 let name = runner.name.as_ref().unwrap();
1487 if !self.all_capability_ids.insert(name) {
1488 self.errors.push(Error::duplicate_field(DeclType::Runner, "name", name.as_str()));
1489 }
1490 self.all_runners.insert(name);
1491 }
1492 match as_builtin {
1493 true => {
1494 if let Some(path) = runner.source_path.as_ref() {
1495 self.errors.push(Error::extraneous_source_path(DeclType::Runner, path))
1496 }
1497 }
1498 false => {
1499 check_path(
1500 runner.source_path.as_ref(),
1501 DeclType::Runner,
1502 "source_path",
1503 &mut self.errors,
1504 );
1505 }
1506 }
1507 }
1508
1509 fn validate_resolver_decl(&mut self, resolver: &'a fdecl::Resolver, as_builtin: bool) {
1510 if check_name(resolver.name.as_ref(), DeclType::Resolver, "name", &mut self.errors) {
1511 let name = resolver.name.as_ref().unwrap();
1512 if !self.all_capability_ids.insert(name) {
1513 self.errors.push(Error::duplicate_field(DeclType::Resolver, "name", name.as_str()));
1514 }
1515 self.all_resolvers.insert(name);
1516 }
1517 match as_builtin {
1518 true => {
1519 if let Some(path) = resolver.source_path.as_ref() {
1520 self.errors.push(Error::extraneous_source_path(DeclType::Resolver, path))
1521 }
1522 }
1523 false => {
1524 check_path(
1525 resolver.source_path.as_ref(),
1526 DeclType::Resolver,
1527 "source_path",
1528 &mut self.errors,
1529 );
1530 }
1531 }
1532 }
1533
1534 fn load_dictionary_names(&mut self, dictionaries: impl Iterator<Item = &'a fdecl::Dictionary>) {
1538 for dictionary in dictionaries {
1539 let decl = DeclType::Dictionary;
1540 if check_name(dictionary.name.as_ref(), decl, "name", &mut self.errors) {
1541 let name = dictionary.name.as_ref().unwrap();
1542 if !self.all_capability_ids.insert(name) {
1543 self.errors.push(Error::duplicate_field(decl, "name", name.as_str()));
1544 }
1545 self.all_dictionaries.insert(name, &dictionary);
1546 }
1547 }
1548 }
1549
1550 fn validate_dictionary_decl(&mut self, dictionary: &'a fdecl::Dictionary) {
1551 let decl = DeclType::Dictionary;
1552 if let Some(path) = dictionary.source_path.as_ref() {
1553 if dictionary.source.is_some() {
1554 self.errors.push(Error::extraneous_field(decl, "source"));
1555 }
1556 check_path(Some(path), DeclType::Dictionary, "source_path", &mut self.errors);
1557 }
1558 }
1559
1560 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1561 fn validate_configuration_decl(&mut self, config: &'a fdecl::Configuration) {
1562 let decl = DeclType::Configuration;
1563 if check_name(config.name.as_ref(), decl, "name", &mut self.errors) {
1564 let name = config.name.as_ref().unwrap();
1565 if !self.all_capability_ids.insert(name) {
1566 self.errors.push(Error::duplicate_field(decl, "name", name.as_str()));
1567 }
1568 self.all_configs.insert(name);
1569 }
1570 }
1571
1572 fn validate_environment_debug_registration(&mut self, debug: &'a fdecl::DebugRegistration) {
1573 match debug {
1574 fdecl::DebugRegistration::Protocol(o) => {
1575 let decl = DeclType::DebugProtocolRegistration;
1576 self.validate_environment_debug_fields(
1577 decl,
1578 o.source.as_ref(),
1579 o.source_name.as_ref(),
1580 o.target_name.as_ref(),
1581 );
1582
1583 if let (Some(fdecl::Ref::Self_(_)), Some(name)) =
1584 (&o.source, o.source_name.as_ref())
1585 {
1586 if !self.all_protocols.contains(&name as &str) {
1587 self.errors.push(Error::invalid_field(decl, "source"));
1588 }
1589 }
1590 }
1591 _ => {
1592 self.errors.push(Error::invalid_field(DeclType::Environment, "debug"));
1593 }
1594 }
1595 }
1596
1597 fn validate_environment_debug_fields(
1598 &mut self,
1599 decl: DeclType,
1600 source: Option<&fdecl::Ref>,
1601 source_name: Option<&String>,
1602 target_name: Option<&'a String>,
1603 ) {
1604 match source {
1606 Some(fdecl::Ref::Parent(_)) => {}
1607 Some(fdecl::Ref::Self_(_)) => {}
1608 Some(fdecl::Ref::Framework(_)) => {}
1609 Some(fdecl::Ref::Child(child)) => {
1610 let _ = self.validate_child_ref(decl, "source", &child, OfferType::Static);
1611 }
1612 Some(_) => self.errors.push(Error::invalid_field(decl, "source")),
1613 None => self.errors.push(Error::missing_field(decl, "source")),
1614 }
1615 check_name(source_name, decl, "source_name", &mut self.errors);
1616 check_name(target_name, decl, "target_name", &mut self.errors);
1617 }
1618
1619 fn validate_event_stream_decl(&mut self, event: &'a fdecl::EventStream) {
1620 if check_name(event.name.as_ref(), DeclType::EventStream, "name", &mut self.errors) {
1621 let name = event.name.as_ref().unwrap();
1622 if !self.all_capability_ids.insert(name) {
1623 self.errors.push(Error::duplicate_field(
1624 DeclType::EventStream,
1625 "name",
1626 name.as_str(),
1627 ));
1628 }
1629 }
1630 }
1631
1632 fn validate_source_collection(
1633 &mut self,
1634 collection: &fdecl::CollectionRef,
1635 decl_type: DeclType,
1636 ) -> bool {
1637 let num_errors = self.errors.len();
1638 if check_name(Some(&collection.name), decl_type, "source.collection.name", &mut self.errors)
1639 && !self.all_collections.contains(&collection.name as &str)
1640 {
1641 self.errors.push(Error::invalid_collection(
1642 decl_type,
1643 "source",
1644 &collection.name as &str,
1645 ));
1646 }
1647 num_errors == self.errors.len()
1648 }
1649
1650 fn validate_filtered_service_fields(
1651 &mut self,
1652 decl_type: DeclType,
1653 source_instance_filter: Option<&Vec<String>>,
1654 renamed_instances: Option<&Vec<fdecl::NameMapping>>,
1655 ) {
1656 if let Some(source_instance_filter) = source_instance_filter {
1657 if source_instance_filter.is_empty() {
1658 self.errors.push(Error::invalid_field(decl_type, "source_instance_filter"));
1661 }
1662 for name in source_instance_filter {
1663 check_name(Some(name), decl_type, "source_instance_filter", &mut self.errors);
1664 }
1665 }
1666 if let Some(renamed_instances) = renamed_instances {
1667 let mut seen_target_names = HashSet::<String>::new();
1669 for mapping in renamed_instances {
1670 check_name(
1671 Some(&mapping.source_name),
1672 decl_type,
1673 "renamed_instances.source_name",
1674 &mut self.errors,
1675 );
1676 check_name(
1677 Some(&mapping.target_name),
1678 decl_type,
1679 "renamed_instances.target_name",
1680 &mut self.errors,
1681 );
1682 if !seen_target_names.insert(mapping.target_name.clone()) {
1683 self.errors.push(Error::invalid_field(decl_type, "renamed_instances"));
1684 break;
1685 }
1686 }
1687 }
1688 }
1689
1690 fn validate_source_capability(
1691 &mut self,
1692 capability: &fdecl::CapabilityRef,
1693 decl_type: DeclType,
1694 field: &str,
1695 ) -> bool {
1696 let num_errors = self.errors.len();
1697 if check_name(Some(&capability.name), decl_type, "source.capability.name", &mut self.errors)
1698 && !self.all_capability_ids.contains(capability.name.as_str())
1699 {
1700 self.errors.push(Error::invalid_capability(decl_type, field, &capability.name));
1701 }
1702 num_errors == self.errors.len()
1703 }
1704
1705 fn make_group_key(
1709 target_name: Option<&'a String>,
1710 target: Option<&'a fdecl::Ref>,
1711 ) -> Option<(&'a str, RefKey<'a>)> {
1712 if target_name.is_none() {
1713 return None;
1714 }
1715 let target_name = target_name.unwrap().as_str();
1716 if target.is_none() {
1717 return None;
1718 }
1719 let target = match target.unwrap() {
1720 fdecl::Ref::Parent(_) => RefKey::Parent,
1721 fdecl::Ref::Self_(_) => RefKey::Self_,
1722 fdecl::Ref::Child(r) => RefKey::Child(r.name.as_str()),
1723 fdecl::Ref::Collection(r) => RefKey::Collection(r.name.as_str()),
1724 fdecl::Ref::Framework(_) => RefKey::Framework,
1725 fdecl::Ref::Capability(_) => RefKey::Capability,
1726 fdecl::Ref::Debug(_) => RefKey::Debug,
1727 fdecl::RefUnknown!() => {
1728 return None;
1729 }
1730 };
1731 Some((target_name, target))
1732 }
1733
1734 fn validate_aggregation_has_same_availability(
1735 &mut self,
1736 route_group: &Vec<impl HasAvailability>,
1737 ) {
1738 let availability_of_sources: BTreeSet<_> =
1740 route_group.iter().map(|r| r.availability()).collect();
1741
1742 if availability_of_sources.len() > 1 {
1744 self.errors.push(Error::different_availability_in_aggregation(
1745 availability_of_sources.into_iter().collect(),
1746 ));
1747 }
1748 }
1749
1750 fn validate_expose_group(&mut self, exposes: &'a [fdecl::Expose]) {
1753 let mut expose_groups: HashMap<_, Vec<fdecl::ExposeService>> = HashMap::new();
1754 let service_exposes = exposes
1755 .iter()
1756 .filter_map(|o| if let fdecl::Expose::Service(s) = o { Some(s) } else { None });
1757 for expose in service_exposes {
1758 let key = Self::make_group_key(expose.target_name.as_ref(), expose.target.as_ref());
1759 if let Some(key) = key {
1760 expose_groups.entry(key).or_insert_with(|| vec![]).push(expose.clone());
1761 }
1762 }
1763 for expose_group in expose_groups.into_values() {
1764 if expose_group.len() == 1 {
1765 continue;
1768 }
1769
1770 self.validate_aggregation_has_same_availability(&expose_group);
1771 }
1772 }
1773
1774 fn validate_expose_decl(
1775 &mut self,
1776 expose: &'a fdecl::Expose,
1777 expose_to_parent_ids: &mut HashMap<&'a str, AllowableIds>,
1778 expose_to_framework_ids: &mut HashMap<&'a str, AllowableIds>,
1779 ) {
1780 match expose {
1781 fdecl::Expose::Service(e) => {
1782 let decl = DeclType::ExposeService;
1783 self.validate_expose_fields(
1784 decl,
1785 AllowableIds::Many,
1786 CollectionSource::Allow,
1787 Self::service_checker,
1788 e.source.as_ref(),
1789 e.source_name.as_ref(),
1790 e.source_dictionary.as_ref(),
1791 e.target.as_ref(),
1792 e.target_name.as_ref(),
1793 e.availability.as_ref(),
1794 expose_to_parent_ids,
1795 expose_to_framework_ids,
1796 );
1797 }
1798 fdecl::Expose::Protocol(e) => {
1799 let decl = DeclType::ExposeProtocol;
1800 self.validate_expose_fields(
1801 decl,
1802 AllowableIds::One,
1803 CollectionSource::Deny,
1804 Self::protocol_checker,
1805 e.source.as_ref(),
1806 e.source_name.as_ref(),
1807 e.source_dictionary.as_ref(),
1808 e.target.as_ref(),
1809 e.target_name.as_ref(),
1810 e.availability.as_ref(),
1811 expose_to_parent_ids,
1812 expose_to_framework_ids,
1813 );
1814 }
1815 fdecl::Expose::Directory(e) => {
1816 let decl = DeclType::ExposeDirectory;
1817 self.validate_expose_fields(
1818 decl,
1819 AllowableIds::One,
1820 CollectionSource::Deny,
1821 Self::directory_checker,
1822 e.source.as_ref(),
1823 e.source_name.as_ref(),
1824 e.source_dictionary.as_ref(),
1825 e.target.as_ref(),
1826 e.target_name.as_ref(),
1827 e.availability.as_ref(),
1828 expose_to_parent_ids,
1829 expose_to_framework_ids,
1830 );
1831
1832 match e.target.as_ref() {
1835 Some(fdecl::Ref::Framework(_)) => {
1836 if e.subdir.is_some() {
1837 self.errors.push(Error::invalid_field(decl, "subdir"));
1838 }
1839 }
1840 _ => {}
1841 }
1842
1843 if let Some(subdir) = e.subdir.as_ref() {
1844 check_relative_path(Some(subdir), decl, "subdir", &mut self.errors);
1845 }
1846 }
1847 fdecl::Expose::Runner(e) => {
1848 let decl = DeclType::ExposeRunner;
1849 self.validate_expose_fields(
1850 decl,
1851 AllowableIds::One,
1852 CollectionSource::Deny,
1853 Self::runner_checker,
1854 e.source.as_ref(),
1855 e.source_name.as_ref(),
1856 e.source_dictionary.as_ref(),
1857 e.target.as_ref(),
1858 e.target_name.as_ref(),
1859 Some(&fdecl::Availability::Required),
1860 expose_to_parent_ids,
1861 expose_to_framework_ids,
1862 );
1863 }
1864 fdecl::Expose::Resolver(e) => {
1865 let decl = DeclType::ExposeResolver;
1866 self.validate_expose_fields(
1867 decl,
1868 AllowableIds::One,
1869 CollectionSource::Deny,
1870 Self::resolver_checker,
1871 e.source.as_ref(),
1872 e.source_name.as_ref(),
1873 e.source_dictionary.as_ref(),
1874 e.target.as_ref(),
1875 e.target_name.as_ref(),
1876 Some(&fdecl::Availability::Required),
1877 expose_to_parent_ids,
1878 expose_to_framework_ids,
1879 );
1880 }
1881 fdecl::Expose::Dictionary(e) => {
1882 let decl = DeclType::ExposeDictionary;
1883 self.validate_expose_fields(
1884 decl,
1885 AllowableIds::One,
1886 CollectionSource::Deny,
1887 Self::dictionary_checker,
1888 e.source.as_ref(),
1889 e.source_name.as_ref(),
1890 e.source_dictionary.as_ref(),
1891 e.target.as_ref(),
1892 e.target_name.as_ref(),
1893 e.availability.as_ref(),
1894 expose_to_parent_ids,
1895 expose_to_framework_ids,
1896 );
1897 }
1898 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1899 fdecl::Expose::Config(e) => {
1900 let decl = DeclType::ExposeConfig;
1901 self.validate_expose_fields(
1902 decl,
1903 AllowableIds::One,
1904 CollectionSource::Deny,
1905 Self::config_checker,
1906 e.source.as_ref(),
1907 e.source_name.as_ref(),
1908 None,
1909 e.target.as_ref(),
1910 e.target_name.as_ref(),
1911 e.availability.as_ref(),
1912 expose_to_parent_ids,
1913 expose_to_framework_ids,
1914 );
1915 }
1916 _ => {
1917 self.errors.push(Error::invalid_field(DeclType::Component, "expose"));
1918 }
1919 }
1920 }
1921
1922 fn validate_expose_fields(
1923 &mut self,
1924 decl: DeclType,
1925 allowable_ids: AllowableIds,
1926 collection_source: CollectionSource,
1927 capability_checker: impl Fn(&Self) -> &dyn Container,
1931 source: Option<&fdecl::Ref>,
1932 source_name: Option<&String>,
1933 source_dictionary: Option<&String>,
1934 target: Option<&fdecl::Ref>,
1935 target_name: Option<&'a String>,
1936 availability: Option<&fdecl::Availability>,
1937 expose_to_parent_ids: &mut HashMap<&'a str, AllowableIds>,
1938 expose_to_framework_ids: &mut HashMap<&'a str, AllowableIds>,
1939 ) {
1940 self.validate_expose_source(decl, collection_source, source, source_dictionary);
1941 check_route_availability(decl, availability, source, source_name, &mut self.errors);
1942 match target {
1943 Some(r) => match r {
1944 fdecl::Ref::Parent(_) => {}
1945 fdecl::Ref::Framework(_) => {}
1946 _ => {
1947 self.errors.push(Error::invalid_field(decl, "target"));
1948 }
1949 },
1950 None => {
1951 self.errors.push(Error::missing_field(decl, "target"));
1952 }
1953 }
1954 check_name(source_name, decl, "source_name", &mut self.errors);
1955 if source_dictionary.is_some() {
1956 check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
1957 }
1958 if check_name(target_name, decl, "target_name", &mut self.errors) {
1959 let maybe_ids_set = match target {
1960 Some(fdecl::Ref::Parent(_)) => Some(expose_to_parent_ids),
1961 Some(fdecl::Ref::Framework(_)) => Some(expose_to_framework_ids),
1962 _ => None,
1963 };
1964 if let Some(ids_set) = maybe_ids_set {
1965 let target_name = target_name.unwrap();
1966 if let Some(prev_state) = ids_set.insert(target_name, allowable_ids) {
1967 if prev_state == AllowableIds::One || prev_state != allowable_ids {
1968 self.errors.push(Error::duplicate_field(decl, "target_name", target_name));
1969 }
1970 }
1971 }
1972 }
1973
1974 self.validate_route_from_self(
1975 decl,
1976 source,
1977 source_name,
1978 source_dictionary,
1979 capability_checker,
1980 );
1981 }
1982
1983 fn validate_expose_source(
1984 &mut self,
1985 decl: DeclType,
1986 collection_source: CollectionSource,
1987 source: Option<&fdecl::Ref>,
1988 source_dictionary: Option<&String>,
1989 ) {
1990 match (source, source_dictionary) {
1991 (Some(fdecl::Ref::Self_(_)), _) => {}
1993 (Some(fdecl::Ref::Child(child)), _) => {
1994 let _ = self.validate_child_ref(decl, "source", &child, OfferType::Static);
1995 }
1996 (Some(fdecl::Ref::VoidType(_)), None) => {}
1998 (Some(fdecl::Ref::Framework(_)), None) => {}
1999 (Some(fdecl::Ref::Capability(c)), None) => {
2000 self.validate_source_capability(c, decl, "source");
2001 }
2002 (Some(fdecl::Ref::Collection(c)), None)
2003 if collection_source == CollectionSource::Allow =>
2004 {
2005 self.validate_source_collection(c, decl);
2006 }
2007 (None, _) => {
2009 self.errors.push(Error::missing_field(decl, "source"));
2010 }
2011 (_, _) => {
2013 self.errors.push(Error::invalid_field(decl, "source"));
2014 }
2015 }
2016 }
2017
2018 fn validate_offer_group(&mut self, offers: &'a [fdecl::Offer], offer_type: OfferType) {
2021 let mut offer_groups: HashMap<_, Vec<fdecl::OfferService>> = HashMap::new();
2022 let service_offers = offers
2023 .iter()
2024 .filter_map(|o| if let fdecl::Offer::Service(s) = o { Some(s) } else { None });
2025 for offer in service_offers {
2026 let key = Self::make_group_key(offer.target_name.as_ref(), offer.target.as_ref());
2027 if let Some(key) = key {
2028 offer_groups.entry(key).or_insert_with(|| vec![]).push(offer.clone());
2029 }
2030 }
2031 for offer_group in offer_groups.into_values() {
2032 if offer_group.len() == 1 {
2033 continue;
2036 }
2037
2038 self.validate_aggregation_has_same_availability(&offer_group);
2039
2040 let mut source_instance_filter_entries: HashSet<String> = HashSet::new();
2041 let mut service_source_names: HashSet<String> = HashSet::new();
2042 for o in offer_group {
2043 match (o.source_instance_filter, offer_type) {
2045 (Some(source_instance_filter), _) => {
2046 for instance_name in source_instance_filter {
2047 if !source_instance_filter_entries.insert(instance_name.clone()) {
2048 self.errors.push(Error::invalid_aggregate_offer(format!(
2051 "Conflicting source_instance_filter in aggregate service \
2052 offer, instance_name '{}' seen in filter lists multiple times",
2053 instance_name,
2054 )));
2055 }
2056 }
2057 }
2058 (None, OfferType::Static) => {}
2059 (None, OfferType::Dynamic) => {
2060 self.errors.push(Error::invalid_aggregate_offer(
2062 "source_instance_filter must be set for dynamic aggregate service \
2063 offers",
2064 ));
2065 }
2066 }
2067 service_source_names.insert(
2068 o.source_name
2069 .expect("Offer Service declarations must always contain source_name"),
2070 );
2071 }
2072
2073 if service_source_names.len() > 1 {
2074 self.errors.push(Error::invalid_aggregate_offer(format!(
2075 "All aggregate service offers must have the same source_name, saw {}. Use \
2076 renamed_instances to rename instance names to avoid conflict.",
2077 service_source_names.into_iter().sorted().collect::<Vec<String>>().join(", ")
2078 )));
2079 }
2080 }
2081 }
2082
2083 fn validate_offer_decl(&mut self, offer: &'a fdecl::Offer, offer_type: OfferType) {
2084 match offer {
2085 fdecl::Offer::Service(o) => {
2086 let decl = DeclType::OfferService;
2087 self.validate_offer_fields(
2088 decl,
2089 AllowableIds::Many,
2090 CollectionSource::Allow,
2091 Self::service_checker,
2092 o.source.as_ref(),
2093 o.source_name.as_ref(),
2094 o.source_dictionary.as_ref(),
2095 o.target.as_ref(),
2096 o.target_name.as_ref(),
2097 #[cfg(fuchsia_api_level_at_least = "HEAD")]
2098 Some(o.dependency_type.as_ref().unwrap_or(&fdecl::DependencyType::Strong)),
2099 #[cfg(fuchsia_api_level_less_than = "HEAD")]
2100 Some(&fdecl::DependencyType::Strong),
2101 o.availability.as_ref(),
2102 offer_type,
2103 );
2104 self.validate_filtered_service_fields(
2105 decl,
2106 o.source_instance_filter.as_ref(),
2107 o.renamed_instances.as_ref(),
2108 );
2109 }
2110 fdecl::Offer::Protocol(o) => {
2111 let decl = DeclType::OfferProtocol;
2112 self.validate_offer_fields(
2113 decl,
2114 AllowableIds::One,
2115 CollectionSource::Deny,
2116 Self::protocol_checker,
2117 o.source.as_ref(),
2118 o.source_name.as_ref(),
2119 o.source_dictionary.as_ref(),
2120 o.target.as_ref(),
2121 o.target_name.as_ref(),
2122 o.dependency_type.as_ref(),
2123 o.availability.as_ref(),
2124 offer_type,
2125 );
2126 }
2127 fdecl::Offer::Directory(o) => {
2128 let decl = DeclType::OfferDirectory;
2129 self.validate_offer_fields(
2130 decl,
2131 AllowableIds::One,
2132 CollectionSource::Deny,
2133 Self::directory_checker,
2134 o.source.as_ref(),
2135 o.source_name.as_ref(),
2136 o.source_dictionary.as_ref(),
2137 o.target.as_ref(),
2138 o.target_name.as_ref(),
2139 o.dependency_type.as_ref(),
2140 o.availability.as_ref(),
2141 offer_type,
2142 );
2143 if let Some(subdir) = o.subdir.as_ref() {
2144 check_relative_path(
2145 Some(subdir),
2146 DeclType::OfferDirectory,
2147 "subdir",
2148 &mut self.errors,
2149 );
2150 }
2151 }
2152 fdecl::Offer::Storage(o) => {
2153 let decl = DeclType::OfferStorage;
2154 self.validate_storage_offer_fields(
2155 decl,
2156 Self::storage_checker,
2157 o.source.as_ref(),
2158 o.source_name.as_ref(),
2159 o.target.as_ref(),
2160 o.target_name.as_ref(),
2161 o.availability.as_ref(),
2162 offer_type,
2163 );
2164 }
2165 fdecl::Offer::Runner(o) => {
2166 let decl = DeclType::OfferRunner;
2167 self.validate_offer_fields(
2168 decl,
2169 AllowableIds::One,
2170 CollectionSource::Deny,
2171 Self::runner_checker,
2172 o.source.as_ref(),
2173 o.source_name.as_ref(),
2174 o.source_dictionary.as_ref(),
2175 o.target.as_ref(),
2176 o.target_name.as_ref(),
2177 Some(&fdecl::DependencyType::Strong),
2178 Some(&fdecl::Availability::Required),
2179 offer_type,
2180 );
2181 }
2182 fdecl::Offer::Resolver(o) => {
2183 let decl = DeclType::OfferResolver;
2184 self.validate_offer_fields(
2185 decl,
2186 AllowableIds::One,
2187 CollectionSource::Deny,
2188 Self::resolver_checker,
2189 o.source.as_ref(),
2190 o.source_name.as_ref(),
2191 o.source_dictionary.as_ref(),
2192 o.target.as_ref(),
2193 o.target_name.as_ref(),
2194 Some(&fdecl::DependencyType::Strong),
2195 Some(&fdecl::Availability::Required),
2196 offer_type,
2197 );
2198 }
2199 fdecl::Offer::EventStream(e) => {
2200 self.validate_event_stream_offer_fields(e, offer_type);
2201 }
2202 fdecl::Offer::Dictionary(o) => {
2203 let decl = DeclType::OfferDictionary;
2204 self.validate_offer_fields(
2205 decl,
2206 AllowableIds::One,
2207 CollectionSource::Deny,
2208 Self::dictionary_checker,
2209 o.source.as_ref(),
2210 o.source_name.as_ref(),
2211 o.source_dictionary.as_ref(),
2212 o.target.as_ref(),
2213 o.target_name.as_ref(),
2214 o.dependency_type.as_ref(),
2215 o.availability.as_ref(),
2216 offer_type,
2217 );
2218 }
2219 #[cfg(fuchsia_api_level_at_least = "HEAD")]
2220 fdecl::Offer::Config(o) => {
2221 let decl = DeclType::OfferConfig;
2222 self.validate_offer_fields(
2223 decl,
2224 AllowableIds::One,
2225 CollectionSource::Deny,
2226 Self::config_checker,
2227 o.source.as_ref(),
2228 o.source_name.as_ref(),
2229 None,
2230 o.target.as_ref(),
2231 o.target_name.as_ref(),
2232 Some(&fdecl::DependencyType::Strong),
2233 o.availability.as_ref(),
2234 offer_type,
2235 );
2236 }
2237 fdecl::OfferUnknown!() => {
2238 self.errors.push(Error::invalid_field(DeclType::Component, "offer"));
2239 }
2240 }
2241 }
2242
2243 fn validate_offer_fields(
2244 &mut self,
2245 decl: DeclType,
2246 allowable_names: AllowableIds,
2247 collection_source: CollectionSource,
2248 capability_checker: impl Fn(&Self) -> &dyn Container,
2249 source: Option<&'a fdecl::Ref>,
2250 source_name: Option<&'a String>,
2251 source_dictionary: Option<&'a String>,
2252 target: Option<&'a fdecl::Ref>,
2253 target_name: Option<&'a String>,
2254 dependency_type: Option<&'a fdecl::DependencyType>,
2255 availability: Option<&'a fdecl::Availability>,
2256 offer_type: OfferType,
2257 ) {
2258 self.validate_offer_source(decl, collection_source, source, source_dictionary, offer_type);
2259 check_route_availability(decl, availability, source, source_name, &mut self.errors);
2260 check_offer_name(source_name, decl, "source_name", offer_type, &mut self.errors);
2261 if source_dictionary.is_some() {
2262 check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
2263 }
2264 self.validate_offer_target(decl, allowable_names, target, target_name, offer_type);
2265 check_offer_name(target_name, decl, "target_name", offer_type, &mut self.errors);
2266
2267 if dependency_type.is_none() {
2268 self.errors.push(Error::missing_field(decl, "dependency_type"));
2269 }
2270
2271 self.validate_route_from_self(
2272 decl,
2273 source,
2274 source_name,
2275 source_dictionary,
2276 capability_checker,
2277 );
2278 }
2279
2280 fn validate_offer_source(
2281 &mut self,
2282 decl: DeclType,
2283 collection_source: CollectionSource,
2284 source: Option<&'a fdecl::Ref>,
2285 source_dictionary: Option<&'a String>,
2286 offer_type: OfferType,
2287 ) {
2288 match (source, source_dictionary) {
2289 (Some(fdecl::Ref::Parent(_)), _) => {}
2291 (Some(fdecl::Ref::Self_(_)), _) => {}
2292 (Some(fdecl::Ref::Child(child)), _) => {
2293 self.validate_child_ref(decl, "source", &child, offer_type);
2294 }
2295 (Some(fdecl::Ref::VoidType(_)), None) => {}
2297 (Some(fdecl::Ref::Framework(_)), None) => {}
2298 (Some(fdecl::Ref::Capability(c)), None) => {
2299 self.validate_source_capability(c, decl, "source");
2300 }
2301 (Some(fdecl::Ref::Collection(c)), None)
2302 if collection_source == CollectionSource::Allow =>
2303 {
2304 self.validate_source_collection(c, decl);
2305 }
2306 (None, _) => self.errors.push(Error::missing_field(decl, "source")),
2308 (_, _) => self.errors.push(Error::invalid_field(decl, "source")),
2310 }
2311 }
2312
2313 fn validate_storage_offer_fields(
2314 &mut self,
2315 decl: DeclType,
2316 capability_checker: impl Fn(&Self) -> &dyn Container,
2320 source: Option<&'a fdecl::Ref>,
2321 source_name: Option<&'a String>,
2322 target: Option<&'a fdecl::Ref>,
2323 target_name: Option<&'a String>,
2324 availability: Option<&fdecl::Availability>,
2325 offer_type: OfferType,
2326 ) {
2327 match source {
2328 Some(fdecl::Ref::Parent(_) | fdecl::Ref::VoidType(_) | fdecl::Ref::Self_(_)) => {}
2329 Some(_) => {
2330 self.errors.push(Error::invalid_field(decl, "source"));
2331 }
2332 None => {
2333 self.errors.push(Error::missing_field(decl, "source"));
2334 }
2335 }
2336 check_route_availability(decl, availability, source, source_name, &mut self.errors);
2337 check_offer_name(source_name, decl, "source_name", offer_type, &mut self.errors);
2338 self.validate_offer_target(decl, AllowableIds::One, target, target_name, offer_type);
2339 check_offer_name(target_name, decl, "target_name", offer_type, &mut self.errors);
2340
2341 if let (Some(fdecl::Ref::Self_(_)), Some(name)) = (source, source_name) {
2342 if !(capability_checker)(self).contains(name) {
2343 self.errors.push(Error::invalid_capability(decl, "source", name));
2344 }
2345 }
2346 }
2347
2348 fn validate_event_stream_offer_fields(
2349 &mut self,
2350 event_stream: &'a fdecl::OfferEventStream,
2351 offer_type: OfferType,
2352 ) {
2353 let decl = DeclType::OfferEventStream;
2354 check_name(event_stream.source_name.as_ref(), decl, "source_name", &mut self.errors);
2355 if event_stream.target == Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})) {
2356 self.errors.push(Error::invalid_field(DeclType::OfferEventStream, "target"));
2358 }
2359 if let Some(scope) = &event_stream.scope {
2360 if scope.is_empty() {
2361 self.errors.push(Error::invalid_field(decl, "scope"));
2362 }
2363 for value in scope {
2364 match value {
2365 fdecl::Ref::Child(child) => {
2366 self.validate_child_ref(
2367 DeclType::OfferEventStream,
2368 "scope",
2369 &child,
2370 offer_type,
2371 );
2372 }
2373 fdecl::Ref::Collection(collection) => {
2374 self.validate_collection_ref(
2375 DeclType::OfferEventStream,
2376 "scope",
2377 &collection,
2378 );
2379 }
2380 _ => {
2381 self.errors.push(Error::invalid_field(DeclType::OfferEventStream, "scope"));
2382 }
2383 }
2384 }
2385 }
2386 match event_stream.source {
2388 Some(
2389 fdecl::Ref::Parent(_)
2390 | fdecl::Ref::Framework(_)
2391 | fdecl::Ref::Child(_)
2392 | fdecl::Ref::VoidType(_),
2393 ) => {}
2394 Some(_) => {
2395 self.errors.push(Error::invalid_field(decl, "source"));
2396 }
2397 None => {
2398 self.errors.push(Error::missing_field(decl, "source"));
2399 }
2400 };
2401
2402 check_route_availability(
2403 decl,
2404 event_stream.availability.as_ref(),
2405 event_stream.source.as_ref(),
2406 event_stream.source_name.as_ref(),
2407 &mut self.errors,
2408 );
2409
2410 self.validate_offer_target(
2411 decl,
2412 AllowableIds::One,
2413 event_stream.target.as_ref(),
2414 event_stream.target_name.as_ref(),
2415 offer_type,
2416 );
2417 check_name(event_stream.target_name.as_ref(), decl, "target_name", &mut self.errors);
2418 }
2419
2420 fn validate_child_ref(
2422 &mut self,
2423 decl: DeclType,
2424 field_name: &str,
2425 child: &fdecl::ChildRef,
2426 offer_type: OfferType,
2427 ) -> bool {
2428 if offer_type == OfferType::Dynamic && child.collection.is_some() {
2429 return self.validate_dynamic_child_ref(decl, field_name, child);
2430 }
2431 let mut valid = true;
2435 if !check_name(
2436 Some(&child.name),
2437 decl,
2438 &format!("{}.child.name", field_name),
2439 &mut self.errors,
2440 ) {
2441 valid = false;
2442 }
2443 if child.collection.is_some() {
2444 self.errors
2445 .push(Error::extraneous_field(decl, format!("{}.child.collection", field_name)));
2446 valid = false;
2447 }
2448 if !valid {
2449 return false;
2450 }
2451
2452 let name: &str = &child.name;
2454 if !self.all_children.contains(name) {
2455 self.errors.push(Error::invalid_child(decl, field_name, name));
2456 return false;
2457 }
2458
2459 true
2460 }
2461
2462 fn validate_dynamic_child_ref(
2467 &mut self,
2468 decl: DeclType,
2469 field_name: &str,
2470 child: &fdecl::ChildRef,
2471 ) -> bool {
2472 let mut valid = true;
2476 if !check_dynamic_name(
2477 Some(&child.name),
2478 decl,
2479 &format!("{}.child.name", field_name),
2480 &mut self.errors,
2481 ) {
2482 valid = false;
2483 }
2484 if !check_name(
2485 child.collection.as_ref(),
2486 decl,
2487 &format!("{}.child.collection", field_name),
2488 &mut self.errors,
2489 ) {
2490 valid = false;
2491 }
2492 valid
2493 }
2494
2495 fn validate_collection_ref(
2497 &mut self,
2498 decl: DeclType,
2499 field_name: &str,
2500 collection: &fdecl::CollectionRef,
2501 ) -> bool {
2502 if !check_name(
2504 Some(&collection.name),
2505 decl,
2506 &format!("{}.collection.name", field_name),
2507 &mut self.errors,
2508 ) {
2509 return false;
2510 }
2511
2512 if !self.all_collections.contains(&collection.name as &str) {
2514 self.errors.push(Error::invalid_collection(decl, field_name, &collection.name as &str));
2515 return false;
2516 }
2517
2518 true
2519 }
2520
2521 fn validate_offer_target(
2522 &mut self,
2523 decl: DeclType,
2524 allowable_names: AllowableIds,
2525 target: Option<&'a fdecl::Ref>,
2526 target_name: Option<&'a String>,
2527 offer_type: OfferType,
2528 ) {
2529 match target {
2530 Some(fdecl::Ref::Child(c)) => {
2531 self.validate_target_child(decl, allowable_names, c, target_name, offer_type);
2532 }
2533 Some(fdecl::Ref::Collection(c)) => {
2534 self.validate_target_collection(decl, allowable_names, c, target_name);
2535 }
2536 Some(fdecl::Ref::Capability(c)) => {
2537 if let Some(d) = self.all_dictionaries.get(&c.name.as_str()) {
2539 if d.source_path.is_some() {
2540 self.errors.push(Error::invalid_field(decl, "target"));
2543 }
2544 } else {
2545 self.errors.push(Error::invalid_field(decl, "target"));
2546 }
2547 }
2548 Some(_) => {
2549 self.errors.push(Error::invalid_field(decl, "target"));
2550 }
2551 None => {
2552 self.errors.push(Error::missing_field(decl, "target"));
2553 }
2554 }
2555 }
2556
2557 fn validate_target_child(
2558 &mut self,
2559 decl: DeclType,
2560 allowable_names: AllowableIds,
2561 child: &'a fdecl::ChildRef,
2562 target_name: Option<&'a String>,
2563 offer_type: OfferType,
2564 ) {
2565 if !self.validate_child_ref(decl, "target", child, offer_type) {
2566 return;
2567 }
2568 if let Some(target_name) = target_name {
2569 let names_for_target = self
2570 .target_ids
2571 .entry(TargetId::Component(
2572 &child.name,
2573 child.collection.as_ref().map(|s| s.as_str()),
2574 ))
2575 .or_insert(HashMap::new());
2576 if let Some(prev_state) = names_for_target.insert(target_name, allowable_names) {
2577 if prev_state == AllowableIds::One || prev_state != allowable_names {
2578 self.errors.push(Error::duplicate_field(
2579 decl,
2580 "target_name",
2581 target_name as &str,
2582 ));
2583 }
2584 }
2585 if let Some(collection) = child.collection.as_ref() {
2586 if let Some(names_for_target) =
2587 self.target_ids.get(&TargetId::Collection(&collection))
2588 {
2589 if names_for_target.contains_key(&target_name.as_str()) {
2590 self.errors.push(Error::duplicate_field(
2592 decl,
2593 "target_name",
2594 target_name as &str,
2595 ));
2596 }
2597 }
2598 }
2599 }
2600 }
2601
2602 fn validate_target_collection(
2603 &mut self,
2604 decl: DeclType,
2605 allowable_names: AllowableIds,
2606 collection: &'a fdecl::CollectionRef,
2607 target_name: Option<&'a String>,
2608 ) {
2609 if !self.validate_collection_ref(decl, "target", &collection) {
2610 return;
2611 }
2612 if let Some(target_name) = target_name {
2613 let names_for_target = self
2614 .target_ids
2615 .entry(TargetId::Collection(&collection.name))
2616 .or_insert(HashMap::new());
2617 if let Some(prev_state) = names_for_target.insert(target_name, allowable_names) {
2618 if prev_state == AllowableIds::One || prev_state != allowable_names {
2619 self.errors.push(Error::duplicate_field(
2620 decl,
2621 "target_name",
2622 target_name as &str,
2623 ));
2624 }
2625 }
2626 }
2627 }
2628
2629 fn validate_route_from_self(
2630 &mut self,
2631 decl: DeclType,
2632 source: Option<&fdecl::Ref>,
2633 source_name: Option<&String>,
2634 source_dictionary: Option<&String>,
2635 capability_checker: impl Fn(&Self) -> &dyn Container,
2636 ) {
2637 let (Some(fdecl::Ref::Self_(_)), Some(name)) = (source, source_name) else {
2638 return;
2639 };
2640 match source_dictionary {
2641 Some(source_dictionary) => {
2642 if let Ok(path) = cm_types::RelativePath::new(source_dictionary) {
2643 if let Some(first_segment) = path.iter_segments().next().map(|s| s.as_str()) {
2644 if !self.all_dictionaries.contains_key(first_segment) {
2645 self.errors.push(Error::invalid_capability(
2646 decl,
2647 "source",
2648 first_segment,
2649 ));
2650 }
2651 }
2652 }
2653 }
2654 None => {
2655 if !(capability_checker)(self).contains(name) {
2656 self.errors.push(Error::invalid_capability(decl, "source", name));
2657 }
2658 }
2659 }
2660 }
2661
2662 fn service_checker(&self) -> &dyn Container {
2665 &self.all_services
2666 }
2667 fn protocol_checker(&self) -> &dyn Container {
2668 &self.all_protocols
2669 }
2670 fn directory_checker(&self) -> &dyn Container {
2671 &self.all_directories
2672 }
2673 fn runner_checker(&self) -> &dyn Container {
2674 &self.all_runners
2675 }
2676 fn resolver_checker(&self) -> &dyn Container {
2677 &self.all_resolvers
2678 }
2679
2680 fn dictionary_checker(&self) -> &dyn Container {
2681 &self.all_dictionaries
2682 }
2683
2684 #[cfg(fuchsia_api_level_at_least = "HEAD")]
2685 fn config_checker(&self) -> &dyn Container {
2686 &self.all_configs
2687 }
2688 fn storage_checker(&self) -> &dyn Container {
2689 &self.all_storages
2690 }
2691 fn event_stream_checker(&self) -> &dyn Container {
2692 struct AlwaysTrueContainer {}
2696 impl Container for AlwaysTrueContainer {
2697 fn contains(&self, _key: &str) -> bool {
2698 true
2699 }
2700 }
2701 static CONTAINER: AlwaysTrueContainer = AlwaysTrueContainer {};
2702 &CONTAINER
2703 }
2704}
2705
2706#[cfg(test)]
2707mod tests {
2708 use super::*;
2709 use cm_types::MAX_LONG_NAME_LENGTH;
2710 use fidl_fuchsia_component_decl_common as fdecl;
2711 use fidl_fuchsia_data_common as fdata;
2712 use fidl_fuchsia_io_common as fio;
2713 use test_case::test_case;
2714
2715 macro_rules! test_validate {
2716 (
2717 $(
2718 $test_name:ident => {
2719 input = $input:expr,
2720 result = $result:expr,
2721 },
2722 )+
2723 ) => {
2724 $(
2725 #[test]
2726 fn $test_name() {
2727 validate_test($input, $result);
2728 }
2729 )+
2730 }
2731 }
2732
2733 macro_rules! test_validate_any_result {
2734 (
2735 $(
2736 $test_name:ident => {
2737 input = $input:expr,
2738 results = $results:expr,
2739 },
2740 )+
2741 ) => {
2742 $(
2743 #[test]
2744 fn $test_name() {
2745 validate_test_any_result($input, $results);
2746 }
2747 )+
2748 }
2749 }
2750
2751 macro_rules! test_validate_values_data {
2752 (
2753 $(
2754 $test_name:ident => {
2755 input = $input:expr,
2756 result = $result:expr,
2757 },
2758 )+
2759 ) => {
2760 $(
2761 #[test]
2762 fn $test_name() {
2763 validate_values_data_test($input, $result);
2764 }
2765 )+
2766 }
2767 }
2768
2769 macro_rules! test_validate_capabilities {
2770 (
2771 $(
2772 $test_name:ident => {
2773 input = $input:expr,
2774 as_builtin = $as_builtin:expr,
2775 result = $result:expr,
2776 },
2777 )+
2778 ) => {
2779 $(
2780 #[test]
2781 fn $test_name() {
2782 validate_capabilities_test($input, $as_builtin, $result);
2783 }
2784 )+
2785 }
2786 }
2787
2788 macro_rules! test_dependency {
2789 (
2790 $(
2791 ($test_name:ident) => {
2792 ty = $ty:expr,
2793 offer_decl = $offer_decl:expr,
2794 },
2795 )+
2796 ) => {
2797 $(
2798 #[test]
2799 fn $test_name() {
2800 let mut decl = new_component_decl();
2801 let dependencies = vec![
2802 ("a", "b"),
2803 ("b", "a"),
2804 ];
2805 let offers = dependencies.into_iter().map(|(from,to)| {
2806 let mut offer_decl = $offer_decl;
2807 offer_decl.source = Some(fdecl::Ref::Child(
2808 fdecl::ChildRef { name: from.to_string(), collection: None },
2809 ));
2810 offer_decl.target = Some(fdecl::Ref::Child(
2811 fdecl::ChildRef { name: to.to_string(), collection: None },
2812 ));
2813 $ty(offer_decl)
2814 }).collect();
2815 let children = ["a", "b"].iter().map(|name| {
2816 fdecl::Child {
2817 name: Some(name.to_string()),
2818 url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
2819 startup: Some(fdecl::StartupMode::Lazy),
2820 on_terminate: None,
2821 environment: None,
2822 ..Default::default()
2823 }
2824 }).collect();
2825 decl.offers = Some(offers);
2826 decl.children = Some(children);
2827 let result = Err(ErrorList::new(vec![
2828 Error::dependency_cycle("{{child a -> child b -> child a}}")
2829 ]));
2830 validate_test(decl, result);
2831 }
2832 )+
2833 }
2834 }
2835
2836 macro_rules! test_weak_dependency {
2837 (
2838 $(
2839 ($test_name:ident) => {
2840 ty = $ty:expr,
2841 offer_decl = $offer_decl:expr,
2842 },
2843 )+
2844 ) => {
2845 $(
2846 #[test_case(fdecl::DependencyType::Weak)]
2847 fn $test_name(weak_dep: fdecl::DependencyType) {
2848 let mut decl = new_component_decl();
2849 let offers = vec![
2850 {
2851 let mut offer_decl = $offer_decl;
2852 offer_decl.source = Some(fdecl::Ref::Child(
2853 fdecl::ChildRef { name: "a".to_string(), collection: None },
2854 ));
2855 offer_decl.target = Some(fdecl::Ref::Child(
2856 fdecl::ChildRef { name: "b".to_string(), collection: None },
2857 ));
2858 offer_decl.dependency_type = Some(fdecl::DependencyType::Strong);
2859 $ty(offer_decl)
2860 },
2861 {
2862 let mut offer_decl = $offer_decl;
2863 offer_decl.source = Some(fdecl::Ref::Child(
2864 fdecl::ChildRef { name: "b".to_string(), collection: None },
2865 ));
2866 offer_decl.target = Some(fdecl::Ref::Child(
2867 fdecl::ChildRef { name: "a".to_string(), collection: None },
2868 ));
2869 offer_decl.dependency_type = Some(weak_dep);
2870 $ty(offer_decl)
2871 },
2872 ];
2873 let children = ["a", "b"].iter().map(|name| {
2874 fdecl::Child {
2875 name: Some(name.to_string()),
2876 url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
2877 startup: Some(fdecl::StartupMode::Lazy),
2878 on_terminate: None,
2879 environment: None,
2880 ..Default::default()
2881 }
2882 }).collect();
2883 decl.offers = Some(offers);
2884 decl.children = Some(children);
2885 let result = Ok(());
2886 validate_test(decl, result);
2887 }
2888 )+
2889 }
2890 }
2891
2892 #[track_caller]
2893 fn validate_test(input: fdecl::Component, expected_res: Result<(), ErrorList>) {
2894 let res = validate(&input, &mut DirectedGraph::new());
2895 assert_eq!(res, expected_res);
2896 }
2897
2898 #[track_caller]
2899 fn validate_test_any_result(input: fdecl::Component, expected_res: Vec<Result<(), ErrorList>>) {
2900 let res = format!("{:?}", validate(&input, &mut DirectedGraph::new()));
2901 let expected_res_debug = format!("{:?}", expected_res);
2902
2903 let matched_exp =
2904 expected_res.into_iter().find(|expected| res == format!("{:?}", expected));
2905
2906 assert!(
2907 matched_exp.is_some(),
2908 "assertion failed: Expected one of:\n{:?}\nActual:\n{:?}",
2909 expected_res_debug,
2910 res
2911 );
2912 }
2913
2914 #[track_caller]
2915 fn validate_values_data_test(
2916 input: fdecl::ConfigValuesData,
2917 expected_res: Result<(), ErrorList>,
2918 ) {
2919 let res = validate_values_data(&input);
2920 assert_eq!(res, expected_res);
2921 }
2922
2923 #[track_caller]
2924 fn validate_capabilities_test(
2925 input: Vec<fdecl::Capability>,
2926 as_builtin: bool,
2927 expected_res: Result<(), ErrorList>,
2928 ) {
2929 let res = validate_capabilities(&input, as_builtin);
2930 assert_eq!(res, expected_res);
2931 }
2932
2933 fn new_component_decl() -> fdecl::Component {
2934 fdecl::Component {
2935 program: None,
2936 uses: None,
2937 exposes: None,
2938 offers: None,
2939 facets: None,
2940 capabilities: None,
2941 children: None,
2942 collections: None,
2943 environments: None,
2944 ..Default::default()
2945 }
2946 }
2947
2948 test_validate_any_result! {
2949 test_validate_use_disallows_nested_dirs => {
2950 input = {
2951 let mut decl = new_component_decl();
2952 decl.uses = Some(vec![
2953 fdecl::Use::Directory(fdecl::UseDirectory {
2954 dependency_type: Some(fdecl::DependencyType::Strong),
2955 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2956 source_name: Some("abc".to_string()),
2957 target_path: Some("/foo/bar".to_string()),
2958 rights: Some(fio::Operations::CONNECT),
2959 subdir: None,
2960 ..Default::default()
2961 }),
2962 fdecl::Use::Directory(fdecl::UseDirectory {
2963 dependency_type: Some(fdecl::DependencyType::Strong),
2964 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2965 source_name: Some("abc".to_string()),
2966 target_path: Some("/foo/bar/baz".to_string()),
2967 rights: Some(fio::Operations::CONNECT),
2968 subdir: None,
2969 ..Default::default()
2970 }),
2971 ]);
2972 decl
2973 },
2974 results = vec![
2975 Err(ErrorList::new(vec![
2976 Error::invalid_path_overlap(
2977 DeclType::UseDirectory, "/foo/bar/baz", DeclType::UseDirectory, "/foo/bar"),
2978 ])),
2979 Err(ErrorList::new(vec![
2980 Error::invalid_path_overlap(
2981 DeclType::UseDirectory, "/foo/bar", DeclType::UseDirectory, "/foo/bar/baz"),
2982 ])),
2983 ],
2984 },
2985 test_validate_use_disallows_nested_dirs_storage => {
2986 input = {
2987 let mut decl = new_component_decl();
2988 decl.uses = Some(vec![
2989 fdecl::Use::Storage(fdecl::UseStorage {
2990 source_name: Some("abc".to_string()),
2991 target_path: Some("/foo/bar".to_string()),
2992 ..Default::default()
2993 }),
2994 fdecl::Use::Storage(fdecl::UseStorage {
2995 source_name: Some("abc".to_string()),
2996 target_path: Some("/foo/bar/baz".to_string()),
2997 ..Default::default()
2998 }),
2999 ]);
3000 decl
3001 },
3002 results = vec![
3003 Err(ErrorList::new(vec![
3004 Error::invalid_path_overlap(
3005 DeclType::UseStorage, "/foo/bar/baz", DeclType::UseStorage, "/foo/bar"),
3006 ])),
3007 Err(ErrorList::new(vec![
3008 Error::invalid_path_overlap(
3009 DeclType::UseStorage, "/foo/bar", DeclType::UseStorage, "/foo/bar/baz"),
3010 ])),
3011 ],
3012 },
3013 test_validate_use_disallows_nested_dirs_directory_and_storage => {
3014 input = {
3015 let mut decl = new_component_decl();
3016 decl.uses = Some(vec![
3017 fdecl::Use::Directory(fdecl::UseDirectory {
3018 dependency_type: Some(fdecl::DependencyType::Strong),
3019 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3020 source_name: Some("abc".to_string()),
3021 target_path: Some("/foo/bar".to_string()),
3022 rights: Some(fio::Operations::CONNECT),
3023 subdir: None,
3024 ..Default::default()
3025 }),
3026 fdecl::Use::Storage(fdecl::UseStorage {
3027 source_name: Some("abc".to_string()),
3028 target_path: Some("/foo/bar/baz".to_string()),
3029 ..Default::default()
3030 }),
3031 ]);
3032 decl
3033 },
3034 results = vec![
3035 Err(ErrorList::new(vec![
3036 Error::invalid_path_overlap(
3037 DeclType::UseStorage, "/foo/bar/baz", DeclType::UseDirectory, "/foo/bar"),
3038 ])),
3039 Err(ErrorList::new(vec![
3040 Error::invalid_path_overlap(
3041 DeclType::UseDirectory, "/foo/bar", DeclType::UseStorage, "/foo/bar/baz"),
3042 ])),
3043 ],
3044 },
3045 test_validate_use_disallows_common_prefixes_protocol => {
3046 input = {
3047 let mut decl = new_component_decl();
3048 decl.uses = Some(vec![
3049 fdecl::Use::Directory(fdecl::UseDirectory {
3050 dependency_type: Some(fdecl::DependencyType::Strong),
3051 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3052 source_name: Some("abc".to_string()),
3053 target_path: Some("/foo/bar".to_string()),
3054 rights: Some(fio::Operations::CONNECT),
3055 subdir: None,
3056 ..Default::default()
3057 }),
3058 fdecl::Use::Protocol(fdecl::UseProtocol {
3059 dependency_type: Some(fdecl::DependencyType::Strong),
3060 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3061 source_name: Some("crow".to_string()),
3062 target_path: Some("/foo/bar/fuchsia.2".to_string()),
3063 ..Default::default()
3064 }),
3065 ]);
3066 decl
3067 },
3068 results = vec![
3069 Err(ErrorList::new(vec![
3070 Error::invalid_path_overlap(
3071 DeclType::UseProtocol, "/foo/bar/fuchsia.2", DeclType::UseDirectory, "/foo/bar"),
3072 ])),
3073 Err(ErrorList::new(vec![
3074 Error::invalid_path_overlap(
3075 DeclType::UseDirectory, "/foo/bar", DeclType::UseProtocol, "/foo/bar/fuchsia.2"),
3076 ])),
3077 ],
3078 },
3079 test_validate_use_disallows_common_prefixes_service => {
3080 input = {
3081 let mut decl = new_component_decl();
3082 decl.uses = Some(vec![
3083 fdecl::Use::Directory(fdecl::UseDirectory {
3084 dependency_type: Some(fdecl::DependencyType::Strong),
3085 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3086 source_name: Some("abc".to_string()),
3087 target_path: Some("/foo/bar".to_string()),
3088 rights: Some(fio::Operations::CONNECT),
3089 subdir: None,
3090 ..Default::default()
3091 }),
3092 fdecl::Use::Service(fdecl::UseService {
3093 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3094 source_name: Some("space".to_string()),
3095 target_path: Some("/foo/bar/baz/fuchsia.logger.Log".to_string()),
3096 dependency_type: Some(fdecl::DependencyType::Strong),
3097 ..Default::default()
3098 }),
3099 ]);
3100 decl
3101 },
3102 results = vec![
3103 Err(ErrorList::new(vec![
3104 Error::invalid_path_overlap(
3105 DeclType::UseService, "/foo/bar/baz/fuchsia.logger.Log", DeclType::UseDirectory, "/foo/bar"),
3106 ])),
3107 Err(ErrorList::new(vec![
3108 Error::invalid_path_overlap(
3109 DeclType::UseDirectory, "/foo/bar", DeclType::UseService, "/foo/bar/baz/fuchsia.logger.Log"),
3110 ])),
3111 ],
3112 },
3113 test_validate_use_disallows_pkg => {
3114 input = {
3115 let mut decl = new_component_decl();
3116 decl.uses = Some(vec![
3117 fdecl::Use::Directory(fdecl::UseDirectory {
3118 dependency_type: Some(fdecl::DependencyType::Strong),
3119 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3120 source_name: Some("abc".to_string()),
3121 target_path: Some("/pkg".to_string()),
3122 rights: Some(fio::Operations::CONNECT),
3123 subdir: None,
3124 ..Default::default()
3125 }),
3126 ]);
3127 decl
3128 },
3129 results = vec![
3130 Err(ErrorList::new(vec![
3131 Error::pkg_path_overlap(DeclType::UseDirectory, "/pkg"),
3132 ])),
3133 ],
3134 },
3135 test_validate_use_disallows_pkg_overlap => {
3136 input = {
3137 let mut decl = new_component_decl();
3138 decl.uses = Some(vec![
3139 fdecl::Use::Directory(fdecl::UseDirectory {
3140 dependency_type: Some(fdecl::DependencyType::Strong),
3141 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3142 source_name: Some("abc".to_string()),
3143 target_path: Some("/pkg/foo".to_string()),
3144 rights: Some(fio::Operations::CONNECT),
3145 subdir: None,
3146 ..Default::default()
3147 }),
3148 ]);
3149 decl
3150 },
3151 results = vec![
3152 Err(ErrorList::new(vec![
3153 Error::pkg_path_overlap(DeclType::UseDirectory, "/pkg/foo"),
3154 ])),
3155 ],
3156 },
3157 test_validate_use_optional_config_correct => {
3158 input = {
3159 let mut decl = new_component_decl();
3160 decl.uses = Some(vec![
3161 fdecl::Use::Config(fdecl::UseConfiguration {
3162 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3163 source_name: Some("abc".to_string()),
3164 target_name: Some("foo".to_string()),
3165 availability: Some(fdecl::Availability::Optional),
3166 type_: Some(fdecl::ConfigType {
3167 layout: fdecl::ConfigTypeLayout::Bool,
3168 parameters: Some(Vec::new()),
3169 constraints: Vec::new(),
3170 }),
3171 ..Default::default()
3172 }),
3173 ]);
3174 decl.config = Some(fdecl::ConfigSchema {
3175 fields: Some(vec![fdecl::ConfigField {
3176 key: Some("foo".into()),
3177 type_: Some(fdecl::ConfigType {
3178 layout: fdecl::ConfigTypeLayout::Bool,
3179 parameters: Some(Vec::new()),
3180 constraints: Vec::new(),
3181 }),
3182 mutability: None,
3183 ..Default::default()}]),
3184 checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3185 value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3186 ..Default::default()
3187 });
3188 decl
3189 },
3190 results = vec![Ok(())],
3191 },
3192 test_validate_use_optional_config_no_config_schema => {
3193 input = {
3194 let mut decl = new_component_decl();
3195 decl.uses = Some(vec![
3196 fdecl::Use::Config(fdecl::UseConfiguration {
3197 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3198 source_name: Some("abc".to_string()),
3199 target_name: Some("foo".to_string()),
3200 availability: Some(fdecl::Availability::Optional),
3201 type_: Some(fdecl::ConfigType {
3202 layout: fdecl::ConfigTypeLayout::Bool,
3203 parameters: None,
3204 constraints: Vec::new(),
3205 }),
3206 ..Default::default()
3207 }),
3208 ]);
3209 decl
3210 },
3211 results = vec![
3212 Err(ErrorList::new(vec![
3213 Error::missing_field(DeclType::ConfigField, "config"),
3214 ])),
3215 ],
3216 },
3217 test_validate_use_optional_config_no_config_field => {
3218 input = {
3219 let mut decl = new_component_decl();
3220 decl.uses = Some(vec![
3221 fdecl::Use::Config(fdecl::UseConfiguration {
3222 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3223 source_name: Some("abc".to_string()),
3224 target_name: Some("foo".to_string()),
3225 availability: Some(fdecl::Availability::Optional),
3226 type_: Some(fdecl::ConfigType {
3227 layout: fdecl::ConfigTypeLayout::Bool,
3228 parameters: None,
3229 constraints: Vec::new(),
3230 }),
3231 ..Default::default()
3232 }),
3233 ]);
3234 decl.config = Some(fdecl::ConfigSchema {
3235 fields: Some(vec![]),
3236 checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3237 value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3238 ..Default::default()
3239 });
3240 decl
3241 },
3242 results = vec![
3243 Err(ErrorList::new(vec![
3244 Error::missing_field(DeclType::ConfigField, "foo"),
3245 ])),
3246 ],
3247 },
3248 test_validate_use_optional_config_bad_type => {
3249 input = {
3250 let mut decl = new_component_decl();
3251 decl.uses = Some(vec![
3252 fdecl::Use::Config(fdecl::UseConfiguration {
3253 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3254 source_name: Some("abc".to_string()),
3255 target_name: Some("foo".to_string()),
3256 availability: Some(fdecl::Availability::Optional),
3257 type_: Some(fdecl::ConfigType {
3258 layout: fdecl::ConfigTypeLayout::Bool,
3259 parameters: None,
3260 constraints: Vec::new(),
3261 }),
3262 ..Default::default()
3263 }),
3264 ]);
3265 decl.config = Some(fdecl::ConfigSchema {
3266 fields: Some(vec![fdecl::ConfigField {
3267 key: Some("foo".into()),
3268 type_: Some(fdecl::ConfigType {
3269 layout: fdecl::ConfigTypeLayout::Int16,
3270 parameters: Some(Vec::new()),
3271 constraints: Vec::new(),
3272 }),
3273 mutability: None,
3274 ..Default::default()}]),
3275 checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3276 value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3277 ..Default::default()
3278 });
3279 decl
3280 },
3281 results = vec![
3282 Err(ErrorList::new(vec![
3283 Error::invalid_field(DeclType::ConfigField, "foo"),
3284 ])),
3285 ],
3286 },
3287 }
3288
3289 #[cfg(fuchsia_api_level_at_least = "29")]
3290 test_validate_any_result! {
3291 test_validate_use_dictionary => {
3292 input = {
3293 let mut decl = new_component_decl();
3294 decl.uses = Some(vec![
3295 fdecl::Use::Dictionary(fdecl::UseDictionary {
3296 dependency_type: Some(fdecl::DependencyType::Strong),
3297 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3298 source_name: Some("toolbox".to_string()),
3299 target_path: Some("/svc".to_string()),
3300 availability: Some(fdecl::Availability::Required),
3301 ..Default::default()
3302 }),
3303 ]);
3304 decl
3305 },
3306 results = vec![
3307 Ok(()),
3308 ],
3309 },
3310 test_validate_use_dictionary_invalid_name => {
3311 input = {
3312 let mut decl = new_component_decl();
3313 decl.uses = Some(vec![
3314 fdecl::Use::Dictionary(fdecl::UseDictionary {
3315 dependency_type: Some(fdecl::DependencyType::Strong),
3316 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3317 source_name: Some("toolbox@".to_string()),
3318 target_path: Some("/svc".to_string()),
3319 availability: Some(fdecl::Availability::Required),
3320 ..Default::default()
3321 }),
3322 ]);
3323 decl
3324 },
3325 results = vec![
3326 Err(ErrorList::new(vec![Error::invalid_field(DeclType::UseDictionary, "source_name")])),
3327 ],
3328 },
3329 test_validate_use_dictionary_invalid_path => {
3330 input = {
3331 let mut decl = new_component_decl();
3332 decl.uses = Some(vec![
3333 fdecl::Use::Dictionary(fdecl::UseDictionary {
3334 dependency_type: Some(fdecl::DependencyType::Strong),
3335 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3336 source_name: Some("toolbox".to_string()),
3337 target_path: Some("/svc@".to_string()),
3338 availability: Some(fdecl::Availability::Required),
3339 ..Default::default()
3340 }),
3341 ]);
3342 decl
3343 },
3344 results = vec![
3345 Err(ErrorList::new(vec![Error::field_invalid_segment(DeclType::UseDictionary, "target_path")])),
3346 ],
3347 },
3348 test_validate_use_dictionary_disallows_pkg_overlap => {
3349 input = {
3350 let mut decl = new_component_decl();
3351 decl.uses = Some(vec![
3352 fdecl::Use::Dictionary(fdecl::UseDictionary {
3353 dependency_type: Some(fdecl::DependencyType::Strong),
3354 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3355 source_name: Some("toolbox".to_string()),
3356 target_path: Some("/pkg/toolbox".to_string()),
3357 availability: Some(fdecl::Availability::Required),
3358 ..Default::default()
3359 }),
3360 ]);
3361 decl
3362 },
3363 results = vec![
3364 Err(ErrorList::new(vec![
3365 Error::pkg_path_overlap(DeclType::UseDictionary, "/pkg/toolbox"),
3366 ])),
3367 ],
3368 },
3369 test_validate_use_dictionary_path_overlap_directory => {
3370 input = {
3371 let mut decl = new_component_decl();
3372 decl.uses = Some(vec![
3373 fdecl::Use::Dictionary(fdecl::UseDictionary {
3374 dependency_type: Some(fdecl::DependencyType::Strong),
3375 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3376 source_name: Some("toolbox".to_string()),
3377 target_path: Some("/foo/bar".to_string()),
3378 availability: Some(fdecl::Availability::Required),
3379 ..Default::default()
3380 }),
3381 fdecl::Use::Directory(fdecl::UseDirectory {
3382 dependency_type: Some(fdecl::DependencyType::Strong),
3383 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3384 source_name: Some("data".to_string()),
3385 target_path: Some("/foo".to_string()),
3386 rights: Some(fio::Operations::CONNECT),
3387 subdir: None,
3388 ..Default::default()
3389 }),
3390 ]);
3391 decl
3392 },
3393 results = vec![
3394 Err(ErrorList::new(vec![
3395 Error::invalid_path_overlap(
3396 DeclType::UseDirectory,
3397 "/foo",
3398 DeclType::UseDictionary,
3399 "/foo/bar",
3400 ),
3401 ])),
3402 Err(ErrorList::new(vec![
3403 Error::invalid_path_overlap(
3404 DeclType::UseDictionary,
3405 "/foo/bar",
3406 DeclType::UseDirectory,
3407 "/foo",
3408 ),
3409 ])),
3410 ],
3411 },
3412 test_validate_use_dictionary_path_overlap_protocol => {
3413 input = {
3414 let mut decl = new_component_decl();
3415 decl.uses = Some(vec![
3416 fdecl::Use::Dictionary(fdecl::UseDictionary {
3417 dependency_type: Some(fdecl::DependencyType::Strong),
3418 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3419 source_name: Some("toolbox".to_string()),
3420 target_path: Some("/svc/toolbox".to_string()),
3421 availability: Some(fdecl::Availability::Required),
3422 ..Default::default()
3423 }),
3424 fdecl::Use::Protocol(fdecl::UseProtocol {
3425 dependency_type: Some(fdecl::DependencyType::Strong),
3426 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3427 source_name: Some("fuchsia.examples.Echo".to_string()),
3428 target_path: Some("/svc/fuchsia.examples.Echo".to_string()),
3429 ..Default::default()
3430 }),
3431 ]);
3432 decl
3433 },
3434 results = vec![
3435 Ok(()),
3436 ],
3437 },
3438 test_validate_use_dictionary_path_overlap_protocol_2 => {
3439 input = {
3440 let mut decl = new_component_decl();
3441 decl.uses = Some(vec![
3442 fdecl::Use::Dictionary(fdecl::UseDictionary {
3443 dependency_type: Some(fdecl::DependencyType::Strong),
3444 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3445 source_name: Some("toolbox".to_string()),
3446 target_path: Some("/svc".to_string()),
3447 availability: Some(fdecl::Availability::Required),
3448 ..Default::default()
3449 }),
3450 fdecl::Use::Protocol(fdecl::UseProtocol {
3451 dependency_type: Some(fdecl::DependencyType::Strong),
3452 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3453 source_name: Some("fuchsia.examples.Echo".to_string()),
3454 target_path: Some("/svc/fuchsia.examples.Echo".to_string()),
3455 ..Default::default()
3456 }),
3457 ]);
3458 decl
3459 },
3460 results = vec![
3461 Ok(()),
3462 ],
3463 },
3464 test_validate_use_dictionary_path_identical_dictionary => {
3465 input = {
3466 let mut decl = new_component_decl();
3467 decl.uses = Some(vec![
3468 fdecl::Use::Dictionary(fdecl::UseDictionary {
3469 dependency_type: Some(fdecl::DependencyType::Strong),
3470 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3471 source_name: Some("toolbox-1".to_string()),
3472 target_path: Some("/svc".to_string()),
3473 availability: Some(fdecl::Availability::Required),
3474 ..Default::default()
3475 }),
3476 fdecl::Use::Dictionary(fdecl::UseDictionary {
3477 dependency_type: Some(fdecl::DependencyType::Strong),
3478 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3479 source_name: Some("toolbox-2".to_string()),
3480 target_path: Some("/svc".to_string()),
3481 availability: Some(fdecl::Availability::Required),
3482 ..Default::default()
3483 }),
3484 fdecl::Use::Protocol(fdecl::UseProtocol {
3485 dependency_type: Some(fdecl::DependencyType::Strong),
3486 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3487 source_name: Some("fuchsia.examples.Echo".to_string()),
3488 target_path: Some("/svc/fuchsia.examples.Echo".to_string()),
3489 ..Default::default()
3490 }),
3491 ]);
3492 decl
3493 },
3494 results = vec![
3495 Ok(()),
3496 ],
3497 },
3498 test_validate_use_two_dictionaires_and_a_protocol_at_the_same_path => {
3499 input = {
3500 fdecl::Component {
3501 program: Some(fdecl::Program {
3502 runner: Some("realm_builder".to_string()),
3503 info: Some(fdata::Dictionary {
3504 entries: Some(vec![ ]),
3505 ..Default::default()
3506 }),
3507 ..Default::default()
3508 }),
3509 uses: Some(vec![
3510 fdecl::Use::Dictionary(fdecl::UseDictionary {
3511 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3512 source_name: Some("my_dictionary".to_string()),
3513 target_path: Some("/svc".to_string()),
3514 dependency_type: Some(fdecl::DependencyType::Strong),
3515 availability: Some(fdecl::Availability::Required),
3516 source_dictionary: None,
3517 ..Default::default()
3518 }),
3519 fdecl::Use::Dictionary(fdecl::UseDictionary {
3520 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
3521 source_name: Some("my_dictionary2".to_string()),
3522 target_path: Some("/svc".to_string()),
3523 dependency_type: Some(fdecl::DependencyType::Strong),
3524 availability: Some(fdecl::Availability::Required),
3525 source_dictionary: None,
3526 ..Default::default()
3527 }),
3528 fdecl::Use::Protocol(fdecl::UseProtocol {
3529 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
3530 source_name: Some("fuchsia.echo2".to_string()),
3531 target_path: Some("/svc/fuchsia.echo2".to_string()),
3532 dependency_type: Some(fdecl::DependencyType::Strong),
3533 availability: Some(fdecl::Availability::Required),
3534 source_dictionary: None,
3535 numbered_handle: None,
3536 ..Default::default()
3537 }),
3538 ]),
3539 ..Default::default()
3540 }
3541 },
3542 results = vec![
3543 Ok(()),
3544 ],
3545 },
3546 }
3547
3548 test_validate_values_data! {
3549 test_values_data_ok => {
3550 input = fdecl::ConfigValuesData {
3551 values: Some(vec![
3552 fdecl::ConfigValueSpec {
3553 value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(true))),
3554 ..Default::default()
3555 }
3556 ]),
3557 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3558 ..Default::default()
3559 },
3560 result = Ok(()),
3561 },
3562 test_values_data_no_checksum => {
3563 input = fdecl::ConfigValuesData {
3564 values: Some(vec![]),
3565 checksum: None,
3566 ..Default::default()
3567 },
3568 result = Err(ErrorList::new(vec![
3569 Error::missing_field(DeclType::ConfigValuesData, "checksum")
3570 ])),
3571 },
3572 test_values_data_unknown_checksum => {
3573 input = fdecl::ConfigValuesData {
3574 values: Some(vec![]),
3575 checksum: Some(fdecl::ConfigChecksum::unknown_variant_for_testing()),
3576 ..Default::default()
3577 },
3578 result = Err(ErrorList::new(vec![
3579 Error::invalid_field(DeclType::ConfigValuesData, "checksum")
3580 ])),
3581 },
3582 test_values_data_no_values => {
3583 input = fdecl::ConfigValuesData {
3584 values: None,
3585 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3586 ..Default::default()
3587 },
3588 result = Err(ErrorList::new(vec![
3589 Error::missing_field(DeclType::ConfigValuesData, "values")
3590 ])),
3591 },
3592 test_values_data_no_inner_value => {
3593 input = fdecl::ConfigValuesData {
3594 values: Some(vec![
3595 fdecl::ConfigValueSpec::default()
3596 ]),
3597 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3598 ..Default::default()
3599 },
3600 result = Err(ErrorList::new(vec![
3601 Error::missing_field(DeclType::ConfigValueSpec, "value")
3602 ])),
3603 },
3604 test_values_data_unknown_inner_value => {
3605 input = fdecl::ConfigValuesData {
3606 values: Some(vec![
3607 fdecl::ConfigValueSpec {
3608 value: Some(fdecl::ConfigValue::unknown_variant_for_testing()),
3609 ..Default::default()
3610 }
3611 ]),
3612 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3613 ..Default::default()
3614 },
3615 result = Err(ErrorList::new(vec![
3616 Error::invalid_field(DeclType::ConfigValueSpec, "value")
3617 ])),
3618 },
3619 test_values_data_unknown_single_value => {
3620 input = fdecl::ConfigValuesData {
3621 values: Some(vec![
3622 fdecl::ConfigValueSpec {
3623 value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::unknown_variant_for_testing())),
3624 ..Default::default()
3625 }
3626 ]),
3627 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3628 ..Default::default()
3629 },
3630 result = Err(ErrorList::new(vec![
3631 Error::invalid_field(DeclType::ConfigValueSpec, "value")
3632 ])),
3633 },
3634 test_values_data_unknown_list_value => {
3635 input = fdecl::ConfigValuesData {
3636 values: Some(vec![
3637 fdecl::ConfigValueSpec {
3638 value: Some(fdecl::ConfigValue::Vector(fdecl::ConfigVectorValue::unknown_variant_for_testing())),
3639 ..Default::default()
3640 }
3641 ]),
3642 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3643 ..Default::default()
3644 },
3645 result = Err(ErrorList::new(vec![
3646 Error::invalid_field(DeclType::ConfigValueSpec, "value")
3647 ])),
3648 },
3649 }
3650
3651 test_validate! {
3652 test_validate_uses_empty => {
3654 input = {
3655 let mut decl = new_component_decl();
3656 decl.program = Some(fdecl::Program {
3657 runner: Some("elf".to_string()),
3658 info: Some(fdata::Dictionary {
3659 entries: None,
3660 ..Default::default()
3661 }),
3662 ..Default::default()
3663 });
3664 decl.uses = Some(vec![
3665 fdecl::Use::Service(fdecl::UseService {
3666 source: None,
3667 source_name: None,
3668 target_path: None,
3669 dependency_type: None,
3670 ..Default::default()
3671 }),
3672 fdecl::Use::Protocol(fdecl::UseProtocol {
3673 dependency_type: None,
3674 source: None,
3675 source_name: None,
3676 target_path: None,
3677 ..Default::default()
3678 }),
3679 fdecl::Use::Directory(fdecl::UseDirectory {
3680 dependency_type: None,
3681 source: None,
3682 source_name: None,
3683 target_path: None,
3684 rights: None,
3685 subdir: None,
3686 ..Default::default()
3687 }),
3688 fdecl::Use::Storage(fdecl::UseStorage {
3689 source_name: None,
3690 target_path: None,
3691 ..Default::default()
3692 }),
3693 fdecl::Use::EventStream(fdecl::UseEventStream {
3694 source_name: None,
3695 source: None,
3696 target_path: None,
3697 ..Default::default()
3698 }),
3699 fdecl::Use::Runner(fdecl::UseRunner {
3700 source_name: None,
3701 source: None,
3702 ..Default::default()
3703 }),
3704 ]);
3705 decl
3706 },
3707 result = Err(ErrorList::new(vec![
3708 Error::missing_field(DeclType::UseService, "source"),
3709 Error::missing_field(DeclType::UseService, "source_name"),
3710 Error::missing_field(DeclType::UseService, "target_path"),
3711 Error::missing_field(DeclType::UseService, "dependency_type"),
3712 Error::missing_field(DeclType::UseProtocol, "source"),
3713 Error::missing_field(DeclType::UseProtocol, "source_name"),
3714 Error::missing_field(DeclType::UseProtocol, "target_path"),
3715 Error::missing_field(DeclType::UseProtocol, "dependency_type"),
3716 Error::missing_field(DeclType::UseDirectory, "source"),
3717 Error::missing_field(DeclType::UseDirectory, "source_name"),
3718 Error::missing_field(DeclType::UseDirectory, "target_path"),
3719 Error::missing_field(DeclType::UseDirectory, "dependency_type"),
3720 Error::missing_field(DeclType::UseDirectory, "rights"),
3721 Error::missing_field(DeclType::UseStorage, "source_name"),
3722 Error::missing_field(DeclType::UseStorage, "target_path"),
3723 Error::missing_field(DeclType::UseEventStream, "source"),
3724 Error::missing_field(DeclType::UseEventStream, "source_name"),
3725 Error::missing_field(DeclType::UseEventStream, "target_path"),
3726 Error::missing_field(DeclType::UseRunner, "source"),
3727 Error::missing_field(DeclType::UseRunner, "source_name"),
3728 ])),
3729 },
3730 test_validate_missing_program_info => {
3731 input = {
3732 let mut decl = new_component_decl();
3733 decl.program = Some(fdecl::Program {
3734 runner: Some("runner".to_string()),
3735 info: None,
3736 ..Default::default()
3737 });
3738 decl
3739 },
3740 result = Err(ErrorList::new(vec![
3741 Error::missing_field(DeclType::Program, "info")
3742 ])),
3743 },
3744 test_validate_uses_invalid_identifiers => {
3745 input = {
3746 let mut decl = new_component_decl();
3747 decl.uses = Some(vec![
3748 fdecl::Use::Service(fdecl::UseService {
3749 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
3750 name: "^bad".to_string(),
3751 })),
3752 source_name: Some("foo/".to_string()),
3753 target_path: Some("a/foo".to_string()),
3754 dependency_type: Some(fdecl::DependencyType::Strong),
3755 ..Default::default()
3756 }),
3757 fdecl::Use::Protocol(fdecl::UseProtocol {
3758 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3759 name: "^bad".to_string(),
3760 collection: None,
3761 })),
3762 source_name: Some("foo/".to_string()),
3763 target_path: Some("b/foo".to_string()),
3764 dependency_type: Some(fdecl::DependencyType::Strong),
3765 ..Default::default()
3766 }),
3767 fdecl::Use::Directory(fdecl::UseDirectory {
3768 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3769 name: "^bad".to_string(),
3770 collection: None,
3771 })),
3772 source_name: Some("foo/".to_string()),
3773 target_path: Some("c".to_string()),
3774 rights: Some(fio::Operations::CONNECT),
3775 subdir: Some("/foo".to_string()),
3776 dependency_type: Some(fdecl::DependencyType::Strong),
3777 ..Default::default()
3778 }),
3779 fdecl::Use::Storage(fdecl::UseStorage {
3780 source_name: Some("foo/".to_string()),
3781 target_path: Some("d".to_string()),
3782 ..Default::default()
3783 }),
3784 fdecl::Use::EventStream(fdecl::UseEventStream {
3785 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3786 name: "^bad".to_string(),
3787 collection: None,
3788 })),
3789 source_name: Some("foo/".to_string()),
3790 target_path: Some("e".to_string()),
3791 ..Default::default()
3792 }),
3793 fdecl::Use::Runner(fdecl::UseRunner {
3794 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3795 name: "^bad".to_string(),
3796 collection: None,
3797 })),
3798 source_name: Some("foo/".to_string()),
3799 ..Default::default()
3800 }),
3801 ]);
3802 decl
3803 },
3804 result = Err(ErrorList::new(vec![
3805 Error::invalid_field(DeclType::UseService, "source.capability.name"),
3806 Error::invalid_field(DeclType::UseService, "source_name"),
3807 Error::invalid_field(DeclType::UseService, "target_path"),
3808 Error::invalid_field(DeclType::UseProtocol, "source.child.name"),
3809 Error::invalid_field(DeclType::UseProtocol, "source_name"),
3810 Error::invalid_field(DeclType::UseProtocol, "target_path"),
3811 Error::invalid_field(DeclType::UseDirectory, "source.child.name"),
3812 Error::invalid_field(DeclType::UseDirectory, "source_name"),
3813 Error::invalid_field(DeclType::UseDirectory, "target_path"),
3814 Error::invalid_field(DeclType::UseDirectory, "subdir"),
3815 Error::invalid_field(DeclType::UseStorage, "source_name"),
3816 Error::invalid_field(DeclType::UseStorage, "target_path"),
3817 Error::invalid_field(DeclType::UseEventStream, "source.child.name"),
3818 Error::invalid_field(DeclType::UseEventStream, "source_name"),
3819 Error::invalid_field(DeclType::UseEventStream, "target_path"),
3820 Error::invalid_field(DeclType::UseRunner, "source.child.name"),
3821 Error::invalid_field(DeclType::UseRunner, "source_name"),
3822 ])),
3823 },
3824 test_validate_uses_missing_source => {
3825 input = {
3826 fdecl::Component {
3827 uses: Some(vec![
3828 fdecl::Use::Protocol(fdecl::UseProtocol {
3829 dependency_type: Some(fdecl::DependencyType::Strong),
3830 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
3831 name: "this-storage-doesnt-exist".to_string(),
3832 })),
3833 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3834 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3835 ..Default::default()
3836 })
3837 ]),
3838 ..new_component_decl()
3839 }
3840 },
3841 result = Err(ErrorList::new(vec![
3842 Error::invalid_capability(DeclType::UseProtocol, "source", "this-storage-doesnt-exist"),
3843 ])),
3844 },
3845 test_validate_uses_invalid_child => {
3846 input = {
3847 fdecl::Component {
3848 uses: Some(vec![
3849 fdecl::Use::Protocol(fdecl::UseProtocol {
3850 dependency_type: Some(fdecl::DependencyType::Strong),
3851 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3852 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3853 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3854 ..Default::default()
3855 }),
3856 fdecl::Use::Service(fdecl::UseService {
3857 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3858 source_name: Some("service_name".to_string()),
3859 target_path: Some("/svc/service_name".to_string()),
3860 dependency_type: Some(fdecl::DependencyType::Strong),
3861 ..Default::default()
3862 }),
3863 fdecl::Use::Directory(fdecl::UseDirectory {
3864 dependency_type: Some(fdecl::DependencyType::Strong),
3865 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3866 source_name: Some("DirectoryName".to_string()),
3867 target_path: Some("/data/DirectoryName".to_string()),
3868 rights: Some(fio::Operations::CONNECT),
3869 subdir: None,
3870 ..Default::default()
3871 }),
3872 fdecl::Use::Runner(fdecl::UseRunner {
3873 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3874 source_name: Some("RunnerName".to_string()),
3875 ..Default::default()
3876 }),
3877 ]),
3878 ..new_component_decl()
3879 }
3880 },
3881 result = Err(ErrorList::new(vec![
3882 Error::invalid_child(DeclType::UseProtocol, "source", "no-such-child"),
3883 Error::invalid_child(DeclType::UseService, "source", "no-such-child"),
3884 Error::invalid_child(DeclType::UseDirectory, "source", "no-such-child"),
3885 Error::invalid_child(DeclType::UseRunner, "source", "no-such-child"),
3886 ])),
3887 },
3888 test_validate_uses_invalid_capability_from_self => {
3889 input = {
3890 let mut decl = new_component_decl();
3891 decl.uses = Some(vec![
3892 fdecl::Use::Service(fdecl::UseService {
3893 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3894 source_name: Some("fuchsia.some.library.SomeService".into()),
3895 target_path: Some("/svc/foo".into()),
3896 dependency_type: Some(fdecl::DependencyType::Strong),
3897 ..Default::default()
3898 }),
3899 fdecl::Use::Protocol(fdecl::UseProtocol {
3900 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3901 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
3902 target_path: Some("/svc/bar".into()),
3903 dependency_type: Some(fdecl::DependencyType::Strong),
3904 ..Default::default()
3905 }),
3906 fdecl::Use::Directory(fdecl::UseDirectory {
3907 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3908 source_name: Some("dir".into()),
3909 target_path: Some("/assets".into()),
3910 dependency_type: Some(fdecl::DependencyType::Strong),
3911 rights: Some(fio::Operations::CONNECT),
3912 ..Default::default()
3913 }),
3914 fdecl::Use::Runner(fdecl::UseRunner {
3915 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3916 source_name: Some("source_elf".into()),
3917 ..Default::default()
3918 }),
3919 fdecl::Use::Config(fdecl::UseConfiguration {
3920 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3921 source_name: Some("source_config".into()),
3922 target_name: Some("config".into()),
3923 type_: Some(fdecl::ConfigType {
3924 layout: fdecl::ConfigTypeLayout::Bool,
3925 parameters: Some(Vec::new()),
3926 constraints: Vec::new(),
3927 }),
3928 ..Default::default()
3929 }),
3930 fdecl::Use::Protocol(fdecl::UseProtocol {
3931 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3932 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
3933 source_dictionary: Some("dict/inner".into()),
3934 target_path: Some("/svc/baz".into()),
3935 dependency_type: Some(fdecl::DependencyType::Strong),
3936 ..Default::default()
3937 }),
3938 ]);
3939 decl
3940 },
3941 result = Err(ErrorList::new(vec![
3942 Error::invalid_capability(
3943 DeclType::UseService,
3944 "source",
3945 "fuchsia.some.library.SomeService"),
3946 Error::invalid_capability(
3947 DeclType::UseProtocol,
3948 "source",
3949 "fuchsia.some.library.SomeProtocol"),
3950 Error::invalid_capability(DeclType::UseDirectory, "source", "dir"),
3951 Error::invalid_capability(DeclType::UseRunner, "source", "source_elf"),
3952 Error::invalid_capability(DeclType::UseConfiguration, "source", "source_config"),
3953 Error::invalid_capability(DeclType::UseProtocol, "source", "dict"),
3954 ])),
3955 },
3956 test_validate_use_numbered_handle => {
3957 input = {
3958 fdecl::Component {
3959 uses: Some(vec![
3960 fdecl::Use::Protocol(fdecl::UseProtocol {
3962 dependency_type: Some(fdecl::DependencyType::Strong),
3963 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
3964 source_name: Some("fuchsia.logger.LogSink".into()),
3965 numbered_handle: Some(0xab),
3966 ..Default::default()
3967 }),
3968 fdecl::Use::Protocol(fdecl::UseProtocol {
3970 dependency_type: Some(fdecl::DependencyType::Strong),
3971 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
3972 source_name: Some("fuchsia.logger.LogSink".into()),
3973 target_path: Some("/svc/fuchsia.logger.LogSink".into()),
3974 numbered_handle: Some(0xab),
3975 ..Default::default()
3976 }),
3977 ]),
3978 ..new_component_decl()
3979 }
3980 },
3981 result = Err(ErrorList::new(vec![
3982 Error::extraneous_field(DeclType::UseProtocol, "numbered_handle"),
3983 ])),
3984 },
3985 test_validate_use_from_child_offer_to_child_strong_cycle => {
3986 input = {
3987 fdecl::Component {
3988 capabilities: Some(vec![
3989 fdecl::Capability::Service(fdecl::Service {
3990 name: Some("a".to_string()),
3991 source_path: Some("/a".to_string()),
3992 ..Default::default()
3993 })]),
3994 uses: Some(vec![
3995 fdecl::Use::Protocol(fdecl::UseProtocol {
3996 dependency_type: Some(fdecl::DependencyType::Strong),
3997 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3998 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3999 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
4000 ..Default::default()
4001 }),
4002 fdecl::Use::Service(fdecl::UseService {
4003 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4004 source_name: Some("service_name".to_string()),
4005 target_path: Some("/svc/service_name".to_string()),
4006 dependency_type: Some(fdecl::DependencyType::Strong),
4007 ..Default::default()
4008 }),
4009 fdecl::Use::Directory(fdecl::UseDirectory {
4010 dependency_type: Some(fdecl::DependencyType::Strong),
4011 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4012 source_name: Some("DirectoryName".to_string()),
4013 target_path: Some("/data/DirectoryName".to_string()),
4014 rights: Some(fio::Operations::CONNECT),
4015 subdir: None,
4016 ..Default::default()
4017 }),
4018 ]),
4019 offers: Some(vec![
4020 fdecl::Offer::Service(fdecl::OfferService {
4021 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4022 source_name: Some("a".to_string()),
4023 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4024 target_name: Some("a".to_string()),
4025 ..Default::default()
4026 })
4027 ]),
4028 children: Some(vec![
4029 fdecl::Child {
4030 name: Some("child".to_string()),
4031 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4032 startup: Some(fdecl::StartupMode::Lazy),
4033 on_terminate: None,
4034 ..Default::default()
4035 }
4036 ]),
4037 ..new_component_decl()
4038 }
4039 },
4040 result = Err(ErrorList::new(vec![
4041 Error::dependency_cycle("{{self -> child child -> self}}"),
4042 ])),
4043 },
4044 test_validate_use_from_child_storage_no_cycle => {
4045 input = {
4046 fdecl::Component {
4047 capabilities: Some(vec![
4048 fdecl::Capability::Storage(fdecl::Storage {
4049 name: Some("cdata".to_string()),
4050 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None } )),
4051 backing_dir: Some("minfs".to_string()),
4052 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4053 ..Default::default()
4054 }),
4055 fdecl::Capability::Storage(fdecl::Storage {
4056 name: Some("pdata".to_string()),
4057 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4058 backing_dir: Some("minfs".to_string()),
4059 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4060 ..Default::default()
4061 }),
4062 ]),
4063 uses: Some(vec![
4064 fdecl::Use::Protocol(fdecl::UseProtocol {
4065 dependency_type: Some(fdecl::DependencyType::Strong),
4066 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child1".to_string(), collection: None})),
4067 source_name: Some("a".to_string()),
4068 target_path: Some("/svc/a".to_string()),
4069 ..Default::default()
4070 }),
4071 ]),
4072 offers: Some(vec![
4073 fdecl::Offer::Storage(fdecl::OfferStorage {
4074 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4075 source_name: Some("cdata".to_string()),
4076 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4077 target_name: Some("cdata".to_string()),
4078 ..Default::default()
4079 }),
4080 fdecl::Offer::Storage(fdecl::OfferStorage {
4081 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4082 source_name: Some("pdata".to_string()),
4083 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4084 target_name: Some("pdata".to_string()),
4085 ..Default::default()
4086 }),
4087 ]),
4088 children: Some(vec![
4089 fdecl::Child {
4090 name: Some("child1".to_string()),
4091 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4092 startup: Some(fdecl::StartupMode::Lazy),
4093 on_terminate: None,
4094 ..Default::default()
4095 },
4096 fdecl::Child {
4097 name: Some("child2".to_string()),
4098 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4099 startup: Some(fdecl::StartupMode::Lazy),
4100 on_terminate: None,
4101 ..Default::default()
4102 }
4103 ]),
4104 ..new_component_decl()
4105 }
4106 },
4107 result = Ok(()),
4108 },
4109 test_validate_use_from_child_storage_cycle => {
4110 input = {
4111 fdecl::Component {
4112 capabilities: Some(vec![
4113 fdecl::Capability::Storage(fdecl::Storage {
4114 name: Some("data".to_string()),
4115 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4116 backing_dir: Some("minfs".to_string()),
4117 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4118 ..Default::default()
4119 }),
4120 ]),
4121 uses: Some(vec![
4122 fdecl::Use::Protocol(fdecl::UseProtocol {
4123 dependency_type: Some(fdecl::DependencyType::Strong),
4124 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4125 source_name: Some("a".to_string()),
4126 target_path: Some("/svc/a".to_string()),
4127 ..Default::default()
4128 }),
4129 ]),
4130 offers: Some(vec![
4131 fdecl::Offer::Storage(fdecl::OfferStorage {
4132 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4133 source_name: Some("data".to_string()),
4134 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4135 target_name: Some("data".to_string()),
4136 ..Default::default()
4137 }),
4138 ]),
4139 children: Some(vec![
4140 fdecl::Child {
4141 name: Some("child".to_string()),
4142 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4143 startup: Some(fdecl::StartupMode::Lazy),
4144 on_terminate: None,
4145 ..Default::default()
4146 },
4147 ]),
4148 ..new_component_decl()
4149 }
4150 },
4151 result = Err(ErrorList::new(vec![
4152 Error::dependency_cycle("{{self -> capability data -> child child -> self}}"),
4153 ])),
4154 },
4155 test_validate_storage_strong_cycle_between_children => {
4156 input = {
4157 fdecl::Component {
4158 capabilities: Some(vec![
4159 fdecl::Capability::Storage(fdecl::Storage {
4160 name: Some("data".to_string()),
4161 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None } )),
4162 backing_dir: Some("minfs".to_string()),
4163 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4164 ..Default::default()
4165 })
4166 ]),
4167 offers: Some(vec![
4168 fdecl::Offer::Storage(fdecl::OfferStorage {
4169 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4170 source_name: Some("data".to_string()),
4171 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4172 target_name: Some("data".to_string()),
4173 ..Default::default()
4174 }),
4175 fdecl::Offer::Service(fdecl::OfferService {
4176 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4177 source_name: Some("a".to_string()),
4178 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4179 target_name: Some("a".to_string()),
4180 ..Default::default()
4181 }),
4182 ]),
4183 children: Some(vec![
4184 fdecl::Child {
4185 name: Some("child1".to_string()),
4186 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4187 startup: Some(fdecl::StartupMode::Lazy),
4188 on_terminate: None,
4189 ..Default::default()
4190 },
4191 fdecl::Child {
4192 name: Some("child2".to_string()),
4193 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4194 startup: Some(fdecl::StartupMode::Lazy),
4195 on_terminate: None,
4196 ..Default::default()
4197 }
4198 ]),
4199 ..new_component_decl()
4200 }
4201 },
4202 result = Err(ErrorList::new(vec![
4203 Error::dependency_cycle("{{child child1 -> capability data -> child child2 -> child child1}}"),
4204 ])),
4205 },
4206 test_validate_strong_cycle_between_children_through_environment_debug => {
4207 input = {
4208 fdecl::Component {
4209 environments: Some(vec![
4210 fdecl::Environment {
4211 name: Some("env".to_string()),
4212 extends: Some(fdecl::EnvironmentExtends::Realm),
4213 debug_capabilities: Some(vec![
4214 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
4215 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4216 source_name: Some("fuchsia.foo.Bar".to_string()),
4217 target_name: Some("fuchsia.foo.Bar".to_string()),
4218 ..Default::default()
4219 }),
4220 ]),
4221 ..Default::default()
4222 },
4223 ]),
4224 offers: Some(vec![
4225 fdecl::Offer::Service(fdecl::OfferService {
4226 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4227 source_name: Some("a".to_string()),
4228 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4229 target_name: Some("a".to_string()),
4230 ..Default::default()
4231 }),
4232 ]),
4233 children: Some(vec![
4234 fdecl::Child {
4235 name: Some("child1".to_string()),
4236 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4237 startup: Some(fdecl::StartupMode::Lazy),
4238 on_terminate: None,
4239 ..Default::default()
4240 },
4241 fdecl::Child {
4242 name: Some("child2".to_string()),
4243 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4244 startup: Some(fdecl::StartupMode::Lazy),
4245 environment: Some("env".to_string()),
4246 on_terminate: None,
4247 ..Default::default()
4248 }
4249 ]),
4250 ..new_component_decl()
4251 }
4252 },
4253 result = Err(ErrorList::new(vec![
4254 Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}"),
4255 ])),
4256 },
4257 test_validate_strong_cycle_between_children_through_environment_runner => {
4258 input = {
4259 fdecl::Component {
4260 environments: Some(vec![
4261 fdecl::Environment {
4262 name: Some("env".to_string()),
4263 extends: Some(fdecl::EnvironmentExtends::Realm),
4264 runners: Some(vec![
4265 fdecl::RunnerRegistration {
4266 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4267 source_name: Some("coff".to_string()),
4268 target_name: Some("coff".to_string()),
4269 ..Default::default()
4270 }
4271 ]),
4272 ..Default::default()
4273 },
4274 ]),
4275 offers: Some(vec![
4276 fdecl::Offer::Service(fdecl::OfferService {
4277 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4278 source_name: Some("a".to_string()),
4279 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4280 target_name: Some("a".to_string()),
4281 ..Default::default()
4282 }),
4283 ]),
4284 children: Some(vec![
4285 fdecl::Child {
4286 name: Some("child1".to_string()),
4287 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4288 startup: Some(fdecl::StartupMode::Lazy),
4289 on_terminate: None,
4290 ..Default::default()
4291 },
4292 fdecl::Child {
4293 name: Some("child2".to_string()),
4294 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4295 startup: Some(fdecl::StartupMode::Lazy),
4296 environment: Some("env".to_string()),
4297 on_terminate: None,
4298 ..Default::default()
4299 }
4300 ]),
4301 ..new_component_decl()
4302 }
4303 },
4304 result = Err(ErrorList::new(vec![
4305 Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}"),
4306 ])),
4307 },
4308 test_validate_strong_cycle_between_children_through_environment_resolver => {
4309 input = {
4310 fdecl::Component {
4311 environments: Some(vec![
4312 fdecl::Environment {
4313 name: Some("env".to_string()),
4314 extends: Some(fdecl::EnvironmentExtends::Realm),
4315 resolvers: Some(vec![
4316 fdecl::ResolverRegistration {
4317 resolver: Some("gopher".to_string()),
4318 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4319 scheme: Some("gopher".to_string()),
4320 ..Default::default()
4321 }
4322 ]),
4323 ..Default::default()
4324 },
4325 ]),
4326 offers: Some(vec![
4327 fdecl::Offer::Service(fdecl::OfferService {
4328 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4329 source_name: Some("a".to_string()),
4330 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4331 target_name: Some("a".to_string()),
4332 ..Default::default()
4333 }),
4334 ]),
4335 children: Some(vec![
4336 fdecl::Child {
4337 name: Some("child1".to_string()),
4338 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4339 startup: Some(fdecl::StartupMode::Lazy),
4340 on_terminate: None,
4341 ..Default::default()
4342 },
4343 fdecl::Child {
4344 name: Some("child2".to_string()),
4345 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4346 startup: Some(fdecl::StartupMode::Lazy),
4347 environment: Some("env".to_string()),
4348 on_terminate: None,
4349 ..Default::default()
4350 }
4351 ]),
4352 ..new_component_decl()
4353 }
4354 },
4355 result = Err(ErrorList::new(vec![
4356 Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}"),
4357 ])),
4358 },
4359 test_validate_strong_cycle_between_self_and_two_children => {
4360 input = {
4361 fdecl::Component {
4362 capabilities: Some(vec![
4363 fdecl::Capability::Protocol(fdecl::Protocol {
4364 name: Some("fuchsia.foo.Bar".to_string()),
4365 source_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4366 ..Default::default()
4367 })
4368 ]),
4369 offers: Some(vec![
4370 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4371 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4372 source_name: Some("fuchsia.foo.Bar".to_string()),
4373 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4374 target_name: Some("fuchsia.foo.Bar".to_string()),
4375 dependency_type: Some(fdecl::DependencyType::Strong),
4376 ..Default::default()
4377 }),
4378 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4379 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4380 source_name: Some("fuchsia.bar.Baz".to_string()),
4381 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4382 target_name: Some("fuchsia.bar.Baz".to_string()),
4383 dependency_type: Some(fdecl::DependencyType::Strong),
4384 ..Default::default()
4385 }),
4386 ]),
4387 uses: Some(vec![
4388 fdecl::Use::Protocol(fdecl::UseProtocol {
4389 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child2".to_string(), collection: None})),
4390 source_name: Some("fuchsia.baz.Foo".to_string()),
4391 target_path: Some("/svc/fuchsia.baz.Foo".to_string()),
4392 dependency_type: Some(fdecl::DependencyType::Strong),
4393 ..Default::default()
4394 }),
4395 ]),
4396 children: Some(vec![
4397 fdecl::Child {
4398 name: Some("child1".to_string()),
4399 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4400 startup: Some(fdecl::StartupMode::Lazy),
4401 on_terminate: None,
4402 ..Default::default()
4403 },
4404 fdecl::Child {
4405 name: Some("child2".to_string()),
4406 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4407 startup: Some(fdecl::StartupMode::Lazy),
4408 on_terminate: None,
4409 ..Default::default()
4410 }
4411 ]),
4412 ..new_component_decl()
4413 }
4414 },
4415 result = Err(ErrorList::new(vec![
4416 Error::dependency_cycle("{{self -> child child1 -> child child2 -> self}}"),
4417 ])),
4418 },
4419 test_validate_strong_cycle_with_self_storage => {
4420 input = {
4421 fdecl::Component {
4422 capabilities: Some(vec![
4423 fdecl::Capability::Storage(fdecl::Storage {
4424 name: Some("data".to_string()),
4425 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4426 backing_dir: Some("minfs".to_string()),
4427 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4428 ..Default::default()
4429 }),
4430 fdecl::Capability::Directory(fdecl::Directory {
4431 name: Some("minfs".to_string()),
4432 source_path: Some("/minfs".to_string()),
4433 rights: Some(fio::RW_STAR_DIR),
4434 ..Default::default()
4435 }),
4436 ]),
4437 offers: Some(vec![
4438 fdecl::Offer::Storage(fdecl::OfferStorage {
4439 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4440 source_name: Some("data".to_string()),
4441 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4442 target_name: Some("data".to_string()),
4443 ..Default::default()
4444 }),
4445 ]),
4446 uses: Some(vec![
4447 fdecl::Use::Protocol(fdecl::UseProtocol {
4448 dependency_type: Some(fdecl::DependencyType::Strong),
4449 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4450 source_name: Some("fuchsia.foo.Bar".to_string()),
4451 target_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4452 ..Default::default()
4453 }),
4454 ]),
4455 children: Some(vec![
4456 fdecl::Child {
4457 name: Some("child".to_string()),
4458 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4459 startup: Some(fdecl::StartupMode::Lazy),
4460 ..Default::default()
4461 },
4462 ]),
4463 ..new_component_decl()
4464 }
4465 },
4466 result = Err(ErrorList::new(vec![
4467 Error::dependency_cycle("{{self -> capability data -> child child -> self}}"),
4468 ])),
4469 },
4470 test_validate_strong_cycle_with_self_storage_admin_protocol => {
4471 input = {
4472 fdecl::Component {
4473 capabilities: Some(vec![
4474 fdecl::Capability::Storage(fdecl::Storage {
4475 name: Some("data".to_string()),
4476 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4477 backing_dir: Some("minfs".to_string()),
4478 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4479 ..Default::default()
4480 }),
4481 fdecl::Capability::Directory(fdecl::Directory {
4482 name: Some("minfs".to_string()),
4483 source_path: Some("/minfs".to_string()),
4484 rights: Some(fio::RW_STAR_DIR),
4485 ..Default::default()
4486 }),
4487 ]),
4488 offers: Some(vec![
4489 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4490 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef { name: "data".to_string() })),
4491 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4492 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4493 target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4494 dependency_type: Some(fdecl::DependencyType::Strong),
4495 ..Default::default()
4496 }),
4497 ]),
4498 uses: Some(vec![
4499 fdecl::Use::Protocol(fdecl::UseProtocol {
4500 dependency_type: Some(fdecl::DependencyType::Strong),
4501 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4502 source_name: Some("fuchsia.foo.Bar".to_string()),
4503 target_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4504 ..Default::default()
4505 }),
4506 ]),
4507 children: Some(vec![
4508 fdecl::Child {
4509 name: Some("child".to_string()),
4510 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4511 startup: Some(fdecl::StartupMode::Lazy),
4512 ..Default::default()
4513 },
4514 ]),
4515 ..new_component_decl()
4516 }
4517 },
4518 result = Err(ErrorList::new(vec![
4519 Error::dependency_cycle("{{self -> capability data -> child child -> self}}"),
4520 ])),
4521 },
4522 test_validate_strong_cycle_with_dictionary => {
4523 input = fdecl::Component {
4524 offers: Some(vec![
4525 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
4526 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4527 source_name: Some("dict".into()),
4528 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4529 name: "a".into(),
4530 collection: None,
4531 })),
4532 target_name: Some("dict".into()),
4533 dependency_type: Some(fdecl::DependencyType::Strong),
4534 ..Default::default()
4535 }),
4536 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4537 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4538 name: "b".into(),
4539 collection: None,
4540 })),
4541 source_name: Some("1".into()),
4542 target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4543 name: "dict".into(),
4544 })),
4545 target_name: Some("1".into()),
4546 dependency_type: Some(fdecl::DependencyType::Strong),
4547 ..Default::default()
4548 }),
4549 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4550 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4551 name: "a".into(),
4552 collection: None,
4553 })),
4554 source_name: Some("2".into()),
4555 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4556 name: "b".into(),
4557 collection: None,
4558 })),
4559 target_name: Some("2".into()),
4560 dependency_type: Some(fdecl::DependencyType::Strong),
4561 ..Default::default()
4562 }),
4563 ]),
4564 children: Some(vec![
4565 fdecl::Child {
4566 name: Some("a".into()),
4567 url: Some("fuchsia-pkg://child".into()),
4568 startup: Some(fdecl::StartupMode::Lazy),
4569 ..Default::default()
4570 },
4571 fdecl::Child {
4572 name: Some("b".into()),
4573 url: Some("fuchsia-pkg://child".into()),
4574 startup: Some(fdecl::StartupMode::Lazy),
4575 ..Default::default()
4576 },
4577 ]),
4578 capabilities: Some(vec![
4579 fdecl::Capability::Dictionary(fdecl::Dictionary {
4580 name: Some("dict".into()),
4581 ..Default::default()
4582 }),
4583 ]),
4584 ..Default::default()
4585 },
4586 result = Err(ErrorList::new(vec![
4587 Error::dependency_cycle("{{child a -> child b -> capability dict -> child a}}"),
4588 ])),
4589 },
4590 test_validate_strong_cycle_with_dictionary_indirect => {
4591 input = fdecl::Component {
4592 offers: Some(vec![
4593 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4594 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4595 source_name: Some("3".into()),
4596 source_dictionary: Some("dict".into()),
4597 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4598 name: "a".into(),
4599 collection: None,
4600 })),
4601 target_name: Some("3".into()),
4602 dependency_type: Some(fdecl::DependencyType::Strong),
4603 ..Default::default()
4604 }),
4605 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4606 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4607 name: "b".into(),
4608 collection: None,
4609 })),
4610 source_name: Some("1".into()),
4611 target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4612 name: "dict".into(),
4613 })),
4614 target_name: Some("1".into()),
4615 dependency_type: Some(fdecl::DependencyType::Strong),
4616 ..Default::default()
4617 }),
4618 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4619 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4620 name: "a".into(),
4621 collection: None,
4622 })),
4623 source_name: Some("2".into()),
4624 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4625 name: "b".into(),
4626 collection: None,
4627 })),
4628 target_name: Some("2".into()),
4629 dependency_type: Some(fdecl::DependencyType::Strong),
4630 ..Default::default()
4631 }),
4632 ]),
4633 children: Some(vec![
4634 fdecl::Child {
4635 name: Some("a".into()),
4636 url: Some("fuchsia-pkg://child".into()),
4637 startup: Some(fdecl::StartupMode::Lazy),
4638 ..Default::default()
4639 },
4640 fdecl::Child {
4641 name: Some("b".into()),
4642 url: Some("fuchsia-pkg://child".into()),
4643 startup: Some(fdecl::StartupMode::Lazy),
4644 ..Default::default()
4645 },
4646 ]),
4647 capabilities: Some(vec![
4648 fdecl::Capability::Dictionary(fdecl::Dictionary {
4649 name: Some("dict".into()),
4650 ..Default::default()
4651 }),
4652 ]),
4653 ..Default::default()
4654 },
4655 result = Err(ErrorList::new(vec![
4656 Error::dependency_cycle("{{child a -> child b -> capability dict -> child a}}"),
4657 ])),
4658 },
4659 test_validate_use_dependency_cycle_with_dictionary => {
4660 input = fdecl::Component {
4661 offers: Some(vec![
4662 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
4663 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4664 source_name: Some("dict".into()),
4665 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4666 name: "a".into(),
4667 collection: None,
4668 })),
4669 target_name: Some("dict".into()),
4670 dependency_type: Some(fdecl::DependencyType::Strong),
4671 ..Default::default()
4672 }),
4673 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4674 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4675 source_name: Some("1".into()),
4676 target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4677 name: "dict".into(),
4678 })),
4679 target_name: Some("1".into()),
4680 dependency_type: Some(fdecl::DependencyType::Strong),
4681 ..Default::default()
4682 }),
4683 ]),
4684 children: Some(vec![
4685 fdecl::Child {
4686 name: Some("a".into()),
4687 url: Some("fuchsia-pkg://child".into()),
4688 startup: Some(fdecl::StartupMode::Lazy),
4689 ..Default::default()
4690 },
4691 ]),
4692 uses: Some(vec![
4693 fdecl::Use::Protocol(fdecl::UseProtocol {
4694 dependency_type: Some(fdecl::DependencyType::Strong),
4695 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "a".to_string(), collection: None})),
4696 source_name: Some("2".to_string()),
4697 target_path: Some("/svc/foo".into()),
4698 ..Default::default()
4699 }),
4700 ]),
4701 capabilities: Some(vec![
4702 fdecl::Capability::Dictionary(fdecl::Dictionary {
4703 name: Some("dict".into()),
4704 ..Default::default()
4705 }),
4706 fdecl::Capability::Protocol(fdecl::Protocol {
4707 name: Some("1".to_string()),
4708 source_path: Some("/path".to_string()),
4709 ..Default::default()
4710 }),
4711 ]),
4712 ..Default::default()
4713 },
4714 result = Err(ErrorList::new(vec![
4715 Error::dependency_cycle("{{self -> capability dict -> child a -> self}}"),
4716 ])),
4717 },
4718 test_validate_use_from_child_offer_to_child_weak_cycle => {
4719 input = {
4720 fdecl::Component {
4721 capabilities: Some(vec![
4722 fdecl::Capability::Service(fdecl::Service {
4723 name: Some("a".to_string()),
4724 source_path: Some("/a".to_string()),
4725 ..Default::default()
4726 })]),
4727 uses: Some(vec![
4728 fdecl::Use::Protocol(fdecl::UseProtocol {
4729 dependency_type: Some(fdecl::DependencyType::Weak),
4730 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4731 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4732 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
4733 ..Default::default()
4734 }),
4735 fdecl::Use::Service(fdecl::UseService {
4736 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4737 source_name: Some("service_name".to_string()),
4738 target_path: Some("/svc/service_name".to_string()),
4739 dependency_type: Some(fdecl::DependencyType::Weak),
4740 ..Default::default()
4741 }),
4742 fdecl::Use::Directory(fdecl::UseDirectory {
4743 dependency_type: Some(fdecl::DependencyType::Weak),
4744 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4745 source_name: Some("DirectoryName".to_string()),
4746 target_path: Some("/data/DirectoryName".to_string()),
4747 rights: Some(fio::Operations::CONNECT),
4748 subdir: None,
4749 ..Default::default()
4750 }),
4751 ]),
4752 offers: Some(vec![
4753 fdecl::Offer::Service(fdecl::OfferService {
4754 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4755 source_name: Some("a".to_string()),
4756 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4757 target_name: Some("a".to_string()),
4758 ..Default::default()
4759 })
4760 ]),
4761 children: Some(vec![
4762 fdecl::Child {
4763 name: Some("child".to_string()),
4764 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4765 startup: Some(fdecl::StartupMode::Lazy),
4766 on_terminate: None,
4767 ..Default::default()
4768 }
4769 ]),
4770 ..new_component_decl()
4771 }
4772 },
4773 result = Ok(()),
4774 },
4775 test_validate_expose_from_self_to_framework_and_parent => {
4776 input = {
4777 fdecl::Component {
4778 capabilities: Some(vec![
4779 fdecl::Capability::Protocol(fdecl::Protocol {
4780 name: Some("a".to_string()),
4781 source_path: Some("/a".to_string()),
4782 ..Default::default()
4783 }),
4784 ]),
4785 exposes: Some(vec![
4786 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
4787 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4788 source_name: Some("a".to_string()),
4789 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4790 target_name: Some("a".to_string()),
4791 ..Default::default()
4792 }),
4793 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
4794 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4795 source_name: Some("a".to_string()),
4796 target: Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})),
4797 target_name: Some("a".to_string()),
4798 ..Default::default()
4799 }),
4800 ]),
4801 ..new_component_decl()
4802 }
4803 },
4804 result = Ok(()),
4805 },
4806 test_validate_use_from_not_child_weak => {
4807 input = {
4808 fdecl::Component {
4809 uses: Some(vec![
4810 fdecl::Use::Protocol(fdecl::UseProtocol {
4811 dependency_type: Some(fdecl::DependencyType::Weak),
4812 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4813 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4814 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
4815 ..Default::default()
4816 }),
4817 ]),
4818 ..new_component_decl()
4819 }
4820 },
4821 result = Err(ErrorList::new(vec![
4822 Error::invalid_field(DeclType::UseProtocol, "dependency_type"),
4823 ])),
4824 },
4825 test_validate_event_stream_offer_valid_decls => {
4826 input = {
4827 let mut decl = new_component_decl();
4828 decl.offers = Some(vec![
4829 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4830 source_name: Some("stopped".to_string()),
4831 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4832 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4833 target_name: Some("stopped".to_string()),
4834 ..Default::default()
4835 }),
4836 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4837 source_name: Some("started".to_string()),
4838 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4839 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4840 target_name: Some("started".to_string()),
4841 ..Default::default()
4842 }),
4843 ]);
4844 decl.children = Some(vec![fdecl::Child{
4845 name: Some("test".to_string()),
4846 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4847 startup: Some(fdecl::StartupMode::Lazy),
4848 on_terminate: None,
4849 environment: None,
4850 ..Default::default()
4851 },
4852 fdecl::Child{
4853 name: Some("test2".to_string()),
4854 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4855 startup: Some(fdecl::StartupMode::Lazy),
4856 on_terminate: None,
4857 environment: None,
4858 ..Default::default()
4859 }
4860 ]);
4861 decl
4862 },
4863 result = Ok(()),
4864 },
4865 test_validate_event_stream_offer_to_framework_invalid => {
4866 input = {
4867 let mut decl = new_component_decl();
4868 decl.offers = Some(vec![
4869 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4870 source_name: Some("stopped".to_string()),
4871 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4872 target: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
4873 target_name: Some("stopped".to_string()),
4874 ..Default::default()
4875 }),
4876 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4877 source_name: Some("started".to_string()),
4878 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4879 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4880 target_name: Some("started".to_string()),
4881 ..Default::default()
4882 }),
4883 ]);
4884 decl.children = Some(vec![fdecl::Child{
4885 name: Some("test".to_string()),
4886 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4887 startup: Some(fdecl::StartupMode::Lazy),
4888 on_terminate: None,
4889 environment: None,
4890 ..Default::default()
4891 },
4892 fdecl::Child{
4893 name: Some("test2".to_string()),
4894 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4895 startup: Some(fdecl::StartupMode::Lazy),
4896 on_terminate: None,
4897 environment: None,
4898 ..Default::default()
4899 }
4900 ]);
4901 decl
4902 },
4903 result = Err(ErrorList::new(vec![
4904 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "target".to_string() }),
4905 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "target".to_string() }),
4906 ])),
4907 },
4908 test_validate_event_stream_offer_to_scope_zero_length_invalid => {
4909 input = {
4910 let mut decl = new_component_decl();
4911 decl.offers = Some(vec![
4912 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4913 source_name: Some("started".to_string()),
4914 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4915 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4916 scope: Some(vec![]),
4917 target_name: Some("started".to_string()),
4918 ..Default::default()
4919 }),
4920 ]);
4921 decl.children = Some(vec![fdecl::Child{
4922 name: Some("test".to_string()),
4923 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4924 startup: Some(fdecl::StartupMode::Lazy),
4925 on_terminate: None,
4926 environment: None,
4927 ..Default::default()
4928 },
4929 fdecl::Child{
4930 name: Some("test2".to_string()),
4931 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4932 startup: Some(fdecl::StartupMode::Lazy),
4933 on_terminate: None,
4934 environment: None,
4935 ..Default::default()
4936 }
4937 ]);
4938 decl
4939 },
4940 result = Err(ErrorList::new(vec![
4941 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "scope".to_string() }),
4942 ])),
4943 },
4944 test_validate_event_stream_offer_to_scope_framework_invalid => {
4945 input = {
4946 let mut decl = new_component_decl();
4947 decl.offers = Some(vec![
4948 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4949 source_name: Some("started".to_string()),
4950 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4951 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4952 scope: Some(vec![fdecl::Ref::Framework(fdecl::FrameworkRef{})]),
4953 target_name: Some("started".to_string()),
4954 ..Default::default()
4955 }),
4956 ]);
4957 decl.children = Some(vec![fdecl::Child{
4958 name: Some("test".to_string()),
4959 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4960 startup: Some(fdecl::StartupMode::Lazy),
4961 on_terminate: None,
4962 environment: None,
4963 ..Default::default()
4964 },
4965 fdecl::Child{
4966 name: Some("test2".to_string()),
4967 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4968 startup: Some(fdecl::StartupMode::Lazy),
4969 on_terminate: None,
4970 environment: None,
4971 ..Default::default()
4972 }
4973 ]);
4974 decl
4975 },
4976 result = Err(ErrorList::new(vec![
4977 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "scope".to_string() }),
4978 ])),
4979 },
4980 test_validate_event_stream_offer_to_scope_valid => {
4981 input = {
4982 let mut decl = new_component_decl();
4983 decl.offers = Some(vec![
4984 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4985 source_name: Some("started".to_string()),
4986 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4987 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4988 scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
4989 target_name: Some("started".to_string()),
4990 ..Default::default()
4991 }),
4992 ]);
4993 decl.children = Some(vec![fdecl::Child{
4994 name: Some("test".to_string()),
4995 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4996 startup: Some(fdecl::StartupMode::Lazy),
4997 on_terminate: None,
4998 environment: None,
4999 ..Default::default()
5000 },
5001 fdecl::Child{
5002 name: Some("test2".to_string()),
5003 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
5004 startup: Some(fdecl::StartupMode::Lazy),
5005 on_terminate: None,
5006 environment: None,
5007 ..Default::default()
5008 }
5009 ]);
5010 decl
5011 },
5012 result = Ok(()),
5013 },
5014 test_validate_event_stream_offer_to_scope_with_capability_requested => {
5015 input = {
5016 let mut decl = new_component_decl();
5017 decl.offers = Some(vec![
5018 fdecl::Offer::EventStream(fdecl::OfferEventStream {
5019 source_name: Some("capability_requested".to_string()),
5020 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5021 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5022 scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
5023 target_name: Some("started".to_string()),
5024 ..Default::default()
5025 }),
5026 ]);
5027 decl.children = Some(vec![fdecl::Child{
5028 name: Some("test".to_string()),
5029 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5030 startup: Some(fdecl::StartupMode::Lazy),
5031 on_terminate: None,
5032 environment: None,
5033 ..Default::default()
5034 },
5035 fdecl::Child{
5036 name: Some("test2".to_string()),
5037 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
5038 startup: Some(fdecl::StartupMode::Lazy),
5039 on_terminate: None,
5040 environment: None,
5041 ..Default::default()
5042 }
5043 ]);
5044 decl
5045 },
5046 result = Ok(()),
5047 },
5048 test_validate_event_stream_offer_with_no_source_name_invalid => {
5049 input = {
5050 let mut decl = new_component_decl();
5051 decl.offers = Some(vec![
5052 fdecl::Offer::EventStream(fdecl::OfferEventStream {
5053 source_name: None,
5054 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5055 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5056 scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
5057 target_name: Some("started".to_string()),
5058 ..Default::default()
5059 }),
5060 ]);
5061 decl.children = Some(vec![fdecl::Child{
5062 name: Some("test".to_string()),
5063 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5064 startup: Some(fdecl::StartupMode::Lazy),
5065 on_terminate: None,
5066 environment: None,
5067 ..Default::default()
5068 },
5069 fdecl::Child{
5070 name: Some("test2".to_string()),
5071 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
5072 startup: Some(fdecl::StartupMode::Lazy),
5073 on_terminate: None,
5074 environment: None,
5075 ..Default::default()
5076 }
5077 ]);
5078 decl
5079 },
5080 result = Err(ErrorList::new(vec![
5081 Error::MissingField(DeclField { decl: DeclType::OfferEventStream, field: "source_name".to_string() }),
5082 ])),
5083 },
5084 test_validate_event_stream_offer_invalid_source => {
5085 input = {
5086 let mut decl = new_component_decl();
5087 decl.offers = Some(vec![
5088 fdecl::Offer::EventStream(fdecl::OfferEventStream {
5089 source_name: Some("stopped".to_string()),
5090 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
5091 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5092 target_name: Some("stopped".to_string()),
5093 ..Default::default()
5094 }),
5095 fdecl::Offer::EventStream(fdecl::OfferEventStream {
5096 source_name: Some("started".to_string()),
5097 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5098 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5099 target_name: Some("started".to_string()),
5100 ..Default::default()
5101 }),
5102 fdecl::Offer::EventStream(fdecl::OfferEventStream {
5103 source_name: Some("capability_requested".to_string()),
5104 source: Some(fdecl::Ref::Debug(fdecl::DebugRef{})),
5105 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5106 target_name: Some("capability_requested".to_string()),
5107 ..Default::default()
5108 }),
5109 ]);
5110 decl.children = Some(vec![fdecl::Child{
5111 name: Some("test".to_string()),
5112 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5113 startup: Some(fdecl::StartupMode::Lazy),
5114 on_terminate: None,
5115 environment: None,
5116 ..Default::default()
5117 },
5118 fdecl::Child{
5119 name: Some("test2".to_string()),
5120 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
5121 startup: Some(fdecl::StartupMode::Lazy),
5122 on_terminate: None,
5123 environment: None,
5124 ..Default::default()
5125 }
5126 ]);
5127 decl
5128 },
5129 result = Err(ErrorList::new(vec![
5130 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "source".to_string() }),
5131 ])),
5132 },
5133
5134 test_validate_event_stream_offer_missing_source => {
5135 input = {
5136 let mut decl = new_component_decl();
5137 decl.offers = Some(vec![
5138 fdecl::Offer::EventStream(fdecl::OfferEventStream {
5139 source_name: Some("stopped".to_string()),
5140 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
5141 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5142 target_name: Some("stopped".to_string()),
5143 ..Default::default()
5144 }),
5145 fdecl::Offer::EventStream(fdecl::OfferEventStream {
5146 source_name: Some("started".to_string()),
5147 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5148 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5149 target_name: Some("started".to_string()),
5150 ..Default::default()
5151 }),
5152 fdecl::Offer::EventStream(fdecl::OfferEventStream {
5153 source_name: Some("capability_requested".to_string()),
5154 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5155 target_name: Some("capability_requested".to_string()),
5156 ..Default::default()
5157 }),
5158 ]);
5159 decl.children = Some(vec![fdecl::Child{
5160 name: Some("test".to_string()),
5161 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5162 startup: Some(fdecl::StartupMode::Lazy),
5163 on_terminate: None,
5164 environment: None,
5165 ..Default::default()
5166 },
5167 fdecl::Child{
5168 name: Some("test2".to_string()),
5169 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
5170 startup: Some(fdecl::StartupMode::Lazy),
5171 on_terminate: None,
5172 environment: None,
5173 ..Default::default()
5174 }
5175 ]);
5176 decl
5177 },
5178 result = Err(ErrorList::new(vec![
5179 Error::MissingField(DeclField { decl: DeclType::OfferEventStream, field: "source".to_string() }),
5180 ])),
5181 },
5182 test_validate_event_stream_must_have_target_path => {
5183 input = {
5184 let mut decl = new_component_decl();
5185 decl.uses = Some(vec![
5186 fdecl::Use::EventStream(fdecl::UseEventStream {
5187 source_name: Some("bar".to_string()),
5188 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5189 ..Default::default()
5190 }),
5191 ]);
5192 decl
5193 },
5194 result = Err(ErrorList::new(vec![
5195 Error::MissingField(DeclField { decl: DeclType::UseEventStream, field: "target_path".to_string() })
5196 ])),
5197 },
5198 test_validate_event_stream_must_have_source_names => {
5199 input = {
5200 let mut decl = new_component_decl();
5201 decl.uses = Some(vec![
5202 fdecl::Use::EventStream(fdecl::UseEventStream {
5203 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5204 target_path: Some("/svc/something".to_string()),
5205 ..Default::default()
5206 }),
5207 ]);
5208 decl
5209 },
5210 result = Err(ErrorList::new(vec![
5211 Error::MissingField(DeclField { decl: DeclType::UseEventStream, field: "source_name".to_string() })
5212 ])),
5213 },
5214 test_validate_event_stream_scope_must_be_child_or_collection => {
5215 input = {
5216 let mut decl = new_component_decl();
5217 decl.uses = Some(vec![
5218 fdecl::Use::EventStream(fdecl::UseEventStream {
5219 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5220 target_path: Some("/svc/something".to_string()),
5221 source_name: Some("some_source".to_string()),
5222 scope: Some(vec![fdecl::Ref::Framework(fdecl::FrameworkRef{})]),
5223 ..Default::default()
5224 }),
5225 ]);
5226 decl
5227 },
5228 result = Err(ErrorList::new(vec![
5229 Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "scope".to_string() })
5230 ])),
5231 },
5232 test_validate_event_stream_source_must_be_parent_or_child => {
5233 input = {
5234 let mut decl = new_component_decl();
5235 decl.uses = Some(vec![
5236 fdecl::Use::EventStream(fdecl::UseEventStream {
5237 source: Some(fdecl::Ref::Debug(fdecl::DebugRef{})),
5238 target_path: Some("/svc/something".to_string()),
5239 source_name: Some("some_source".to_string()),
5240 scope: Some(vec![]),
5241 ..Default::default()
5242 }),
5243 fdecl::Use::EventStream(fdecl::UseEventStream {
5244 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
5245 target_path: Some("/svc/something_else".to_string()),
5246 source_name: Some("some_source".to_string()),
5247 scope: Some(vec![]),
5248 ..Default::default()
5249 }),
5250 fdecl::Use::EventStream(fdecl::UseEventStream {
5251 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5252 target_path: Some("/svc/yet_something_else".to_string()),
5253 source_name: Some("some_source".to_string()),
5254 scope: Some(vec![]),
5255 ..Default::default()
5256 }),
5257 ]);
5258 decl
5259 },
5260 result = Err(ErrorList::new(vec![
5261 Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() }),
5262 Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() }),
5263 Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() })
5264 ])),
5265 },
5266 test_validate_no_runner => {
5267 input = {
5268 let mut decl = new_component_decl();
5269 decl.program = Some(fdecl::Program {
5270 runner: None,
5271 info: Some(fdata::Dictionary {
5272 entries: None,
5273 ..Default::default()
5274 }),
5275 ..Default::default()
5276 });
5277 decl
5278 },
5279 result = Err(ErrorList::new(vec![
5280 Error::MissingRunner,
5281 ])),
5282 },
5283 test_validate_uses_runner => {
5284 input = {
5285 let mut decl = new_component_decl();
5286 decl.program = Some(fdecl::Program {
5287 runner: None,
5288 info: Some(fdata::Dictionary {
5289 entries: None,
5290 ..Default::default()
5291 }),
5292 ..Default::default()
5293 });
5294 decl.uses = Some(vec![
5295 fdecl::Use::Runner(fdecl::UseRunner {
5296 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5297 source_name: Some("runner".to_string()),
5298 ..Default::default()
5299 }),
5300 ]);
5301 decl
5302 },
5303 result = Ok(()),
5304 },
5305 test_validate_program_and_uses_runner_match => {
5306 input = {
5307 let mut decl = new_component_decl();
5308 decl.program = Some(fdecl::Program {
5309 runner: Some("runner".to_string()),
5310 info: Some(fdata::Dictionary {
5311 entries: None,
5312 ..Default::default()
5313 }),
5314 ..Default::default()
5315 });
5316 decl.uses = Some(vec![
5317 fdecl::Use::Runner(fdecl::UseRunner {
5318 source: Some(fdecl::Ref::Environment(fdecl::EnvironmentRef {})),
5319 source_name: Some("runner".to_string()),
5320 ..Default::default()
5321 }),
5322 ]);
5323 decl
5324 },
5325 result = Ok(()),
5326 },
5327 test_validate_runner_names_conflict => {
5328 input = {
5329 let mut decl = new_component_decl();
5330 decl.program = Some(fdecl::Program {
5331 runner: Some("runner".to_string()),
5332 info: Some(fdata::Dictionary {
5333 entries: None,
5334 ..Default::default()
5335 }),
5336 ..Default::default()
5337 });
5338 decl.uses = Some(vec![
5339 fdecl::Use::Runner(fdecl::UseRunner {
5340 source: Some(fdecl::Ref::Environment(fdecl::EnvironmentRef {})),
5341 source_name: Some("other.runner".to_string()),
5342 ..Default::default()
5343 }),
5344 ]);
5345 decl
5346 },
5347 result = Err(ErrorList::new(vec![
5348 Error::ConflictingRunners,
5349 ])),
5350 },
5351 test_validate_uses_runner_not_environement => {
5352 input = {
5353 let mut decl = new_component_decl();
5354 decl.program = Some(fdecl::Program {
5355 runner: Some("runner".to_string()),
5356 info: Some(fdata::Dictionary {
5357 entries: None,
5358 ..Default::default()
5359 }),
5360 ..Default::default()
5361 });
5362 decl.uses = Some(vec![
5363 fdecl::Use::Runner(fdecl::UseRunner {
5364 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5365 source_name: Some("runner".to_string()),
5366 ..Default::default()
5367 }),
5368 ]);
5369 decl
5370 },
5371 result = Err(ErrorList::new(vec![
5372 Error::ConflictingRunners,
5373 ])),
5374 },
5375 test_validate_uses_long_identifiers => {
5376 input = {
5377 let mut decl = new_component_decl();
5378 decl.program = Some(fdecl::Program {
5379 runner: Some("elf".to_string()),
5380 info: Some(fdata::Dictionary {
5381 entries: None,
5382 ..Default::default()
5383 }),
5384 ..Default::default()
5385 });
5386 decl.uses = Some(vec![
5387 fdecl::Use::Service(fdecl::UseService {
5388 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5389 source_name: Some(format!("{}", "a".repeat(256))),
5390 target_path: Some("/a".repeat(2048)),
5391 dependency_type: Some(fdecl::DependencyType::Strong),
5392 ..Default::default()
5393 }),
5394 fdecl::Use::Protocol(fdecl::UseProtocol {
5395 dependency_type: Some(fdecl::DependencyType::Strong),
5396 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5397 source_name: Some(format!("{}", "a".repeat(256))),
5398 target_path: Some("/b".repeat(2048)),
5399 ..Default::default()
5400 }),
5401 fdecl::Use::Directory(fdecl::UseDirectory {
5402 dependency_type: Some(fdecl::DependencyType::Strong),
5403 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5404 source_name: Some(format!("{}", "a".repeat(256))),
5405 target_path: Some("/c".repeat(2048)),
5406 rights: Some(fio::Operations::CONNECT),
5407 subdir: None,
5408 ..Default::default()
5409 }),
5410 fdecl::Use::Storage(fdecl::UseStorage {
5411 source_name: Some("cache".to_string()),
5412 target_path: Some("/d".repeat(2048)),
5413 ..Default::default()
5414 }),
5415 ]);
5416 decl
5417 },
5418 result = Err(ErrorList::new(vec![
5419 Error::field_too_long(DeclType::UseService, "source_name"),
5420 Error::field_too_long(DeclType::UseService, "target_path"),
5421 Error::field_too_long(DeclType::UseProtocol, "source_name"),
5422 Error::field_too_long(DeclType::UseProtocol, "target_path"),
5423 Error::field_too_long(DeclType::UseDirectory, "source_name"),
5424 Error::field_too_long(DeclType::UseDirectory, "target_path"),
5425 Error::field_too_long(DeclType::UseStorage, "target_path"),
5426 ])),
5427 },
5428 test_validate_conflicting_paths => {
5429 input = {
5430 let mut decl = new_component_decl();
5431 decl.uses = Some(vec![
5432 fdecl::Use::Service(fdecl::UseService {
5433 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5434 source_name: Some("foo".to_string()),
5435 target_path: Some("/bar".to_string()),
5436 dependency_type: Some(fdecl::DependencyType::Strong),
5437 ..Default::default()
5438 }),
5439 fdecl::Use::Protocol(fdecl::UseProtocol {
5440 dependency_type: Some(fdecl::DependencyType::Strong),
5441 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5442 source_name: Some("space".to_string()),
5443 target_path: Some("/bar".to_string()),
5444 ..Default::default()
5445 }),
5446 fdecl::Use::Directory(fdecl::UseDirectory {
5447 dependency_type: Some(fdecl::DependencyType::Strong),
5448 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5449 source_name: Some("crow".to_string()),
5450 target_path: Some("/bar".to_string()),
5451 rights: Some(fio::Operations::CONNECT),
5452 subdir: None,
5453 ..Default::default()
5454 }),
5455 ]);
5456 decl
5457 },
5458 result = Err(ErrorList::new(vec![
5459 Error::duplicate_field(DeclType::UseDirectory, "target_path", "/bar"),
5460 Error::duplicate_field(DeclType::UseProtocol, "target_path", "/bar"),
5461 ])),
5462 },
5463 test_validate_exposes_empty => {
5465 input = {
5466 let mut decl = new_component_decl();
5467 decl.exposes = Some(vec![
5468 fdecl::Expose::Service(fdecl::ExposeService {
5469 source: None,
5470 source_name: None,
5471 target_name: None,
5472 target: None,
5473 ..Default::default()
5474 }),
5475 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5476 source: None,
5477 source_name: None,
5478 target_name: None,
5479 target: None,
5480 ..Default::default()
5481 }),
5482 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5483 source: None,
5484 source_name: None,
5485 target_name: None,
5486 target: None,
5487 rights: None,
5488 subdir: None,
5489 ..Default::default()
5490 }),
5491 fdecl::Expose::Runner(fdecl::ExposeRunner {
5492 source: None,
5493 source_name: None,
5494 target: None,
5495 target_name: None,
5496 ..Default::default()
5497 }),
5498 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5499 source: None,
5500 source_name: None,
5501 target: None,
5502 target_name: None,
5503 ..Default::default()
5504 }),
5505 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5506 ..Default::default()
5507 }),
5508 ]);
5509 decl
5510 },
5511 result = Err(ErrorList::new(vec![
5512 Error::missing_field(DeclType::ExposeService, "source"),
5513 Error::missing_field(DeclType::ExposeService, "target"),
5514 Error::missing_field(DeclType::ExposeService, "source_name"),
5515 Error::missing_field(DeclType::ExposeService, "target_name"),
5516 Error::missing_field(DeclType::ExposeProtocol, "source"),
5517 Error::missing_field(DeclType::ExposeProtocol, "target"),
5518 Error::missing_field(DeclType::ExposeProtocol, "source_name"),
5519 Error::missing_field(DeclType::ExposeProtocol, "target_name"),
5520 Error::missing_field(DeclType::ExposeDirectory, "source"),
5521 Error::missing_field(DeclType::ExposeDirectory, "target"),
5522 Error::missing_field(DeclType::ExposeDirectory, "source_name"),
5523 Error::missing_field(DeclType::ExposeDirectory, "target_name"),
5524 Error::missing_field(DeclType::ExposeRunner, "source"),
5525 Error::missing_field(DeclType::ExposeRunner, "target"),
5526 Error::missing_field(DeclType::ExposeRunner, "source_name"),
5527 Error::missing_field(DeclType::ExposeRunner, "target_name"),
5528 Error::missing_field(DeclType::ExposeResolver, "source"),
5529 Error::missing_field(DeclType::ExposeResolver, "target"),
5530 Error::missing_field(DeclType::ExposeResolver, "source_name"),
5531 Error::missing_field(DeclType::ExposeResolver, "target_name"),
5532 Error::missing_field(DeclType::ExposeDictionary, "source"),
5533 Error::missing_field(DeclType::ExposeDictionary, "target"),
5534 Error::missing_field(DeclType::ExposeDictionary, "source_name"),
5535 Error::missing_field(DeclType::ExposeDictionary, "target_name"),
5536 ])),
5537 },
5538 test_validate_exposes_extraneous => {
5539 input = {
5540 let mut decl = new_component_decl();
5541 decl.exposes = Some(vec![
5542 fdecl::Expose::Service(fdecl::ExposeService {
5543 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5544 name: "logger".to_string(),
5545 collection: Some("modular".to_string()),
5546 })),
5547 source_name: Some("logger".to_string()),
5548 target_name: Some("logger".to_string()),
5549 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5550 ..Default::default()
5551 }),
5552 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5553 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5554 name: "logger".to_string(),
5555 collection: Some("modular".to_string()),
5556 })),
5557 source_name: Some("legacy_logger".to_string()),
5558 target_name: Some("legacy_logger".to_string()),
5559 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5560 ..Default::default()
5561 }),
5562 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5563 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5564 name: "netstack".to_string(),
5565 collection: Some("modular".to_string()),
5566 })),
5567 source_name: Some("data".to_string()),
5568 target_name: Some("data".to_string()),
5569 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5570 rights: Some(fio::Operations::CONNECT),
5571 subdir: None,
5572 ..Default::default()
5573 }),
5574 fdecl::Expose::Runner(fdecl::ExposeRunner {
5575 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5576 name: "netstack".to_string(),
5577 collection: Some("modular".to_string()),
5578 })),
5579 source_name: Some("elf".to_string()),
5580 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5581 target_name: Some("elf".to_string()),
5582 ..Default::default()
5583 }),
5584 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5585 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5586 name: "netstack".to_string(),
5587 collection: Some("modular".to_string()),
5588 })),
5589 source_name: Some("pkg".to_string()),
5590 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5591 target_name: Some("pkg".to_string()),
5592 ..Default::default()
5593 }),
5594 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5595 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5596 name: "netstack".to_string(),
5597 collection: Some("modular".to_string()),
5598 })),
5599 source_name: Some("dict".to_string()),
5600 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5601 target_name: Some("dict".to_string()),
5602 ..Default::default()
5603 }),
5604 ]);
5605 decl
5606 },
5607 result = Err(ErrorList::new(vec![
5608 Error::extraneous_field(DeclType::ExposeService, "source.child.collection"),
5609 Error::extraneous_field(DeclType::ExposeProtocol, "source.child.collection"),
5610 Error::extraneous_field(DeclType::ExposeDirectory, "source.child.collection"),
5611 Error::extraneous_field(DeclType::ExposeRunner, "source.child.collection"),
5612 Error::extraneous_field(DeclType::ExposeResolver, "source.child.collection"),
5613 Error::extraneous_field(DeclType::ExposeDictionary, "source.child.collection"),
5614 ])),
5615 },
5616 test_validate_exposes_invalid_identifiers => {
5617 input = {
5618 let mut decl = new_component_decl();
5619 decl.exposes = Some(vec![
5620 fdecl::Expose::Service(fdecl::ExposeService {
5621 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5622 name: "^bad".to_string(),
5623 collection: None,
5624 })),
5625 source_name: Some("foo/".to_string()),
5626 target_name: Some("/".to_string()),
5627 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5628 ..Default::default()
5629 }),
5630 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5631 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5632 name: "^bad".to_string(),
5633 collection: None,
5634 })),
5635 source_name: Some("foo/".to_string()),
5636 target_name: Some("/".to_string()),
5637 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5638 ..Default::default()
5639 }),
5640 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5641 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5642 name: "^bad".to_string(),
5643 collection: None,
5644 })),
5645 source_name: Some("foo/".to_string()),
5646 target_name: Some("/".to_string()),
5647 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5648 rights: Some(fio::Operations::CONNECT),
5649 subdir: Some("/foo".to_string()),
5650 ..Default::default()
5651 }),
5652 fdecl::Expose::Runner(fdecl::ExposeRunner {
5653 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5654 name: "^bad".to_string(),
5655 collection: None,
5656 })),
5657 source_name: Some("/path".to_string()),
5658 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5659 target_name: Some("elf!".to_string()),
5660 ..Default::default()
5661 }),
5662 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5663 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5664 name: "^bad".to_string(),
5665 collection: None,
5666 })),
5667 source_name: Some("/path".to_string()),
5668 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5669 target_name: Some("pkg!".to_string()),
5670 ..Default::default()
5671 }),
5672 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5673 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5674 name: "^bad".to_string(),
5675 collection: None,
5676 })),
5677 source_name: Some("/path".to_string()),
5678 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5679 target_name: Some("pkg!".to_string()),
5680 ..Default::default()
5681 }),
5682 ]);
5683 decl
5684 },
5685 result = Err(ErrorList::new(vec![
5686 Error::invalid_field(DeclType::ExposeService, "source.child.name"),
5687 Error::invalid_field(DeclType::ExposeService, "source_name"),
5688 Error::invalid_field(DeclType::ExposeService, "target_name"),
5689 Error::invalid_field(DeclType::ExposeProtocol, "source.child.name"),
5690 Error::invalid_field(DeclType::ExposeProtocol, "source_name"),
5691 Error::invalid_field(DeclType::ExposeProtocol, "target_name"),
5692 Error::invalid_field(DeclType::ExposeDirectory, "source.child.name"),
5693 Error::invalid_field(DeclType::ExposeDirectory, "source_name"),
5694 Error::invalid_field(DeclType::ExposeDirectory, "target_name"),
5695 Error::invalid_field(DeclType::ExposeDirectory, "subdir"),
5696 Error::invalid_field(DeclType::ExposeRunner, "source.child.name"),
5697 Error::invalid_field(DeclType::ExposeRunner, "source_name"),
5698 Error::invalid_field(DeclType::ExposeRunner, "target_name"),
5699 Error::invalid_field(DeclType::ExposeResolver, "source.child.name"),
5700 Error::invalid_field(DeclType::ExposeResolver, "source_name"),
5701 Error::invalid_field(DeclType::ExposeResolver, "target_name"),
5702 Error::invalid_field(DeclType::ExposeDictionary, "source.child.name"),
5703 Error::invalid_field(DeclType::ExposeDictionary, "source_name"),
5704 Error::invalid_field(DeclType::ExposeDictionary, "target_name"),
5705 ])),
5706 },
5707 test_validate_exposes_invalid_source_target => {
5708 input = {
5709 let mut decl = new_component_decl();
5710 decl.children = Some(vec![fdecl::Child{
5711 name: Some("logger".to_string()),
5712 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5713 startup: Some(fdecl::StartupMode::Lazy),
5714 on_terminate: None,
5715 environment: None,
5716 ..Default::default()
5717 }]);
5718 decl.exposes = Some(vec![
5719 fdecl::Expose::Service(fdecl::ExposeService {
5720 source: None,
5721 source_name: Some("a".to_string()),
5722 target_name: Some("b".to_string()),
5723 target: None,
5724 ..Default::default()
5725 }),
5726 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5727 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5728 source_name: Some("c".to_string()),
5729 target_name: Some("d".to_string()),
5730 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5731 ..Default::default()
5732 }),
5733 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5734 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "z".to_string()})),
5735 source_name: Some("e".to_string()),
5736 target_name: Some("f".to_string()),
5737 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "z".to_string()})),
5738 rights: Some(fio::Operations::CONNECT),
5739 subdir: None,
5740 ..Default::default()
5741 }),
5742 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5743 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5744 source_name: Some("g".to_string()),
5745 target_name: Some("h".to_string()),
5746 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5747 rights: Some(fio::Operations::CONNECT),
5748 subdir: None,
5749 ..Default::default()
5750 }),
5751 fdecl::Expose::Runner(fdecl::ExposeRunner {
5752 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5753 source_name: Some("i".to_string()),
5754 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5755 target_name: Some("j".to_string()),
5756 ..Default::default()
5757 }),
5758 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5759 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5760 source_name: Some("k".to_string()),
5761 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5762 target_name: Some("l".to_string()),
5763 ..Default::default()
5764 }),
5765 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5766 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5767 name: "logger".to_string(),
5768 collection: None,
5769 })),
5770 source_name: Some("m".to_string()),
5771 target_name: Some("n".to_string()),
5772 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5773 ..Default::default()
5774 }),
5775 ]);
5776 decl
5777 },
5778 result = Err(ErrorList::new(vec![
5779 Error::missing_field(DeclType::ExposeService, "source"),
5780 Error::missing_field(DeclType::ExposeService, "target"),
5781 Error::invalid_field(DeclType::ExposeProtocol, "source"),
5782 Error::invalid_field(DeclType::ExposeProtocol, "target"),
5783 Error::invalid_field(DeclType::ExposeDirectory, "source"),
5784 Error::invalid_field(DeclType::ExposeDirectory, "target"),
5785 Error::invalid_field(DeclType::ExposeDirectory, "source"),
5786 Error::invalid_field(DeclType::ExposeDirectory, "target"),
5787 Error::invalid_field(DeclType::ExposeRunner, "source"),
5788 Error::invalid_field(DeclType::ExposeRunner, "target"),
5789 Error::invalid_field(DeclType::ExposeResolver, "source"),
5790 Error::invalid_field(DeclType::ExposeResolver, "target"),
5791 Error::invalid_field(DeclType::ExposeDictionary, "target"),
5792 ])),
5793 },
5794 test_validate_exposes_invalid_source_collection => {
5795 input = {
5796 let mut decl = new_component_decl();
5797 decl.collections = Some(vec![fdecl::Collection{
5798 name: Some("col".to_string()),
5799 durability: Some(fdecl::Durability::Transient),
5800 allowed_offers: None,
5801 allow_long_names: None,
5802 ..Default::default()
5803 }]);
5804 decl.exposes = Some(vec![
5805 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5806 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
5807 source_name: Some("a".to_string()),
5808 target_name: Some("a".to_string()),
5809 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5810 ..Default::default()
5811 }),
5812 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5813 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5814 source_name: Some("b".to_string()),
5815 target_name: Some("b".to_string()),
5816 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5817 rights: Some(fio::Operations::CONNECT),
5818 subdir: None,
5819 ..Default::default()
5820 }),
5821 fdecl::Expose::Runner(fdecl::ExposeRunner {
5822 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5823 source_name: Some("c".to_string()),
5824 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5825 target_name: Some("c".to_string()),
5826 ..Default::default()
5827 }),
5828 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5829 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5830 source_name: Some("d".to_string()),
5831 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5832 target_name: Some("d".to_string()),
5833 ..Default::default()
5834 }),
5835 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5836 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5837 source_name: Some("e".to_string()),
5838 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5839 target_name: Some("e".to_string()),
5840 ..Default::default()
5841 }),
5842 ]);
5843 decl
5844 },
5845 result = Err(ErrorList::new(vec![
5846 Error::invalid_field(DeclType::ExposeProtocol, "source"),
5847 Error::invalid_field(DeclType::ExposeDirectory, "source"),
5848 Error::invalid_field(DeclType::ExposeRunner, "source"),
5849 Error::invalid_field(DeclType::ExposeResolver, "source"),
5850 Error::invalid_field(DeclType::ExposeDictionary, "source"),
5851 ])),
5852 },
5853 test_validate_exposes_sources_collection => {
5854 input = {
5855 let mut decl = new_component_decl();
5856 decl.collections = Some(vec![
5857 fdecl::Collection {
5858 name: Some("col".to_string()),
5859 durability: Some(fdecl::Durability::Transient),
5860 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
5861 allow_long_names: None,
5862 ..Default::default()
5863 }
5864 ]);
5865 decl.exposes = Some(vec![
5866 fdecl::Expose::Service(fdecl::ExposeService {
5867 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
5868 source_name: Some("a".to_string()),
5869 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5870 target_name: Some("a".to_string()),
5871 ..Default::default()
5872 })
5873 ]);
5874 decl
5875 },
5876 result = Ok(()),
5877 },
5878 test_validate_exposes_long_identifiers => {
5879 input = {
5880 let mut decl = new_component_decl();
5881 decl.exposes = Some(vec![
5882 fdecl::Expose::Service(fdecl::ExposeService {
5883 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5884 name: "b".repeat(256),
5885 collection: None,
5886 })),
5887 source_name: Some(format!("{}", "a".repeat(1025))),
5888 target_name: Some(format!("{}", "b".repeat(1025))),
5889 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5890 ..Default::default()
5891 }),
5892 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5893 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5894 name: "b".repeat(256),
5895 collection: None,
5896 })),
5897 source_name: Some(format!("{}", "a".repeat(256))),
5898 target_name: Some(format!("{}", "b".repeat(256))),
5899 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5900 ..Default::default()
5901 }),
5902 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5903 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5904 name: "b".repeat(256),
5905 collection: None,
5906 })),
5907 source_name: Some(format!("{}", "a".repeat(256))),
5908 target_name: Some(format!("{}", "b".repeat(256))),
5909 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5910 rights: Some(fio::Operations::CONNECT),
5911 subdir: None,
5912 ..Default::default()
5913 }),
5914 fdecl::Expose::Runner(fdecl::ExposeRunner {
5915 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5916 name: "b".repeat(256),
5917 collection: None,
5918 })),
5919 source_name: Some("a".repeat(256)),
5920 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5921 target_name: Some("b".repeat(256)),
5922 ..Default::default()
5923 }),
5924 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5925 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5926 name: "b".repeat(256),
5927 collection: None,
5928 })),
5929 source_name: Some("a".repeat(256)),
5930 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5931 target_name: Some("b".repeat(256)),
5932 ..Default::default()
5933 }),
5934 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5935 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5936 name: "b".repeat(256),
5937 collection: None,
5938 })),
5939 source_name: Some("a".repeat(256)),
5940 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5941 target_name: Some("b".repeat(256)),
5942 ..Default::default()
5943 }),
5944 ]);
5945 decl
5946 },
5947 result = Err(ErrorList::new(vec![
5948 Error::field_too_long(DeclType::ExposeService, "source.child.name"),
5949 Error::field_too_long(DeclType::ExposeService, "source_name"),
5950 Error::field_too_long(DeclType::ExposeService, "target_name"),
5951 Error::field_too_long(DeclType::ExposeProtocol, "source.child.name"),
5952 Error::field_too_long(DeclType::ExposeProtocol, "source_name"),
5953 Error::field_too_long(DeclType::ExposeProtocol, "target_name"),
5954 Error::field_too_long(DeclType::ExposeDirectory, "source.child.name"),
5955 Error::field_too_long(DeclType::ExposeDirectory, "source_name"),
5956 Error::field_too_long(DeclType::ExposeDirectory, "target_name"),
5957 Error::field_too_long(DeclType::ExposeRunner, "source.child.name"),
5958 Error::field_too_long(DeclType::ExposeRunner, "source_name"),
5959 Error::field_too_long(DeclType::ExposeRunner, "target_name"),
5960 Error::field_too_long(DeclType::ExposeResolver, "source.child.name"),
5961 Error::field_too_long(DeclType::ExposeResolver, "source_name"),
5962 Error::field_too_long(DeclType::ExposeResolver, "target_name"),
5963 Error::field_too_long(DeclType::ExposeDictionary, "source.child.name"),
5964 Error::field_too_long(DeclType::ExposeDictionary, "source_name"),
5965 Error::field_too_long(DeclType::ExposeDictionary, "target_name"),
5966 ])),
5967 },
5968 test_validate_exposes_invalid_child => {
5969 input = {
5970 let mut decl = new_component_decl();
5971 decl.exposes = Some(vec![
5972 fdecl::Expose::Service(fdecl::ExposeService {
5973 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5974 name: "netstack".to_string(),
5975 collection: None,
5976 })),
5977 source_name: Some("fuchsia.logger.Log".to_string()),
5978 target_name: Some("fuchsia.logger.Log".to_string()),
5979 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5980 ..Default::default()
5981 }),
5982 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5983 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5984 name: "netstack".to_string(),
5985 collection: None,
5986 })),
5987 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
5988 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
5989 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5990 ..Default::default()
5991 }),
5992 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5993 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5994 name: "netstack".to_string(),
5995 collection: None,
5996 })),
5997 source_name: Some("data".to_string()),
5998 target_name: Some("data".to_string()),
5999 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6000 rights: Some(fio::Operations::CONNECT),
6001 subdir: None,
6002 ..Default::default()
6003 }),
6004 fdecl::Expose::Runner(fdecl::ExposeRunner {
6005 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6006 name: "netstack".to_string(),
6007 collection: None,
6008 })),
6009 source_name: Some("elf".to_string()),
6010 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6011 target_name: Some("elf".to_string()),
6012 ..Default::default()
6013 }),
6014 fdecl::Expose::Resolver(fdecl::ExposeResolver {
6015 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6016 name: "netstack".to_string(),
6017 collection: None,
6018 })),
6019 source_name: Some("pkg".to_string()),
6020 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6021 target_name: Some("pkg".to_string()),
6022 ..Default::default()
6023 }),
6024 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
6025 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6026 name: "netstack".to_string(),
6027 collection: None,
6028 })),
6029 source_name: Some("dict".to_string()),
6030 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6031 target_name: Some("dict".to_string()),
6032 ..Default::default()
6033 }),
6034 ]);
6035 decl
6036 },
6037 result = Err(ErrorList::new(vec![
6038 Error::invalid_child(DeclType::ExposeService, "source", "netstack"),
6039 Error::invalid_child(DeclType::ExposeProtocol, "source", "netstack"),
6040 Error::invalid_child(DeclType::ExposeDirectory, "source", "netstack"),
6041 Error::invalid_child(DeclType::ExposeRunner, "source", "netstack"),
6042 Error::invalid_child(DeclType::ExposeResolver, "source", "netstack"),
6043 Error::invalid_child(DeclType::ExposeDictionary, "source", "netstack"),
6044 ])),
6045 },
6046 test_validate_exposes_invalid_source_capability => {
6047 input = {
6048 fdecl::Component {
6049 exposes: Some(vec![
6050 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6051 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
6052 name: "this-storage-doesnt-exist".to_string(),
6053 })),
6054 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
6055 target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
6056 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6057 ..Default::default()
6058 }),
6059 ]),
6060 ..new_component_decl()
6061 }
6062 },
6063 result = Err(ErrorList::new(vec![
6064 Error::invalid_capability(DeclType::ExposeProtocol, "source", "this-storage-doesnt-exist"),
6065 ])),
6066 },
6067 test_validate_exposes_duplicate_target => {
6068 input = {
6069 let mut decl = new_component_decl();
6070 decl.exposes = Some(vec![
6071 fdecl::Expose::Service(fdecl::ExposeService {
6072 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6073 name: "coll".into(),
6074 })),
6075 source_name: Some("netstack".to_string()),
6076 target_name: Some("fuchsia.net.Stack".to_string()),
6077 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6078 ..Default::default()
6079 }),
6080 fdecl::Expose::Service(fdecl::ExposeService {
6081 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6082 name: "coll2".into(),
6083 })),
6084 source_name: Some("netstack2".to_string()),
6085 target_name: Some("fuchsia.net.Stack".to_string()),
6086 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6087 ..Default::default()
6088 }),
6089 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6090 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6091 source_name: Some("fonts".to_string()),
6092 target_name: Some("fuchsia.fonts.Provider".to_string()),
6093 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6094 ..Default::default()
6095 }),
6096 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6097 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6098 source_name: Some("fonts2".to_string()),
6099 target_name: Some("fuchsia.fonts.Provider".to_string()),
6100 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6101 ..Default::default()
6102 }),
6103 fdecl::Expose::Directory(fdecl::ExposeDirectory {
6104 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6105 source_name: Some("assets".to_string()),
6106 target_name: Some("stuff".to_string()),
6107 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6108 rights: None,
6109 subdir: None,
6110 ..Default::default()
6111 }),
6112 fdecl::Expose::Directory(fdecl::ExposeDirectory {
6113 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6114 source_name: Some("assets2".to_string()),
6115 target_name: Some("stuff".to_string()),
6116 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6117 rights: None,
6118 subdir: None,
6119 ..Default::default()
6120 }),
6121 fdecl::Expose::Runner(fdecl::ExposeRunner {
6122 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6123 source_name: Some("source_elf".to_string()),
6124 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6125 target_name: Some("elf".to_string()),
6126 ..Default::default()
6127 }),
6128 fdecl::Expose::Runner(fdecl::ExposeRunner {
6129 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6130 source_name: Some("source_elf".to_string()),
6131 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6132 target_name: Some("elf".to_string()),
6133 ..Default::default()
6134 }),
6135 fdecl::Expose::Resolver(fdecl::ExposeResolver {
6136 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6137 source_name: Some("source_pkg".to_string()),
6138 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6139 target_name: Some("pkg".to_string()),
6140 ..Default::default()
6141 }),
6142 fdecl::Expose::Resolver(fdecl::ExposeResolver {
6143 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6144 source_name: Some("source_pkg".to_string()),
6145 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6146 target_name: Some("pkg".to_string()),
6147 ..Default::default()
6148 }),
6149 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
6150 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6151 source_name: Some("source_dict".to_string()),
6152 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6153 target_name: Some("dict".to_string()),
6154 ..Default::default()
6155 }),
6156 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
6157 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6158 source_name: Some("source_dict".to_string()),
6159 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6160 target_name: Some("dict".to_string()),
6161 ..Default::default()
6162 }),
6163 ]);
6164 decl.collections = Some(vec![
6165 fdecl::Collection {
6166 name: Some("coll".into()),
6167 durability: Some(fdecl::Durability::Transient),
6168 ..Default::default()
6169 },
6170 fdecl::Collection {
6171 name: Some("coll2".into()),
6172 durability: Some(fdecl::Durability::Transient),
6173 ..Default::default()
6174 },
6175 ]);
6176 decl.capabilities = Some(vec![
6177 fdecl::Capability::Service(fdecl::Service {
6178 name: Some("netstack".to_string()),
6179 source_path: Some("/path".to_string()),
6180 ..Default::default()
6181 }),
6182 fdecl::Capability::Service(fdecl::Service {
6183 name: Some("netstack2".to_string()),
6184 source_path: Some("/path".to_string()),
6185 ..Default::default()
6186 }),
6187 fdecl::Capability::Protocol(fdecl::Protocol {
6188 name: Some("fonts".to_string()),
6189 source_path: Some("/path".to_string()),
6190 ..Default::default()
6191 }),
6192 fdecl::Capability::Protocol(fdecl::Protocol {
6193 name: Some("fonts2".to_string()),
6194 source_path: Some("/path".to_string()),
6195 ..Default::default()
6196 }),
6197 fdecl::Capability::Directory(fdecl::Directory {
6198 name: Some("assets".to_string()),
6199 source_path: Some("/path".to_string()),
6200 rights: Some(fio::Operations::CONNECT),
6201 ..Default::default()
6202 }),
6203 fdecl::Capability::Directory(fdecl::Directory {
6204 name: Some("assets2".to_string()),
6205 source_path: Some("/path".to_string()),
6206 rights: Some(fio::Operations::CONNECT),
6207 ..Default::default()
6208 }),
6209 fdecl::Capability::Runner(fdecl::Runner {
6210 name: Some("source_elf".to_string()),
6211 source_path: Some("/path".to_string()),
6212 ..Default::default()
6213 }),
6214 fdecl::Capability::Resolver(fdecl::Resolver {
6215 name: Some("source_pkg".to_string()),
6216 source_path: Some("/path".to_string()),
6217 ..Default::default()
6218 }),
6219 fdecl::Capability::Dictionary(fdecl::Dictionary {
6220 name: Some("source_dict".to_string()),
6221 ..Default::default()
6222 }),
6223 ]);
6224 decl
6225 },
6226 result = Err(ErrorList::new(vec![
6227 Error::duplicate_field(DeclType::ExposeProtocol, "target_name",
6229 "fuchsia.fonts.Provider"),
6230 Error::duplicate_field(DeclType::ExposeDirectory, "target_name",
6231 "stuff"),
6232 Error::duplicate_field(DeclType::ExposeRunner, "target_name",
6233 "elf"),
6234 Error::duplicate_field(DeclType::ExposeResolver, "target_name", "pkg"),
6235 Error::duplicate_field(DeclType::ExposeDictionary, "target_name", "dict"),
6236 ])),
6237 },
6238 test_validate_exposes_invalid_capability_from_self => {
6239 input = {
6240 let mut decl = new_component_decl();
6241 decl.exposes = Some(vec![
6242 fdecl::Expose::Service(fdecl::ExposeService {
6243 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6244 source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
6245 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6246 target_name: Some("foo".to_string()),
6247 ..Default::default()
6248 }),
6249 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6250 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6251 source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
6252 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6253 target_name: Some("bar".to_string()),
6254 ..Default::default()
6255 }),
6256 fdecl::Expose::Directory(fdecl::ExposeDirectory {
6257 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6258 source_name: Some("dir".to_string()),
6259 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6260 target_name: Some("assets".to_string()),
6261 rights: None,
6262 subdir: None,
6263 ..Default::default()
6264 }),
6265 fdecl::Expose::Runner(fdecl::ExposeRunner {
6266 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6267 source_name: Some("source_elf".to_string()),
6268 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6269 target_name: Some("elf".to_string()),
6270 ..Default::default()
6271 }),
6272 fdecl::Expose::Resolver(fdecl::ExposeResolver {
6273 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6274 source_name: Some("source_pkg".to_string()),
6275 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6276 target_name: Some("pkg".to_string()),
6277 ..Default::default()
6278 }),
6279 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
6280 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6281 source_name: Some("source_dict".to_string()),
6282 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6283 target_name: Some("dict".to_string()),
6284 ..Default::default()
6285 }),
6286 fdecl::Expose::Config(fdecl::ExposeConfiguration {
6287 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6288 source_name: Some("source_config".to_string()),
6289 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6290 target_name: Some("config".to_string()),
6291 ..Default::default()
6292 }),
6293 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6294 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6295 source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
6296 source_dictionary: Some("dict/inner".to_string()),
6297 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6298 target_name: Some("baz".to_string()),
6299 ..Default::default()
6300 }),
6301 ]);
6302 decl
6303 },
6304 result = Err(ErrorList::new(vec![
6305 Error::invalid_capability(
6306 DeclType::ExposeService,
6307 "source",
6308 "fuchsia.some.library.SomeProtocol"),
6309 Error::invalid_capability(
6310 DeclType::ExposeProtocol,
6311 "source",
6312 "fuchsia.some.library.SomeProtocol"),
6313 Error::invalid_capability(DeclType::ExposeDirectory, "source", "dir"),
6314 Error::invalid_capability(DeclType::ExposeRunner, "source", "source_elf"),
6315 Error::invalid_capability(DeclType::ExposeResolver, "source", "source_pkg"),
6316 Error::invalid_capability(DeclType::ExposeDictionary, "source", "source_dict"),
6317 Error::invalid_capability(DeclType::ExposeConfig, "source", "source_config"),
6318 Error::invalid_capability(DeclType::ExposeProtocol, "source", "dict"),
6319 ])),
6320 },
6321
6322 test_validate_exposes_availability_service => {
6323 input = {
6324 let mut decl = generate_expose_different_source_and_availability_decl(
6325 |source, availability, target_name|
6326 fdecl::Expose::Service(fdecl::ExposeService {
6327 source: Some(source),
6328 source_name: Some("fuchsia.examples.Echo".to_string()),
6329 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6330 target_name: Some(target_name.to_string()),
6331 availability: Some(availability),
6332 ..Default::default()
6333 })
6334 );
6335 decl.capabilities = Some(vec![
6336 fdecl::Capability::Service(fdecl::Service {
6337 name: Some("fuchsia.examples.Echo".to_string()),
6338 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
6339 ..Default::default()
6340 }),
6341 ]);
6342 decl
6343 },
6344 result = {
6345 Err(ErrorList::new(vec![
6346 Error::availability_must_be_optional(
6347 DeclType::ExposeService,
6348 "availability",
6349 Some(&"fuchsia.examples.Echo".to_string()),
6350 ),
6351 Error::availability_must_be_optional(
6352 DeclType::ExposeService,
6353 "availability",
6354 Some(&"fuchsia.examples.Echo".to_string()),
6355 ),
6356 ]))
6357 },
6358 },
6359 test_validate_exposes_availability_protocol => {
6360 input = {
6361 let mut decl = generate_expose_different_source_and_availability_decl(
6362 |source, availability, target_name|
6363 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6364 source: Some(source),
6365 source_name: Some("fuchsia.examples.Echo".to_string()),
6366 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6367 target_name: Some(target_name.to_string()),
6368 availability: Some(availability),
6369 ..Default::default()
6370 })
6371 );
6372 decl.capabilities = Some(vec![
6373 fdecl::Capability::Protocol(fdecl::Protocol {
6374 name: Some("fuchsia.examples.Echo".to_string()),
6375 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
6376 ..Default::default()
6377 }),
6378 ]);
6379 decl
6380 },
6381 result = {
6382 Err(ErrorList::new(vec![
6383 Error::availability_must_be_optional(
6384 DeclType::ExposeProtocol,
6385 "availability",
6386 Some(&"fuchsia.examples.Echo".to_string()),
6387 ),
6388 Error::availability_must_be_optional(
6389 DeclType::ExposeProtocol,
6390 "availability",
6391 Some(&"fuchsia.examples.Echo".to_string()),
6392 ),
6393 ]))
6394 },
6395 },
6396 test_validate_exposes_availability_directory => {
6397 input = {
6398 let mut decl = generate_expose_different_source_and_availability_decl(
6399 |source, availability, target_name|
6400 fdecl::Expose::Directory(fdecl::ExposeDirectory {
6401 source: Some(source),
6402 source_name: Some("fuchsia.examples.Echo".to_string()),
6403 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6404 target_name: Some(target_name.to_string()),
6405 availability: Some(availability),
6406 ..Default::default()
6407 })
6408 );
6409 decl.capabilities = Some(vec![
6410 fdecl::Capability::Directory(fdecl::Directory {
6411 name: Some("fuchsia.examples.Echo".to_string()),
6412 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
6413 rights: Some(fio::Operations::READ_BYTES),
6414 ..Default::default()
6415 }),
6416 ]);
6417 decl
6418 },
6419 result = {
6420 Err(ErrorList::new(vec![
6421 Error::availability_must_be_optional(
6422 DeclType::ExposeDirectory,
6423 "availability",
6424 Some(&"fuchsia.examples.Echo".to_string()),
6425 ),
6426 Error::availability_must_be_optional(
6427 DeclType::ExposeDirectory,
6428 "availability",
6429 Some(&"fuchsia.examples.Echo".to_string()),
6430 ),
6431 ]))
6432 },
6433 },
6434
6435 test_validate_offers_empty => {
6437 input = {
6438 let mut decl = new_component_decl();
6439 decl.offers = Some(vec![
6440 fdecl::Offer::Service(fdecl::OfferService {
6441 source: None,
6442 source_name: None,
6443 target: None,
6444 target_name: None,
6445 ..Default::default()
6446 }),
6447 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6448 source: None,
6449 source_name: None,
6450 target: None,
6451 target_name: None,
6452 dependency_type: None,
6453 ..Default::default()
6454 }),
6455 fdecl::Offer::Directory(fdecl::OfferDirectory {
6456 source: None,
6457 source_name: None,
6458 target: None,
6459 target_name: None,
6460 rights: None,
6461 subdir: None,
6462 dependency_type: None,
6463 ..Default::default()
6464 }),
6465 fdecl::Offer::Storage(fdecl::OfferStorage {
6466 source_name: None,
6467 source: None,
6468 target: None,
6469 target_name: None,
6470 ..Default::default()
6471 }),
6472 fdecl::Offer::Runner(fdecl::OfferRunner {
6473 source: None,
6474 source_name: None,
6475 target: None,
6476 target_name: None,
6477 ..Default::default()
6478 }),
6479 fdecl::Offer::Resolver(fdecl::OfferResolver {
6480 ..Default::default()
6481 }),
6482 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6483 ..Default::default()
6484 }),
6485 ]);
6486 decl
6487 },
6488 result = Err(ErrorList::new(vec![
6491 Error::missing_field(DeclType::OfferService, "source"),
6492 Error::missing_field(DeclType::OfferService, "source_name"),
6493 Error::missing_field(DeclType::OfferService, "target"),
6494 Error::missing_field(DeclType::OfferService, "target_name"),
6495 Error::missing_field(DeclType::OfferProtocol, "source"),
6497 Error::missing_field(DeclType::OfferProtocol, "source_name"),
6498 Error::missing_field(DeclType::OfferProtocol, "target"),
6499 Error::missing_field(DeclType::OfferProtocol, "target_name"),
6500 Error::missing_field(DeclType::OfferProtocol, "dependency_type"),
6501 Error::missing_field(DeclType::OfferDirectory, "source"),
6503 Error::missing_field(DeclType::OfferDirectory, "source_name"),
6504 Error::missing_field(DeclType::OfferDirectory, "target"),
6505 Error::missing_field(DeclType::OfferDirectory, "target_name"),
6506 Error::missing_field(DeclType::OfferDirectory, "dependency_type"),
6507 Error::missing_field(DeclType::OfferStorage, "source"),
6509 Error::missing_field(DeclType::OfferStorage, "source_name"),
6510 Error::missing_field(DeclType::OfferStorage, "target"),
6511 Error::missing_field(DeclType::OfferStorage, "target_name"),
6512 Error::missing_field(DeclType::OfferRunner, "source"),
6514 Error::missing_field(DeclType::OfferRunner, "source_name"),
6515 Error::missing_field(DeclType::OfferRunner, "target"),
6516 Error::missing_field(DeclType::OfferRunner, "target_name"),
6517 Error::missing_field(DeclType::OfferResolver, "source"),
6519 Error::missing_field(DeclType::OfferResolver, "source_name"),
6520 Error::missing_field(DeclType::OfferResolver, "target"),
6521 Error::missing_field(DeclType::OfferResolver, "target_name"),
6522 Error::missing_field(DeclType::OfferDictionary, "source"),
6523 Error::missing_field(DeclType::OfferDictionary, "source_name"),
6524 Error::missing_field(DeclType::OfferDictionary, "target"),
6525 Error::missing_field(DeclType::OfferDictionary, "target_name"),
6526 Error::missing_field(DeclType::OfferDictionary, "dependency_type"),
6527 ])),
6528 },
6529 test_validate_offers_long_identifiers => {
6530 input = {
6531 let mut decl = new_component_decl();
6532 decl.offers = Some(vec![
6533 fdecl::Offer::Service(fdecl::OfferService {
6534 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6535 name: "a".repeat(256),
6536 collection: None,
6537 })),
6538 source_name: Some(format!("{}", "a".repeat(256))),
6539 target: Some(fdecl::Ref::Child(
6540 fdecl::ChildRef {
6541 name: "b".repeat(256),
6542 collection: None,
6543 }
6544 )),
6545 target_name: Some(format!("{}", "b".repeat(256))),
6546 ..Default::default()
6547 }),
6548 fdecl::Offer::Service(fdecl::OfferService {
6549 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6550 source_name: Some("a".to_string()),
6551 target: Some(fdecl::Ref::Collection(
6552 fdecl::CollectionRef {
6553 name: "b".repeat(256),
6554 }
6555 )),
6556 target_name: Some(format!("{}", "b".repeat(256))),
6557 ..Default::default()
6558 }),
6559 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6560 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6561 name: "a".repeat(256),
6562 collection: None,
6563 })),
6564 source_name: Some(format!("{}", "a".repeat(256))),
6565 target: Some(fdecl::Ref::Child(
6566 fdecl::ChildRef {
6567 name: "b".repeat(256),
6568 collection: None,
6569 }
6570 )),
6571 target_name: Some(format!("{}", "b".repeat(256))),
6572 dependency_type: Some(fdecl::DependencyType::Strong),
6573 ..Default::default()
6574 }),
6575 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6576 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6577 source_name: Some("a".to_string()),
6578 target: Some(fdecl::Ref::Collection(
6579 fdecl::CollectionRef {
6580 name: "b".repeat(256),
6581 }
6582 )),
6583 target_name: Some(format!("{}", "b".repeat(256))),
6584 dependency_type: Some(fdecl::DependencyType::Weak),
6585 ..Default::default()
6586 }),
6587 fdecl::Offer::Directory(fdecl::OfferDirectory {
6588 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6589 name: "a".repeat(256),
6590 collection: None,
6591 })),
6592 source_name: Some(format!("{}", "a".repeat(256))),
6593 target: Some(fdecl::Ref::Child(
6594 fdecl::ChildRef {
6595 name: "b".repeat(256),
6596 collection: None,
6597 }
6598 )),
6599 target_name: Some(format!("{}", "b".repeat(256))),
6600 rights: Some(fio::Operations::CONNECT),
6601 subdir: None,
6602 dependency_type: Some(fdecl::DependencyType::Strong),
6603 ..Default::default()
6604 }),
6605 fdecl::Offer::Directory(fdecl::OfferDirectory {
6606 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6607 source_name: Some("a".to_string()),
6608 target: Some(fdecl::Ref::Collection(
6609 fdecl::CollectionRef {
6610 name: "b".repeat(256),
6611 }
6612 )),
6613 target_name: Some(format!("{}", "b".repeat(256))),
6614 rights: Some(fio::Operations::CONNECT),
6615 subdir: None,
6616 dependency_type: Some(fdecl::DependencyType::Weak),
6617 ..Default::default()
6618 }),
6619 fdecl::Offer::Storage(fdecl::OfferStorage {
6620 source_name: Some("data".to_string()),
6621 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6622 target: Some(fdecl::Ref::Child(
6623 fdecl::ChildRef {
6624 name: "b".repeat(256),
6625 collection: None,
6626 }
6627 )),
6628 target_name: Some("data".to_string()),
6629 ..Default::default()
6630 }),
6631 fdecl::Offer::Storage(fdecl::OfferStorage {
6632 source_name: Some("data".to_string()),
6633 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6634 target: Some(fdecl::Ref::Collection(
6635 fdecl::CollectionRef { name: "b".repeat(256) }
6636 )),
6637 target_name: Some("data".to_string()),
6638 ..Default::default()
6639 }),
6640 fdecl::Offer::Runner(fdecl::OfferRunner {
6641 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6642 name: "a".repeat(256),
6643 collection: None,
6644 })),
6645 source_name: Some("b".repeat(256)),
6646 target: Some(fdecl::Ref::Collection(
6647 fdecl::CollectionRef {
6648 name: "c".repeat(256),
6649 }
6650 )),
6651 target_name: Some("d".repeat(256)),
6652 ..Default::default()
6653 }),
6654 fdecl::Offer::Resolver(fdecl::OfferResolver {
6655 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6656 name: "a".repeat(256),
6657 collection: None,
6658 })),
6659 source_name: Some("b".repeat(256)),
6660 target: Some(fdecl::Ref::Collection(
6661 fdecl::CollectionRef {
6662 name: "c".repeat(256),
6663 }
6664 )),
6665 target_name: Some("d".repeat(256)),
6666 ..Default::default()
6667 }),
6668 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6669 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6670 name: "a".repeat(256),
6671 collection: None,
6672 })),
6673 source_name: Some("b".repeat(256)),
6674 target: Some(fdecl::Ref::Collection(
6675 fdecl::CollectionRef {
6676 name: "c".repeat(256),
6677 }
6678 )),
6679 target_name: Some("d".repeat(256)),
6680 dependency_type: Some(fdecl::DependencyType::Strong),
6681 ..Default::default()
6682 }),
6683 ]);
6684 decl
6685 },
6686 result = Err(ErrorList::new(vec![
6687 Error::field_too_long(DeclType::OfferService, "source.child.name"),
6688 Error::field_too_long(DeclType::OfferService, "source_name"),
6689 Error::field_too_long(DeclType::OfferService, "target.child.name"),
6690 Error::field_too_long(DeclType::OfferService, "target_name"),
6691 Error::field_too_long(DeclType::OfferService, "target.collection.name"),
6692 Error::field_too_long(DeclType::OfferService, "target_name"),
6693 Error::field_too_long(DeclType::OfferProtocol, "source.child.name"),
6694 Error::field_too_long(DeclType::OfferProtocol, "source_name"),
6695 Error::field_too_long(DeclType::OfferProtocol, "target.child.name"),
6696 Error::field_too_long(DeclType::OfferProtocol, "target_name"),
6697 Error::field_too_long(DeclType::OfferProtocol, "target.collection.name"),
6698 Error::field_too_long(DeclType::OfferProtocol, "target_name"),
6699 Error::field_too_long(DeclType::OfferDirectory, "source.child.name"),
6700 Error::field_too_long(DeclType::OfferDirectory, "source_name"),
6701 Error::field_too_long(DeclType::OfferDirectory, "target.child.name"),
6702 Error::field_too_long(DeclType::OfferDirectory, "target_name"),
6703 Error::field_too_long(DeclType::OfferDirectory, "target.collection.name"),
6704 Error::field_too_long(DeclType::OfferDirectory, "target_name"),
6705 Error::field_too_long(DeclType::OfferStorage, "target.child.name"),
6706 Error::field_too_long(DeclType::OfferStorage, "target.collection.name"),
6707 Error::field_too_long(DeclType::OfferRunner, "source.child.name"),
6708 Error::field_too_long(DeclType::OfferRunner, "source_name"),
6709 Error::field_too_long(DeclType::OfferRunner, "target.collection.name"),
6710 Error::field_too_long(DeclType::OfferRunner, "target_name"),
6711 Error::field_too_long(DeclType::OfferResolver, "source.child.name"),
6712 Error::field_too_long(DeclType::OfferResolver, "source_name"),
6713 Error::field_too_long(DeclType::OfferResolver, "target.collection.name"),
6714 Error::field_too_long(DeclType::OfferResolver, "target_name"),
6715 Error::field_too_long(DeclType::OfferDictionary, "source.child.name"),
6716 Error::field_too_long(DeclType::OfferDictionary, "source_name"),
6717 Error::field_too_long(DeclType::OfferDictionary, "target.collection.name"),
6718 Error::field_too_long(DeclType::OfferDictionary, "target_name"),
6719 ])),
6720 },
6721 test_validate_offers_extraneous => {
6722 input = {
6723 let mut decl = new_component_decl();
6724 decl.offers = Some(vec![
6725 fdecl::Offer::Service(fdecl::OfferService {
6726 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6727 name: "logger".to_string(),
6728 collection: Some("modular".to_string()),
6729 })),
6730 source_name: Some("fuchsia.logger.Log".to_string()),
6731 target: Some(fdecl::Ref::Child(
6732 fdecl::ChildRef {
6733 name: "netstack".to_string(),
6734 collection: Some("modular".to_string()),
6735 }
6736 )),
6737 target_name: Some("fuchsia.logger.Log".to_string()),
6738 ..Default::default()
6739 }),
6740 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6741 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6742 name: "logger".to_string(),
6743 collection: Some("modular".to_string()),
6744 })),
6745 source_name: Some("fuchsia.logger.Log".to_string()),
6746 target: Some(fdecl::Ref::Child(
6747 fdecl::ChildRef {
6748 name: "netstack".to_string(),
6749 collection: Some("modular".to_string()),
6750 }
6751 )),
6752 target_name: Some("fuchsia.logger.Log".to_string()),
6753 dependency_type: Some(fdecl::DependencyType::Strong),
6754 ..Default::default()
6755 }),
6756 fdecl::Offer::Directory(fdecl::OfferDirectory {
6757 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6758 name: "logger".to_string(),
6759 collection: Some("modular".to_string()),
6760 })),
6761 source_name: Some("assets".to_string()),
6762 target: Some(fdecl::Ref::Child(
6763 fdecl::ChildRef {
6764 name: "netstack".to_string(),
6765 collection: Some("modular".to_string()),
6766 }
6767 )),
6768 target_name: Some("assets".to_string()),
6769 rights: Some(fio::Operations::CONNECT),
6770 subdir: None,
6771 dependency_type: Some(fdecl::DependencyType::Weak),
6772 ..Default::default()
6773 }),
6774 fdecl::Offer::Storage(fdecl::OfferStorage {
6775 source_name: Some("data".to_string()),
6776 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{ })),
6777 target: Some(fdecl::Ref::Child(
6778 fdecl::ChildRef {
6779 name: "netstack".to_string(),
6780 collection: Some("modular".to_string()),
6781 }
6782 )),
6783 target_name: Some("data".to_string()),
6784 ..Default::default()
6785 }),
6786 fdecl::Offer::Runner(fdecl::OfferRunner {
6787 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6788 name: "logger".to_string(),
6789 collection: Some("modular".to_string()),
6790 })),
6791 source_name: Some("elf".to_string()),
6792 target: Some(fdecl::Ref::Child(
6793 fdecl::ChildRef {
6794 name: "netstack".to_string(),
6795 collection: Some("modular".to_string()),
6796 }
6797 )),
6798 target_name: Some("elf".to_string()),
6799 ..Default::default()
6800 }),
6801 fdecl::Offer::Resolver(fdecl::OfferResolver {
6802 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6803 name: "logger".to_string(),
6804 collection: Some("modular".to_string()),
6805 })),
6806 source_name: Some("pkg".to_string()),
6807 target: Some(fdecl::Ref::Child(
6808 fdecl::ChildRef {
6809 name: "netstack".to_string(),
6810 collection: Some("modular".to_string()),
6811 }
6812 )),
6813 target_name: Some("pkg".to_string()),
6814 ..Default::default()
6815 }),
6816 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6817 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6818 name: "logger".to_string(),
6819 collection: Some("modular".to_string()),
6820 })),
6821 source_name: Some("dict".to_string()),
6822 target: Some(fdecl::Ref::Child(
6823 fdecl::ChildRef {
6824 name: "netstack".to_string(),
6825 collection: Some("modular".to_string()),
6826 }
6827 )),
6828 target_name: Some("dict".to_string()),
6829 dependency_type: Some(fdecl::DependencyType::Strong),
6830 ..Default::default()
6831 }),
6832 ]);
6833 decl.capabilities = Some(vec![
6834 fdecl::Capability::Protocol(fdecl::Protocol {
6835 name: Some("fuchsia.logger.Log".to_string()),
6836 source_path: Some("/svc/logger".to_string()),
6837 ..Default::default()
6838 }),
6839 fdecl::Capability::Directory(fdecl::Directory {
6840 name: Some("assets".to_string()),
6841 source_path: Some("/data/assets".to_string()),
6842 rights: Some(fio::Operations::CONNECT),
6843 ..Default::default()
6844 }),
6845 ]);
6846 decl
6847 },
6848 result = Err(ErrorList::new(vec![
6849 Error::extraneous_field(DeclType::OfferService, "source.child.collection"),
6850 Error::extraneous_field(DeclType::OfferService, "target.child.collection"),
6851 Error::extraneous_field(DeclType::OfferProtocol, "source.child.collection"),
6852 Error::extraneous_field(DeclType::OfferProtocol, "target.child.collection"),
6853 Error::extraneous_field(DeclType::OfferDirectory, "source.child.collection"),
6854 Error::extraneous_field(DeclType::OfferDirectory, "target.child.collection"),
6855 Error::extraneous_field(DeclType::OfferStorage, "target.child.collection"),
6856 Error::extraneous_field(DeclType::OfferRunner, "source.child.collection"),
6857 Error::extraneous_field(DeclType::OfferRunner, "target.child.collection"),
6858 Error::extraneous_field(DeclType::OfferResolver, "source.child.collection"),
6859 Error::extraneous_field(DeclType::OfferResolver, "target.child.collection"),
6860 Error::extraneous_field(DeclType::OfferDictionary, "source.child.collection"),
6861 Error::extraneous_field(DeclType::OfferDictionary, "target.child.collection"),
6862 ])),
6863 },
6864 test_validate_offers_invalid_filtered_service_fields => {
6865 input = {
6866 let mut decl = new_component_decl();
6867 decl.offers = Some(vec![
6868 fdecl::Offer::Service(fdecl::OfferService {
6869 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6870 source_name: Some("fuchsia.logger.Log".to_string()),
6871 target: Some(fdecl::Ref::Child(
6872 fdecl::ChildRef {
6873 name: "logger".to_string(),
6874 collection: None,
6875 }
6876 )),
6877 target_name: Some("fuchsia.logger.Log".to_string()),
6878 source_instance_filter: Some(vec![]),
6879 ..Default::default()
6880 }),
6881 fdecl::Offer::Service(fdecl::OfferService {
6882 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6883 source_name: Some("fuchsia.logger.Log".to_string()),
6884 target: Some(fdecl::Ref::Child(
6885 fdecl::ChildRef {
6886 name: "logger".to_string(),
6887 collection: None,
6888 }
6889 )),
6890 target_name: Some("fuchsia.logger.Log2".to_string()),
6891 source_instance_filter: Some(vec!["^badname".to_string()]),
6892 ..Default::default()
6893 }),
6894 fdecl::Offer::Service(fdecl::OfferService {
6895 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6896 source_name: Some("fuchsia.logger.Log".to_string()),
6897 target: Some(fdecl::Ref::Child(
6898 fdecl::ChildRef {
6899 name: "logger".to_string(),
6900 collection: None,
6901 }
6902 )),
6903 target_name: Some("fuchsia.logger.Log1".to_string()),
6904 renamed_instances: Some(vec![fdecl::NameMapping{source_name: "a".to_string(), target_name: "b".to_string()}, fdecl::NameMapping{source_name: "c".to_string(), target_name: "b".to_string()}]),
6905 ..Default::default()
6906 }),
6907 fdecl::Offer::Service(fdecl::OfferService {
6908 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6909 source_name: Some("fuchsia.logger.Log".to_string()),
6910 target: Some(fdecl::Ref::Child(
6911 fdecl::ChildRef {
6912 name: "logger".to_string(),
6913 collection: None,
6914 }
6915 )),
6916 target_name: Some("fuchsia.logger.Log3".to_string()),
6917 renamed_instances: Some(vec![
6918 fdecl::NameMapping {
6919 source_name: "^badname".to_string(),
6920 target_name: "^badname".to_string(),
6921 }
6922 ]),
6923 ..Default::default()
6924 })
6925 ]);
6926 decl.children = Some(vec![
6927 fdecl::Child {
6928 name: Some("logger".to_string()),
6929 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
6930 startup: Some(fdecl::StartupMode::Lazy),
6931 on_terminate: None,
6932 environment: None,
6933 ..Default::default()
6934 },
6935 ]);
6936 decl
6937 },
6938 result = Err(ErrorList::new(vec![
6939 Error::invalid_field(DeclType::OfferService, "source_instance_filter"),
6940 Error::invalid_field(DeclType::OfferService, "source_instance_filter"),
6941 Error::invalid_field(DeclType::OfferService, "renamed_instances"),
6942 Error::invalid_field(DeclType::OfferService, "renamed_instances.source_name"),
6943 Error::invalid_field(DeclType::OfferService, "renamed_instances.target_name"),
6944 ])),
6945 },
6946 test_validate_offers_invalid_identifiers => {
6947 input = {
6948 let mut decl = new_component_decl();
6949 decl.offers = Some(vec![
6950 fdecl::Offer::Service(fdecl::OfferService {
6951 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6952 name: "^bad".to_string(),
6953 collection: None,
6954 })),
6955 source_name: Some("foo/".to_string()),
6956 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6957 name: "%bad".to_string(),
6958 collection: None,
6959 })),
6960 target_name: Some("/".to_string()),
6961 ..Default::default()
6962 }),
6963 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6964 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6965 name: "^bad".to_string(),
6966 collection: None,
6967 })),
6968 source_name: Some("foo/".to_string()),
6969 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6970 name: "%bad".to_string(),
6971 collection: None,
6972 })),
6973 target_name: Some("/".to_string()),
6974 dependency_type: Some(fdecl::DependencyType::Strong),
6975 ..Default::default()
6976 }),
6977 fdecl::Offer::Directory(fdecl::OfferDirectory {
6978 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6979 name: "^bad".to_string(),
6980 collection: None,
6981 })),
6982 source_name: Some("foo/".to_string()),
6983 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6984 name: "%bad".to_string(),
6985 collection: None,
6986 })),
6987 target_name: Some("/".to_string()),
6988 rights: Some(fio::Operations::CONNECT),
6989 subdir: Some("/foo".to_string()),
6990 dependency_type: Some(fdecl::DependencyType::Strong),
6991 ..Default::default()
6992 }),
6993 fdecl::Offer::Runner(fdecl::OfferRunner {
6994 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6995 name: "^bad".to_string(),
6996 collection: None,
6997 })),
6998 source_name: Some("/path".to_string()),
6999 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7000 name: "%bad".to_string(),
7001 collection: None,
7002 })),
7003 target_name: Some("elf!".to_string()),
7004 ..Default::default()
7005 }),
7006 fdecl::Offer::Resolver(fdecl::OfferResolver {
7007 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7008 name: "^bad".to_string(),
7009 collection: None,
7010 })),
7011 source_name: Some("/path".to_string()),
7012 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7013 name: "%bad".to_string(),
7014 collection: None,
7015 })),
7016 target_name: Some("pkg!".to_string()),
7017 ..Default::default()
7018 }),
7019 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7020 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7021 name: "^bad".to_string(),
7022 collection: None,
7023 })),
7024 source_name: Some("/path".to_string()),
7025 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7026 name: "%bad".to_string(),
7027 collection: None,
7028 })),
7029 target_name: Some("pkg!".to_string()),
7030 dependency_type: Some(fdecl::DependencyType::Strong),
7031 ..Default::default()
7032 }),
7033 ]);
7034 decl
7035 },
7036 result = Err(ErrorList::new(vec![
7037 Error::invalid_field(DeclType::OfferService, "source.child.name"),
7038 Error::invalid_field(DeclType::OfferService, "source_name"),
7039 Error::invalid_field(DeclType::OfferService, "target.child.name"),
7040 Error::invalid_field(DeclType::OfferService, "target_name"),
7041 Error::invalid_field(DeclType::OfferProtocol, "source.child.name"),
7042 Error::invalid_field(DeclType::OfferProtocol, "source_name"),
7043 Error::invalid_field(DeclType::OfferProtocol, "target.child.name"),
7044 Error::invalid_field(DeclType::OfferProtocol, "target_name"),
7045 Error::invalid_field(DeclType::OfferDirectory, "source.child.name"),
7046 Error::invalid_field(DeclType::OfferDirectory, "source_name"),
7047 Error::invalid_field(DeclType::OfferDirectory, "target.child.name"),
7048 Error::invalid_field(DeclType::OfferDirectory, "target_name"),
7049 Error::invalid_field(DeclType::OfferDirectory, "subdir"),
7050 Error::invalid_field(DeclType::OfferRunner, "source.child.name"),
7051 Error::invalid_field(DeclType::OfferRunner, "source_name"),
7052 Error::invalid_field(DeclType::OfferRunner, "target.child.name"),
7053 Error::invalid_field(DeclType::OfferRunner, "target_name"),
7054 Error::invalid_field(DeclType::OfferResolver, "source.child.name"),
7055 Error::invalid_field(DeclType::OfferResolver, "source_name"),
7056 Error::invalid_field(DeclType::OfferResolver, "target.child.name"),
7057 Error::invalid_field(DeclType::OfferResolver, "target_name"),
7058 Error::invalid_field(DeclType::OfferDictionary, "source.child.name"),
7059 Error::invalid_field(DeclType::OfferDictionary, "source_name"),
7060 Error::invalid_field(DeclType::OfferDictionary, "target.child.name"),
7061 Error::invalid_field(DeclType::OfferDictionary, "target_name"),
7062 ])),
7063 },
7064 test_validate_offers_target_equals_source => {
7065 input = {
7066 let mut decl = new_component_decl();
7067 decl.offers = Some(vec![
7068 fdecl::Offer::Service(fdecl::OfferService {
7069 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7070 name: "logger".to_string(),
7071 collection: None,
7072 })),
7073 source_name: Some("logger".to_string()),
7074 target: Some(fdecl::Ref::Child(
7075 fdecl::ChildRef {
7076 name: "logger".to_string(),
7077 collection: None,
7078 }
7079 )),
7080 target_name: Some("logger".to_string()),
7081 ..Default::default()
7082 }),
7083 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7084 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7085 name: "logger".to_string(),
7086 collection: None,
7087 })),
7088 source_name: Some("legacy_logger".to_string()),
7089 target: Some(fdecl::Ref::Child(
7090 fdecl::ChildRef {
7091 name: "logger".to_string(),
7092 collection: None,
7093 }
7094 )),
7095 target_name: Some("weak_legacy_logger".to_string()),
7096 dependency_type: Some(fdecl::DependencyType::Weak),
7097 ..Default::default()
7098 }),
7099 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7100 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7101 name: "logger".to_string(),
7102 collection: None,
7103 })),
7104 source_name: Some("legacy_logger".to_string()),
7105 target: Some(fdecl::Ref::Child(
7106 fdecl::ChildRef {
7107 name: "logger".to_string(),
7108 collection: None,
7109 }
7110 )),
7111 target_name: Some("strong_legacy_logger".to_string()),
7112 dependency_type: Some(fdecl::DependencyType::Strong),
7113 ..Default::default()
7114 }),
7115 fdecl::Offer::Directory(fdecl::OfferDirectory {
7116 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7117 name: "logger".to_string(),
7118 collection: None,
7119 })),
7120 source_name: Some("assets".to_string()),
7121 target: Some(fdecl::Ref::Child(
7122 fdecl::ChildRef {
7123 name: "logger".to_string(),
7124 collection: None,
7125 }
7126 )),
7127 target_name: Some("assets".to_string()),
7128 rights: Some(fio::Operations::CONNECT),
7129 subdir: None,
7130 dependency_type: Some(fdecl::DependencyType::Strong),
7131 ..Default::default()
7132 }),
7133 fdecl::Offer::Runner(fdecl::OfferRunner {
7134 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7135 name: "logger".to_string(),
7136 collection: None,
7137 })),
7138 source_name: Some("web".to_string()),
7139 target: Some(fdecl::Ref::Child(
7140 fdecl::ChildRef {
7141 name: "logger".to_string(),
7142 collection: None,
7143 }
7144 )),
7145 target_name: Some("web".to_string()),
7146 ..Default::default()
7147 }),
7148 fdecl::Offer::Resolver(fdecl::OfferResolver {
7149 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7150 name: "logger".to_string(),
7151 collection: None,
7152 })),
7153 source_name: Some("pkg".to_string()),
7154 target: Some(fdecl::Ref::Child(
7155 fdecl::ChildRef {
7156 name: "logger".to_string(),
7157 collection: None,
7158 }
7159 )),
7160 target_name: Some("pkg".to_string()),
7161 ..Default::default()
7162 }),
7163 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7164 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7165 name: "logger".to_string(),
7166 collection: None,
7167 })),
7168 source_name: Some("dict".to_string()),
7169 target: Some(fdecl::Ref::Child(
7170 fdecl::ChildRef {
7171 name: "logger".to_string(),
7172 collection: None,
7173 }
7174 )),
7175 target_name: Some("dict".to_string()),
7176 dependency_type: Some(fdecl::DependencyType::Strong),
7177 ..Default::default()
7178 }),
7179 ]);
7180 decl.children = Some(vec![fdecl::Child{
7181 name: Some("logger".to_string()),
7182 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
7183 startup: Some(fdecl::StartupMode::Lazy),
7184 on_terminate: None,
7185 environment: None,
7186 ..Default::default()
7187 }]);
7188 decl
7189 },
7190 result = Err(ErrorList::new(vec![
7191 Error::dependency_cycle("{{child logger -> child logger}}"),
7192 ])),
7193 },
7194 test_validate_offers_storage_target_equals_source => {
7195 input = fdecl::Component {
7196 offers: Some(vec![
7197 fdecl::Offer::Storage(fdecl::OfferStorage {
7198 source_name: Some("data".to_string()),
7199 source: Some(fdecl::Ref::Self_(fdecl::SelfRef { })),
7200 target: Some(fdecl::Ref::Child(
7201 fdecl::ChildRef {
7202 name: "logger".to_string(),
7203 collection: None,
7204 }
7205 )),
7206 target_name: Some("data".to_string()),
7207 ..Default::default()
7208 })
7209 ]),
7210 capabilities: Some(vec![
7211 fdecl::Capability::Storage(fdecl::Storage {
7212 name: Some("data".to_string()),
7213 backing_dir: Some("minfs".to_string()),
7214 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7215 name: "logger".to_string(),
7216 collection: None,
7217 })),
7218 subdir: None,
7219 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
7220 ..Default::default()
7221 }),
7222 ]),
7223 children: Some(vec![
7224 fdecl::Child {
7225 name: Some("logger".to_string()),
7226 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
7227 startup: Some(fdecl::StartupMode::Lazy),
7228 on_terminate: None,
7229 environment: None,
7230 ..Default::default()
7231 },
7232 ]),
7233 ..new_component_decl()
7234 },
7235 result = Err(ErrorList::new(vec![
7236 Error::dependency_cycle("{{child logger -> capability data -> child logger}}"),
7237 ])),
7238 },
7239 test_validate_offers_invalid_child => {
7240 input = {
7241 let mut decl = new_component_decl();
7242 decl.offers = Some(vec![
7243 fdecl::Offer::Service(fdecl::OfferService {
7244 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7245 name: "logger".to_string(),
7246 collection: None,
7247 })),
7248 source_name: Some("fuchsia.logger.Log".to_string()),
7249 target: Some(fdecl::Ref::Child(
7250 fdecl::ChildRef {
7251 name: "netstack".to_string(),
7252 collection: None,
7253 }
7254 )),
7255 target_name: Some("fuchsia.logger.Log".to_string()),
7256 ..Default::default()
7257 }),
7258 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7259 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7260 name: "logger".to_string(),
7261 collection: None,
7262 })),
7263 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
7264 target: Some(fdecl::Ref::Child(
7265 fdecl::ChildRef {
7266 name: "netstack".to_string(),
7267 collection: None,
7268 }
7269 )),
7270 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7271 dependency_type: Some(fdecl::DependencyType::Strong),
7272 ..Default::default()
7273 }),
7274 fdecl::Offer::Directory(fdecl::OfferDirectory {
7275 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7276 name: "logger".to_string(),
7277 collection: None,
7278 })),
7279 source_name: Some("assets".to_string()),
7280 target: Some(fdecl::Ref::Collection(
7281 fdecl::CollectionRef { name: "modular".to_string() }
7282 )),
7283 target_name: Some("assets".to_string()),
7284 rights: Some(fio::Operations::CONNECT),
7285 subdir: None,
7286 dependency_type: Some(fdecl::DependencyType::Weak),
7287 ..Default::default()
7288 }),
7289 ]);
7290 decl.capabilities = Some(vec![
7291 fdecl::Capability::Storage(fdecl::Storage {
7292 name: Some("memfs".to_string()),
7293 backing_dir: Some("memfs".to_string()),
7294 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7295 name: "logger".to_string(),
7296 collection: None,
7297 })),
7298 subdir: None,
7299 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
7300 ..Default::default()
7301 }),
7302 ]);
7303 decl.children = Some(vec![
7304 fdecl::Child {
7305 name: Some("netstack".to_string()),
7306 url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
7307 startup: Some(fdecl::StartupMode::Lazy),
7308 on_terminate: None,
7309 environment: None,
7310 ..Default::default()
7311 },
7312 ]);
7313 decl.collections = Some(vec![
7314 fdecl::Collection {
7315 name: Some("modular".to_string()),
7316 durability: Some(fdecl::Durability::Transient),
7317 environment: None,
7318 allowed_offers: Some(fdecl::AllowedOffers::StaticAndDynamic),
7319 allow_long_names: None,
7320 ..Default::default()
7321 },
7322 ]);
7323 decl
7324 },
7325 result = Err(ErrorList::new(vec![
7326 Error::invalid_child(DeclType::Storage, "source", "logger"),
7327 Error::invalid_child(DeclType::OfferService, "source", "logger"),
7328 Error::invalid_child(DeclType::OfferProtocol, "source", "logger"),
7329 Error::invalid_child(DeclType::OfferDirectory, "source", "logger"),
7330 ])),
7331 },
7332 test_validate_offers_invalid_source_capability => {
7333 input = {
7334 fdecl::Component {
7335 offers: Some(vec![
7336 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7337 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
7338 name: "this-storage-doesnt-exist".to_string(),
7339 })),
7340 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
7341 target: Some(fdecl::Ref::Child(
7342 fdecl::ChildRef {
7343 name: "netstack".to_string(),
7344 collection: None,
7345 }
7346 )),
7347 target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
7348 dependency_type: Some(fdecl::DependencyType::Strong),
7349 ..Default::default()
7350 }),
7351 ]),
7352 ..new_component_decl()
7353 }
7354 },
7355 result = Err(ErrorList::new(vec![
7356 Error::invalid_capability(DeclType::OfferProtocol, "source", "this-storage-doesnt-exist"),
7357 Error::invalid_child(DeclType::OfferProtocol, "target", "netstack"),
7358 ])),
7359 },
7360 test_validate_offers_target => {
7361 input = {
7362 let mut decl = new_component_decl();
7363 decl.offers = Some(vec![
7364 fdecl::Offer::Service(fdecl::OfferService {
7365 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
7366 name: "modular".into()
7367 })),
7368 source_name: Some("logger".to_string()),
7369 target: Some(fdecl::Ref::Child(
7370 fdecl::ChildRef {
7371 name: "netstack".to_string(),
7372 collection: None,
7373 }
7374 )),
7375 target_name: Some("fuchsia.logger.Log".to_string()),
7376 ..Default::default()
7377 }),
7378 fdecl::Offer::Service(fdecl::OfferService {
7379 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
7380 name: "modular".into()
7381 })),
7382 source_name: Some("logger".to_string()),
7383 target: Some(fdecl::Ref::Child(
7384 fdecl::ChildRef {
7385 name: "netstack".to_string(),
7386 collection: None,
7387 }
7388 )),
7389 target_name: Some("fuchsia.logger.Log".to_string()),
7390 ..Default::default()
7391 }),
7392 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7393 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7394 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
7395 target: Some(fdecl::Ref::Child(
7396 fdecl::ChildRef {
7397 name: "netstack".to_string(),
7398 collection: None,
7399 }
7400 )),
7401 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7402 dependency_type: Some(fdecl::DependencyType::Strong),
7403 ..Default::default()
7404 }),
7405 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7406 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7407 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
7408 target: Some(fdecl::Ref::Child(
7409 fdecl::ChildRef {
7410 name: "netstack".to_string(),
7411 collection: None,
7412 }
7413 )),
7414 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7415 dependency_type: Some(fdecl::DependencyType::Strong),
7416 ..Default::default()
7417 }),
7418 fdecl::Offer::Directory(fdecl::OfferDirectory {
7419 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7420 source_name: Some("assets".to_string()),
7421 target: Some(fdecl::Ref::Collection(
7422 fdecl::CollectionRef { name: "modular".to_string() }
7423 )),
7424 target_name: Some("assets".to_string()),
7425 rights: Some(fio::Operations::CONNECT),
7426 subdir: None,
7427 dependency_type: Some(fdecl::DependencyType::Strong),
7428 ..Default::default()
7429 }),
7430 fdecl::Offer::Directory(fdecl::OfferDirectory {
7431 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7432 source_name: Some("assets".to_string()),
7433 target: Some(fdecl::Ref::Collection(
7434 fdecl::CollectionRef { name: "modular".to_string() }
7435 )),
7436 target_name: Some("assets".to_string()),
7437 rights: Some(fio::Operations::CONNECT),
7438 subdir: None,
7439 dependency_type: Some(fdecl::DependencyType::Weak),
7440 ..Default::default()
7441 }),
7442 fdecl::Offer::Storage(fdecl::OfferStorage {
7443 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7444 source_name: Some("data".to_string()),
7445 target: Some(fdecl::Ref::Collection(
7446 fdecl::CollectionRef { name: "modular".to_string() }
7447 )),
7448 target_name: Some("data".to_string()),
7449 ..Default::default()
7450 }),
7451 fdecl::Offer::Storage(fdecl::OfferStorage {
7452 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7453 source_name: Some("data".to_string()),
7454 target: Some(fdecl::Ref::Collection(
7455 fdecl::CollectionRef { name: "modular".to_string() }
7456 )),
7457 target_name: Some("data".to_string()),
7458 ..Default::default()
7459 }),
7460 fdecl::Offer::Runner(fdecl::OfferRunner {
7461 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7462 source_name: Some("elf".to_string()),
7463 target: Some(fdecl::Ref::Collection(
7464 fdecl::CollectionRef { name: "modular".to_string() }
7465 )),
7466 target_name: Some("duplicated".to_string()),
7467 ..Default::default()
7468 }),
7469 fdecl::Offer::Runner(fdecl::OfferRunner {
7470 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7471 source_name: Some("elf".to_string()),
7472 target: Some(fdecl::Ref::Collection(
7473 fdecl::CollectionRef { name: "modular".to_string() }
7474 )),
7475 target_name: Some("duplicated".to_string()),
7476 ..Default::default()
7477 }),
7478 fdecl::Offer::Resolver(fdecl::OfferResolver {
7479 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7480 source_name: Some("pkg".to_string()),
7481 target: Some(fdecl::Ref::Collection(
7482 fdecl::CollectionRef { name: "modular".to_string() }
7483 )),
7484 target_name: Some("duplicated".to_string()),
7485 ..Default::default()
7486 }),
7487 fdecl::Offer::EventStream(fdecl::OfferEventStream {
7488 source_name: Some("started".to_string()),
7489 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7490 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "netstack".to_string(), collection: None})),
7491 target_name: Some("started".to_string()),
7492 ..Default::default()
7493 }),
7494 fdecl::Offer::EventStream(fdecl::OfferEventStream {
7495 source_name: Some("started".to_string()),
7496 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7497 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "netstack".to_string(), collection: None})),
7498 target_name: Some("started".to_string()),
7499 ..Default::default()
7500 }),
7501 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7502 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7503 source_name: Some("a".to_string()),
7504 target: Some(fdecl::Ref::Collection(
7505 fdecl::CollectionRef { name: "modular".to_string() }
7506 )),
7507 target_name: Some("dict".to_string()),
7508 dependency_type: Some(fdecl::DependencyType::Strong),
7509 ..Default::default()
7510 }),
7511 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7512 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7513 source_name: Some("b".to_string()),
7514 target: Some(fdecl::Ref::Collection(
7515 fdecl::CollectionRef { name: "modular".to_string() }
7516 )),
7517 target_name: Some("dict".to_string()),
7518 dependency_type: Some(fdecl::DependencyType::Strong),
7519 ..Default::default()
7520 }),
7521 ]);
7522 decl.children = Some(vec![
7523 fdecl::Child{
7524 name: Some("netstack".to_string()),
7525 url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
7526 startup: Some(fdecl::StartupMode::Eager),
7527 on_terminate: None,
7528 environment: None,
7529 ..Default::default()
7530 },
7531 ]);
7532 decl.collections = Some(vec![
7533 fdecl::Collection{
7534 name: Some("modular".to_string()),
7535 durability: Some(fdecl::Durability::Transient),
7536 environment: None,
7537 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7538 allow_long_names: None,
7539 ..Default::default()
7540 },
7541 ]);
7542 decl
7543 },
7544 result = Err(ErrorList::new(vec![
7545 Error::duplicate_field(DeclType::OfferProtocol, "target_name", "fuchsia.logger.LegacyLog"),
7547 Error::duplicate_field(DeclType::OfferDirectory, "target_name", "assets"),
7548 Error::duplicate_field(DeclType::OfferStorage, "target_name", "data"),
7549 Error::duplicate_field(DeclType::OfferRunner, "target_name", "duplicated"),
7550 Error::duplicate_field(DeclType::OfferResolver, "target_name", "duplicated"),
7551 Error::duplicate_field(DeclType::OfferEventStream, "target_name", "started"),
7552 Error::duplicate_field(DeclType::OfferDictionary, "target_name", "dict"),
7553 ])),
7554 },
7555 test_validate_offers_target_invalid => {
7556 input = {
7557 let mut decl = new_component_decl();
7558 decl.offers = Some(vec![
7559 fdecl::Offer::Service(fdecl::OfferService {
7560 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7561 source_name: Some("logger".to_string()),
7562 target: Some(fdecl::Ref::Child(
7563 fdecl::ChildRef {
7564 name: "netstack".to_string(),
7565 collection: None,
7566 }
7567 )),
7568 target_name: Some("fuchsia.logger.Log".to_string()),
7569 ..Default::default()
7570 }),
7571 fdecl::Offer::Service(fdecl::OfferService {
7572 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7573 source_name: Some("logger".to_string()),
7574 target: Some(fdecl::Ref::Collection(
7575 fdecl::CollectionRef { name: "modular".to_string(), }
7576 )),
7577 target_name: Some("fuchsia.logger.Log".to_string()),
7578 ..Default::default()
7579 }),
7580 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7581 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7582 source_name: Some("legacy_logger".to_string()),
7583 target: Some(fdecl::Ref::Child(
7584 fdecl::ChildRef {
7585 name: "netstack".to_string(),
7586 collection: None,
7587 }
7588 )),
7589 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7590 dependency_type: Some(fdecl::DependencyType::Weak),
7591 ..Default::default()
7592 }),
7593 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7594 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7595 source_name: Some("legacy_logger".to_string()),
7596 target: Some(fdecl::Ref::Collection(
7597 fdecl::CollectionRef { name: "modular".to_string(), }
7598 )),
7599 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7600 dependency_type: Some(fdecl::DependencyType::Strong),
7601 ..Default::default()
7602 }),
7603 fdecl::Offer::Directory(fdecl::OfferDirectory {
7604 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7605 source_name: Some("assets".to_string()),
7606 target: Some(fdecl::Ref::Child(
7607 fdecl::ChildRef {
7608 name: "netstack".to_string(),
7609 collection: None,
7610 }
7611 )),
7612 target_name: Some("data".to_string()),
7613 rights: Some(fio::Operations::CONNECT),
7614 subdir: None,
7615 dependency_type: Some(fdecl::DependencyType::Strong),
7616 ..Default::default()
7617 }),
7618 fdecl::Offer::Directory(fdecl::OfferDirectory {
7619 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7620 source_name: Some("assets".to_string()),
7621 target: Some(fdecl::Ref::Collection(
7622 fdecl::CollectionRef { name: "modular".to_string(), }
7623 )),
7624 target_name: Some("data".to_string()),
7625 rights: Some(fio::Operations::CONNECT),
7626 subdir: None,
7627 dependency_type: Some(fdecl::DependencyType::Weak),
7628 ..Default::default()
7629 }),
7630 fdecl::Offer::Storage(fdecl::OfferStorage {
7631 source_name: Some("data".to_string()),
7632 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7633 target: Some(fdecl::Ref::Child(
7634 fdecl::ChildRef {
7635 name: "netstack".to_string(),
7636 collection: None,
7637 }
7638 )),
7639 target_name: Some("data".to_string()),
7640 ..Default::default()
7641 }),
7642 fdecl::Offer::Storage(fdecl::OfferStorage {
7643 source_name: Some("data".to_string()),
7644 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7645 target: Some(fdecl::Ref::Collection(
7646 fdecl::CollectionRef { name: "modular".to_string(), }
7647 )),
7648 target_name: Some("data".to_string()),
7649 ..Default::default()
7650 }),
7651 fdecl::Offer::Runner(fdecl::OfferRunner {
7652 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7653 source_name: Some("elf".to_string()),
7654 target: Some(fdecl::Ref::Child(
7655 fdecl::ChildRef {
7656 name: "netstack".to_string(),
7657 collection: None,
7658 }
7659 )),
7660 target_name: Some("elf".to_string()),
7661 ..Default::default()
7662 }),
7663 fdecl::Offer::Runner(fdecl::OfferRunner {
7664 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7665 source_name: Some("elf".to_string()),
7666 target: Some(fdecl::Ref::Collection(
7667 fdecl::CollectionRef { name: "modular".to_string(), }
7668 )),
7669 target_name: Some("elf".to_string()),
7670 ..Default::default()
7671 }),
7672 fdecl::Offer::Resolver(fdecl::OfferResolver {
7673 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7674 source_name: Some("pkg".to_string()),
7675 target: Some(fdecl::Ref::Child(
7676 fdecl::ChildRef {
7677 name: "netstack".to_string(),
7678 collection: None,
7679 }
7680 )),
7681 target_name: Some("pkg".to_string()),
7682 ..Default::default()
7683 }),
7684 fdecl::Offer::Resolver(fdecl::OfferResolver {
7685 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7686 source_name: Some("pkg".to_string()),
7687 target: Some(fdecl::Ref::Collection(
7688 fdecl::CollectionRef { name: "modular".to_string(), }
7689 )),
7690 target_name: Some("pkg".to_string()),
7691 ..Default::default()
7692 }),
7693 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7694 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7695 source_name: Some("pkg".to_string()),
7696 target: Some(fdecl::Ref::Child(
7697 fdecl::ChildRef {
7698 name: "netstack".to_string(),
7699 collection: None,
7700 }
7701 )),
7702 target_name: Some("pkg".to_string()),
7703 dependency_type: Some(fdecl::DependencyType::Strong),
7704 ..Default::default()
7705 }),
7706 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7707 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7708 source_name: Some("pkg".to_string()),
7709 target: Some(fdecl::Ref::Collection(
7710 fdecl::CollectionRef { name: "modular".to_string(), }
7711 )),
7712 target_name: Some("pkg".to_string()),
7713 dependency_type: Some(fdecl::DependencyType::Strong),
7714 ..Default::default()
7715 }),
7716 ]);
7717 decl
7718 },
7719 result = Err(ErrorList::new(vec![
7720 Error::invalid_child(DeclType::OfferService, "target", "netstack"),
7721 Error::invalid_collection(DeclType::OfferService, "target", "modular"),
7722 Error::invalid_child(DeclType::OfferProtocol, "target", "netstack"),
7723 Error::invalid_collection(DeclType::OfferProtocol, "target", "modular"),
7724 Error::invalid_child(DeclType::OfferDirectory, "target", "netstack"),
7725 Error::invalid_collection(DeclType::OfferDirectory, "target", "modular"),
7726 Error::invalid_child(DeclType::OfferStorage, "target", "netstack"),
7727 Error::invalid_collection(DeclType::OfferStorage, "target", "modular"),
7728 Error::invalid_child(DeclType::OfferRunner, "target", "netstack"),
7729 Error::invalid_collection(DeclType::OfferRunner, "target", "modular"),
7730 Error::invalid_child(DeclType::OfferResolver, "target", "netstack"),
7731 Error::invalid_collection(DeclType::OfferResolver, "target", "modular"),
7732 Error::invalid_child(DeclType::OfferDictionary, "target", "netstack"),
7733 Error::invalid_collection(DeclType::OfferDictionary, "target", "modular"),
7734 ])),
7735 },
7736 test_validate_offers_target_dictionary => {
7737 input = fdecl::Component {
7738 offers: Some(vec![
7739 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7741 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7742 source_name: Some("p".to_string()),
7743 target: Some(fdecl::Ref::Capability(
7744 fdecl::CapabilityRef {
7745 name: "dict".into(),
7746 },
7747 )),
7748 target_name: Some("p".into()),
7749 dependency_type: Some(fdecl::DependencyType::Strong),
7750 ..Default::default()
7751 }),
7752 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7754 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7755 source_name: Some("p".to_string()),
7756 target: Some(fdecl::Ref::Capability(
7757 fdecl::CapabilityRef {
7758 name: "dynamic".into(),
7759 },
7760 )),
7761 target_name: Some("p".into()),
7762 dependency_type: Some(fdecl::DependencyType::Strong),
7763 ..Default::default()
7764 }),
7765 ]),
7766 capabilities: Some(vec![
7767 fdecl::Capability::Dictionary(fdecl::Dictionary {
7768 name: Some("dict".into()),
7769 ..Default::default()
7770 }),
7771 fdecl::Capability::Dictionary(fdecl::Dictionary {
7772 name: Some("dynamic".into()),
7773 source_path: Some("/out/dir".into()),
7774 ..Default::default()
7775 }),
7776 ]),
7777 ..Default::default()
7778 },
7779 result = Err(ErrorList::new(vec![
7780 Error::invalid_field(DeclType::OfferProtocol, "target"),
7781 ])),
7782 },
7783 test_validate_offers_invalid_source_collection => {
7784 input = {
7785 let mut decl = new_component_decl();
7786 decl.collections = Some(vec![
7787 fdecl::Collection {
7788 name: Some("col".to_string()),
7789 durability: Some(fdecl::Durability::Transient),
7790 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7791 allow_long_names: None,
7792 ..Default::default()
7793 }
7794 ]);
7795 decl.children = Some(vec![
7796 fdecl::Child {
7797 name: Some("child".to_string()),
7798 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7799 startup: Some(fdecl::StartupMode::Lazy),
7800 on_terminate: None,
7801 ..Default::default()
7802 }
7803 ]);
7804 decl.offers = Some(vec![
7805 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7806 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7807 source_name: Some("a".to_string()),
7808 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7809 target_name: Some("a".to_string()),
7810 dependency_type: Some(fdecl::DependencyType::Strong),
7811 ..Default::default()
7812 }),
7813 fdecl::Offer::Directory(fdecl::OfferDirectory {
7814 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7815 source_name: Some("b".to_string()),
7816 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7817 target_name: Some("b".to_string()),
7818 rights: Some(fio::Operations::CONNECT),
7819 subdir: None,
7820 dependency_type: Some(fdecl::DependencyType::Strong),
7821 ..Default::default()
7822 }),
7823 fdecl::Offer::Storage(fdecl::OfferStorage {
7824 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7825 source_name: Some("c".to_string()),
7826 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7827 target_name: Some("c".to_string()),
7828 ..Default::default()
7829 }),
7830 fdecl::Offer::Runner(fdecl::OfferRunner {
7831 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7832 source_name: Some("d".to_string()),
7833 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7834 target_name: Some("d".to_string()),
7835 ..Default::default()
7836 }),
7837 fdecl::Offer::Resolver(fdecl::OfferResolver {
7838 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7839 source_name: Some("e".to_string()),
7840 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7841 target_name: Some("e".to_string()),
7842 ..Default::default()
7843 }),
7844 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7845 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7846 source_name: Some("f".to_string()),
7847 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7848 target_name: Some("f".to_string()),
7849 dependency_type: Some(fdecl::DependencyType::Strong),
7850 ..Default::default()
7851 }),
7852 ]);
7853 decl
7854 },
7855 result = Err(ErrorList::new(vec![
7856 Error::invalid_field(DeclType::OfferProtocol, "source"),
7857 Error::invalid_field(DeclType::OfferDirectory, "source"),
7858 Error::invalid_field(DeclType::OfferStorage, "source"),
7859 Error::invalid_field(DeclType::OfferRunner, "source"),
7860 Error::invalid_field(DeclType::OfferResolver, "source"),
7861 Error::invalid_field(DeclType::OfferDictionary, "source"),
7862 ])),
7863 },
7864 test_validate_offers_source_collection => {
7865 input = {
7866 let mut decl = new_component_decl();
7867 decl.collections = Some(vec![
7868 fdecl::Collection {
7869 name: Some("col".to_string()),
7870 durability: Some(fdecl::Durability::Transient),
7871 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7872 allow_long_names: None,
7873 ..Default::default()
7874 }
7875 ]);
7876 decl.children = Some(vec![
7877 fdecl::Child {
7878 name: Some("child".to_string()),
7879 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7880 startup: Some(fdecl::StartupMode::Lazy),
7881 on_terminate: None,
7882 ..Default::default()
7883 }
7884 ]);
7885 decl.offers = Some(vec![
7886 fdecl::Offer::Service(fdecl::OfferService {
7887 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7888 source_name: Some("a".to_string()),
7889 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7890 target_name: Some("a".to_string()),
7891 ..Default::default()
7892 })
7893 ]);
7894 decl
7895 },
7896 result = Ok(()),
7897 },
7898 test_validate_offers_invalid_capability_from_self => {
7899 input = {
7900 let mut decl = new_component_decl();
7901 decl.children = Some(vec![
7902 fdecl::Child {
7903 name: Some("child".to_string()),
7904 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7905 startup: Some(fdecl::StartupMode::Lazy),
7906 ..Default::default()
7907 }
7908 ]);
7909 decl.offers = Some(vec![
7910 fdecl::Offer::Service(fdecl::OfferService {
7911 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7912 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7913 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7914 name: "child".into(),
7915 collection: None
7916 })),
7917 target_name: Some("foo".into()),
7918 ..Default::default()
7919 }),
7920 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7921 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7922 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7923 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7924 name: "child".into(),
7925 collection: None
7926 })),
7927 target_name: Some("bar".into()),
7928 dependency_type: Some(fdecl::DependencyType::Strong),
7929 ..Default::default()
7930 }),
7931 fdecl::Offer::Directory(fdecl::OfferDirectory {
7932 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7933 source_name: Some("dir".into()),
7934 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7935 name: "child".into(),
7936 collection: None
7937 })),
7938 target_name: Some("assets".into()),
7939 dependency_type: Some(fdecl::DependencyType::Strong),
7940 ..Default::default()
7941 }),
7942 fdecl::Offer::Runner(fdecl::OfferRunner {
7943 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7944 source_name: Some("source_elf".into()),
7945 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7946 name: "child".into(),
7947 collection: None
7948 })),
7949 target_name: Some("elf".into()),
7950 ..Default::default()
7951 }),
7952 fdecl::Offer::Resolver(fdecl::OfferResolver {
7953 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7954 source_name: Some("source_pkg".into()),
7955 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7956 name: "child".into(),
7957 collection: None
7958 })),
7959 target_name: Some("pkg".into()),
7960 ..Default::default()
7961 }),
7962 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7963 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7964 source_name: Some("source_dict".into()),
7965 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7966 name: "child".into(),
7967 collection: None
7968 })),
7969 target_name: Some("dict".into()),
7970 dependency_type: Some(fdecl::DependencyType::Strong),
7971 ..Default::default()
7972 }),
7973 fdecl::Offer::Storage(fdecl::OfferStorage {
7974 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7975 source_name: Some("source_storage".into()),
7976 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7977 name: "child".into(),
7978 collection: None
7979 })),
7980 target_name: Some("storage".into()),
7981 ..Default::default()
7982 }),
7983 fdecl::Offer::Config(fdecl::OfferConfiguration {
7984 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7985 source_name: Some("source_config".into()),
7986 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7987 name: "child".into(),
7988 collection: None
7989 })),
7990 target_name: Some("config".into()),
7991 ..Default::default()
7992 }),
7993 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7994 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7995 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7996 source_dictionary: Some("dict/inner".into()),
7997 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7998 name: "child".into(),
7999 collection: None
8000 })),
8001 target_name: Some("baz".into()),
8002 dependency_type: Some(fdecl::DependencyType::Strong),
8003 ..Default::default()
8004 }),
8005 ]);
8006 decl
8007 },
8008 result = Err(ErrorList::new(vec![
8009 Error::invalid_capability(
8010 DeclType::OfferService,
8011 "source",
8012 "fuchsia.some.library.SomeProtocol"),
8013 Error::invalid_capability(
8014 DeclType::OfferProtocol,
8015 "source",
8016 "fuchsia.some.library.SomeProtocol"),
8017 Error::invalid_capability(DeclType::OfferDirectory, "source", "dir"),
8018 Error::invalid_capability(DeclType::OfferRunner, "source", "source_elf"),
8019 Error::invalid_capability(DeclType::OfferResolver, "source", "source_pkg"),
8020 Error::invalid_capability(DeclType::OfferDictionary, "source", "source_dict"),
8021 Error::invalid_capability(DeclType::OfferStorage, "source", "source_storage"),
8022 Error::invalid_capability(DeclType::OfferConfig, "source", "source_config"),
8023 Error::invalid_capability(DeclType::OfferProtocol, "source", "dict"),
8024 ])),
8025 },
8026 test_validate_offers_long_dependency_cycle => {
8027 input = {
8028 let mut decl = new_component_decl();
8029 let dependencies = vec![
8030 ("d", "b"),
8031 ("a", "b"),
8032 ("b", "c"),
8033 ("b", "d"),
8034 ("c", "a"),
8035 ];
8036 let offers = dependencies.into_iter().map(|(from,to)|
8037 fdecl::Offer::Protocol(fdecl::OfferProtocol {
8038 source: Some(fdecl::Ref::Child(
8039 fdecl::ChildRef { name: from.to_string(), collection: None },
8040 )),
8041 source_name: Some(format!("thing_{}", from)),
8042 target: Some(fdecl::Ref::Child(
8043 fdecl::ChildRef { name: to.to_string(), collection: None },
8044 )),
8045 target_name: Some(format!("thing_{}", from)),
8046 dependency_type: Some(fdecl::DependencyType::Strong),
8047 ..Default::default()
8048 })).collect();
8049 let children = ["a", "b", "c", "d"].iter().map(|name| {
8050 fdecl::Child {
8051 name: Some(name.to_string()),
8052 url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
8053 startup: Some(fdecl::StartupMode::Lazy),
8054 on_terminate: None,
8055 environment: None,
8056 ..Default::default()
8057 }
8058 }).collect();
8059 decl.offers = Some(offers);
8060 decl.children = Some(children);
8061 decl
8062 },
8063 result = Err(ErrorList::new(vec![
8064 Error::dependency_cycle("{{child a -> child b -> child c -> child a}, {child b -> child d -> child b}}")
8065 ])),
8066 },
8067 test_validate_offers_not_required_invalid_source_service => {
8068 input = {
8069 let mut decl = generate_offer_different_source_and_availability_decl(
8070 |source, availability, target_name|
8071 fdecl::Offer::Service(fdecl::OfferService {
8072 source: Some(source),
8073 source_name: Some("fuchsia.examples.Echo".to_string()),
8074 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8075 name: "sink".to_string(),
8076 collection: None,
8077 })),
8078 target_name: Some(target_name.into()),
8079 availability: Some(availability),
8080 ..Default::default()
8081 })
8082 );
8083 decl.capabilities = Some(vec![
8084 fdecl::Capability::Service(fdecl::Service {
8085 name: Some("fuchsia.examples.Echo".to_string()),
8086 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
8087 ..Default::default()
8088 }),
8089 ]);
8090 decl
8091 },
8092 result = {
8093 Err(ErrorList::new(vec![
8094 Error::availability_must_be_optional(
8095 DeclType::OfferService,
8096 "availability",
8097 Some(&"fuchsia.examples.Echo".to_string()),
8098 ),
8099 Error::availability_must_be_optional(
8100 DeclType::OfferService,
8101 "availability",
8102 Some(&"fuchsia.examples.Echo".to_string()),
8103 ),
8104 ]))
8105 },
8106 },
8107 test_validate_offers_not_required_invalid_source_protocol => {
8108 input = {
8109 let mut decl = generate_offer_different_source_and_availability_decl(
8110 |source, availability, target_name|
8111 fdecl::Offer::Protocol(fdecl::OfferProtocol {
8112 source: Some(source),
8113 source_name: Some("fuchsia.examples.Echo".to_string()),
8114 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8115 name: "sink".to_string(),
8116 collection: None,
8117 })),
8118 target_name: Some(target_name.into()),
8119 dependency_type: Some(fdecl::DependencyType::Strong),
8120 availability: Some(availability),
8121 ..Default::default()
8122 })
8123 );
8124 decl.capabilities = Some(vec![
8125 fdecl::Capability::Protocol(fdecl::Protocol {
8126 name: Some("fuchsia.examples.Echo".to_string()),
8127 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
8128 ..Default::default()
8129 }),
8130 ]);
8131 decl
8132 },
8133 result = {
8134 Err(ErrorList::new(vec![
8135 Error::availability_must_be_optional(
8136 DeclType::OfferProtocol,
8137 "availability",
8138 Some(&"fuchsia.examples.Echo".to_string()),
8139 ),
8140 Error::availability_must_be_optional(
8141 DeclType::OfferProtocol,
8142 "availability",
8143 Some(&"fuchsia.examples.Echo".to_string()),
8144 ),
8145 ]))
8146 },
8147 },
8148 test_validate_offers_not_required_invalid_source_directory => {
8149 input = {
8150 let mut decl = generate_offer_different_source_and_availability_decl(
8151 |source, availability, target_name|
8152 fdecl::Offer::Directory(fdecl::OfferDirectory {
8153 source: Some(source),
8154 source_name: Some("assets".to_string()),
8155 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8156 name: "sink".to_string(),
8157 collection: None,
8158 })),
8159 target_name: Some(target_name.into()),
8160 rights: Some(fio::Operations::CONNECT),
8161 subdir: None,
8162 dependency_type: Some(fdecl::DependencyType::Weak),
8163 availability: Some(availability),
8164 ..Default::default()
8165 })
8166 );
8167 decl.capabilities = Some(vec![
8168 fdecl::Capability::Directory(fdecl::Directory {
8169 name: Some("assets".to_string()),
8170 source_path: Some("/assets".to_string()),
8171 rights: Some(fio::Operations::CONNECT),
8172 ..Default::default()
8173 }),
8174 ]);
8175 decl
8176 },
8177 result = {
8178 Err(ErrorList::new(vec![
8179 Error::availability_must_be_optional(
8180 DeclType::OfferDirectory,
8181 "availability",
8182 Some(&"assets".to_string()),
8183 ),
8184 Error::availability_must_be_optional(
8185 DeclType::OfferDirectory,
8186 "availability",
8187 Some(&"assets".to_string()),
8188 ),
8189 ]))
8190 },
8191 },
8192 test_validate_offers_not_required_invalid_source_storage => {
8193 input = {
8194 let mut decl = new_component_decl();
8195 decl.children = Some(vec![
8196 fdecl::Child {
8197 name: Some("sink".to_string()),
8198 url: Some("fuchsia-pkg://fuchsia.com/sink#meta/sink.cm".to_string()),
8199 startup: Some(fdecl::StartupMode::Lazy),
8200 on_terminate: None,
8201 environment: None,
8202 ..Default::default()
8203 },
8204 ]);
8205 decl.capabilities = Some(vec![
8206 fdecl::Capability::Storage(fdecl::Storage {
8207 name: Some("data".to_string()),
8208 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8209 backing_dir: Some("minfs".to_string()),
8210 subdir: None,
8211 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
8212 ..Default::default()
8213 }),
8214 ]);
8215 let new_offer = |source: fdecl::Ref, availability: fdecl::Availability,
8216 target_name: &str|
8217 {
8218 fdecl::Offer::Storage(fdecl::OfferStorage {
8219 source: Some(source),
8220 source_name: Some("data".to_string()),
8221 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8222 name: "sink".to_string(),
8223 collection: None,
8224 })),
8225 target_name: Some(target_name.into()),
8226 availability: Some(availability),
8227 ..Default::default()
8228 })
8229 };
8230 decl.offers = Some(vec![
8231 new_offer(
8234 fdecl::Ref::Parent(fdecl::ParentRef {}),
8235 fdecl::Availability::Required,
8236 "data0",
8237 ),
8238 new_offer(
8239 fdecl::Ref::Parent(fdecl::ParentRef {}),
8240 fdecl::Availability::Optional,
8241 "data1",
8242 ),
8243 new_offer(
8244 fdecl::Ref::Parent(fdecl::ParentRef {}),
8245 fdecl::Availability::SameAsTarget,
8246 "data2",
8247 ),
8248 new_offer(
8249 fdecl::Ref::VoidType(fdecl::VoidRef {}),
8250 fdecl::Availability::Optional,
8251 "data3",
8252 ),
8253 new_offer(
8256 fdecl::Ref::Self_(fdecl::SelfRef {}),
8257 fdecl::Availability::Optional,
8258 "data4",
8259 ),
8260 new_offer(
8261 fdecl::Ref::Self_(fdecl::SelfRef {}),
8262 fdecl::Availability::SameAsTarget,
8263 "data5",
8264 ),
8265 new_offer(
8267 fdecl::Ref::VoidType(fdecl::VoidRef {}),
8268 fdecl::Availability::Required,
8269 "data6",
8270 ),
8271 new_offer(
8272 fdecl::Ref::VoidType(fdecl::VoidRef {}),
8273 fdecl::Availability::SameAsTarget,
8274 "data7",
8275 ),
8276 ]);
8277 decl
8278 },
8279 result = {
8280 Err(ErrorList::new(vec![
8281 Error::availability_must_be_optional(
8282 DeclType::OfferStorage,
8283 "availability",
8284 Some(&"data".to_string()),
8285 ),
8286 Error::availability_must_be_optional(
8287 DeclType::OfferStorage,
8288 "availability",
8289 Some(&"data".to_string()),
8290 ),
8291 ]))
8292 },
8293 },
8294
8295 test_validate_offers_valid_service_aggregation => {
8296 input = {
8297 let mut decl = new_component_decl();
8298 decl.offers = Some(vec![
8299 fdecl::Offer::Service(fdecl::OfferService {
8300 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
8301 name: "coll_a".to_string()
8302 })),
8303 source_name: Some("fuchsia.logger.Log".to_string()),
8304 target: Some(fdecl::Ref::Child(
8305 fdecl::ChildRef {
8306 name: "child_c".to_string(),
8307 collection: None,
8308 }
8309 )),
8310 target_name: Some("fuchsia.logger.Log".to_string()),
8311 source_instance_filter: None,
8312 ..Default::default()
8313 }),
8314 fdecl::Offer::Service(fdecl::OfferService {
8315 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
8316 name: "coll_b".to_string()
8317 })),
8318 source_name: Some("fuchsia.logger.Log".to_string()),
8319 target: Some(fdecl::Ref::Child(
8320 fdecl::ChildRef {
8321 name: "child_c".to_string(),
8322 collection: None,
8323 }
8324 )),
8325 target_name: Some("fuchsia.logger.Log".to_string()),
8326 source_instance_filter: Some(vec!["a_different_default".to_string()]),
8327 ..Default::default()
8328 })
8329 ]);
8330 decl.children = Some(vec![
8331 fdecl::Child {
8332 name: Some("child_c".to_string()),
8333 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
8334 startup: Some(fdecl::StartupMode::Lazy),
8335 ..Default::default()
8336 },
8337 ]);
8338 decl.collections = Some(vec![
8339 fdecl::Collection {
8340 name: Some("coll_a".into()),
8341 durability: Some(fdecl::Durability::Transient),
8342 ..Default::default()
8343 },
8344 fdecl::Collection {
8345 name: Some("coll_b".into()),
8346 durability: Some(fdecl::Durability::Transient),
8347 ..Default::default()
8348 },
8349 ]);
8350 decl
8351 },
8352 result = Ok(()),
8353 },
8354
8355 test_validate_source_dictionary => {
8357 input = fdecl::Component {
8358 program: Some(fdecl::Program {
8359 runner: Some("elf".into()),
8360 info: Some(fdata::Dictionary {
8361 entries: None,
8362 ..Default::default()
8363 }),
8364 ..Default::default()
8365 }),
8366 uses: Some(vec![
8367 fdecl::Use::Protocol(fdecl::UseProtocol {
8368 dependency_type: Some(fdecl::DependencyType::Strong),
8369 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8370 source_dictionary: Some("bad//".into()),
8371 source_name: Some("foo".into()),
8372 target_path: Some("/svc/foo".into()),
8373 ..Default::default()
8374 }),
8375 ]),
8376 exposes: Some(vec![
8377 fdecl::Expose::Directory(fdecl::ExposeDirectory {
8378 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8379 name: "missing".into(),
8380 collection: None,
8381 })),
8382 source_dictionary: Some("in/dict".into()),
8383 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8384 source_name: Some("foo".into()),
8385 target_name: Some("bar".into()),
8386 ..Default::default()
8387 }),
8388 ]),
8389 offers: Some(vec![
8390 fdecl::Offer::Service(fdecl::OfferService {
8391 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8392 source_dictionary: Some("bad//".into()),
8393 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8394 name: "child".into(),
8395 collection: None,
8396 })),
8397 source_name: Some("foo".into()),
8398 target_name: Some("bar".into()),
8399 ..Default::default()
8400 }),
8401 ]),
8402 children: Some(vec![
8403 fdecl::Child {
8404 name: Some("child".into()),
8405 url: Some("fuchsia-pkg://child".into()),
8406 startup: Some(fdecl::StartupMode::Lazy),
8407 ..Default::default()
8408 },
8409 ]),
8410 ..Default::default()
8411 },
8412 result = Err(ErrorList::new(vec![
8413 Error::invalid_field(DeclType::UseProtocol, "source_dictionary"),
8414 Error::invalid_child(DeclType::ExposeDirectory, "source", "missing"),
8415 Error::invalid_field(DeclType::OfferService, "source_dictionary"),
8416 ])),
8417 },
8418 test_validate_dictionary_too_long => {
8419 input = fdecl::Component {
8420 program: Some(fdecl::Program {
8421 runner: Some("elf".into()),
8422 info: Some(fdata::Dictionary {
8423 entries: None,
8424 ..Default::default()
8425 }),
8426 ..Default::default()
8427 }),
8428 uses: Some(vec![
8429 fdecl::Use::Protocol(fdecl::UseProtocol {
8430 dependency_type: Some(fdecl::DependencyType::Strong),
8431 source: Some(fdecl::Ref::Parent( fdecl::ParentRef {} )),
8432 source_dictionary: Some("a".repeat(4096)),
8433 source_name: Some("foo".into()),
8434 target_path: Some("/svc/foo".into()),
8435 ..Default::default()
8436 }),
8437 ]),
8438 ..Default::default()
8439 },
8440 result = Err(ErrorList::new(vec![
8441 Error::field_too_long(DeclType::UseProtocol, "source_dictionary"),
8442 ])),
8443 },
8444
8445 test_validate_environment_empty => {
8447 input = {
8448 let mut decl = new_component_decl();
8449 decl.environments = Some(vec![fdecl::Environment {
8450 name: None,
8451 extends: None,
8452 runners: None,
8453 resolvers: None,
8454 stop_timeout_ms: None,
8455 debug_capabilities: None,
8456 ..Default::default()
8457 }]);
8458 decl
8459 },
8460 result = Err(ErrorList::new(vec![
8461 Error::missing_field(DeclType::Environment, "name"),
8462 Error::missing_field(DeclType::Environment, "extends"),
8463 ])),
8464 },
8465
8466 test_validate_environment_no_stop_timeout => {
8467 input = {
8468 let mut decl = new_component_decl();
8469 decl.environments = Some(vec![fdecl::Environment {
8470 name: Some("env".to_string()),
8471 extends: Some(fdecl::EnvironmentExtends::None),
8472 runners: None,
8473 resolvers: None,
8474 stop_timeout_ms: None,
8475 ..Default::default()
8476 }]);
8477 decl
8478 },
8479 result = Err(ErrorList::new(vec![Error::missing_field(DeclType::Environment, "stop_timeout_ms")])),
8480 },
8481
8482 test_validate_environment_extends_stop_timeout => {
8483 input = { let mut decl = new_component_decl();
8484 decl.environments = Some(vec![fdecl::Environment {
8485 name: Some("env".to_string()),
8486 extends: Some(fdecl::EnvironmentExtends::Realm),
8487 runners: None,
8488 resolvers: None,
8489 stop_timeout_ms: None,
8490 ..Default::default()
8491 }]);
8492 decl
8493 },
8494 result = Ok(()),
8495 },
8496 test_validate_environment_long_identifiers => {
8497 input = {
8498 let mut decl = new_component_decl();
8499 decl.environments = Some(vec![fdecl::Environment {
8500 name: Some("a".repeat(256)),
8501 extends: Some(fdecl::EnvironmentExtends::None),
8502 runners: Some(vec![
8503 fdecl::RunnerRegistration {
8504 source_name: Some("a".repeat(256)),
8505 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8506 target_name: Some("a".repeat(256)),
8507 ..Default::default()
8508 },
8509 ]),
8510 resolvers: Some(vec![
8511 fdecl::ResolverRegistration {
8512 resolver: Some("a".repeat(256)),
8513 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8514 scheme: Some("a".repeat(256)),
8515 ..Default::default()
8516 },
8517 ]),
8518 stop_timeout_ms: Some(1234),
8519 ..Default::default()
8520 }]);
8521 decl
8522 },
8523 result = Err(ErrorList::new(vec![
8524 Error::field_too_long(DeclType::Environment, "name"),
8525 Error::field_too_long(DeclType::RunnerRegistration, "source_name"),
8526 Error::field_too_long(DeclType::RunnerRegistration, "target_name"),
8527 Error::field_too_long(DeclType::ResolverRegistration, "resolver"),
8528 Error::field_too_long(DeclType::ResolverRegistration, "scheme"),
8529 ])),
8530 },
8531 test_validate_environment_empty_runner_resolver_fields => {
8532 input = {
8533 let mut decl = new_component_decl();
8534 decl.environments = Some(vec![fdecl::Environment {
8535 name: Some("a".to_string()),
8536 extends: Some(fdecl::EnvironmentExtends::None),
8537 runners: Some(vec![
8538 fdecl::RunnerRegistration {
8539 source_name: None,
8540 source: None,
8541 target_name: None,
8542 ..Default::default()
8543 },
8544 ]),
8545 resolvers: Some(vec![
8546 fdecl::ResolverRegistration {
8547 resolver: None,
8548 source: None,
8549 scheme: None,
8550 ..Default::default()
8551 },
8552 ]),
8553 stop_timeout_ms: Some(1234),
8554 ..Default::default()
8555 }]);
8556 decl
8557 },
8558 result = Err(ErrorList::new(vec![
8559 Error::missing_field(DeclType::RunnerRegistration, "source_name"),
8560 Error::missing_field(DeclType::RunnerRegistration, "source"),
8561 Error::missing_field(DeclType::RunnerRegistration, "target_name"),
8562 Error::missing_field(DeclType::ResolverRegistration, "resolver"),
8563 Error::missing_field(DeclType::ResolverRegistration, "source"),
8564 Error::missing_field(DeclType::ResolverRegistration, "scheme"),
8565 ])),
8566 },
8567 test_validate_environment_invalid_fields => {
8568 input = {
8569 let mut decl = new_component_decl();
8570 decl.environments = Some(vec![fdecl::Environment {
8571 name: Some("a".to_string()),
8572 extends: Some(fdecl::EnvironmentExtends::None),
8573 runners: Some(vec![
8574 fdecl::RunnerRegistration {
8575 source_name: Some("^a".to_string()),
8576 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
8577 target_name: Some("%a".to_string()),
8578 ..Default::default()
8579 },
8580 ]),
8581 resolvers: Some(vec![
8582 fdecl::ResolverRegistration {
8583 resolver: Some("^a".to_string()),
8584 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
8585 scheme: Some("9scheme".to_string()),
8586 ..Default::default()
8587 },
8588 ]),
8589 stop_timeout_ms: Some(1234),
8590 ..Default::default()
8591 }]);
8592 decl
8593 },
8594 result = Err(ErrorList::new(vec![
8595 Error::invalid_field(DeclType::RunnerRegistration, "source_name"),
8596 Error::invalid_field(DeclType::RunnerRegistration, "source"),
8597 Error::invalid_field(DeclType::RunnerRegistration, "target_name"),
8598 Error::invalid_field(DeclType::ResolverRegistration, "resolver"),
8599 Error::invalid_field(DeclType::ResolverRegistration, "source"),
8600 Error::invalid_field(DeclType::ResolverRegistration, "scheme"),
8601 ])),
8602 },
8603 test_validate_environment_missing_runner => {
8604 input = {
8605 let mut decl = new_component_decl();
8606 decl.environments = Some(vec![fdecl::Environment {
8607 name: Some("a".to_string()),
8608 extends: Some(fdecl::EnvironmentExtends::None),
8609 runners: Some(vec![
8610 fdecl::RunnerRegistration {
8611 source_name: Some("dart".to_string()),
8612 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
8613 target_name: Some("dart".to_string()),
8614 ..Default::default()
8615 },
8616 ]),
8617 resolvers: None,
8618 stop_timeout_ms: Some(1234),
8619 ..Default::default()
8620 }]);
8621 decl
8622 },
8623 result = Err(ErrorList::new(vec![
8624 Error::invalid_runner(DeclType::RunnerRegistration, "source_name", "dart"),
8625 ])),
8626 },
8627 test_validate_environment_duplicate_registrations => {
8628 input = {
8629 let mut decl = new_component_decl();
8630 decl.environments = Some(vec![fdecl::Environment {
8631 name: Some("a".to_string()),
8632 extends: Some(fdecl::EnvironmentExtends::None),
8633 runners: Some(vec![
8634 fdecl::RunnerRegistration {
8635 source_name: Some("dart".to_string()),
8636 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8637 target_name: Some("dart".to_string()),
8638 ..Default::default()
8639 },
8640 fdecl::RunnerRegistration {
8641 source_name: Some("other-dart".to_string()),
8642 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8643 target_name: Some("dart".to_string()),
8644 ..Default::default()
8645 },
8646 ]),
8647 resolvers: Some(vec![
8648 fdecl::ResolverRegistration {
8649 resolver: Some("pkg_resolver".to_string()),
8650 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8651 scheme: Some("fuchsia-pkg".to_string()),
8652 ..Default::default()
8653 },
8654 fdecl::ResolverRegistration {
8655 resolver: Some("base_resolver".to_string()),
8656 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8657 scheme: Some("fuchsia-pkg".to_string()),
8658 ..Default::default()
8659 },
8660 ]),
8661 stop_timeout_ms: Some(1234),
8662 ..Default::default()
8663 }]);
8664 decl
8665 },
8666 result = Err(ErrorList::new(vec![
8667 Error::duplicate_field(DeclType::RunnerRegistration, "target_name", "dart"),
8668 Error::duplicate_field(DeclType::ResolverRegistration, "scheme", "fuchsia-pkg"),
8669 ])),
8670 },
8671 test_validate_environment_from_missing_child => {
8672 input = {
8673 let mut decl = new_component_decl();
8674 decl.environments = Some(vec![fdecl::Environment {
8675 name: Some("a".to_string()),
8676 extends: Some(fdecl::EnvironmentExtends::None),
8677 runners: Some(vec![
8678 fdecl::RunnerRegistration {
8679 source_name: Some("elf".to_string()),
8680 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8681 name: "missing".to_string(),
8682 collection: None,
8683 })),
8684 target_name: Some("elf".to_string()),
8685 ..Default::default()
8686 },
8687 ]),
8688 resolvers: Some(vec![
8689 fdecl::ResolverRegistration {
8690 resolver: Some("pkg_resolver".to_string()),
8691 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8692 name: "missing".to_string(),
8693 collection: None,
8694 })),
8695 scheme: Some("fuchsia-pkg".to_string()),
8696 ..Default::default()
8697 },
8698 ]),
8699 stop_timeout_ms: Some(1234),
8700 ..Default::default()
8701 }]);
8702 decl
8703 },
8704 result = Err(ErrorList::new(vec![
8705 Error::invalid_child(DeclType::RunnerRegistration, "source", "missing"),
8706 Error::invalid_child(DeclType::ResolverRegistration, "source", "missing"),
8707 ])),
8708 },
8709 test_validate_environment_runner_child_cycle => {
8710 input = {
8711 let mut decl = new_component_decl();
8712 decl.environments = Some(vec![fdecl::Environment {
8713 name: Some("env".to_string()),
8714 extends: Some(fdecl::EnvironmentExtends::None),
8715 runners: Some(vec![
8716 fdecl::RunnerRegistration {
8717 source_name: Some("elf".to_string()),
8718 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8719 name: "child".to_string(),
8720 collection: None,
8721 })),
8722 target_name: Some("elf".to_string()),
8723 ..Default::default()
8724 },
8725 ]),
8726 resolvers: None,
8727 stop_timeout_ms: Some(1234),
8728 ..Default::default()
8729 }]);
8730 decl.children = Some(vec![fdecl::Child {
8731 name: Some("child".to_string()),
8732 startup: Some(fdecl::StartupMode::Lazy),
8733 on_terminate: None,
8734 url: Some("fuchsia-pkg://child".to_string()),
8735 environment: Some("env".to_string()),
8736 ..Default::default()
8737 }]);
8738 decl
8739 },
8740 result = Err(ErrorList::new(vec![
8741 Error::dependency_cycle(
8742 "{{child child -> environment env -> child child}}"
8743 ),
8744 ])),
8745 },
8746 test_validate_environment_resolver_child_cycle => {
8747 input = {
8748 let mut decl = new_component_decl();
8749 decl.environments = Some(vec![fdecl::Environment {
8750 name: Some("env".to_string()),
8751 extends: Some(fdecl::EnvironmentExtends::None),
8752 runners: None,
8753 resolvers: Some(vec![
8754 fdecl::ResolverRegistration {
8755 resolver: Some("pkg_resolver".to_string()),
8756 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8757 name: "child".to_string(),
8758 collection: None,
8759 })),
8760 scheme: Some("fuchsia-pkg".to_string()),
8761 ..Default::default()
8762 },
8763 ]),
8764 stop_timeout_ms: Some(1234),
8765 ..Default::default()
8766 }]);
8767 decl.children = Some(vec![fdecl::Child {
8768 name: Some("child".to_string()),
8769 startup: Some(fdecl::StartupMode::Lazy),
8770 on_terminate: None,
8771 url: Some("fuchsia-pkg://child".to_string()),
8772 environment: Some("env".to_string()),
8773 ..Default::default()
8774 }]);
8775 decl
8776 },
8777 result = Err(ErrorList::new(vec![
8778 Error::dependency_cycle(
8779 "{{child child -> environment env -> child child}}"
8780 ),
8781 ])),
8782 },
8783 test_validate_environment_resolver_multiple_children_cycle => {
8784 input = {
8785 let mut decl = new_component_decl();
8786 decl.environments = Some(vec![fdecl::Environment {
8787 name: Some("env".to_string()),
8788 extends: Some(fdecl::EnvironmentExtends::None),
8789 runners: None,
8790 resolvers: Some(vec![
8791 fdecl::ResolverRegistration {
8792 resolver: Some("pkg_resolver".to_string()),
8793 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8794 name: "a".to_string(),
8795 collection: None,
8796 })),
8797 scheme: Some("fuchsia-pkg".to_string()),
8798 ..Default::default()
8799 },
8800 ]),
8801 stop_timeout_ms: Some(1234),
8802 ..Default::default()
8803 }]);
8804 decl.children = Some(vec![
8805 fdecl::Child {
8806 name: Some("a".to_string()),
8807 startup: Some(fdecl::StartupMode::Lazy),
8808 on_terminate: None,
8809 url: Some("fuchsia-pkg://child-a".to_string()),
8810 environment: None,
8811 ..Default::default()
8812 },
8813 fdecl::Child {
8814 name: Some("b".to_string()),
8815 startup: Some(fdecl::StartupMode::Lazy),
8816 on_terminate: None,
8817 url: Some("fuchsia-pkg://child-b".to_string()),
8818 environment: Some("env".to_string()),
8819 ..Default::default()
8820 },
8821 ]);
8822 decl.offers = Some(vec![fdecl::Offer::Service(fdecl::OfferService {
8823 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8824 name: "b".to_string(),
8825 collection: None,
8826 })),
8827 source_name: Some("thing".to_string()),
8828 target: Some(fdecl::Ref::Child(
8829 fdecl::ChildRef {
8830 name: "a".to_string(),
8831 collection: None,
8832 }
8833 )),
8834 target_name: Some("thing".to_string()),
8835 ..Default::default()
8836 })]);
8837 decl
8838 },
8839 result = Err(ErrorList::new(vec![
8840 Error::dependency_cycle(
8841 "{{child a -> environment env -> child b -> child a}}"
8842 ),
8843 ])),
8844 },
8845 test_validate_environment_debug_empty => {
8846 input = {
8847 let mut decl = new_component_decl();
8848 decl.environments = Some(vec![
8849 fdecl::Environment {
8850 name: Some("a".to_string()),
8851 extends: Some(fdecl::EnvironmentExtends::None),
8852 stop_timeout_ms: Some(2),
8853 debug_capabilities:Some(vec![
8854 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8855 source: None,
8856 source_name: None,
8857 target_name: None,
8858 ..Default::default()
8859 }),
8860 ]),
8861 ..Default::default()
8862 }]);
8863 decl
8864 },
8865 result = Err(ErrorList::new(vec![
8866 Error::missing_field(DeclType::DebugProtocolRegistration, "source"),
8867 Error::missing_field(DeclType::DebugProtocolRegistration, "source_name"),
8868 Error::missing_field(DeclType::DebugProtocolRegistration, "target_name"),
8869 ])),
8870 },
8871 test_validate_environment_debug_log_identifier => {
8872 input = {
8873 let mut decl = new_component_decl();
8874 decl.environments = Some(vec![
8875 fdecl::Environment {
8876 name: Some("a".to_string()),
8877 extends: Some(fdecl::EnvironmentExtends::None),
8878 stop_timeout_ms: Some(2),
8879 debug_capabilities:Some(vec![
8880 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8881 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8882 name: "a".repeat(256),
8883 collection: None,
8884 })),
8885 source_name: Some(format!("{}", "a".repeat(256))),
8886 target_name: Some(format!("{}", "b".repeat(256))),
8887 ..Default::default()
8888 }),
8889 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8890 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8891 source_name: Some("a".to_string()),
8892 target_name: Some(format!("{}", "b".repeat(256))),
8893 ..Default::default()
8894 }),
8895 ]),
8896 ..Default::default()
8897 }]);
8898 decl
8899 },
8900 result = Err(ErrorList::new(vec![
8901 Error::field_too_long(DeclType::DebugProtocolRegistration, "source.child.name"),
8902 Error::field_too_long(DeclType::DebugProtocolRegistration, "source_name"),
8903 Error::field_too_long(DeclType::DebugProtocolRegistration, "target_name"),
8904 Error::field_too_long(DeclType::DebugProtocolRegistration, "target_name"),
8905 ])),
8906 },
8907 test_validate_environment_debug_log_extraneous => {
8908 input = {
8909 let mut decl = new_component_decl();
8910 decl.environments = Some(vec![
8911 fdecl::Environment {
8912 name: Some("a".to_string()),
8913 extends: Some(fdecl::EnvironmentExtends::None),
8914 stop_timeout_ms: Some(2),
8915 debug_capabilities:Some(vec![
8916 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8917 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8918 name: "logger".to_string(),
8919 collection: Some("modular".to_string()),
8920 })),
8921 source_name: Some("fuchsia.logger.Log".to_string()),
8922 target_name: Some("fuchsia.logger.Log".to_string()),
8923 ..Default::default()
8924 }),
8925 ]),
8926 ..Default::default()
8927 }]);
8928 decl
8929 },
8930 result = Err(ErrorList::new(vec![
8931 Error::extraneous_field(DeclType::DebugProtocolRegistration, "source.child.collection"),
8932 ])),
8933 },
8934 test_validate_environment_debug_log_invalid_identifiers => {
8935 input = {
8936 let mut decl = new_component_decl();
8937 decl.environments = Some(vec![
8938 fdecl::Environment {
8939 name: Some("a".to_string()),
8940 extends: Some(fdecl::EnvironmentExtends::None),
8941 stop_timeout_ms: Some(2),
8942 debug_capabilities:Some(vec![
8943 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8944 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8945 name: "^bad".to_string(),
8946 collection: None,
8947 })),
8948 source_name: Some("foo/".to_string()),
8949 target_name: Some("/".to_string()),
8950 ..Default::default()
8951 }),
8952 ]),
8953 ..Default::default()
8954 }]);
8955 decl
8956 },
8957 result = Err(ErrorList::new(vec![
8958 Error::invalid_field(DeclType::DebugProtocolRegistration, "source.child.name"),
8959 Error::invalid_field(DeclType::DebugProtocolRegistration, "source_name"),
8960 Error::invalid_field(DeclType::DebugProtocolRegistration, "target_name"),
8961 ])),
8962 },
8963 test_validate_environment_debug_log_invalid_child => {
8964 input = {
8965 let mut decl = new_component_decl();
8966 decl.environments = Some(vec![
8967 fdecl::Environment {
8968 name: Some("a".to_string()),
8969 extends: Some(fdecl::EnvironmentExtends::None),
8970 stop_timeout_ms: Some(2),
8971 debug_capabilities:Some(vec![
8972 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8973 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8974 name: "logger".to_string(),
8975 collection: None,
8976 })),
8977 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
8978 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
8979 ..Default::default()
8980 }),
8981 ]),
8982 ..Default::default()
8983 }]);
8984 decl.children = Some(vec![
8985 fdecl::Child {
8986 name: Some("netstack".to_string()),
8987 url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
8988 startup: Some(fdecl::StartupMode::Lazy),
8989 on_terminate: None,
8990 environment: None,
8991 ..Default::default()
8992 },
8993 ]);
8994 decl
8995 },
8996 result = Err(ErrorList::new(vec![
8997 Error::invalid_child(DeclType::DebugProtocolRegistration, "source", "logger"),
8998
8999 ])),
9000 },
9001 test_validate_environment_debug_source_capability => {
9002 input = {
9003 let mut decl = new_component_decl();
9004 decl.environments = Some(vec![
9005 fdecl::Environment {
9006 name: Some("a".to_string()),
9007 extends: Some(fdecl::EnvironmentExtends::None),
9008 stop_timeout_ms: Some(2),
9009 debug_capabilities:Some(vec![
9010 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
9011 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
9012 name: "storage".to_string(),
9013 })),
9014 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
9015 target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
9016 ..Default::default()
9017 }),
9018 ]),
9019 ..Default::default()
9020 }]);
9021 decl
9022 },
9023 result = Err(ErrorList::new(vec![
9024 Error::invalid_field(DeclType::DebugProtocolRegistration, "source"),
9025 ])),
9026 },
9027
9028 test_validate_children_empty => {
9030 input = {
9031 let mut decl = new_component_decl();
9032 decl.children = Some(vec![fdecl::Child{
9033 name: None,
9034 url: None,
9035 startup: None,
9036 on_terminate: None,
9037 environment: None,
9038 ..Default::default()
9039 }]);
9040 decl
9041 },
9042 result = Err(ErrorList::new(vec![
9043 Error::missing_field(DeclType::Child, "name"),
9044 Error::missing_field(DeclType::Child, "url"),
9045 Error::missing_field(DeclType::Child, "startup"),
9046 ])),
9048 },
9049 test_validate_children_invalid_identifiers => {
9050 input = {
9051 let mut decl = new_component_decl();
9052 decl.children = Some(vec![fdecl::Child{
9053 name: Some("^bad".to_string()),
9054 url: Some("scheme://invalid-port:99999999/path#frag".to_string()),
9055 startup: Some(fdecl::StartupMode::Lazy),
9056 on_terminate: None,
9057 environment: None,
9058 ..Default::default()
9059 }]);
9060 decl
9061 },
9062 result = Err(ErrorList::new(vec![
9063 Error::invalid_field(DeclType::Child, "name"),
9064 Error::invalid_url(DeclType::Child, "url", "\"scheme://invalid-port:99999999/path#frag\": Malformed URL: InvalidPort."),
9065 ])),
9066 },
9067 test_validate_children_long_identifiers => {
9068 input = {
9069 let mut decl = new_component_decl();
9070 decl.children = Some(vec![fdecl::Child{
9071 name: Some("a".repeat(1025)),
9072 url: Some(format!("fuchsia-pkg://{}", "a".repeat(4083))),
9073 startup: Some(fdecl::StartupMode::Lazy),
9074 on_terminate: None,
9075 environment: Some("a".repeat(1025)),
9076 ..Default::default()
9077 }]);
9078 decl
9079 },
9080 result = Err(ErrorList::new(vec![
9081 Error::field_too_long(DeclType::Child, "name"),
9082 Error::field_too_long(DeclType::Child, "url"),
9083 Error::field_too_long(DeclType::Child, "environment"),
9084 Error::invalid_environment(DeclType::Child, "environment", "a".repeat(1025)),
9085 ])),
9086 },
9087 test_validate_child_references_unknown_env => {
9088 input = {
9089 let mut decl = new_component_decl();
9090 decl.children = Some(vec![fdecl::Child{
9091 name: Some("foo".to_string()),
9092 url: Some("fuchsia-pkg://foo".to_string()),
9093 startup: Some(fdecl::StartupMode::Lazy),
9094 on_terminate: None,
9095 environment: Some("test_env".to_string()),
9096 ..Default::default()
9097 }]);
9098 decl
9099 },
9100 result = Err(ErrorList::new(vec![
9101 Error::invalid_environment(DeclType::Child, "environment", "test_env"),
9102 ])),
9103 },
9104
9105 test_validate_collections_empty => {
9107 input = {
9108 let mut decl = new_component_decl();
9109 decl.collections = Some(vec![fdecl::Collection{
9110 name: None,
9111 durability: None,
9112 environment: None,
9113 allowed_offers: None,
9114 allow_long_names: None,
9115 ..Default::default()
9116 }]);
9117 decl
9118 },
9119 result = Err(ErrorList::new(vec![
9120 Error::missing_field(DeclType::Collection, "name"),
9121 Error::missing_field(DeclType::Collection, "durability"),
9122 ])),
9123 },
9124 test_validate_collections_invalid_identifiers => {
9125 input = {
9126 let mut decl = new_component_decl();
9127 decl.collections = Some(vec![fdecl::Collection{
9128 name: Some("^bad".to_string()),
9129 durability: Some(fdecl::Durability::Transient),
9130 environment: None,
9131 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
9132 allow_long_names: None,
9133 ..Default::default()
9134 }]);
9135 decl
9136 },
9137 result = Err(ErrorList::new(vec![
9138 Error::invalid_field(DeclType::Collection, "name"),
9139 ])),
9140 },
9141 test_validate_collections_long_identifiers => {
9142 input = {
9143 let mut decl = new_component_decl();
9144 decl.collections = Some(vec![fdecl::Collection{
9145 name: Some("a".repeat(1025)),
9146 durability: Some(fdecl::Durability::Transient),
9147 environment: None,
9148 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
9149 allow_long_names: None,
9150 ..Default::default()
9151 }]);
9152 decl
9153 },
9154 result = Err(ErrorList::new(vec![
9155 Error::field_too_long(DeclType::Collection, "name"),
9156 ])),
9157 },
9158 test_validate_collection_references_unknown_env => {
9159 input = {
9160 let mut decl = new_component_decl();
9161 decl.collections = Some(vec![fdecl::Collection {
9162 name: Some("foo".to_string()),
9163 durability: Some(fdecl::Durability::Transient),
9164 environment: Some("test_env".to_string()),
9165 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
9166 allow_long_names: None,
9167 ..Default::default()
9168 }]);
9169 decl
9170 },
9171 result = Err(ErrorList::new(vec![
9172 Error::invalid_environment(DeclType::Collection, "environment", "test_env"),
9173 ])),
9174 },
9175
9176 test_validate_capabilities_empty => {
9178 input = {
9179 let mut decl = new_component_decl();
9180 decl.capabilities = Some(vec![
9181 fdecl::Capability::Service(fdecl::Service {
9182 name: None,
9183 source_path: None,
9184 ..Default::default()
9185 }),
9186 fdecl::Capability::Protocol(fdecl::Protocol {
9187 name: None,
9188 source_path: None,
9189 ..Default::default()
9190 }),
9191 fdecl::Capability::Directory(fdecl::Directory {
9192 name: None,
9193 source_path: None,
9194 rights: None,
9195 ..Default::default()
9196 }),
9197 fdecl::Capability::Storage(fdecl::Storage {
9198 name: None,
9199 source: None,
9200 backing_dir: None,
9201 subdir: None,
9202 storage_id: None,
9203 ..Default::default()
9204 }),
9205 fdecl::Capability::Runner(fdecl::Runner {
9206 name: None,
9207 source_path: None,
9208 ..Default::default()
9209 }),
9210 fdecl::Capability::Resolver(fdecl::Resolver {
9211 name: None,
9212 source_path: None,
9213 ..Default::default()
9214 }),
9215 fdecl::Capability::Dictionary(fdecl::Dictionary {
9216 ..Default::default()
9217 }),
9218 ]);
9219 decl
9220 },
9221 result = Err(ErrorList::new(vec![
9222 Error::missing_field(DeclType::Dictionary, "name"),
9223 Error::missing_field(DeclType::Service, "name"),
9224 Error::missing_field(DeclType::Service, "source_path"),
9225 Error::missing_field(DeclType::Protocol, "name"),
9226 Error::missing_field(DeclType::Protocol, "source_path"),
9227 Error::missing_field(DeclType::Directory, "name"),
9228 Error::missing_field(DeclType::Directory, "source_path"),
9229 Error::missing_field(DeclType::Directory, "rights"),
9230 Error::missing_field(DeclType::Storage, "source"),
9231 Error::missing_field(DeclType::Storage, "name"),
9232 Error::missing_field(DeclType::Storage, "storage_id"),
9233 Error::missing_field(DeclType::Storage, "backing_dir"),
9234 Error::missing_field(DeclType::Runner, "name"),
9235 Error::missing_field(DeclType::Runner, "source_path"),
9236 Error::missing_field(DeclType::Resolver, "name"),
9237 Error::missing_field(DeclType::Resolver, "source_path"),
9238 ])),
9239 },
9240 test_validate_capabilities_invalid_identifiers => {
9241 input = {
9242 let mut decl = new_component_decl();
9243 decl.capabilities = Some(vec![
9244 fdecl::Capability::Service(fdecl::Service {
9245 name: Some("^bad".to_string()),
9246 source_path: Some("&bad".to_string()),
9247 ..Default::default()
9248 }),
9249 fdecl::Capability::Protocol(fdecl::Protocol {
9250 name: Some("^bad".to_string()),
9251 source_path: Some("&bad".to_string()),
9252 ..Default::default()
9253 }),
9254 fdecl::Capability::Directory(fdecl::Directory {
9255 name: Some("^bad".to_string()),
9256 source_path: Some("&bad".to_string()),
9257 rights: Some(fio::Operations::CONNECT),
9258 ..Default::default()
9259 }),
9260 fdecl::Capability::Storage(fdecl::Storage {
9261 name: Some("^bad".to_string()),
9262 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9263 name: "/bad".to_string()
9264 })),
9265 backing_dir: Some("&bad".to_string()),
9266 subdir: None,
9267 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9268 ..Default::default()
9269 }),
9270 fdecl::Capability::Runner(fdecl::Runner {
9271 name: Some("^bad".to_string()),
9272 source_path: Some("&bad".to_string()),
9273 ..Default::default()
9274 }),
9275 fdecl::Capability::Resolver(fdecl::Resolver {
9276 name: Some("^bad".to_string()),
9277 source_path: Some("&bad".to_string()),
9278 ..Default::default()
9279 }),
9280 fdecl::Capability::Dictionary(fdecl::Dictionary {
9281 name: Some("^bad".to_string()),
9282 ..Default::default()
9283 }),
9284 ]);
9285 decl
9286 },
9287 result = Err(ErrorList::new(vec![
9288 Error::invalid_field(DeclType::Dictionary, "name"),
9289 Error::invalid_field(DeclType::Service, "name"),
9290 Error::invalid_field(DeclType::Service, "source_path"),
9291 Error::invalid_field(DeclType::Protocol, "name"),
9292 Error::invalid_field(DeclType::Protocol, "source_path"),
9293 Error::invalid_field(DeclType::Directory, "name"),
9294 Error::invalid_field(DeclType::Directory, "source_path"),
9295 Error::invalid_field(DeclType::Storage, "source"),
9296 Error::invalid_field(DeclType::Storage, "name"),
9297 Error::invalid_field(DeclType::Storage, "backing_dir"),
9298 Error::invalid_field(DeclType::Runner, "name"),
9299 Error::invalid_field(DeclType::Runner, "source_path"),
9300 Error::invalid_field(DeclType::Resolver, "name"),
9301 Error::invalid_field(DeclType::Resolver, "source_path"),
9302 ])),
9303 },
9304 test_validate_capabilities_invalid_child => {
9305 input = {
9306 let mut decl = new_component_decl();
9307 decl.capabilities = Some(vec![
9308 fdecl::Capability::Storage(fdecl::Storage {
9309 name: Some("foo".to_string()),
9310 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9311 name: "invalid".to_string(),
9312 })),
9313 backing_dir: Some("foo".to_string()),
9314 subdir: None,
9315 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9316 ..Default::default()
9317 }),
9318 ]);
9319 decl
9320 },
9321 result = Err(ErrorList::new(vec![
9322 Error::invalid_field(DeclType::Storage, "source"),
9323 ])),
9324 },
9325 test_validate_capabilities_long_identifiers => {
9326 input = {
9327 let mut decl = new_component_decl();
9328 decl.capabilities = Some(vec![
9329 fdecl::Capability::Service(fdecl::Service {
9330 name: Some("a".repeat(256)),
9331 source_path: Some("/c".repeat(2048)),
9332 ..Default::default()
9333 }),
9334 fdecl::Capability::Protocol(fdecl::Protocol {
9335 name: Some("a".repeat(256)),
9336 source_path: Some("/c".repeat(2048)),
9337 ..Default::default()
9338 }),
9339 fdecl::Capability::Directory(fdecl::Directory {
9340 name: Some("a".repeat(256)),
9341 source_path: Some("/c".repeat(2048)),
9342 rights: Some(fio::Operations::CONNECT),
9343 ..Default::default()
9344 }),
9345 fdecl::Capability::Storage(fdecl::Storage {
9346 name: Some("a".repeat(256)),
9347 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
9348 name: "b".repeat(256),
9349 collection: None,
9350 })),
9351 backing_dir: Some(format!("{}", "c".repeat(256))),
9352 subdir: None,
9353 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9354 ..Default::default()
9355 }),
9356 fdecl::Capability::Runner(fdecl::Runner {
9357 name: Some("a".repeat(256)),
9358 source_path: Some("/c".repeat(2048)),
9359 ..Default::default()
9360 }),
9361 fdecl::Capability::Resolver(fdecl::Resolver {
9362 name: Some("a".repeat(256)),
9363 source_path: Some("/c".repeat(2048)),
9364 ..Default::default()
9365 }),
9366 fdecl::Capability::Dictionary(fdecl::Dictionary {
9367 name: Some("a".repeat(256)),
9368 ..Default::default()
9369 }),
9370 ]);
9371 decl
9372 },
9373 result = Err(ErrorList::new(vec![
9374 Error::field_too_long(DeclType::Dictionary, "name"),
9375 Error::field_too_long(DeclType::Service, "name"),
9376 Error::field_too_long(DeclType::Service, "source_path"),
9377 Error::field_too_long(DeclType::Protocol, "name"),
9378 Error::field_too_long(DeclType::Protocol, "source_path"),
9379 Error::field_too_long(DeclType::Directory, "name"),
9380 Error::field_too_long(DeclType::Directory, "source_path"),
9381 Error::field_too_long(DeclType::Storage, "source.child.name"),
9382 Error::field_too_long(DeclType::Storage, "name"),
9383 Error::field_too_long(DeclType::Storage, "backing_dir"),
9384 Error::field_too_long(DeclType::Runner, "name"),
9385 Error::field_too_long(DeclType::Runner, "source_path"),
9386 Error::field_too_long(DeclType::Resolver, "name"),
9387 Error::field_too_long(DeclType::Resolver, "source_path"),
9388 ])),
9389 },
9390 test_validate_capabilities_duplicate_name => {
9391 input = {
9392 let mut decl = new_component_decl();
9393 decl.capabilities = Some(vec![
9394 fdecl::Capability::Service(fdecl::Service {
9395 name: Some("service".to_string()),
9396 source_path: Some("/service".to_string()),
9397 ..Default::default()
9398 }),
9399 fdecl::Capability::Service(fdecl::Service {
9400 name: Some("service".to_string()),
9401 source_path: Some("/service".to_string()),
9402 ..Default::default()
9403 }),
9404 fdecl::Capability::Protocol(fdecl::Protocol {
9405 name: Some("protocol".to_string()),
9406 source_path: Some("/protocol".to_string()),
9407 ..Default::default()
9408 }),
9409 fdecl::Capability::Protocol(fdecl::Protocol {
9410 name: Some("protocol".to_string()),
9411 source_path: Some("/protocol".to_string()),
9412 ..Default::default()
9413 }),
9414 fdecl::Capability::Directory(fdecl::Directory {
9415 name: Some("directory".to_string()),
9416 source_path: Some("/directory".to_string()),
9417 rights: Some(fio::Operations::CONNECT),
9418 ..Default::default()
9419 }),
9420 fdecl::Capability::Directory(fdecl::Directory {
9421 name: Some("directory".to_string()),
9422 source_path: Some("/directory".to_string()),
9423 rights: Some(fio::Operations::CONNECT),
9424 ..Default::default()
9425 }),
9426 fdecl::Capability::Storage(fdecl::Storage {
9427 name: Some("storage".to_string()),
9428 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
9429 backing_dir: Some("directory".to_string()),
9430 subdir: None,
9431 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9432 ..Default::default()
9433 }),
9434 fdecl::Capability::Storage(fdecl::Storage {
9435 name: Some("storage".to_string()),
9436 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
9437 backing_dir: Some("directory".to_string()),
9438 subdir: None,
9439 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9440 ..Default::default()
9441 }),
9442 fdecl::Capability::Runner(fdecl::Runner {
9443 name: Some("runner".to_string()),
9444 source_path: Some("/runner".to_string()),
9445 ..Default::default()
9446 }),
9447 fdecl::Capability::Runner(fdecl::Runner {
9448 name: Some("runner".to_string()),
9449 source_path: Some("/runner".to_string()),
9450 ..Default::default()
9451 }),
9452 fdecl::Capability::Resolver(fdecl::Resolver {
9453 name: Some("resolver".to_string()),
9454 source_path: Some("/resolver".to_string()),
9455 ..Default::default()
9456 }),
9457 fdecl::Capability::Resolver(fdecl::Resolver {
9458 name: Some("resolver".to_string()),
9459 source_path: Some("/resolver".to_string()),
9460 ..Default::default()
9461 }),
9462 fdecl::Capability::Dictionary(fdecl::Dictionary {
9463 name: Some("dictionary".to_string()),
9464 ..Default::default()
9465 }),
9466 fdecl::Capability::Dictionary(fdecl::Dictionary {
9467 name: Some("dictionary".to_string()),
9468 ..Default::default()
9469 }),
9470 ]);
9471 decl
9472 },
9473 result = Err(ErrorList::new(vec![
9474 Error::duplicate_field(DeclType::Dictionary, "name", "dictionary"),
9475 Error::duplicate_field(DeclType::Service, "name", "service"),
9476 Error::duplicate_field(DeclType::Protocol, "name", "protocol"),
9477 Error::duplicate_field(DeclType::Directory, "name", "directory"),
9478 Error::duplicate_field(DeclType::Storage, "name", "storage"),
9479 Error::duplicate_field(DeclType::Runner, "name", "runner"),
9480 Error::duplicate_field(DeclType::Resolver, "name", "resolver"),
9481 ])),
9482 },
9483 test_validate_invalid_service_aggregation_conflicting_filter => {
9484 input = {
9485 let mut decl = new_component_decl();
9486 decl.offers = Some(vec![
9487 fdecl::Offer::Service(fdecl::OfferService {
9488 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9489 name: "coll_a".to_string()
9490 })),
9491 source_name: Some("fuchsia.logger.Log".to_string()),
9492 target: Some(fdecl::Ref::Child(
9493 fdecl::ChildRef {
9494 name: "child_c".to_string(),
9495 collection: None,
9496 }
9497 )),
9498 target_name: Some("fuchsia.logger.Log1".to_string()),
9499 source_instance_filter: Some(vec!["default".to_string()]),
9500 ..Default::default()
9501 }),
9502 fdecl::Offer::Service(fdecl::OfferService {
9503 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9504 name: "coll_b".to_string()
9505 })),
9506 source_name: Some("fuchsia.logger.Log".to_string()),
9507 target: Some(fdecl::Ref::Child(
9508 fdecl::ChildRef {
9509 name: "child_c".to_string(),
9510 collection: None,
9511 }
9512 )),
9513 target_name: Some("fuchsia.logger.Log1".to_string()),
9514 source_instance_filter: Some(vec!["default".to_string()]),
9515 ..Default::default()
9516 }),
9517 ]);
9518 decl.collections = Some(vec![
9519 fdecl::Collection {
9520 name: Some("coll_a".to_string()),
9521 durability: Some(fdecl::Durability::Transient),
9522 ..Default::default()
9523 },
9524 fdecl::Collection {
9525 name: Some("coll_b".to_string()),
9526 durability: Some(fdecl::Durability::Transient),
9527 ..Default::default()
9528 },
9529 ]);
9530 decl.children = Some(vec![
9531 fdecl::Child {
9532 name: Some("child_c".to_string()),
9533 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9534 startup: Some(fdecl::StartupMode::Lazy),
9535 ..Default::default()
9536 },
9537 ]);
9538 decl
9539 },
9540 result = Err(ErrorList::new(vec![
9541 Error::invalid_aggregate_offer("Conflicting source_instance_filter in aggregate \
9542 service offer, instance_name 'default' seen in filter lists multiple times"),
9543 ])),
9544 },
9545
9546 test_validate_invalid_service_aggregation_conflicting_source_name => {
9547 input = {
9548 let mut decl = new_component_decl();
9549 decl.offers = Some(vec![
9550 fdecl::Offer::Service(fdecl::OfferService {
9551 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9552 name: "coll_a".into()
9553 })),
9554 source_name: Some("fuchsia.logger.Log".to_string()),
9555 target: Some(fdecl::Ref::Child(
9556 fdecl::ChildRef {
9557 name: "child_c".to_string(),
9558 collection: None,
9559 }
9560 )),
9561 target_name: Some("fuchsia.logger.Log2".to_string()),
9562 source_instance_filter: Some(vec!["default2".to_string()]),
9563 ..Default::default()
9564 }),
9565 fdecl::Offer::Service(fdecl::OfferService {
9566 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9567 name: "coll_b".into()
9568 })),
9569 source_name: Some("fuchsia.logger.LogAlt".to_string()),
9570 target: Some(fdecl::Ref::Child(
9571 fdecl::ChildRef {
9572 name: "child_c".to_string(),
9573 collection: None,
9574 }
9575 )),
9576 target_name: Some("fuchsia.logger.Log2".to_string()),
9577 source_instance_filter: Some(vec!["default".to_string()]),
9578 ..Default::default()
9579 })
9580 ]);
9581 decl.collections = Some(vec![
9582 fdecl::Collection {
9583 name: Some("coll_a".to_string()),
9584 durability: Some(fdecl::Durability::Transient),
9585 ..Default::default()
9586 },
9587 fdecl::Collection {
9588 name: Some("coll_b".to_string()),
9589 durability: Some(fdecl::Durability::Transient),
9590 ..Default::default()
9591 },
9592 ]);
9593 decl.children = Some(vec![
9594 fdecl::Child {
9595 name: Some("child_c".to_string()),
9596 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9597 startup: Some(fdecl::StartupMode::Lazy),
9598 on_terminate: None,
9599 environment: None,
9600 ..Default::default()
9601 },
9602 ]);
9603 decl
9604 },
9605 result = Err(ErrorList::new(vec![
9606 Error::invalid_aggregate_offer("All aggregate service offers must have the same source_name, saw fuchsia.logger.Log, fuchsia.logger.LogAlt. Use renamed_instances to rename instance names to avoid conflict."),
9607 ])),
9608 },
9609
9610 test_validate_resolvers_missing_from_offer => {
9611 input = {
9612 let mut decl = new_component_decl();
9613 decl.offers = Some(vec![fdecl::Offer::Resolver(fdecl::OfferResolver {
9614 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
9615 source_name: Some("a".to_string()),
9616 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
9617 target_name: Some("a".to_string()),
9618 ..Default::default()
9619 })]);
9620 decl.children = Some(vec![fdecl::Child {
9621 name: Some("child".to_string()),
9622 url: Some("test:///child".to_string()),
9623 startup: Some(fdecl::StartupMode::Eager),
9624 on_terminate: None,
9625 environment: None,
9626 ..Default::default()
9627 }]);
9628 decl
9629 },
9630 result = Err(ErrorList::new(vec![
9631 Error::invalid_capability(DeclType::OfferResolver, "source", "a"),
9632 ])),
9633 },
9634 test_validate_resolvers_missing_from_expose => {
9635 input = {
9636 let mut decl = new_component_decl();
9637 decl.exposes = Some(vec![fdecl::Expose::Resolver(fdecl::ExposeResolver {
9638 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
9639 source_name: Some("a".to_string()),
9640 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
9641 target_name: Some("a".to_string()),
9642 ..Default::default()
9643 })]);
9644 decl
9645 },
9646 result = Err(ErrorList::new(vec![
9647 Error::invalid_capability(DeclType::ExposeResolver, "source", "a"),
9648 ])),
9649 },
9650
9651 test_validate_config_missing_config => {
9652 input = {
9653 let mut decl = new_component_decl();
9654 decl.config = Some(fdecl::ConfigSchema{
9655 fields: None,
9656 checksum: None,
9657 value_source: None,
9658 ..Default::default()
9659 });
9660 decl
9661 },
9662 result = Err(ErrorList::new(vec![
9663 Error::missing_field(DeclType::ConfigSchema, "fields"),
9664 Error::missing_field(DeclType::ConfigSchema, "checksum"),
9665 Error::missing_field(DeclType::ConfigSchema, "value_source"),
9666 ])),
9667 },
9668
9669 test_validate_config_missing_config_field => {
9670 input = {
9671 let mut decl = new_component_decl();
9672 decl.config = Some(fdecl::ConfigSchema{
9673 fields: Some(vec![
9674 fdecl::ConfigField {
9675 key: None,
9676 type_: None,
9677 ..Default::default()
9678 }
9679 ]),
9680 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9681 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9682 ..Default::default()
9683 });
9684 decl
9685 },
9686 result = Err(ErrorList::new(vec![
9687 Error::missing_field(DeclType::ConfigField, "key"),
9688 Error::missing_field(DeclType::ConfigField, "value_type"),
9689 ])),
9690 },
9691
9692 test_validate_config_bool => {
9693 input = {
9694 let mut decl = new_component_decl();
9695 decl.config = Some(fdecl::ConfigSchema{
9696 fields: Some(vec![
9697 fdecl::ConfigField {
9698 key: Some("test".to_string()),
9699 type_: Some(fdecl::ConfigType {
9700 layout: fdecl::ConfigTypeLayout::Bool,
9701 parameters: Some(vec![]),
9702 constraints: vec![]
9703 }),
9704 ..Default::default()
9705 }
9706 ]),
9707 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9708 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9709 ..Default::default()
9710 });
9711 decl
9712 },
9713 result = Ok(()),
9714 },
9715
9716 test_validate_config_bool_extra_constraint => {
9717 input = {
9718 let mut decl = new_component_decl();
9719 decl.config = Some(fdecl::ConfigSchema{
9720 fields: Some(vec![
9721 fdecl::ConfigField {
9722 key: Some("test".to_string()),
9723 type_: Some(fdecl::ConfigType {
9724 layout: fdecl::ConfigTypeLayout::Bool,
9725 parameters: Some(vec![]),
9726 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9727 }),
9728 ..Default::default()
9729 }
9730 ]),
9731 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9732 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9733 ..Default::default()
9734 });
9735 decl
9736 },
9737 result = Err(ErrorList::new(vec![
9738 Error::extraneous_field(DeclType::ConfigType, "constraints")
9739 ])),
9740 },
9741
9742 test_validate_config_bool_missing_parameters => {
9743 input = {
9744 let mut decl = new_component_decl();
9745 decl.config = Some(fdecl::ConfigSchema{
9746 fields: Some(vec![
9747 fdecl::ConfigField {
9748 key: Some("test".to_string()),
9749 type_: Some(fdecl::ConfigType {
9750 layout: fdecl::ConfigTypeLayout::Bool,
9751 parameters: None,
9752 constraints: vec![]
9753 }),
9754 ..Default::default()
9755 }
9756 ]),
9757 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9758 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9759 ..Default::default()
9760 });
9761 decl
9762 },
9763 result = Err(ErrorList::new(vec![
9764 Error::missing_field(DeclType::ConfigType, "parameters")
9765 ])),
9766 },
9767
9768 test_validate_config_string => {
9769 input = {
9770 let mut decl = new_component_decl();
9771 decl.config = Some(fdecl::ConfigSchema{
9772 fields: Some(vec![
9773 fdecl::ConfigField {
9774 key: Some("test".to_string()),
9775 type_: Some(fdecl::ConfigType {
9776 layout: fdecl::ConfigTypeLayout::String,
9777 parameters: Some(vec![]),
9778 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9779 }),
9780 ..Default::default()
9781 }
9782 ]),
9783 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9784 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9785 ..Default::default()
9786 });
9787 decl
9788 },
9789 result = Ok(()),
9790 },
9791
9792 test_validate_config_string_missing_parameter => {
9793 input = {
9794 let mut decl = new_component_decl();
9795 decl.config = Some(fdecl::ConfigSchema{
9796 fields: Some(vec![
9797 fdecl::ConfigField {
9798 key: Some("test".to_string()),
9799 type_: Some(fdecl::ConfigType {
9800 layout: fdecl::ConfigTypeLayout::String,
9801 parameters: None,
9802 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9803 }),
9804 ..Default::default()
9805 }
9806 ]),
9807 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9808 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9809 ..Default::default()
9810 });
9811 decl
9812 },
9813 result = Err(ErrorList::new(vec![
9814 Error::missing_field(DeclType::ConfigType, "parameters")
9815 ])),
9816 },
9817
9818 test_validate_config_string_missing_constraint => {
9819 input = {
9820 let mut decl = new_component_decl();
9821 decl.config = Some(fdecl::ConfigSchema{
9822 fields: Some(vec![
9823 fdecl::ConfigField {
9824 key: Some("test".to_string()),
9825 type_: Some(fdecl::ConfigType {
9826 layout: fdecl::ConfigTypeLayout::String,
9827 parameters: Some(vec![]),
9828 constraints: vec![]
9829 }),
9830 ..Default::default()
9831 }
9832 ]),
9833 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9834 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9835 ..Default::default()
9836 });
9837 decl
9838 },
9839 result = Err(ErrorList::new(vec![
9840 Error::missing_field(DeclType::ConfigType, "constraints")
9841 ])),
9842 },
9843
9844 test_validate_config_string_extra_constraint => {
9845 input = {
9846 let mut decl = new_component_decl();
9847 decl.config = Some(fdecl::ConfigSchema{
9848 fields: Some(vec![
9849 fdecl::ConfigField {
9850 key: Some("test".to_string()),
9851 type_: Some(fdecl::ConfigType {
9852 layout: fdecl::ConfigTypeLayout::String,
9853 parameters: Some(vec![]),
9854 constraints: vec![fdecl::LayoutConstraint::MaxSize(10), fdecl::LayoutConstraint::MaxSize(10)]
9855 }),
9856 ..Default::default()
9857 }
9858 ]),
9859 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9860 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9861 ..Default::default()
9862 });
9863 decl
9864 },
9865 result = Err(ErrorList::new(vec![
9866 Error::extraneous_field(DeclType::ConfigType, "constraints")
9867 ])),
9868 },
9869
9870 test_validate_config_vector_bool => {
9871 input = {
9872 let mut decl = new_component_decl();
9873 decl.config = Some(fdecl::ConfigSchema{
9874 fields: Some(vec![
9875 fdecl::ConfigField {
9876 key: Some("test".to_string()),
9877 type_: Some(fdecl::ConfigType {
9878 layout: fdecl::ConfigTypeLayout::Vector,
9879 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9880 layout: fdecl::ConfigTypeLayout::Bool,
9881 parameters: Some(vec![]),
9882 constraints: vec![],
9883 })]),
9884 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9885 }),
9886 ..Default::default()
9887 }
9888 ]),
9889 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9890 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9891 ..Default::default()
9892 });
9893 decl
9894 },
9895 result = Ok(()),
9896 },
9897
9898 test_validate_config_vector_extra_parameter => {
9899 input = {
9900 let mut decl = new_component_decl();
9901 decl.config = Some(fdecl::ConfigSchema{
9902 fields: Some(vec![
9903 fdecl::ConfigField {
9904 key: Some("test".to_string()),
9905 type_: Some(fdecl::ConfigType {
9906 layout: fdecl::ConfigTypeLayout::Vector,
9907 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9908 layout: fdecl::ConfigTypeLayout::Bool,
9909 parameters: Some(vec![]),
9910 constraints: vec![],
9911 }), fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9912 layout: fdecl::ConfigTypeLayout::Uint8,
9913 parameters: Some(vec![]),
9914 constraints: vec![],
9915 })]),
9916 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9917 }),
9918 ..Default::default()
9919 }
9920 ]),
9921 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9922 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9923 ..Default::default()
9924 });
9925 decl
9926 },
9927 result = Err(ErrorList::new(vec![
9928 Error::extraneous_field(DeclType::ConfigType, "parameters")
9929 ])),
9930 },
9931
9932 test_validate_config_vector_missing_parameter => {
9933 input = {
9934 let mut decl = new_component_decl();
9935 decl.config = Some(fdecl::ConfigSchema{
9936 fields: Some(vec![
9937 fdecl::ConfigField {
9938 key: Some("test".to_string()),
9939 type_: Some(fdecl::ConfigType {
9940 layout: fdecl::ConfigTypeLayout::Vector,
9941 parameters: Some(vec![]),
9942 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9943 }),
9944 ..Default::default()
9945 }
9946 ]),
9947 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9948 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9949 ..Default::default()
9950 });
9951 decl
9952 },
9953 result = Err(ErrorList::new(vec![
9954 Error::missing_field(DeclType::ConfigType, "parameters")
9955 ])),
9956 },
9957
9958 test_validate_config_vector_string => {
9959 input = {
9960 let mut decl = new_component_decl();
9961 decl.config = Some(fdecl::ConfigSchema{
9962 fields: Some(vec![
9963 fdecl::ConfigField {
9964 key: Some("test".to_string()),
9965 type_: Some(fdecl::ConfigType {
9966 layout: fdecl::ConfigTypeLayout::Vector,
9967 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9968 layout: fdecl::ConfigTypeLayout::String,
9969 parameters: Some(vec![]),
9970 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9971 })]),
9972 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9973 }),
9974 ..Default::default()
9975 }
9976 ]),
9977 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9978 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9979 ..Default::default()
9980 });
9981 decl
9982 },
9983 result = Ok(()),
9984 },
9985
9986 test_validate_config_vector_vector => {
9987 input = {
9988 let mut decl = new_component_decl();
9989 decl.config = Some(fdecl::ConfigSchema{
9990 fields: Some(vec![
9991 fdecl::ConfigField {
9992 key: Some("test".to_string()),
9993 type_: Some(fdecl::ConfigType {
9994 layout: fdecl::ConfigTypeLayout::Vector,
9995 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9996 layout: fdecl::ConfigTypeLayout::Vector,
9997 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9998 layout: fdecl::ConfigTypeLayout::String,
9999 parameters: Some(vec![]),
10000 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
10001 })]),
10002 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
10003 })]),
10004 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
10005 }),
10006 ..Default::default()
10007 }
10008 ]),
10009 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
10010 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
10011 ..Default::default()
10012 });
10013 decl
10014 },
10015 result = Err(ErrorList::new(vec![
10016 Error::nested_vector()
10017 ])),
10018 },
10019
10020 test_validate_exposes_invalid_aggregation_different_availability => {
10021 input = {
10022 let mut decl = new_component_decl();
10023 decl.exposes = Some(vec![
10024 fdecl::Expose::Service(fdecl::ExposeService {
10025 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10026 name: "coll_a".into()
10027 })),
10028 source_name: Some("fuchsia.logger.Log".to_string()),
10029 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
10030 target_name: Some("fuchsia.logger.Log".to_string()),
10031 availability: Some(fdecl::Availability::Required),
10032 ..Default::default()
10033 }),
10034 fdecl::Expose::Service(fdecl::ExposeService {
10035 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10036 name: "coll_b".into()
10037 })),
10038 source_name: Some("fuchsia.logger.Log".to_string()),
10039 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
10040 target_name: Some("fuchsia.logger.Log".to_string()),
10041 availability: Some(fdecl::Availability::Optional),
10042 ..Default::default()
10043 })
10044 ]);
10045 decl.collections = Some(vec![
10046 fdecl::Collection {
10047 name: Some("coll_a".to_string()),
10048 durability: Some(fdecl::Durability::Transient),
10049 ..Default::default()
10050 },
10051 fdecl::Collection {
10052 name: Some("coll_b".to_string()),
10053 durability: Some(fdecl::Durability::Transient),
10054 ..Default::default()
10055 },
10056 ]);
10057 decl
10058 },
10059 result = Err(ErrorList::new(vec![
10060 Error::DifferentAvailabilityInAggregation(AvailabilityList(vec![
10061 fdecl::Availability::Required,
10062 fdecl::Availability::Optional,
10063 ]))
10064 ])),
10065 },
10066
10067 test_validate_offers_invalid_aggregation_different_availability => {
10068 input = {
10069 let mut decl = new_component_decl();
10070 decl.offers = Some(vec![
10071 fdecl::Offer::Service(fdecl::OfferService {
10072 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10073 name: "coll_a".into()
10074 })),
10075 source_name: Some("fuchsia.logger.Log".to_string()),
10076 target: Some(fdecl::Ref::Child(
10077 fdecl::ChildRef {
10078 name: "child_c".to_string(),
10079 collection: None,
10080 }
10081 )),
10082 target_name: Some("fuchsia.logger.Log".to_string()),
10083 source_instance_filter: Some(vec!["default".to_string()]),
10084 availability: Some(fdecl::Availability::Required),
10085 ..Default::default()
10086 }),
10087 fdecl::Offer::Service(fdecl::OfferService {
10088 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10089 name: "coll_b".into()
10090 })),
10091 source_name: Some("fuchsia.logger.Log".to_string()),
10092 target: Some(fdecl::Ref::Child(
10093 fdecl::ChildRef {
10094 name: "child_c".to_string(),
10095 collection: None,
10096 }
10097 )),
10098 target_name: Some("fuchsia.logger.Log".to_string()),
10099 source_instance_filter: Some(vec!["a_different_default".to_string()]),
10100 availability: Some(fdecl::Availability::Optional),
10101 ..Default::default()
10102 })
10103 ]);
10104 decl.collections = Some(vec![
10105 fdecl::Collection {
10106 name: Some("coll_a".to_string()),
10107 durability: Some(fdecl::Durability::Transient),
10108 ..Default::default()
10109 },
10110 fdecl::Collection {
10111 name: Some("coll_b".to_string()),
10112 durability: Some(fdecl::Durability::Transient),
10113 ..Default::default()
10114 },
10115 ]);
10116 decl.children = Some(vec![
10117 fdecl::Child {
10118 name: Some("child_c".to_string()),
10119 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
10120 startup: Some(fdecl::StartupMode::Lazy),
10121 on_terminate: None,
10122 environment: None,
10123 ..Default::default()
10124 },
10125 ]);
10126 decl
10127 },
10128 result = Err(ErrorList::new(vec![
10129 Error::DifferentAvailabilityInAggregation(AvailabilityList(vec![
10130 fdecl::Availability::Required,
10131 fdecl::Availability::Optional,
10132 ]))
10133 ])),
10134 },
10135 }
10136
10137 test_validate_capabilities! {
10138 test_validate_capabilities_individually_ok => {
10139 input = vec![
10140 fdecl::Capability::Protocol(fdecl::Protocol {
10141 name: Some("foo_svc".into()),
10142 source_path: Some("/svc/foo".into()),
10143 ..Default::default()
10144 }),
10145 fdecl::Capability::Directory(fdecl::Directory {
10146 name: Some("foo_dir".into()),
10147 source_path: Some("/foo".into()),
10148 rights: Some(fio::Operations::CONNECT),
10149 ..Default::default()
10150 }),
10151 ],
10152 as_builtin = false,
10153 result = Ok(()),
10154 },
10155 test_validate_capabilities_individually_err => {
10156 input = vec![
10157 fdecl::Capability::Protocol(fdecl::Protocol {
10158 name: None,
10159 source_path: None,
10160 ..Default::default()
10161 }),
10162 fdecl::Capability::Directory(fdecl::Directory {
10163 name: None,
10164 source_path: None,
10165 rights: None,
10166 ..Default::default()
10167 }),
10168 ],
10169 as_builtin = false,
10170 result = Err(ErrorList::new(vec![
10171 Error::missing_field(DeclType::Protocol, "name"),
10172 Error::missing_field(DeclType::Protocol, "source_path"),
10173 Error::missing_field(DeclType::Directory, "name"),
10174 Error::missing_field(DeclType::Directory, "source_path"),
10175 Error::missing_field(DeclType::Directory, "rights"),
10176 ])),
10177 },
10178 test_validate_builtin_capabilities_individually_ok => {
10179 input = vec![
10180 fdecl::Capability::Protocol(fdecl::Protocol {
10181 name: Some("foo_protocol".into()),
10182 source_path: None,
10183 ..Default::default()
10184 }),
10185 fdecl::Capability::Directory(fdecl::Directory {
10186 name: Some("foo_dir".into()),
10187 source_path: None,
10188 rights: Some(fio::Operations::CONNECT),
10189 ..Default::default()
10190 }),
10191 fdecl::Capability::Service(fdecl::Service {
10192 name: Some("foo_svc".into()),
10193 source_path: None,
10194 ..Default::default()
10195 }),
10196 fdecl::Capability::Runner(fdecl::Runner {
10197 name: Some("foo_runner".into()),
10198 source_path: None,
10199 ..Default::default()
10200 }),
10201 fdecl::Capability::Resolver(fdecl::Resolver {
10202 name: Some("foo_resolver".into()),
10203 source_path: None,
10204 ..Default::default()
10205 }),
10206 ],
10207 as_builtin = true,
10208 result = Ok(()),
10209 },
10210 test_validate_builtin_capabilities_individually_err => {
10211 input = vec![
10212 fdecl::Capability::Protocol(fdecl::Protocol {
10213 name: None,
10214 source_path: Some("/svc/foo".into()),
10215 ..Default::default()
10216 }),
10217 fdecl::Capability::Directory(fdecl::Directory {
10218 name: None,
10219 source_path: Some("/foo".into()),
10220 rights: None,
10221 ..Default::default()
10222 }),
10223 fdecl::Capability::Service(fdecl::Service {
10224 name: None,
10225 source_path: Some("/svc/foo".into()),
10226 ..Default::default()
10227 }),
10228 fdecl::Capability::Runner(fdecl::Runner {
10229 name: None,
10230 source_path: Some("/foo".into()),
10231 ..Default::default()
10232 }),
10233 fdecl::Capability::Resolver(fdecl::Resolver {
10234 name: None,
10235 source_path: Some("/foo".into()),
10236 ..Default::default()
10237 }),
10238 fdecl::Capability::Storage(fdecl::Storage {
10239 name: None,
10240 ..Default::default()
10241 }),
10242 ],
10243 as_builtin = true,
10244 result = Err(ErrorList::new(vec![
10245 Error::missing_field(DeclType::Protocol, "name"),
10246 Error::extraneous_source_path(DeclType::Protocol, "/svc/foo"),
10247 Error::missing_field(DeclType::Directory, "name"),
10248 Error::extraneous_source_path(DeclType::Directory, "/foo"),
10249 Error::missing_field(DeclType::Directory, "rights"),
10250 Error::missing_field(DeclType::Service, "name"),
10251 Error::extraneous_source_path(DeclType::Service, "/svc/foo"),
10252 Error::missing_field(DeclType::Runner, "name"),
10253 Error::extraneous_source_path(DeclType::Runner, "/foo"),
10254 Error::missing_field(DeclType::Resolver, "name"),
10255 Error::extraneous_source_path(DeclType::Resolver, "/foo"),
10256 Error::CapabilityCannotBeBuiltin(DeclType::Storage),
10257 ])),
10258 },
10259 test_validate_delivery_type_ok => {
10260 input = vec![
10261 fdecl::Capability::Protocol(fdecl::Protocol {
10262 name: Some("foo_svc1".into()),
10263 source_path: Some("/svc/foo1".into()),
10264 ..Default::default()
10265 }),
10266 fdecl::Capability::Protocol(fdecl::Protocol {
10267 name: Some("foo_svc2".into()),
10268 source_path: Some("/svc/foo2".into()),
10269 delivery: Some(fdecl::DeliveryType::Immediate),
10270 ..Default::default()
10271 }),
10272 fdecl::Capability::Protocol(fdecl::Protocol {
10273 name: Some("foo_svc3".into()),
10274 source_path: Some("/svc/foo3".into()),
10275 delivery: Some(fdecl::DeliveryType::OnReadable),
10276 ..Default::default()
10277 }),
10278 ],
10279 as_builtin = false,
10280 result = Ok(()),
10281 },
10282 test_validate_delivery_type_err => {
10283 input = vec![
10284 fdecl::Capability::Protocol(fdecl::Protocol {
10285 name: Some("foo_svc".into()),
10286 source_path: Some("/svc/foo".into()),
10287 delivery: Some(fdecl::DeliveryType::unknown()),
10288 ..Default::default()
10289 }),
10290 ],
10291 as_builtin = false,
10292 result = Err(ErrorList::new(vec![
10293 Error::invalid_field(DeclType::Protocol, "delivery"),
10294 ])),
10295 },
10296 }
10297
10298 fn generate_expose_different_source_and_availability_decl(
10301 new_expose: impl Fn(fdecl::Ref, fdecl::Availability, &str) -> fdecl::Expose,
10302 ) -> fdecl::Component {
10303 let mut decl = new_component_decl();
10304 let child = "child";
10305 decl.children = Some(vec![fdecl::Child {
10306 name: Some(child.to_string()),
10307 url: Some("fuchsia-pkg://fuchsia.com/source#meta/source.cm".to_string()),
10308 startup: Some(fdecl::StartupMode::Lazy),
10309 ..Default::default()
10310 }]);
10311 decl.exposes = Some(vec![
10312 new_expose(
10314 fdecl::Ref::Self_(fdecl::SelfRef {}),
10315 fdecl::Availability::Optional,
10316 "fuchsia.examples.Echo1",
10317 ),
10318 new_expose(
10320 fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
10321 fdecl::Availability::Optional,
10322 "fuchsia.examples.Echo2",
10323 ),
10324 new_expose(
10326 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10327 fdecl::Availability::Optional,
10328 "fuchsia.examples.Echo3",
10329 ),
10330 new_expose(
10332 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10333 fdecl::Availability::Optional,
10334 "fuchsia.examples.Echo4",
10335 ),
10336 new_expose(
10338 fdecl::Ref::Self_(fdecl::SelfRef {}),
10339 fdecl::Availability::Transitional,
10340 "fuchsia.examples.Echo5",
10341 ),
10342 new_expose(
10344 fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
10345 fdecl::Availability::Transitional,
10346 "fuchsia.examples.Echo6",
10347 ),
10348 new_expose(
10350 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10351 fdecl::Availability::Transitional,
10352 "fuchsia.examples.Echo7",
10353 ),
10354 new_expose(
10356 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10357 fdecl::Availability::Transitional,
10358 "fuchsia.examples.Echo8",
10359 ),
10360 new_expose(
10362 fdecl::Ref::Self_(fdecl::SelfRef {}),
10363 fdecl::Availability::SameAsTarget,
10364 "fuchsia.examples.Echo9",
10365 ),
10366 new_expose(
10368 fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
10369 fdecl::Availability::SameAsTarget,
10370 "fuchsia.examples.Echo10",
10371 ),
10372 new_expose(
10374 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10375 fdecl::Availability::SameAsTarget,
10376 "fuchsia.examples.Echo11",
10377 ),
10378 new_expose(
10380 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10381 fdecl::Availability::Required,
10382 "fuchsia.examples.Echo12",
10383 ),
10384 new_expose(
10386 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10387 fdecl::Availability::SameAsTarget,
10388 "fuchsia.examples.Echo13",
10389 ),
10390 ]);
10391 decl
10392 }
10393
10394 fn generate_offer_different_source_and_availability_decl(
10397 new_offer: impl Fn(fdecl::Ref, fdecl::Availability, &str) -> fdecl::Offer,
10398 ) -> fdecl::Component {
10399 let mut decl = new_component_decl();
10400 decl.children = Some(vec![
10401 fdecl::Child {
10402 name: Some("source".to_string()),
10403 url: Some("fuchsia-pkg://fuchsia.com/source#meta/source.cm".to_string()),
10404 startup: Some(fdecl::StartupMode::Lazy),
10405 on_terminate: None,
10406 environment: None,
10407 ..Default::default()
10408 },
10409 fdecl::Child {
10410 name: Some("sink".to_string()),
10411 url: Some("fuchsia-pkg://fuchsia.com/sink#meta/sink.cm".to_string()),
10412 startup: Some(fdecl::StartupMode::Lazy),
10413 on_terminate: None,
10414 environment: None,
10415 ..Default::default()
10416 },
10417 ]);
10418 decl.offers = Some(vec![
10419 new_offer(
10422 fdecl::Ref::Parent(fdecl::ParentRef {}),
10423 fdecl::Availability::Required,
10424 "fuchsia.examples.Echo0",
10425 ),
10426 new_offer(
10427 fdecl::Ref::Parent(fdecl::ParentRef {}),
10428 fdecl::Availability::Optional,
10429 "fuchsia.examples.Echo1",
10430 ),
10431 new_offer(
10432 fdecl::Ref::Parent(fdecl::ParentRef {}),
10433 fdecl::Availability::SameAsTarget,
10434 "fuchsia.examples.Echo2",
10435 ),
10436 new_offer(
10437 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10438 fdecl::Availability::Optional,
10439 "fuchsia.examples.Echo3",
10440 ),
10441 new_offer(
10444 fdecl::Ref::Self_(fdecl::SelfRef {}),
10445 fdecl::Availability::Optional,
10446 "fuchsia.examples.Echo4",
10447 ),
10448 new_offer(
10449 fdecl::Ref::Self_(fdecl::SelfRef {}),
10450 fdecl::Availability::SameAsTarget,
10451 "fuchsia.examples.Echo5",
10452 ),
10453 new_offer(
10454 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10455 fdecl::Availability::Optional,
10456 "fuchsia.examples.Echo6",
10457 ),
10458 new_offer(
10459 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10460 fdecl::Availability::SameAsTarget,
10461 "fuchsia.examples.Echo7",
10462 ),
10463 new_offer(
10464 fdecl::Ref::Child(fdecl::ChildRef { name: "source".to_string(), collection: None }),
10465 fdecl::Availability::Optional,
10466 "fuchsia.examples.Echo8",
10467 ),
10468 new_offer(
10469 fdecl::Ref::Child(fdecl::ChildRef { name: "source".to_string(), collection: None }),
10470 fdecl::Availability::SameAsTarget,
10471 "fuchsia.examples.Echo9",
10472 ),
10473 new_offer(
10475 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10476 fdecl::Availability::Required,
10477 "fuchsia.examples.Echo10",
10478 ),
10479 new_offer(
10480 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10481 fdecl::Availability::SameAsTarget,
10482 "fuchsia.examples.Echo11",
10483 ),
10484 ]);
10485 decl
10486 }
10487
10488 #[test]
10489 fn test_validate_dynamic_offers_empty() {
10490 assert_eq!(
10491 validate_dynamic_offers(vec![], [], [], &[], &[], &mut DirectedGraph::new(), &vec![]),
10492 Ok(())
10493 );
10494 }
10495
10496 #[test]
10497 fn test_validate_dynamic_offers_okay() {
10498 assert_eq!(
10499 validate_dynamic_offers(
10500 vec![],
10501 [],
10502 [],
10503 &[fdecl::Capability::Protocol(fdecl::Protocol {
10504 name: Some("thing".to_string()),
10505 source_path: Some("/svc/foo".into()),
10506 ..Default::default()
10507 })],
10508 &[],
10509 &mut DirectedGraph::new(),
10510 &vec![
10511 fdecl::Offer::Protocol(fdecl::OfferProtocol {
10512 dependency_type: Some(fdecl::DependencyType::Strong),
10513 source: Some(fdecl::Ref::Self_(fdecl::SelfRef)),
10514 source_name: Some("thing".to_string()),
10515 target_name: Some("thing".repeat(26)),
10516 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10517 name: "foo".to_string(),
10518 collection: Some("foo".to_string()),
10519 })),
10520 ..Default::default()
10521 }),
10522 fdecl::Offer::Service(fdecl::OfferService {
10523 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10524 source_name: Some("thang".repeat(26)),
10525 target_name: Some("thang".repeat(26)),
10526 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10527 name: "foo".to_string(),
10528 collection: Some("foo".to_string()),
10529 })),
10530 ..Default::default()
10531 }),
10532 fdecl::Offer::Directory(fdecl::OfferDirectory {
10533 dependency_type: Some(fdecl::DependencyType::Strong),
10534 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10535 source_name: Some("thung1".repeat(26)),
10536 target_name: Some("thung1".repeat(26)),
10537 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10538 name: "foo".to_string(),
10539 collection: Some("foo".to_string()),
10540 })),
10541 ..Default::default()
10542 }),
10543 fdecl::Offer::Storage(fdecl::OfferStorage {
10544 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10545 source_name: Some("thung2".repeat(26)),
10546 target_name: Some("thung2".repeat(26)),
10547 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10548 name: "foo".to_string(),
10549 collection: Some("foo".to_string()),
10550 })),
10551 ..Default::default()
10552 }),
10553 fdecl::Offer::Runner(fdecl::OfferRunner {
10554 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10555 source_name: Some("thung3".repeat(26)),
10556 target_name: Some("thung3".repeat(26)),
10557 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10558 name: "foo".to_string(),
10559 collection: Some("foo".to_string()),
10560 })),
10561 ..Default::default()
10562 }),
10563 fdecl::Offer::Resolver(fdecl::OfferResolver {
10564 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10565 source_name: Some("thung4".repeat(26)),
10566 target_name: Some("thung4".repeat(26)),
10567 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10568 name: "foo".to_string(),
10569 collection: Some("foo".to_string()),
10570 })),
10571 ..Default::default()
10572 }),
10573 ],
10574 ),
10575 Ok(())
10576 );
10577 }
10578
10579 #[test]
10580 fn test_validate_dynamic_offers_valid_service_aggregation() {
10581 assert_eq!(
10582 validate_dynamic_offers(
10583 vec![],
10584 ["child_a", "child_b", "child_c"],
10585 [],
10586 &[],
10587 &[],
10588 &mut DirectedGraph::new(),
10589 &vec![
10590 fdecl::Offer::Service(fdecl::OfferService {
10591 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10592 name: "child_a".to_string(),
10593 collection: None
10594 })),
10595 source_name: Some("fuchsia.logger.Log".to_string()),
10596 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10597 name: "child_c".to_string(),
10598 collection: None,
10599 })),
10600 target_name: Some("fuchsia.logger.Log".to_string()),
10601 source_instance_filter: Some(vec!["default".to_string()]),
10602 ..Default::default()
10603 }),
10604 fdecl::Offer::Service(fdecl::OfferService {
10605 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10606 name: "child_b".to_string(),
10607 collection: None
10608 })),
10609 source_name: Some("fuchsia.logger.Log".to_string()),
10610 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10611 name: "child_c".to_string(),
10612 collection: None,
10613 })),
10614 target_name: Some("fuchsia.logger.Log".to_string()),
10615 source_instance_filter: Some(vec!["a_different_default".to_string()]),
10616 ..Default::default()
10617 })
10618 ],
10619 ),
10620 Ok(())
10621 );
10622 }
10623
10624 #[test]
10625 fn test_validate_dynamic_service_aggregation_missing_filter() {
10626 assert_eq!(
10627 validate_dynamic_offers(
10628 vec![],
10629 ["child_a", "child_b", "child_c"],
10630 [],
10631 &[],
10632 &[],
10633 &mut DirectedGraph::new(),
10634 &vec![
10635 fdecl::Offer::Service(fdecl::OfferService {
10636 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10637 name: "child_a".to_string(),
10638 collection: None
10639 })),
10640 source_name: Some("fuchsia.logger.Log".to_string()),
10641 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10642 name: "child_c".to_string(),
10643 collection: None,
10644 })),
10645 target_name: Some("fuchsia.logger.Log".to_string()),
10646 source_instance_filter: Some(vec!["default".to_string()]),
10647 ..Default::default()
10648 }),
10649 fdecl::Offer::Service(fdecl::OfferService {
10650 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10651 name: "child_b".to_string(),
10652 collection: None
10653 })),
10654 source_name: Some("fuchsia.logger.Log".to_string()),
10655 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10656 name: "child_c".to_string(),
10657 collection: None,
10658 })),
10659 target_name: Some("fuchsia.logger.Log".to_string()),
10660 source_instance_filter: None,
10661 ..Default::default()
10662 }),
10663 ],
10664 ),
10665 Err(ErrorList::new(vec![Error::invalid_aggregate_offer(
10666 "source_instance_filter must be set for dynamic aggregate service offers"
10667 ),]))
10668 );
10669 }
10670
10671 #[test]
10672 fn test_validate_dynamic_offers_omit_target() {
10673 assert_eq!(
10674 validate_dynamic_offers(
10675 vec![],
10676 [],
10677 [],
10678 &[fdecl::Capability::Protocol(fdecl::Protocol {
10679 name: Some("thing".to_string()),
10680 source_path: Some("/svc/foo".into()),
10681 ..Default::default()
10682 })],
10683 &[],
10684 &mut DirectedGraph::new(),
10685 &vec![
10686 fdecl::Offer::Protocol(fdecl::OfferProtocol {
10687 dependency_type: Some(fdecl::DependencyType::Strong),
10688 source: Some(fdecl::Ref::Self_(fdecl::SelfRef)),
10689 source_name: Some("thing".to_string()),
10690 target_name: Some("thing".to_string()),
10691 ..Default::default()
10692 }),
10693 fdecl::Offer::Service(fdecl::OfferService {
10694 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10695 source_name: Some("thang".to_string()),
10696 target_name: Some("thang".to_string()),
10697 ..Default::default()
10698 }),
10699 fdecl::Offer::Directory(fdecl::OfferDirectory {
10700 dependency_type: Some(fdecl::DependencyType::Strong),
10701 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10702 source_name: Some("thung1".to_string()),
10703 target_name: Some("thung1".to_string()),
10704 ..Default::default()
10705 }),
10706 fdecl::Offer::Storage(fdecl::OfferStorage {
10707 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10708 source_name: Some("thung2".to_string()),
10709 target_name: Some("thung2".to_string()),
10710 ..Default::default()
10711 }),
10712 fdecl::Offer::Runner(fdecl::OfferRunner {
10713 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10714 source_name: Some("thung3".to_string()),
10715 target_name: Some("thung3".to_string()),
10716 ..Default::default()
10717 }),
10718 fdecl::Offer::Resolver(fdecl::OfferResolver {
10719 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10720 source_name: Some("thung4".to_string()),
10721 target_name: Some("thung4".to_string()),
10722 ..Default::default()
10723 }),
10724 ],
10725 ),
10726 Err(ErrorList::new(vec![
10727 Error::missing_field(DeclType::OfferProtocol, "target"),
10728 Error::missing_field(DeclType::OfferService, "target"),
10729 Error::missing_field(DeclType::OfferDirectory, "target"),
10730 Error::missing_field(DeclType::OfferStorage, "target"),
10731 Error::missing_field(DeclType::OfferRunner, "target"),
10732 Error::missing_field(DeclType::OfferResolver, "target"),
10733 ]))
10734 );
10735 }
10736
10737 #[test]
10738 fn test_validate_dynamic_offers_collection_collision() {
10739 assert_eq!(
10740 validate_dynamic_offers(
10741 vec![],
10742 [],
10743 ["coll"],
10744 &[],
10745 &[fdecl::Offer::Protocol(fdecl::OfferProtocol {
10746 dependency_type: Some(fdecl::DependencyType::Strong),
10747 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10748 source_name: Some("thing".to_string()),
10749 target_name: Some("thing".to_string()),
10750 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10751 name: "coll".into()
10752 })),
10753 ..Default::default()
10754 })],
10755 &mut DirectedGraph::new(),
10756 &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10757 dependency_type: Some(fdecl::DependencyType::Strong),
10758 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10759 source_name: Some("thing".to_string()),
10760 target_name: Some("thing".to_string()),
10761 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10762 name: "child".to_string(),
10763 collection: Some("coll".to_string()),
10764 })),
10765 ..Default::default()
10766 }),],
10767 ),
10768 Err(ErrorList::new(vec![Error::duplicate_field(
10769 DeclType::OfferProtocol,
10770 "target_name",
10771 "thing"
10772 ),]))
10773 );
10774 }
10775
10776 #[test]
10777 fn test_validate_dynamic_offers_cycle_collection_to_static_child() {
10778 assert_eq!(
10779 validate_dynamic_offers(
10780 vec![("dyn", "coll")],
10781 ["static_child"],
10782 ["coll"],
10783 &[],
10784 &[fdecl::Offer::Service(fdecl::OfferService {
10785 source_name: Some("foo".to_string()),
10786 target_name: Some("foo".to_string()),
10787 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10788 name: "coll".into(),
10789 })),
10790 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10791 name: "static_child".into(),
10792 collection: None,
10793 })),
10794 ..Default::default()
10795 })],
10796 &mut DirectedGraph::new(),
10797 &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10798 source_name: Some("bar".to_string()),
10799 target_name: Some("bar".to_string()),
10800 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10801 name: "static_child".into(),
10802 collection: None,
10803 })),
10804 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10805 name: "dyn".to_string(),
10806 collection: Some("coll".to_string()),
10807 })),
10808 dependency_type: Some(fdecl::DependencyType::Strong),
10809 ..Default::default()
10810 }),],
10811 ),
10812 Err(ErrorList::new(vec![Error::dependency_cycle(
10813 "{{child coll:dyn -> collection coll -> child static_child -> child coll:dyn}}"
10814 )]))
10815 );
10816 }
10817
10818 #[test]
10819 fn test_validate_dynamic_offers_cycle_collection_to_dynamic_child() {
10820 assert_eq!(
10821 validate_dynamic_offers(
10822 vec![("dyn", "coll1"), ("dyn", "coll2")],
10823 [],
10824 ["coll1", "coll2"],
10825 &[],
10826 &[],
10827 &mut DirectedGraph::new(),
10828 &vec![
10829 fdecl::Offer::Service(fdecl::OfferService {
10830 source_name: Some("foo".to_string()),
10831 target_name: Some("foo".to_string()),
10832 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10833 name: "coll2".into(),
10834 })),
10835 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10836 name: "dyn".into(),
10837 collection: Some("coll1".into()),
10838 })),
10839 ..Default::default()
10840 }),
10841 fdecl::Offer::Protocol(fdecl::OfferProtocol {
10842 source_name: Some("bar".to_string()),
10843 target_name: Some("bar".to_string()),
10844 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10845 name: "dyn".into(),
10846 collection: Some("coll1".into()),
10847 })),
10848 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10849 name: "dyn".to_string(),
10850 collection: Some("coll2".to_string()),
10851 })),
10852 dependency_type: Some(fdecl::DependencyType::Strong),
10853 ..Default::default()
10854 }),
10855 ],
10856 ),
10857 Err(ErrorList::new(vec![Error::dependency_cycle(
10858 "{{child coll1:dyn -> child coll2:dyn -> collection coll2 -> child coll1:dyn}}"
10859 )]))
10860 );
10861 }
10862
10863 #[test]
10864 fn test_validate_dynamic_offers_cycle_collection_to_dynamic_child_between_new_and_existing() {
10865 assert_eq!(
10866 validate_dynamic_offers(
10867 vec![("dyn", "coll1"), ("dyn", "coll2")],
10868 [],
10869 ["coll1", "coll2"],
10870 &[],
10871 &[],
10872 &mut DirectedGraph::from([(
10873 DependencyNode::Child("dyn".into(), Some("coll1".into())),
10874 DependencyNode::Child("dyn".into(), Some("coll2".into())),
10875 )]),
10876 &vec![fdecl::Offer::Service(fdecl::OfferService {
10877 source_name: Some("foo".to_string()),
10878 target_name: Some("foo".to_string()),
10879 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10880 name: "coll2".into(),
10881 })),
10882 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10883 name: "dyn".into(),
10884 collection: Some("coll1".into()),
10885 })),
10886 ..Default::default()
10887 }),],
10888 ),
10889 Err(ErrorList::new(vec![Error::dependency_cycle(
10890 "{{child coll1:dyn -> child coll2:dyn -> collection coll2 -> child coll1:dyn}}"
10891 )]))
10892 );
10893 }
10894
10895 #[test]
10896 fn test_validate_dynamic_offers_cycle_collection_to_collection() {
10897 assert_eq!(
10898 validate_dynamic_offers(
10899 vec![("dyn", "coll1"), ("dyn", "coll2")],
10900 [],
10901 ["coll1", "coll2"],
10902 &[],
10903 &[fdecl::Offer::Service(fdecl::OfferService {
10904 source_name: Some("foo".to_string()),
10905 target_name: Some("foo".to_string()),
10906 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10907 name: "coll1".into(),
10908 })),
10909 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10910 name: "coll2".into(),
10911 })),
10912 ..Default::default()
10913 })],
10914 &mut DirectedGraph::new(),
10915 &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10916 source_name: Some("bar".to_string()),
10917 target_name: Some("bar".to_string()),
10918 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10919 name: "dyn".into(),
10920 collection: Some("coll2".parse().unwrap()),
10921 })),
10922 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10923 name: "dyn".into(),
10924 collection: Some("coll1".parse().unwrap()),
10925 })),
10926 dependency_type: Some(fdecl::DependencyType::Strong),
10927 ..Default::default()
10928 }),],
10929 ),
10930 Err(ErrorList::new(vec![Error::dependency_cycle(
10931 "{{child coll1:dyn -> collection coll1 -> child coll2:dyn -> child coll1:dyn}}",
10932 )]))
10933 );
10934 }
10935
10936 #[test]
10937 fn test_validate_dynamic_child() {
10938 assert_eq!(
10939 Ok(()),
10940 validate_dynamic_child(&fdecl::Child {
10941 name: Some("a".repeat(MAX_LONG_NAME_LENGTH).to_string()),
10942 url: Some("test:///child".to_string()),
10943 startup: Some(fdecl::StartupMode::Lazy),
10944 on_terminate: None,
10945 environment: None,
10946 ..Default::default()
10947 })
10948 );
10949 }
10950
10951 #[test]
10952 fn test_validate_dynamic_child_environment_is_invalid() {
10953 assert_eq!(
10954 validate_dynamic_child(&fdecl::Child {
10955 name: Some("a".repeat(MAX_LONG_NAME_LENGTH).to_string()),
10956 url: Some("test:///child".to_string()),
10957 startup: Some(fdecl::StartupMode::Lazy),
10958 on_terminate: None,
10959 environment: Some("env".to_string()),
10960 ..Default::default()
10961 }),
10962 Err(ErrorList::new(vec![Error::DynamicChildWithEnvironment]))
10963 );
10964 }
10965
10966 #[test]
10967 fn test_validate_dynamic_offers_missing_stuff() {
10968 assert_eq!(
10969 validate_dynamic_offers(
10970 vec![],
10971 [],
10972 [],
10973 &[],
10974 &[],
10975 &mut DirectedGraph::new(),
10976 &vec![
10977 fdecl::Offer::Protocol(fdecl::OfferProtocol::default()),
10978 fdecl::Offer::Service(fdecl::OfferService::default()),
10979 fdecl::Offer::Directory(fdecl::OfferDirectory::default()),
10980 fdecl::Offer::Storage(fdecl::OfferStorage::default()),
10981 fdecl::Offer::Runner(fdecl::OfferRunner::default()),
10982 fdecl::Offer::Resolver(fdecl::OfferResolver::default()),
10983 ],
10984 ),
10985 Err(ErrorList::new(vec![
10986 Error::missing_field(DeclType::OfferProtocol, "source"),
10987 Error::missing_field(DeclType::OfferProtocol, "source_name"),
10988 Error::missing_field(DeclType::OfferProtocol, "target"),
10989 Error::missing_field(DeclType::OfferProtocol, "target_name"),
10990 Error::missing_field(DeclType::OfferProtocol, "dependency_type"),
10991 Error::missing_field(DeclType::OfferService, "source"),
10992 Error::missing_field(DeclType::OfferService, "source_name"),
10993 Error::missing_field(DeclType::OfferService, "target"),
10994 Error::missing_field(DeclType::OfferService, "target_name"),
10995 Error::missing_field(DeclType::OfferDirectory, "source"),
10996 Error::missing_field(DeclType::OfferDirectory, "source_name"),
10997 Error::missing_field(DeclType::OfferDirectory, "target"),
10998 Error::missing_field(DeclType::OfferDirectory, "target_name"),
10999 Error::missing_field(DeclType::OfferDirectory, "dependency_type"),
11000 Error::missing_field(DeclType::OfferStorage, "source"),
11001 Error::missing_field(DeclType::OfferStorage, "source_name"),
11002 Error::missing_field(DeclType::OfferStorage, "target"),
11003 Error::missing_field(DeclType::OfferStorage, "target_name"),
11004 Error::missing_field(DeclType::OfferRunner, "source"),
11005 Error::missing_field(DeclType::OfferRunner, "source_name"),
11006 Error::missing_field(DeclType::OfferRunner, "target"),
11007 Error::missing_field(DeclType::OfferRunner, "target_name"),
11008 Error::missing_field(DeclType::OfferResolver, "source"),
11009 Error::missing_field(DeclType::OfferResolver, "source_name"),
11010 Error::missing_field(DeclType::OfferResolver, "target"),
11011 Error::missing_field(DeclType::OfferResolver, "target_name"),
11012 ]))
11013 );
11014 }
11015
11016 test_dependency! {
11017 (test_validate_offers_protocol_dependency_cycle) => {
11018 ty = fdecl::Offer::Protocol,
11019 offer_decl = fdecl::OfferProtocol {
11020 source: None, target: None, source_name: Some(format!("thing")),
11023 target_name: Some(format!("thing")),
11024 dependency_type: Some(fdecl::DependencyType::Strong),
11025 ..Default::default()
11026 },
11027 },
11028 (test_validate_offers_directory_dependency_cycle) => {
11029 ty = fdecl::Offer::Directory,
11030 offer_decl = fdecl::OfferDirectory {
11031 source: None, target: None, source_name: Some(format!("thing")),
11034 target_name: Some(format!("thing")),
11035 rights: Some(fio::Operations::CONNECT),
11036 subdir: None,
11037 dependency_type: Some(fdecl::DependencyType::Strong),
11038 ..Default::default()
11039 },
11040 },
11041 (test_validate_offers_service_dependency_cycle) => {
11042 ty = fdecl::Offer::Service,
11043 offer_decl = fdecl::OfferService {
11044 source: None, target: None, source_name: Some(format!("thing")),
11047 target_name: Some(format!("thing")),
11048 ..Default::default()
11049 },
11050 },
11051 (test_validate_offers_runner_dependency_cycle) => {
11052 ty = fdecl::Offer::Runner,
11053 offer_decl = fdecl::OfferRunner {
11054 source: None, target: None, source_name: Some(format!("thing")),
11057 target_name: Some(format!("thing")),
11058 ..Default::default()
11059 },
11060 },
11061 (test_validate_offers_resolver_dependency_cycle) => {
11062 ty = fdecl::Offer::Resolver,
11063 offer_decl = fdecl::OfferResolver {
11064 source: None, target: None, source_name: Some(format!("thing")),
11067 target_name: Some(format!("thing")),
11068 ..Default::default()
11069 },
11070 },
11071 }
11072 test_weak_dependency! {
11073 (test_validate_offers_protocol_weak_dependency_cycle) => {
11074 ty = fdecl::Offer::Protocol,
11075 offer_decl = fdecl::OfferProtocol {
11076 source: None, target: None, source_name: Some(format!("thing")),
11079 target_name: Some(format!("thing")),
11080 dependency_type: None, ..Default::default()
11082 },
11083 },
11084 (test_validate_offers_directory_weak_dependency_cycle) => {
11085 ty = fdecl::Offer::Directory,
11086 offer_decl = fdecl::OfferDirectory {
11087 source: None, target: None, source_name: Some(format!("thing")),
11090 target_name: Some(format!("thing")),
11091 rights: Some(fio::Operations::CONNECT),
11092 subdir: None,
11093 dependency_type: None, ..Default::default()
11095 },
11096 },
11097 (test_validate_offers_service_weak_dependency_cycle) => {
11098 ty = fdecl::Offer::Service,
11099 offer_decl = fdecl::OfferService {
11100 source: None, target: None, source_name: Some(format!("thing")),
11103 target_name: Some(format!("thing")),
11104 dependency_type: None, ..Default::default()
11106 },
11107 },
11108 }
11109}