1pub(crate) mod util;
6
7pub mod error;
8
9pub use crate::util::check_url;
10
11use crate::error::*;
12use crate::util::*;
13use directed_graph::DirectedGraph;
14use fidl_fuchsia_component_decl as fdecl;
15use itertools::Itertools;
16use std::collections::{BTreeSet, HashMap, HashSet};
17use std::path::Path;
18
19trait HasAvailability {
20 fn availability(&self) -> fdecl::Availability;
21}
22
23impl HasAvailability for fdecl::ExposeService {
24 fn availability(&self) -> fdecl::Availability {
25 return self.availability.unwrap_or(fdecl::Availability::Required);
26 }
27}
28
29impl HasAvailability for fdecl::OfferService {
30 fn availability(&self) -> fdecl::Availability {
31 return self.availability.unwrap_or(fdecl::Availability::Required);
32 }
33}
34
35#[cfg(fuchsia_api_level_at_least = "25")]
36macro_rules! get_source_dictionary {
37 ($decl:ident) => {
38 $decl.source_dictionary.as_ref()
39 };
40}
41#[cfg(fuchsia_api_level_less_than = "25")]
42macro_rules! get_source_dictionary {
43 ($decl:ident) => {
44 None
45 };
46}
47
48pub fn validate_value_spec(spec: &fdecl::ConfigValueSpec) -> Result<(), ErrorList> {
52 let mut errors = vec![];
53 if let Some(value) = &spec.value {
54 match value {
55 fdecl::ConfigValue::Single(s) => match s {
56 fdecl::ConfigSingleValue::Bool(_)
57 | fdecl::ConfigSingleValue::Uint8(_)
58 | fdecl::ConfigSingleValue::Uint16(_)
59 | fdecl::ConfigSingleValue::Uint32(_)
60 | fdecl::ConfigSingleValue::Uint64(_)
61 | fdecl::ConfigSingleValue::Int8(_)
62 | fdecl::ConfigSingleValue::Int16(_)
63 | fdecl::ConfigSingleValue::Int32(_)
64 | fdecl::ConfigSingleValue::Int64(_)
65 | fdecl::ConfigSingleValue::String(_) => {}
66 fdecl::ConfigSingleValueUnknown!() => {
67 errors.push(Error::invalid_field(DeclType::ConfigValueSpec, "value"));
68 }
69 },
70 fdecl::ConfigValue::Vector(l) => match l {
71 fdecl::ConfigVectorValue::BoolVector(_)
72 | fdecl::ConfigVectorValue::Uint8Vector(_)
73 | fdecl::ConfigVectorValue::Uint16Vector(_)
74 | fdecl::ConfigVectorValue::Uint32Vector(_)
75 | fdecl::ConfigVectorValue::Uint64Vector(_)
76 | fdecl::ConfigVectorValue::Int8Vector(_)
77 | fdecl::ConfigVectorValue::Int16Vector(_)
78 | fdecl::ConfigVectorValue::Int32Vector(_)
79 | fdecl::ConfigVectorValue::Int64Vector(_)
80 | fdecl::ConfigVectorValue::StringVector(_) => {}
81 fdecl::ConfigVectorValueUnknown!() => {
82 errors.push(Error::invalid_field(DeclType::ConfigValueSpec, "value"));
83 }
84 },
85 fdecl::ConfigValueUnknown!() => {
86 errors.push(Error::invalid_field(DeclType::ConfigValueSpec, "value"));
87 }
88 }
89 } else {
90 errors.push(Error::missing_field(DeclType::ConfigValueSpec, "value"));
91 }
92
93 if errors.is_empty() {
94 Ok(())
95 } else {
96 Err(ErrorList::new(errors))
97 }
98}
99
100pub fn validate_values_data(data: &fdecl::ConfigValuesData) -> Result<(), ErrorList> {
108 let mut errors = vec![];
109 if let Some(values) = &data.values {
110 for spec in values {
111 if let Err(mut e) = validate_value_spec(spec) {
112 errors.append(&mut e.errs);
113 }
114 }
115 } else {
116 errors.push(Error::missing_field(DeclType::ConfigValuesData, "values"));
117 }
118
119 if let Some(checksum) = &data.checksum {
120 match checksum {
121 fdecl::ConfigChecksum::Sha256(_) => {}
122 fdecl::ConfigChecksumUnknown!() => {
123 errors.push(Error::invalid_field(DeclType::ConfigValuesData, "checksum"));
124 }
125 }
126 } else {
127 errors.push(Error::missing_field(DeclType::ConfigValuesData, "checksum"));
128 }
129
130 if errors.is_empty() {
131 Ok(())
132 } else {
133 Err(ErrorList::new(errors))
134 }
135}
136
137#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
139enum RefKey<'a> {
140 Parent,
141 Self_,
142 Child(&'a str),
143 Collection(&'a str),
144 Framework,
145 Capability,
146 Debug,
147}
148
149pub fn validate(decl: &fdecl::Component) -> Result<(), ErrorList> {
163 let ctx = ValidationContext::default();
164 ctx.validate(decl, None).map_err(|errs| ErrorList::new(errs))
165}
166
167fn validate_capabilities(
169 capabilities: &[fdecl::Capability],
170 as_builtin: bool,
171) -> Result<(), ErrorList> {
172 let mut ctx = ValidationContext::default();
173
174 #[cfg(fuchsia_api_level_at_least = "25")]
175 ctx.load_dictionary_names(capabilities.iter().filter_map(|capability| match capability {
176 fdecl::Capability::Dictionary(dictionary_decl) => Some(dictionary_decl),
177 _ => None,
178 }));
179
180 ctx.validate_capability_decls(capabilities, as_builtin);
181 if ctx.errors.is_empty() {
182 Ok(())
183 } else {
184 Err(ErrorList::new(ctx.errors))
185 }
186}
187
188pub fn validate_builtin_capabilities(
190 capabilities: &Vec<fdecl::Capability>,
191) -> Result<(), ErrorList> {
192 validate_capabilities(capabilities, true)
193}
194
195pub fn validate_namespace_capabilities(
197 capabilities: &Vec<fdecl::Capability>,
198) -> Result<(), ErrorList> {
199 validate_capabilities(capabilities, false)
200}
201
202type CheckChildNameFn = fn(Option<&String>, DeclType, &str, &mut Vec<Error>) -> bool;
205
206pub fn validate_dynamic_child(child: &fdecl::Child) -> Result<(), ErrorList> {
207 let mut errors = vec![];
208
209 if let Err(mut error_list) = validate_child(child, check_dynamic_name) {
210 errors.append(&mut error_list.errs);
211 }
212
213 if child.environment.is_some() {
214 errors.push(Error::DynamicChildWithEnvironment);
215 }
216
217 if errors.is_empty() {
218 Ok(())
219 } else {
220 Err(ErrorList { errs: errors })
221 }
222}
223
224fn validate_child(
227 child: &fdecl::Child,
228 check_child_name: CheckChildNameFn,
229) -> Result<(), ErrorList> {
230 let mut errors = vec![];
231 check_child_name(child.name.as_ref(), DeclType::Child, "name", &mut errors);
232 check_url(child.url.as_ref(), DeclType::Child, "url", &mut errors);
233 if child.startup.is_none() {
234 errors.push(Error::missing_field(DeclType::Child, "startup"));
235 }
236 if child.environment.is_some() {
238 check_name(child.environment.as_ref(), DeclType::Child, "environment", &mut errors);
239 }
240 if errors.is_empty() {
241 Ok(())
242 } else {
243 Err(ErrorList { errs: errors })
244 }
245}
246
247pub fn validate_dynamic_offers<'a>(
256 dynamic_children: Vec<(&'a str, &'a str)>,
257 offers: &'a Vec<fdecl::Offer>,
258 decl: &'a fdecl::Component,
259) -> Result<(), ErrorList> {
260 let mut ctx = ValidationContext::default();
261 ctx.dynamic_children = dynamic_children;
262 ctx.validate(decl, Some(offers)).map_err(|errs| ErrorList::new(errs))
263}
264
265fn check_offer_name(
266 prop: Option<&String>,
267 decl: DeclType,
268 keyword: &str,
269 offer_type: OfferType,
270 errors: &mut Vec<Error>,
271) -> bool {
272 if offer_type == OfferType::Dynamic {
273 check_dynamic_name(prop, decl, keyword, errors)
274 } else {
275 check_name(prop, decl, keyword, errors)
276 }
277}
278
279#[derive(Default)]
280struct ValidationContext<'a> {
281 all_children: HashMap<&'a str, &'a fdecl::Child>,
282 all_collections: HashSet<&'a str>,
283 all_capability_ids: HashSet<&'a str>,
284 all_storages: HashMap<&'a str, Option<&'a fdecl::Ref>>,
285 all_services: HashSet<&'a str>,
286 all_protocols: HashSet<&'a str>,
287 all_directories: HashSet<&'a str>,
288 all_runners: HashSet<&'a str>,
289 all_resolvers: HashSet<&'a str>,
290 #[cfg(fuchsia_api_level_at_least = "25")]
291 all_dictionaries: HashMap<&'a str, &'a fdecl::Dictionary>,
292
293 #[cfg(fuchsia_api_level_at_least = "HEAD")]
294 all_configs: HashSet<&'a str>,
295
296 all_environment_names: HashSet<&'a str>,
297 dynamic_children: Vec<(&'a str, &'a str)>,
298 strong_dependencies: DirectedGraph<cm_graph::DependencyNode<'a>>,
299 target_ids: IdMap<'a>,
300 errors: Vec<Error>,
301}
302
303trait Container {
307 fn contains(&self, key: &str) -> bool;
308}
309
310impl<'a> Container for HashSet<&'a str> {
311 fn contains(&self, key: &str) -> bool {
312 self.contains(key)
313 }
314}
315
316impl<'a, T> Container for HashMap<&'a str, T> {
317 fn contains(&self, key: &str) -> bool {
318 self.contains_key(key)
319 }
320}
321
322impl<'a> ValidationContext<'a> {
323 fn validate(
324 mut self,
325 decl: &'a fdecl::Component,
326 dynamic_offers: Option<&'a Vec<fdecl::Offer>>,
327 ) -> Result<(), Vec<Error>> {
328 if let Some(envs) = &decl.environments {
330 self.collect_environment_names(&envs);
331 }
332
333 if let Some(children) = decl.children.as_ref() {
335 for child in children {
336 self.validate_child_decl(&child);
337 }
338 }
339
340 if let Some(collections) = decl.collections.as_ref() {
342 for collection in collections {
343 self.validate_collection_decl(&collection);
344 }
345 }
346
347 if let Some(capabilities) = decl.capabilities.as_ref() {
349 #[cfg(fuchsia_api_level_at_least = "25")]
350 self.load_dictionary_names(capabilities.iter().filter_map(
351 |capability| match capability {
352 fdecl::Capability::Dictionary(dictionary_decl) => Some(dictionary_decl),
353 _ => None,
354 },
355 ));
356 self.validate_capability_decls(capabilities, false);
357 }
358
359 let mut use_runner_name = None;
361 let mut use_runner_source = None;
362 if let Some(uses) = decl.uses.as_ref() {
363 (use_runner_name, use_runner_source) = self.validate_use_decls(uses);
364 }
365
366 if let Some(program) = decl.program.as_ref() {
368 self.validate_program(program, use_runner_name, use_runner_source);
369 }
370
371 if let Some(exposes) = decl.exposes.as_ref() {
373 let mut expose_to_parent_ids = HashMap::new();
374 let mut expose_to_framework_ids = HashMap::new();
375 for expose in exposes.iter() {
376 self.validate_expose_decl(
377 &expose,
378 &mut expose_to_parent_ids,
379 &mut expose_to_framework_ids,
380 );
381 }
382 self.validate_expose_group(&exposes);
383 }
384
385 if let Some(offers) = decl.offers.as_ref() {
387 for offer in offers.iter() {
388 self.validate_offer_decl(&offer, OfferType::Static);
389 }
390 self.validate_offer_group(&offers, OfferType::Static);
391 }
392
393 if let Some(dynamic_offers) = dynamic_offers.as_ref() {
394 for dynamic_offer in dynamic_offers.iter() {
395 self.validate_offer_decl(&dynamic_offer, OfferType::Dynamic);
396 }
397 self.validate_offer_group(&dynamic_offers, OfferType::Dynamic);
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 #[cfg(fuchsia_api_level_at_least = "20")]
409 self.validate_config(decl.config.as_ref(), decl.uses.as_ref());
410
411 cm_graph::generate_dependency_graph(
413 &mut self.strong_dependencies,
414 &decl,
415 &self.dynamic_children,
416 dynamic_offers,
417 );
418 if let Err(e) = self.strong_dependencies.topological_sort() {
419 self.errors.push(Error::dependency_cycle(e.format_cycle()));
420 }
421
422 if self.errors.is_empty() {
423 Ok(())
424 } else {
425 Err(self.errors)
426 }
427 }
428
429 fn collect_environment_names(&mut self, envs: &'a [fdecl::Environment]) {
431 for env in envs {
432 if let Some(name) = env.name.as_ref() {
433 if !self.all_environment_names.insert(name) {
434 self.errors.push(Error::duplicate_field(DeclType::Environment, "name", name));
435 }
436 }
437 }
438 }
439
440 #[cfg(fuchsia_api_level_at_least = "20")]
443 fn validate_config(
444 &mut self,
445 config: Option<&fdecl::ConfigSchema>,
446 uses: Option<&Vec<fdecl::Use>>,
447 ) {
448 use std::collections::BTreeMap;
449
450 let optional_use_keys: BTreeMap<String, fdecl::ConfigType> =
452 uses.map_or(BTreeMap::new(), |u| {
453 u.iter()
454 .map(|u| {
455 let fdecl::Use::Config(config) = u else {
456 return None;
457 };
458 if config.availability == Some(fdecl::Availability::Required)
459 || config.availability == None
460 {
461 return None;
462 }
463 if let Some(_) = config.default.as_ref() {
464 return None;
465 }
466 let Some(key) = config.target_name.clone() else {
467 return None;
468 };
469 let Some(value) = config.type_.clone() else {
470 return None;
471 };
472 Some((key, value))
473 })
474 .flatten()
475 .collect()
476 });
477
478 for u in uses.iter().flat_map(|x| x.iter()) {
480 let fdecl::Use::Config(config) = u else { continue };
481 let Some(default) = config.default.as_ref() else { continue };
482 validate_value_spec(&fdecl::ConfigValueSpec {
483 value: Some(default.clone()),
484 ..Default::default()
485 })
486 .map_err(|mut e| self.errors.append(&mut e.errs))
487 .ok();
488 }
489
490 let Some(config) = config else {
491 if !optional_use_keys.is_empty() {
492 self.errors.push(Error::missing_field(DeclType::ConfigField, "config"))
493 }
494 return;
495 };
496
497 if let Some(fields) = &config.fields {
498 for field in fields {
499 if field.key.is_none() {
500 self.errors.push(Error::missing_field(DeclType::ConfigField, "key"));
501 }
502 if let Some(type_) = &field.type_ {
503 self.validate_config_type(type_, true);
504 } else {
505 self.errors.push(Error::missing_field(DeclType::ConfigField, "value_type"));
506 }
507 }
508 } else {
509 self.errors.push(Error::missing_field(DeclType::ConfigSchema, "fields"));
510 }
511
512 if let Some(checksum) = &config.checksum {
513 match checksum {
514 fdecl::ConfigChecksum::Sha256(_) => {}
515 fdecl::ConfigChecksumUnknown!() => {
516 self.errors.push(Error::invalid_field(DeclType::ConfigSchema, "checksum"));
517 }
518 }
519 } else {
520 self.errors.push(Error::missing_field(DeclType::ConfigSchema, "checksum"));
521 }
522
523 'outer: for (key, value) in optional_use_keys.iter() {
524 for field in config.fields.iter().flatten() {
525 if field.key.as_ref() == Some(key) {
526 if field.type_.as_ref() != Some(value) {
527 self.errors.push(Error::invalid_field(DeclType::ConfigField, key.clone()));
528 }
529 continue 'outer;
530 }
531 }
532 self.errors.push(Error::missing_field(DeclType::ConfigField, key.clone()));
533 }
534
535 match config.value_source {
536 None => self.errors.push(Error::missing_field(DeclType::ConfigSchema, "value_source")),
537 #[cfg(fuchsia_api_level_at_least = "HEAD")]
538 Some(fdecl::ConfigValueSource::Capabilities(_)) => {
539 if !optional_use_keys.is_empty() {
540 self.errors
541 .push(Error::invalid_field(DeclType::ConfigValueSource, "ValueSource"))
542 }
543 }
544 Some(fdecl::ConfigValueSource::__SourceBreaking { .. }) => {
545 self.errors.push(Error::invalid_field(DeclType::ConfigValueSource, "ValueSource"))
546 }
547 _ => (),
548 };
549 }
550
551 #[cfg(fuchsia_api_level_at_least = "20")]
552 fn validate_config_type(&mut self, type_: &fdecl::ConfigType, accept_vectors: bool) {
553 match &type_.layout {
554 fdecl::ConfigTypeLayout::Bool
555 | fdecl::ConfigTypeLayout::Uint8
556 | fdecl::ConfigTypeLayout::Uint16
557 | fdecl::ConfigTypeLayout::Uint32
558 | fdecl::ConfigTypeLayout::Uint64
559 | fdecl::ConfigTypeLayout::Int8
560 | fdecl::ConfigTypeLayout::Int16
561 | fdecl::ConfigTypeLayout::Int32
562 | fdecl::ConfigTypeLayout::Int64 => {
563 if let Some(parameters) = &type_.parameters {
565 if !parameters.is_empty() {
566 self.errors
567 .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
568 }
569 } else {
570 self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"));
571 }
572
573 if !type_.constraints.is_empty() {
574 self.errors.push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
575 }
576 }
577 fdecl::ConfigTypeLayout::String => {
578 if let Some(parameters) = &type_.parameters {
580 if !parameters.is_empty() {
581 self.errors
582 .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
583 }
584 } else {
585 self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"));
586 }
587
588 if type_.constraints.is_empty() {
589 self.errors.push(Error::missing_field(DeclType::ConfigType, "constraints"));
590 } else if type_.constraints.len() > 1 {
591 self.errors.push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
592 } else if let fdecl::LayoutConstraint::MaxSize(_) = &type_.constraints[0] {
593 } else {
594 self.errors.push(Error::invalid_field(DeclType::ConfigType, "constraints"));
595 }
596 }
597 fdecl::ConfigTypeLayout::Vector => {
598 if accept_vectors {
599 if let Some(parameters) = &type_.parameters {
601 if parameters.is_empty() {
602 self.errors
603 .push(Error::missing_field(DeclType::ConfigType, "parameters"));
604 } else if parameters.len() > 1 {
605 self.errors
606 .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
607 } else if let fdecl::LayoutParameter::NestedType(nested_type) =
608 ¶meters[0]
609 {
610 self.validate_config_type(nested_type, false);
611 } else {
612 self.errors
613 .push(Error::invalid_field(DeclType::ConfigType, "parameters"));
614 }
615 } else {
616 self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"))
617 }
618
619 if type_.constraints.is_empty() {
620 self.errors.push(Error::missing_field(DeclType::ConfigType, "constraints"));
621 } else if type_.constraints.len() > 1 {
622 self.errors
623 .push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
624 } else if let fdecl::LayoutConstraint::MaxSize(_) = &type_.constraints[0] {
625 } else {
626 self.errors.push(Error::invalid_field(DeclType::ConfigType, "constraints"));
627 }
628 } else {
629 self.errors.push(Error::nested_vector());
630 }
631 }
632 _ => self.errors.push(Error::invalid_field(DeclType::ConfigType, "layout")),
633 }
634 }
635
636 fn validate_capability_decls(
637 &mut self,
638 capabilities: &'a [fdecl::Capability],
639 as_builtin: bool,
640 ) {
641 for capability in capabilities {
642 self.validate_capability_decl(capability, as_builtin);
643 }
644 }
645
646 fn validate_capability_decl(&mut self, capability: &'a fdecl::Capability, as_builtin: bool) {
651 match capability {
652 fdecl::Capability::Service(service) => self.validate_service_decl(&service, as_builtin),
653 fdecl::Capability::Protocol(protocol) => {
654 self.validate_protocol_decl(&protocol, as_builtin)
655 }
656 fdecl::Capability::Directory(directory) => {
657 self.validate_directory_decl(&directory, as_builtin)
658 }
659 fdecl::Capability::Storage(storage) => {
660 if as_builtin {
661 self.errors.push(Error::CapabilityCannotBeBuiltin(DeclType::Storage))
662 } else {
663 self.validate_storage_decl(&storage)
664 }
665 }
666 fdecl::Capability::Runner(runner) => self.validate_runner_decl(&runner, as_builtin),
667 fdecl::Capability::Resolver(resolver) => {
668 self.validate_resolver_decl(&resolver, as_builtin)
669 }
670 fdecl::Capability::EventStream(event) => {
671 if as_builtin {
672 self.validate_event_stream_decl(&event)
673 } else {
674 self.errors.push(Error::CapabilityMustBeBuiltin(DeclType::EventStream))
675 }
676 }
677 #[cfg(fuchsia_api_level_at_least = "25")]
678 fdecl::Capability::Dictionary(dictionary) => {
679 self.validate_dictionary_decl(&dictionary);
680 }
681 #[cfg(fuchsia_api_level_at_least = "HEAD")]
682 fdecl::Capability::Config(config) => {
683 self.validate_configuration_decl(&config);
684 }
685 fdecl::CapabilityUnknown!() => self.errors.push(Error::UnknownCapability),
686 }
687 }
688
689 fn validate_use_decls(
691 &mut self,
692 uses: &'a [fdecl::Use],
693 ) -> (Option<&'a String>, Option<&'a fdecl::Ref>) {
694 for use_ in uses.iter() {
696 self.validate_use_decl(&use_);
697 }
698 self.validate_use_paths(&uses);
699
700 #[cfg(fuchsia_api_level_at_least = "HEAD")]
701 {
702 let mut use_runner_name = None;
703 let mut use_runner_source = None;
704 for use_ in uses.iter() {
705 if let fdecl::Use::Runner(use_runner) = use_ {
706 if use_runner_name.is_some() {
707 self.errors.push(Error::MultipleRunnersUsed);
708 }
709
710 use_runner_name = use_runner.source_name.as_ref();
711 use_runner_source = use_runner.source.as_ref();
712 }
713 }
714 return (use_runner_name, use_runner_source);
715 }
716 #[cfg(fuchsia_api_level_less_than = "HEAD")]
717 return (None, None);
718 }
719
720 fn validate_use_decl(&mut self, use_: &'a fdecl::Use) {
721 match use_ {
722 fdecl::Use::Service(u) => {
723 let decl = DeclType::UseService;
724 self.validate_use_fields(
725 decl,
726 Self::service_checker,
727 u.source.as_ref(),
728 u.source_name.as_ref(),
729 get_source_dictionary!(u),
730 u.target_path.as_ref(),
731 u.dependency_type.as_ref(),
732 u.availability.as_ref(),
733 );
734 if u.dependency_type.is_none() {
735 self.errors.push(Error::missing_field(decl, "dependency_type"));
736 }
737 }
738 fdecl::Use::Protocol(u) => {
739 let decl = DeclType::UseProtocol;
740 self.validate_use_fields(
741 decl,
742 Self::protocol_checker,
743 u.source.as_ref(),
744 u.source_name.as_ref(),
745 get_source_dictionary!(u),
746 u.target_path.as_ref(),
747 u.dependency_type.as_ref(),
748 u.availability.as_ref(),
749 );
750 if u.dependency_type.is_none() {
751 self.errors.push(Error::missing_field(decl, "dependency_type"));
752 }
753 }
754 fdecl::Use::Directory(u) => {
755 let decl = DeclType::UseDirectory;
756 self.validate_use_fields(
757 decl,
758 Self::directory_checker,
759 u.source.as_ref(),
760 u.source_name.as_ref(),
761 get_source_dictionary!(u),
762 u.target_path.as_ref(),
763 u.dependency_type.as_ref(),
764 u.availability.as_ref(),
765 );
766 if u.dependency_type.is_none() {
767 self.errors.push(Error::missing_field(decl, "dependency_type"));
768 }
769 if u.rights.is_none() {
770 self.errors.push(Error::missing_field(DeclType::UseDirectory, "rights"));
771 }
772 if let Some(subdir) = u.subdir.as_ref() {
773 check_relative_path(
774 Some(subdir),
775 DeclType::UseDirectory,
776 "subdir",
777 &mut self.errors,
778 );
779 }
780 }
781 fdecl::Use::Storage(u) => {
782 const SOURCE: Option<fdecl::Ref> = Some(fdecl::Ref::Parent(fdecl::ParentRef {}));
783 const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
784 Some(fdecl::DependencyType::Strong);
785 self.validate_use_fields(
786 DeclType::UseStorage,
787 Self::storage_checker,
788 SOURCE.as_ref(),
789 u.source_name.as_ref(),
790 None,
791 u.target_path.as_ref(),
792 DEPENDENCY_TYPE.as_ref(),
793 u.availability.as_ref(),
794 );
795 }
796 fdecl::Use::EventStream(u) => {
797 const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
798 Some(fdecl::DependencyType::Strong);
799 let decl = DeclType::UseEventStream;
800 self.validate_use_fields(
801 decl,
802 Self::event_stream_checker,
803 u.source.as_ref(),
804 u.source_name.as_ref(),
805 None,
806 u.target_path.as_ref(),
807 DEPENDENCY_TYPE.as_ref(),
808 u.availability.as_ref(),
809 );
810 match u.source {
812 Some(fdecl::Ref::Child(_)) | Some(fdecl::Ref::Parent(_)) => {
813 }
815 Some(fdecl::Ref::Framework(_))
816 | Some(fdecl::Ref::Self_(_))
817 | Some(fdecl::Ref::Debug(_)) => {
818 self.errors.push(Error::invalid_field(decl, "source"));
820 }
821 Some(fdecl::Ref::Collection(_)) | Some(fdecl::RefUnknown!()) | None => {
822 }
824 }
825 if let Some(scope) = &u.scope {
826 for reference in scope {
827 if !matches!(reference, fdecl::Ref::Child(_) | fdecl::Ref::Collection(_)) {
828 self.errors.push(Error::invalid_field(decl, "scope"));
829 }
830 }
831 }
832 }
833 #[cfg(fuchsia_api_level_at_least = "HEAD")]
834 fdecl::Use::Runner(u) => {
835 const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
836 Some(fdecl::DependencyType::Strong);
837 const AVAILABILITY: Option<fdecl::Availability> =
838 Some(fdecl::Availability::Required);
839 let decl = DeclType::UseRunner;
840 self.validate_use_fields(
841 decl,
842 Self::runner_checker,
843 u.source.as_ref(),
844 u.source_name.as_ref(),
845 get_source_dictionary!(u),
846 None,
847 DEPENDENCY_TYPE.as_ref(),
848 AVAILABILITY.as_ref(),
849 );
850 }
851 #[cfg(fuchsia_api_level_at_least = "HEAD")]
852 fdecl::Use::Config(u) => {
853 const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
854 Some(fdecl::DependencyType::Strong);
855 let decl = DeclType::UseConfiguration;
856 self.validate_use_fields(
857 decl,
858 Self::config_checker,
859 u.source.as_ref(),
860 u.source_name.as_ref(),
861 None,
862 None,
863 DEPENDENCY_TYPE.as_ref(),
864 u.availability.as_ref(),
865 );
866 }
867 fdecl::UseUnknown!() => {
868 self.errors.push(Error::invalid_field(DeclType::Component, "use"));
869 }
870 }
871 }
872
873 fn validate_program(
876 &mut self,
877 program: &fdecl::Program,
878 use_runner_name: Option<&String>,
879 _use_runner_source: Option<&fdecl::Ref>,
880 ) {
881 match &program.runner {
882 Some(_) =>
883 {
884 #[cfg(fuchsia_api_level_at_least = "HEAD")]
885 if use_runner_name.is_some() {
886 if use_runner_name != program.runner.as_ref()
887 || _use_runner_source
888 != Some(&fdecl::Ref::Environment(fdecl::EnvironmentRef))
889 {
890 self.errors.push(Error::ConflictingRunners);
891 }
892 }
893 }
894 None => {
895 if use_runner_name.is_none() {
896 self.errors.push(Error::MissingRunner);
897 }
898 }
899 }
900
901 if program.info.is_none() {
902 self.errors.push(Error::missing_field(DeclType::Program, "info"));
903 }
904 }
905
906 fn validate_use_paths(&mut self, uses: &[fdecl::Use]) {
909 #[derive(Debug, PartialEq, Clone, Copy)]
910 struct PathCapability<'a> {
911 decl: DeclType,
912 dir: &'a Path,
913 use_: &'a fdecl::Use,
914 }
915 let mut used_paths = HashMap::new();
916 for use_ in uses.iter() {
917 match use_ {
918 fdecl::Use::Service(fdecl::UseService { target_path: Some(path), .. })
919 | fdecl::Use::Protocol(fdecl::UseProtocol { target_path: Some(path), .. })
920 | fdecl::Use::Directory(fdecl::UseDirectory { target_path: Some(path), .. })
921 | fdecl::Use::Storage(fdecl::UseStorage { target_path: Some(path), .. }) => {
922 let capability = match use_ {
923 fdecl::Use::Service(_) => {
924 let dir = match Path::new(path).parent() {
925 Some(p) => p,
926 None => continue, };
928 PathCapability { decl: DeclType::UseService, dir, use_ }
929 }
930 fdecl::Use::Protocol(_) => {
931 let dir = match Path::new(path).parent() {
932 Some(p) => p,
933 None => continue, };
935 PathCapability { decl: DeclType::UseProtocol, dir, use_ }
936 }
937 fdecl::Use::Directory(_) => PathCapability {
938 decl: DeclType::UseDirectory,
939 dir: Path::new(path),
940 use_,
941 },
942 fdecl::Use::Storage(_) => PathCapability {
943 decl: DeclType::UseStorage,
944 dir: Path::new(path),
945 use_,
946 },
947 _ => unreachable!(),
948 };
949 if used_paths.insert(path, capability).is_some() {
950 self.errors.push(Error::duplicate_field(
952 capability.decl,
953 "target_path",
954 path,
955 ));
956 }
957 }
958 _ => {}
959 }
960 }
961 for ((&path_a, capability_a), (&path_b, capability_b)) in
962 used_paths.iter().tuple_combinations()
963 {
964 if match (capability_a.use_, capability_b.use_) {
965 (fdecl::Use::Directory(_), fdecl::Use::Directory(_))
967 | (fdecl::Use::Storage(_), fdecl::Use::Directory(_))
968 | (fdecl::Use::Directory(_), fdecl::Use::Storage(_))
969 | (fdecl::Use::Storage(_), fdecl::Use::Storage(_)) => {
970 capability_b.dir == capability_a.dir
971 || capability_b.dir.starts_with(capability_a.dir)
972 || capability_a.dir.starts_with(capability_b.dir)
973 }
974
975 (_, fdecl::Use::Directory(_)) | (fdecl::Use::Directory(_), _) => {
977 capability_b.dir == capability_a.dir
978 || capability_b.dir.starts_with(capability_a.dir)
979 || capability_a.dir.starts_with(capability_b.dir)
980 }
981
982 (_, _) => {
985 capability_b.dir != capability_a.dir
986 && (capability_b.dir.starts_with(capability_a.dir)
987 || capability_a.dir.starts_with(capability_b.dir))
988 }
989 } {
990 self.errors.push(Error::invalid_path_overlap(
991 capability_a.decl,
992 path_a,
993 capability_b.decl,
994 path_b,
995 ));
996 }
997 }
998 for (used_path, capability) in used_paths.iter() {
999 if used_path.as_str() == "/pkg" || used_path.starts_with("/pkg/") {
1000 self.errors.push(Error::pkg_path_overlap(capability.decl, *used_path));
1001 }
1002 }
1003 }
1004
1005 fn validate_use_fields(
1006 &mut self,
1007 decl: DeclType,
1008 capability_checker: impl Fn(&Self) -> &dyn Container,
1012 source: Option<&'a fdecl::Ref>,
1013 source_name: Option<&'a String>,
1014 source_dictionary: Option<&'a String>,
1015 target_path: Option<&'a String>,
1016 dependency_type: Option<&fdecl::DependencyType>,
1017 availability: Option<&'a fdecl::Availability>,
1018 ) {
1019 self.validate_use_source(decl, source, source_dictionary);
1020
1021 check_name(source_name, decl, "source_name", &mut self.errors);
1022 if source_dictionary.is_some() {
1023 check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
1024 }
1025 if decl != DeclType::UseRunner && decl != DeclType::UseConfiguration {
1026 check_path(target_path, decl, "target_path", &mut self.errors);
1027 }
1028 check_use_availability(decl, availability, &mut self.errors);
1029
1030 let is_use_from_child = match source {
1032 Some(fdecl::Ref::Child(_)) => true,
1033 _ => false,
1034 };
1035 match (is_use_from_child, dependency_type) {
1036 (false, Some(fdecl::DependencyType::Weak)) => {
1037 self.errors.push(Error::invalid_field(decl, "dependency_type"));
1038 }
1039 _ => {}
1040 }
1041
1042 self.validate_route_from_self(
1043 decl,
1044 source,
1045 source_name,
1046 source_dictionary,
1047 capability_checker,
1048 );
1049 }
1050
1051 fn validate_use_source(
1052 &mut self,
1053 decl: DeclType,
1054 source: Option<&'a fdecl::Ref>,
1055 source_dictionary: Option<&'a String>,
1056 ) {
1057 match (source, source_dictionary) {
1058 (Some(fdecl::Ref::Parent(_)), _) => {}
1060 (Some(fdecl::Ref::Self_(_)), _) => {}
1061 (Some(fdecl::Ref::Child(child)), _) => {
1062 self.validate_child_ref(decl, "source", &child, OfferType::Static);
1063 return;
1064 }
1065 (Some(fdecl::Ref::Framework(_)), None) => {}
1067 (Some(fdecl::Ref::Debug(_)), None) => {}
1068 (Some(fdecl::Ref::Capability(c)), None) => {
1069 self.validate_source_capability(&c, decl, "source");
1070 return;
1071 }
1072 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1073 (Some(fdecl::Ref::Environment(_)), None) => {}
1074 (Some(fdecl::Ref::Collection(collection)), None) if decl == DeclType::UseService => {
1075 self.validate_collection_ref(decl, "source", &collection);
1076 return;
1077 }
1078 (None, _) => self.errors.push(Error::missing_field(decl, "source")),
1080 (_, _) => self.errors.push(Error::invalid_field(decl, "source")),
1082 }
1083 }
1084
1085 fn validate_child_decl(&mut self, child: &'a fdecl::Child) {
1086 if let Err(mut e) = validate_child(child, check_name) {
1087 self.errors.append(&mut e.errs);
1088 }
1089 if let Some(name) = child.name.as_ref() {
1090 let name: &str = name;
1091 if self.all_children.insert(name, child).is_some() {
1092 self.errors.push(Error::duplicate_field(DeclType::Child, "name", name));
1093 }
1094 }
1095 if let Some(environment) = child.environment.as_ref() {
1096 if !self.all_environment_names.contains(environment.as_str()) {
1097 self.errors.push(Error::invalid_environment(
1098 DeclType::Child,
1099 "environment",
1100 environment,
1101 ));
1102 }
1103 }
1104 }
1105
1106 fn validate_collection_decl(&mut self, collection: &'a fdecl::Collection) {
1107 let name = collection.name.as_ref();
1108 if check_name(name, DeclType::Collection, "name", &mut self.errors) {
1109 let name: &str = name.unwrap();
1110 if !self.all_collections.insert(name) {
1111 self.errors.push(Error::duplicate_field(DeclType::Collection, "name", name));
1112 }
1113 }
1114 if collection.durability.is_none() {
1115 self.errors.push(Error::missing_field(DeclType::Collection, "durability"));
1116 }
1117 if let Some(environment) = collection.environment.as_ref() {
1118 if !self.all_environment_names.contains(environment.as_str()) {
1119 self.errors.push(Error::invalid_environment(
1120 DeclType::Collection,
1121 "environment",
1122 environment,
1123 ));
1124 }
1125 }
1126 }
1128
1129 fn validate_environment_decl(&mut self, environment: &'a fdecl::Environment) {
1130 let name = environment.name.as_ref();
1131 check_name(name, DeclType::Environment, "name", &mut self.errors);
1132 if environment.extends.is_none() {
1133 self.errors.push(Error::missing_field(DeclType::Environment, "extends"));
1134 }
1135 if let Some(runners) = environment.runners.as_ref() {
1136 let mut registered_runners = HashSet::new();
1137 for runner in runners {
1138 self.validate_runner_registration(runner, &mut registered_runners);
1139 }
1140 }
1141 if let Some(resolvers) = environment.resolvers.as_ref() {
1142 let mut registered_schemes = HashSet::new();
1143 for resolver in resolvers {
1144 self.validate_resolver_registration(resolver, &mut registered_schemes);
1145 }
1146 }
1147
1148 match environment.extends.as_ref() {
1149 Some(fdecl::EnvironmentExtends::None) => {
1150 if environment.stop_timeout_ms.is_none() {
1151 self.errors
1152 .push(Error::missing_field(DeclType::Environment, "stop_timeout_ms"));
1153 }
1154 }
1155 None | Some(fdecl::EnvironmentExtends::Realm) => {}
1156 }
1157
1158 if let Some(debugs) = environment.debug_capabilities.as_ref() {
1159 for debug in debugs {
1160 self.validate_environment_debug_registration(debug);
1161 }
1162 }
1163 }
1164
1165 fn validate_runner_registration(
1166 &mut self,
1167 runner_registration: &'a fdecl::RunnerRegistration,
1168 runner_names: &mut HashSet<&'a str>,
1169 ) {
1170 check_name(
1171 runner_registration.source_name.as_ref(),
1172 DeclType::RunnerRegistration,
1173 "source_name",
1174 &mut self.errors,
1175 );
1176 self.validate_registration_source(
1177 runner_registration.source.as_ref(),
1178 DeclType::RunnerRegistration,
1179 );
1180 if let (Some(fdecl::Ref::Self_(_)), Some(ref name)) =
1182 (&runner_registration.source, &runner_registration.source_name)
1183 {
1184 if !self.all_runners.contains(name as &str) {
1185 self.errors.push(Error::invalid_runner(
1186 DeclType::RunnerRegistration,
1187 "source_name",
1188 name,
1189 ));
1190 }
1191 }
1192
1193 check_name(
1194 runner_registration.target_name.as_ref(),
1195 DeclType::RunnerRegistration,
1196 "target_name",
1197 &mut self.errors,
1198 );
1199 if let Some(name) = runner_registration.target_name.as_ref() {
1200 if !runner_names.insert(name.as_str()) {
1201 self.errors.push(Error::duplicate_field(
1202 DeclType::RunnerRegistration,
1203 "target_name",
1204 name,
1205 ));
1206 }
1207 }
1208 }
1209
1210 fn validate_resolver_registration(
1211 &mut self,
1212 resolver_registration: &'a fdecl::ResolverRegistration,
1213 schemes: &mut HashSet<&'a str>,
1214 ) {
1215 check_name(
1216 resolver_registration.resolver.as_ref(),
1217 DeclType::ResolverRegistration,
1218 "resolver",
1219 &mut self.errors,
1220 );
1221 self.validate_registration_source(
1222 resolver_registration.source.as_ref(),
1223 DeclType::ResolverRegistration,
1224 );
1225 check_url_scheme(
1226 resolver_registration.scheme.as_ref(),
1227 DeclType::ResolverRegistration,
1228 "scheme",
1229 &mut self.errors,
1230 );
1231 if let Some(scheme) = resolver_registration.scheme.as_ref() {
1232 if !schemes.insert(scheme.as_str()) {
1233 self.errors.push(Error::duplicate_field(
1234 DeclType::ResolverRegistration,
1235 "scheme",
1236 scheme,
1237 ));
1238 }
1239 }
1240 }
1241
1242 fn validate_registration_source(&mut self, source: Option<&'a fdecl::Ref>, ty: DeclType) {
1243 match source {
1244 Some(fdecl::Ref::Parent(_)) => {}
1245 Some(fdecl::Ref::Self_(_)) => {}
1246 Some(fdecl::Ref::Child(child_ref)) => {
1247 self.validate_child_ref(ty, "source", &child_ref, OfferType::Static);
1249 }
1250 Some(_) => {
1251 self.errors.push(Error::invalid_field(ty, "source"));
1252 }
1253 None => {
1254 self.errors.push(Error::missing_field(ty, "source"));
1255 }
1256 }
1257 }
1258
1259 fn validate_service_decl(&mut self, service: &'a fdecl::Service, as_builtin: bool) {
1260 if check_name(service.name.as_ref(), DeclType::Service, "name", &mut self.errors) {
1261 let name = service.name.as_ref().unwrap();
1262 if !self.all_capability_ids.insert(name) {
1263 self.errors.push(Error::duplicate_field(DeclType::Service, "name", name.as_str()));
1264 }
1265 self.all_services.insert(name);
1266 }
1267 match as_builtin {
1268 true => {
1269 if let Some(path) = service.source_path.as_ref() {
1270 self.errors.push(Error::extraneous_source_path(DeclType::Service, path))
1271 }
1272 }
1273 false => {
1274 check_path(
1275 service.source_path.as_ref(),
1276 DeclType::Service,
1277 "source_path",
1278 &mut self.errors,
1279 );
1280 }
1281 }
1282 }
1283
1284 fn validate_protocol_decl(&mut self, protocol: &'a fdecl::Protocol, as_builtin: bool) {
1285 if check_name(protocol.name.as_ref(), DeclType::Protocol, "name", &mut self.errors) {
1286 let name = protocol.name.as_ref().unwrap();
1287 if !self.all_capability_ids.insert(name) {
1288 self.errors.push(Error::duplicate_field(DeclType::Protocol, "name", name.as_str()));
1289 }
1290 self.all_protocols.insert(name);
1291 }
1292 match as_builtin {
1293 true => {
1294 if let Some(path) = protocol.source_path.as_ref() {
1295 self.errors.push(Error::extraneous_source_path(DeclType::Protocol, path))
1296 }
1297 }
1298 false => {
1299 check_path(
1300 protocol.source_path.as_ref(),
1301 DeclType::Protocol,
1302 "source_path",
1303 &mut self.errors,
1304 );
1305 }
1306 }
1307
1308 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1309 match protocol.delivery {
1310 Some(delivery) => match cm_types::DeliveryType::try_from(delivery) {
1311 Ok(_) => {}
1312 Err(_) => self.errors.push(Error::invalid_field(DeclType::Protocol, "delivery")),
1313 },
1314 None => {}
1315 }
1316 }
1317
1318 fn validate_directory_decl(&mut self, directory: &'a fdecl::Directory, as_builtin: bool) {
1319 if check_name(directory.name.as_ref(), DeclType::Directory, "name", &mut self.errors) {
1320 let name = directory.name.as_ref().unwrap();
1321 if !self.all_capability_ids.insert(name) {
1322 self.errors.push(Error::duplicate_field(
1323 DeclType::Directory,
1324 "name",
1325 name.as_str(),
1326 ));
1327 }
1328 self.all_directories.insert(name);
1329 }
1330 match as_builtin {
1331 true => {
1332 if let Some(path) = directory.source_path.as_ref() {
1333 self.errors.push(Error::extraneous_source_path(DeclType::Directory, path))
1334 }
1335 }
1336 false => {
1337 check_path(
1338 directory.source_path.as_ref(),
1339 DeclType::Directory,
1340 "source_path",
1341 &mut self.errors,
1342 );
1343 }
1344 }
1345 if directory.rights.is_none() {
1346 self.errors.push(Error::missing_field(DeclType::Directory, "rights"));
1347 }
1348 }
1349
1350 fn validate_storage_decl(&mut self, storage: &'a fdecl::Storage) {
1351 match storage.source.as_ref() {
1352 Some(fdecl::Ref::Parent(_)) => {}
1353 Some(fdecl::Ref::Self_(_)) => {}
1354 Some(fdecl::Ref::Child(child)) => {
1355 let _ =
1356 self.validate_child_ref(DeclType::Storage, "source", &child, OfferType::Static);
1357 }
1358 Some(_) => {
1359 self.errors.push(Error::invalid_field(DeclType::Storage, "source"));
1360 }
1361 None => {
1362 self.errors.push(Error::missing_field(DeclType::Storage, "source"));
1363 }
1364 };
1365 if check_name(storage.name.as_ref(), DeclType::Storage, "name", &mut self.errors) {
1366 let name = storage.name.as_ref().unwrap();
1367 if !self.all_capability_ids.insert(name) {
1368 self.errors.push(Error::duplicate_field(DeclType::Storage, "name", name.as_str()));
1369 }
1370 self.all_storages.insert(name, storage.source.as_ref());
1371 }
1372 if storage.storage_id.is_none() {
1373 self.errors.push(Error::missing_field(DeclType::Storage, "storage_id"));
1374 }
1375 check_name(
1376 storage.backing_dir.as_ref(),
1377 DeclType::Storage,
1378 "backing_dir",
1379 &mut self.errors,
1380 );
1381 }
1382
1383 fn validate_runner_decl(&mut self, runner: &'a fdecl::Runner, as_builtin: bool) {
1384 if check_name(runner.name.as_ref(), DeclType::Runner, "name", &mut self.errors) {
1385 let name = runner.name.as_ref().unwrap();
1386 if !self.all_capability_ids.insert(name) {
1387 self.errors.push(Error::duplicate_field(DeclType::Runner, "name", name.as_str()));
1388 }
1389 self.all_runners.insert(name);
1390 }
1391 match as_builtin {
1392 true => {
1393 if let Some(path) = runner.source_path.as_ref() {
1394 self.errors.push(Error::extraneous_source_path(DeclType::Runner, path))
1395 }
1396 }
1397 false => {
1398 check_path(
1399 runner.source_path.as_ref(),
1400 DeclType::Runner,
1401 "source_path",
1402 &mut self.errors,
1403 );
1404 }
1405 }
1406 }
1407
1408 fn validate_resolver_decl(&mut self, resolver: &'a fdecl::Resolver, as_builtin: bool) {
1409 if check_name(resolver.name.as_ref(), DeclType::Resolver, "name", &mut self.errors) {
1410 let name = resolver.name.as_ref().unwrap();
1411 if !self.all_capability_ids.insert(name) {
1412 self.errors.push(Error::duplicate_field(DeclType::Resolver, "name", name.as_str()));
1413 }
1414 self.all_resolvers.insert(name);
1415 }
1416 match as_builtin {
1417 true => {
1418 if let Some(path) = resolver.source_path.as_ref() {
1419 self.errors.push(Error::extraneous_source_path(DeclType::Resolver, path))
1420 }
1421 }
1422 false => {
1423 check_path(
1424 resolver.source_path.as_ref(),
1425 DeclType::Resolver,
1426 "source_path",
1427 &mut self.errors,
1428 );
1429 }
1430 }
1431 }
1432
1433 #[cfg(fuchsia_api_level_at_least = "25")]
1437 fn load_dictionary_names(&mut self, dictionaries: impl Iterator<Item = &'a fdecl::Dictionary>) {
1438 for dictionary in dictionaries {
1439 let decl = DeclType::Dictionary;
1440 if check_name(dictionary.name.as_ref(), decl, "name", &mut self.errors) {
1441 let name = dictionary.name.as_ref().unwrap();
1442 if !self.all_capability_ids.insert(name) {
1443 self.errors.push(Error::duplicate_field(decl, "name", name.as_str()));
1444 }
1445 self.all_dictionaries.insert(name, &dictionary);
1446 }
1447 }
1448 }
1449
1450 #[cfg(fuchsia_api_level_at_least = "25")]
1451 fn validate_dictionary_decl(&mut self, dictionary: &'a fdecl::Dictionary) {
1452 let decl = DeclType::Dictionary;
1453 if let Some(path) = dictionary.source_path.as_ref() {
1454 if dictionary.source.is_some() {
1455 self.errors.push(Error::extraneous_field(decl, "source"));
1456 }
1457 check_path(Some(path), DeclType::Dictionary, "source_path", &mut self.errors);
1458 }
1459 }
1460
1461 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1462 fn validate_configuration_decl(&mut self, config: &'a fdecl::Configuration) {
1463 let decl = DeclType::Configuration;
1464 if check_name(config.name.as_ref(), decl, "name", &mut self.errors) {
1465 let name = config.name.as_ref().unwrap();
1466 if !self.all_capability_ids.insert(name) {
1467 self.errors.push(Error::duplicate_field(decl, "name", name.as_str()));
1468 }
1469 self.all_configs.insert(name);
1470 }
1471 }
1472
1473 fn validate_environment_debug_registration(&mut self, debug: &'a fdecl::DebugRegistration) {
1474 match debug {
1475 fdecl::DebugRegistration::Protocol(o) => {
1476 let decl = DeclType::DebugProtocolRegistration;
1477 self.validate_environment_debug_fields(
1478 decl,
1479 o.source.as_ref(),
1480 o.source_name.as_ref(),
1481 o.target_name.as_ref(),
1482 );
1483
1484 if let (Some(fdecl::Ref::Self_(_)), Some(ref name)) = (&o.source, &o.source_name) {
1485 if !self.all_protocols.contains(&name as &str) {
1486 self.errors.push(Error::invalid_field(decl, "source"));
1487 }
1488 }
1489 }
1490 _ => {
1491 self.errors.push(Error::invalid_field(DeclType::Environment, "debug"));
1492 }
1493 }
1494 }
1495
1496 fn validate_environment_debug_fields(
1497 &mut self,
1498 decl: DeclType,
1499 source: Option<&fdecl::Ref>,
1500 source_name: Option<&String>,
1501 target_name: Option<&'a String>,
1502 ) {
1503 match source {
1505 Some(fdecl::Ref::Parent(_)) => {}
1506 Some(fdecl::Ref::Self_(_)) => {}
1507 Some(fdecl::Ref::Framework(_)) => {}
1508 Some(fdecl::Ref::Child(child)) => {
1509 let _ = self.validate_child_ref(decl, "source", &child, OfferType::Static);
1510 }
1511 Some(_) => self.errors.push(Error::invalid_field(decl, "source")),
1512 None => self.errors.push(Error::missing_field(decl, "source")),
1513 }
1514 check_name(source_name, decl, "source_name", &mut self.errors);
1515 check_name(target_name, decl, "target_name", &mut self.errors);
1516 }
1517
1518 fn validate_event_stream_decl(&mut self, event: &'a fdecl::EventStream) {
1519 if check_name(event.name.as_ref(), DeclType::EventStream, "name", &mut self.errors) {
1520 let name = event.name.as_ref().unwrap();
1521 if !self.all_capability_ids.insert(name) {
1522 self.errors.push(Error::duplicate_field(
1523 DeclType::EventStream,
1524 "name",
1525 name.as_str(),
1526 ));
1527 }
1528 }
1529 }
1530
1531 fn validate_source_collection(
1532 &mut self,
1533 collection: &fdecl::CollectionRef,
1534 decl_type: DeclType,
1535 ) -> bool {
1536 let num_errors = self.errors.len();
1537 if check_name(Some(&collection.name), decl_type, "source.collection.name", &mut self.errors)
1538 && !self.all_collections.contains(&collection.name as &str)
1539 {
1540 self.errors.push(Error::invalid_collection(
1541 decl_type,
1542 "source",
1543 &collection.name as &str,
1544 ));
1545 }
1546 num_errors == self.errors.len()
1547 }
1548
1549 fn validate_filtered_service_fields(
1550 &mut self,
1551 decl_type: DeclType,
1552 source_instance_filter: Option<&Vec<String>>,
1553 renamed_instances: Option<&Vec<fdecl::NameMapping>>,
1554 ) {
1555 if let Some(source_instance_filter) = source_instance_filter {
1556 if source_instance_filter.is_empty() {
1557 self.errors.push(Error::invalid_field(decl_type, "source_instance_filter"));
1560 }
1561 for name in source_instance_filter {
1562 check_name(Some(name), decl_type, "source_instance_filter", &mut self.errors);
1563 }
1564 }
1565 if let Some(renamed_instances) = renamed_instances {
1566 let mut seen_target_names = HashSet::<String>::new();
1568 for mapping in renamed_instances {
1569 check_name(
1570 Some(&mapping.source_name),
1571 decl_type,
1572 "renamed_instances.source_name",
1573 &mut self.errors,
1574 );
1575 check_name(
1576 Some(&mapping.target_name),
1577 decl_type,
1578 "renamed_instances.target_name",
1579 &mut self.errors,
1580 );
1581 if !seen_target_names.insert(mapping.target_name.clone()) {
1582 self.errors.push(Error::invalid_field(decl_type, "renamed_instances"));
1583 break;
1584 }
1585 }
1586 }
1587 }
1588
1589 fn validate_source_capability(
1590 &mut self,
1591 capability: &fdecl::CapabilityRef,
1592 decl_type: DeclType,
1593 field: &str,
1594 ) -> bool {
1595 let num_errors = self.errors.len();
1596 if check_name(Some(&capability.name), decl_type, "source.capability.name", &mut self.errors)
1597 && !self.all_capability_ids.contains(capability.name.as_str())
1598 {
1599 self.errors.push(Error::invalid_capability(decl_type, field, &capability.name));
1600 }
1601 num_errors == self.errors.len()
1602 }
1603
1604 fn make_group_key(
1608 target_name: Option<&'a String>,
1609 target: Option<&'a fdecl::Ref>,
1610 ) -> Option<(&'a str, RefKey<'a>)> {
1611 if target_name.is_none() {
1612 return None;
1613 }
1614 let target_name = target_name.unwrap().as_str();
1615 if target.is_none() {
1616 return None;
1617 }
1618 let target = match target.unwrap() {
1619 fdecl::Ref::Parent(_) => RefKey::Parent,
1620 fdecl::Ref::Self_(_) => RefKey::Self_,
1621 fdecl::Ref::Child(r) => RefKey::Child(r.name.as_str()),
1622 fdecl::Ref::Collection(r) => RefKey::Collection(r.name.as_str()),
1623 fdecl::Ref::Framework(_) => RefKey::Framework,
1624 fdecl::Ref::Capability(_) => RefKey::Capability,
1625 fdecl::Ref::Debug(_) => RefKey::Debug,
1626 fdecl::RefUnknown!() => {
1627 return None;
1628 }
1629 };
1630 Some((target_name, target))
1631 }
1632
1633 fn validate_aggregation_has_same_availability(
1634 &mut self,
1635 route_group: &Vec<impl HasAvailability>,
1636 ) {
1637 let availability_of_sources: BTreeSet<_> =
1639 route_group.iter().map(|r| r.availability()).collect();
1640
1641 if availability_of_sources.len() > 1 {
1643 self.errors.push(Error::different_availability_in_aggregation(
1644 availability_of_sources.into_iter().collect(),
1645 ));
1646 }
1647 }
1648
1649 fn validate_expose_group(&mut self, exposes: &'a Vec<fdecl::Expose>) {
1652 let mut expose_groups: HashMap<_, Vec<fdecl::ExposeService>> = HashMap::new();
1653 let service_exposes = exposes.into_iter().filter_map(|o| {
1654 if let fdecl::Expose::Service(s) = o {
1655 Some(s)
1656 } else {
1657 None
1658 }
1659 });
1660 for expose in service_exposes {
1661 let key = Self::make_group_key(expose.target_name.as_ref(), expose.target.as_ref());
1662 if let Some(key) = key {
1663 expose_groups.entry(key).or_insert_with(|| vec![]).push(expose.clone());
1664 }
1665 }
1666 for expose_group in expose_groups.into_values() {
1667 if expose_group.len() == 1 {
1668 continue;
1671 }
1672
1673 self.validate_aggregation_has_same_availability(&expose_group);
1674 }
1675 }
1676
1677 fn validate_expose_decl(
1678 &mut self,
1679 expose: &'a fdecl::Expose,
1680 expose_to_parent_ids: &mut HashMap<&'a str, AllowableIds>,
1681 expose_to_framework_ids: &mut HashMap<&'a str, AllowableIds>,
1682 ) {
1683 match expose {
1684 fdecl::Expose::Service(e) => {
1685 let decl = DeclType::ExposeService;
1686 self.validate_expose_fields(
1687 decl,
1688 AllowableIds::Many,
1689 CollectionSource::Allow,
1690 Self::service_checker,
1691 e.source.as_ref(),
1692 e.source_name.as_ref(),
1693 get_source_dictionary!(e),
1694 e.target.as_ref(),
1695 e.target_name.as_ref(),
1696 e.availability.as_ref(),
1697 expose_to_parent_ids,
1698 expose_to_framework_ids,
1699 );
1700 }
1701 fdecl::Expose::Protocol(e) => {
1702 let decl = DeclType::ExposeProtocol;
1703 self.validate_expose_fields(
1704 decl,
1705 AllowableIds::One,
1706 CollectionSource::Deny,
1707 Self::protocol_checker,
1708 e.source.as_ref(),
1709 e.source_name.as_ref(),
1710 get_source_dictionary!(e),
1711 e.target.as_ref(),
1712 e.target_name.as_ref(),
1713 e.availability.as_ref(),
1714 expose_to_parent_ids,
1715 expose_to_framework_ids,
1716 );
1717 }
1718 fdecl::Expose::Directory(e) => {
1719 let decl = DeclType::ExposeDirectory;
1720 self.validate_expose_fields(
1721 decl,
1722 AllowableIds::One,
1723 CollectionSource::Deny,
1724 Self::directory_checker,
1725 e.source.as_ref(),
1726 e.source_name.as_ref(),
1727 get_source_dictionary!(e),
1728 e.target.as_ref(),
1729 e.target_name.as_ref(),
1730 e.availability.as_ref(),
1731 expose_to_parent_ids,
1732 expose_to_framework_ids,
1733 );
1734
1735 match e.target.as_ref() {
1738 Some(fdecl::Ref::Framework(_)) => {
1739 if e.subdir.is_some() {
1740 self.errors.push(Error::invalid_field(decl, "subdir"));
1741 }
1742 }
1743 _ => {}
1744 }
1745
1746 if let Some(subdir) = e.subdir.as_ref() {
1747 check_relative_path(Some(subdir), decl, "subdir", &mut self.errors);
1748 }
1749 }
1750 fdecl::Expose::Runner(e) => {
1751 let decl = DeclType::ExposeRunner;
1752 self.validate_expose_fields(
1753 decl,
1754 AllowableIds::One,
1755 CollectionSource::Deny,
1756 Self::runner_checker,
1757 e.source.as_ref(),
1758 e.source_name.as_ref(),
1759 get_source_dictionary!(e),
1760 e.target.as_ref(),
1761 e.target_name.as_ref(),
1762 Some(&fdecl::Availability::Required),
1763 expose_to_parent_ids,
1764 expose_to_framework_ids,
1765 );
1766 }
1767 fdecl::Expose::Resolver(e) => {
1768 let decl = DeclType::ExposeResolver;
1769 self.validate_expose_fields(
1770 decl,
1771 AllowableIds::One,
1772 CollectionSource::Deny,
1773 Self::resolver_checker,
1774 e.source.as_ref(),
1775 e.source_name.as_ref(),
1776 get_source_dictionary!(e),
1777 e.target.as_ref(),
1778 e.target_name.as_ref(),
1779 Some(&fdecl::Availability::Required),
1780 expose_to_parent_ids,
1781 expose_to_framework_ids,
1782 );
1783 }
1784 #[cfg(fuchsia_api_level_at_least = "25")]
1785 fdecl::Expose::Dictionary(e) => {
1786 let decl = DeclType::ExposeDictionary;
1787 self.validate_expose_fields(
1788 decl,
1789 AllowableIds::One,
1790 CollectionSource::Deny,
1791 Self::dictionary_checker,
1792 e.source.as_ref(),
1793 e.source_name.as_ref(),
1794 get_source_dictionary!(e),
1795 e.target.as_ref(),
1796 e.target_name.as_ref(),
1797 Some(&fdecl::Availability::Required),
1798 expose_to_parent_ids,
1799 expose_to_framework_ids,
1800 );
1801 }
1802 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1803 fdecl::Expose::Config(e) => {
1804 let decl = DeclType::ExposeConfig;
1805 self.validate_expose_fields(
1806 decl,
1807 AllowableIds::One,
1808 CollectionSource::Deny,
1809 Self::config_checker,
1810 e.source.as_ref(),
1811 e.source_name.as_ref(),
1812 None,
1813 e.target.as_ref(),
1814 e.target_name.as_ref(),
1815 e.availability.as_ref(),
1816 expose_to_parent_ids,
1817 expose_to_framework_ids,
1818 );
1819 }
1820 _ => {
1821 self.errors.push(Error::invalid_field(DeclType::Component, "expose"));
1822 }
1823 }
1824 }
1825
1826 fn validate_expose_fields(
1827 &mut self,
1828 decl: DeclType,
1829 allowable_ids: AllowableIds,
1830 collection_source: CollectionSource,
1831 capability_checker: impl Fn(&Self) -> &dyn Container,
1835 source: Option<&fdecl::Ref>,
1836 source_name: Option<&String>,
1837 source_dictionary: Option<&String>,
1838 target: Option<&fdecl::Ref>,
1839 target_name: Option<&'a String>,
1840 availability: Option<&fdecl::Availability>,
1841 expose_to_parent_ids: &mut HashMap<&'a str, AllowableIds>,
1842 expose_to_framework_ids: &mut HashMap<&'a str, AllowableIds>,
1843 ) {
1844 self.validate_expose_source(decl, collection_source, source, source_dictionary);
1845 check_route_availability(decl, availability, source, source_name, &mut self.errors);
1846 match target {
1847 Some(r) => match r {
1848 fdecl::Ref::Parent(_) => {}
1849 fdecl::Ref::Framework(_) => {}
1850 _ => {
1851 self.errors.push(Error::invalid_field(decl, "target"));
1852 }
1853 },
1854 None => {
1855 self.errors.push(Error::missing_field(decl, "target"));
1856 }
1857 }
1858 check_name(source_name, decl, "source_name", &mut self.errors);
1859 if source_dictionary.is_some() {
1860 check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
1861 }
1862 if check_name(target_name, decl, "target_name", &mut self.errors) {
1863 let maybe_ids_set = match target {
1864 Some(fdecl::Ref::Parent(_)) => Some(expose_to_parent_ids),
1865 Some(fdecl::Ref::Framework(_)) => Some(expose_to_framework_ids),
1866 _ => None,
1867 };
1868 if let Some(ids_set) = maybe_ids_set {
1869 let target_name = target_name.unwrap();
1870 if let Some(prev_state) = ids_set.insert(target_name, allowable_ids) {
1871 if prev_state == AllowableIds::One || prev_state != allowable_ids {
1872 self.errors.push(Error::duplicate_field(decl, "target_name", target_name));
1873 }
1874 }
1875 }
1876 }
1877
1878 self.validate_route_from_self(
1879 decl,
1880 source,
1881 source_name,
1882 source_dictionary,
1883 capability_checker,
1884 );
1885 }
1886
1887 fn validate_expose_source(
1888 &mut self,
1889 decl: DeclType,
1890 collection_source: CollectionSource,
1891 source: Option<&fdecl::Ref>,
1892 source_dictionary: Option<&String>,
1893 ) {
1894 match (source, source_dictionary) {
1895 (Some(fdecl::Ref::Self_(_)), _) => {}
1897 (Some(fdecl::Ref::Child(child)), _) => {
1898 let _ = self.validate_child_ref(decl, "source", &child, OfferType::Static);
1899 }
1900 (Some(fdecl::Ref::VoidType(_)), None) => {}
1902 (Some(fdecl::Ref::Framework(_)), None) => {}
1903 (Some(fdecl::Ref::Capability(c)), None) => {
1904 self.validate_source_capability(c, decl, "source");
1905 }
1906 (Some(fdecl::Ref::Collection(c)), None)
1907 if collection_source == CollectionSource::Allow =>
1908 {
1909 self.validate_source_collection(c, decl);
1910 }
1911 (None, _) => {
1913 self.errors.push(Error::missing_field(decl, "source"));
1914 }
1915 (_, _) => {
1917 self.errors.push(Error::invalid_field(decl, "source"));
1918 }
1919 }
1920 }
1921
1922 fn validate_offer_group(&mut self, offers: &'a Vec<fdecl::Offer>, offer_type: OfferType) {
1925 let mut offer_groups: HashMap<_, Vec<fdecl::OfferService>> = HashMap::new();
1926 let service_offers = offers.into_iter().filter_map(|o| {
1927 if let fdecl::Offer::Service(s) = o {
1928 Some(s)
1929 } else {
1930 None
1931 }
1932 });
1933 for offer in service_offers {
1934 let key = Self::make_group_key(offer.target_name.as_ref(), offer.target.as_ref());
1935 if let Some(key) = key {
1936 offer_groups.entry(key).or_insert_with(|| vec![]).push(offer.clone());
1937 }
1938 }
1939 for offer_group in offer_groups.into_values() {
1940 if offer_group.len() == 1 {
1941 continue;
1944 }
1945
1946 self.validate_aggregation_has_same_availability(&offer_group);
1947
1948 let mut source_instance_filter_entries: HashSet<String> = HashSet::new();
1949 let mut service_source_names: HashSet<String> = HashSet::new();
1950 for o in offer_group {
1951 match (o.source_instance_filter, offer_type) {
1953 (Some(source_instance_filter), _) => {
1954 for instance_name in source_instance_filter {
1955 if !source_instance_filter_entries.insert(instance_name.clone()) {
1956 self.errors.push(Error::invalid_aggregate_offer(format!(
1959 "Conflicting source_instance_filter in aggregate service \
1960 offer, instance_name '{}' seen in filter lists multiple times",
1961 instance_name,
1962 )));
1963 }
1964 }
1965 }
1966 (None, OfferType::Static) => {}
1967 (None, OfferType::Dynamic) => {
1968 self.errors.push(Error::invalid_aggregate_offer(
1970 "source_instance_filter must be set for dynamic aggregate service \
1971 offers",
1972 ));
1973 }
1974 }
1975 service_source_names.insert(
1976 o.source_name
1977 .expect("Offer Service declarations must always contain source_name"),
1978 );
1979 }
1980
1981 if service_source_names.len() > 1 {
1982 self.errors.push(Error::invalid_aggregate_offer(format!(
1983 "All aggregate service offers must have the same source_name, saw {}. Use \
1984 renamed_instances to rename instance names to avoid conflict.",
1985 service_source_names.into_iter().sorted().collect::<Vec<String>>().join(", ")
1986 )));
1987 }
1988 }
1989 }
1990
1991 fn validate_offer_decl(&mut self, offer: &'a fdecl::Offer, offer_type: OfferType) {
1992 match offer {
1993 fdecl::Offer::Service(o) => {
1994 let decl = DeclType::OfferService;
1995 self.validate_offer_fields(
1996 decl,
1997 AllowableIds::Many,
1998 CollectionSource::Allow,
1999 Self::service_checker,
2000 o.source.as_ref(),
2001 o.source_name.as_ref(),
2002 get_source_dictionary!(o),
2003 o.target.as_ref(),
2004 o.target_name.as_ref(),
2005 #[cfg(fuchsia_api_level_at_least = "HEAD")]
2006 Some(o.dependency_type.as_ref().unwrap_or(&fdecl::DependencyType::Strong)),
2007 #[cfg(fuchsia_api_level_less_than = "HEAD")]
2008 Some(&fdecl::DependencyType::Strong),
2009 o.availability.as_ref(),
2010 offer_type,
2011 );
2012 self.validate_filtered_service_fields(
2013 decl,
2014 o.source_instance_filter.as_ref(),
2015 o.renamed_instances.as_ref(),
2016 );
2017 }
2018 fdecl::Offer::Protocol(o) => {
2019 let decl = DeclType::OfferProtocol;
2020 self.validate_offer_fields(
2021 decl,
2022 AllowableIds::One,
2023 CollectionSource::Deny,
2024 Self::protocol_checker,
2025 o.source.as_ref(),
2026 o.source_name.as_ref(),
2027 get_source_dictionary!(o),
2028 o.target.as_ref(),
2029 o.target_name.as_ref(),
2030 o.dependency_type.as_ref(),
2031 o.availability.as_ref(),
2032 offer_type,
2033 );
2034 }
2035 fdecl::Offer::Directory(o) => {
2036 let decl = DeclType::OfferDirectory;
2037 self.validate_offer_fields(
2038 decl,
2039 AllowableIds::One,
2040 CollectionSource::Deny,
2041 Self::directory_checker,
2042 o.source.as_ref(),
2043 o.source_name.as_ref(),
2044 get_source_dictionary!(o),
2045 o.target.as_ref(),
2046 o.target_name.as_ref(),
2047 o.dependency_type.as_ref(),
2048 o.availability.as_ref(),
2049 offer_type,
2050 );
2051 if let Some(subdir) = o.subdir.as_ref() {
2052 check_relative_path(
2053 Some(subdir),
2054 DeclType::OfferDirectory,
2055 "subdir",
2056 &mut self.errors,
2057 );
2058 }
2059 }
2060 fdecl::Offer::Storage(o) => {
2061 let decl = DeclType::OfferStorage;
2062 self.validate_storage_offer_fields(
2063 decl,
2064 Self::storage_checker,
2065 o.source.as_ref(),
2066 o.source_name.as_ref(),
2067 o.target.as_ref(),
2068 o.target_name.as_ref(),
2069 o.availability.as_ref(),
2070 offer_type,
2071 );
2072 }
2073 fdecl::Offer::Runner(o) => {
2074 let decl = DeclType::OfferRunner;
2075 self.validate_offer_fields(
2076 decl,
2077 AllowableIds::One,
2078 CollectionSource::Deny,
2079 Self::runner_checker,
2080 o.source.as_ref(),
2081 o.source_name.as_ref(),
2082 get_source_dictionary!(o),
2083 o.target.as_ref(),
2084 o.target_name.as_ref(),
2085 Some(&fdecl::DependencyType::Strong),
2086 Some(&fdecl::Availability::Required),
2087 offer_type,
2088 );
2089 }
2090 fdecl::Offer::Resolver(o) => {
2091 let decl = DeclType::OfferResolver;
2092 self.validate_offer_fields(
2093 decl,
2094 AllowableIds::One,
2095 CollectionSource::Deny,
2096 Self::resolver_checker,
2097 o.source.as_ref(),
2098 o.source_name.as_ref(),
2099 get_source_dictionary!(o),
2100 o.target.as_ref(),
2101 o.target_name.as_ref(),
2102 Some(&fdecl::DependencyType::Strong),
2103 Some(&fdecl::Availability::Required),
2104 offer_type,
2105 );
2106 }
2107 fdecl::Offer::EventStream(e) => {
2108 self.validate_event_stream_offer_fields(e, offer_type);
2109 }
2110 #[cfg(fuchsia_api_level_at_least = "25")]
2111 fdecl::Offer::Dictionary(o) => {
2112 let decl = DeclType::OfferDictionary;
2113 self.validate_offer_fields(
2114 decl,
2115 AllowableIds::One,
2116 CollectionSource::Deny,
2117 Self::dictionary_checker,
2118 o.source.as_ref(),
2119 o.source_name.as_ref(),
2120 get_source_dictionary!(o),
2121 o.target.as_ref(),
2122 o.target_name.as_ref(),
2123 o.dependency_type.as_ref(),
2124 o.availability.as_ref(),
2125 offer_type,
2126 );
2127 }
2128 #[cfg(fuchsia_api_level_at_least = "HEAD")]
2129 fdecl::Offer::Config(o) => {
2130 let decl = DeclType::OfferConfig;
2131 self.validate_offer_fields(
2132 decl,
2133 AllowableIds::One,
2134 CollectionSource::Deny,
2135 Self::config_checker,
2136 o.source.as_ref(),
2137 o.source_name.as_ref(),
2138 None,
2139 o.target.as_ref(),
2140 o.target_name.as_ref(),
2141 Some(&fdecl::DependencyType::Strong),
2142 o.availability.as_ref(),
2143 offer_type,
2144 );
2145 }
2146 fdecl::OfferUnknown!() => {
2147 self.errors.push(Error::invalid_field(DeclType::Component, "offer"));
2148 }
2149 }
2150 }
2151
2152 fn validate_offer_fields(
2153 &mut self,
2154 decl: DeclType,
2155 allowable_names: AllowableIds,
2156 collection_source: CollectionSource,
2157 capability_checker: impl Fn(&Self) -> &dyn Container,
2158 source: Option<&'a fdecl::Ref>,
2159 source_name: Option<&'a String>,
2160 source_dictionary: Option<&'a String>,
2161 target: Option<&'a fdecl::Ref>,
2162 target_name: Option<&'a String>,
2163 dependency_type: Option<&'a fdecl::DependencyType>,
2164 availability: Option<&'a fdecl::Availability>,
2165 offer_type: OfferType,
2166 ) {
2167 self.validate_offer_source(decl, collection_source, source, source_dictionary, offer_type);
2168 check_route_availability(decl, availability, source, source_name, &mut self.errors);
2169 check_offer_name(source_name, decl, "source_name", offer_type, &mut self.errors);
2170 if source_dictionary.is_some() {
2171 check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
2172 }
2173 self.validate_offer_target(decl, allowable_names, target, target_name, offer_type);
2174 check_offer_name(target_name, decl, "target_name", offer_type, &mut self.errors);
2175
2176 if dependency_type.is_none() {
2177 self.errors.push(Error::missing_field(decl, "dependency_type"));
2178 }
2179
2180 self.validate_route_from_self(
2181 decl,
2182 source,
2183 source_name,
2184 source_dictionary,
2185 capability_checker,
2186 );
2187 }
2188
2189 fn validate_offer_source(
2190 &mut self,
2191 decl: DeclType,
2192 collection_source: CollectionSource,
2193 source: Option<&'a fdecl::Ref>,
2194 source_dictionary: Option<&'a String>,
2195 offer_type: OfferType,
2196 ) {
2197 match (source, source_dictionary) {
2198 (Some(fdecl::Ref::Parent(_)), _) => {}
2200 (Some(fdecl::Ref::Self_(_)), _) => {}
2201 (Some(fdecl::Ref::Child(child)), _) => {
2202 self.validate_child_ref(decl, "source", &child, offer_type);
2203 }
2204 (Some(fdecl::Ref::VoidType(_)), None) => {}
2206 (Some(fdecl::Ref::Framework(_)), None) => {}
2207 (Some(fdecl::Ref::Capability(c)), None) => {
2208 self.validate_source_capability(c, decl, "source");
2209 }
2210 (Some(fdecl::Ref::Collection(c)), None)
2211 if collection_source == CollectionSource::Allow =>
2212 {
2213 self.validate_source_collection(c, decl);
2214 }
2215 (None, _) => self.errors.push(Error::missing_field(decl, "source")),
2217 (_, _) => self.errors.push(Error::invalid_field(decl, "source")),
2219 }
2220 }
2221
2222 fn validate_storage_offer_fields(
2223 &mut self,
2224 decl: DeclType,
2225 capability_checker: impl Fn(&Self) -> &dyn Container,
2229 source: Option<&'a fdecl::Ref>,
2230 source_name: Option<&'a String>,
2231 target: Option<&'a fdecl::Ref>,
2232 target_name: Option<&'a String>,
2233 availability: Option<&fdecl::Availability>,
2234 offer_type: OfferType,
2235 ) {
2236 match source {
2237 Some(fdecl::Ref::Parent(_) | fdecl::Ref::VoidType(_) | fdecl::Ref::Self_(_)) => {}
2238 Some(_) => {
2239 self.errors.push(Error::invalid_field(decl, "source"));
2240 }
2241 None => {
2242 self.errors.push(Error::missing_field(decl, "source"));
2243 }
2244 }
2245 check_route_availability(decl, availability, source, source_name, &mut self.errors);
2246 check_offer_name(source_name, decl, "source_name", offer_type, &mut self.errors);
2247 self.validate_offer_target(decl, AllowableIds::One, target, target_name, offer_type);
2248 check_offer_name(target_name, decl, "target_name", offer_type, &mut self.errors);
2249
2250 if let (Some(fdecl::Ref::Self_(_)), Some(name)) = (source, source_name) {
2251 if !(capability_checker)(self).contains(name) {
2252 self.errors.push(Error::invalid_capability(decl, "source", name));
2253 }
2254 }
2255 }
2256
2257 fn validate_event_stream_offer_fields(
2258 &mut self,
2259 event_stream: &'a fdecl::OfferEventStream,
2260 offer_type: OfferType,
2261 ) {
2262 let decl = DeclType::OfferEventStream;
2263 check_name(event_stream.source_name.as_ref(), decl, "source_name", &mut self.errors);
2264 if event_stream.target == Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})) {
2265 self.errors.push(Error::invalid_field(DeclType::OfferEventStream, "target"));
2267 }
2268 if let Some(scope) = &event_stream.scope {
2269 if scope.is_empty() {
2270 self.errors.push(Error::invalid_field(decl, "scope"));
2271 }
2272 for value in scope {
2273 match value {
2274 fdecl::Ref::Child(child) => {
2275 self.validate_child_ref(
2276 DeclType::OfferEventStream,
2277 "scope",
2278 &child,
2279 offer_type,
2280 );
2281 }
2282 fdecl::Ref::Collection(collection) => {
2283 self.validate_collection_ref(
2284 DeclType::OfferEventStream,
2285 "scope",
2286 &collection,
2287 );
2288 }
2289 _ => {
2290 self.errors.push(Error::invalid_field(DeclType::OfferEventStream, "scope"));
2291 }
2292 }
2293 }
2294 }
2295 match event_stream.source {
2297 Some(
2298 fdecl::Ref::Parent(_)
2299 | fdecl::Ref::Framework(_)
2300 | fdecl::Ref::Child(_)
2301 | fdecl::Ref::VoidType(_),
2302 ) => {}
2303 Some(_) => {
2304 self.errors.push(Error::invalid_field(decl, "source"));
2305 }
2306 None => {
2307 self.errors.push(Error::missing_field(decl, "source"));
2308 }
2309 };
2310
2311 check_route_availability(
2312 decl,
2313 event_stream.availability.as_ref(),
2314 event_stream.source.as_ref(),
2315 event_stream.source_name.as_ref(),
2316 &mut self.errors,
2317 );
2318
2319 self.validate_offer_target(
2320 decl,
2321 AllowableIds::One,
2322 event_stream.target.as_ref(),
2323 event_stream.target_name.as_ref(),
2324 offer_type,
2325 );
2326 check_name(event_stream.target_name.as_ref(), decl, "target_name", &mut self.errors);
2327 }
2328
2329 fn validate_child_ref(
2331 &mut self,
2332 decl: DeclType,
2333 field_name: &str,
2334 child: &fdecl::ChildRef,
2335 offer_type: OfferType,
2336 ) -> bool {
2337 if offer_type == OfferType::Dynamic && child.collection.is_some() {
2338 return self.validate_dynamic_child_ref(decl, field_name, child);
2339 }
2340 let mut valid = true;
2344 if !check_name(
2345 Some(&child.name),
2346 decl,
2347 &format!("{}.child.name", field_name),
2348 &mut self.errors,
2349 ) {
2350 valid = false;
2351 }
2352 if child.collection.is_some() {
2353 self.errors
2354 .push(Error::extraneous_field(decl, format!("{}.child.collection", field_name)));
2355 valid = false;
2356 }
2357 if !valid {
2358 return false;
2359 }
2360
2361 let name: &str = &child.name;
2363 if !self.all_children.contains_key(name) {
2364 self.errors.push(Error::invalid_child(decl, field_name, name));
2365 return false;
2366 }
2367
2368 true
2369 }
2370
2371 fn validate_dynamic_child_ref(
2376 &mut self,
2377 decl: DeclType,
2378 field_name: &str,
2379 child: &fdecl::ChildRef,
2380 ) -> bool {
2381 let mut valid = true;
2385 if !check_dynamic_name(
2386 Some(&child.name),
2387 decl,
2388 &format!("{}.child.name", field_name),
2389 &mut self.errors,
2390 ) {
2391 valid = false;
2392 }
2393 if !check_name(
2394 child.collection.as_ref(),
2395 decl,
2396 &format!("{}.child.collection", field_name),
2397 &mut self.errors,
2398 ) {
2399 valid = false;
2400 }
2401 valid
2402 }
2403
2404 fn validate_collection_ref(
2406 &mut self,
2407 decl: DeclType,
2408 field_name: &str,
2409 collection: &fdecl::CollectionRef,
2410 ) -> bool {
2411 if !check_name(
2413 Some(&collection.name),
2414 decl,
2415 &format!("{}.collection.name", field_name),
2416 &mut self.errors,
2417 ) {
2418 return false;
2419 }
2420
2421 if !self.all_collections.contains(&collection.name as &str) {
2423 self.errors.push(Error::invalid_collection(decl, field_name, &collection.name as &str));
2424 return false;
2425 }
2426
2427 true
2428 }
2429
2430 fn validate_offer_target(
2431 &mut self,
2432 decl: DeclType,
2433 allowable_names: AllowableIds,
2434 target: Option<&'a fdecl::Ref>,
2435 target_name: Option<&'a String>,
2436 offer_type: OfferType,
2437 ) {
2438 match target {
2439 Some(fdecl::Ref::Child(c)) => {
2440 self.validate_target_child(decl, allowable_names, c, target_name, offer_type);
2441 }
2442 Some(fdecl::Ref::Collection(c)) => {
2443 self.validate_target_collection(decl, allowable_names, c, target_name);
2444 }
2445 Some(fdecl::Ref::Capability(c)) => {
2446 #[cfg(fuchsia_api_level_at_least = "25")]
2448 if let Some(d) = self.all_dictionaries.get(&c.name.as_str()) {
2449 if d.source_path.is_some() {
2450 self.errors.push(Error::invalid_field(decl, "target"));
2453 }
2454 } else {
2455 self.errors.push(Error::invalid_field(decl, "target"));
2456 }
2457 #[cfg(not(fuchsia_api_level_at_least = "25"))]
2458 {
2459 let _ = c;
2460 self.errors.push(Error::invalid_field(decl, "target"));
2461 }
2462 }
2463 Some(_) => {
2464 self.errors.push(Error::invalid_field(decl, "target"));
2465 }
2466 None => {
2467 self.errors.push(Error::missing_field(decl, "target"));
2468 }
2469 }
2470 }
2471
2472 fn validate_target_child(
2473 &mut self,
2474 decl: DeclType,
2475 allowable_names: AllowableIds,
2476 child: &'a fdecl::ChildRef,
2477 target_name: Option<&'a String>,
2478 offer_type: OfferType,
2479 ) {
2480 if !self.validate_child_ref(decl, "target", child, offer_type) {
2481 return;
2482 }
2483 if let Some(target_name) = target_name {
2484 let names_for_target = self
2485 .target_ids
2486 .entry(TargetId::Component(
2487 &child.name,
2488 child.collection.as_ref().map(|s| s.as_str()),
2489 ))
2490 .or_insert(HashMap::new());
2491 if let Some(prev_state) = names_for_target.insert(target_name, allowable_names) {
2492 if prev_state == AllowableIds::One || prev_state != allowable_names {
2493 self.errors.push(Error::duplicate_field(
2494 decl,
2495 "target_name",
2496 target_name as &str,
2497 ));
2498 }
2499 }
2500 if let Some(collection) = child.collection.as_ref() {
2501 if let Some(names_for_target) =
2502 self.target_ids.get(&TargetId::Collection(&collection))
2503 {
2504 if names_for_target.contains_key(&target_name.as_str()) {
2505 self.errors.push(Error::duplicate_field(
2507 decl,
2508 "target_name",
2509 target_name as &str,
2510 ));
2511 }
2512 }
2513 }
2514 }
2515 }
2516
2517 fn validate_target_collection(
2518 &mut self,
2519 decl: DeclType,
2520 allowable_names: AllowableIds,
2521 collection: &'a fdecl::CollectionRef,
2522 target_name: Option<&'a String>,
2523 ) {
2524 if !self.validate_collection_ref(decl, "target", &collection) {
2525 return;
2526 }
2527 if let Some(target_name) = target_name {
2528 let names_for_target = self
2529 .target_ids
2530 .entry(TargetId::Collection(&collection.name))
2531 .or_insert(HashMap::new());
2532 if let Some(prev_state) = names_for_target.insert(target_name, allowable_names) {
2533 if prev_state == AllowableIds::One || prev_state != allowable_names {
2534 self.errors.push(Error::duplicate_field(
2535 decl,
2536 "target_name",
2537 target_name as &str,
2538 ));
2539 }
2540 }
2541 }
2542 }
2543
2544 fn validate_route_from_self(
2545 &mut self,
2546 decl: DeclType,
2547 source: Option<&fdecl::Ref>,
2548 source_name: Option<&String>,
2549 source_dictionary: Option<&String>,
2550 capability_checker: impl Fn(&Self) -> &dyn Container,
2551 ) {
2552 let (Some(fdecl::Ref::Self_(_)), Some(name)) = (source, source_name) else {
2553 return;
2554 };
2555 match source_dictionary {
2556 Some(source_dictionary) => {
2557 #[cfg(fuchsia_api_level_at_least = "25")]
2558 {
2559 use cm_types::IterablePath;
2560 if let Ok(path) = cm_types::RelativePath::new(source_dictionary) {
2561 if let Some(first_segment) = path.iter_segments().next().map(|s| s.as_str())
2562 {
2563 if !self.all_dictionaries.contains_key(first_segment) {
2564 self.errors.push(Error::invalid_capability(
2565 decl,
2566 "source",
2567 first_segment,
2568 ));
2569 }
2570 }
2571 }
2572 }
2573 #[cfg(not(fuchsia_api_level_at_least = "25"))]
2574 let _ = source_dictionary;
2575 }
2576 None => {
2577 if !(capability_checker)(self).contains(name) {
2578 self.errors.push(Error::invalid_capability(decl, "source", name));
2579 }
2580 }
2581 }
2582 }
2583
2584 fn service_checker(&self) -> &dyn Container {
2587 &self.all_services
2588 }
2589 fn protocol_checker(&self) -> &dyn Container {
2590 &self.all_protocols
2591 }
2592 fn directory_checker(&self) -> &dyn Container {
2593 &self.all_directories
2594 }
2595 fn runner_checker(&self) -> &dyn Container {
2596 &self.all_runners
2597 }
2598 fn resolver_checker(&self) -> &dyn Container {
2599 &self.all_resolvers
2600 }
2601
2602 #[cfg(fuchsia_api_level_at_least = "25")]
2603 fn dictionary_checker(&self) -> &dyn Container {
2604 &self.all_dictionaries
2605 }
2606
2607 #[cfg(fuchsia_api_level_at_least = "HEAD")]
2608 fn config_checker(&self) -> &dyn Container {
2609 &self.all_configs
2610 }
2611 fn storage_checker(&self) -> &dyn Container {
2612 &self.all_storages
2613 }
2614 fn event_stream_checker(&self) -> &dyn Container {
2615 struct AlwaysTrueContainer {}
2619 impl Container for AlwaysTrueContainer {
2620 fn contains(&self, _key: &str) -> bool {
2621 true
2622 }
2623 }
2624 static CONTAINER: AlwaysTrueContainer = AlwaysTrueContainer {};
2625 &CONTAINER
2626 }
2627}
2628
2629#[cfg(test)]
2630mod tests {
2631 use super::*;
2632 use cm_types::MAX_LONG_NAME_LENGTH;
2633 use test_case::test_case;
2634 use {
2635 fidl_fuchsia_component_decl as fdecl, fidl_fuchsia_data as fdata, fidl_fuchsia_io as fio,
2636 };
2637
2638 macro_rules! test_validate {
2639 (
2640 $(
2641 $test_name:ident => {
2642 input = $input:expr,
2643 result = $result:expr,
2644 },
2645 )+
2646 ) => {
2647 $(
2648 #[test]
2649 fn $test_name() {
2650 validate_test($input, $result);
2651 }
2652 )+
2653 }
2654 }
2655
2656 macro_rules! test_validate_any_result {
2657 (
2658 $(
2659 $test_name:ident => {
2660 input = $input:expr,
2661 results = $results:expr,
2662 },
2663 )+
2664 ) => {
2665 $(
2666 #[test]
2667 fn $test_name() {
2668 validate_test_any_result($input, $results);
2669 }
2670 )+
2671 }
2672 }
2673
2674 macro_rules! test_validate_values_data {
2675 (
2676 $(
2677 $test_name:ident => {
2678 input = $input:expr,
2679 result = $result:expr,
2680 },
2681 )+
2682 ) => {
2683 $(
2684 #[test]
2685 fn $test_name() {
2686 validate_values_data_test($input, $result);
2687 }
2688 )+
2689 }
2690 }
2691
2692 macro_rules! test_validate_capabilities {
2693 (
2694 $(
2695 $test_name:ident => {
2696 input = $input:expr,
2697 as_builtin = $as_builtin:expr,
2698 result = $result:expr,
2699 },
2700 )+
2701 ) => {
2702 $(
2703 #[test]
2704 fn $test_name() {
2705 validate_capabilities_test($input, $as_builtin, $result);
2706 }
2707 )+
2708 }
2709 }
2710
2711 macro_rules! test_dependency {
2712 (
2713 $(
2714 ($test_name:ident) => {
2715 ty = $ty:expr,
2716 offer_decl = $offer_decl:expr,
2717 },
2718 )+
2719 ) => {
2720 $(
2721 #[test]
2722 fn $test_name() {
2723 let mut decl = new_component_decl();
2724 let dependencies = vec![
2725 ("a", "b"),
2726 ("b", "a"),
2727 ];
2728 let offers = dependencies.into_iter().map(|(from,to)| {
2729 let mut offer_decl = $offer_decl;
2730 offer_decl.source = Some(fdecl::Ref::Child(
2731 fdecl::ChildRef { name: from.to_string(), collection: None },
2732 ));
2733 offer_decl.target = Some(fdecl::Ref::Child(
2734 fdecl::ChildRef { name: to.to_string(), collection: None },
2735 ));
2736 $ty(offer_decl)
2737 }).collect();
2738 let children = ["a", "b"].iter().map(|name| {
2739 fdecl::Child {
2740 name: Some(name.to_string()),
2741 url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
2742 startup: Some(fdecl::StartupMode::Lazy),
2743 on_terminate: None,
2744 environment: None,
2745 ..Default::default()
2746 }
2747 }).collect();
2748 decl.offers = Some(offers);
2749 decl.children = Some(children);
2750 let result = Err(ErrorList::new(vec![
2751 Error::dependency_cycle(
2752 directed_graph::Error::CyclesDetected([vec!["child a", "child b", "child a"]].iter().cloned().collect()).format_cycle()),
2753 ]));
2754 validate_test(decl, result);
2755 }
2756 )+
2757 }
2758 }
2759
2760 macro_rules! test_weak_dependency {
2761 (
2762 $(
2763 ($test_name:ident) => {
2764 ty = $ty:expr,
2765 offer_decl = $offer_decl:expr,
2766 },
2767 )+
2768 ) => {
2769 $(
2770 #[test_case(fdecl::DependencyType::Weak)]
2771 fn $test_name(weak_dep: fdecl::DependencyType) {
2772 let mut decl = new_component_decl();
2773 let offers = vec![
2774 {
2775 let mut offer_decl = $offer_decl;
2776 offer_decl.source = Some(fdecl::Ref::Child(
2777 fdecl::ChildRef { name: "a".to_string(), collection: None },
2778 ));
2779 offer_decl.target = Some(fdecl::Ref::Child(
2780 fdecl::ChildRef { name: "b".to_string(), collection: None },
2781 ));
2782 offer_decl.dependency_type = Some(fdecl::DependencyType::Strong);
2783 $ty(offer_decl)
2784 },
2785 {
2786 let mut offer_decl = $offer_decl;
2787 offer_decl.source = Some(fdecl::Ref::Child(
2788 fdecl::ChildRef { name: "b".to_string(), collection: None },
2789 ));
2790 offer_decl.target = Some(fdecl::Ref::Child(
2791 fdecl::ChildRef { name: "a".to_string(), collection: None },
2792 ));
2793 offer_decl.dependency_type = Some(weak_dep);
2794 $ty(offer_decl)
2795 },
2796 ];
2797 let children = ["a", "b"].iter().map(|name| {
2798 fdecl::Child {
2799 name: Some(name.to_string()),
2800 url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
2801 startup: Some(fdecl::StartupMode::Lazy),
2802 on_terminate: None,
2803 environment: None,
2804 ..Default::default()
2805 }
2806 }).collect();
2807 decl.offers = Some(offers);
2808 decl.children = Some(children);
2809 let result = Ok(());
2810 validate_test(decl, result);
2811 }
2812 )+
2813 }
2814 }
2815
2816 #[track_caller]
2817 fn validate_test(input: fdecl::Component, expected_res: Result<(), ErrorList>) {
2818 let res = validate(&input);
2819 assert_eq!(res, expected_res);
2820 }
2821
2822 #[track_caller]
2823 fn validate_test_any_result(input: fdecl::Component, expected_res: Vec<Result<(), ErrorList>>) {
2824 let res = format!("{:?}", validate(&input));
2825 let expected_res_debug = format!("{:?}", expected_res);
2826
2827 let matched_exp =
2828 expected_res.into_iter().find(|expected| res == format!("{:?}", expected));
2829
2830 assert!(
2831 matched_exp.is_some(),
2832 "assertion failed: Expected one of:\n{:?}\nActual:\n{:?}",
2833 expected_res_debug,
2834 res
2835 );
2836 }
2837
2838 #[track_caller]
2839 fn validate_values_data_test(
2840 input: fdecl::ConfigValuesData,
2841 expected_res: Result<(), ErrorList>,
2842 ) {
2843 let res = validate_values_data(&input);
2844 assert_eq!(res, expected_res);
2845 }
2846
2847 #[track_caller]
2848 fn validate_capabilities_test(
2849 input: Vec<fdecl::Capability>,
2850 as_builtin: bool,
2851 expected_res: Result<(), ErrorList>,
2852 ) {
2853 let res = validate_capabilities(&input, as_builtin);
2854 assert_eq!(res, expected_res);
2855 }
2856
2857 fn new_component_decl() -> fdecl::Component {
2858 fdecl::Component {
2859 program: None,
2860 uses: None,
2861 exposes: None,
2862 offers: None,
2863 facets: None,
2864 capabilities: None,
2865 children: None,
2866 collections: None,
2867 environments: None,
2868 ..Default::default()
2869 }
2870 }
2871
2872 test_validate_any_result! {
2873 test_validate_use_disallows_nested_dirs => {
2874 input = {
2875 let mut decl = new_component_decl();
2876 decl.uses = Some(vec![
2877 fdecl::Use::Directory(fdecl::UseDirectory {
2878 dependency_type: Some(fdecl::DependencyType::Strong),
2879 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2880 source_name: Some("abc".to_string()),
2881 target_path: Some("/foo/bar".to_string()),
2882 rights: Some(fio::Operations::CONNECT),
2883 subdir: None,
2884 ..Default::default()
2885 }),
2886 fdecl::Use::Directory(fdecl::UseDirectory {
2887 dependency_type: Some(fdecl::DependencyType::Strong),
2888 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2889 source_name: Some("abc".to_string()),
2890 target_path: Some("/foo/bar/baz".to_string()),
2891 rights: Some(fio::Operations::CONNECT),
2892 subdir: None,
2893 ..Default::default()
2894 }),
2895 ]);
2896 decl
2897 },
2898 results = vec![
2899 Err(ErrorList::new(vec![
2900 Error::invalid_path_overlap(
2901 DeclType::UseDirectory, "/foo/bar/baz", DeclType::UseDirectory, "/foo/bar"),
2902 ])),
2903 Err(ErrorList::new(vec![
2904 Error::invalid_path_overlap(
2905 DeclType::UseDirectory, "/foo/bar", DeclType::UseDirectory, "/foo/bar/baz"),
2906 ])),
2907 ],
2908 },
2909 test_validate_use_disallows_nested_dirs_storage => {
2910 input = {
2911 let mut decl = new_component_decl();
2912 decl.uses = Some(vec![
2913 fdecl::Use::Storage(fdecl::UseStorage {
2914 source_name: Some("abc".to_string()),
2915 target_path: Some("/foo/bar".to_string()),
2916 ..Default::default()
2917 }),
2918 fdecl::Use::Storage(fdecl::UseStorage {
2919 source_name: Some("abc".to_string()),
2920 target_path: Some("/foo/bar/baz".to_string()),
2921 ..Default::default()
2922 }),
2923 ]);
2924 decl
2925 },
2926 results = vec![
2927 Err(ErrorList::new(vec![
2928 Error::invalid_path_overlap(
2929 DeclType::UseStorage, "/foo/bar/baz", DeclType::UseStorage, "/foo/bar"),
2930 ])),
2931 Err(ErrorList::new(vec![
2932 Error::invalid_path_overlap(
2933 DeclType::UseStorage, "/foo/bar", DeclType::UseStorage, "/foo/bar/baz"),
2934 ])),
2935 ],
2936 },
2937 test_validate_use_disallows_nested_dirs_directory_and_storage => {
2938 input = {
2939 let mut decl = new_component_decl();
2940 decl.uses = Some(vec![
2941 fdecl::Use::Directory(fdecl::UseDirectory {
2942 dependency_type: Some(fdecl::DependencyType::Strong),
2943 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2944 source_name: Some("abc".to_string()),
2945 target_path: Some("/foo/bar".to_string()),
2946 rights: Some(fio::Operations::CONNECT),
2947 subdir: None,
2948 ..Default::default()
2949 }),
2950 fdecl::Use::Storage(fdecl::UseStorage {
2951 source_name: Some("abc".to_string()),
2952 target_path: Some("/foo/bar/baz".to_string()),
2953 ..Default::default()
2954 }),
2955 ]);
2956 decl
2957 },
2958 results = vec![
2959 Err(ErrorList::new(vec![
2960 Error::invalid_path_overlap(
2961 DeclType::UseStorage, "/foo/bar/baz", DeclType::UseDirectory, "/foo/bar"),
2962 ])),
2963 Err(ErrorList::new(vec![
2964 Error::invalid_path_overlap(
2965 DeclType::UseDirectory, "/foo/bar", DeclType::UseStorage, "/foo/bar/baz"),
2966 ])),
2967 ],
2968 },
2969 test_validate_use_disallows_common_prefixes_protocol => {
2970 input = {
2971 let mut decl = new_component_decl();
2972 decl.uses = Some(vec![
2973 fdecl::Use::Directory(fdecl::UseDirectory {
2974 dependency_type: Some(fdecl::DependencyType::Strong),
2975 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2976 source_name: Some("abc".to_string()),
2977 target_path: Some("/foo/bar".to_string()),
2978 rights: Some(fio::Operations::CONNECT),
2979 subdir: None,
2980 ..Default::default()
2981 }),
2982 fdecl::Use::Protocol(fdecl::UseProtocol {
2983 dependency_type: Some(fdecl::DependencyType::Strong),
2984 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2985 source_name: Some("crow".to_string()),
2986 target_path: Some("/foo/bar/fuchsia.2".to_string()),
2987 ..Default::default()
2988 }),
2989 ]);
2990 decl
2991 },
2992 results = vec![
2993 Err(ErrorList::new(vec![
2994 Error::invalid_path_overlap(
2995 DeclType::UseProtocol, "/foo/bar/fuchsia.2", DeclType::UseDirectory, "/foo/bar"),
2996 ])),
2997 Err(ErrorList::new(vec![
2998 Error::invalid_path_overlap(
2999 DeclType::UseDirectory, "/foo/bar", DeclType::UseProtocol, "/foo/bar/fuchsia.2"),
3000 ])),
3001 ],
3002 },
3003 test_validate_use_disallows_common_prefixes_service => {
3004 input = {
3005 let mut decl = new_component_decl();
3006 decl.uses = Some(vec![
3007 fdecl::Use::Directory(fdecl::UseDirectory {
3008 dependency_type: Some(fdecl::DependencyType::Strong),
3009 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3010 source_name: Some("abc".to_string()),
3011 target_path: Some("/foo/bar".to_string()),
3012 rights: Some(fio::Operations::CONNECT),
3013 subdir: None,
3014 ..Default::default()
3015 }),
3016 fdecl::Use::Service(fdecl::UseService {
3017 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3018 source_name: Some("space".to_string()),
3019 target_path: Some("/foo/bar/baz/fuchsia.logger.Log".to_string()),
3020 dependency_type: Some(fdecl::DependencyType::Strong),
3021 ..Default::default()
3022 }),
3023 ]);
3024 decl
3025 },
3026 results = vec![
3027 Err(ErrorList::new(vec![
3028 Error::invalid_path_overlap(
3029 DeclType::UseService, "/foo/bar/baz/fuchsia.logger.Log", DeclType::UseDirectory, "/foo/bar"),
3030 ])),
3031 Err(ErrorList::new(vec![
3032 Error::invalid_path_overlap(
3033 DeclType::UseDirectory, "/foo/bar", DeclType::UseService, "/foo/bar/baz/fuchsia.logger.Log"),
3034 ])),
3035 ],
3036 },
3037 test_validate_use_disallows_pkg => {
3038 input = {
3039 let mut decl = new_component_decl();
3040 decl.uses = Some(vec![
3041 fdecl::Use::Directory(fdecl::UseDirectory {
3042 dependency_type: Some(fdecl::DependencyType::Strong),
3043 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3044 source_name: Some("abc".to_string()),
3045 target_path: Some("/pkg".to_string()),
3046 rights: Some(fio::Operations::CONNECT),
3047 subdir: None,
3048 ..Default::default()
3049 }),
3050 ]);
3051 decl
3052 },
3053 results = vec![
3054 Err(ErrorList::new(vec![
3055 Error::pkg_path_overlap(DeclType::UseDirectory, "/pkg"),
3056 ])),
3057 ],
3058 },
3059 test_validate_use_disallows_pkg_overlap => {
3060 input = {
3061 let mut decl = new_component_decl();
3062 decl.uses = Some(vec![
3063 fdecl::Use::Directory(fdecl::UseDirectory {
3064 dependency_type: Some(fdecl::DependencyType::Strong),
3065 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3066 source_name: Some("abc".to_string()),
3067 target_path: Some("/pkg/foo".to_string()),
3068 rights: Some(fio::Operations::CONNECT),
3069 subdir: None,
3070 ..Default::default()
3071 }),
3072 ]);
3073 decl
3074 },
3075 results = vec![
3076 Err(ErrorList::new(vec![
3077 Error::pkg_path_overlap(DeclType::UseDirectory, "/pkg/foo"),
3078 ])),
3079 ],
3080 },
3081 test_validate_use_optional_config_correct => {
3082 input = {
3083 let mut decl = new_component_decl();
3084 decl.uses = Some(vec![
3085 fdecl::Use::Config(fdecl::UseConfiguration {
3086 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3087 source_name: Some("abc".to_string()),
3088 target_name: Some("foo".to_string()),
3089 availability: Some(fdecl::Availability::Optional),
3090 type_: Some(fdecl::ConfigType {
3091 layout: fdecl::ConfigTypeLayout::Bool,
3092 parameters: Some(Vec::new()),
3093 constraints: Vec::new(),
3094 }),
3095 ..Default::default()
3096 }),
3097 ]);
3098 decl.config = Some(fdecl::ConfigSchema {
3099 fields: Some(vec![fdecl::ConfigField {
3100 key: Some("foo".into()),
3101 type_: Some(fdecl::ConfigType {
3102 layout: fdecl::ConfigTypeLayout::Bool,
3103 parameters: Some(Vec::new()),
3104 constraints: Vec::new(),
3105 }),
3106 mutability: None,
3107 ..Default::default()}]),
3108 checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3109 value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3110 ..Default::default()
3111 });
3112 decl
3113 },
3114 results = vec![Ok(())],
3115 },
3116 test_validate_use_optional_config_no_config_schema => {
3117 input = {
3118 let mut decl = new_component_decl();
3119 decl.uses = Some(vec![
3120 fdecl::Use::Config(fdecl::UseConfiguration {
3121 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3122 source_name: Some("abc".to_string()),
3123 target_name: Some("foo".to_string()),
3124 availability: Some(fdecl::Availability::Optional),
3125 type_: Some(fdecl::ConfigType {
3126 layout: fdecl::ConfigTypeLayout::Bool,
3127 parameters: None,
3128 constraints: Vec::new(),
3129 }),
3130 ..Default::default()
3131 }),
3132 ]);
3133 decl
3134 },
3135 results = vec![
3136 Err(ErrorList::new(vec![
3137 Error::missing_field(DeclType::ConfigField, "config"),
3138 ])),
3139 ],
3140 },
3141 test_validate_use_optional_config_no_config_field => {
3142 input = {
3143 let mut decl = new_component_decl();
3144 decl.uses = Some(vec![
3145 fdecl::Use::Config(fdecl::UseConfiguration {
3146 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3147 source_name: Some("abc".to_string()),
3148 target_name: Some("foo".to_string()),
3149 availability: Some(fdecl::Availability::Optional),
3150 type_: Some(fdecl::ConfigType {
3151 layout: fdecl::ConfigTypeLayout::Bool,
3152 parameters: None,
3153 constraints: Vec::new(),
3154 }),
3155 ..Default::default()
3156 }),
3157 ]);
3158 decl.config = Some(fdecl::ConfigSchema {
3159 fields: Some(vec![]),
3160 checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3161 value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3162 ..Default::default()
3163 });
3164 decl
3165 },
3166 results = vec![
3167 Err(ErrorList::new(vec![
3168 Error::missing_field(DeclType::ConfigField, "foo"),
3169 ])),
3170 ],
3171 },
3172 test_validate_use_optional_config_bad_type => {
3173 input = {
3174 let mut decl = new_component_decl();
3175 decl.uses = Some(vec![
3176 fdecl::Use::Config(fdecl::UseConfiguration {
3177 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3178 source_name: Some("abc".to_string()),
3179 target_name: Some("foo".to_string()),
3180 availability: Some(fdecl::Availability::Optional),
3181 type_: Some(fdecl::ConfigType {
3182 layout: fdecl::ConfigTypeLayout::Bool,
3183 parameters: None,
3184 constraints: Vec::new(),
3185 }),
3186 ..Default::default()
3187 }),
3188 ]);
3189 decl.config = Some(fdecl::ConfigSchema {
3190 fields: Some(vec![fdecl::ConfigField {
3191 key: Some("foo".into()),
3192 type_: Some(fdecl::ConfigType {
3193 layout: fdecl::ConfigTypeLayout::Int16,
3194 parameters: Some(Vec::new()),
3195 constraints: Vec::new(),
3196 }),
3197 mutability: None,
3198 ..Default::default()}]),
3199 checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3200 value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3201 ..Default::default()
3202 });
3203 decl
3204 },
3205 results = vec![
3206 Err(ErrorList::new(vec![
3207 Error::invalid_field(DeclType::ConfigField, "foo"),
3208 ])),
3209 ],
3210 },
3211 }
3212
3213 test_validate_values_data! {
3214 test_values_data_ok => {
3215 input = fdecl::ConfigValuesData {
3216 values: Some(vec![
3217 fdecl::ConfigValueSpec {
3218 value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(true))),
3219 ..Default::default()
3220 }
3221 ]),
3222 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3223 ..Default::default()
3224 },
3225 result = Ok(()),
3226 },
3227 test_values_data_no_checksum => {
3228 input = fdecl::ConfigValuesData {
3229 values: Some(vec![]),
3230 checksum: None,
3231 ..Default::default()
3232 },
3233 result = Err(ErrorList::new(vec![
3234 Error::missing_field(DeclType::ConfigValuesData, "checksum")
3235 ])),
3236 },
3237 test_values_data_unknown_checksum => {
3238 input = fdecl::ConfigValuesData {
3239 values: Some(vec![]),
3240 checksum: Some(fdecl::ConfigChecksum::unknown_variant_for_testing()),
3241 ..Default::default()
3242 },
3243 result = Err(ErrorList::new(vec![
3244 Error::invalid_field(DeclType::ConfigValuesData, "checksum")
3245 ])),
3246 },
3247 test_values_data_no_values => {
3248 input = fdecl::ConfigValuesData {
3249 values: None,
3250 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3251 ..Default::default()
3252 },
3253 result = Err(ErrorList::new(vec![
3254 Error::missing_field(DeclType::ConfigValuesData, "values")
3255 ])),
3256 },
3257 test_values_data_no_inner_value => {
3258 input = fdecl::ConfigValuesData {
3259 values: Some(vec![
3260 fdecl::ConfigValueSpec::default()
3261 ]),
3262 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3263 ..Default::default()
3264 },
3265 result = Err(ErrorList::new(vec![
3266 Error::missing_field(DeclType::ConfigValueSpec, "value")
3267 ])),
3268 },
3269 test_values_data_unknown_inner_value => {
3270 input = fdecl::ConfigValuesData {
3271 values: Some(vec![
3272 fdecl::ConfigValueSpec {
3273 value: Some(fdecl::ConfigValue::unknown_variant_for_testing()),
3274 ..Default::default()
3275 }
3276 ]),
3277 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3278 ..Default::default()
3279 },
3280 result = Err(ErrorList::new(vec![
3281 Error::invalid_field(DeclType::ConfigValueSpec, "value")
3282 ])),
3283 },
3284 test_values_data_unknown_single_value => {
3285 input = fdecl::ConfigValuesData {
3286 values: Some(vec![
3287 fdecl::ConfigValueSpec {
3288 value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::unknown_variant_for_testing())),
3289 ..Default::default()
3290 }
3291 ]),
3292 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3293 ..Default::default()
3294 },
3295 result = Err(ErrorList::new(vec![
3296 Error::invalid_field(DeclType::ConfigValueSpec, "value")
3297 ])),
3298 },
3299 test_values_data_unknown_list_value => {
3300 input = fdecl::ConfigValuesData {
3301 values: Some(vec![
3302 fdecl::ConfigValueSpec {
3303 value: Some(fdecl::ConfigValue::Vector(fdecl::ConfigVectorValue::unknown_variant_for_testing())),
3304 ..Default::default()
3305 }
3306 ]),
3307 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3308 ..Default::default()
3309 },
3310 result = Err(ErrorList::new(vec![
3311 Error::invalid_field(DeclType::ConfigValueSpec, "value")
3312 ])),
3313 },
3314 }
3315
3316 test_validate! {
3317 test_validate_uses_empty => {
3319 input = {
3320 let mut decl = new_component_decl();
3321 decl.program = Some(fdecl::Program {
3322 runner: Some("elf".to_string()),
3323 info: Some(fdata::Dictionary {
3324 entries: None,
3325 ..Default::default()
3326 }),
3327 ..Default::default()
3328 });
3329 decl.uses = Some(vec![
3330 fdecl::Use::Service(fdecl::UseService {
3331 source: None,
3332 source_name: None,
3333 target_path: None,
3334 dependency_type: None,
3335 ..Default::default()
3336 }),
3337 fdecl::Use::Protocol(fdecl::UseProtocol {
3338 dependency_type: None,
3339 source: None,
3340 source_name: None,
3341 target_path: None,
3342 ..Default::default()
3343 }),
3344 fdecl::Use::Directory(fdecl::UseDirectory {
3345 dependency_type: None,
3346 source: None,
3347 source_name: None,
3348 target_path: None,
3349 rights: None,
3350 subdir: None,
3351 ..Default::default()
3352 }),
3353 fdecl::Use::Storage(fdecl::UseStorage {
3354 source_name: None,
3355 target_path: None,
3356 ..Default::default()
3357 }),
3358 fdecl::Use::EventStream(fdecl::UseEventStream {
3359 source_name: None,
3360 source: None,
3361 target_path: None,
3362 ..Default::default()
3363 }),
3364 fdecl::Use::Runner(fdecl::UseRunner {
3365 source_name: None,
3366 source: None,
3367 ..Default::default()
3368 }),
3369 ]);
3370 decl
3371 },
3372 result = Err(ErrorList::new(vec![
3373 Error::missing_field(DeclType::UseService, "source"),
3374 Error::missing_field(DeclType::UseService, "source_name"),
3375 Error::missing_field(DeclType::UseService, "target_path"),
3376 Error::missing_field(DeclType::UseService, "dependency_type"),
3377 Error::missing_field(DeclType::UseProtocol, "source"),
3378 Error::missing_field(DeclType::UseProtocol, "source_name"),
3379 Error::missing_field(DeclType::UseProtocol, "target_path"),
3380 Error::missing_field(DeclType::UseProtocol, "dependency_type"),
3381 Error::missing_field(DeclType::UseDirectory, "source"),
3382 Error::missing_field(DeclType::UseDirectory, "source_name"),
3383 Error::missing_field(DeclType::UseDirectory, "target_path"),
3384 Error::missing_field(DeclType::UseDirectory, "dependency_type"),
3385 Error::missing_field(DeclType::UseDirectory, "rights"),
3386 Error::missing_field(DeclType::UseStorage, "source_name"),
3387 Error::missing_field(DeclType::UseStorage, "target_path"),
3388 Error::missing_field(DeclType::UseEventStream, "source"),
3389 Error::missing_field(DeclType::UseEventStream, "source_name"),
3390 Error::missing_field(DeclType::UseEventStream, "target_path"),
3391 Error::missing_field(DeclType::UseRunner, "source"),
3392 Error::missing_field(DeclType::UseRunner, "source_name"),
3393 ])),
3394 },
3395 test_validate_missing_program_info => {
3396 input = {
3397 let mut decl = new_component_decl();
3398 decl.program = Some(fdecl::Program {
3399 runner: Some("runner".to_string()),
3400 info: None,
3401 ..Default::default()
3402 });
3403 decl
3404 },
3405 result = Err(ErrorList::new(vec![
3406 Error::missing_field(DeclType::Program, "info")
3407 ])),
3408 },
3409 test_validate_uses_invalid_identifiers => {
3410 input = {
3411 let mut decl = new_component_decl();
3412 decl.uses = Some(vec![
3413 fdecl::Use::Service(fdecl::UseService {
3414 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
3415 name: "^bad".to_string(),
3416 })),
3417 source_name: Some("foo/".to_string()),
3418 target_path: Some("a/foo".to_string()),
3419 dependency_type: Some(fdecl::DependencyType::Strong),
3420 ..Default::default()
3421 }),
3422 fdecl::Use::Protocol(fdecl::UseProtocol {
3423 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3424 name: "^bad".to_string(),
3425 collection: None,
3426 })),
3427 source_name: Some("foo/".to_string()),
3428 target_path: Some("b/foo".to_string()),
3429 dependency_type: Some(fdecl::DependencyType::Strong),
3430 ..Default::default()
3431 }),
3432 fdecl::Use::Directory(fdecl::UseDirectory {
3433 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3434 name: "^bad".to_string(),
3435 collection: None,
3436 })),
3437 source_name: Some("foo/".to_string()),
3438 target_path: Some("c".to_string()),
3439 rights: Some(fio::Operations::CONNECT),
3440 subdir: Some("/foo".to_string()),
3441 dependency_type: Some(fdecl::DependencyType::Strong),
3442 ..Default::default()
3443 }),
3444 fdecl::Use::Storage(fdecl::UseStorage {
3445 source_name: Some("foo/".to_string()),
3446 target_path: Some("d".to_string()),
3447 ..Default::default()
3448 }),
3449 fdecl::Use::EventStream(fdecl::UseEventStream {
3450 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3451 name: "^bad".to_string(),
3452 collection: None,
3453 })),
3454 source_name: Some("foo/".to_string()),
3455 target_path: Some("e".to_string()),
3456 ..Default::default()
3457 }),
3458 fdecl::Use::Runner(fdecl::UseRunner {
3459 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3460 name: "^bad".to_string(),
3461 collection: None,
3462 })),
3463 source_name: Some("foo/".to_string()),
3464 ..Default::default()
3465 }),
3466 ]);
3467 decl
3468 },
3469 result = Err(ErrorList::new(vec![
3470 Error::invalid_field(DeclType::UseService, "source.capability.name"),
3471 Error::invalid_field(DeclType::UseService, "source_name"),
3472 Error::invalid_field(DeclType::UseService, "target_path"),
3473 Error::invalid_field(DeclType::UseProtocol, "source.child.name"),
3474 Error::invalid_field(DeclType::UseProtocol, "source_name"),
3475 Error::invalid_field(DeclType::UseProtocol, "target_path"),
3476 Error::invalid_field(DeclType::UseDirectory, "source.child.name"),
3477 Error::invalid_field(DeclType::UseDirectory, "source_name"),
3478 Error::invalid_field(DeclType::UseDirectory, "target_path"),
3479 Error::invalid_field(DeclType::UseDirectory, "subdir"),
3480 Error::invalid_field(DeclType::UseStorage, "source_name"),
3481 Error::invalid_field(DeclType::UseStorage, "target_path"),
3482 Error::invalid_field(DeclType::UseEventStream, "source.child.name"),
3483 Error::invalid_field(DeclType::UseEventStream, "source_name"),
3484 Error::invalid_field(DeclType::UseEventStream, "target_path"),
3485 Error::invalid_field(DeclType::UseRunner, "source.child.name"),
3486 Error::invalid_field(DeclType::UseRunner, "source_name"),
3487 ])),
3488 },
3489 test_validate_uses_missing_source => {
3490 input = {
3491 fdecl::Component {
3492 uses: Some(vec![
3493 fdecl::Use::Protocol(fdecl::UseProtocol {
3494 dependency_type: Some(fdecl::DependencyType::Strong),
3495 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
3496 name: "this-storage-doesnt-exist".to_string(),
3497 })),
3498 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3499 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3500 ..Default::default()
3501 })
3502 ]),
3503 ..new_component_decl()
3504 }
3505 },
3506 result = Err(ErrorList::new(vec![
3507 Error::invalid_capability(DeclType::UseProtocol, "source", "this-storage-doesnt-exist"),
3508 ])),
3509 },
3510 test_validate_uses_invalid_child => {
3511 input = {
3512 fdecl::Component {
3513 uses: Some(vec![
3514 fdecl::Use::Protocol(fdecl::UseProtocol {
3515 dependency_type: Some(fdecl::DependencyType::Strong),
3516 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3517 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3518 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3519 ..Default::default()
3520 }),
3521 fdecl::Use::Service(fdecl::UseService {
3522 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3523 source_name: Some("service_name".to_string()),
3524 target_path: Some("/svc/service_name".to_string()),
3525 dependency_type: Some(fdecl::DependencyType::Strong),
3526 ..Default::default()
3527 }),
3528 fdecl::Use::Directory(fdecl::UseDirectory {
3529 dependency_type: Some(fdecl::DependencyType::Strong),
3530 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3531 source_name: Some("DirectoryName".to_string()),
3532 target_path: Some("/data/DirectoryName".to_string()),
3533 rights: Some(fio::Operations::CONNECT),
3534 subdir: None,
3535 ..Default::default()
3536 }),
3537 fdecl::Use::Runner(fdecl::UseRunner {
3538 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3539 source_name: Some("RunnerName".to_string()),
3540 ..Default::default()
3541 }),
3542 ]),
3543 ..new_component_decl()
3544 }
3545 },
3546 result = Err(ErrorList::new(vec![
3547 Error::invalid_child(DeclType::UseProtocol, "source", "no-such-child"),
3548 Error::invalid_child(DeclType::UseService, "source", "no-such-child"),
3549 Error::invalid_child(DeclType::UseDirectory, "source", "no-such-child"),
3550 Error::invalid_child(DeclType::UseRunner, "source", "no-such-child"),
3551 ])),
3552 },
3553 test_validate_uses_invalid_capability_from_self => {
3554 input = {
3555 let mut decl = new_component_decl();
3556 decl.uses = Some(vec![
3557 fdecl::Use::Service(fdecl::UseService {
3558 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3559 source_name: Some("fuchsia.some.library.SomeService".into()),
3560 target_path: Some("/svc/foo".into()),
3561 dependency_type: Some(fdecl::DependencyType::Strong),
3562 ..Default::default()
3563 }),
3564 fdecl::Use::Protocol(fdecl::UseProtocol {
3565 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3566 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
3567 target_path: Some("/svc/bar".into()),
3568 dependency_type: Some(fdecl::DependencyType::Strong),
3569 ..Default::default()
3570 }),
3571 fdecl::Use::Directory(fdecl::UseDirectory {
3572 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3573 source_name: Some("dir".into()),
3574 target_path: Some("/assets".into()),
3575 dependency_type: Some(fdecl::DependencyType::Strong),
3576 rights: Some(fio::Operations::CONNECT),
3577 ..Default::default()
3578 }),
3579 fdecl::Use::Runner(fdecl::UseRunner {
3580 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3581 source_name: Some("source_elf".into()),
3582 ..Default::default()
3583 }),
3584 fdecl::Use::Config(fdecl::UseConfiguration {
3585 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3586 source_name: Some("source_config".into()),
3587 target_name: Some("config".into()),
3588 type_: Some(fdecl::ConfigType {
3589 layout: fdecl::ConfigTypeLayout::Bool,
3590 parameters: Some(Vec::new()),
3591 constraints: Vec::new(),
3592 }),
3593 ..Default::default()
3594 }),
3595 fdecl::Use::Protocol(fdecl::UseProtocol {
3596 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3597 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
3598 source_dictionary: Some("dict/inner".into()),
3599 target_path: Some("/svc/baz".into()),
3600 dependency_type: Some(fdecl::DependencyType::Strong),
3601 ..Default::default()
3602 }),
3603 ]);
3604 decl
3605 },
3606 result = Err(ErrorList::new(vec![
3607 Error::invalid_capability(
3608 DeclType::UseService,
3609 "source",
3610 "fuchsia.some.library.SomeService"),
3611 Error::invalid_capability(
3612 DeclType::UseProtocol,
3613 "source",
3614 "fuchsia.some.library.SomeProtocol"),
3615 Error::invalid_capability(DeclType::UseDirectory, "source", "dir"),
3616 Error::invalid_capability(DeclType::UseRunner, "source", "source_elf"),
3617 Error::invalid_capability(DeclType::UseConfiguration, "source", "source_config"),
3618 Error::invalid_capability(DeclType::UseProtocol, "source", "dict"),
3619 ])),
3620 },
3621 test_validate_use_from_child_offer_to_child_strong_cycle => {
3622 input = {
3623 fdecl::Component {
3624 capabilities: Some(vec![
3625 fdecl::Capability::Service(fdecl::Service {
3626 name: Some("a".to_string()),
3627 source_path: Some("/a".to_string()),
3628 ..Default::default()
3629 })]),
3630 uses: Some(vec![
3631 fdecl::Use::Protocol(fdecl::UseProtocol {
3632 dependency_type: Some(fdecl::DependencyType::Strong),
3633 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3634 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3635 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3636 ..Default::default()
3637 }),
3638 fdecl::Use::Service(fdecl::UseService {
3639 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3640 source_name: Some("service_name".to_string()),
3641 target_path: Some("/svc/service_name".to_string()),
3642 dependency_type: Some(fdecl::DependencyType::Strong),
3643 ..Default::default()
3644 }),
3645 fdecl::Use::Directory(fdecl::UseDirectory {
3646 dependency_type: Some(fdecl::DependencyType::Strong),
3647 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3648 source_name: Some("DirectoryName".to_string()),
3649 target_path: Some("/data/DirectoryName".to_string()),
3650 rights: Some(fio::Operations::CONNECT),
3651 subdir: None,
3652 ..Default::default()
3653 }),
3654 ]),
3655 offers: Some(vec![
3656 fdecl::Offer::Service(fdecl::OfferService {
3657 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3658 source_name: Some("a".to_string()),
3659 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
3660 target_name: Some("a".to_string()),
3661 ..Default::default()
3662 })
3663 ]),
3664 children: Some(vec![
3665 fdecl::Child {
3666 name: Some("child".to_string()),
3667 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3668 startup: Some(fdecl::StartupMode::Lazy),
3669 on_terminate: None,
3670 ..Default::default()
3671 }
3672 ]),
3673 ..new_component_decl()
3674 }
3675 },
3676 result = Err(ErrorList::new(vec![
3677 Error::dependency_cycle("{{self -> child child -> self}}".to_string()),
3678 ])),
3679 },
3680 test_validate_use_from_child_storage_no_cycle => {
3681 input = {
3682 fdecl::Component {
3683 capabilities: Some(vec![
3684 fdecl::Capability::Storage(fdecl::Storage {
3685 name: Some("cdata".to_string()),
3686 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None } )),
3687 backing_dir: Some("minfs".to_string()),
3688 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
3689 ..Default::default()
3690 }),
3691 fdecl::Capability::Storage(fdecl::Storage {
3692 name: Some("pdata".to_string()),
3693 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
3694 backing_dir: Some("minfs".to_string()),
3695 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
3696 ..Default::default()
3697 }),
3698 ]),
3699 uses: Some(vec![
3700 fdecl::Use::Protocol(fdecl::UseProtocol {
3701 dependency_type: Some(fdecl::DependencyType::Strong),
3702 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child1".to_string(), collection: None})),
3703 source_name: Some("a".to_string()),
3704 target_path: Some("/svc/a".to_string()),
3705 ..Default::default()
3706 }),
3707 ]),
3708 offers: Some(vec![
3709 fdecl::Offer::Storage(fdecl::OfferStorage {
3710 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3711 source_name: Some("cdata".to_string()),
3712 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3713 target_name: Some("cdata".to_string()),
3714 ..Default::default()
3715 }),
3716 fdecl::Offer::Storage(fdecl::OfferStorage {
3717 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3718 source_name: Some("pdata".to_string()),
3719 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3720 target_name: Some("pdata".to_string()),
3721 ..Default::default()
3722 }),
3723 ]),
3724 children: Some(vec![
3725 fdecl::Child {
3726 name: Some("child1".to_string()),
3727 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3728 startup: Some(fdecl::StartupMode::Lazy),
3729 on_terminate: None,
3730 ..Default::default()
3731 },
3732 fdecl::Child {
3733 name: Some("child2".to_string()),
3734 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
3735 startup: Some(fdecl::StartupMode::Lazy),
3736 on_terminate: None,
3737 ..Default::default()
3738 }
3739 ]),
3740 ..new_component_decl()
3741 }
3742 },
3743 result = Ok(()),
3744 },
3745 test_validate_use_from_child_storage_cycle => {
3746 input = {
3747 fdecl::Component {
3748 capabilities: Some(vec![
3749 fdecl::Capability::Storage(fdecl::Storage {
3750 name: Some("data".to_string()),
3751 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
3752 backing_dir: Some("minfs".to_string()),
3753 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
3754 ..Default::default()
3755 }),
3756 ]),
3757 uses: Some(vec![
3758 fdecl::Use::Protocol(fdecl::UseProtocol {
3759 dependency_type: Some(fdecl::DependencyType::Strong),
3760 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3761 source_name: Some("a".to_string()),
3762 target_path: Some("/svc/a".to_string()),
3763 ..Default::default()
3764 }),
3765 ]),
3766 offers: Some(vec![
3767 fdecl::Offer::Storage(fdecl::OfferStorage {
3768 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3769 source_name: Some("data".to_string()),
3770 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
3771 target_name: Some("data".to_string()),
3772 ..Default::default()
3773 }),
3774 ]),
3775 children: Some(vec![
3776 fdecl::Child {
3777 name: Some("child".to_string()),
3778 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3779 startup: Some(fdecl::StartupMode::Lazy),
3780 on_terminate: None,
3781 ..Default::default()
3782 },
3783 ]),
3784 ..new_component_decl()
3785 }
3786 },
3787 result = Err(ErrorList::new(vec![
3788 Error::dependency_cycle("{{self -> capability data -> child child -> self}}".to_string()),
3789 ])),
3790 },
3791 test_validate_storage_strong_cycle_between_children => {
3792 input = {
3793 fdecl::Component {
3794 capabilities: Some(vec![
3795 fdecl::Capability::Storage(fdecl::Storage {
3796 name: Some("data".to_string()),
3797 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None } )),
3798 backing_dir: Some("minfs".to_string()),
3799 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
3800 ..Default::default()
3801 })
3802 ]),
3803 offers: Some(vec![
3804 fdecl::Offer::Storage(fdecl::OfferStorage {
3805 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3806 source_name: Some("data".to_string()),
3807 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
3808 target_name: Some("data".to_string()),
3809 ..Default::default()
3810 }),
3811 fdecl::Offer::Service(fdecl::OfferService {
3812 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
3813 source_name: Some("a".to_string()),
3814 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3815 target_name: Some("a".to_string()),
3816 ..Default::default()
3817 }),
3818 ]),
3819 children: Some(vec![
3820 fdecl::Child {
3821 name: Some("child1".to_string()),
3822 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3823 startup: Some(fdecl::StartupMode::Lazy),
3824 on_terminate: None,
3825 ..Default::default()
3826 },
3827 fdecl::Child {
3828 name: Some("child2".to_string()),
3829 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
3830 startup: Some(fdecl::StartupMode::Lazy),
3831 on_terminate: None,
3832 ..Default::default()
3833 }
3834 ]),
3835 ..new_component_decl()
3836 }
3837 },
3838 result = Err(ErrorList::new(vec![
3839 Error::dependency_cycle("{{child child1 -> capability data -> child child2 -> child child1}}".to_string()),
3840 ])),
3841 },
3842 test_validate_strong_cycle_between_children_through_environment_debug => {
3843 input = {
3844 fdecl::Component {
3845 environments: Some(vec![
3846 fdecl::Environment {
3847 name: Some("env".to_string()),
3848 extends: Some(fdecl::EnvironmentExtends::Realm),
3849 debug_capabilities: Some(vec![
3850 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
3851 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3852 source_name: Some("fuchsia.foo.Bar".to_string()),
3853 target_name: Some("fuchsia.foo.Bar".to_string()),
3854 ..Default::default()
3855 }),
3856 ]),
3857 ..Default::default()
3858 },
3859 ]),
3860 offers: Some(vec![
3861 fdecl::Offer::Service(fdecl::OfferService {
3862 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
3863 source_name: Some("a".to_string()),
3864 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3865 target_name: Some("a".to_string()),
3866 ..Default::default()
3867 }),
3868 ]),
3869 children: Some(vec![
3870 fdecl::Child {
3871 name: Some("child1".to_string()),
3872 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3873 startup: Some(fdecl::StartupMode::Lazy),
3874 on_terminate: None,
3875 ..Default::default()
3876 },
3877 fdecl::Child {
3878 name: Some("child2".to_string()),
3879 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
3880 startup: Some(fdecl::StartupMode::Lazy),
3881 environment: Some("env".to_string()),
3882 on_terminate: None,
3883 ..Default::default()
3884 }
3885 ]),
3886 ..new_component_decl()
3887 }
3888 },
3889 result = Err(ErrorList::new(vec![
3890 Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}".to_string()),
3891 ])),
3892 },
3893 test_validate_strong_cycle_between_children_through_environment_runner => {
3894 input = {
3895 fdecl::Component {
3896 environments: Some(vec![
3897 fdecl::Environment {
3898 name: Some("env".to_string()),
3899 extends: Some(fdecl::EnvironmentExtends::Realm),
3900 runners: Some(vec![
3901 fdecl::RunnerRegistration {
3902 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3903 source_name: Some("coff".to_string()),
3904 target_name: Some("coff".to_string()),
3905 ..Default::default()
3906 }
3907 ]),
3908 ..Default::default()
3909 },
3910 ]),
3911 offers: Some(vec![
3912 fdecl::Offer::Service(fdecl::OfferService {
3913 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
3914 source_name: Some("a".to_string()),
3915 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3916 target_name: Some("a".to_string()),
3917 ..Default::default()
3918 }),
3919 ]),
3920 children: Some(vec![
3921 fdecl::Child {
3922 name: Some("child1".to_string()),
3923 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3924 startup: Some(fdecl::StartupMode::Lazy),
3925 on_terminate: None,
3926 ..Default::default()
3927 },
3928 fdecl::Child {
3929 name: Some("child2".to_string()),
3930 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
3931 startup: Some(fdecl::StartupMode::Lazy),
3932 environment: Some("env".to_string()),
3933 on_terminate: None,
3934 ..Default::default()
3935 }
3936 ]),
3937 ..new_component_decl()
3938 }
3939 },
3940 result = Err(ErrorList::new(vec![
3941 Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}".to_string()),
3942 ])),
3943 },
3944 test_validate_strong_cycle_between_children_through_environment_resolver => {
3945 input = {
3946 fdecl::Component {
3947 environments: Some(vec![
3948 fdecl::Environment {
3949 name: Some("env".to_string()),
3950 extends: Some(fdecl::EnvironmentExtends::Realm),
3951 resolvers: Some(vec![
3952 fdecl::ResolverRegistration {
3953 resolver: Some("gopher".to_string()),
3954 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3955 scheme: Some("gopher".to_string()),
3956 ..Default::default()
3957 }
3958 ]),
3959 ..Default::default()
3960 },
3961 ]),
3962 offers: Some(vec![
3963 fdecl::Offer::Service(fdecl::OfferService {
3964 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
3965 source_name: Some("a".to_string()),
3966 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3967 target_name: Some("a".to_string()),
3968 ..Default::default()
3969 }),
3970 ]),
3971 children: Some(vec![
3972 fdecl::Child {
3973 name: Some("child1".to_string()),
3974 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3975 startup: Some(fdecl::StartupMode::Lazy),
3976 on_terminate: None,
3977 ..Default::default()
3978 },
3979 fdecl::Child {
3980 name: Some("child2".to_string()),
3981 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
3982 startup: Some(fdecl::StartupMode::Lazy),
3983 environment: Some("env".to_string()),
3984 on_terminate: None,
3985 ..Default::default()
3986 }
3987 ]),
3988 ..new_component_decl()
3989 }
3990 },
3991 result = Err(ErrorList::new(vec![
3992 Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}".to_string()),
3993 ])),
3994 },
3995 test_validate_strong_cycle_between_self_and_two_children => {
3996 input = {
3997 fdecl::Component {
3998 capabilities: Some(vec![
3999 fdecl::Capability::Protocol(fdecl::Protocol {
4000 name: Some("fuchsia.foo.Bar".to_string()),
4001 source_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4002 ..Default::default()
4003 })
4004 ]),
4005 offers: Some(vec![
4006 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4007 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4008 source_name: Some("fuchsia.foo.Bar".to_string()),
4009 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4010 target_name: Some("fuchsia.foo.Bar".to_string()),
4011 dependency_type: Some(fdecl::DependencyType::Strong),
4012 ..Default::default()
4013 }),
4014 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4015 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4016 source_name: Some("fuchsia.bar.Baz".to_string()),
4017 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4018 target_name: Some("fuchsia.bar.Baz".to_string()),
4019 dependency_type: Some(fdecl::DependencyType::Strong),
4020 ..Default::default()
4021 }),
4022 ]),
4023 uses: Some(vec![
4024 fdecl::Use::Protocol(fdecl::UseProtocol {
4025 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child2".to_string(), collection: None})),
4026 source_name: Some("fuchsia.baz.Foo".to_string()),
4027 target_path: Some("/svc/fuchsia.baz.Foo".to_string()),
4028 dependency_type: Some(fdecl::DependencyType::Strong),
4029 ..Default::default()
4030 }),
4031 ]),
4032 children: Some(vec![
4033 fdecl::Child {
4034 name: Some("child1".to_string()),
4035 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4036 startup: Some(fdecl::StartupMode::Lazy),
4037 on_terminate: None,
4038 ..Default::default()
4039 },
4040 fdecl::Child {
4041 name: Some("child2".to_string()),
4042 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4043 startup: Some(fdecl::StartupMode::Lazy),
4044 on_terminate: None,
4045 ..Default::default()
4046 }
4047 ]),
4048 ..new_component_decl()
4049 }
4050 },
4051 result = Err(ErrorList::new(vec![
4052 Error::dependency_cycle("{{self -> child child1 -> child child2 -> self}}".to_string()),
4053 ])),
4054 },
4055 test_validate_strong_cycle_with_self_storage => {
4056 input = {
4057 fdecl::Component {
4058 capabilities: Some(vec![
4059 fdecl::Capability::Storage(fdecl::Storage {
4060 name: Some("data".to_string()),
4061 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4062 backing_dir: Some("minfs".to_string()),
4063 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4064 ..Default::default()
4065 }),
4066 fdecl::Capability::Directory(fdecl::Directory {
4067 name: Some("minfs".to_string()),
4068 source_path: Some("/minfs".to_string()),
4069 rights: Some(fio::RW_STAR_DIR),
4070 ..Default::default()
4071 }),
4072 ]),
4073 offers: Some(vec![
4074 fdecl::Offer::Storage(fdecl::OfferStorage {
4075 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4076 source_name: Some("data".to_string()),
4077 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4078 target_name: Some("data".to_string()),
4079 ..Default::default()
4080 }),
4081 ]),
4082 uses: Some(vec![
4083 fdecl::Use::Protocol(fdecl::UseProtocol {
4084 dependency_type: Some(fdecl::DependencyType::Strong),
4085 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4086 source_name: Some("fuchsia.foo.Bar".to_string()),
4087 target_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4088 ..Default::default()
4089 }),
4090 ]),
4091 children: Some(vec![
4092 fdecl::Child {
4093 name: Some("child".to_string()),
4094 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4095 startup: Some(fdecl::StartupMode::Lazy),
4096 ..Default::default()
4097 },
4098 ]),
4099 ..new_component_decl()
4100 }
4101 },
4102 result = Err(ErrorList::new(vec![
4103 Error::dependency_cycle("{{self -> capability data -> child child -> self}}".to_string()),
4104 ])),
4105 },
4106 test_validate_strong_cycle_with_self_storage_admin_protocol => {
4107 input = {
4108 fdecl::Component {
4109 capabilities: Some(vec![
4110 fdecl::Capability::Storage(fdecl::Storage {
4111 name: Some("data".to_string()),
4112 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4113 backing_dir: Some("minfs".to_string()),
4114 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4115 ..Default::default()
4116 }),
4117 fdecl::Capability::Directory(fdecl::Directory {
4118 name: Some("minfs".to_string()),
4119 source_path: Some("/minfs".to_string()),
4120 rights: Some(fio::RW_STAR_DIR),
4121 ..Default::default()
4122 }),
4123 ]),
4124 offers: Some(vec![
4125 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4126 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef { name: "data".to_string() })),
4127 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4128 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4129 target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4130 dependency_type: Some(fdecl::DependencyType::Strong),
4131 ..Default::default()
4132 }),
4133 ]),
4134 uses: Some(vec![
4135 fdecl::Use::Protocol(fdecl::UseProtocol {
4136 dependency_type: Some(fdecl::DependencyType::Strong),
4137 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4138 source_name: Some("fuchsia.foo.Bar".to_string()),
4139 target_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4140 ..Default::default()
4141 }),
4142 ]),
4143 children: Some(vec![
4144 fdecl::Child {
4145 name: Some("child".to_string()),
4146 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4147 startup: Some(fdecl::StartupMode::Lazy),
4148 ..Default::default()
4149 },
4150 ]),
4151 ..new_component_decl()
4152 }
4153 },
4154 result = Err(ErrorList::new(vec![
4155 Error::dependency_cycle("{{self -> capability data -> child child -> self}}".to_string()),
4156 ])),
4157 },
4158 test_validate_strong_cycle_with_dictionary => {
4159 input = fdecl::Component {
4160 offers: Some(vec![
4161 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
4162 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4163 source_name: Some("dict".into()),
4164 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4165 name: "a".into(),
4166 collection: None,
4167 })),
4168 target_name: Some("dict".into()),
4169 dependency_type: Some(fdecl::DependencyType::Strong),
4170 ..Default::default()
4171 }),
4172 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4173 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4174 name: "b".into(),
4175 collection: None,
4176 })),
4177 source_name: Some("1".into()),
4178 target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4179 name: "dict".into(),
4180 })),
4181 target_name: Some("1".into()),
4182 dependency_type: Some(fdecl::DependencyType::Strong),
4183 ..Default::default()
4184 }),
4185 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4186 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4187 name: "a".into(),
4188 collection: None,
4189 })),
4190 source_name: Some("2".into()),
4191 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4192 name: "b".into(),
4193 collection: None,
4194 })),
4195 target_name: Some("2".into()),
4196 dependency_type: Some(fdecl::DependencyType::Strong),
4197 ..Default::default()
4198 }),
4199 ]),
4200 children: Some(vec![
4201 fdecl::Child {
4202 name: Some("a".into()),
4203 url: Some("fuchsia-pkg://child".into()),
4204 startup: Some(fdecl::StartupMode::Lazy),
4205 ..Default::default()
4206 },
4207 fdecl::Child {
4208 name: Some("b".into()),
4209 url: Some("fuchsia-pkg://child".into()),
4210 startup: Some(fdecl::StartupMode::Lazy),
4211 ..Default::default()
4212 },
4213 ]),
4214 capabilities: Some(vec![
4215 fdecl::Capability::Dictionary(fdecl::Dictionary {
4216 name: Some("dict".into()),
4217 ..Default::default()
4218 }),
4219 ]),
4220 ..Default::default()
4221 },
4222 result = Err(ErrorList::new(vec![
4223 Error::dependency_cycle("{{child a -> child b -> capability dict -> child a}}".to_string()),
4224 ])),
4225 },
4226 test_validate_strong_cycle_with_dictionary_indirect => {
4227 input = fdecl::Component {
4228 offers: Some(vec![
4229 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4230 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4231 source_name: Some("3".into()),
4232 source_dictionary: Some("dict".into()),
4233 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4234 name: "a".into(),
4235 collection: None,
4236 })),
4237 target_name: Some("3".into()),
4238 dependency_type: Some(fdecl::DependencyType::Strong),
4239 ..Default::default()
4240 }),
4241 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4242 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4243 name: "b".into(),
4244 collection: None,
4245 })),
4246 source_name: Some("1".into()),
4247 target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4248 name: "dict".into(),
4249 })),
4250 target_name: Some("1".into()),
4251 dependency_type: Some(fdecl::DependencyType::Strong),
4252 ..Default::default()
4253 }),
4254 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4255 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4256 name: "a".into(),
4257 collection: None,
4258 })),
4259 source_name: Some("2".into()),
4260 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4261 name: "b".into(),
4262 collection: None,
4263 })),
4264 target_name: Some("2".into()),
4265 dependency_type: Some(fdecl::DependencyType::Strong),
4266 ..Default::default()
4267 }),
4268 ]),
4269 children: Some(vec![
4270 fdecl::Child {
4271 name: Some("a".into()),
4272 url: Some("fuchsia-pkg://child".into()),
4273 startup: Some(fdecl::StartupMode::Lazy),
4274 ..Default::default()
4275 },
4276 fdecl::Child {
4277 name: Some("b".into()),
4278 url: Some("fuchsia-pkg://child".into()),
4279 startup: Some(fdecl::StartupMode::Lazy),
4280 ..Default::default()
4281 },
4282 ]),
4283 capabilities: Some(vec![
4284 fdecl::Capability::Dictionary(fdecl::Dictionary {
4285 name: Some("dict".into()),
4286 ..Default::default()
4287 }),
4288 ]),
4289 ..Default::default()
4290 },
4291 result = Err(ErrorList::new(vec![
4292 Error::dependency_cycle("{{child a -> child b -> capability dict -> child a}}".to_string()),
4293 ])),
4294 },
4295 test_validate_use_from_child_offer_to_child_weak_cycle => {
4296 input = {
4297 fdecl::Component {
4298 capabilities: Some(vec![
4299 fdecl::Capability::Service(fdecl::Service {
4300 name: Some("a".to_string()),
4301 source_path: Some("/a".to_string()),
4302 ..Default::default()
4303 })]),
4304 uses: Some(vec![
4305 fdecl::Use::Protocol(fdecl::UseProtocol {
4306 dependency_type: Some(fdecl::DependencyType::Weak),
4307 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4308 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4309 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
4310 ..Default::default()
4311 }),
4312 fdecl::Use::Service(fdecl::UseService {
4313 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4314 source_name: Some("service_name".to_string()),
4315 target_path: Some("/svc/service_name".to_string()),
4316 dependency_type: Some(fdecl::DependencyType::Weak),
4317 ..Default::default()
4318 }),
4319 fdecl::Use::Directory(fdecl::UseDirectory {
4320 dependency_type: Some(fdecl::DependencyType::Weak),
4321 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4322 source_name: Some("DirectoryName".to_string()),
4323 target_path: Some("/data/DirectoryName".to_string()),
4324 rights: Some(fio::Operations::CONNECT),
4325 subdir: None,
4326 ..Default::default()
4327 }),
4328 ]),
4329 offers: Some(vec![
4330 fdecl::Offer::Service(fdecl::OfferService {
4331 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4332 source_name: Some("a".to_string()),
4333 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4334 target_name: Some("a".to_string()),
4335 ..Default::default()
4336 })
4337 ]),
4338 children: Some(vec![
4339 fdecl::Child {
4340 name: Some("child".to_string()),
4341 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4342 startup: Some(fdecl::StartupMode::Lazy),
4343 on_terminate: None,
4344 ..Default::default()
4345 }
4346 ]),
4347 ..new_component_decl()
4348 }
4349 },
4350 result = Ok(()),
4351 },
4352 test_validate_expose_from_self_to_framework_and_parent => {
4353 input = {
4354 fdecl::Component {
4355 capabilities: Some(vec![
4356 fdecl::Capability::Protocol(fdecl::Protocol {
4357 name: Some("a".to_string()),
4358 source_path: Some("/a".to_string()),
4359 ..Default::default()
4360 }),
4361 ]),
4362 exposes: Some(vec![
4363 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
4364 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4365 source_name: Some("a".to_string()),
4366 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4367 target_name: Some("a".to_string()),
4368 ..Default::default()
4369 }),
4370 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
4371 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4372 source_name: Some("a".to_string()),
4373 target: Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})),
4374 target_name: Some("a".to_string()),
4375 ..Default::default()
4376 }),
4377 ]),
4378 ..new_component_decl()
4379 }
4380 },
4381 result = Ok(()),
4382 },
4383 test_validate_use_from_not_child_weak => {
4384 input = {
4385 fdecl::Component {
4386 uses: Some(vec![
4387 fdecl::Use::Protocol(fdecl::UseProtocol {
4388 dependency_type: Some(fdecl::DependencyType::Weak),
4389 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4390 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4391 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
4392 ..Default::default()
4393 }),
4394 ]),
4395 ..new_component_decl()
4396 }
4397 },
4398 result = Err(ErrorList::new(vec![
4399 Error::invalid_field(DeclType::UseProtocol, "dependency_type"),
4400 ])),
4401 },
4402 test_validate_event_stream_offer_valid_decls => {
4403 input = {
4404 let mut decl = new_component_decl();
4405 decl.offers = Some(vec![
4406 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4407 source_name: Some("stopped".to_string()),
4408 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4409 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4410 target_name: Some("stopped".to_string()),
4411 ..Default::default()
4412 }),
4413 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4414 source_name: Some("started".to_string()),
4415 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4416 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4417 target_name: Some("started".to_string()),
4418 ..Default::default()
4419 }),
4420 ]);
4421 decl.children = Some(vec![fdecl::Child{
4422 name: Some("test".to_string()),
4423 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4424 startup: Some(fdecl::StartupMode::Lazy),
4425 on_terminate: None,
4426 environment: None,
4427 ..Default::default()
4428 },
4429 fdecl::Child{
4430 name: Some("test2".to_string()),
4431 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4432 startup: Some(fdecl::StartupMode::Lazy),
4433 on_terminate: None,
4434 environment: None,
4435 ..Default::default()
4436 }
4437 ]);
4438 decl
4439 },
4440 result = Ok(()),
4441 },
4442 test_validate_event_stream_offer_to_framework_invalid => {
4443 input = {
4444 let mut decl = new_component_decl();
4445 decl.offers = Some(vec![
4446 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4447 source_name: Some("stopped".to_string()),
4448 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4449 target: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
4450 target_name: Some("stopped".to_string()),
4451 ..Default::default()
4452 }),
4453 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4454 source_name: Some("started".to_string()),
4455 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4456 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4457 target_name: Some("started".to_string()),
4458 ..Default::default()
4459 }),
4460 ]);
4461 decl.children = Some(vec![fdecl::Child{
4462 name: Some("test".to_string()),
4463 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4464 startup: Some(fdecl::StartupMode::Lazy),
4465 on_terminate: None,
4466 environment: None,
4467 ..Default::default()
4468 },
4469 fdecl::Child{
4470 name: Some("test2".to_string()),
4471 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4472 startup: Some(fdecl::StartupMode::Lazy),
4473 on_terminate: None,
4474 environment: None,
4475 ..Default::default()
4476 }
4477 ]);
4478 decl
4479 },
4480 result = Err(ErrorList::new(vec![
4481 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "target".to_string() }),
4482 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "target".to_string() }),
4483 ])),
4484 },
4485 test_validate_event_stream_offer_to_scope_zero_length_invalid => {
4486 input = {
4487 let mut decl = new_component_decl();
4488 decl.offers = Some(vec![
4489 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4490 source_name: Some("started".to_string()),
4491 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4492 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4493 scope: Some(vec![]),
4494 target_name: Some("started".to_string()),
4495 ..Default::default()
4496 }),
4497 ]);
4498 decl.children = Some(vec![fdecl::Child{
4499 name: Some("test".to_string()),
4500 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4501 startup: Some(fdecl::StartupMode::Lazy),
4502 on_terminate: None,
4503 environment: None,
4504 ..Default::default()
4505 },
4506 fdecl::Child{
4507 name: Some("test2".to_string()),
4508 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4509 startup: Some(fdecl::StartupMode::Lazy),
4510 on_terminate: None,
4511 environment: None,
4512 ..Default::default()
4513 }
4514 ]);
4515 decl
4516 },
4517 result = Err(ErrorList::new(vec![
4518 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "scope".to_string() }),
4519 ])),
4520 },
4521 test_validate_event_stream_offer_to_scope_framework_invalid => {
4522 input = {
4523 let mut decl = new_component_decl();
4524 decl.offers = Some(vec![
4525 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4526 source_name: Some("started".to_string()),
4527 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4528 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4529 scope: Some(vec![fdecl::Ref::Framework(fdecl::FrameworkRef{})]),
4530 target_name: Some("started".to_string()),
4531 ..Default::default()
4532 }),
4533 ]);
4534 decl.children = Some(vec![fdecl::Child{
4535 name: Some("test".to_string()),
4536 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4537 startup: Some(fdecl::StartupMode::Lazy),
4538 on_terminate: None,
4539 environment: None,
4540 ..Default::default()
4541 },
4542 fdecl::Child{
4543 name: Some("test2".to_string()),
4544 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4545 startup: Some(fdecl::StartupMode::Lazy),
4546 on_terminate: None,
4547 environment: None,
4548 ..Default::default()
4549 }
4550 ]);
4551 decl
4552 },
4553 result = Err(ErrorList::new(vec![
4554 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "scope".to_string() }),
4555 ])),
4556 },
4557 test_validate_event_stream_offer_to_scope_valid => {
4558 input = {
4559 let mut decl = new_component_decl();
4560 decl.offers = Some(vec![
4561 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4562 source_name: Some("started".to_string()),
4563 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4564 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4565 scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
4566 target_name: Some("started".to_string()),
4567 ..Default::default()
4568 }),
4569 ]);
4570 decl.children = Some(vec![fdecl::Child{
4571 name: Some("test".to_string()),
4572 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4573 startup: Some(fdecl::StartupMode::Lazy),
4574 on_terminate: None,
4575 environment: None,
4576 ..Default::default()
4577 },
4578 fdecl::Child{
4579 name: Some("test2".to_string()),
4580 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4581 startup: Some(fdecl::StartupMode::Lazy),
4582 on_terminate: None,
4583 environment: None,
4584 ..Default::default()
4585 }
4586 ]);
4587 decl
4588 },
4589 result = Ok(()),
4590 },
4591 test_validate_event_stream_offer_to_scope_with_capability_requested => {
4592 input = {
4593 let mut decl = new_component_decl();
4594 decl.offers = Some(vec![
4595 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4596 source_name: Some("capability_requested".to_string()),
4597 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4598 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4599 scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
4600 target_name: Some("started".to_string()),
4601 ..Default::default()
4602 }),
4603 ]);
4604 decl.children = Some(vec![fdecl::Child{
4605 name: Some("test".to_string()),
4606 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4607 startup: Some(fdecl::StartupMode::Lazy),
4608 on_terminate: None,
4609 environment: None,
4610 ..Default::default()
4611 },
4612 fdecl::Child{
4613 name: Some("test2".to_string()),
4614 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4615 startup: Some(fdecl::StartupMode::Lazy),
4616 on_terminate: None,
4617 environment: None,
4618 ..Default::default()
4619 }
4620 ]);
4621 decl
4622 },
4623 result = Ok(()),
4624 },
4625 test_validate_event_stream_offer_with_no_source_name_invalid => {
4626 input = {
4627 let mut decl = new_component_decl();
4628 decl.offers = Some(vec![
4629 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4630 source_name: None,
4631 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4632 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4633 scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
4634 target_name: Some("started".to_string()),
4635 ..Default::default()
4636 }),
4637 ]);
4638 decl.children = Some(vec![fdecl::Child{
4639 name: Some("test".to_string()),
4640 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4641 startup: Some(fdecl::StartupMode::Lazy),
4642 on_terminate: None,
4643 environment: None,
4644 ..Default::default()
4645 },
4646 fdecl::Child{
4647 name: Some("test2".to_string()),
4648 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4649 startup: Some(fdecl::StartupMode::Lazy),
4650 on_terminate: None,
4651 environment: None,
4652 ..Default::default()
4653 }
4654 ]);
4655 decl
4656 },
4657 result = Err(ErrorList::new(vec![
4658 Error::MissingField(DeclField { decl: DeclType::OfferEventStream, field: "source_name".to_string() }),
4659 ])),
4660 },
4661 test_validate_event_stream_offer_invalid_source => {
4662 input = {
4663 let mut decl = new_component_decl();
4664 decl.offers = Some(vec![
4665 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4666 source_name: Some("stopped".to_string()),
4667 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
4668 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4669 target_name: Some("stopped".to_string()),
4670 ..Default::default()
4671 }),
4672 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4673 source_name: Some("started".to_string()),
4674 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4675 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4676 target_name: Some("started".to_string()),
4677 ..Default::default()
4678 }),
4679 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4680 source_name: Some("capability_requested".to_string()),
4681 source: Some(fdecl::Ref::Debug(fdecl::DebugRef{})),
4682 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4683 target_name: Some("capability_requested".to_string()),
4684 ..Default::default()
4685 }),
4686 ]);
4687 decl.children = Some(vec![fdecl::Child{
4688 name: Some("test".to_string()),
4689 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4690 startup: Some(fdecl::StartupMode::Lazy),
4691 on_terminate: None,
4692 environment: None,
4693 ..Default::default()
4694 },
4695 fdecl::Child{
4696 name: Some("test2".to_string()),
4697 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4698 startup: Some(fdecl::StartupMode::Lazy),
4699 on_terminate: None,
4700 environment: None,
4701 ..Default::default()
4702 }
4703 ]);
4704 decl
4705 },
4706 result = Err(ErrorList::new(vec![
4707 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "source".to_string() }),
4708 ])),
4709 },
4710
4711 test_validate_event_stream_offer_missing_source => {
4712 input = {
4713 let mut decl = new_component_decl();
4714 decl.offers = Some(vec![
4715 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4716 source_name: Some("stopped".to_string()),
4717 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
4718 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4719 target_name: Some("stopped".to_string()),
4720 ..Default::default()
4721 }),
4722 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4723 source_name: Some("started".to_string()),
4724 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4725 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4726 target_name: Some("started".to_string()),
4727 ..Default::default()
4728 }),
4729 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4730 source_name: Some("capability_requested".to_string()),
4731 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4732 target_name: Some("capability_requested".to_string()),
4733 ..Default::default()
4734 }),
4735 ]);
4736 decl.children = Some(vec![fdecl::Child{
4737 name: Some("test".to_string()),
4738 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4739 startup: Some(fdecl::StartupMode::Lazy),
4740 on_terminate: None,
4741 environment: None,
4742 ..Default::default()
4743 },
4744 fdecl::Child{
4745 name: Some("test2".to_string()),
4746 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4747 startup: Some(fdecl::StartupMode::Lazy),
4748 on_terminate: None,
4749 environment: None,
4750 ..Default::default()
4751 }
4752 ]);
4753 decl
4754 },
4755 result = Err(ErrorList::new(vec![
4756 Error::MissingField(DeclField { decl: DeclType::OfferEventStream, field: "source".to_string() }),
4757 ])),
4758 },
4759 test_validate_event_stream_must_have_target_path => {
4760 input = {
4761 let mut decl = new_component_decl();
4762 decl.uses = Some(vec![
4763 fdecl::Use::EventStream(fdecl::UseEventStream {
4764 source_name: Some("bar".to_string()),
4765 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4766 ..Default::default()
4767 }),
4768 ]);
4769 decl
4770 },
4771 result = Err(ErrorList::new(vec![
4772 Error::MissingField(DeclField { decl: DeclType::UseEventStream, field: "target_path".to_string() })
4773 ])),
4774 },
4775 test_validate_event_stream_must_have_source_names => {
4776 input = {
4777 let mut decl = new_component_decl();
4778 decl.uses = Some(vec![
4779 fdecl::Use::EventStream(fdecl::UseEventStream {
4780 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4781 target_path: Some("/svc/something".to_string()),
4782 ..Default::default()
4783 }),
4784 ]);
4785 decl
4786 },
4787 result = Err(ErrorList::new(vec![
4788 Error::MissingField(DeclField { decl: DeclType::UseEventStream, field: "source_name".to_string() })
4789 ])),
4790 },
4791 test_validate_event_stream_scope_must_be_child_or_collection => {
4792 input = {
4793 let mut decl = new_component_decl();
4794 decl.uses = Some(vec![
4795 fdecl::Use::EventStream(fdecl::UseEventStream {
4796 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4797 target_path: Some("/svc/something".to_string()),
4798 source_name: Some("some_source".to_string()),
4799 scope: Some(vec![fdecl::Ref::Framework(fdecl::FrameworkRef{})]),
4800 ..Default::default()
4801 }),
4802 ]);
4803 decl
4804 },
4805 result = Err(ErrorList::new(vec![
4806 Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "scope".to_string() })
4807 ])),
4808 },
4809 test_validate_event_stream_source_must_be_parent_or_child => {
4810 input = {
4811 let mut decl = new_component_decl();
4812 decl.uses = Some(vec![
4813 fdecl::Use::EventStream(fdecl::UseEventStream {
4814 source: Some(fdecl::Ref::Debug(fdecl::DebugRef{})),
4815 target_path: Some("/svc/something".to_string()),
4816 source_name: Some("some_source".to_string()),
4817 scope: Some(vec![]),
4818 ..Default::default()
4819 }),
4820 fdecl::Use::EventStream(fdecl::UseEventStream {
4821 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
4822 target_path: Some("/svc/something_else".to_string()),
4823 source_name: Some("some_source".to_string()),
4824 scope: Some(vec![]),
4825 ..Default::default()
4826 }),
4827 fdecl::Use::EventStream(fdecl::UseEventStream {
4828 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4829 target_path: Some("/svc/yet_something_else".to_string()),
4830 source_name: Some("some_source".to_string()),
4831 scope: Some(vec![]),
4832 ..Default::default()
4833 }),
4834 ]);
4835 decl
4836 },
4837 result = Err(ErrorList::new(vec![
4838 Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() }),
4839 Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() }),
4840 Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() })
4841 ])),
4842 },
4843 test_validate_no_runner => {
4844 input = {
4845 let mut decl = new_component_decl();
4846 decl.program = Some(fdecl::Program {
4847 runner: None,
4848 info: Some(fdata::Dictionary {
4849 entries: None,
4850 ..Default::default()
4851 }),
4852 ..Default::default()
4853 });
4854 decl
4855 },
4856 result = Err(ErrorList::new(vec![
4857 Error::MissingRunner,
4858 ])),
4859 },
4860 test_validate_uses_runner => {
4861 input = {
4862 let mut decl = new_component_decl();
4863 decl.program = Some(fdecl::Program {
4864 runner: None,
4865 info: Some(fdata::Dictionary {
4866 entries: None,
4867 ..Default::default()
4868 }),
4869 ..Default::default()
4870 });
4871 decl.uses = Some(vec![
4872 fdecl::Use::Runner(fdecl::UseRunner {
4873 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4874 source_name: Some("runner".to_string()),
4875 ..Default::default()
4876 }),
4877 ]);
4878 decl
4879 },
4880 result = Ok(()),
4881 },
4882 test_validate_program_and_uses_runner_match => {
4883 input = {
4884 let mut decl = new_component_decl();
4885 decl.program = Some(fdecl::Program {
4886 runner: Some("runner".to_string()),
4887 info: Some(fdata::Dictionary {
4888 entries: None,
4889 ..Default::default()
4890 }),
4891 ..Default::default()
4892 });
4893 decl.uses = Some(vec![
4894 fdecl::Use::Runner(fdecl::UseRunner {
4895 source: Some(fdecl::Ref::Environment(fdecl::EnvironmentRef {})),
4896 source_name: Some("runner".to_string()),
4897 ..Default::default()
4898 }),
4899 ]);
4900 decl
4901 },
4902 result = Ok(()),
4903 },
4904 test_validate_runner_names_conflict => {
4905 input = {
4906 let mut decl = new_component_decl();
4907 decl.program = Some(fdecl::Program {
4908 runner: Some("runner".to_string()),
4909 info: Some(fdata::Dictionary {
4910 entries: None,
4911 ..Default::default()
4912 }),
4913 ..Default::default()
4914 });
4915 decl.uses = Some(vec![
4916 fdecl::Use::Runner(fdecl::UseRunner {
4917 source: Some(fdecl::Ref::Environment(fdecl::EnvironmentRef {})),
4918 source_name: Some("other.runner".to_string()),
4919 ..Default::default()
4920 }),
4921 ]);
4922 decl
4923 },
4924 result = Err(ErrorList::new(vec![
4925 Error::ConflictingRunners,
4926 ])),
4927 },
4928 test_validate_uses_runner_not_environement => {
4929 input = {
4930 let mut decl = new_component_decl();
4931 decl.program = Some(fdecl::Program {
4932 runner: Some("runner".to_string()),
4933 info: Some(fdata::Dictionary {
4934 entries: None,
4935 ..Default::default()
4936 }),
4937 ..Default::default()
4938 });
4939 decl.uses = Some(vec![
4940 fdecl::Use::Runner(fdecl::UseRunner {
4941 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4942 source_name: Some("runner".to_string()),
4943 ..Default::default()
4944 }),
4945 ]);
4946 decl
4947 },
4948 result = Err(ErrorList::new(vec![
4949 Error::ConflictingRunners,
4950 ])),
4951 },
4952 test_validate_uses_long_identifiers => {
4953 input = {
4954 let mut decl = new_component_decl();
4955 decl.program = Some(fdecl::Program {
4956 runner: Some("elf".to_string()),
4957 info: Some(fdata::Dictionary {
4958 entries: None,
4959 ..Default::default()
4960 }),
4961 ..Default::default()
4962 });
4963 decl.uses = Some(vec![
4964 fdecl::Use::Service(fdecl::UseService {
4965 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4966 source_name: Some(format!("{}", "a".repeat(256))),
4967 target_path: Some("/a".repeat(2048)),
4968 dependency_type: Some(fdecl::DependencyType::Strong),
4969 ..Default::default()
4970 }),
4971 fdecl::Use::Protocol(fdecl::UseProtocol {
4972 dependency_type: Some(fdecl::DependencyType::Strong),
4973 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4974 source_name: Some(format!("{}", "a".repeat(256))),
4975 target_path: Some("/b".repeat(2048)),
4976 ..Default::default()
4977 }),
4978 fdecl::Use::Directory(fdecl::UseDirectory {
4979 dependency_type: Some(fdecl::DependencyType::Strong),
4980 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4981 source_name: Some(format!("{}", "a".repeat(256))),
4982 target_path: Some("/c".repeat(2048)),
4983 rights: Some(fio::Operations::CONNECT),
4984 subdir: None,
4985 ..Default::default()
4986 }),
4987 fdecl::Use::Storage(fdecl::UseStorage {
4988 source_name: Some("cache".to_string()),
4989 target_path: Some("/d".repeat(2048)),
4990 ..Default::default()
4991 }),
4992 ]);
4993 decl
4994 },
4995 result = Err(ErrorList::new(vec![
4996 Error::field_too_long(DeclType::UseService, "source_name"),
4997 Error::field_too_long(DeclType::UseService, "target_path"),
4998 Error::field_too_long(DeclType::UseProtocol, "source_name"),
4999 Error::field_too_long(DeclType::UseProtocol, "target_path"),
5000 Error::field_too_long(DeclType::UseDirectory, "source_name"),
5001 Error::field_too_long(DeclType::UseDirectory, "target_path"),
5002 Error::field_too_long(DeclType::UseStorage, "target_path"),
5003 ])),
5004 },
5005 test_validate_conflicting_paths => {
5006 input = {
5007 let mut decl = new_component_decl();
5008 decl.uses = Some(vec![
5009 fdecl::Use::Service(fdecl::UseService {
5010 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5011 source_name: Some("foo".to_string()),
5012 target_path: Some("/bar".to_string()),
5013 dependency_type: Some(fdecl::DependencyType::Strong),
5014 ..Default::default()
5015 }),
5016 fdecl::Use::Protocol(fdecl::UseProtocol {
5017 dependency_type: Some(fdecl::DependencyType::Strong),
5018 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5019 source_name: Some("space".to_string()),
5020 target_path: Some("/bar".to_string()),
5021 ..Default::default()
5022 }),
5023 fdecl::Use::Directory(fdecl::UseDirectory {
5024 dependency_type: Some(fdecl::DependencyType::Strong),
5025 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5026 source_name: Some("crow".to_string()),
5027 target_path: Some("/bar".to_string()),
5028 rights: Some(fio::Operations::CONNECT),
5029 subdir: None,
5030 ..Default::default()
5031 }),
5032 ]);
5033 decl
5034 },
5035 result = Err(ErrorList::new(vec![
5036 Error::duplicate_field(DeclType::UseProtocol, "target_path", "/bar"),
5037 Error::duplicate_field(DeclType::UseDirectory, "target_path", "/bar"),
5038 ])),
5039 },
5040 test_validate_exposes_empty => {
5042 input = {
5043 let mut decl = new_component_decl();
5044 decl.exposes = Some(vec![
5045 fdecl::Expose::Service(fdecl::ExposeService {
5046 source: None,
5047 source_name: None,
5048 target_name: None,
5049 target: None,
5050 ..Default::default()
5051 }),
5052 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5053 source: None,
5054 source_name: None,
5055 target_name: None,
5056 target: None,
5057 ..Default::default()
5058 }),
5059 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5060 source: None,
5061 source_name: None,
5062 target_name: None,
5063 target: None,
5064 rights: None,
5065 subdir: None,
5066 ..Default::default()
5067 }),
5068 fdecl::Expose::Runner(fdecl::ExposeRunner {
5069 source: None,
5070 source_name: None,
5071 target: None,
5072 target_name: None,
5073 ..Default::default()
5074 }),
5075 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5076 source: None,
5077 source_name: None,
5078 target: None,
5079 target_name: None,
5080 ..Default::default()
5081 }),
5082 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5083 ..Default::default()
5084 }),
5085 ]);
5086 decl
5087 },
5088 result = Err(ErrorList::new(vec![
5089 Error::missing_field(DeclType::ExposeService, "source"),
5090 Error::missing_field(DeclType::ExposeService, "target"),
5091 Error::missing_field(DeclType::ExposeService, "source_name"),
5092 Error::missing_field(DeclType::ExposeService, "target_name"),
5093 Error::missing_field(DeclType::ExposeProtocol, "source"),
5094 Error::missing_field(DeclType::ExposeProtocol, "target"),
5095 Error::missing_field(DeclType::ExposeProtocol, "source_name"),
5096 Error::missing_field(DeclType::ExposeProtocol, "target_name"),
5097 Error::missing_field(DeclType::ExposeDirectory, "source"),
5098 Error::missing_field(DeclType::ExposeDirectory, "target"),
5099 Error::missing_field(DeclType::ExposeDirectory, "source_name"),
5100 Error::missing_field(DeclType::ExposeDirectory, "target_name"),
5101 Error::missing_field(DeclType::ExposeRunner, "source"),
5102 Error::missing_field(DeclType::ExposeRunner, "target"),
5103 Error::missing_field(DeclType::ExposeRunner, "source_name"),
5104 Error::missing_field(DeclType::ExposeRunner, "target_name"),
5105 Error::missing_field(DeclType::ExposeResolver, "source"),
5106 Error::missing_field(DeclType::ExposeResolver, "target"),
5107 Error::missing_field(DeclType::ExposeResolver, "source_name"),
5108 Error::missing_field(DeclType::ExposeResolver, "target_name"),
5109 Error::missing_field(DeclType::ExposeDictionary, "source"),
5110 Error::missing_field(DeclType::ExposeDictionary, "target"),
5111 Error::missing_field(DeclType::ExposeDictionary, "source_name"),
5112 Error::missing_field(DeclType::ExposeDictionary, "target_name"),
5113 ])),
5114 },
5115 test_validate_exposes_extraneous => {
5116 input = {
5117 let mut decl = new_component_decl();
5118 decl.exposes = Some(vec![
5119 fdecl::Expose::Service(fdecl::ExposeService {
5120 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5121 name: "logger".to_string(),
5122 collection: Some("modular".to_string()),
5123 })),
5124 source_name: Some("logger".to_string()),
5125 target_name: Some("logger".to_string()),
5126 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5127 ..Default::default()
5128 }),
5129 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5130 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5131 name: "logger".to_string(),
5132 collection: Some("modular".to_string()),
5133 })),
5134 source_name: Some("legacy_logger".to_string()),
5135 target_name: Some("legacy_logger".to_string()),
5136 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5137 ..Default::default()
5138 }),
5139 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5140 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5141 name: "netstack".to_string(),
5142 collection: Some("modular".to_string()),
5143 })),
5144 source_name: Some("data".to_string()),
5145 target_name: Some("data".to_string()),
5146 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5147 rights: Some(fio::Operations::CONNECT),
5148 subdir: None,
5149 ..Default::default()
5150 }),
5151 fdecl::Expose::Runner(fdecl::ExposeRunner {
5152 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5153 name: "netstack".to_string(),
5154 collection: Some("modular".to_string()),
5155 })),
5156 source_name: Some("elf".to_string()),
5157 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5158 target_name: Some("elf".to_string()),
5159 ..Default::default()
5160 }),
5161 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5162 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5163 name: "netstack".to_string(),
5164 collection: Some("modular".to_string()),
5165 })),
5166 source_name: Some("pkg".to_string()),
5167 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5168 target_name: Some("pkg".to_string()),
5169 ..Default::default()
5170 }),
5171 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5172 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5173 name: "netstack".to_string(),
5174 collection: Some("modular".to_string()),
5175 })),
5176 source_name: Some("dict".to_string()),
5177 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5178 target_name: Some("dict".to_string()),
5179 ..Default::default()
5180 }),
5181 ]);
5182 decl
5183 },
5184 result = Err(ErrorList::new(vec![
5185 Error::extraneous_field(DeclType::ExposeService, "source.child.collection"),
5186 Error::extraneous_field(DeclType::ExposeProtocol, "source.child.collection"),
5187 Error::extraneous_field(DeclType::ExposeDirectory, "source.child.collection"),
5188 Error::extraneous_field(DeclType::ExposeRunner, "source.child.collection"),
5189 Error::extraneous_field(DeclType::ExposeResolver, "source.child.collection"),
5190 Error::extraneous_field(DeclType::ExposeDictionary, "source.child.collection"),
5191 ])),
5192 },
5193 test_validate_exposes_invalid_identifiers => {
5194 input = {
5195 let mut decl = new_component_decl();
5196 decl.exposes = Some(vec![
5197 fdecl::Expose::Service(fdecl::ExposeService {
5198 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5199 name: "^bad".to_string(),
5200 collection: None,
5201 })),
5202 source_name: Some("foo/".to_string()),
5203 target_name: Some("/".to_string()),
5204 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5205 ..Default::default()
5206 }),
5207 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5208 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5209 name: "^bad".to_string(),
5210 collection: None,
5211 })),
5212 source_name: Some("foo/".to_string()),
5213 target_name: Some("/".to_string()),
5214 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5215 ..Default::default()
5216 }),
5217 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5218 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5219 name: "^bad".to_string(),
5220 collection: None,
5221 })),
5222 source_name: Some("foo/".to_string()),
5223 target_name: Some("/".to_string()),
5224 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5225 rights: Some(fio::Operations::CONNECT),
5226 subdir: Some("/foo".to_string()),
5227 ..Default::default()
5228 }),
5229 fdecl::Expose::Runner(fdecl::ExposeRunner {
5230 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5231 name: "^bad".to_string(),
5232 collection: None,
5233 })),
5234 source_name: Some("/path".to_string()),
5235 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5236 target_name: Some("elf!".to_string()),
5237 ..Default::default()
5238 }),
5239 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5240 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5241 name: "^bad".to_string(),
5242 collection: None,
5243 })),
5244 source_name: Some("/path".to_string()),
5245 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5246 target_name: Some("pkg!".to_string()),
5247 ..Default::default()
5248 }),
5249 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5250 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5251 name: "^bad".to_string(),
5252 collection: None,
5253 })),
5254 source_name: Some("/path".to_string()),
5255 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5256 target_name: Some("pkg!".to_string()),
5257 ..Default::default()
5258 }),
5259 ]);
5260 decl
5261 },
5262 result = Err(ErrorList::new(vec![
5263 Error::invalid_field(DeclType::ExposeService, "source.child.name"),
5264 Error::invalid_field(DeclType::ExposeService, "source_name"),
5265 Error::invalid_field(DeclType::ExposeService, "target_name"),
5266 Error::invalid_field(DeclType::ExposeProtocol, "source.child.name"),
5267 Error::invalid_field(DeclType::ExposeProtocol, "source_name"),
5268 Error::invalid_field(DeclType::ExposeProtocol, "target_name"),
5269 Error::invalid_field(DeclType::ExposeDirectory, "source.child.name"),
5270 Error::invalid_field(DeclType::ExposeDirectory, "source_name"),
5271 Error::invalid_field(DeclType::ExposeDirectory, "target_name"),
5272 Error::invalid_field(DeclType::ExposeDirectory, "subdir"),
5273 Error::invalid_field(DeclType::ExposeRunner, "source.child.name"),
5274 Error::invalid_field(DeclType::ExposeRunner, "source_name"),
5275 Error::invalid_field(DeclType::ExposeRunner, "target_name"),
5276 Error::invalid_field(DeclType::ExposeResolver, "source.child.name"),
5277 Error::invalid_field(DeclType::ExposeResolver, "source_name"),
5278 Error::invalid_field(DeclType::ExposeResolver, "target_name"),
5279 Error::invalid_field(DeclType::ExposeDictionary, "source.child.name"),
5280 Error::invalid_field(DeclType::ExposeDictionary, "source_name"),
5281 Error::invalid_field(DeclType::ExposeDictionary, "target_name"),
5282 ])),
5283 },
5284 test_validate_exposes_invalid_source_target => {
5285 input = {
5286 let mut decl = new_component_decl();
5287 decl.children = Some(vec![fdecl::Child{
5288 name: Some("logger".to_string()),
5289 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5290 startup: Some(fdecl::StartupMode::Lazy),
5291 on_terminate: None,
5292 environment: None,
5293 ..Default::default()
5294 }]);
5295 decl.exposes = Some(vec![
5296 fdecl::Expose::Service(fdecl::ExposeService {
5297 source: None,
5298 source_name: Some("a".to_string()),
5299 target_name: Some("b".to_string()),
5300 target: None,
5301 ..Default::default()
5302 }),
5303 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5304 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5305 source_name: Some("c".to_string()),
5306 target_name: Some("d".to_string()),
5307 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5308 ..Default::default()
5309 }),
5310 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5311 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "z".to_string()})),
5312 source_name: Some("e".to_string()),
5313 target_name: Some("f".to_string()),
5314 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "z".to_string()})),
5315 rights: Some(fio::Operations::CONNECT),
5316 subdir: None,
5317 ..Default::default()
5318 }),
5319 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5320 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5321 source_name: Some("g".to_string()),
5322 target_name: Some("h".to_string()),
5323 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5324 rights: Some(fio::Operations::CONNECT),
5325 subdir: None,
5326 ..Default::default()
5327 }),
5328 fdecl::Expose::Runner(fdecl::ExposeRunner {
5329 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5330 source_name: Some("i".to_string()),
5331 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5332 target_name: Some("j".to_string()),
5333 ..Default::default()
5334 }),
5335 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5336 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5337 source_name: Some("k".to_string()),
5338 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5339 target_name: Some("l".to_string()),
5340 ..Default::default()
5341 }),
5342 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5343 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5344 name: "logger".to_string(),
5345 collection: None,
5346 })),
5347 source_name: Some("m".to_string()),
5348 target_name: Some("n".to_string()),
5349 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5350 ..Default::default()
5351 }),
5352 ]);
5353 decl
5354 },
5355 result = Err(ErrorList::new(vec![
5356 Error::missing_field(DeclType::ExposeService, "source"),
5357 Error::missing_field(DeclType::ExposeService, "target"),
5358 Error::invalid_field(DeclType::ExposeProtocol, "source"),
5359 Error::invalid_field(DeclType::ExposeProtocol, "target"),
5360 Error::invalid_field(DeclType::ExposeDirectory, "source"),
5361 Error::invalid_field(DeclType::ExposeDirectory, "target"),
5362 Error::invalid_field(DeclType::ExposeDirectory, "source"),
5363 Error::invalid_field(DeclType::ExposeDirectory, "target"),
5364 Error::invalid_field(DeclType::ExposeRunner, "source"),
5365 Error::invalid_field(DeclType::ExposeRunner, "target"),
5366 Error::invalid_field(DeclType::ExposeResolver, "source"),
5367 Error::invalid_field(DeclType::ExposeResolver, "target"),
5368 Error::invalid_field(DeclType::ExposeDictionary, "target"),
5369 ])),
5370 },
5371 test_validate_exposes_invalid_source_collection => {
5372 input = {
5373 let mut decl = new_component_decl();
5374 decl.collections = Some(vec![fdecl::Collection{
5375 name: Some("col".to_string()),
5376 durability: Some(fdecl::Durability::Transient),
5377 allowed_offers: None,
5378 allow_long_names: None,
5379 ..Default::default()
5380 }]);
5381 decl.exposes = Some(vec![
5382 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5383 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
5384 source_name: Some("a".to_string()),
5385 target_name: Some("a".to_string()),
5386 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5387 ..Default::default()
5388 }),
5389 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5390 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5391 source_name: Some("b".to_string()),
5392 target_name: Some("b".to_string()),
5393 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5394 rights: Some(fio::Operations::CONNECT),
5395 subdir: None,
5396 ..Default::default()
5397 }),
5398 fdecl::Expose::Runner(fdecl::ExposeRunner {
5399 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5400 source_name: Some("c".to_string()),
5401 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5402 target_name: Some("c".to_string()),
5403 ..Default::default()
5404 }),
5405 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5406 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5407 source_name: Some("d".to_string()),
5408 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5409 target_name: Some("d".to_string()),
5410 ..Default::default()
5411 }),
5412 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5413 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5414 source_name: Some("e".to_string()),
5415 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5416 target_name: Some("e".to_string()),
5417 ..Default::default()
5418 }),
5419 ]);
5420 decl
5421 },
5422 result = Err(ErrorList::new(vec![
5423 Error::invalid_field(DeclType::ExposeProtocol, "source"),
5424 Error::invalid_field(DeclType::ExposeDirectory, "source"),
5425 Error::invalid_field(DeclType::ExposeRunner, "source"),
5426 Error::invalid_field(DeclType::ExposeResolver, "source"),
5427 Error::invalid_field(DeclType::ExposeDictionary, "source"),
5428 ])),
5429 },
5430 test_validate_exposes_sources_collection => {
5431 input = {
5432 let mut decl = new_component_decl();
5433 decl.collections = Some(vec![
5434 fdecl::Collection {
5435 name: Some("col".to_string()),
5436 durability: Some(fdecl::Durability::Transient),
5437 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
5438 allow_long_names: None,
5439 ..Default::default()
5440 }
5441 ]);
5442 decl.exposes = Some(vec![
5443 fdecl::Expose::Service(fdecl::ExposeService {
5444 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
5445 source_name: Some("a".to_string()),
5446 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5447 target_name: Some("a".to_string()),
5448 ..Default::default()
5449 })
5450 ]);
5451 decl
5452 },
5453 result = Ok(()),
5454 },
5455 test_validate_exposes_long_identifiers => {
5456 input = {
5457 let mut decl = new_component_decl();
5458 decl.exposes = Some(vec![
5459 fdecl::Expose::Service(fdecl::ExposeService {
5460 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5461 name: "b".repeat(256),
5462 collection: None,
5463 })),
5464 source_name: Some(format!("{}", "a".repeat(1025))),
5465 target_name: Some(format!("{}", "b".repeat(1025))),
5466 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5467 ..Default::default()
5468 }),
5469 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5470 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5471 name: "b".repeat(256),
5472 collection: None,
5473 })),
5474 source_name: Some(format!("{}", "a".repeat(256))),
5475 target_name: Some(format!("{}", "b".repeat(256))),
5476 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5477 ..Default::default()
5478 }),
5479 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5480 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5481 name: "b".repeat(256),
5482 collection: None,
5483 })),
5484 source_name: Some(format!("{}", "a".repeat(256))),
5485 target_name: Some(format!("{}", "b".repeat(256))),
5486 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5487 rights: Some(fio::Operations::CONNECT),
5488 subdir: None,
5489 ..Default::default()
5490 }),
5491 fdecl::Expose::Runner(fdecl::ExposeRunner {
5492 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5493 name: "b".repeat(256),
5494 collection: None,
5495 })),
5496 source_name: Some("a".repeat(256)),
5497 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5498 target_name: Some("b".repeat(256)),
5499 ..Default::default()
5500 }),
5501 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5502 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5503 name: "b".repeat(256),
5504 collection: None,
5505 })),
5506 source_name: Some("a".repeat(256)),
5507 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5508 target_name: Some("b".repeat(256)),
5509 ..Default::default()
5510 }),
5511 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5512 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5513 name: "b".repeat(256),
5514 collection: None,
5515 })),
5516 source_name: Some("a".repeat(256)),
5517 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5518 target_name: Some("b".repeat(256)),
5519 ..Default::default()
5520 }),
5521 ]);
5522 decl
5523 },
5524 result = Err(ErrorList::new(vec![
5525 Error::field_too_long(DeclType::ExposeService, "source.child.name"),
5526 Error::field_too_long(DeclType::ExposeService, "source_name"),
5527 Error::field_too_long(DeclType::ExposeService, "target_name"),
5528 Error::field_too_long(DeclType::ExposeProtocol, "source.child.name"),
5529 Error::field_too_long(DeclType::ExposeProtocol, "source_name"),
5530 Error::field_too_long(DeclType::ExposeProtocol, "target_name"),
5531 Error::field_too_long(DeclType::ExposeDirectory, "source.child.name"),
5532 Error::field_too_long(DeclType::ExposeDirectory, "source_name"),
5533 Error::field_too_long(DeclType::ExposeDirectory, "target_name"),
5534 Error::field_too_long(DeclType::ExposeRunner, "source.child.name"),
5535 Error::field_too_long(DeclType::ExposeRunner, "source_name"),
5536 Error::field_too_long(DeclType::ExposeRunner, "target_name"),
5537 Error::field_too_long(DeclType::ExposeResolver, "source.child.name"),
5538 Error::field_too_long(DeclType::ExposeResolver, "source_name"),
5539 Error::field_too_long(DeclType::ExposeResolver, "target_name"),
5540 Error::field_too_long(DeclType::ExposeDictionary, "source.child.name"),
5541 Error::field_too_long(DeclType::ExposeDictionary, "source_name"),
5542 Error::field_too_long(DeclType::ExposeDictionary, "target_name"),
5543 ])),
5544 },
5545 test_validate_exposes_invalid_child => {
5546 input = {
5547 let mut decl = new_component_decl();
5548 decl.exposes = Some(vec![
5549 fdecl::Expose::Service(fdecl::ExposeService {
5550 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5551 name: "netstack".to_string(),
5552 collection: None,
5553 })),
5554 source_name: Some("fuchsia.logger.Log".to_string()),
5555 target_name: Some("fuchsia.logger.Log".to_string()),
5556 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5557 ..Default::default()
5558 }),
5559 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5560 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5561 name: "netstack".to_string(),
5562 collection: None,
5563 })),
5564 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
5565 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
5566 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5567 ..Default::default()
5568 }),
5569 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5570 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5571 name: "netstack".to_string(),
5572 collection: None,
5573 })),
5574 source_name: Some("data".to_string()),
5575 target_name: Some("data".to_string()),
5576 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5577 rights: Some(fio::Operations::CONNECT),
5578 subdir: None,
5579 ..Default::default()
5580 }),
5581 fdecl::Expose::Runner(fdecl::ExposeRunner {
5582 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5583 name: "netstack".to_string(),
5584 collection: None,
5585 })),
5586 source_name: Some("elf".to_string()),
5587 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5588 target_name: Some("elf".to_string()),
5589 ..Default::default()
5590 }),
5591 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5592 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5593 name: "netstack".to_string(),
5594 collection: None,
5595 })),
5596 source_name: Some("pkg".to_string()),
5597 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5598 target_name: Some("pkg".to_string()),
5599 ..Default::default()
5600 }),
5601 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5602 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5603 name: "netstack".to_string(),
5604 collection: None,
5605 })),
5606 source_name: Some("dict".to_string()),
5607 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5608 target_name: Some("dict".to_string()),
5609 ..Default::default()
5610 }),
5611 ]);
5612 decl
5613 },
5614 result = Err(ErrorList::new(vec![
5615 Error::invalid_child(DeclType::ExposeService, "source", "netstack"),
5616 Error::invalid_child(DeclType::ExposeProtocol, "source", "netstack"),
5617 Error::invalid_child(DeclType::ExposeDirectory, "source", "netstack"),
5618 Error::invalid_child(DeclType::ExposeRunner, "source", "netstack"),
5619 Error::invalid_child(DeclType::ExposeResolver, "source", "netstack"),
5620 Error::invalid_child(DeclType::ExposeDictionary, "source", "netstack"),
5621 ])),
5622 },
5623 test_validate_exposes_invalid_source_capability => {
5624 input = {
5625 fdecl::Component {
5626 exposes: Some(vec![
5627 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5628 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
5629 name: "this-storage-doesnt-exist".to_string(),
5630 })),
5631 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
5632 target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
5633 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5634 ..Default::default()
5635 }),
5636 ]),
5637 ..new_component_decl()
5638 }
5639 },
5640 result = Err(ErrorList::new(vec![
5641 Error::invalid_capability(DeclType::ExposeProtocol, "source", "this-storage-doesnt-exist"),
5642 ])),
5643 },
5644 test_validate_exposes_duplicate_target => {
5645 input = {
5646 let mut decl = new_component_decl();
5647 decl.exposes = Some(vec![
5648 fdecl::Expose::Service(fdecl::ExposeService {
5649 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
5650 name: "coll".into(),
5651 })),
5652 source_name: Some("netstack".to_string()),
5653 target_name: Some("fuchsia.net.Stack".to_string()),
5654 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5655 ..Default::default()
5656 }),
5657 fdecl::Expose::Service(fdecl::ExposeService {
5658 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
5659 name: "coll2".into(),
5660 })),
5661 source_name: Some("netstack2".to_string()),
5662 target_name: Some("fuchsia.net.Stack".to_string()),
5663 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5664 ..Default::default()
5665 }),
5666 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5667 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5668 source_name: Some("fonts".to_string()),
5669 target_name: Some("fuchsia.fonts.Provider".to_string()),
5670 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5671 ..Default::default()
5672 }),
5673 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5674 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5675 source_name: Some("fonts2".to_string()),
5676 target_name: Some("fuchsia.fonts.Provider".to_string()),
5677 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5678 ..Default::default()
5679 }),
5680 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5681 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5682 source_name: Some("assets".to_string()),
5683 target_name: Some("stuff".to_string()),
5684 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5685 rights: None,
5686 subdir: None,
5687 ..Default::default()
5688 }),
5689 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5690 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5691 source_name: Some("assets2".to_string()),
5692 target_name: Some("stuff".to_string()),
5693 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5694 rights: None,
5695 subdir: None,
5696 ..Default::default()
5697 }),
5698 fdecl::Expose::Runner(fdecl::ExposeRunner {
5699 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5700 source_name: Some("source_elf".to_string()),
5701 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5702 target_name: Some("elf".to_string()),
5703 ..Default::default()
5704 }),
5705 fdecl::Expose::Runner(fdecl::ExposeRunner {
5706 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5707 source_name: Some("source_elf".to_string()),
5708 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5709 target_name: Some("elf".to_string()),
5710 ..Default::default()
5711 }),
5712 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5713 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5714 source_name: Some("source_pkg".to_string()),
5715 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5716 target_name: Some("pkg".to_string()),
5717 ..Default::default()
5718 }),
5719 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5720 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5721 source_name: Some("source_pkg".to_string()),
5722 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5723 target_name: Some("pkg".to_string()),
5724 ..Default::default()
5725 }),
5726 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5727 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5728 source_name: Some("source_dict".to_string()),
5729 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5730 target_name: Some("dict".to_string()),
5731 ..Default::default()
5732 }),
5733 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5734 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5735 source_name: Some("source_dict".to_string()),
5736 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5737 target_name: Some("dict".to_string()),
5738 ..Default::default()
5739 }),
5740 ]);
5741 decl.collections = Some(vec![
5742 fdecl::Collection {
5743 name: Some("coll".into()),
5744 durability: Some(fdecl::Durability::Transient),
5745 ..Default::default()
5746 },
5747 fdecl::Collection {
5748 name: Some("coll2".into()),
5749 durability: Some(fdecl::Durability::Transient),
5750 ..Default::default()
5751 },
5752 ]);
5753 decl.capabilities = Some(vec![
5754 fdecl::Capability::Service(fdecl::Service {
5755 name: Some("netstack".to_string()),
5756 source_path: Some("/path".to_string()),
5757 ..Default::default()
5758 }),
5759 fdecl::Capability::Service(fdecl::Service {
5760 name: Some("netstack2".to_string()),
5761 source_path: Some("/path".to_string()),
5762 ..Default::default()
5763 }),
5764 fdecl::Capability::Protocol(fdecl::Protocol {
5765 name: Some("fonts".to_string()),
5766 source_path: Some("/path".to_string()),
5767 ..Default::default()
5768 }),
5769 fdecl::Capability::Protocol(fdecl::Protocol {
5770 name: Some("fonts2".to_string()),
5771 source_path: Some("/path".to_string()),
5772 ..Default::default()
5773 }),
5774 fdecl::Capability::Directory(fdecl::Directory {
5775 name: Some("assets".to_string()),
5776 source_path: Some("/path".to_string()),
5777 rights: Some(fio::Operations::CONNECT),
5778 ..Default::default()
5779 }),
5780 fdecl::Capability::Directory(fdecl::Directory {
5781 name: Some("assets2".to_string()),
5782 source_path: Some("/path".to_string()),
5783 rights: Some(fio::Operations::CONNECT),
5784 ..Default::default()
5785 }),
5786 fdecl::Capability::Runner(fdecl::Runner {
5787 name: Some("source_elf".to_string()),
5788 source_path: Some("/path".to_string()),
5789 ..Default::default()
5790 }),
5791 fdecl::Capability::Resolver(fdecl::Resolver {
5792 name: Some("source_pkg".to_string()),
5793 source_path: Some("/path".to_string()),
5794 ..Default::default()
5795 }),
5796 fdecl::Capability::Dictionary(fdecl::Dictionary {
5797 name: Some("source_dict".to_string()),
5798 ..Default::default()
5799 }),
5800 ]);
5801 decl
5802 },
5803 result = Err(ErrorList::new(vec![
5804 Error::duplicate_field(DeclType::ExposeProtocol, "target_name",
5806 "fuchsia.fonts.Provider"),
5807 Error::duplicate_field(DeclType::ExposeDirectory, "target_name",
5808 "stuff"),
5809 Error::duplicate_field(DeclType::ExposeRunner, "target_name",
5810 "elf"),
5811 Error::duplicate_field(DeclType::ExposeResolver, "target_name", "pkg"),
5812 Error::duplicate_field(DeclType::ExposeDictionary, "target_name", "dict"),
5813 ])),
5814 },
5815 test_validate_exposes_invalid_capability_from_self => {
5816 input = {
5817 let mut decl = new_component_decl();
5818 decl.exposes = Some(vec![
5819 fdecl::Expose::Service(fdecl::ExposeService {
5820 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5821 source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
5822 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5823 target_name: Some("foo".to_string()),
5824 ..Default::default()
5825 }),
5826 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5827 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5828 source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
5829 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5830 target_name: Some("bar".to_string()),
5831 ..Default::default()
5832 }),
5833 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5834 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5835 source_name: Some("dir".to_string()),
5836 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5837 target_name: Some("assets".to_string()),
5838 rights: None,
5839 subdir: None,
5840 ..Default::default()
5841 }),
5842 fdecl::Expose::Runner(fdecl::ExposeRunner {
5843 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5844 source_name: Some("source_elf".to_string()),
5845 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5846 target_name: Some("elf".to_string()),
5847 ..Default::default()
5848 }),
5849 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5850 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5851 source_name: Some("source_pkg".to_string()),
5852 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5853 target_name: Some("pkg".to_string()),
5854 ..Default::default()
5855 }),
5856 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5857 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5858 source_name: Some("source_dict".to_string()),
5859 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5860 target_name: Some("dict".to_string()),
5861 ..Default::default()
5862 }),
5863 fdecl::Expose::Config(fdecl::ExposeConfiguration {
5864 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5865 source_name: Some("source_config".to_string()),
5866 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5867 target_name: Some("config".to_string()),
5868 ..Default::default()
5869 }),
5870 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5871 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5872 source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
5873 source_dictionary: Some("dict/inner".to_string()),
5874 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5875 target_name: Some("baz".to_string()),
5876 ..Default::default()
5877 }),
5878 ]);
5879 decl
5880 },
5881 result = Err(ErrorList::new(vec![
5882 Error::invalid_capability(
5883 DeclType::ExposeService,
5884 "source",
5885 "fuchsia.some.library.SomeProtocol"),
5886 Error::invalid_capability(
5887 DeclType::ExposeProtocol,
5888 "source",
5889 "fuchsia.some.library.SomeProtocol"),
5890 Error::invalid_capability(DeclType::ExposeDirectory, "source", "dir"),
5891 Error::invalid_capability(DeclType::ExposeRunner, "source", "source_elf"),
5892 Error::invalid_capability(DeclType::ExposeResolver, "source", "source_pkg"),
5893 Error::invalid_capability(DeclType::ExposeDictionary, "source", "source_dict"),
5894 Error::invalid_capability(DeclType::ExposeConfig, "source", "source_config"),
5895 Error::invalid_capability(DeclType::ExposeProtocol, "source", "dict"),
5896 ])),
5897 },
5898
5899 test_validate_exposes_availability_service => {
5900 input = {
5901 let mut decl = generate_expose_different_source_and_availability_decl(
5902 |source, availability, target_name|
5903 fdecl::Expose::Service(fdecl::ExposeService {
5904 source: Some(source),
5905 source_name: Some("fuchsia.examples.Echo".to_string()),
5906 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5907 target_name: Some(target_name.to_string()),
5908 availability: Some(availability),
5909 ..Default::default()
5910 })
5911 );
5912 decl.capabilities = Some(vec![
5913 fdecl::Capability::Service(fdecl::Service {
5914 name: Some("fuchsia.examples.Echo".to_string()),
5915 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
5916 ..Default::default()
5917 }),
5918 ]);
5919 decl
5920 },
5921 result = {
5922 Err(ErrorList::new(vec![
5923 Error::availability_must_be_optional(
5924 DeclType::ExposeService,
5925 "availability",
5926 Some(&"fuchsia.examples.Echo".to_string()),
5927 ),
5928 Error::availability_must_be_optional(
5929 DeclType::ExposeService,
5930 "availability",
5931 Some(&"fuchsia.examples.Echo".to_string()),
5932 ),
5933 ]))
5934 },
5935 },
5936 test_validate_exposes_availability_protocol => {
5937 input = {
5938 let mut decl = generate_expose_different_source_and_availability_decl(
5939 |source, availability, target_name|
5940 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5941 source: Some(source),
5942 source_name: Some("fuchsia.examples.Echo".to_string()),
5943 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5944 target_name: Some(target_name.to_string()),
5945 availability: Some(availability),
5946 ..Default::default()
5947 })
5948 );
5949 decl.capabilities = Some(vec![
5950 fdecl::Capability::Protocol(fdecl::Protocol {
5951 name: Some("fuchsia.examples.Echo".to_string()),
5952 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
5953 ..Default::default()
5954 }),
5955 ]);
5956 decl
5957 },
5958 result = {
5959 Err(ErrorList::new(vec![
5960 Error::availability_must_be_optional(
5961 DeclType::ExposeProtocol,
5962 "availability",
5963 Some(&"fuchsia.examples.Echo".to_string()),
5964 ),
5965 Error::availability_must_be_optional(
5966 DeclType::ExposeProtocol,
5967 "availability",
5968 Some(&"fuchsia.examples.Echo".to_string()),
5969 ),
5970 ]))
5971 },
5972 },
5973 test_validate_exposes_availability_directory => {
5974 input = {
5975 let mut decl = generate_expose_different_source_and_availability_decl(
5976 |source, availability, target_name|
5977 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5978 source: Some(source),
5979 source_name: Some("fuchsia.examples.Echo".to_string()),
5980 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5981 target_name: Some(target_name.to_string()),
5982 availability: Some(availability),
5983 ..Default::default()
5984 })
5985 );
5986 decl.capabilities = Some(vec![
5987 fdecl::Capability::Directory(fdecl::Directory {
5988 name: Some("fuchsia.examples.Echo".to_string()),
5989 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
5990 rights: Some(fio::Operations::READ_BYTES),
5991 ..Default::default()
5992 }),
5993 ]);
5994 decl
5995 },
5996 result = {
5997 Err(ErrorList::new(vec![
5998 Error::availability_must_be_optional(
5999 DeclType::ExposeDirectory,
6000 "availability",
6001 Some(&"fuchsia.examples.Echo".to_string()),
6002 ),
6003 Error::availability_must_be_optional(
6004 DeclType::ExposeDirectory,
6005 "availability",
6006 Some(&"fuchsia.examples.Echo".to_string()),
6007 ),
6008 ]))
6009 },
6010 },
6011
6012 test_validate_offers_empty => {
6014 input = {
6015 let mut decl = new_component_decl();
6016 decl.offers = Some(vec![
6017 fdecl::Offer::Service(fdecl::OfferService {
6018 source: None,
6019 source_name: None,
6020 target: None,
6021 target_name: None,
6022 ..Default::default()
6023 }),
6024 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6025 source: None,
6026 source_name: None,
6027 target: None,
6028 target_name: None,
6029 dependency_type: None,
6030 ..Default::default()
6031 }),
6032 fdecl::Offer::Directory(fdecl::OfferDirectory {
6033 source: None,
6034 source_name: None,
6035 target: None,
6036 target_name: None,
6037 rights: None,
6038 subdir: None,
6039 dependency_type: None,
6040 ..Default::default()
6041 }),
6042 fdecl::Offer::Storage(fdecl::OfferStorage {
6043 source_name: None,
6044 source: None,
6045 target: None,
6046 target_name: None,
6047 ..Default::default()
6048 }),
6049 fdecl::Offer::Runner(fdecl::OfferRunner {
6050 source: None,
6051 source_name: None,
6052 target: None,
6053 target_name: None,
6054 ..Default::default()
6055 }),
6056 fdecl::Offer::Resolver(fdecl::OfferResolver {
6057 ..Default::default()
6058 }),
6059 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6060 ..Default::default()
6061 }),
6062 ]);
6063 decl
6064 },
6065 result = Err(ErrorList::new(vec![
6068 Error::missing_field(DeclType::OfferService, "source"),
6069 Error::missing_field(DeclType::OfferService, "source_name"),
6070 Error::missing_field(DeclType::OfferService, "target"),
6071 Error::missing_field(DeclType::OfferService, "target_name"),
6072 Error::missing_field(DeclType::OfferProtocol, "source"),
6074 Error::missing_field(DeclType::OfferProtocol, "source_name"),
6075 Error::missing_field(DeclType::OfferProtocol, "target"),
6076 Error::missing_field(DeclType::OfferProtocol, "target_name"),
6077 Error::missing_field(DeclType::OfferProtocol, "dependency_type"),
6078 Error::missing_field(DeclType::OfferDirectory, "source"),
6080 Error::missing_field(DeclType::OfferDirectory, "source_name"),
6081 Error::missing_field(DeclType::OfferDirectory, "target"),
6082 Error::missing_field(DeclType::OfferDirectory, "target_name"),
6083 Error::missing_field(DeclType::OfferDirectory, "dependency_type"),
6084 Error::missing_field(DeclType::OfferStorage, "source"),
6086 Error::missing_field(DeclType::OfferStorage, "source_name"),
6087 Error::missing_field(DeclType::OfferStorage, "target"),
6088 Error::missing_field(DeclType::OfferStorage, "target_name"),
6089 Error::missing_field(DeclType::OfferRunner, "source"),
6091 Error::missing_field(DeclType::OfferRunner, "source_name"),
6092 Error::missing_field(DeclType::OfferRunner, "target"),
6093 Error::missing_field(DeclType::OfferRunner, "target_name"),
6094 Error::missing_field(DeclType::OfferResolver, "source"),
6096 Error::missing_field(DeclType::OfferResolver, "source_name"),
6097 Error::missing_field(DeclType::OfferResolver, "target"),
6098 Error::missing_field(DeclType::OfferResolver, "target_name"),
6099 Error::missing_field(DeclType::OfferDictionary, "source"),
6100 Error::missing_field(DeclType::OfferDictionary, "source_name"),
6101 Error::missing_field(DeclType::OfferDictionary, "target"),
6102 Error::missing_field(DeclType::OfferDictionary, "target_name"),
6103 Error::missing_field(DeclType::OfferDictionary, "dependency_type"),
6104 ])),
6105 },
6106 test_validate_offers_long_identifiers => {
6107 input = {
6108 let mut decl = new_component_decl();
6109 decl.offers = Some(vec![
6110 fdecl::Offer::Service(fdecl::OfferService {
6111 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6112 name: "a".repeat(256),
6113 collection: None,
6114 })),
6115 source_name: Some(format!("{}", "a".repeat(256))),
6116 target: Some(fdecl::Ref::Child(
6117 fdecl::ChildRef {
6118 name: "b".repeat(256),
6119 collection: None,
6120 }
6121 )),
6122 target_name: Some(format!("{}", "b".repeat(256))),
6123 ..Default::default()
6124 }),
6125 fdecl::Offer::Service(fdecl::OfferService {
6126 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6127 source_name: Some("a".to_string()),
6128 target: Some(fdecl::Ref::Collection(
6129 fdecl::CollectionRef {
6130 name: "b".repeat(256),
6131 }
6132 )),
6133 target_name: Some(format!("{}", "b".repeat(256))),
6134 ..Default::default()
6135 }),
6136 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6137 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6138 name: "a".repeat(256),
6139 collection: None,
6140 })),
6141 source_name: Some(format!("{}", "a".repeat(256))),
6142 target: Some(fdecl::Ref::Child(
6143 fdecl::ChildRef {
6144 name: "b".repeat(256),
6145 collection: None,
6146 }
6147 )),
6148 target_name: Some(format!("{}", "b".repeat(256))),
6149 dependency_type: Some(fdecl::DependencyType::Strong),
6150 ..Default::default()
6151 }),
6152 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6153 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6154 source_name: Some("a".to_string()),
6155 target: Some(fdecl::Ref::Collection(
6156 fdecl::CollectionRef {
6157 name: "b".repeat(256),
6158 }
6159 )),
6160 target_name: Some(format!("{}", "b".repeat(256))),
6161 dependency_type: Some(fdecl::DependencyType::Weak),
6162 ..Default::default()
6163 }),
6164 fdecl::Offer::Directory(fdecl::OfferDirectory {
6165 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6166 name: "a".repeat(256),
6167 collection: None,
6168 })),
6169 source_name: Some(format!("{}", "a".repeat(256))),
6170 target: Some(fdecl::Ref::Child(
6171 fdecl::ChildRef {
6172 name: "b".repeat(256),
6173 collection: None,
6174 }
6175 )),
6176 target_name: Some(format!("{}", "b".repeat(256))),
6177 rights: Some(fio::Operations::CONNECT),
6178 subdir: None,
6179 dependency_type: Some(fdecl::DependencyType::Strong),
6180 ..Default::default()
6181 }),
6182 fdecl::Offer::Directory(fdecl::OfferDirectory {
6183 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6184 source_name: Some("a".to_string()),
6185 target: Some(fdecl::Ref::Collection(
6186 fdecl::CollectionRef {
6187 name: "b".repeat(256),
6188 }
6189 )),
6190 target_name: Some(format!("{}", "b".repeat(256))),
6191 rights: Some(fio::Operations::CONNECT),
6192 subdir: None,
6193 dependency_type: Some(fdecl::DependencyType::Weak),
6194 ..Default::default()
6195 }),
6196 fdecl::Offer::Storage(fdecl::OfferStorage {
6197 source_name: Some("data".to_string()),
6198 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6199 target: Some(fdecl::Ref::Child(
6200 fdecl::ChildRef {
6201 name: "b".repeat(256),
6202 collection: None,
6203 }
6204 )),
6205 target_name: Some("data".to_string()),
6206 ..Default::default()
6207 }),
6208 fdecl::Offer::Storage(fdecl::OfferStorage {
6209 source_name: Some("data".to_string()),
6210 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6211 target: Some(fdecl::Ref::Collection(
6212 fdecl::CollectionRef { name: "b".repeat(256) }
6213 )),
6214 target_name: Some("data".to_string()),
6215 ..Default::default()
6216 }),
6217 fdecl::Offer::Runner(fdecl::OfferRunner {
6218 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6219 name: "a".repeat(256),
6220 collection: None,
6221 })),
6222 source_name: Some("b".repeat(256)),
6223 target: Some(fdecl::Ref::Collection(
6224 fdecl::CollectionRef {
6225 name: "c".repeat(256),
6226 }
6227 )),
6228 target_name: Some("d".repeat(256)),
6229 ..Default::default()
6230 }),
6231 fdecl::Offer::Resolver(fdecl::OfferResolver {
6232 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6233 name: "a".repeat(256),
6234 collection: None,
6235 })),
6236 source_name: Some("b".repeat(256)),
6237 target: Some(fdecl::Ref::Collection(
6238 fdecl::CollectionRef {
6239 name: "c".repeat(256),
6240 }
6241 )),
6242 target_name: Some("d".repeat(256)),
6243 ..Default::default()
6244 }),
6245 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6246 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6247 name: "a".repeat(256),
6248 collection: None,
6249 })),
6250 source_name: Some("b".repeat(256)),
6251 target: Some(fdecl::Ref::Collection(
6252 fdecl::CollectionRef {
6253 name: "c".repeat(256),
6254 }
6255 )),
6256 target_name: Some("d".repeat(256)),
6257 dependency_type: Some(fdecl::DependencyType::Strong),
6258 ..Default::default()
6259 }),
6260 ]);
6261 decl
6262 },
6263 result = Err(ErrorList::new(vec![
6264 Error::field_too_long(DeclType::OfferService, "source.child.name"),
6265 Error::field_too_long(DeclType::OfferService, "source_name"),
6266 Error::field_too_long(DeclType::OfferService, "target.child.name"),
6267 Error::field_too_long(DeclType::OfferService, "target_name"),
6268 Error::field_too_long(DeclType::OfferService, "target.collection.name"),
6269 Error::field_too_long(DeclType::OfferService, "target_name"),
6270 Error::field_too_long(DeclType::OfferProtocol, "source.child.name"),
6271 Error::field_too_long(DeclType::OfferProtocol, "source_name"),
6272 Error::field_too_long(DeclType::OfferProtocol, "target.child.name"),
6273 Error::field_too_long(DeclType::OfferProtocol, "target_name"),
6274 Error::field_too_long(DeclType::OfferProtocol, "target.collection.name"),
6275 Error::field_too_long(DeclType::OfferProtocol, "target_name"),
6276 Error::field_too_long(DeclType::OfferDirectory, "source.child.name"),
6277 Error::field_too_long(DeclType::OfferDirectory, "source_name"),
6278 Error::field_too_long(DeclType::OfferDirectory, "target.child.name"),
6279 Error::field_too_long(DeclType::OfferDirectory, "target_name"),
6280 Error::field_too_long(DeclType::OfferDirectory, "target.collection.name"),
6281 Error::field_too_long(DeclType::OfferDirectory, "target_name"),
6282 Error::field_too_long(DeclType::OfferStorage, "target.child.name"),
6283 Error::field_too_long(DeclType::OfferStorage, "target.collection.name"),
6284 Error::field_too_long(DeclType::OfferRunner, "source.child.name"),
6285 Error::field_too_long(DeclType::OfferRunner, "source_name"),
6286 Error::field_too_long(DeclType::OfferRunner, "target.collection.name"),
6287 Error::field_too_long(DeclType::OfferRunner, "target_name"),
6288 Error::field_too_long(DeclType::OfferResolver, "source.child.name"),
6289 Error::field_too_long(DeclType::OfferResolver, "source_name"),
6290 Error::field_too_long(DeclType::OfferResolver, "target.collection.name"),
6291 Error::field_too_long(DeclType::OfferResolver, "target_name"),
6292 Error::field_too_long(DeclType::OfferDictionary, "source.child.name"),
6293 Error::field_too_long(DeclType::OfferDictionary, "source_name"),
6294 Error::field_too_long(DeclType::OfferDictionary, "target.collection.name"),
6295 Error::field_too_long(DeclType::OfferDictionary, "target_name"),
6296 ])),
6297 },
6298 test_validate_offers_extraneous => {
6299 input = {
6300 let mut decl = new_component_decl();
6301 decl.offers = Some(vec![
6302 fdecl::Offer::Service(fdecl::OfferService {
6303 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6304 name: "logger".to_string(),
6305 collection: Some("modular".to_string()),
6306 })),
6307 source_name: Some("fuchsia.logger.Log".to_string()),
6308 target: Some(fdecl::Ref::Child(
6309 fdecl::ChildRef {
6310 name: "netstack".to_string(),
6311 collection: Some("modular".to_string()),
6312 }
6313 )),
6314 target_name: Some("fuchsia.logger.Log".to_string()),
6315 ..Default::default()
6316 }),
6317 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6318 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6319 name: "logger".to_string(),
6320 collection: Some("modular".to_string()),
6321 })),
6322 source_name: Some("fuchsia.logger.Log".to_string()),
6323 target: Some(fdecl::Ref::Child(
6324 fdecl::ChildRef {
6325 name: "netstack".to_string(),
6326 collection: Some("modular".to_string()),
6327 }
6328 )),
6329 target_name: Some("fuchsia.logger.Log".to_string()),
6330 dependency_type: Some(fdecl::DependencyType::Strong),
6331 ..Default::default()
6332 }),
6333 fdecl::Offer::Directory(fdecl::OfferDirectory {
6334 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6335 name: "logger".to_string(),
6336 collection: Some("modular".to_string()),
6337 })),
6338 source_name: Some("assets".to_string()),
6339 target: Some(fdecl::Ref::Child(
6340 fdecl::ChildRef {
6341 name: "netstack".to_string(),
6342 collection: Some("modular".to_string()),
6343 }
6344 )),
6345 target_name: Some("assets".to_string()),
6346 rights: Some(fio::Operations::CONNECT),
6347 subdir: None,
6348 dependency_type: Some(fdecl::DependencyType::Weak),
6349 ..Default::default()
6350 }),
6351 fdecl::Offer::Storage(fdecl::OfferStorage {
6352 source_name: Some("data".to_string()),
6353 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{ })),
6354 target: Some(fdecl::Ref::Child(
6355 fdecl::ChildRef {
6356 name: "netstack".to_string(),
6357 collection: Some("modular".to_string()),
6358 }
6359 )),
6360 target_name: Some("data".to_string()),
6361 ..Default::default()
6362 }),
6363 fdecl::Offer::Runner(fdecl::OfferRunner {
6364 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6365 name: "logger".to_string(),
6366 collection: Some("modular".to_string()),
6367 })),
6368 source_name: Some("elf".to_string()),
6369 target: Some(fdecl::Ref::Child(
6370 fdecl::ChildRef {
6371 name: "netstack".to_string(),
6372 collection: Some("modular".to_string()),
6373 }
6374 )),
6375 target_name: Some("elf".to_string()),
6376 ..Default::default()
6377 }),
6378 fdecl::Offer::Resolver(fdecl::OfferResolver {
6379 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6380 name: "logger".to_string(),
6381 collection: Some("modular".to_string()),
6382 })),
6383 source_name: Some("pkg".to_string()),
6384 target: Some(fdecl::Ref::Child(
6385 fdecl::ChildRef {
6386 name: "netstack".to_string(),
6387 collection: Some("modular".to_string()),
6388 }
6389 )),
6390 target_name: Some("pkg".to_string()),
6391 ..Default::default()
6392 }),
6393 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6394 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6395 name: "logger".to_string(),
6396 collection: Some("modular".to_string()),
6397 })),
6398 source_name: Some("dict".to_string()),
6399 target: Some(fdecl::Ref::Child(
6400 fdecl::ChildRef {
6401 name: "netstack".to_string(),
6402 collection: Some("modular".to_string()),
6403 }
6404 )),
6405 target_name: Some("dict".to_string()),
6406 dependency_type: Some(fdecl::DependencyType::Strong),
6407 ..Default::default()
6408 }),
6409 ]);
6410 decl.capabilities = Some(vec![
6411 fdecl::Capability::Protocol(fdecl::Protocol {
6412 name: Some("fuchsia.logger.Log".to_string()),
6413 source_path: Some("/svc/logger".to_string()),
6414 ..Default::default()
6415 }),
6416 fdecl::Capability::Directory(fdecl::Directory {
6417 name: Some("assets".to_string()),
6418 source_path: Some("/data/assets".to_string()),
6419 rights: Some(fio::Operations::CONNECT),
6420 ..Default::default()
6421 }),
6422 ]);
6423 decl
6424 },
6425 result = Err(ErrorList::new(vec![
6426 Error::extraneous_field(DeclType::OfferService, "source.child.collection"),
6427 Error::extraneous_field(DeclType::OfferService, "target.child.collection"),
6428 Error::extraneous_field(DeclType::OfferProtocol, "source.child.collection"),
6429 Error::extraneous_field(DeclType::OfferProtocol, "target.child.collection"),
6430 Error::extraneous_field(DeclType::OfferDirectory, "source.child.collection"),
6431 Error::extraneous_field(DeclType::OfferDirectory, "target.child.collection"),
6432 Error::extraneous_field(DeclType::OfferStorage, "target.child.collection"),
6433 Error::extraneous_field(DeclType::OfferRunner, "source.child.collection"),
6434 Error::extraneous_field(DeclType::OfferRunner, "target.child.collection"),
6435 Error::extraneous_field(DeclType::OfferResolver, "source.child.collection"),
6436 Error::extraneous_field(DeclType::OfferResolver, "target.child.collection"),
6437 Error::extraneous_field(DeclType::OfferDictionary, "source.child.collection"),
6438 Error::extraneous_field(DeclType::OfferDictionary, "target.child.collection"),
6439 ])),
6440 },
6441 test_validate_offers_invalid_filtered_service_fields => {
6442 input = {
6443 let mut decl = new_component_decl();
6444 decl.offers = Some(vec![
6445 fdecl::Offer::Service(fdecl::OfferService {
6446 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6447 source_name: Some("fuchsia.logger.Log".to_string()),
6448 target: Some(fdecl::Ref::Child(
6449 fdecl::ChildRef {
6450 name: "logger".to_string(),
6451 collection: None,
6452 }
6453 )),
6454 target_name: Some("fuchsia.logger.Log".to_string()),
6455 source_instance_filter: Some(vec![]),
6456 ..Default::default()
6457 }),
6458 fdecl::Offer::Service(fdecl::OfferService {
6459 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6460 source_name: Some("fuchsia.logger.Log".to_string()),
6461 target: Some(fdecl::Ref::Child(
6462 fdecl::ChildRef {
6463 name: "logger".to_string(),
6464 collection: None,
6465 }
6466 )),
6467 target_name: Some("fuchsia.logger.Log2".to_string()),
6468 source_instance_filter: Some(vec!["^badname".to_string()]),
6469 ..Default::default()
6470 }),
6471 fdecl::Offer::Service(fdecl::OfferService {
6472 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6473 source_name: Some("fuchsia.logger.Log".to_string()),
6474 target: Some(fdecl::Ref::Child(
6475 fdecl::ChildRef {
6476 name: "logger".to_string(),
6477 collection: None,
6478 }
6479 )),
6480 target_name: Some("fuchsia.logger.Log1".to_string()),
6481 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()}]),
6482 ..Default::default()
6483 }),
6484 fdecl::Offer::Service(fdecl::OfferService {
6485 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6486 source_name: Some("fuchsia.logger.Log".to_string()),
6487 target: Some(fdecl::Ref::Child(
6488 fdecl::ChildRef {
6489 name: "logger".to_string(),
6490 collection: None,
6491 }
6492 )),
6493 target_name: Some("fuchsia.logger.Log3".to_string()),
6494 renamed_instances: Some(vec![
6495 fdecl::NameMapping {
6496 source_name: "^badname".to_string(),
6497 target_name: "^badname".to_string(),
6498 }
6499 ]),
6500 ..Default::default()
6501 })
6502 ]);
6503 decl.children = Some(vec![
6504 fdecl::Child {
6505 name: Some("logger".to_string()),
6506 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
6507 startup: Some(fdecl::StartupMode::Lazy),
6508 on_terminate: None,
6509 environment: None,
6510 ..Default::default()
6511 },
6512 ]);
6513 decl
6514 },
6515 result = Err(ErrorList::new(vec![
6516 Error::invalid_field(DeclType::OfferService, "source_instance_filter"),
6517 Error::invalid_field(DeclType::OfferService, "source_instance_filter"),
6518 Error::invalid_field(DeclType::OfferService, "renamed_instances"),
6519 Error::invalid_field(DeclType::OfferService, "renamed_instances.source_name"),
6520 Error::invalid_field(DeclType::OfferService, "renamed_instances.target_name"),
6521 ])),
6522 },
6523 test_validate_offers_invalid_identifiers => {
6524 input = {
6525 let mut decl = new_component_decl();
6526 decl.offers = Some(vec![
6527 fdecl::Offer::Service(fdecl::OfferService {
6528 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6529 name: "^bad".to_string(),
6530 collection: None,
6531 })),
6532 source_name: Some("foo/".to_string()),
6533 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6534 name: "%bad".to_string(),
6535 collection: None,
6536 })),
6537 target_name: Some("/".to_string()),
6538 ..Default::default()
6539 }),
6540 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6541 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6542 name: "^bad".to_string(),
6543 collection: None,
6544 })),
6545 source_name: Some("foo/".to_string()),
6546 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6547 name: "%bad".to_string(),
6548 collection: None,
6549 })),
6550 target_name: Some("/".to_string()),
6551 dependency_type: Some(fdecl::DependencyType::Strong),
6552 ..Default::default()
6553 }),
6554 fdecl::Offer::Directory(fdecl::OfferDirectory {
6555 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6556 name: "^bad".to_string(),
6557 collection: None,
6558 })),
6559 source_name: Some("foo/".to_string()),
6560 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6561 name: "%bad".to_string(),
6562 collection: None,
6563 })),
6564 target_name: Some("/".to_string()),
6565 rights: Some(fio::Operations::CONNECT),
6566 subdir: Some("/foo".to_string()),
6567 dependency_type: Some(fdecl::DependencyType::Strong),
6568 ..Default::default()
6569 }),
6570 fdecl::Offer::Runner(fdecl::OfferRunner {
6571 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6572 name: "^bad".to_string(),
6573 collection: None,
6574 })),
6575 source_name: Some("/path".to_string()),
6576 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6577 name: "%bad".to_string(),
6578 collection: None,
6579 })),
6580 target_name: Some("elf!".to_string()),
6581 ..Default::default()
6582 }),
6583 fdecl::Offer::Resolver(fdecl::OfferResolver {
6584 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6585 name: "^bad".to_string(),
6586 collection: None,
6587 })),
6588 source_name: Some("/path".to_string()),
6589 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6590 name: "%bad".to_string(),
6591 collection: None,
6592 })),
6593 target_name: Some("pkg!".to_string()),
6594 ..Default::default()
6595 }),
6596 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6597 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6598 name: "^bad".to_string(),
6599 collection: None,
6600 })),
6601 source_name: Some("/path".to_string()),
6602 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6603 name: "%bad".to_string(),
6604 collection: None,
6605 })),
6606 target_name: Some("pkg!".to_string()),
6607 dependency_type: Some(fdecl::DependencyType::Strong),
6608 ..Default::default()
6609 }),
6610 ]);
6611 decl
6612 },
6613 result = Err(ErrorList::new(vec![
6614 Error::invalid_field(DeclType::OfferService, "source.child.name"),
6615 Error::invalid_field(DeclType::OfferService, "source_name"),
6616 Error::invalid_field(DeclType::OfferService, "target.child.name"),
6617 Error::invalid_field(DeclType::OfferService, "target_name"),
6618 Error::invalid_field(DeclType::OfferProtocol, "source.child.name"),
6619 Error::invalid_field(DeclType::OfferProtocol, "source_name"),
6620 Error::invalid_field(DeclType::OfferProtocol, "target.child.name"),
6621 Error::invalid_field(DeclType::OfferProtocol, "target_name"),
6622 Error::invalid_field(DeclType::OfferDirectory, "source.child.name"),
6623 Error::invalid_field(DeclType::OfferDirectory, "source_name"),
6624 Error::invalid_field(DeclType::OfferDirectory, "target.child.name"),
6625 Error::invalid_field(DeclType::OfferDirectory, "target_name"),
6626 Error::invalid_field(DeclType::OfferDirectory, "subdir"),
6627 Error::invalid_field(DeclType::OfferRunner, "source.child.name"),
6628 Error::invalid_field(DeclType::OfferRunner, "source_name"),
6629 Error::invalid_field(DeclType::OfferRunner, "target.child.name"),
6630 Error::invalid_field(DeclType::OfferRunner, "target_name"),
6631 Error::invalid_field(DeclType::OfferResolver, "source.child.name"),
6632 Error::invalid_field(DeclType::OfferResolver, "source_name"),
6633 Error::invalid_field(DeclType::OfferResolver, "target.child.name"),
6634 Error::invalid_field(DeclType::OfferResolver, "target_name"),
6635 Error::invalid_field(DeclType::OfferDictionary, "source.child.name"),
6636 Error::invalid_field(DeclType::OfferDictionary, "source_name"),
6637 Error::invalid_field(DeclType::OfferDictionary, "target.child.name"),
6638 Error::invalid_field(DeclType::OfferDictionary, "target_name"),
6639 ])),
6640 },
6641 test_validate_offers_target_equals_source => {
6642 input = {
6643 let mut decl = new_component_decl();
6644 decl.offers = Some(vec![
6645 fdecl::Offer::Service(fdecl::OfferService {
6646 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6647 name: "logger".to_string(),
6648 collection: None,
6649 })),
6650 source_name: Some("logger".to_string()),
6651 target: Some(fdecl::Ref::Child(
6652 fdecl::ChildRef {
6653 name: "logger".to_string(),
6654 collection: None,
6655 }
6656 )),
6657 target_name: Some("logger".to_string()),
6658 ..Default::default()
6659 }),
6660 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6661 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6662 name: "logger".to_string(),
6663 collection: None,
6664 })),
6665 source_name: Some("legacy_logger".to_string()),
6666 target: Some(fdecl::Ref::Child(
6667 fdecl::ChildRef {
6668 name: "logger".to_string(),
6669 collection: None,
6670 }
6671 )),
6672 target_name: Some("weak_legacy_logger".to_string()),
6673 dependency_type: Some(fdecl::DependencyType::Weak),
6674 ..Default::default()
6675 }),
6676 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6677 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6678 name: "logger".to_string(),
6679 collection: None,
6680 })),
6681 source_name: Some("legacy_logger".to_string()),
6682 target: Some(fdecl::Ref::Child(
6683 fdecl::ChildRef {
6684 name: "logger".to_string(),
6685 collection: None,
6686 }
6687 )),
6688 target_name: Some("strong_legacy_logger".to_string()),
6689 dependency_type: Some(fdecl::DependencyType::Strong),
6690 ..Default::default()
6691 }),
6692 fdecl::Offer::Directory(fdecl::OfferDirectory {
6693 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6694 name: "logger".to_string(),
6695 collection: None,
6696 })),
6697 source_name: Some("assets".to_string()),
6698 target: Some(fdecl::Ref::Child(
6699 fdecl::ChildRef {
6700 name: "logger".to_string(),
6701 collection: None,
6702 }
6703 )),
6704 target_name: Some("assets".to_string()),
6705 rights: Some(fio::Operations::CONNECT),
6706 subdir: None,
6707 dependency_type: Some(fdecl::DependencyType::Strong),
6708 ..Default::default()
6709 }),
6710 fdecl::Offer::Runner(fdecl::OfferRunner {
6711 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6712 name: "logger".to_string(),
6713 collection: None,
6714 })),
6715 source_name: Some("web".to_string()),
6716 target: Some(fdecl::Ref::Child(
6717 fdecl::ChildRef {
6718 name: "logger".to_string(),
6719 collection: None,
6720 }
6721 )),
6722 target_name: Some("web".to_string()),
6723 ..Default::default()
6724 }),
6725 fdecl::Offer::Resolver(fdecl::OfferResolver {
6726 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6727 name: "logger".to_string(),
6728 collection: None,
6729 })),
6730 source_name: Some("pkg".to_string()),
6731 target: Some(fdecl::Ref::Child(
6732 fdecl::ChildRef {
6733 name: "logger".to_string(),
6734 collection: None,
6735 }
6736 )),
6737 target_name: Some("pkg".to_string()),
6738 ..Default::default()
6739 }),
6740 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6741 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6742 name: "logger".to_string(),
6743 collection: None,
6744 })),
6745 source_name: Some("dict".to_string()),
6746 target: Some(fdecl::Ref::Child(
6747 fdecl::ChildRef {
6748 name: "logger".to_string(),
6749 collection: None,
6750 }
6751 )),
6752 target_name: Some("dict".to_string()),
6753 dependency_type: Some(fdecl::DependencyType::Strong),
6754 ..Default::default()
6755 }),
6756 ]);
6757 decl.children = Some(vec![fdecl::Child{
6758 name: Some("logger".to_string()),
6759 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
6760 startup: Some(fdecl::StartupMode::Lazy),
6761 on_terminate: None,
6762 environment: None,
6763 ..Default::default()
6764 }]);
6765 decl
6766 },
6767 result = Err(ErrorList::new(vec![
6768 Error::dependency_cycle("{{child logger -> child logger}}".to_string()),
6769 ])),
6770 },
6771 test_validate_offers_storage_target_equals_source => {
6772 input = fdecl::Component {
6773 offers: Some(vec![
6774 fdecl::Offer::Storage(fdecl::OfferStorage {
6775 source_name: Some("data".to_string()),
6776 source: Some(fdecl::Ref::Self_(fdecl::SelfRef { })),
6777 target: Some(fdecl::Ref::Child(
6778 fdecl::ChildRef {
6779 name: "logger".to_string(),
6780 collection: None,
6781 }
6782 )),
6783 target_name: Some("data".to_string()),
6784 ..Default::default()
6785 })
6786 ]),
6787 capabilities: Some(vec![
6788 fdecl::Capability::Storage(fdecl::Storage {
6789 name: Some("data".to_string()),
6790 backing_dir: Some("minfs".to_string()),
6791 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6792 name: "logger".to_string(),
6793 collection: None,
6794 })),
6795 subdir: None,
6796 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
6797 ..Default::default()
6798 }),
6799 ]),
6800 children: Some(vec![
6801 fdecl::Child {
6802 name: Some("logger".to_string()),
6803 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
6804 startup: Some(fdecl::StartupMode::Lazy),
6805 on_terminate: None,
6806 environment: None,
6807 ..Default::default()
6808 },
6809 ]),
6810 ..new_component_decl()
6811 },
6812 result = Err(ErrorList::new(vec![
6813 Error::dependency_cycle("{{child logger -> capability data -> child logger}}".to_string()),
6814 ])),
6815 },
6816 test_validate_offers_invalid_child => {
6817 input = {
6818 let mut decl = new_component_decl();
6819 decl.offers = Some(vec![
6820 fdecl::Offer::Service(fdecl::OfferService {
6821 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6822 name: "logger".to_string(),
6823 collection: None,
6824 })),
6825 source_name: Some("fuchsia.logger.Log".to_string()),
6826 target: Some(fdecl::Ref::Child(
6827 fdecl::ChildRef {
6828 name: "netstack".to_string(),
6829 collection: None,
6830 }
6831 )),
6832 target_name: Some("fuchsia.logger.Log".to_string()),
6833 ..Default::default()
6834 }),
6835 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6836 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6837 name: "logger".to_string(),
6838 collection: None,
6839 })),
6840 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
6841 target: Some(fdecl::Ref::Child(
6842 fdecl::ChildRef {
6843 name: "netstack".to_string(),
6844 collection: None,
6845 }
6846 )),
6847 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
6848 dependency_type: Some(fdecl::DependencyType::Strong),
6849 ..Default::default()
6850 }),
6851 fdecl::Offer::Directory(fdecl::OfferDirectory {
6852 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6853 name: "logger".to_string(),
6854 collection: None,
6855 })),
6856 source_name: Some("assets".to_string()),
6857 target: Some(fdecl::Ref::Collection(
6858 fdecl::CollectionRef { name: "modular".to_string() }
6859 )),
6860 target_name: Some("assets".to_string()),
6861 rights: Some(fio::Operations::CONNECT),
6862 subdir: None,
6863 dependency_type: Some(fdecl::DependencyType::Weak),
6864 ..Default::default()
6865 }),
6866 ]);
6867 decl.capabilities = Some(vec![
6868 fdecl::Capability::Storage(fdecl::Storage {
6869 name: Some("memfs".to_string()),
6870 backing_dir: Some("memfs".to_string()),
6871 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6872 name: "logger".to_string(),
6873 collection: None,
6874 })),
6875 subdir: None,
6876 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
6877 ..Default::default()
6878 }),
6879 ]);
6880 decl.children = Some(vec![
6881 fdecl::Child {
6882 name: Some("netstack".to_string()),
6883 url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
6884 startup: Some(fdecl::StartupMode::Lazy),
6885 on_terminate: None,
6886 environment: None,
6887 ..Default::default()
6888 },
6889 ]);
6890 decl.collections = Some(vec![
6891 fdecl::Collection {
6892 name: Some("modular".to_string()),
6893 durability: Some(fdecl::Durability::Transient),
6894 environment: None,
6895 allowed_offers: Some(fdecl::AllowedOffers::StaticAndDynamic),
6896 allow_long_names: None,
6897 ..Default::default()
6898 },
6899 ]);
6900 decl
6901 },
6902 result = Err(ErrorList::new(vec![
6903 Error::invalid_child(DeclType::Storage, "source", "logger"),
6904 Error::invalid_child(DeclType::OfferService, "source", "logger"),
6905 Error::invalid_child(DeclType::OfferProtocol, "source", "logger"),
6906 Error::invalid_child(DeclType::OfferDirectory, "source", "logger"),
6907 ])),
6908 },
6909 test_validate_offers_invalid_source_capability => {
6910 input = {
6911 fdecl::Component {
6912 offers: Some(vec![
6913 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6914 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
6915 name: "this-storage-doesnt-exist".to_string(),
6916 })),
6917 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
6918 target: Some(fdecl::Ref::Child(
6919 fdecl::ChildRef {
6920 name: "netstack".to_string(),
6921 collection: None,
6922 }
6923 )),
6924 target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
6925 dependency_type: Some(fdecl::DependencyType::Strong),
6926 ..Default::default()
6927 }),
6928 ]),
6929 ..new_component_decl()
6930 }
6931 },
6932 result = Err(ErrorList::new(vec![
6933 Error::invalid_capability(DeclType::OfferProtocol, "source", "this-storage-doesnt-exist"),
6934 Error::invalid_child(DeclType::OfferProtocol, "target", "netstack"),
6935 ])),
6936 },
6937 test_validate_offers_target => {
6938 input = {
6939 let mut decl = new_component_decl();
6940 decl.offers = Some(vec![
6941 fdecl::Offer::Service(fdecl::OfferService {
6942 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6943 name: "modular".into()
6944 })),
6945 source_name: Some("logger".to_string()),
6946 target: Some(fdecl::Ref::Child(
6947 fdecl::ChildRef {
6948 name: "netstack".to_string(),
6949 collection: None,
6950 }
6951 )),
6952 target_name: Some("fuchsia.logger.Log".to_string()),
6953 ..Default::default()
6954 }),
6955 fdecl::Offer::Service(fdecl::OfferService {
6956 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6957 name: "modular".into()
6958 })),
6959 source_name: Some("logger".to_string()),
6960 target: Some(fdecl::Ref::Child(
6961 fdecl::ChildRef {
6962 name: "netstack".to_string(),
6963 collection: None,
6964 }
6965 )),
6966 target_name: Some("fuchsia.logger.Log".to_string()),
6967 ..Default::default()
6968 }),
6969 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6970 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6971 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
6972 target: Some(fdecl::Ref::Child(
6973 fdecl::ChildRef {
6974 name: "netstack".to_string(),
6975 collection: None,
6976 }
6977 )),
6978 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
6979 dependency_type: Some(fdecl::DependencyType::Strong),
6980 ..Default::default()
6981 }),
6982 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6983 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6984 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
6985 target: Some(fdecl::Ref::Child(
6986 fdecl::ChildRef {
6987 name: "netstack".to_string(),
6988 collection: None,
6989 }
6990 )),
6991 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
6992 dependency_type: Some(fdecl::DependencyType::Strong),
6993 ..Default::default()
6994 }),
6995 fdecl::Offer::Directory(fdecl::OfferDirectory {
6996 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6997 source_name: Some("assets".to_string()),
6998 target: Some(fdecl::Ref::Collection(
6999 fdecl::CollectionRef { name: "modular".to_string() }
7000 )),
7001 target_name: Some("assets".to_string()),
7002 rights: Some(fio::Operations::CONNECT),
7003 subdir: None,
7004 dependency_type: Some(fdecl::DependencyType::Strong),
7005 ..Default::default()
7006 }),
7007 fdecl::Offer::Directory(fdecl::OfferDirectory {
7008 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7009 source_name: Some("assets".to_string()),
7010 target: Some(fdecl::Ref::Collection(
7011 fdecl::CollectionRef { name: "modular".to_string() }
7012 )),
7013 target_name: Some("assets".to_string()),
7014 rights: Some(fio::Operations::CONNECT),
7015 subdir: None,
7016 dependency_type: Some(fdecl::DependencyType::Weak),
7017 ..Default::default()
7018 }),
7019 fdecl::Offer::Storage(fdecl::OfferStorage {
7020 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7021 source_name: Some("data".to_string()),
7022 target: Some(fdecl::Ref::Collection(
7023 fdecl::CollectionRef { name: "modular".to_string() }
7024 )),
7025 target_name: Some("data".to_string()),
7026 ..Default::default()
7027 }),
7028 fdecl::Offer::Storage(fdecl::OfferStorage {
7029 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7030 source_name: Some("data".to_string()),
7031 target: Some(fdecl::Ref::Collection(
7032 fdecl::CollectionRef { name: "modular".to_string() }
7033 )),
7034 target_name: Some("data".to_string()),
7035 ..Default::default()
7036 }),
7037 fdecl::Offer::Runner(fdecl::OfferRunner {
7038 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7039 source_name: Some("elf".to_string()),
7040 target: Some(fdecl::Ref::Collection(
7041 fdecl::CollectionRef { name: "modular".to_string() }
7042 )),
7043 target_name: Some("duplicated".to_string()),
7044 ..Default::default()
7045 }),
7046 fdecl::Offer::Runner(fdecl::OfferRunner {
7047 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7048 source_name: Some("elf".to_string()),
7049 target: Some(fdecl::Ref::Collection(
7050 fdecl::CollectionRef { name: "modular".to_string() }
7051 )),
7052 target_name: Some("duplicated".to_string()),
7053 ..Default::default()
7054 }),
7055 fdecl::Offer::Resolver(fdecl::OfferResolver {
7056 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7057 source_name: Some("pkg".to_string()),
7058 target: Some(fdecl::Ref::Collection(
7059 fdecl::CollectionRef { name: "modular".to_string() }
7060 )),
7061 target_name: Some("duplicated".to_string()),
7062 ..Default::default()
7063 }),
7064 fdecl::Offer::EventStream(fdecl::OfferEventStream {
7065 source_name: Some("started".to_string()),
7066 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7067 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "netstack".to_string(), collection: None})),
7068 target_name: Some("started".to_string()),
7069 ..Default::default()
7070 }),
7071 fdecl::Offer::EventStream(fdecl::OfferEventStream {
7072 source_name: Some("started".to_string()),
7073 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7074 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "netstack".to_string(), collection: None})),
7075 target_name: Some("started".to_string()),
7076 ..Default::default()
7077 }),
7078 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7079 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7080 source_name: Some("a".to_string()),
7081 target: Some(fdecl::Ref::Collection(
7082 fdecl::CollectionRef { name: "modular".to_string() }
7083 )),
7084 target_name: Some("dict".to_string()),
7085 dependency_type: Some(fdecl::DependencyType::Strong),
7086 ..Default::default()
7087 }),
7088 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7089 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7090 source_name: Some("b".to_string()),
7091 target: Some(fdecl::Ref::Collection(
7092 fdecl::CollectionRef { name: "modular".to_string() }
7093 )),
7094 target_name: Some("dict".to_string()),
7095 dependency_type: Some(fdecl::DependencyType::Strong),
7096 ..Default::default()
7097 }),
7098 ]);
7099 decl.children = Some(vec![
7100 fdecl::Child{
7101 name: Some("netstack".to_string()),
7102 url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
7103 startup: Some(fdecl::StartupMode::Eager),
7104 on_terminate: None,
7105 environment: None,
7106 ..Default::default()
7107 },
7108 ]);
7109 decl.collections = Some(vec![
7110 fdecl::Collection{
7111 name: Some("modular".to_string()),
7112 durability: Some(fdecl::Durability::Transient),
7113 environment: None,
7114 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7115 allow_long_names: None,
7116 ..Default::default()
7117 },
7118 ]);
7119 decl
7120 },
7121 result = Err(ErrorList::new(vec![
7122 Error::duplicate_field(DeclType::OfferProtocol, "target_name", "fuchsia.logger.LegacyLog"),
7124 Error::duplicate_field(DeclType::OfferDirectory, "target_name", "assets"),
7125 Error::duplicate_field(DeclType::OfferStorage, "target_name", "data"),
7126 Error::duplicate_field(DeclType::OfferRunner, "target_name", "duplicated"),
7127 Error::duplicate_field(DeclType::OfferResolver, "target_name", "duplicated"),
7128 Error::duplicate_field(DeclType::OfferEventStream, "target_name", "started"),
7129 Error::duplicate_field(DeclType::OfferDictionary, "target_name", "dict"),
7130 ])),
7131 },
7132 test_validate_offers_target_invalid => {
7133 input = {
7134 let mut decl = new_component_decl();
7135 decl.offers = Some(vec![
7136 fdecl::Offer::Service(fdecl::OfferService {
7137 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7138 source_name: Some("logger".to_string()),
7139 target: Some(fdecl::Ref::Child(
7140 fdecl::ChildRef {
7141 name: "netstack".to_string(),
7142 collection: None,
7143 }
7144 )),
7145 target_name: Some("fuchsia.logger.Log".to_string()),
7146 ..Default::default()
7147 }),
7148 fdecl::Offer::Service(fdecl::OfferService {
7149 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7150 source_name: Some("logger".to_string()),
7151 target: Some(fdecl::Ref::Collection(
7152 fdecl::CollectionRef { name: "modular".to_string(), }
7153 )),
7154 target_name: Some("fuchsia.logger.Log".to_string()),
7155 ..Default::default()
7156 }),
7157 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7158 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7159 source_name: Some("legacy_logger".to_string()),
7160 target: Some(fdecl::Ref::Child(
7161 fdecl::ChildRef {
7162 name: "netstack".to_string(),
7163 collection: None,
7164 }
7165 )),
7166 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7167 dependency_type: Some(fdecl::DependencyType::Weak),
7168 ..Default::default()
7169 }),
7170 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7171 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7172 source_name: Some("legacy_logger".to_string()),
7173 target: Some(fdecl::Ref::Collection(
7174 fdecl::CollectionRef { name: "modular".to_string(), }
7175 )),
7176 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7177 dependency_type: Some(fdecl::DependencyType::Strong),
7178 ..Default::default()
7179 }),
7180 fdecl::Offer::Directory(fdecl::OfferDirectory {
7181 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7182 source_name: Some("assets".to_string()),
7183 target: Some(fdecl::Ref::Child(
7184 fdecl::ChildRef {
7185 name: "netstack".to_string(),
7186 collection: None,
7187 }
7188 )),
7189 target_name: Some("data".to_string()),
7190 rights: Some(fio::Operations::CONNECT),
7191 subdir: None,
7192 dependency_type: Some(fdecl::DependencyType::Strong),
7193 ..Default::default()
7194 }),
7195 fdecl::Offer::Directory(fdecl::OfferDirectory {
7196 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7197 source_name: Some("assets".to_string()),
7198 target: Some(fdecl::Ref::Collection(
7199 fdecl::CollectionRef { name: "modular".to_string(), }
7200 )),
7201 target_name: Some("data".to_string()),
7202 rights: Some(fio::Operations::CONNECT),
7203 subdir: None,
7204 dependency_type: Some(fdecl::DependencyType::Weak),
7205 ..Default::default()
7206 }),
7207 fdecl::Offer::Storage(fdecl::OfferStorage {
7208 source_name: Some("data".to_string()),
7209 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7210 target: Some(fdecl::Ref::Child(
7211 fdecl::ChildRef {
7212 name: "netstack".to_string(),
7213 collection: None,
7214 }
7215 )),
7216 target_name: Some("data".to_string()),
7217 ..Default::default()
7218 }),
7219 fdecl::Offer::Storage(fdecl::OfferStorage {
7220 source_name: Some("data".to_string()),
7221 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7222 target: Some(fdecl::Ref::Collection(
7223 fdecl::CollectionRef { name: "modular".to_string(), }
7224 )),
7225 target_name: Some("data".to_string()),
7226 ..Default::default()
7227 }),
7228 fdecl::Offer::Runner(fdecl::OfferRunner {
7229 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7230 source_name: Some("elf".to_string()),
7231 target: Some(fdecl::Ref::Child(
7232 fdecl::ChildRef {
7233 name: "netstack".to_string(),
7234 collection: None,
7235 }
7236 )),
7237 target_name: Some("elf".to_string()),
7238 ..Default::default()
7239 }),
7240 fdecl::Offer::Runner(fdecl::OfferRunner {
7241 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7242 source_name: Some("elf".to_string()),
7243 target: Some(fdecl::Ref::Collection(
7244 fdecl::CollectionRef { name: "modular".to_string(), }
7245 )),
7246 target_name: Some("elf".to_string()),
7247 ..Default::default()
7248 }),
7249 fdecl::Offer::Resolver(fdecl::OfferResolver {
7250 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7251 source_name: Some("pkg".to_string()),
7252 target: Some(fdecl::Ref::Child(
7253 fdecl::ChildRef {
7254 name: "netstack".to_string(),
7255 collection: None,
7256 }
7257 )),
7258 target_name: Some("pkg".to_string()),
7259 ..Default::default()
7260 }),
7261 fdecl::Offer::Resolver(fdecl::OfferResolver {
7262 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7263 source_name: Some("pkg".to_string()),
7264 target: Some(fdecl::Ref::Collection(
7265 fdecl::CollectionRef { name: "modular".to_string(), }
7266 )),
7267 target_name: Some("pkg".to_string()),
7268 ..Default::default()
7269 }),
7270 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7271 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7272 source_name: Some("pkg".to_string()),
7273 target: Some(fdecl::Ref::Child(
7274 fdecl::ChildRef {
7275 name: "netstack".to_string(),
7276 collection: None,
7277 }
7278 )),
7279 target_name: Some("pkg".to_string()),
7280 dependency_type: Some(fdecl::DependencyType::Strong),
7281 ..Default::default()
7282 }),
7283 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7284 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7285 source_name: Some("pkg".to_string()),
7286 target: Some(fdecl::Ref::Collection(
7287 fdecl::CollectionRef { name: "modular".to_string(), }
7288 )),
7289 target_name: Some("pkg".to_string()),
7290 dependency_type: Some(fdecl::DependencyType::Strong),
7291 ..Default::default()
7292 }),
7293 ]);
7294 decl
7295 },
7296 result = Err(ErrorList::new(vec![
7297 Error::invalid_child(DeclType::OfferService, "target", "netstack"),
7298 Error::invalid_collection(DeclType::OfferService, "target", "modular"),
7299 Error::invalid_child(DeclType::OfferProtocol, "target", "netstack"),
7300 Error::invalid_collection(DeclType::OfferProtocol, "target", "modular"),
7301 Error::invalid_child(DeclType::OfferDirectory, "target", "netstack"),
7302 Error::invalid_collection(DeclType::OfferDirectory, "target", "modular"),
7303 Error::invalid_child(DeclType::OfferStorage, "target", "netstack"),
7304 Error::invalid_collection(DeclType::OfferStorage, "target", "modular"),
7305 Error::invalid_child(DeclType::OfferRunner, "target", "netstack"),
7306 Error::invalid_collection(DeclType::OfferRunner, "target", "modular"),
7307 Error::invalid_child(DeclType::OfferResolver, "target", "netstack"),
7308 Error::invalid_collection(DeclType::OfferResolver, "target", "modular"),
7309 Error::invalid_child(DeclType::OfferDictionary, "target", "netstack"),
7310 Error::invalid_collection(DeclType::OfferDictionary, "target", "modular"),
7311 ])),
7312 },
7313 test_validate_offers_target_dictionary => {
7314 input = fdecl::Component {
7315 offers: Some(vec![
7316 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7318 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7319 source_name: Some("p".to_string()),
7320 target: Some(fdecl::Ref::Capability(
7321 fdecl::CapabilityRef {
7322 name: "dict".into(),
7323 },
7324 )),
7325 target_name: Some("p".into()),
7326 dependency_type: Some(fdecl::DependencyType::Strong),
7327 ..Default::default()
7328 }),
7329 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7331 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7332 source_name: Some("p".to_string()),
7333 target: Some(fdecl::Ref::Capability(
7334 fdecl::CapabilityRef {
7335 name: "dynamic".into(),
7336 },
7337 )),
7338 target_name: Some("p".into()),
7339 dependency_type: Some(fdecl::DependencyType::Strong),
7340 ..Default::default()
7341 }),
7342 ]),
7343 capabilities: Some(vec![
7344 fdecl::Capability::Dictionary(fdecl::Dictionary {
7345 name: Some("dict".into()),
7346 ..Default::default()
7347 }),
7348 fdecl::Capability::Dictionary(fdecl::Dictionary {
7349 name: Some("dynamic".into()),
7350 source_path: Some("/out/dir".into()),
7351 ..Default::default()
7352 }),
7353 ]),
7354 ..Default::default()
7355 },
7356 result = Err(ErrorList::new(vec![
7357 Error::invalid_field(DeclType::OfferProtocol, "target"),
7358 ])),
7359 },
7360 test_validate_offers_invalid_source_collection => {
7361 input = {
7362 let mut decl = new_component_decl();
7363 decl.collections = Some(vec![
7364 fdecl::Collection {
7365 name: Some("col".to_string()),
7366 durability: Some(fdecl::Durability::Transient),
7367 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7368 allow_long_names: None,
7369 ..Default::default()
7370 }
7371 ]);
7372 decl.children = Some(vec![
7373 fdecl::Child {
7374 name: Some("child".to_string()),
7375 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7376 startup: Some(fdecl::StartupMode::Lazy),
7377 on_terminate: None,
7378 ..Default::default()
7379 }
7380 ]);
7381 decl.offers = Some(vec![
7382 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7383 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7384 source_name: Some("a".to_string()),
7385 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7386 target_name: Some("a".to_string()),
7387 dependency_type: Some(fdecl::DependencyType::Strong),
7388 ..Default::default()
7389 }),
7390 fdecl::Offer::Directory(fdecl::OfferDirectory {
7391 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7392 source_name: Some("b".to_string()),
7393 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7394 target_name: Some("b".to_string()),
7395 rights: Some(fio::Operations::CONNECT),
7396 subdir: None,
7397 dependency_type: Some(fdecl::DependencyType::Strong),
7398 ..Default::default()
7399 }),
7400 fdecl::Offer::Storage(fdecl::OfferStorage {
7401 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7402 source_name: Some("c".to_string()),
7403 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7404 target_name: Some("c".to_string()),
7405 ..Default::default()
7406 }),
7407 fdecl::Offer::Runner(fdecl::OfferRunner {
7408 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7409 source_name: Some("d".to_string()),
7410 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7411 target_name: Some("d".to_string()),
7412 ..Default::default()
7413 }),
7414 fdecl::Offer::Resolver(fdecl::OfferResolver {
7415 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7416 source_name: Some("e".to_string()),
7417 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7418 target_name: Some("e".to_string()),
7419 ..Default::default()
7420 }),
7421 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7422 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7423 source_name: Some("f".to_string()),
7424 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7425 target_name: Some("f".to_string()),
7426 dependency_type: Some(fdecl::DependencyType::Strong),
7427 ..Default::default()
7428 }),
7429 ]);
7430 decl
7431 },
7432 result = Err(ErrorList::new(vec![
7433 Error::invalid_field(DeclType::OfferProtocol, "source"),
7434 Error::invalid_field(DeclType::OfferDirectory, "source"),
7435 Error::invalid_field(DeclType::OfferStorage, "source"),
7436 Error::invalid_field(DeclType::OfferRunner, "source"),
7437 Error::invalid_field(DeclType::OfferResolver, "source"),
7438 Error::invalid_field(DeclType::OfferDictionary, "source"),
7439 ])),
7440 },
7441 test_validate_offers_source_collection => {
7442 input = {
7443 let mut decl = new_component_decl();
7444 decl.collections = Some(vec![
7445 fdecl::Collection {
7446 name: Some("col".to_string()),
7447 durability: Some(fdecl::Durability::Transient),
7448 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7449 allow_long_names: None,
7450 ..Default::default()
7451 }
7452 ]);
7453 decl.children = Some(vec![
7454 fdecl::Child {
7455 name: Some("child".to_string()),
7456 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7457 startup: Some(fdecl::StartupMode::Lazy),
7458 on_terminate: None,
7459 ..Default::default()
7460 }
7461 ]);
7462 decl.offers = Some(vec![
7463 fdecl::Offer::Service(fdecl::OfferService {
7464 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7465 source_name: Some("a".to_string()),
7466 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7467 target_name: Some("a".to_string()),
7468 ..Default::default()
7469 })
7470 ]);
7471 decl
7472 },
7473 result = Ok(()),
7474 },
7475 test_validate_offers_invalid_capability_from_self => {
7476 input = {
7477 let mut decl = new_component_decl();
7478 decl.children = Some(vec![
7479 fdecl::Child {
7480 name: Some("child".to_string()),
7481 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7482 startup: Some(fdecl::StartupMode::Lazy),
7483 ..Default::default()
7484 }
7485 ]);
7486 decl.offers = Some(vec![
7487 fdecl::Offer::Service(fdecl::OfferService {
7488 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7489 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7490 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7491 name: "child".into(),
7492 collection: None
7493 })),
7494 target_name: Some("foo".into()),
7495 ..Default::default()
7496 }),
7497 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7498 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7499 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7500 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7501 name: "child".into(),
7502 collection: None
7503 })),
7504 target_name: Some("bar".into()),
7505 dependency_type: Some(fdecl::DependencyType::Strong),
7506 ..Default::default()
7507 }),
7508 fdecl::Offer::Directory(fdecl::OfferDirectory {
7509 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7510 source_name: Some("dir".into()),
7511 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7512 name: "child".into(),
7513 collection: None
7514 })),
7515 target_name: Some("assets".into()),
7516 dependency_type: Some(fdecl::DependencyType::Strong),
7517 ..Default::default()
7518 }),
7519 fdecl::Offer::Runner(fdecl::OfferRunner {
7520 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7521 source_name: Some("source_elf".into()),
7522 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7523 name: "child".into(),
7524 collection: None
7525 })),
7526 target_name: Some("elf".into()),
7527 ..Default::default()
7528 }),
7529 fdecl::Offer::Resolver(fdecl::OfferResolver {
7530 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7531 source_name: Some("source_pkg".into()),
7532 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7533 name: "child".into(),
7534 collection: None
7535 })),
7536 target_name: Some("pkg".into()),
7537 ..Default::default()
7538 }),
7539 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7540 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7541 source_name: Some("source_dict".into()),
7542 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7543 name: "child".into(),
7544 collection: None
7545 })),
7546 target_name: Some("dict".into()),
7547 dependency_type: Some(fdecl::DependencyType::Strong),
7548 ..Default::default()
7549 }),
7550 fdecl::Offer::Storage(fdecl::OfferStorage {
7551 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7552 source_name: Some("source_storage".into()),
7553 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7554 name: "child".into(),
7555 collection: None
7556 })),
7557 target_name: Some("storage".into()),
7558 ..Default::default()
7559 }),
7560 fdecl::Offer::Config(fdecl::OfferConfiguration {
7561 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7562 source_name: Some("source_config".into()),
7563 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7564 name: "child".into(),
7565 collection: None
7566 })),
7567 target_name: Some("config".into()),
7568 ..Default::default()
7569 }),
7570 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7571 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7572 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7573 source_dictionary: Some("dict/inner".into()),
7574 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7575 name: "child".into(),
7576 collection: None
7577 })),
7578 target_name: Some("baz".into()),
7579 dependency_type: Some(fdecl::DependencyType::Strong),
7580 ..Default::default()
7581 }),
7582 ]);
7583 decl
7584 },
7585 result = Err(ErrorList::new(vec![
7586 Error::invalid_capability(
7587 DeclType::OfferService,
7588 "source",
7589 "fuchsia.some.library.SomeProtocol"),
7590 Error::invalid_capability(
7591 DeclType::OfferProtocol,
7592 "source",
7593 "fuchsia.some.library.SomeProtocol"),
7594 Error::invalid_capability(DeclType::OfferDirectory, "source", "dir"),
7595 Error::invalid_capability(DeclType::OfferRunner, "source", "source_elf"),
7596 Error::invalid_capability(DeclType::OfferResolver, "source", "source_pkg"),
7597 Error::invalid_capability(DeclType::OfferDictionary, "source", "source_dict"),
7598 Error::invalid_capability(DeclType::OfferStorage, "source", "source_storage"),
7599 Error::invalid_capability(DeclType::OfferConfig, "source", "source_config"),
7600 Error::invalid_capability(DeclType::OfferProtocol, "source", "dict"),
7601 ])),
7602 },
7603 test_validate_offers_long_dependency_cycle => {
7604 input = {
7605 let mut decl = new_component_decl();
7606 let dependencies = vec![
7607 ("d", "b"),
7608 ("a", "b"),
7609 ("b", "c"),
7610 ("b", "d"),
7611 ("c", "a"),
7612 ];
7613 let offers = dependencies.into_iter().map(|(from,to)|
7614 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7615 source: Some(fdecl::Ref::Child(
7616 fdecl::ChildRef { name: from.to_string(), collection: None },
7617 )),
7618 source_name: Some(format!("thing_{}", from)),
7619 target: Some(fdecl::Ref::Child(
7620 fdecl::ChildRef { name: to.to_string(), collection: None },
7621 )),
7622 target_name: Some(format!("thing_{}", from)),
7623 dependency_type: Some(fdecl::DependencyType::Strong),
7624 ..Default::default()
7625 })).collect();
7626 let children = ["a", "b", "c", "d"].iter().map(|name| {
7627 fdecl::Child {
7628 name: Some(name.to_string()),
7629 url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
7630 startup: Some(fdecl::StartupMode::Lazy),
7631 on_terminate: None,
7632 environment: None,
7633 ..Default::default()
7634 }
7635 }).collect();
7636 decl.offers = Some(offers);
7637 decl.children = Some(children);
7638 decl
7639 },
7640 result = Err(ErrorList::new(vec![
7641 Error::dependency_cycle(directed_graph::Error::CyclesDetected([vec!["child a", "child b", "child c", "child a"], vec!["child b", "child d", "child b"]].iter().cloned().collect()).format_cycle()),
7642 ])),
7643 },
7644 test_validate_offers_not_required_invalid_source_service => {
7645 input = {
7646 let mut decl = generate_offer_different_source_and_availability_decl(
7647 |source, availability, target_name|
7648 fdecl::Offer::Service(fdecl::OfferService {
7649 source: Some(source),
7650 source_name: Some("fuchsia.examples.Echo".to_string()),
7651 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7652 name: "sink".to_string(),
7653 collection: None,
7654 })),
7655 target_name: Some(target_name.into()),
7656 availability: Some(availability),
7657 ..Default::default()
7658 })
7659 );
7660 decl.capabilities = Some(vec![
7661 fdecl::Capability::Service(fdecl::Service {
7662 name: Some("fuchsia.examples.Echo".to_string()),
7663 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
7664 ..Default::default()
7665 }),
7666 ]);
7667 decl
7668 },
7669 result = {
7670 Err(ErrorList::new(vec![
7671 Error::availability_must_be_optional(
7672 DeclType::OfferService,
7673 "availability",
7674 Some(&"fuchsia.examples.Echo".to_string()),
7675 ),
7676 Error::availability_must_be_optional(
7677 DeclType::OfferService,
7678 "availability",
7679 Some(&"fuchsia.examples.Echo".to_string()),
7680 ),
7681 ]))
7682 },
7683 },
7684 test_validate_offers_not_required_invalid_source_protocol => {
7685 input = {
7686 let mut decl = generate_offer_different_source_and_availability_decl(
7687 |source, availability, target_name|
7688 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7689 source: Some(source),
7690 source_name: Some("fuchsia.examples.Echo".to_string()),
7691 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7692 name: "sink".to_string(),
7693 collection: None,
7694 })),
7695 target_name: Some(target_name.into()),
7696 dependency_type: Some(fdecl::DependencyType::Strong),
7697 availability: Some(availability),
7698 ..Default::default()
7699 })
7700 );
7701 decl.capabilities = Some(vec![
7702 fdecl::Capability::Protocol(fdecl::Protocol {
7703 name: Some("fuchsia.examples.Echo".to_string()),
7704 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
7705 ..Default::default()
7706 }),
7707 ]);
7708 decl
7709 },
7710 result = {
7711 Err(ErrorList::new(vec![
7712 Error::availability_must_be_optional(
7713 DeclType::OfferProtocol,
7714 "availability",
7715 Some(&"fuchsia.examples.Echo".to_string()),
7716 ),
7717 Error::availability_must_be_optional(
7718 DeclType::OfferProtocol,
7719 "availability",
7720 Some(&"fuchsia.examples.Echo".to_string()),
7721 ),
7722 ]))
7723 },
7724 },
7725 test_validate_offers_not_required_invalid_source_directory => {
7726 input = {
7727 let mut decl = generate_offer_different_source_and_availability_decl(
7728 |source, availability, target_name|
7729 fdecl::Offer::Directory(fdecl::OfferDirectory {
7730 source: Some(source),
7731 source_name: Some("assets".to_string()),
7732 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7733 name: "sink".to_string(),
7734 collection: None,
7735 })),
7736 target_name: Some(target_name.into()),
7737 rights: Some(fio::Operations::CONNECT),
7738 subdir: None,
7739 dependency_type: Some(fdecl::DependencyType::Weak),
7740 availability: Some(availability),
7741 ..Default::default()
7742 })
7743 );
7744 decl.capabilities = Some(vec![
7745 fdecl::Capability::Directory(fdecl::Directory {
7746 name: Some("assets".to_string()),
7747 source_path: Some("/assets".to_string()),
7748 rights: Some(fio::Operations::CONNECT),
7749 ..Default::default()
7750 }),
7751 ]);
7752 decl
7753 },
7754 result = {
7755 Err(ErrorList::new(vec![
7756 Error::availability_must_be_optional(
7757 DeclType::OfferDirectory,
7758 "availability",
7759 Some(&"assets".to_string()),
7760 ),
7761 Error::availability_must_be_optional(
7762 DeclType::OfferDirectory,
7763 "availability",
7764 Some(&"assets".to_string()),
7765 ),
7766 ]))
7767 },
7768 },
7769 test_validate_offers_not_required_invalid_source_storage => {
7770 input = {
7771 let mut decl = new_component_decl();
7772 decl.children = Some(vec![
7773 fdecl::Child {
7774 name: Some("sink".to_string()),
7775 url: Some("fuchsia-pkg://fuchsia.com/sink#meta/sink.cm".to_string()),
7776 startup: Some(fdecl::StartupMode::Lazy),
7777 on_terminate: None,
7778 environment: None,
7779 ..Default::default()
7780 },
7781 ]);
7782 decl.capabilities = Some(vec![
7783 fdecl::Capability::Storage(fdecl::Storage {
7784 name: Some("data".to_string()),
7785 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
7786 backing_dir: Some("minfs".to_string()),
7787 subdir: None,
7788 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
7789 ..Default::default()
7790 }),
7791 ]);
7792 let new_offer = |source: fdecl::Ref, availability: fdecl::Availability,
7793 target_name: &str|
7794 {
7795 fdecl::Offer::Storage(fdecl::OfferStorage {
7796 source: Some(source),
7797 source_name: Some("data".to_string()),
7798 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7799 name: "sink".to_string(),
7800 collection: None,
7801 })),
7802 target_name: Some(target_name.into()),
7803 availability: Some(availability),
7804 ..Default::default()
7805 })
7806 };
7807 decl.offers = Some(vec![
7808 new_offer(
7811 fdecl::Ref::Parent(fdecl::ParentRef {}),
7812 fdecl::Availability::Required,
7813 "data0",
7814 ),
7815 new_offer(
7816 fdecl::Ref::Parent(fdecl::ParentRef {}),
7817 fdecl::Availability::Optional,
7818 "data1",
7819 ),
7820 new_offer(
7821 fdecl::Ref::Parent(fdecl::ParentRef {}),
7822 fdecl::Availability::SameAsTarget,
7823 "data2",
7824 ),
7825 new_offer(
7826 fdecl::Ref::VoidType(fdecl::VoidRef {}),
7827 fdecl::Availability::Optional,
7828 "data3",
7829 ),
7830 new_offer(
7833 fdecl::Ref::Self_(fdecl::SelfRef {}),
7834 fdecl::Availability::Optional,
7835 "data4",
7836 ),
7837 new_offer(
7838 fdecl::Ref::Self_(fdecl::SelfRef {}),
7839 fdecl::Availability::SameAsTarget,
7840 "data5",
7841 ),
7842 new_offer(
7844 fdecl::Ref::VoidType(fdecl::VoidRef {}),
7845 fdecl::Availability::Required,
7846 "data6",
7847 ),
7848 new_offer(
7849 fdecl::Ref::VoidType(fdecl::VoidRef {}),
7850 fdecl::Availability::SameAsTarget,
7851 "data7",
7852 ),
7853 ]);
7854 decl
7855 },
7856 result = {
7857 Err(ErrorList::new(vec![
7858 Error::availability_must_be_optional(
7859 DeclType::OfferStorage,
7860 "availability",
7861 Some(&"data".to_string()),
7862 ),
7863 Error::availability_must_be_optional(
7864 DeclType::OfferStorage,
7865 "availability",
7866 Some(&"data".to_string()),
7867 ),
7868 ]))
7869 },
7870 },
7871
7872 test_validate_offers_valid_service_aggregation => {
7873 input = {
7874 let mut decl = new_component_decl();
7875 decl.offers = Some(vec![
7876 fdecl::Offer::Service(fdecl::OfferService {
7877 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
7878 name: "coll_a".to_string()
7879 })),
7880 source_name: Some("fuchsia.logger.Log".to_string()),
7881 target: Some(fdecl::Ref::Child(
7882 fdecl::ChildRef {
7883 name: "child_c".to_string(),
7884 collection: None,
7885 }
7886 )),
7887 target_name: Some("fuchsia.logger.Log".to_string()),
7888 source_instance_filter: None,
7889 ..Default::default()
7890 }),
7891 fdecl::Offer::Service(fdecl::OfferService {
7892 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
7893 name: "coll_b".to_string()
7894 })),
7895 source_name: Some("fuchsia.logger.Log".to_string()),
7896 target: Some(fdecl::Ref::Child(
7897 fdecl::ChildRef {
7898 name: "child_c".to_string(),
7899 collection: None,
7900 }
7901 )),
7902 target_name: Some("fuchsia.logger.Log".to_string()),
7903 source_instance_filter: Some(vec!["a_different_default".to_string()]),
7904 ..Default::default()
7905 })
7906 ]);
7907 decl.children = Some(vec![
7908 fdecl::Child {
7909 name: Some("child_c".to_string()),
7910 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
7911 startup: Some(fdecl::StartupMode::Lazy),
7912 ..Default::default()
7913 },
7914 ]);
7915 decl.collections = Some(vec![
7916 fdecl::Collection {
7917 name: Some("coll_a".into()),
7918 durability: Some(fdecl::Durability::Transient),
7919 ..Default::default()
7920 },
7921 fdecl::Collection {
7922 name: Some("coll_b".into()),
7923 durability: Some(fdecl::Durability::Transient),
7924 ..Default::default()
7925 },
7926 ]);
7927 decl
7928 },
7929 result = Ok(()),
7930 },
7931
7932 test_validate_source_dictionary => {
7934 input = fdecl::Component {
7935 program: Some(fdecl::Program {
7936 runner: Some("elf".into()),
7937 info: Some(fdata::Dictionary {
7938 entries: None,
7939 ..Default::default()
7940 }),
7941 ..Default::default()
7942 }),
7943 uses: Some(vec![
7944 fdecl::Use::Protocol(fdecl::UseProtocol {
7945 dependency_type: Some(fdecl::DependencyType::Strong),
7946 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
7947 source_dictionary: Some("bad//".into()),
7948 source_name: Some("foo".into()),
7949 target_path: Some("/svc/foo".into()),
7950 ..Default::default()
7951 }),
7952 ]),
7953 exposes: Some(vec![
7954 fdecl::Expose::Directory(fdecl::ExposeDirectory {
7955 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7956 name: "missing".into(),
7957 collection: None,
7958 })),
7959 source_dictionary: Some("in/dict".into()),
7960 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
7961 source_name: Some("foo".into()),
7962 target_name: Some("bar".into()),
7963 ..Default::default()
7964 }),
7965 ]),
7966 offers: Some(vec![
7967 fdecl::Offer::Service(fdecl::OfferService {
7968 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
7969 source_dictionary: Some("bad//".into()),
7970 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7971 name: "child".into(),
7972 collection: None,
7973 })),
7974 source_name: Some("foo".into()),
7975 target_name: Some("bar".into()),
7976 ..Default::default()
7977 }),
7978 ]),
7979 children: Some(vec![
7980 fdecl::Child {
7981 name: Some("child".into()),
7982 url: Some("fuchsia-pkg://child".into()),
7983 startup: Some(fdecl::StartupMode::Lazy),
7984 ..Default::default()
7985 },
7986 ]),
7987 ..Default::default()
7988 },
7989 result = Err(ErrorList::new(vec![
7990 Error::invalid_field(DeclType::UseProtocol, "source_dictionary"),
7991 Error::invalid_child(DeclType::ExposeDirectory, "source", "missing"),
7992 Error::invalid_field(DeclType::OfferService, "source_dictionary"),
7993 ])),
7994 },
7995 test_validate_dictionary_too_long => {
7996 input = fdecl::Component {
7997 program: Some(fdecl::Program {
7998 runner: Some("elf".into()),
7999 info: Some(fdata::Dictionary {
8000 entries: None,
8001 ..Default::default()
8002 }),
8003 ..Default::default()
8004 }),
8005 uses: Some(vec![
8006 fdecl::Use::Protocol(fdecl::UseProtocol {
8007 dependency_type: Some(fdecl::DependencyType::Strong),
8008 source: Some(fdecl::Ref::Parent( fdecl::ParentRef {} )),
8009 source_dictionary: Some("a".repeat(4096)),
8010 source_name: Some("foo".into()),
8011 target_path: Some("/svc/foo".into()),
8012 ..Default::default()
8013 }),
8014 ]),
8015 ..Default::default()
8016 },
8017 result = Err(ErrorList::new(vec![
8018 Error::field_too_long(DeclType::UseProtocol, "source_dictionary"),
8019 ])),
8020 },
8021
8022 test_validate_environment_empty => {
8024 input = {
8025 let mut decl = new_component_decl();
8026 decl.environments = Some(vec![fdecl::Environment {
8027 name: None,
8028 extends: None,
8029 runners: None,
8030 resolvers: None,
8031 stop_timeout_ms: None,
8032 debug_capabilities: None,
8033 ..Default::default()
8034 }]);
8035 decl
8036 },
8037 result = Err(ErrorList::new(vec![
8038 Error::missing_field(DeclType::Environment, "name"),
8039 Error::missing_field(DeclType::Environment, "extends"),
8040 ])),
8041 },
8042
8043 test_validate_environment_no_stop_timeout => {
8044 input = {
8045 let mut decl = new_component_decl();
8046 decl.environments = Some(vec![fdecl::Environment {
8047 name: Some("env".to_string()),
8048 extends: Some(fdecl::EnvironmentExtends::None),
8049 runners: None,
8050 resolvers: None,
8051 stop_timeout_ms: None,
8052 ..Default::default()
8053 }]);
8054 decl
8055 },
8056 result = Err(ErrorList::new(vec![Error::missing_field(DeclType::Environment, "stop_timeout_ms")])),
8057 },
8058
8059 test_validate_environment_extends_stop_timeout => {
8060 input = { let mut decl = new_component_decl();
8061 decl.environments = Some(vec![fdecl::Environment {
8062 name: Some("env".to_string()),
8063 extends: Some(fdecl::EnvironmentExtends::Realm),
8064 runners: None,
8065 resolvers: None,
8066 stop_timeout_ms: None,
8067 ..Default::default()
8068 }]);
8069 decl
8070 },
8071 result = Ok(()),
8072 },
8073 test_validate_environment_long_identifiers => {
8074 input = {
8075 let mut decl = new_component_decl();
8076 decl.environments = Some(vec![fdecl::Environment {
8077 name: Some("a".repeat(256)),
8078 extends: Some(fdecl::EnvironmentExtends::None),
8079 runners: Some(vec![
8080 fdecl::RunnerRegistration {
8081 source_name: Some("a".repeat(256)),
8082 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8083 target_name: Some("a".repeat(256)),
8084 ..Default::default()
8085 },
8086 ]),
8087 resolvers: Some(vec![
8088 fdecl::ResolverRegistration {
8089 resolver: Some("a".repeat(256)),
8090 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8091 scheme: Some("a".repeat(256)),
8092 ..Default::default()
8093 },
8094 ]),
8095 stop_timeout_ms: Some(1234),
8096 ..Default::default()
8097 }]);
8098 decl
8099 },
8100 result = Err(ErrorList::new(vec![
8101 Error::field_too_long(DeclType::Environment, "name"),
8102 Error::field_too_long(DeclType::RunnerRegistration, "source_name"),
8103 Error::field_too_long(DeclType::RunnerRegistration, "target_name"),
8104 Error::field_too_long(DeclType::ResolverRegistration, "resolver"),
8105 Error::field_too_long(DeclType::ResolverRegistration, "scheme"),
8106 ])),
8107 },
8108 test_validate_environment_empty_runner_resolver_fields => {
8109 input = {
8110 let mut decl = new_component_decl();
8111 decl.environments = Some(vec![fdecl::Environment {
8112 name: Some("a".to_string()),
8113 extends: Some(fdecl::EnvironmentExtends::None),
8114 runners: Some(vec![
8115 fdecl::RunnerRegistration {
8116 source_name: None,
8117 source: None,
8118 target_name: None,
8119 ..Default::default()
8120 },
8121 ]),
8122 resolvers: Some(vec![
8123 fdecl::ResolverRegistration {
8124 resolver: None,
8125 source: None,
8126 scheme: None,
8127 ..Default::default()
8128 },
8129 ]),
8130 stop_timeout_ms: Some(1234),
8131 ..Default::default()
8132 }]);
8133 decl
8134 },
8135 result = Err(ErrorList::new(vec![
8136 Error::missing_field(DeclType::RunnerRegistration, "source_name"),
8137 Error::missing_field(DeclType::RunnerRegistration, "source"),
8138 Error::missing_field(DeclType::RunnerRegistration, "target_name"),
8139 Error::missing_field(DeclType::ResolverRegistration, "resolver"),
8140 Error::missing_field(DeclType::ResolverRegistration, "source"),
8141 Error::missing_field(DeclType::ResolverRegistration, "scheme"),
8142 ])),
8143 },
8144 test_validate_environment_invalid_fields => {
8145 input = {
8146 let mut decl = new_component_decl();
8147 decl.environments = Some(vec![fdecl::Environment {
8148 name: Some("a".to_string()),
8149 extends: Some(fdecl::EnvironmentExtends::None),
8150 runners: Some(vec![
8151 fdecl::RunnerRegistration {
8152 source_name: Some("^a".to_string()),
8153 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
8154 target_name: Some("%a".to_string()),
8155 ..Default::default()
8156 },
8157 ]),
8158 resolvers: Some(vec![
8159 fdecl::ResolverRegistration {
8160 resolver: Some("^a".to_string()),
8161 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
8162 scheme: Some("9scheme".to_string()),
8163 ..Default::default()
8164 },
8165 ]),
8166 stop_timeout_ms: Some(1234),
8167 ..Default::default()
8168 }]);
8169 decl
8170 },
8171 result = Err(ErrorList::new(vec![
8172 Error::invalid_field(DeclType::RunnerRegistration, "source_name"),
8173 Error::invalid_field(DeclType::RunnerRegistration, "source"),
8174 Error::invalid_field(DeclType::RunnerRegistration, "target_name"),
8175 Error::invalid_field(DeclType::ResolverRegistration, "resolver"),
8176 Error::invalid_field(DeclType::ResolverRegistration, "source"),
8177 Error::invalid_field(DeclType::ResolverRegistration, "scheme"),
8178 ])),
8179 },
8180 test_validate_environment_missing_runner => {
8181 input = {
8182 let mut decl = new_component_decl();
8183 decl.environments = Some(vec![fdecl::Environment {
8184 name: Some("a".to_string()),
8185 extends: Some(fdecl::EnvironmentExtends::None),
8186 runners: Some(vec![
8187 fdecl::RunnerRegistration {
8188 source_name: Some("dart".to_string()),
8189 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
8190 target_name: Some("dart".to_string()),
8191 ..Default::default()
8192 },
8193 ]),
8194 resolvers: None,
8195 stop_timeout_ms: Some(1234),
8196 ..Default::default()
8197 }]);
8198 decl
8199 },
8200 result = Err(ErrorList::new(vec![
8201 Error::invalid_runner(DeclType::RunnerRegistration, "source_name", "dart"),
8202 ])),
8203 },
8204 test_validate_environment_duplicate_registrations => {
8205 input = {
8206 let mut decl = new_component_decl();
8207 decl.environments = Some(vec![fdecl::Environment {
8208 name: Some("a".to_string()),
8209 extends: Some(fdecl::EnvironmentExtends::None),
8210 runners: Some(vec![
8211 fdecl::RunnerRegistration {
8212 source_name: Some("dart".to_string()),
8213 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8214 target_name: Some("dart".to_string()),
8215 ..Default::default()
8216 },
8217 fdecl::RunnerRegistration {
8218 source_name: Some("other-dart".to_string()),
8219 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8220 target_name: Some("dart".to_string()),
8221 ..Default::default()
8222 },
8223 ]),
8224 resolvers: Some(vec![
8225 fdecl::ResolverRegistration {
8226 resolver: Some("pkg_resolver".to_string()),
8227 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8228 scheme: Some("fuchsia-pkg".to_string()),
8229 ..Default::default()
8230 },
8231 fdecl::ResolverRegistration {
8232 resolver: Some("base_resolver".to_string()),
8233 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8234 scheme: Some("fuchsia-pkg".to_string()),
8235 ..Default::default()
8236 },
8237 ]),
8238 stop_timeout_ms: Some(1234),
8239 ..Default::default()
8240 }]);
8241 decl
8242 },
8243 result = Err(ErrorList::new(vec![
8244 Error::duplicate_field(DeclType::RunnerRegistration, "target_name", "dart"),
8245 Error::duplicate_field(DeclType::ResolverRegistration, "scheme", "fuchsia-pkg"),
8246 ])),
8247 },
8248 test_validate_environment_from_missing_child => {
8249 input = {
8250 let mut decl = new_component_decl();
8251 decl.environments = Some(vec![fdecl::Environment {
8252 name: Some("a".to_string()),
8253 extends: Some(fdecl::EnvironmentExtends::None),
8254 runners: Some(vec![
8255 fdecl::RunnerRegistration {
8256 source_name: Some("elf".to_string()),
8257 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8258 name: "missing".to_string(),
8259 collection: None,
8260 })),
8261 target_name: Some("elf".to_string()),
8262 ..Default::default()
8263 },
8264 ]),
8265 resolvers: Some(vec![
8266 fdecl::ResolverRegistration {
8267 resolver: Some("pkg_resolver".to_string()),
8268 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8269 name: "missing".to_string(),
8270 collection: None,
8271 })),
8272 scheme: Some("fuchsia-pkg".to_string()),
8273 ..Default::default()
8274 },
8275 ]),
8276 stop_timeout_ms: Some(1234),
8277 ..Default::default()
8278 }]);
8279 decl
8280 },
8281 result = Err(ErrorList::new(vec![
8282 Error::invalid_child(DeclType::RunnerRegistration, "source", "missing"),
8283 Error::invalid_child(DeclType::ResolverRegistration, "source", "missing"),
8284 ])),
8285 },
8286 test_validate_environment_runner_child_cycle => {
8287 input = {
8288 let mut decl = new_component_decl();
8289 decl.environments = Some(vec![fdecl::Environment {
8290 name: Some("env".to_string()),
8291 extends: Some(fdecl::EnvironmentExtends::None),
8292 runners: Some(vec![
8293 fdecl::RunnerRegistration {
8294 source_name: Some("elf".to_string()),
8295 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8296 name: "child".to_string(),
8297 collection: None,
8298 })),
8299 target_name: Some("elf".to_string()),
8300 ..Default::default()
8301 },
8302 ]),
8303 resolvers: None,
8304 stop_timeout_ms: Some(1234),
8305 ..Default::default()
8306 }]);
8307 decl.children = Some(vec![fdecl::Child {
8308 name: Some("child".to_string()),
8309 startup: Some(fdecl::StartupMode::Lazy),
8310 on_terminate: None,
8311 url: Some("fuchsia-pkg://child".to_string()),
8312 environment: Some("env".to_string()),
8313 ..Default::default()
8314 }]);
8315 decl
8316 },
8317 result = Err(ErrorList::new(vec![
8318 Error::dependency_cycle(
8319 directed_graph::Error::CyclesDetected([vec!["child child", "environment env", "child child"]].iter().cloned().collect()).format_cycle()
8320 ),
8321 ])),
8322 },
8323 test_validate_environment_resolver_child_cycle => {
8324 input = {
8325 let mut decl = new_component_decl();
8326 decl.environments = Some(vec![fdecl::Environment {
8327 name: Some("env".to_string()),
8328 extends: Some(fdecl::EnvironmentExtends::None),
8329 runners: None,
8330 resolvers: Some(vec![
8331 fdecl::ResolverRegistration {
8332 resolver: Some("pkg_resolver".to_string()),
8333 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8334 name: "child".to_string(),
8335 collection: None,
8336 })),
8337 scheme: Some("fuchsia-pkg".to_string()),
8338 ..Default::default()
8339 },
8340 ]),
8341 stop_timeout_ms: Some(1234),
8342 ..Default::default()
8343 }]);
8344 decl.children = Some(vec![fdecl::Child {
8345 name: Some("child".to_string()),
8346 startup: Some(fdecl::StartupMode::Lazy),
8347 on_terminate: None,
8348 url: Some("fuchsia-pkg://child".to_string()),
8349 environment: Some("env".to_string()),
8350 ..Default::default()
8351 }]);
8352 decl
8353 },
8354 result = Err(ErrorList::new(vec![
8355 Error::dependency_cycle(
8356 directed_graph::Error::CyclesDetected([vec!["child child", "environment env", "child child"]].iter().cloned().collect()).format_cycle()
8357 ),
8358 ])),
8359 },
8360 test_validate_environment_resolver_multiple_children_cycle => {
8361 input = {
8362 let mut decl = new_component_decl();
8363 decl.environments = Some(vec![fdecl::Environment {
8364 name: Some("env".to_string()),
8365 extends: Some(fdecl::EnvironmentExtends::None),
8366 runners: None,
8367 resolvers: Some(vec![
8368 fdecl::ResolverRegistration {
8369 resolver: Some("pkg_resolver".to_string()),
8370 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8371 name: "a".to_string(),
8372 collection: None,
8373 })),
8374 scheme: Some("fuchsia-pkg".to_string()),
8375 ..Default::default()
8376 },
8377 ]),
8378 stop_timeout_ms: Some(1234),
8379 ..Default::default()
8380 }]);
8381 decl.children = Some(vec![
8382 fdecl::Child {
8383 name: Some("a".to_string()),
8384 startup: Some(fdecl::StartupMode::Lazy),
8385 on_terminate: None,
8386 url: Some("fuchsia-pkg://child-a".to_string()),
8387 environment: None,
8388 ..Default::default()
8389 },
8390 fdecl::Child {
8391 name: Some("b".to_string()),
8392 startup: Some(fdecl::StartupMode::Lazy),
8393 on_terminate: None,
8394 url: Some("fuchsia-pkg://child-b".to_string()),
8395 environment: Some("env".to_string()),
8396 ..Default::default()
8397 },
8398 ]);
8399 decl.offers = Some(vec![fdecl::Offer::Service(fdecl::OfferService {
8400 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8401 name: "b".to_string(),
8402 collection: None,
8403 })),
8404 source_name: Some("thing".to_string()),
8405 target: Some(fdecl::Ref::Child(
8406 fdecl::ChildRef {
8407 name: "a".to_string(),
8408 collection: None,
8409 }
8410 )),
8411 target_name: Some("thing".to_string()),
8412 ..Default::default()
8413 })]);
8414 decl
8415 },
8416 result = Err(ErrorList::new(vec![
8417 Error::dependency_cycle(
8418 directed_graph::Error::CyclesDetected([vec!["child a", "environment env", "child b", "child a"]].iter().cloned().collect()).format_cycle()
8419 ),
8420 ])),
8421 },
8422 test_validate_environment_debug_empty => {
8423 input = {
8424 let mut decl = new_component_decl();
8425 decl.environments = Some(vec![
8426 fdecl::Environment {
8427 name: Some("a".to_string()),
8428 extends: Some(fdecl::EnvironmentExtends::None),
8429 stop_timeout_ms: Some(2),
8430 debug_capabilities:Some(vec![
8431 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8432 source: None,
8433 source_name: None,
8434 target_name: None,
8435 ..Default::default()
8436 }),
8437 ]),
8438 ..Default::default()
8439 }]);
8440 decl
8441 },
8442 result = Err(ErrorList::new(vec![
8443 Error::missing_field(DeclType::DebugProtocolRegistration, "source"),
8444 Error::missing_field(DeclType::DebugProtocolRegistration, "source_name"),
8445 Error::missing_field(DeclType::DebugProtocolRegistration, "target_name"),
8446 ])),
8447 },
8448 test_validate_environment_debug_log_identifier => {
8449 input = {
8450 let mut decl = new_component_decl();
8451 decl.environments = Some(vec![
8452 fdecl::Environment {
8453 name: Some("a".to_string()),
8454 extends: Some(fdecl::EnvironmentExtends::None),
8455 stop_timeout_ms: Some(2),
8456 debug_capabilities:Some(vec![
8457 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8458 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8459 name: "a".repeat(256),
8460 collection: None,
8461 })),
8462 source_name: Some(format!("{}", "a".repeat(256))),
8463 target_name: Some(format!("{}", "b".repeat(256))),
8464 ..Default::default()
8465 }),
8466 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8467 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8468 source_name: Some("a".to_string()),
8469 target_name: Some(format!("{}", "b".repeat(256))),
8470 ..Default::default()
8471 }),
8472 ]),
8473 ..Default::default()
8474 }]);
8475 decl
8476 },
8477 result = Err(ErrorList::new(vec![
8478 Error::field_too_long(DeclType::DebugProtocolRegistration, "source.child.name"),
8479 Error::field_too_long(DeclType::DebugProtocolRegistration, "source_name"),
8480 Error::field_too_long(DeclType::DebugProtocolRegistration, "target_name"),
8481 Error::field_too_long(DeclType::DebugProtocolRegistration, "target_name"),
8482 ])),
8483 },
8484 test_validate_environment_debug_log_extraneous => {
8485 input = {
8486 let mut decl = new_component_decl();
8487 decl.environments = Some(vec![
8488 fdecl::Environment {
8489 name: Some("a".to_string()),
8490 extends: Some(fdecl::EnvironmentExtends::None),
8491 stop_timeout_ms: Some(2),
8492 debug_capabilities:Some(vec![
8493 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8494 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8495 name: "logger".to_string(),
8496 collection: Some("modular".to_string()),
8497 })),
8498 source_name: Some("fuchsia.logger.Log".to_string()),
8499 target_name: Some("fuchsia.logger.Log".to_string()),
8500 ..Default::default()
8501 }),
8502 ]),
8503 ..Default::default()
8504 }]);
8505 decl
8506 },
8507 result = Err(ErrorList::new(vec![
8508 Error::extraneous_field(DeclType::DebugProtocolRegistration, "source.child.collection"),
8509 ])),
8510 },
8511 test_validate_environment_debug_log_invalid_identifiers => {
8512 input = {
8513 let mut decl = new_component_decl();
8514 decl.environments = Some(vec![
8515 fdecl::Environment {
8516 name: Some("a".to_string()),
8517 extends: Some(fdecl::EnvironmentExtends::None),
8518 stop_timeout_ms: Some(2),
8519 debug_capabilities:Some(vec![
8520 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8521 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8522 name: "^bad".to_string(),
8523 collection: None,
8524 })),
8525 source_name: Some("foo/".to_string()),
8526 target_name: Some("/".to_string()),
8527 ..Default::default()
8528 }),
8529 ]),
8530 ..Default::default()
8531 }]);
8532 decl
8533 },
8534 result = Err(ErrorList::new(vec![
8535 Error::invalid_field(DeclType::DebugProtocolRegistration, "source.child.name"),
8536 Error::invalid_field(DeclType::DebugProtocolRegistration, "source_name"),
8537 Error::invalid_field(DeclType::DebugProtocolRegistration, "target_name"),
8538 ])),
8539 },
8540 test_validate_environment_debug_log_invalid_child => {
8541 input = {
8542 let mut decl = new_component_decl();
8543 decl.environments = Some(vec![
8544 fdecl::Environment {
8545 name: Some("a".to_string()),
8546 extends: Some(fdecl::EnvironmentExtends::None),
8547 stop_timeout_ms: Some(2),
8548 debug_capabilities:Some(vec![
8549 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8550 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8551 name: "logger".to_string(),
8552 collection: None,
8553 })),
8554 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
8555 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
8556 ..Default::default()
8557 }),
8558 ]),
8559 ..Default::default()
8560 }]);
8561 decl.children = Some(vec![
8562 fdecl::Child {
8563 name: Some("netstack".to_string()),
8564 url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
8565 startup: Some(fdecl::StartupMode::Lazy),
8566 on_terminate: None,
8567 environment: None,
8568 ..Default::default()
8569 },
8570 ]);
8571 decl
8572 },
8573 result = Err(ErrorList::new(vec![
8574 Error::invalid_child(DeclType::DebugProtocolRegistration, "source", "logger"),
8575
8576 ])),
8577 },
8578 test_validate_environment_debug_source_capability => {
8579 input = {
8580 let mut decl = new_component_decl();
8581 decl.environments = Some(vec![
8582 fdecl::Environment {
8583 name: Some("a".to_string()),
8584 extends: Some(fdecl::EnvironmentExtends::None),
8585 stop_timeout_ms: Some(2),
8586 debug_capabilities:Some(vec![
8587 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8588 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
8589 name: "storage".to_string(),
8590 })),
8591 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
8592 target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
8593 ..Default::default()
8594 }),
8595 ]),
8596 ..Default::default()
8597 }]);
8598 decl
8599 },
8600 result = Err(ErrorList::new(vec![
8601 Error::invalid_field(DeclType::DebugProtocolRegistration, "source"),
8602 ])),
8603 },
8604
8605 test_validate_children_empty => {
8607 input = {
8608 let mut decl = new_component_decl();
8609 decl.children = Some(vec![fdecl::Child{
8610 name: None,
8611 url: None,
8612 startup: None,
8613 on_terminate: None,
8614 environment: None,
8615 ..Default::default()
8616 }]);
8617 decl
8618 },
8619 result = Err(ErrorList::new(vec![
8620 Error::missing_field(DeclType::Child, "name"),
8621 Error::missing_field(DeclType::Child, "url"),
8622 Error::missing_field(DeclType::Child, "startup"),
8623 ])),
8625 },
8626 test_validate_children_invalid_identifiers => {
8627 input = {
8628 let mut decl = new_component_decl();
8629 decl.children = Some(vec![fdecl::Child{
8630 name: Some("^bad".to_string()),
8631 url: Some("scheme://invalid-port:99999999/path#frag".to_string()),
8632 startup: Some(fdecl::StartupMode::Lazy),
8633 on_terminate: None,
8634 environment: None,
8635 ..Default::default()
8636 }]);
8637 decl
8638 },
8639 result = Err(ErrorList::new(vec![
8640 Error::invalid_field(DeclType::Child, "name"),
8641 Error::invalid_url(DeclType::Child, "url", "\"scheme://invalid-port:99999999/path#frag\": Malformed URL: InvalidPort."),
8642 ])),
8643 },
8644 test_validate_children_long_identifiers => {
8645 input = {
8646 let mut decl = new_component_decl();
8647 decl.children = Some(vec![fdecl::Child{
8648 name: Some("a".repeat(1025)),
8649 url: Some(format!("fuchsia-pkg://{}", "a".repeat(4083))),
8650 startup: Some(fdecl::StartupMode::Lazy),
8651 on_terminate: None,
8652 environment: Some("a".repeat(1025)),
8653 ..Default::default()
8654 }]);
8655 decl
8656 },
8657 result = Err(ErrorList::new(vec![
8658 Error::field_too_long(DeclType::Child, "name"),
8659 Error::field_too_long(DeclType::Child, "url"),
8660 Error::field_too_long(DeclType::Child, "environment"),
8661 Error::invalid_environment(DeclType::Child, "environment", "a".repeat(1025)),
8662 ])),
8663 },
8664 test_validate_child_references_unknown_env => {
8665 input = {
8666 let mut decl = new_component_decl();
8667 decl.children = Some(vec![fdecl::Child{
8668 name: Some("foo".to_string()),
8669 url: Some("fuchsia-pkg://foo".to_string()),
8670 startup: Some(fdecl::StartupMode::Lazy),
8671 on_terminate: None,
8672 environment: Some("test_env".to_string()),
8673 ..Default::default()
8674 }]);
8675 decl
8676 },
8677 result = Err(ErrorList::new(vec![
8678 Error::invalid_environment(DeclType::Child, "environment", "test_env"),
8679 ])),
8680 },
8681
8682 test_validate_collections_empty => {
8684 input = {
8685 let mut decl = new_component_decl();
8686 decl.collections = Some(vec![fdecl::Collection{
8687 name: None,
8688 durability: None,
8689 environment: None,
8690 allowed_offers: None,
8691 allow_long_names: None,
8692 ..Default::default()
8693 }]);
8694 decl
8695 },
8696 result = Err(ErrorList::new(vec![
8697 Error::missing_field(DeclType::Collection, "name"),
8698 Error::missing_field(DeclType::Collection, "durability"),
8699 ])),
8700 },
8701 test_validate_collections_invalid_identifiers => {
8702 input = {
8703 let mut decl = new_component_decl();
8704 decl.collections = Some(vec![fdecl::Collection{
8705 name: Some("^bad".to_string()),
8706 durability: Some(fdecl::Durability::Transient),
8707 environment: None,
8708 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
8709 allow_long_names: None,
8710 ..Default::default()
8711 }]);
8712 decl
8713 },
8714 result = Err(ErrorList::new(vec![
8715 Error::invalid_field(DeclType::Collection, "name"),
8716 ])),
8717 },
8718 test_validate_collections_long_identifiers => {
8719 input = {
8720 let mut decl = new_component_decl();
8721 decl.collections = Some(vec![fdecl::Collection{
8722 name: Some("a".repeat(1025)),
8723 durability: Some(fdecl::Durability::Transient),
8724 environment: None,
8725 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
8726 allow_long_names: None,
8727 ..Default::default()
8728 }]);
8729 decl
8730 },
8731 result = Err(ErrorList::new(vec![
8732 Error::field_too_long(DeclType::Collection, "name"),
8733 ])),
8734 },
8735 test_validate_collection_references_unknown_env => {
8736 input = {
8737 let mut decl = new_component_decl();
8738 decl.collections = Some(vec![fdecl::Collection {
8739 name: Some("foo".to_string()),
8740 durability: Some(fdecl::Durability::Transient),
8741 environment: Some("test_env".to_string()),
8742 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
8743 allow_long_names: None,
8744 ..Default::default()
8745 }]);
8746 decl
8747 },
8748 result = Err(ErrorList::new(vec![
8749 Error::invalid_environment(DeclType::Collection, "environment", "test_env"),
8750 ])),
8751 },
8752
8753 test_validate_capabilities_empty => {
8755 input = {
8756 let mut decl = new_component_decl();
8757 decl.capabilities = Some(vec![
8758 fdecl::Capability::Service(fdecl::Service {
8759 name: None,
8760 source_path: None,
8761 ..Default::default()
8762 }),
8763 fdecl::Capability::Protocol(fdecl::Protocol {
8764 name: None,
8765 source_path: None,
8766 ..Default::default()
8767 }),
8768 fdecl::Capability::Directory(fdecl::Directory {
8769 name: None,
8770 source_path: None,
8771 rights: None,
8772 ..Default::default()
8773 }),
8774 fdecl::Capability::Storage(fdecl::Storage {
8775 name: None,
8776 source: None,
8777 backing_dir: None,
8778 subdir: None,
8779 storage_id: None,
8780 ..Default::default()
8781 }),
8782 fdecl::Capability::Runner(fdecl::Runner {
8783 name: None,
8784 source_path: None,
8785 ..Default::default()
8786 }),
8787 fdecl::Capability::Resolver(fdecl::Resolver {
8788 name: None,
8789 source_path: None,
8790 ..Default::default()
8791 }),
8792 fdecl::Capability::Dictionary(fdecl::Dictionary {
8793 ..Default::default()
8794 }),
8795 ]);
8796 decl
8797 },
8798 result = Err(ErrorList::new(vec![
8799 Error::missing_field(DeclType::Dictionary, "name"),
8800 Error::missing_field(DeclType::Service, "name"),
8801 Error::missing_field(DeclType::Service, "source_path"),
8802 Error::missing_field(DeclType::Protocol, "name"),
8803 Error::missing_field(DeclType::Protocol, "source_path"),
8804 Error::missing_field(DeclType::Directory, "name"),
8805 Error::missing_field(DeclType::Directory, "source_path"),
8806 Error::missing_field(DeclType::Directory, "rights"),
8807 Error::missing_field(DeclType::Storage, "source"),
8808 Error::missing_field(DeclType::Storage, "name"),
8809 Error::missing_field(DeclType::Storage, "storage_id"),
8810 Error::missing_field(DeclType::Storage, "backing_dir"),
8811 Error::missing_field(DeclType::Runner, "name"),
8812 Error::missing_field(DeclType::Runner, "source_path"),
8813 Error::missing_field(DeclType::Resolver, "name"),
8814 Error::missing_field(DeclType::Resolver, "source_path"),
8815 ])),
8816 },
8817 test_validate_capabilities_invalid_identifiers => {
8818 input = {
8819 let mut decl = new_component_decl();
8820 decl.capabilities = Some(vec![
8821 fdecl::Capability::Service(fdecl::Service {
8822 name: Some("^bad".to_string()),
8823 source_path: Some("&bad".to_string()),
8824 ..Default::default()
8825 }),
8826 fdecl::Capability::Protocol(fdecl::Protocol {
8827 name: Some("^bad".to_string()),
8828 source_path: Some("&bad".to_string()),
8829 ..Default::default()
8830 }),
8831 fdecl::Capability::Directory(fdecl::Directory {
8832 name: Some("^bad".to_string()),
8833 source_path: Some("&bad".to_string()),
8834 rights: Some(fio::Operations::CONNECT),
8835 ..Default::default()
8836 }),
8837 fdecl::Capability::Storage(fdecl::Storage {
8838 name: Some("^bad".to_string()),
8839 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
8840 name: "/bad".to_string()
8841 })),
8842 backing_dir: Some("&bad".to_string()),
8843 subdir: None,
8844 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
8845 ..Default::default()
8846 }),
8847 fdecl::Capability::Runner(fdecl::Runner {
8848 name: Some("^bad".to_string()),
8849 source_path: Some("&bad".to_string()),
8850 ..Default::default()
8851 }),
8852 fdecl::Capability::Resolver(fdecl::Resolver {
8853 name: Some("^bad".to_string()),
8854 source_path: Some("&bad".to_string()),
8855 ..Default::default()
8856 }),
8857 fdecl::Capability::Dictionary(fdecl::Dictionary {
8858 name: Some("^bad".to_string()),
8859 ..Default::default()
8860 }),
8861 ]);
8862 decl
8863 },
8864 result = Err(ErrorList::new(vec![
8865 Error::invalid_field(DeclType::Dictionary, "name"),
8866 Error::invalid_field(DeclType::Service, "name"),
8867 Error::invalid_field(DeclType::Service, "source_path"),
8868 Error::invalid_field(DeclType::Protocol, "name"),
8869 Error::invalid_field(DeclType::Protocol, "source_path"),
8870 Error::invalid_field(DeclType::Directory, "name"),
8871 Error::invalid_field(DeclType::Directory, "source_path"),
8872 Error::invalid_field(DeclType::Storage, "source"),
8873 Error::invalid_field(DeclType::Storage, "name"),
8874 Error::invalid_field(DeclType::Storage, "backing_dir"),
8875 Error::invalid_field(DeclType::Runner, "name"),
8876 Error::invalid_field(DeclType::Runner, "source_path"),
8877 Error::invalid_field(DeclType::Resolver, "name"),
8878 Error::invalid_field(DeclType::Resolver, "source_path"),
8879 ])),
8880 },
8881 test_validate_capabilities_invalid_child => {
8882 input = {
8883 let mut decl = new_component_decl();
8884 decl.capabilities = Some(vec![
8885 fdecl::Capability::Storage(fdecl::Storage {
8886 name: Some("foo".to_string()),
8887 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
8888 name: "invalid".to_string(),
8889 })),
8890 backing_dir: Some("foo".to_string()),
8891 subdir: None,
8892 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
8893 ..Default::default()
8894 }),
8895 ]);
8896 decl
8897 },
8898 result = Err(ErrorList::new(vec![
8899 Error::invalid_field(DeclType::Storage, "source"),
8900 ])),
8901 },
8902 test_validate_capabilities_long_identifiers => {
8903 input = {
8904 let mut decl = new_component_decl();
8905 decl.capabilities = Some(vec![
8906 fdecl::Capability::Service(fdecl::Service {
8907 name: Some("a".repeat(256)),
8908 source_path: Some("/c".repeat(2048)),
8909 ..Default::default()
8910 }),
8911 fdecl::Capability::Protocol(fdecl::Protocol {
8912 name: Some("a".repeat(256)),
8913 source_path: Some("/c".repeat(2048)),
8914 ..Default::default()
8915 }),
8916 fdecl::Capability::Directory(fdecl::Directory {
8917 name: Some("a".repeat(256)),
8918 source_path: Some("/c".repeat(2048)),
8919 rights: Some(fio::Operations::CONNECT),
8920 ..Default::default()
8921 }),
8922 fdecl::Capability::Storage(fdecl::Storage {
8923 name: Some("a".repeat(256)),
8924 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8925 name: "b".repeat(256),
8926 collection: None,
8927 })),
8928 backing_dir: Some(format!("{}", "c".repeat(256))),
8929 subdir: None,
8930 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
8931 ..Default::default()
8932 }),
8933 fdecl::Capability::Runner(fdecl::Runner {
8934 name: Some("a".repeat(256)),
8935 source_path: Some("/c".repeat(2048)),
8936 ..Default::default()
8937 }),
8938 fdecl::Capability::Resolver(fdecl::Resolver {
8939 name: Some("a".repeat(256)),
8940 source_path: Some("/c".repeat(2048)),
8941 ..Default::default()
8942 }),
8943 fdecl::Capability::Dictionary(fdecl::Dictionary {
8944 name: Some("a".repeat(256)),
8945 ..Default::default()
8946 }),
8947 ]);
8948 decl
8949 },
8950 result = Err(ErrorList::new(vec![
8951 Error::field_too_long(DeclType::Dictionary, "name"),
8952 Error::field_too_long(DeclType::Service, "name"),
8953 Error::field_too_long(DeclType::Service, "source_path"),
8954 Error::field_too_long(DeclType::Protocol, "name"),
8955 Error::field_too_long(DeclType::Protocol, "source_path"),
8956 Error::field_too_long(DeclType::Directory, "name"),
8957 Error::field_too_long(DeclType::Directory, "source_path"),
8958 Error::field_too_long(DeclType::Storage, "source.child.name"),
8959 Error::field_too_long(DeclType::Storage, "name"),
8960 Error::field_too_long(DeclType::Storage, "backing_dir"),
8961 Error::field_too_long(DeclType::Runner, "name"),
8962 Error::field_too_long(DeclType::Runner, "source_path"),
8963 Error::field_too_long(DeclType::Resolver, "name"),
8964 Error::field_too_long(DeclType::Resolver, "source_path"),
8965 ])),
8966 },
8967 test_validate_capabilities_duplicate_name => {
8968 input = {
8969 let mut decl = new_component_decl();
8970 decl.capabilities = Some(vec![
8971 fdecl::Capability::Service(fdecl::Service {
8972 name: Some("service".to_string()),
8973 source_path: Some("/service".to_string()),
8974 ..Default::default()
8975 }),
8976 fdecl::Capability::Service(fdecl::Service {
8977 name: Some("service".to_string()),
8978 source_path: Some("/service".to_string()),
8979 ..Default::default()
8980 }),
8981 fdecl::Capability::Protocol(fdecl::Protocol {
8982 name: Some("protocol".to_string()),
8983 source_path: Some("/protocol".to_string()),
8984 ..Default::default()
8985 }),
8986 fdecl::Capability::Protocol(fdecl::Protocol {
8987 name: Some("protocol".to_string()),
8988 source_path: Some("/protocol".to_string()),
8989 ..Default::default()
8990 }),
8991 fdecl::Capability::Directory(fdecl::Directory {
8992 name: Some("directory".to_string()),
8993 source_path: Some("/directory".to_string()),
8994 rights: Some(fio::Operations::CONNECT),
8995 ..Default::default()
8996 }),
8997 fdecl::Capability::Directory(fdecl::Directory {
8998 name: Some("directory".to_string()),
8999 source_path: Some("/directory".to_string()),
9000 rights: Some(fio::Operations::CONNECT),
9001 ..Default::default()
9002 }),
9003 fdecl::Capability::Storage(fdecl::Storage {
9004 name: Some("storage".to_string()),
9005 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
9006 backing_dir: Some("directory".to_string()),
9007 subdir: None,
9008 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9009 ..Default::default()
9010 }),
9011 fdecl::Capability::Storage(fdecl::Storage {
9012 name: Some("storage".to_string()),
9013 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
9014 backing_dir: Some("directory".to_string()),
9015 subdir: None,
9016 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9017 ..Default::default()
9018 }),
9019 fdecl::Capability::Runner(fdecl::Runner {
9020 name: Some("runner".to_string()),
9021 source_path: Some("/runner".to_string()),
9022 ..Default::default()
9023 }),
9024 fdecl::Capability::Runner(fdecl::Runner {
9025 name: Some("runner".to_string()),
9026 source_path: Some("/runner".to_string()),
9027 ..Default::default()
9028 }),
9029 fdecl::Capability::Resolver(fdecl::Resolver {
9030 name: Some("resolver".to_string()),
9031 source_path: Some("/resolver".to_string()),
9032 ..Default::default()
9033 }),
9034 fdecl::Capability::Resolver(fdecl::Resolver {
9035 name: Some("resolver".to_string()),
9036 source_path: Some("/resolver".to_string()),
9037 ..Default::default()
9038 }),
9039 fdecl::Capability::Dictionary(fdecl::Dictionary {
9040 name: Some("dictionary".to_string()),
9041 ..Default::default()
9042 }),
9043 fdecl::Capability::Dictionary(fdecl::Dictionary {
9044 name: Some("dictionary".to_string()),
9045 ..Default::default()
9046 }),
9047 ]);
9048 decl
9049 },
9050 result = Err(ErrorList::new(vec![
9051 Error::duplicate_field(DeclType::Dictionary, "name", "dictionary"),
9052 Error::duplicate_field(DeclType::Service, "name", "service"),
9053 Error::duplicate_field(DeclType::Protocol, "name", "protocol"),
9054 Error::duplicate_field(DeclType::Directory, "name", "directory"),
9055 Error::duplicate_field(DeclType::Storage, "name", "storage"),
9056 Error::duplicate_field(DeclType::Runner, "name", "runner"),
9057 Error::duplicate_field(DeclType::Resolver, "name", "resolver"),
9058 ])),
9059 },
9060 test_validate_invalid_service_aggregation_conflicting_filter => {
9061 input = {
9062 let mut decl = new_component_decl();
9063 decl.offers = Some(vec![
9064 fdecl::Offer::Service(fdecl::OfferService {
9065 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9066 name: "coll_a".to_string()
9067 })),
9068 source_name: Some("fuchsia.logger.Log".to_string()),
9069 target: Some(fdecl::Ref::Child(
9070 fdecl::ChildRef {
9071 name: "child_c".to_string(),
9072 collection: None,
9073 }
9074 )),
9075 target_name: Some("fuchsia.logger.Log1".to_string()),
9076 source_instance_filter: Some(vec!["default".to_string()]),
9077 ..Default::default()
9078 }),
9079 fdecl::Offer::Service(fdecl::OfferService {
9080 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9081 name: "coll_b".to_string()
9082 })),
9083 source_name: Some("fuchsia.logger.Log".to_string()),
9084 target: Some(fdecl::Ref::Child(
9085 fdecl::ChildRef {
9086 name: "child_c".to_string(),
9087 collection: None,
9088 }
9089 )),
9090 target_name: Some("fuchsia.logger.Log1".to_string()),
9091 source_instance_filter: Some(vec!["default".to_string()]),
9092 ..Default::default()
9093 }),
9094 ]);
9095 decl.collections = Some(vec![
9096 fdecl::Collection {
9097 name: Some("coll_a".to_string()),
9098 durability: Some(fdecl::Durability::Transient),
9099 ..Default::default()
9100 },
9101 fdecl::Collection {
9102 name: Some("coll_b".to_string()),
9103 durability: Some(fdecl::Durability::Transient),
9104 ..Default::default()
9105 },
9106 ]);
9107 decl.children = Some(vec![
9108 fdecl::Child {
9109 name: Some("child_c".to_string()),
9110 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9111 startup: Some(fdecl::StartupMode::Lazy),
9112 ..Default::default()
9113 },
9114 ]);
9115 decl
9116 },
9117 result = Err(ErrorList::new(vec![
9118 Error::invalid_aggregate_offer("Conflicting source_instance_filter in aggregate \
9119 service offer, instance_name 'default' seen in filter lists multiple times"),
9120 ])),
9121 },
9122
9123 test_validate_invalid_service_aggregation_conflicting_source_name => {
9124 input = {
9125 let mut decl = new_component_decl();
9126 decl.offers = Some(vec![
9127 fdecl::Offer::Service(fdecl::OfferService {
9128 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9129 name: "coll_a".into()
9130 })),
9131 source_name: Some("fuchsia.logger.Log".to_string()),
9132 target: Some(fdecl::Ref::Child(
9133 fdecl::ChildRef {
9134 name: "child_c".to_string(),
9135 collection: None,
9136 }
9137 )),
9138 target_name: Some("fuchsia.logger.Log2".to_string()),
9139 source_instance_filter: Some(vec!["default2".to_string()]),
9140 ..Default::default()
9141 }),
9142 fdecl::Offer::Service(fdecl::OfferService {
9143 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9144 name: "coll_b".into()
9145 })),
9146 source_name: Some("fuchsia.logger.LogAlt".to_string()),
9147 target: Some(fdecl::Ref::Child(
9148 fdecl::ChildRef {
9149 name: "child_c".to_string(),
9150 collection: None,
9151 }
9152 )),
9153 target_name: Some("fuchsia.logger.Log2".to_string()),
9154 source_instance_filter: Some(vec!["default".to_string()]),
9155 ..Default::default()
9156 })
9157 ]);
9158 decl.collections = Some(vec![
9159 fdecl::Collection {
9160 name: Some("coll_a".to_string()),
9161 durability: Some(fdecl::Durability::Transient),
9162 ..Default::default()
9163 },
9164 fdecl::Collection {
9165 name: Some("coll_b".to_string()),
9166 durability: Some(fdecl::Durability::Transient),
9167 ..Default::default()
9168 },
9169 ]);
9170 decl.children = Some(vec![
9171 fdecl::Child {
9172 name: Some("child_c".to_string()),
9173 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9174 startup: Some(fdecl::StartupMode::Lazy),
9175 on_terminate: None,
9176 environment: None,
9177 ..Default::default()
9178 },
9179 ]);
9180 decl
9181 },
9182 result = Err(ErrorList::new(vec![
9183 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."),
9184 ])),
9185 },
9186
9187 test_validate_resolvers_missing_from_offer => {
9188 input = {
9189 let mut decl = new_component_decl();
9190 decl.offers = Some(vec![fdecl::Offer::Resolver(fdecl::OfferResolver {
9191 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
9192 source_name: Some("a".to_string()),
9193 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
9194 target_name: Some("a".to_string()),
9195 ..Default::default()
9196 })]);
9197 decl.children = Some(vec![fdecl::Child {
9198 name: Some("child".to_string()),
9199 url: Some("test:///child".to_string()),
9200 startup: Some(fdecl::StartupMode::Eager),
9201 on_terminate: None,
9202 environment: None,
9203 ..Default::default()
9204 }]);
9205 decl
9206 },
9207 result = Err(ErrorList::new(vec![
9208 Error::invalid_capability(DeclType::OfferResolver, "source", "a"),
9209 ])),
9210 },
9211 test_validate_resolvers_missing_from_expose => {
9212 input = {
9213 let mut decl = new_component_decl();
9214 decl.exposes = Some(vec![fdecl::Expose::Resolver(fdecl::ExposeResolver {
9215 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
9216 source_name: Some("a".to_string()),
9217 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
9218 target_name: Some("a".to_string()),
9219 ..Default::default()
9220 })]);
9221 decl
9222 },
9223 result = Err(ErrorList::new(vec![
9224 Error::invalid_capability(DeclType::ExposeResolver, "source", "a"),
9225 ])),
9226 },
9227
9228 test_validate_config_missing_config => {
9229 input = {
9230 let mut decl = new_component_decl();
9231 decl.config = Some(fdecl::ConfigSchema{
9232 fields: None,
9233 checksum: None,
9234 value_source: None,
9235 ..Default::default()
9236 });
9237 decl
9238 },
9239 result = Err(ErrorList::new(vec![
9240 Error::missing_field(DeclType::ConfigSchema, "fields"),
9241 Error::missing_field(DeclType::ConfigSchema, "checksum"),
9242 Error::missing_field(DeclType::ConfigSchema, "value_source"),
9243 ])),
9244 },
9245
9246 test_validate_config_missing_config_field => {
9247 input = {
9248 let mut decl = new_component_decl();
9249 decl.config = Some(fdecl::ConfigSchema{
9250 fields: Some(vec![
9251 fdecl::ConfigField {
9252 key: None,
9253 type_: None,
9254 ..Default::default()
9255 }
9256 ]),
9257 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9258 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9259 ..Default::default()
9260 });
9261 decl
9262 },
9263 result = Err(ErrorList::new(vec![
9264 Error::missing_field(DeclType::ConfigField, "key"),
9265 Error::missing_field(DeclType::ConfigField, "value_type"),
9266 ])),
9267 },
9268
9269 test_validate_config_bool => {
9270 input = {
9271 let mut decl = new_component_decl();
9272 decl.config = Some(fdecl::ConfigSchema{
9273 fields: Some(vec![
9274 fdecl::ConfigField {
9275 key: Some("test".to_string()),
9276 type_: Some(fdecl::ConfigType {
9277 layout: fdecl::ConfigTypeLayout::Bool,
9278 parameters: Some(vec![]),
9279 constraints: vec![]
9280 }),
9281 ..Default::default()
9282 }
9283 ]),
9284 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9285 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9286 ..Default::default()
9287 });
9288 decl
9289 },
9290 result = Ok(()),
9291 },
9292
9293 test_validate_config_bool_extra_constraint => {
9294 input = {
9295 let mut decl = new_component_decl();
9296 decl.config = Some(fdecl::ConfigSchema{
9297 fields: Some(vec![
9298 fdecl::ConfigField {
9299 key: Some("test".to_string()),
9300 type_: Some(fdecl::ConfigType {
9301 layout: fdecl::ConfigTypeLayout::Bool,
9302 parameters: Some(vec![]),
9303 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9304 }),
9305 ..Default::default()
9306 }
9307 ]),
9308 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9309 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9310 ..Default::default()
9311 });
9312 decl
9313 },
9314 result = Err(ErrorList::new(vec![
9315 Error::extraneous_field(DeclType::ConfigType, "constraints")
9316 ])),
9317 },
9318
9319 test_validate_config_bool_missing_parameters => {
9320 input = {
9321 let mut decl = new_component_decl();
9322 decl.config = Some(fdecl::ConfigSchema{
9323 fields: Some(vec![
9324 fdecl::ConfigField {
9325 key: Some("test".to_string()),
9326 type_: Some(fdecl::ConfigType {
9327 layout: fdecl::ConfigTypeLayout::Bool,
9328 parameters: None,
9329 constraints: vec![]
9330 }),
9331 ..Default::default()
9332 }
9333 ]),
9334 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9335 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9336 ..Default::default()
9337 });
9338 decl
9339 },
9340 result = Err(ErrorList::new(vec![
9341 Error::missing_field(DeclType::ConfigType, "parameters")
9342 ])),
9343 },
9344
9345 test_validate_config_string => {
9346 input = {
9347 let mut decl = new_component_decl();
9348 decl.config = Some(fdecl::ConfigSchema{
9349 fields: Some(vec![
9350 fdecl::ConfigField {
9351 key: Some("test".to_string()),
9352 type_: Some(fdecl::ConfigType {
9353 layout: fdecl::ConfigTypeLayout::String,
9354 parameters: Some(vec![]),
9355 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9356 }),
9357 ..Default::default()
9358 }
9359 ]),
9360 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9361 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9362 ..Default::default()
9363 });
9364 decl
9365 },
9366 result = Ok(()),
9367 },
9368
9369 test_validate_config_string_missing_parameter => {
9370 input = {
9371 let mut decl = new_component_decl();
9372 decl.config = Some(fdecl::ConfigSchema{
9373 fields: Some(vec![
9374 fdecl::ConfigField {
9375 key: Some("test".to_string()),
9376 type_: Some(fdecl::ConfigType {
9377 layout: fdecl::ConfigTypeLayout::String,
9378 parameters: None,
9379 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9380 }),
9381 ..Default::default()
9382 }
9383 ]),
9384 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9385 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9386 ..Default::default()
9387 });
9388 decl
9389 },
9390 result = Err(ErrorList::new(vec![
9391 Error::missing_field(DeclType::ConfigType, "parameters")
9392 ])),
9393 },
9394
9395 test_validate_config_string_missing_constraint => {
9396 input = {
9397 let mut decl = new_component_decl();
9398 decl.config = Some(fdecl::ConfigSchema{
9399 fields: Some(vec![
9400 fdecl::ConfigField {
9401 key: Some("test".to_string()),
9402 type_: Some(fdecl::ConfigType {
9403 layout: fdecl::ConfigTypeLayout::String,
9404 parameters: Some(vec![]),
9405 constraints: vec![]
9406 }),
9407 ..Default::default()
9408 }
9409 ]),
9410 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9411 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9412 ..Default::default()
9413 });
9414 decl
9415 },
9416 result = Err(ErrorList::new(vec![
9417 Error::missing_field(DeclType::ConfigType, "constraints")
9418 ])),
9419 },
9420
9421 test_validate_config_string_extra_constraint => {
9422 input = {
9423 let mut decl = new_component_decl();
9424 decl.config = Some(fdecl::ConfigSchema{
9425 fields: Some(vec![
9426 fdecl::ConfigField {
9427 key: Some("test".to_string()),
9428 type_: Some(fdecl::ConfigType {
9429 layout: fdecl::ConfigTypeLayout::String,
9430 parameters: Some(vec![]),
9431 constraints: vec![fdecl::LayoutConstraint::MaxSize(10), fdecl::LayoutConstraint::MaxSize(10)]
9432 }),
9433 ..Default::default()
9434 }
9435 ]),
9436 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9437 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9438 ..Default::default()
9439 });
9440 decl
9441 },
9442 result = Err(ErrorList::new(vec![
9443 Error::extraneous_field(DeclType::ConfigType, "constraints")
9444 ])),
9445 },
9446
9447 test_validate_config_vector_bool => {
9448 input = {
9449 let mut decl = new_component_decl();
9450 decl.config = Some(fdecl::ConfigSchema{
9451 fields: Some(vec![
9452 fdecl::ConfigField {
9453 key: Some("test".to_string()),
9454 type_: Some(fdecl::ConfigType {
9455 layout: fdecl::ConfigTypeLayout::Vector,
9456 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9457 layout: fdecl::ConfigTypeLayout::Bool,
9458 parameters: Some(vec![]),
9459 constraints: vec![],
9460 })]),
9461 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9462 }),
9463 ..Default::default()
9464 }
9465 ]),
9466 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9467 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9468 ..Default::default()
9469 });
9470 decl
9471 },
9472 result = Ok(()),
9473 },
9474
9475 test_validate_config_vector_extra_parameter => {
9476 input = {
9477 let mut decl = new_component_decl();
9478 decl.config = Some(fdecl::ConfigSchema{
9479 fields: Some(vec![
9480 fdecl::ConfigField {
9481 key: Some("test".to_string()),
9482 type_: Some(fdecl::ConfigType {
9483 layout: fdecl::ConfigTypeLayout::Vector,
9484 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9485 layout: fdecl::ConfigTypeLayout::Bool,
9486 parameters: Some(vec![]),
9487 constraints: vec![],
9488 }), fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9489 layout: fdecl::ConfigTypeLayout::Uint8,
9490 parameters: Some(vec![]),
9491 constraints: vec![],
9492 })]),
9493 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9494 }),
9495 ..Default::default()
9496 }
9497 ]),
9498 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9499 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9500 ..Default::default()
9501 });
9502 decl
9503 },
9504 result = Err(ErrorList::new(vec![
9505 Error::extraneous_field(DeclType::ConfigType, "parameters")
9506 ])),
9507 },
9508
9509 test_validate_config_vector_missing_parameter => {
9510 input = {
9511 let mut decl = new_component_decl();
9512 decl.config = Some(fdecl::ConfigSchema{
9513 fields: Some(vec![
9514 fdecl::ConfigField {
9515 key: Some("test".to_string()),
9516 type_: Some(fdecl::ConfigType {
9517 layout: fdecl::ConfigTypeLayout::Vector,
9518 parameters: Some(vec![]),
9519 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9520 }),
9521 ..Default::default()
9522 }
9523 ]),
9524 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9525 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9526 ..Default::default()
9527 });
9528 decl
9529 },
9530 result = Err(ErrorList::new(vec![
9531 Error::missing_field(DeclType::ConfigType, "parameters")
9532 ])),
9533 },
9534
9535 test_validate_config_vector_string => {
9536 input = {
9537 let mut decl = new_component_decl();
9538 decl.config = Some(fdecl::ConfigSchema{
9539 fields: Some(vec![
9540 fdecl::ConfigField {
9541 key: Some("test".to_string()),
9542 type_: Some(fdecl::ConfigType {
9543 layout: fdecl::ConfigTypeLayout::Vector,
9544 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9545 layout: fdecl::ConfigTypeLayout::String,
9546 parameters: Some(vec![]),
9547 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9548 })]),
9549 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9550 }),
9551 ..Default::default()
9552 }
9553 ]),
9554 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9555 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9556 ..Default::default()
9557 });
9558 decl
9559 },
9560 result = Ok(()),
9561 },
9562
9563 test_validate_config_vector_vector => {
9564 input = {
9565 let mut decl = new_component_decl();
9566 decl.config = Some(fdecl::ConfigSchema{
9567 fields: Some(vec![
9568 fdecl::ConfigField {
9569 key: Some("test".to_string()),
9570 type_: Some(fdecl::ConfigType {
9571 layout: fdecl::ConfigTypeLayout::Vector,
9572 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9573 layout: fdecl::ConfigTypeLayout::Vector,
9574 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9575 layout: fdecl::ConfigTypeLayout::String,
9576 parameters: Some(vec![]),
9577 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9578 })]),
9579 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9580 })]),
9581 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9582 }),
9583 ..Default::default()
9584 }
9585 ]),
9586 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9587 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9588 ..Default::default()
9589 });
9590 decl
9591 },
9592 result = Err(ErrorList::new(vec![
9593 Error::nested_vector()
9594 ])),
9595 },
9596
9597 test_validate_exposes_invalid_aggregation_different_availability => {
9598 input = {
9599 let mut decl = new_component_decl();
9600 decl.exposes = Some(vec![
9601 fdecl::Expose::Service(fdecl::ExposeService {
9602 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9603 name: "coll_a".into()
9604 })),
9605 source_name: Some("fuchsia.logger.Log".to_string()),
9606 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
9607 target_name: Some("fuchsia.logger.Log".to_string()),
9608 availability: Some(fdecl::Availability::Required),
9609 ..Default::default()
9610 }),
9611 fdecl::Expose::Service(fdecl::ExposeService {
9612 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9613 name: "coll_b".into()
9614 })),
9615 source_name: Some("fuchsia.logger.Log".to_string()),
9616 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
9617 target_name: Some("fuchsia.logger.Log".to_string()),
9618 availability: Some(fdecl::Availability::Optional),
9619 ..Default::default()
9620 })
9621 ]);
9622 decl.collections = Some(vec![
9623 fdecl::Collection {
9624 name: Some("coll_a".to_string()),
9625 durability: Some(fdecl::Durability::Transient),
9626 ..Default::default()
9627 },
9628 fdecl::Collection {
9629 name: Some("coll_b".to_string()),
9630 durability: Some(fdecl::Durability::Transient),
9631 ..Default::default()
9632 },
9633 ]);
9634 decl
9635 },
9636 result = Err(ErrorList::new(vec![
9637 Error::DifferentAvailabilityInAggregation(AvailabilityList(vec![
9638 fdecl::Availability::Required,
9639 fdecl::Availability::Optional,
9640 ]))
9641 ])),
9642 },
9643
9644 test_validate_offers_invalid_aggregation_different_availability => {
9645 input = {
9646 let mut decl = new_component_decl();
9647 decl.offers = Some(vec![
9648 fdecl::Offer::Service(fdecl::OfferService {
9649 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9650 name: "coll_a".into()
9651 })),
9652 source_name: Some("fuchsia.logger.Log".to_string()),
9653 target: Some(fdecl::Ref::Child(
9654 fdecl::ChildRef {
9655 name: "child_c".to_string(),
9656 collection: None,
9657 }
9658 )),
9659 target_name: Some("fuchsia.logger.Log".to_string()),
9660 source_instance_filter: Some(vec!["default".to_string()]),
9661 availability: Some(fdecl::Availability::Required),
9662 ..Default::default()
9663 }),
9664 fdecl::Offer::Service(fdecl::OfferService {
9665 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9666 name: "coll_b".into()
9667 })),
9668 source_name: Some("fuchsia.logger.Log".to_string()),
9669 target: Some(fdecl::Ref::Child(
9670 fdecl::ChildRef {
9671 name: "child_c".to_string(),
9672 collection: None,
9673 }
9674 )),
9675 target_name: Some("fuchsia.logger.Log".to_string()),
9676 source_instance_filter: Some(vec!["a_different_default".to_string()]),
9677 availability: Some(fdecl::Availability::Optional),
9678 ..Default::default()
9679 })
9680 ]);
9681 decl.collections = Some(vec![
9682 fdecl::Collection {
9683 name: Some("coll_a".to_string()),
9684 durability: Some(fdecl::Durability::Transient),
9685 ..Default::default()
9686 },
9687 fdecl::Collection {
9688 name: Some("coll_b".to_string()),
9689 durability: Some(fdecl::Durability::Transient),
9690 ..Default::default()
9691 },
9692 ]);
9693 decl.children = Some(vec![
9694 fdecl::Child {
9695 name: Some("child_c".to_string()),
9696 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9697 startup: Some(fdecl::StartupMode::Lazy),
9698 on_terminate: None,
9699 environment: None,
9700 ..Default::default()
9701 },
9702 ]);
9703 decl
9704 },
9705 result = Err(ErrorList::new(vec![
9706 Error::DifferentAvailabilityInAggregation(AvailabilityList(vec![
9707 fdecl::Availability::Required,
9708 fdecl::Availability::Optional,
9709 ]))
9710 ])),
9711 },
9712 }
9713
9714 test_validate_capabilities! {
9715 test_validate_capabilities_individually_ok => {
9716 input = vec![
9717 fdecl::Capability::Protocol(fdecl::Protocol {
9718 name: Some("foo_svc".into()),
9719 source_path: Some("/svc/foo".into()),
9720 ..Default::default()
9721 }),
9722 fdecl::Capability::Directory(fdecl::Directory {
9723 name: Some("foo_dir".into()),
9724 source_path: Some("/foo".into()),
9725 rights: Some(fio::Operations::CONNECT),
9726 ..Default::default()
9727 }),
9728 ],
9729 as_builtin = false,
9730 result = Ok(()),
9731 },
9732 test_validate_capabilities_individually_err => {
9733 input = vec![
9734 fdecl::Capability::Protocol(fdecl::Protocol {
9735 name: None,
9736 source_path: None,
9737 ..Default::default()
9738 }),
9739 fdecl::Capability::Directory(fdecl::Directory {
9740 name: None,
9741 source_path: None,
9742 rights: None,
9743 ..Default::default()
9744 }),
9745 ],
9746 as_builtin = false,
9747 result = Err(ErrorList::new(vec![
9748 Error::missing_field(DeclType::Protocol, "name"),
9749 Error::missing_field(DeclType::Protocol, "source_path"),
9750 Error::missing_field(DeclType::Directory, "name"),
9751 Error::missing_field(DeclType::Directory, "source_path"),
9752 Error::missing_field(DeclType::Directory, "rights"),
9753 ])),
9754 },
9755 test_validate_builtin_capabilities_individually_ok => {
9756 input = vec![
9757 fdecl::Capability::Protocol(fdecl::Protocol {
9758 name: Some("foo_protocol".into()),
9759 source_path: None,
9760 ..Default::default()
9761 }),
9762 fdecl::Capability::Directory(fdecl::Directory {
9763 name: Some("foo_dir".into()),
9764 source_path: None,
9765 rights: Some(fio::Operations::CONNECT),
9766 ..Default::default()
9767 }),
9768 fdecl::Capability::Service(fdecl::Service {
9769 name: Some("foo_svc".into()),
9770 source_path: None,
9771 ..Default::default()
9772 }),
9773 fdecl::Capability::Runner(fdecl::Runner {
9774 name: Some("foo_runner".into()),
9775 source_path: None,
9776 ..Default::default()
9777 }),
9778 fdecl::Capability::Resolver(fdecl::Resolver {
9779 name: Some("foo_resolver".into()),
9780 source_path: None,
9781 ..Default::default()
9782 }),
9783 ],
9784 as_builtin = true,
9785 result = Ok(()),
9786 },
9787 test_validate_builtin_capabilities_individually_err => {
9788 input = vec![
9789 fdecl::Capability::Protocol(fdecl::Protocol {
9790 name: None,
9791 source_path: Some("/svc/foo".into()),
9792 ..Default::default()
9793 }),
9794 fdecl::Capability::Directory(fdecl::Directory {
9795 name: None,
9796 source_path: Some("/foo".into()),
9797 rights: None,
9798 ..Default::default()
9799 }),
9800 fdecl::Capability::Service(fdecl::Service {
9801 name: None,
9802 source_path: Some("/svc/foo".into()),
9803 ..Default::default()
9804 }),
9805 fdecl::Capability::Runner(fdecl::Runner {
9806 name: None,
9807 source_path: Some("/foo".into()),
9808 ..Default::default()
9809 }),
9810 fdecl::Capability::Resolver(fdecl::Resolver {
9811 name: None,
9812 source_path: Some("/foo".into()),
9813 ..Default::default()
9814 }),
9815 fdecl::Capability::Storage(fdecl::Storage {
9816 name: None,
9817 ..Default::default()
9818 }),
9819 ],
9820 as_builtin = true,
9821 result = Err(ErrorList::new(vec![
9822 Error::missing_field(DeclType::Protocol, "name"),
9823 Error::extraneous_source_path(DeclType::Protocol, "/svc/foo"),
9824 Error::missing_field(DeclType::Directory, "name"),
9825 Error::extraneous_source_path(DeclType::Directory, "/foo"),
9826 Error::missing_field(DeclType::Directory, "rights"),
9827 Error::missing_field(DeclType::Service, "name"),
9828 Error::extraneous_source_path(DeclType::Service, "/svc/foo"),
9829 Error::missing_field(DeclType::Runner, "name"),
9830 Error::extraneous_source_path(DeclType::Runner, "/foo"),
9831 Error::missing_field(DeclType::Resolver, "name"),
9832 Error::extraneous_source_path(DeclType::Resolver, "/foo"),
9833 Error::CapabilityCannotBeBuiltin(DeclType::Storage),
9834 ])),
9835 },
9836 test_validate_delivery_type_ok => {
9837 input = vec![
9838 fdecl::Capability::Protocol(fdecl::Protocol {
9839 name: Some("foo_svc1".into()),
9840 source_path: Some("/svc/foo1".into()),
9841 ..Default::default()
9842 }),
9843 fdecl::Capability::Protocol(fdecl::Protocol {
9844 name: Some("foo_svc2".into()),
9845 source_path: Some("/svc/foo2".into()),
9846 delivery: Some(fdecl::DeliveryType::Immediate),
9847 ..Default::default()
9848 }),
9849 fdecl::Capability::Protocol(fdecl::Protocol {
9850 name: Some("foo_svc3".into()),
9851 source_path: Some("/svc/foo3".into()),
9852 delivery: Some(fdecl::DeliveryType::OnReadable),
9853 ..Default::default()
9854 }),
9855 ],
9856 as_builtin = false,
9857 result = Ok(()),
9858 },
9859 test_validate_delivery_type_err => {
9860 input = vec![
9861 fdecl::Capability::Protocol(fdecl::Protocol {
9862 name: Some("foo_svc".into()),
9863 source_path: Some("/svc/foo".into()),
9864 delivery: Some(fdecl::DeliveryType::unknown()),
9865 ..Default::default()
9866 }),
9867 ],
9868 as_builtin = false,
9869 result = Err(ErrorList::new(vec![
9870 Error::invalid_field(DeclType::Protocol, "delivery"),
9871 ])),
9872 },
9873 }
9874
9875 fn generate_expose_different_source_and_availability_decl(
9878 new_expose: impl Fn(fdecl::Ref, fdecl::Availability, &str) -> fdecl::Expose,
9879 ) -> fdecl::Component {
9880 let mut decl = new_component_decl();
9881 let child = "child";
9882 decl.children = Some(vec![fdecl::Child {
9883 name: Some(child.to_string()),
9884 url: Some("fuchsia-pkg://fuchsia.com/source#meta/source.cm".to_string()),
9885 startup: Some(fdecl::StartupMode::Lazy),
9886 ..Default::default()
9887 }]);
9888 decl.exposes = Some(vec![
9889 new_expose(
9891 fdecl::Ref::Self_(fdecl::SelfRef {}),
9892 fdecl::Availability::Optional,
9893 "fuchsia.examples.Echo1",
9894 ),
9895 new_expose(
9897 fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
9898 fdecl::Availability::Optional,
9899 "fuchsia.examples.Echo2",
9900 ),
9901 new_expose(
9903 fdecl::Ref::VoidType(fdecl::VoidRef {}),
9904 fdecl::Availability::Optional,
9905 "fuchsia.examples.Echo3",
9906 ),
9907 new_expose(
9909 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
9910 fdecl::Availability::Optional,
9911 "fuchsia.examples.Echo4",
9912 ),
9913 new_expose(
9915 fdecl::Ref::Self_(fdecl::SelfRef {}),
9916 fdecl::Availability::Transitional,
9917 "fuchsia.examples.Echo5",
9918 ),
9919 new_expose(
9921 fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
9922 fdecl::Availability::Transitional,
9923 "fuchsia.examples.Echo6",
9924 ),
9925 new_expose(
9927 fdecl::Ref::VoidType(fdecl::VoidRef {}),
9928 fdecl::Availability::Transitional,
9929 "fuchsia.examples.Echo7",
9930 ),
9931 new_expose(
9933 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
9934 fdecl::Availability::Transitional,
9935 "fuchsia.examples.Echo8",
9936 ),
9937 new_expose(
9939 fdecl::Ref::Self_(fdecl::SelfRef {}),
9940 fdecl::Availability::SameAsTarget,
9941 "fuchsia.examples.Echo9",
9942 ),
9943 new_expose(
9945 fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
9946 fdecl::Availability::SameAsTarget,
9947 "fuchsia.examples.Echo10",
9948 ),
9949 new_expose(
9951 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
9952 fdecl::Availability::SameAsTarget,
9953 "fuchsia.examples.Echo11",
9954 ),
9955 new_expose(
9957 fdecl::Ref::VoidType(fdecl::VoidRef {}),
9958 fdecl::Availability::Required,
9959 "fuchsia.examples.Echo12",
9960 ),
9961 new_expose(
9963 fdecl::Ref::VoidType(fdecl::VoidRef {}),
9964 fdecl::Availability::SameAsTarget,
9965 "fuchsia.examples.Echo13",
9966 ),
9967 ]);
9968 decl
9969 }
9970
9971 fn generate_offer_different_source_and_availability_decl(
9974 new_offer: impl Fn(fdecl::Ref, fdecl::Availability, &str) -> fdecl::Offer,
9975 ) -> fdecl::Component {
9976 let mut decl = new_component_decl();
9977 decl.children = Some(vec![
9978 fdecl::Child {
9979 name: Some("source".to_string()),
9980 url: Some("fuchsia-pkg://fuchsia.com/source#meta/source.cm".to_string()),
9981 startup: Some(fdecl::StartupMode::Lazy),
9982 on_terminate: None,
9983 environment: None,
9984 ..Default::default()
9985 },
9986 fdecl::Child {
9987 name: Some("sink".to_string()),
9988 url: Some("fuchsia-pkg://fuchsia.com/sink#meta/sink.cm".to_string()),
9989 startup: Some(fdecl::StartupMode::Lazy),
9990 on_terminate: None,
9991 environment: None,
9992 ..Default::default()
9993 },
9994 ]);
9995 decl.offers = Some(vec![
9996 new_offer(
9999 fdecl::Ref::Parent(fdecl::ParentRef {}),
10000 fdecl::Availability::Required,
10001 "fuchsia.examples.Echo0",
10002 ),
10003 new_offer(
10004 fdecl::Ref::Parent(fdecl::ParentRef {}),
10005 fdecl::Availability::Optional,
10006 "fuchsia.examples.Echo1",
10007 ),
10008 new_offer(
10009 fdecl::Ref::Parent(fdecl::ParentRef {}),
10010 fdecl::Availability::SameAsTarget,
10011 "fuchsia.examples.Echo2",
10012 ),
10013 new_offer(
10014 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10015 fdecl::Availability::Optional,
10016 "fuchsia.examples.Echo3",
10017 ),
10018 new_offer(
10021 fdecl::Ref::Self_(fdecl::SelfRef {}),
10022 fdecl::Availability::Optional,
10023 "fuchsia.examples.Echo4",
10024 ),
10025 new_offer(
10026 fdecl::Ref::Self_(fdecl::SelfRef {}),
10027 fdecl::Availability::SameAsTarget,
10028 "fuchsia.examples.Echo5",
10029 ),
10030 new_offer(
10031 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10032 fdecl::Availability::Optional,
10033 "fuchsia.examples.Echo6",
10034 ),
10035 new_offer(
10036 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10037 fdecl::Availability::SameAsTarget,
10038 "fuchsia.examples.Echo7",
10039 ),
10040 new_offer(
10041 fdecl::Ref::Child(fdecl::ChildRef { name: "source".to_string(), collection: None }),
10042 fdecl::Availability::Optional,
10043 "fuchsia.examples.Echo8",
10044 ),
10045 new_offer(
10046 fdecl::Ref::Child(fdecl::ChildRef { name: "source".to_string(), collection: None }),
10047 fdecl::Availability::SameAsTarget,
10048 "fuchsia.examples.Echo9",
10049 ),
10050 new_offer(
10052 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10053 fdecl::Availability::Required,
10054 "fuchsia.examples.Echo10",
10055 ),
10056 new_offer(
10057 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10058 fdecl::Availability::SameAsTarget,
10059 "fuchsia.examples.Echo11",
10060 ),
10061 ]);
10062 decl
10063 }
10064
10065 #[test]
10066 fn test_validate_dynamic_offers_empty() {
10067 assert_eq!(validate_dynamic_offers(vec![], &vec![], &fdecl::Component::default()), Ok(()));
10068 }
10069
10070 #[test]
10071 fn test_validate_dynamic_offers_okay() {
10072 assert_eq!(
10073 validate_dynamic_offers(
10074 vec![],
10075 &vec![
10076 fdecl::Offer::Protocol(fdecl::OfferProtocol {
10077 dependency_type: Some(fdecl::DependencyType::Strong),
10078 source: Some(fdecl::Ref::Self_(fdecl::SelfRef)),
10079 source_name: Some("thing".to_string()),
10080 target_name: Some("thing".repeat(26)),
10081 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10082 name: "foo".to_string(),
10083 collection: Some("foo".to_string()),
10084 })),
10085 ..Default::default()
10086 }),
10087 fdecl::Offer::Service(fdecl::OfferService {
10088 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10089 source_name: Some("thang".repeat(26)),
10090 target_name: Some("thang".repeat(26)),
10091 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10092 name: "foo".to_string(),
10093 collection: Some("foo".to_string()),
10094 })),
10095 ..Default::default()
10096 }),
10097 fdecl::Offer::Directory(fdecl::OfferDirectory {
10098 dependency_type: Some(fdecl::DependencyType::Strong),
10099 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10100 source_name: Some("thung1".repeat(26)),
10101 target_name: Some("thung1".repeat(26)),
10102 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10103 name: "foo".to_string(),
10104 collection: Some("foo".to_string()),
10105 })),
10106 ..Default::default()
10107 }),
10108 fdecl::Offer::Storage(fdecl::OfferStorage {
10109 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10110 source_name: Some("thung2".repeat(26)),
10111 target_name: Some("thung2".repeat(26)),
10112 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10113 name: "foo".to_string(),
10114 collection: Some("foo".to_string()),
10115 })),
10116 ..Default::default()
10117 }),
10118 fdecl::Offer::Runner(fdecl::OfferRunner {
10119 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10120 source_name: Some("thung3".repeat(26)),
10121 target_name: Some("thung3".repeat(26)),
10122 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10123 name: "foo".to_string(),
10124 collection: Some("foo".to_string()),
10125 })),
10126 ..Default::default()
10127 }),
10128 fdecl::Offer::Resolver(fdecl::OfferResolver {
10129 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10130 source_name: Some("thung4".repeat(26)),
10131 target_name: Some("thung4".repeat(26)),
10132 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10133 name: "foo".to_string(),
10134 collection: Some("foo".to_string()),
10135 })),
10136 ..Default::default()
10137 }),
10138 ],
10139 &fdecl::Component {
10140 capabilities: Some(vec![fdecl::Capability::Protocol(fdecl::Protocol {
10141 name: Some("thing".to_string()),
10142 source_path: Some("/svc/foo".into()),
10143 ..Default::default()
10144 }),]),
10145 ..Default::default()
10146 }
10147 ),
10148 Ok(())
10149 );
10150 }
10151
10152 #[test]
10153 fn test_validate_dynamic_offers_valid_service_aggregation() {
10154 assert_eq!(
10155 validate_dynamic_offers(
10156 vec![],
10157 &vec![
10158 fdecl::Offer::Service(fdecl::OfferService {
10159 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10160 name: "child_a".to_string(),
10161 collection: None
10162 })),
10163 source_name: Some("fuchsia.logger.Log".to_string()),
10164 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10165 name: "child_c".to_string(),
10166 collection: None,
10167 })),
10168 target_name: Some("fuchsia.logger.Log".to_string()),
10169 source_instance_filter: Some(vec!["default".to_string()]),
10170 ..Default::default()
10171 }),
10172 fdecl::Offer::Service(fdecl::OfferService {
10173 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10174 name: "child_b".to_string(),
10175 collection: None
10176 })),
10177 source_name: Some("fuchsia.logger.Log".to_string()),
10178 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10179 name: "child_c".to_string(),
10180 collection: None,
10181 })),
10182 target_name: Some("fuchsia.logger.Log".to_string()),
10183 source_instance_filter: Some(vec!["a_different_default".to_string()]),
10184 ..Default::default()
10185 })
10186 ],
10187 &fdecl::Component {
10188 children: Some(vec![
10189 fdecl::Child {
10190 name: Some("child_a".to_string()),
10191 url: Some(
10192 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10193 .to_string()
10194 ),
10195 startup: Some(fdecl::StartupMode::Lazy),
10196 on_terminate: None,
10197 environment: None,
10198 ..Default::default()
10199 },
10200 fdecl::Child {
10201 name: Some("child_b".to_string()),
10202 url: Some(
10203 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10204 .to_string()
10205 ),
10206 startup: Some(fdecl::StartupMode::Lazy),
10207 on_terminate: None,
10208 environment: None,
10209 ..Default::default()
10210 },
10211 fdecl::Child {
10212 name: Some("child_c".to_string()),
10213 url: Some(
10214 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10215 .to_string()
10216 ),
10217 startup: Some(fdecl::StartupMode::Lazy),
10218 on_terminate: None,
10219 environment: None,
10220 ..Default::default()
10221 },
10222 ]),
10223 ..Default::default()
10224 }
10225 ),
10226 Ok(())
10227 );
10228 }
10229
10230 #[test]
10231 fn test_validate_dynamic_service_aggregation_missing_filter() {
10232 assert_eq!(
10233 validate_dynamic_offers(
10234 vec![],
10235 &vec![
10236 fdecl::Offer::Service(fdecl::OfferService {
10237 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10238 name: "child_a".to_string(),
10239 collection: None
10240 })),
10241 source_name: Some("fuchsia.logger.Log".to_string()),
10242 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10243 name: "child_c".to_string(),
10244 collection: None,
10245 })),
10246 target_name: Some("fuchsia.logger.Log".to_string()),
10247 source_instance_filter: Some(vec!["default".to_string()]),
10248 ..Default::default()
10249 }),
10250 fdecl::Offer::Service(fdecl::OfferService {
10251 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10252 name: "child_b".to_string(),
10253 collection: None
10254 })),
10255 source_name: Some("fuchsia.logger.Log".to_string()),
10256 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10257 name: "child_c".to_string(),
10258 collection: None,
10259 })),
10260 target_name: Some("fuchsia.logger.Log".to_string()),
10261 source_instance_filter: None,
10262 ..Default::default()
10263 }),
10264 ],
10265 &fdecl::Component {
10266 children: Some(vec![
10267 fdecl::Child {
10268 name: Some("child_a".to_string()),
10269 url: Some(
10270 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10271 .to_string()
10272 ),
10273 startup: Some(fdecl::StartupMode::Lazy),
10274 ..Default::default()
10275 },
10276 fdecl::Child {
10277 name: Some("child_b".to_string()),
10278 url: Some(
10279 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10280 .to_string()
10281 ),
10282 startup: Some(fdecl::StartupMode::Lazy),
10283 ..Default::default()
10284 },
10285 fdecl::Child {
10286 name: Some("child_c".to_string()),
10287 url: Some(
10288 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10289 .to_string()
10290 ),
10291 startup: Some(fdecl::StartupMode::Lazy),
10292 ..Default::default()
10293 },
10294 ]),
10295 ..Default::default()
10296 },
10297 ),
10298 Err(ErrorList::new(vec![Error::invalid_aggregate_offer(
10299 "source_instance_filter must be set for dynamic aggregate service offers"
10300 ),]))
10301 );
10302 }
10303
10304 #[test]
10305 fn test_validate_dynamic_offers_omit_target() {
10306 assert_eq!(
10307 validate_dynamic_offers(
10308 vec![],
10309 &vec![
10310 fdecl::Offer::Protocol(fdecl::OfferProtocol {
10311 dependency_type: Some(fdecl::DependencyType::Strong),
10312 source: Some(fdecl::Ref::Self_(fdecl::SelfRef)),
10313 source_name: Some("thing".to_string()),
10314 target_name: Some("thing".to_string()),
10315 ..Default::default()
10316 }),
10317 fdecl::Offer::Service(fdecl::OfferService {
10318 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10319 source_name: Some("thang".to_string()),
10320 target_name: Some("thang".to_string()),
10321 ..Default::default()
10322 }),
10323 fdecl::Offer::Directory(fdecl::OfferDirectory {
10324 dependency_type: Some(fdecl::DependencyType::Strong),
10325 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10326 source_name: Some("thung1".to_string()),
10327 target_name: Some("thung1".to_string()),
10328 ..Default::default()
10329 }),
10330 fdecl::Offer::Storage(fdecl::OfferStorage {
10331 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10332 source_name: Some("thung2".to_string()),
10333 target_name: Some("thung2".to_string()),
10334 ..Default::default()
10335 }),
10336 fdecl::Offer::Runner(fdecl::OfferRunner {
10337 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10338 source_name: Some("thung3".to_string()),
10339 target_name: Some("thung3".to_string()),
10340 ..Default::default()
10341 }),
10342 fdecl::Offer::Resolver(fdecl::OfferResolver {
10343 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10344 source_name: Some("thung4".to_string()),
10345 target_name: Some("thung4".to_string()),
10346 ..Default::default()
10347 }),
10348 ],
10349 &fdecl::Component {
10350 capabilities: Some(vec![fdecl::Capability::Protocol(fdecl::Protocol {
10351 name: Some("thing".to_string()),
10352 source_path: Some("/svc/foo".into()),
10353 ..Default::default()
10354 }),]),
10355 ..Default::default()
10356 }
10357 ),
10358 Err(ErrorList::new(vec![
10359 Error::missing_field(DeclType::OfferProtocol, "target"),
10360 Error::missing_field(DeclType::OfferService, "target"),
10361 Error::missing_field(DeclType::OfferDirectory, "target"),
10362 Error::missing_field(DeclType::OfferStorage, "target"),
10363 Error::missing_field(DeclType::OfferRunner, "target"),
10364 Error::missing_field(DeclType::OfferResolver, "target"),
10365 ]))
10366 );
10367 }
10368
10369 #[test]
10370 fn test_validate_dynamic_offers_collection_collision() {
10371 assert_eq!(
10372 validate_dynamic_offers(
10373 vec![],
10374 &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10375 dependency_type: Some(fdecl::DependencyType::Strong),
10376 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10377 source_name: Some("thing".to_string()),
10378 target_name: Some("thing".to_string()),
10379 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10380 name: "child".to_string(),
10381 collection: Some("coll".to_string()),
10382 })),
10383 ..Default::default()
10384 }),],
10385 &fdecl::Component {
10386 offers: Some(vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10387 dependency_type: Some(fdecl::DependencyType::Strong),
10388 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10389 source_name: Some("thing".to_string()),
10390 target_name: Some("thing".to_string()),
10391 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10392 name: "coll".into()
10393 })),
10394 ..Default::default()
10395 }),]),
10396 collections: Some(vec![fdecl::Collection {
10397 name: Some("coll".to_string()),
10398 durability: Some(fdecl::Durability::Transient),
10399 ..Default::default()
10400 },]),
10401 ..Default::default()
10402 }
10403 ),
10404 Err(ErrorList::new(vec![Error::duplicate_field(
10405 DeclType::OfferProtocol,
10406 "target_name",
10407 "thing"
10408 ),]))
10409 );
10410 }
10411
10412 #[test]
10413 fn test_validate_dynamic_offers_cycle_collection_to_static_child() {
10414 assert_eq!(
10415 validate_dynamic_offers(
10416 vec![("dyn", "coll")],
10417 &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10418 source_name: Some("bar".to_string()),
10419 target_name: Some("bar".to_string()),
10420 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10421 name: "static_child".into(),
10422 collection: None,
10423 })),
10424 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10425 name: "dyn".to_string(),
10426 collection: Some("coll".to_string()),
10427 })),
10428 dependency_type: Some(fdecl::DependencyType::Strong),
10429 ..Default::default()
10430 }),],
10431 &fdecl::Component {
10432 offers: Some(vec![fdecl::Offer::Service(fdecl::OfferService {
10433 source_name: Some("foo".to_string()),
10434 target_name: Some("foo".to_string()),
10435 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10436 name: "coll".into(),
10437 })),
10438 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10439 name: "static_child".into(),
10440 collection: None,
10441 })),
10442 ..Default::default()
10443 })]),
10444 children: Some(vec![fdecl::Child {
10445 name: Some("static_child".into()),
10446 url: Some("url#child.cm".into()),
10447 startup: Some(fdecl::StartupMode::Lazy),
10448 ..Default::default()
10449 }]),
10450 collections: Some(vec![fdecl::Collection {
10451 name: Some("coll".into()),
10452 durability: Some(fdecl::Durability::Transient),
10453 ..Default::default()
10454 }]),
10455 ..Default::default()
10456 }
10457 ),
10458 Err(ErrorList::new(vec![Error::dependency_cycle(
10459 directed_graph::Error::CyclesDetected(
10460 [vec![
10461 "child coll:dyn",
10462 "collection coll",
10463 "child static_child",
10464 "child coll:dyn",
10465 ]]
10466 .iter()
10467 .cloned()
10468 .collect()
10469 )
10470 .format_cycle()
10471 )]))
10472 );
10473 }
10474
10475 #[test]
10476 fn test_validate_dynamic_offers_cycle_collection_to_dynamic_child() {
10477 assert_eq!(
10478 validate_dynamic_offers(
10479 vec![("dyn", "coll1"), ("dyn", "coll2")],
10480 &vec![
10481 fdecl::Offer::Service(fdecl::OfferService {
10482 source_name: Some("foo".to_string()),
10483 target_name: Some("foo".to_string()),
10484 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10485 name: "coll2".into(),
10486 })),
10487 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10488 name: "dyn".into(),
10489 collection: Some("coll1".into()),
10490 })),
10491 ..Default::default()
10492 }),
10493 fdecl::Offer::Protocol(fdecl::OfferProtocol {
10494 source_name: Some("bar".to_string()),
10495 target_name: Some("bar".to_string()),
10496 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10497 name: "dyn".into(),
10498 collection: Some("coll1".into()),
10499 })),
10500 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10501 name: "dyn".to_string(),
10502 collection: Some("coll2".to_string()),
10503 })),
10504 dependency_type: Some(fdecl::DependencyType::Strong),
10505 ..Default::default()
10506 }),
10507 ],
10508 &fdecl::Component {
10509 collections: Some(vec![
10510 fdecl::Collection {
10511 name: Some("coll1".into()),
10512 durability: Some(fdecl::Durability::Transient),
10513 ..Default::default()
10514 },
10515 fdecl::Collection {
10516 name: Some("coll2".into()),
10517 durability: Some(fdecl::Durability::Transient),
10518 ..Default::default()
10519 },
10520 ]),
10521 ..Default::default()
10522 }
10523 ),
10524 Err(ErrorList::new(vec![Error::dependency_cycle(
10525 directed_graph::Error::CyclesDetected(
10526 [vec![
10527 "child coll1:dyn",
10528 "child coll2:dyn",
10529 "collection coll2",
10530 "child coll1:dyn",
10531 ]]
10532 .iter()
10533 .cloned()
10534 .collect()
10535 )
10536 .format_cycle()
10537 )]))
10538 );
10539 }
10540
10541 #[test]
10542 fn test_validate_dynamic_offers_cycle_collection_to_collection() {
10543 assert_eq!(
10544 validate_dynamic_offers(
10545 vec![("dyn", "coll1"), ("dyn", "coll2")],
10546 &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10547 source_name: Some("bar".to_string()),
10548 target_name: Some("bar".to_string()),
10549 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10550 name: "dyn".into(),
10551 collection: Some("coll2".parse().unwrap()),
10552 })),
10553 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10554 name: "dyn".into(),
10555 collection: Some("coll1".parse().unwrap()),
10556 })),
10557 dependency_type: Some(fdecl::DependencyType::Strong),
10558 ..Default::default()
10559 }),],
10560 &fdecl::Component {
10561 offers: Some(vec![fdecl::Offer::Service(fdecl::OfferService {
10562 source_name: Some("foo".to_string()),
10563 target_name: Some("foo".to_string()),
10564 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10565 name: "coll1".into(),
10566 })),
10567 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10568 name: "coll2".into(),
10569 })),
10570 ..Default::default()
10571 })]),
10572 collections: Some(vec![
10573 fdecl::Collection {
10574 name: Some("coll1".into()),
10575 durability: Some(fdecl::Durability::Transient),
10576 ..Default::default()
10577 },
10578 fdecl::Collection {
10579 name: Some("coll2".into()),
10580 durability: Some(fdecl::Durability::Transient),
10581 ..Default::default()
10582 },
10583 ]),
10584 ..Default::default()
10585 }
10586 ),
10587 Err(ErrorList::new(vec![Error::dependency_cycle(
10588 directed_graph::Error::CyclesDetected(
10589 [vec![
10590 "child coll1:dyn",
10591 "collection coll1",
10592 "child coll2:dyn",
10593 "child coll1:dyn",
10594 ]]
10595 .iter()
10596 .cloned()
10597 .collect()
10598 )
10599 .format_cycle()
10600 )]))
10601 );
10602 }
10603
10604 #[test]
10605 fn test_validate_dynamic_child() {
10606 assert_eq!(
10607 Ok(()),
10608 validate_dynamic_child(&fdecl::Child {
10609 name: Some("a".repeat(MAX_LONG_NAME_LENGTH).to_string()),
10610 url: Some("test:///child".to_string()),
10611 startup: Some(fdecl::StartupMode::Lazy),
10612 on_terminate: None,
10613 environment: None,
10614 ..Default::default()
10615 })
10616 );
10617 }
10618
10619 #[test]
10620 fn test_validate_dynamic_child_environment_is_invalid() {
10621 assert_eq!(
10622 validate_dynamic_child(&fdecl::Child {
10623 name: Some("a".repeat(MAX_LONG_NAME_LENGTH).to_string()),
10624 url: Some("test:///child".to_string()),
10625 startup: Some(fdecl::StartupMode::Lazy),
10626 on_terminate: None,
10627 environment: Some("env".to_string()),
10628 ..Default::default()
10629 }),
10630 Err(ErrorList::new(vec![Error::DynamicChildWithEnvironment]))
10631 );
10632 }
10633
10634 #[test]
10635 fn test_validate_dynamic_offers_missing_stuff() {
10636 assert_eq!(
10637 validate_dynamic_offers(
10638 vec![],
10639 &vec![
10640 fdecl::Offer::Protocol(fdecl::OfferProtocol::default()),
10641 fdecl::Offer::Service(fdecl::OfferService::default()),
10642 fdecl::Offer::Directory(fdecl::OfferDirectory::default()),
10643 fdecl::Offer::Storage(fdecl::OfferStorage::default()),
10644 fdecl::Offer::Runner(fdecl::OfferRunner::default()),
10645 fdecl::Offer::Resolver(fdecl::OfferResolver::default()),
10646 ],
10647 &fdecl::Component::default()
10648 ),
10649 Err(ErrorList::new(vec![
10650 Error::missing_field(DeclType::OfferProtocol, "source"),
10651 Error::missing_field(DeclType::OfferProtocol, "source_name"),
10652 Error::missing_field(DeclType::OfferProtocol, "target"),
10653 Error::missing_field(DeclType::OfferProtocol, "target_name"),
10654 Error::missing_field(DeclType::OfferProtocol, "dependency_type"),
10655 Error::missing_field(DeclType::OfferService, "source"),
10656 Error::missing_field(DeclType::OfferService, "source_name"),
10657 Error::missing_field(DeclType::OfferService, "target"),
10658 Error::missing_field(DeclType::OfferService, "target_name"),
10659 Error::missing_field(DeclType::OfferDirectory, "source"),
10660 Error::missing_field(DeclType::OfferDirectory, "source_name"),
10661 Error::missing_field(DeclType::OfferDirectory, "target"),
10662 Error::missing_field(DeclType::OfferDirectory, "target_name"),
10663 Error::missing_field(DeclType::OfferDirectory, "dependency_type"),
10664 Error::missing_field(DeclType::OfferStorage, "source"),
10665 Error::missing_field(DeclType::OfferStorage, "source_name"),
10666 Error::missing_field(DeclType::OfferStorage, "target"),
10667 Error::missing_field(DeclType::OfferStorage, "target_name"),
10668 Error::missing_field(DeclType::OfferRunner, "source"),
10669 Error::missing_field(DeclType::OfferRunner, "source_name"),
10670 Error::missing_field(DeclType::OfferRunner, "target"),
10671 Error::missing_field(DeclType::OfferRunner, "target_name"),
10672 Error::missing_field(DeclType::OfferResolver, "source"),
10673 Error::missing_field(DeclType::OfferResolver, "source_name"),
10674 Error::missing_field(DeclType::OfferResolver, "target"),
10675 Error::missing_field(DeclType::OfferResolver, "target_name"),
10676 ]))
10677 );
10678 }
10679
10680 test_dependency! {
10681 (test_validate_offers_protocol_dependency_cycle) => {
10682 ty = fdecl::Offer::Protocol,
10683 offer_decl = fdecl::OfferProtocol {
10684 source: None, target: None, source_name: Some(format!("thing")),
10687 target_name: Some(format!("thing")),
10688 dependency_type: Some(fdecl::DependencyType::Strong),
10689 ..Default::default()
10690 },
10691 },
10692 (test_validate_offers_directory_dependency_cycle) => {
10693 ty = fdecl::Offer::Directory,
10694 offer_decl = fdecl::OfferDirectory {
10695 source: None, target: None, source_name: Some(format!("thing")),
10698 target_name: Some(format!("thing")),
10699 rights: Some(fio::Operations::CONNECT),
10700 subdir: None,
10701 dependency_type: Some(fdecl::DependencyType::Strong),
10702 ..Default::default()
10703 },
10704 },
10705 (test_validate_offers_service_dependency_cycle) => {
10706 ty = fdecl::Offer::Service,
10707 offer_decl = fdecl::OfferService {
10708 source: None, target: None, source_name: Some(format!("thing")),
10711 target_name: Some(format!("thing")),
10712 ..Default::default()
10713 },
10714 },
10715 (test_validate_offers_runner_dependency_cycle) => {
10716 ty = fdecl::Offer::Runner,
10717 offer_decl = fdecl::OfferRunner {
10718 source: None, target: None, source_name: Some(format!("thing")),
10721 target_name: Some(format!("thing")),
10722 ..Default::default()
10723 },
10724 },
10725 (test_validate_offers_resolver_dependency_cycle) => {
10726 ty = fdecl::Offer::Resolver,
10727 offer_decl = fdecl::OfferResolver {
10728 source: None, target: None, source_name: Some(format!("thing")),
10731 target_name: Some(format!("thing")),
10732 ..Default::default()
10733 },
10734 },
10735 }
10736 test_weak_dependency! {
10737 (test_validate_offers_protocol_weak_dependency_cycle) => {
10738 ty = fdecl::Offer::Protocol,
10739 offer_decl = fdecl::OfferProtocol {
10740 source: None, target: None, source_name: Some(format!("thing")),
10743 target_name: Some(format!("thing")),
10744 dependency_type: None, ..Default::default()
10746 },
10747 },
10748 (test_validate_offers_directory_weak_dependency_cycle) => {
10749 ty = fdecl::Offer::Directory,
10750 offer_decl = fdecl::OfferDirectory {
10751 source: None, target: None, source_name: Some(format!("thing")),
10754 target_name: Some(format!("thing")),
10755 rights: Some(fio::Operations::CONNECT),
10756 subdir: None,
10757 dependency_type: None, ..Default::default()
10759 },
10760 },
10761 (test_validate_offers_service_weak_dependency_cycle) => {
10762 ty = fdecl::Offer::Service,
10763 offer_decl = fdecl::OfferService {
10764 source: None, target: None, source_name: Some(format!("thing")),
10767 target_name: Some(format!("thing")),
10768 dependency_type: None, ..Default::default()
10770 },
10771 },
10772 }
10773}