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::fmt;
18use std::path::Path;
19
20trait HasAvailability {
21 fn availability(&self) -> fdecl::Availability;
22}
23
24impl HasAvailability for fdecl::ExposeService {
25 fn availability(&self) -> fdecl::Availability {
26 return self.availability.unwrap_or(fdecl::Availability::Required);
27 }
28}
29
30impl HasAvailability for fdecl::OfferService {
31 fn availability(&self) -> fdecl::Availability {
32 return self.availability.unwrap_or(fdecl::Availability::Required);
33 }
34}
35
36#[cfg(fuchsia_api_level_at_least = "25")]
37macro_rules! get_source_dictionary {
38 ($decl:ident) => {
39 $decl.source_dictionary.as_ref()
40 };
41}
42#[cfg(fuchsia_api_level_less_than = "25")]
43macro_rules! get_source_dictionary {
44 ($decl:ident) => {
45 None
46 };
47}
48
49pub fn validate_value_spec(spec: &fdecl::ConfigValueSpec) -> Result<(), ErrorList> {
53 let mut errors = vec![];
54 if let Some(value) = &spec.value {
55 match value {
56 fdecl::ConfigValue::Single(s) => match s {
57 fdecl::ConfigSingleValue::Bool(_)
58 | fdecl::ConfigSingleValue::Uint8(_)
59 | fdecl::ConfigSingleValue::Uint16(_)
60 | fdecl::ConfigSingleValue::Uint32(_)
61 | fdecl::ConfigSingleValue::Uint64(_)
62 | fdecl::ConfigSingleValue::Int8(_)
63 | fdecl::ConfigSingleValue::Int16(_)
64 | fdecl::ConfigSingleValue::Int32(_)
65 | fdecl::ConfigSingleValue::Int64(_)
66 | fdecl::ConfigSingleValue::String(_) => {}
67 fdecl::ConfigSingleValueUnknown!() => {
68 errors.push(Error::invalid_field(DeclType::ConfigValueSpec, "value"));
69 }
70 },
71 fdecl::ConfigValue::Vector(l) => match l {
72 fdecl::ConfigVectorValue::BoolVector(_)
73 | fdecl::ConfigVectorValue::Uint8Vector(_)
74 | fdecl::ConfigVectorValue::Uint16Vector(_)
75 | fdecl::ConfigVectorValue::Uint32Vector(_)
76 | fdecl::ConfigVectorValue::Uint64Vector(_)
77 | fdecl::ConfigVectorValue::Int8Vector(_)
78 | fdecl::ConfigVectorValue::Int16Vector(_)
79 | fdecl::ConfigVectorValue::Int32Vector(_)
80 | fdecl::ConfigVectorValue::Int64Vector(_)
81 | fdecl::ConfigVectorValue::StringVector(_) => {}
82 fdecl::ConfigVectorValueUnknown!() => {
83 errors.push(Error::invalid_field(DeclType::ConfigValueSpec, "value"));
84 }
85 },
86 fdecl::ConfigValueUnknown!() => {
87 errors.push(Error::invalid_field(DeclType::ConfigValueSpec, "value"));
88 }
89 }
90 } else {
91 errors.push(Error::missing_field(DeclType::ConfigValueSpec, "value"));
92 }
93
94 if errors.is_empty() {
95 Ok(())
96 } else {
97 Err(ErrorList::new(errors))
98 }
99}
100
101pub fn validate_values_data(data: &fdecl::ConfigValuesData) -> Result<(), ErrorList> {
109 let mut errors = vec![];
110 if let Some(values) = &data.values {
111 for spec in values {
112 if let Err(mut e) = validate_value_spec(spec) {
113 errors.append(&mut e.errs);
114 }
115 }
116 } else {
117 errors.push(Error::missing_field(DeclType::ConfigValuesData, "values"));
118 }
119
120 if let Some(checksum) = &data.checksum {
121 match checksum {
122 fdecl::ConfigChecksum::Sha256(_) => {}
123 fdecl::ConfigChecksumUnknown!() => {
124 errors.push(Error::invalid_field(DeclType::ConfigValuesData, "checksum"));
125 }
126 }
127 } else {
128 errors.push(Error::missing_field(DeclType::ConfigValuesData, "checksum"));
129 }
130
131 if errors.is_empty() {
132 Ok(())
133 } else {
134 Err(ErrorList::new(errors))
135 }
136}
137
138#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
140enum RefKey<'a> {
141 Parent,
142 Self_,
143 Child(&'a str),
144 Collection(&'a str),
145 Framework,
146 Capability,
147 Debug,
148}
149
150pub fn validate(decl: &fdecl::Component) -> Result<(), ErrorList> {
164 let ctx = ValidationContext::default();
165 ctx.validate(decl, None).map_err(|errs| ErrorList::new(errs))
166}
167
168fn validate_capabilities(
170 capabilities: &[fdecl::Capability],
171 as_builtin: bool,
172) -> Result<(), ErrorList> {
173 let mut ctx = ValidationContext::default();
174
175 #[cfg(fuchsia_api_level_at_least = "25")]
176 ctx.load_dictionary_names(capabilities.iter().filter_map(|capability| match capability {
177 fdecl::Capability::Dictionary(dictionary_decl) => Some(dictionary_decl),
178 _ => None,
179 }));
180
181 ctx.validate_capability_decls(capabilities, as_builtin);
182 if ctx.errors.is_empty() {
183 Ok(())
184 } else {
185 Err(ErrorList::new(ctx.errors))
186 }
187}
188
189pub fn validate_builtin_capabilities(
191 capabilities: &Vec<fdecl::Capability>,
192) -> Result<(), ErrorList> {
193 validate_capabilities(capabilities, true)
194}
195
196pub fn validate_namespace_capabilities(
198 capabilities: &Vec<fdecl::Capability>,
199) -> Result<(), ErrorList> {
200 validate_capabilities(capabilities, false)
201}
202
203type CheckChildNameFn = fn(Option<&String>, DeclType, &str, &mut Vec<Error>) -> bool;
206
207pub fn validate_dynamic_child(child: &fdecl::Child) -> Result<(), ErrorList> {
208 let mut errors = vec![];
209
210 if let Err(mut error_list) = validate_child(child, check_dynamic_name) {
211 errors.append(&mut error_list.errs);
212 }
213
214 if child.environment.is_some() {
215 errors.push(Error::DynamicChildWithEnvironment);
216 }
217
218 if errors.is_empty() {
219 Ok(())
220 } else {
221 Err(ErrorList { errs: errors })
222 }
223}
224
225fn validate_child(
228 child: &fdecl::Child,
229 check_child_name: CheckChildNameFn,
230) -> Result<(), ErrorList> {
231 let mut errors = vec![];
232 check_child_name(child.name.as_ref(), DeclType::Child, "name", &mut errors);
233 check_url(child.url.as_ref(), DeclType::Child, "url", &mut errors);
234 if child.startup.is_none() {
235 errors.push(Error::missing_field(DeclType::Child, "startup"));
236 }
237 if child.environment.is_some() {
239 check_name(child.environment.as_ref(), DeclType::Child, "environment", &mut errors);
240 }
241 if errors.is_empty() {
242 Ok(())
243 } else {
244 Err(ErrorList { errs: errors })
245 }
246}
247
248pub fn validate_dynamic_offers<'a>(
257 dynamic_children: Vec<(&'a str, &'a str)>,
258 offers: &'a Vec<fdecl::Offer>,
259 decl: &'a fdecl::Component,
260) -> Result<(), ErrorList> {
261 let mut ctx = ValidationContext::default();
262 ctx.dynamic_children = dynamic_children;
263 ctx.validate(decl, Some(offers)).map_err(|errs| ErrorList::new(errs))
264}
265
266fn check_offer_name(
267 prop: Option<&String>,
268 decl: DeclType,
269 keyword: &str,
270 offer_type: OfferType,
271 errors: &mut Vec<Error>,
272) -> bool {
273 if offer_type == OfferType::Dynamic {
274 check_dynamic_name(prop, decl, keyword, errors)
275 } else {
276 check_name(prop, decl, keyword, errors)
277 }
278}
279
280#[derive(Default)]
281struct ValidationContext<'a> {
282 all_children: HashMap<&'a str, &'a fdecl::Child>,
283 all_collections: HashSet<&'a str>,
284 all_capability_ids: HashSet<&'a str>,
285 all_storages: HashMap<&'a str, Option<&'a fdecl::Ref>>,
286 all_services: HashSet<&'a str>,
287 all_protocols: HashSet<&'a str>,
288 all_directories: HashSet<&'a str>,
289 all_runners: HashSet<&'a str>,
290 all_resolvers: HashSet<&'a str>,
291 #[cfg(fuchsia_api_level_at_least = "25")]
292 all_dictionaries: HashMap<&'a str, &'a fdecl::Dictionary>,
293
294 #[cfg(fuchsia_api_level_at_least = "HEAD")]
295 all_configs: HashSet<&'a str>,
296
297 all_environment_names: HashSet<&'a str>,
298 dynamic_children: Vec<(&'a str, &'a str)>,
299 strong_dependencies: DirectedGraph<DependencyNode<'a>>,
300 target_ids: IdMap<'a>,
301 errors: Vec<Error>,
302}
303
304trait Container {
308 fn contains(&self, key: &str) -> bool;
309}
310
311impl<'a> Container for HashSet<&'a str> {
312 fn contains(&self, key: &str) -> bool {
313 self.contains(key)
314 }
315}
316
317impl<'a, T> Container for HashMap<&'a str, T> {
318 fn contains(&self, key: &str) -> bool {
319 self.contains_key(key)
320 }
321}
322
323#[derive(Copy, Clone, Hash, Ord, Debug, PartialOrd, PartialEq, Eq)]
326enum DependencyNode<'a> {
327 Self_,
328 Child(&'a str, Option<&'a str>),
329 Collection(&'a str),
330 Environment(&'a str),
331 Capability(&'a str),
332}
333
334impl<'a> fmt::Display for DependencyNode<'a> {
335 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
336 match self {
337 DependencyNode::Self_ => write!(f, "self"),
338 DependencyNode::Child(name, None) => write!(f, "child {}", name),
339 DependencyNode::Child(name, Some(collection)) => {
340 write!(f, "child {}:{}", collection, name)
341 }
342 DependencyNode::Collection(name) => write!(f, "collection {}", name),
343 DependencyNode::Environment(name) => write!(f, "environment {}", name),
344 DependencyNode::Capability(name) => write!(f, "capability {}", name),
345 }
346 }
347}
348
349fn ref_to_dependency_node<'a>(ref_: Option<&'a fdecl::Ref>) -> Option<DependencyNode<'a>> {
350 match ref_? {
351 fdecl::Ref::Self_(_) => Some(DependencyNode::Self_),
352 fdecl::Ref::Child(fdecl::ChildRef { name, collection }) => {
353 Some(DependencyNode::Child(name, collection.as_ref().map(|s| s.as_str())))
354 }
355 fdecl::Ref::Collection(fdecl::CollectionRef { name }) => {
356 Some(DependencyNode::Collection(name))
357 }
358 fdecl::Ref::Capability(fdecl::CapabilityRef { name }) => {
359 Some(DependencyNode::Capability(name))
360 }
361 fdecl::Ref::Framework(_)
362 | fdecl::Ref::Parent(_)
363 | fdecl::Ref::Debug(_)
364 | fdecl::Ref::VoidType(_) => None,
365 #[cfg(fuchsia_api_level_at_least = "HEAD")]
366 fdecl::Ref::Environment(_) => None,
367 _ => None,
368 }
369}
370
371fn get_dependencies_from_uses<'a>(
372 strong_dependencies: &mut DirectedGraph<DependencyNode<'a>>,
373 decl: &'a fdecl::Component,
374) {
375 if let Some(uses) = decl.uses.as_ref() {
376 for use_ in uses.iter() {
377 let (dependency_type, source) = match use_ {
378 fdecl::Use::Service(u) => (u.dependency_type, &u.source),
379 fdecl::Use::Protocol(u) => (u.dependency_type, &u.source),
380 fdecl::Use::Directory(u) => (u.dependency_type, &u.source),
381 fdecl::Use::EventStream(u) => (Some(fdecl::DependencyType::Strong), &u.source),
382 #[cfg(fuchsia_api_level_at_least = "HEAD")]
383 fdecl::Use::Runner(u) => (Some(fdecl::DependencyType::Strong), &u.source),
384 #[cfg(fuchsia_api_level_at_least = "HEAD")]
385 fdecl::Use::Config(u) => (Some(fdecl::DependencyType::Strong), &u.source),
386 fdecl::Use::Storage(_) => continue,
388 _ => continue,
389 };
390 if dependency_type != Some(fdecl::DependencyType::Strong) {
391 continue;
392 }
393 if let Some(fdecl::Ref::Self_(_)) = &source {
394 continue;
395 }
396 if let Some(source_node) = ref_to_dependency_node(source.as_ref()) {
397 strong_dependencies.add_edge(source_node, DependencyNode::Self_);
398 }
399 }
400 }
401}
402
403fn get_dependencies_from_capabilities<'a>(
404 strong_dependencies: &mut DirectedGraph<DependencyNode<'a>>,
405 decl: &'a fdecl::Component,
406) {
407 if let Some(capabilities) = decl.capabilities.as_ref() {
408 for cap in capabilities {
409 match cap {
410 #[cfg(fuchsia_api_level_at_least = "25")]
411 fdecl::Capability::Dictionary(dictionary) => {
412 if dictionary.source_path.as_ref().is_some() {
413 if let Some(name) = dictionary.name.as_ref() {
414 strong_dependencies
417 .add_edge(DependencyNode::Self_, DependencyNode::Capability(name));
418 }
419 }
420 }
421 fdecl::Capability::Storage(storage) => {
422 if let (Some(name), Some(_backing_dir)) =
423 (storage.name.as_ref(), storage.backing_dir.as_ref())
424 {
425 if let Some(source_node) = ref_to_dependency_node(storage.source.as_ref()) {
426 strong_dependencies
427 .add_edge(source_node, DependencyNode::Capability(name));
428 }
429 }
430 }
431 _ => continue,
432 }
433 }
434 }
435}
436
437fn get_dependencies_from_environments<'a>(
438 strong_dependencies: &mut DirectedGraph<DependencyNode<'a>>,
439 decl: &'a fdecl::Component,
440) {
441 if let Some(environment) = decl.environments.as_ref() {
442 for environment in environment {
443 if let Some(name) = &environment.name {
444 let target = DependencyNode::Environment(name);
445 if let Some(debugs) = environment.debug_capabilities.as_ref() {
446 for debug in debugs {
447 if let fdecl::DebugRegistration::Protocol(o) = debug {
448 if let Some(source_node) = ref_to_dependency_node(o.source.as_ref()) {
449 strong_dependencies.add_edge(source_node, target);
450 }
451 }
452 }
453 }
454 if let Some(runners) = environment.runners.as_ref() {
455 for runner in runners {
456 if let Some(source_node) = ref_to_dependency_node(runner.source.as_ref()) {
457 strong_dependencies.add_edge(source_node, target);
458 }
459 }
460 }
461 if let Some(resolvers) = environment.resolvers.as_ref() {
462 for resolver in resolvers {
463 if let Some(source_node) = ref_to_dependency_node(resolver.source.as_ref())
464 {
465 strong_dependencies.add_edge(source_node, target);
466 }
467 }
468 }
469 }
470 }
471 }
472}
473
474fn generate_dependency_graph<'a>(
475 strong_dependencies: &mut DirectedGraph<DependencyNode<'a>>,
476 decl: &'a fdecl::Component,
477 dynamic_children: &Vec<(&'a str, &'a str)>,
478 dynamic_offers: Option<&'a Vec<fdecl::Offer>>,
479) {
480 get_dependencies_from_uses(strong_dependencies, decl);
481
482 let mut all_offers: Vec<&fdecl::Offer> = vec![];
483
484 if let Some(dynamic_offers) = dynamic_offers.as_ref() {
485 for dynamic_offer in dynamic_offers.iter() {
486 all_offers.push(dynamic_offer);
487 }
488 }
489
490 if let Some(offers) = decl.offers.as_ref() {
491 for offer in offers.iter() {
492 all_offers.push(offer);
493 }
494 }
495
496 for offer in all_offers {
497 let (_dependency_type, source, _target) = match offer {
498 fdecl::Offer::Protocol(o) => (o.dependency_type, &o.source, &o.target),
499 #[cfg(fuchsia_api_level_at_least = "25")]
500 fdecl::Offer::Dictionary(o) => (o.dependency_type, &o.source, &o.target),
501 fdecl::Offer::Directory(o) => (o.dependency_type, &o.source, &o.target),
502 fdecl::Offer::Service(o) => (None, &o.source, &o.target),
503 fdecl::Offer::Storage(o) => (None, &o.source, &o.target),
504 fdecl::Offer::Runner(o) => (Some(fdecl::DependencyType::Strong), &o.source, &o.target),
505 fdecl::Offer::Resolver(o) => {
506 (Some(fdecl::DependencyType::Strong), &o.source, &o.target)
507 }
508 fdecl::Offer::Config(o) => (Some(fdecl::DependencyType::Strong), &o.source, &o.target),
509 _ => continue,
510 };
511
512 if let Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: collection })) =
515 source.as_ref()
516 {
517 for name in
518 ValidationContext::dynamic_children_in_collection(dynamic_children, &collection)
519 {
520 strong_dependencies.add_edge(
521 DependencyNode::Child(&name, Some(&collection)),
522 DependencyNode::Collection(collection),
523 );
524 }
525 }
526 }
527
528 get_dependencies_from_capabilities(strong_dependencies, decl);
529 get_dependencies_from_environments(strong_dependencies, decl);
530
531 if let Some(children) = decl.children.as_ref() {
532 for child in children {
533 if let Some(name) = child.name.as_ref() {
534 if let Some(env) = child.environment.as_ref() {
535 let source = DependencyNode::Environment(env.as_str());
536 let target = DependencyNode::Child(name, None);
537 strong_dependencies.add_edge(source, target);
538 }
539 }
540 }
541 }
542
543 if let Some(collections) = decl.collections.as_ref() {
544 for collection in collections {
545 if let Some(env) = collection.environment.as_ref() {
546 if let Some(name) = collection.name.as_ref() {
547 let source = DependencyNode::Environment(env.as_str());
548 let target = DependencyNode::Collection(name.as_str());
549 strong_dependencies.add_edge(source, target);
550 }
551 }
552 }
553 }
554}
555
556impl<'a> ValidationContext<'a> {
557 fn validate(
558 mut self,
559 decl: &'a fdecl::Component,
560 dynamic_offers: Option<&'a Vec<fdecl::Offer>>,
561 ) -> Result<(), Vec<Error>> {
562 if let Some(envs) = &decl.environments {
564 self.collect_environment_names(&envs);
565 }
566
567 if let Some(children) = decl.children.as_ref() {
569 for child in children {
570 self.validate_child_decl(&child);
571 }
572 }
573
574 if let Some(collections) = decl.collections.as_ref() {
576 for collection in collections {
577 self.validate_collection_decl(&collection);
578 }
579 }
580
581 if let Some(capabilities) = decl.capabilities.as_ref() {
583 #[cfg(fuchsia_api_level_at_least = "25")]
584 self.load_dictionary_names(capabilities.iter().filter_map(
585 |capability| match capability {
586 fdecl::Capability::Dictionary(dictionary_decl) => Some(dictionary_decl),
587 _ => None,
588 },
589 ));
590 self.validate_capability_decls(capabilities, false);
591 }
592
593 let mut use_runner_name = None;
595 let mut use_runner_source = None;
596 if let Some(uses) = decl.uses.as_ref() {
597 (use_runner_name, use_runner_source) = self.validate_use_decls(uses);
598 }
599
600 if let Some(program) = decl.program.as_ref() {
602 self.validate_program(program, use_runner_name, use_runner_source);
603 }
604
605 if let Some(exposes) = decl.exposes.as_ref() {
607 let mut expose_to_parent_ids = HashMap::new();
608 let mut expose_to_framework_ids = HashMap::new();
609 for expose in exposes.iter() {
610 self.validate_expose_decl(
611 &expose,
612 &mut expose_to_parent_ids,
613 &mut expose_to_framework_ids,
614 );
615 }
616 self.validate_expose_group(&exposes);
617 }
618
619 if let Some(offers) = decl.offers.as_ref() {
621 for offer in offers.iter() {
622 self.validate_offer_decl(&offer, OfferType::Static);
623 }
624 self.validate_offer_group(&offers, OfferType::Static);
625 }
626
627 if let Some(dynamic_offers) = dynamic_offers.as_ref() {
628 for dynamic_offer in dynamic_offers.iter() {
629 self.validate_offer_decl(&dynamic_offer, OfferType::Dynamic);
630 }
631 self.validate_offer_group(&dynamic_offers, OfferType::Dynamic);
632 }
633
634 if let Some(environment) = decl.environments.as_ref() {
636 for environment in environment {
637 self.validate_environment_decl(&environment);
638 }
639 }
640
641 #[cfg(fuchsia_api_level_at_least = "20")]
643 self.validate_config(decl.config.as_ref(), decl.uses.as_ref());
644
645 generate_dependency_graph(
647 &mut self.strong_dependencies,
648 &decl,
649 &self.dynamic_children,
650 dynamic_offers,
651 );
652 if let Err(e) = self.strong_dependencies.topological_sort() {
653 self.errors.push(Error::dependency_cycle(e.format_cycle()));
654 }
655
656 if self.errors.is_empty() {
657 Ok(())
658 } else {
659 Err(self.errors)
660 }
661 }
662
663 fn collect_environment_names(&mut self, envs: &'a [fdecl::Environment]) {
665 for env in envs {
666 if let Some(name) = env.name.as_ref() {
667 if !self.all_environment_names.insert(name) {
668 self.errors.push(Error::duplicate_field(DeclType::Environment, "name", name));
669 }
670 }
671 }
672 }
673
674 #[cfg(fuchsia_api_level_at_least = "20")]
677 fn validate_config(
678 &mut self,
679 config: Option<&fdecl::ConfigSchema>,
680 uses: Option<&Vec<fdecl::Use>>,
681 ) {
682 use std::collections::BTreeMap;
683
684 let optional_use_keys: BTreeMap<String, fdecl::ConfigType> =
686 uses.map_or(BTreeMap::new(), |u| {
687 u.iter()
688 .map(|u| {
689 let fdecl::Use::Config(config) = u else {
690 return None;
691 };
692 if config.availability == Some(fdecl::Availability::Required)
693 || config.availability == None
694 {
695 return None;
696 }
697 if let Some(_) = config.default.as_ref() {
698 return None;
699 }
700 let Some(key) = config.target_name.clone() else {
701 return None;
702 };
703 let Some(value) = config.type_.clone() else {
704 return None;
705 };
706 Some((key, value))
707 })
708 .flatten()
709 .collect()
710 });
711
712 for u in uses.iter().flat_map(|x| x.iter()) {
714 let fdecl::Use::Config(config) = u else { continue };
715 let Some(default) = config.default.as_ref() else { continue };
716 validate_value_spec(&fdecl::ConfigValueSpec {
717 value: Some(default.clone()),
718 ..Default::default()
719 })
720 .map_err(|mut e| self.errors.append(&mut e.errs))
721 .ok();
722 }
723
724 let Some(config) = config else {
725 if !optional_use_keys.is_empty() {
726 self.errors.push(Error::missing_field(DeclType::ConfigField, "config"))
727 }
728 return;
729 };
730
731 if let Some(fields) = &config.fields {
732 for field in fields {
733 if field.key.is_none() {
734 self.errors.push(Error::missing_field(DeclType::ConfigField, "key"));
735 }
736 if let Some(type_) = &field.type_ {
737 self.validate_config_type(type_, true);
738 } else {
739 self.errors.push(Error::missing_field(DeclType::ConfigField, "value_type"));
740 }
741 }
742 } else {
743 self.errors.push(Error::missing_field(DeclType::ConfigSchema, "fields"));
744 }
745
746 if let Some(checksum) = &config.checksum {
747 match checksum {
748 fdecl::ConfigChecksum::Sha256(_) => {}
749 fdecl::ConfigChecksumUnknown!() => {
750 self.errors.push(Error::invalid_field(DeclType::ConfigSchema, "checksum"));
751 }
752 }
753 } else {
754 self.errors.push(Error::missing_field(DeclType::ConfigSchema, "checksum"));
755 }
756
757 'outer: for (key, value) in optional_use_keys.iter() {
758 for field in config.fields.iter().flatten() {
759 if field.key.as_ref() == Some(key) {
760 if field.type_.as_ref() != Some(value) {
761 self.errors.push(Error::invalid_field(DeclType::ConfigField, key.clone()));
762 }
763 continue 'outer;
764 }
765 }
766 self.errors.push(Error::missing_field(DeclType::ConfigField, key.clone()));
767 }
768
769 match config.value_source {
770 None => self.errors.push(Error::missing_field(DeclType::ConfigSchema, "value_source")),
771 #[cfg(fuchsia_api_level_at_least = "HEAD")]
772 Some(fdecl::ConfigValueSource::Capabilities(_)) => {
773 if !optional_use_keys.is_empty() {
774 self.errors
775 .push(Error::invalid_field(DeclType::ConfigValueSource, "ValueSource"))
776 }
777 }
778 Some(fdecl::ConfigValueSource::__SourceBreaking { .. }) => {
779 self.errors.push(Error::invalid_field(DeclType::ConfigValueSource, "ValueSource"))
780 }
781 _ => (),
782 };
783 }
784
785 #[cfg(fuchsia_api_level_at_least = "20")]
786 fn validate_config_type(&mut self, type_: &fdecl::ConfigType, accept_vectors: bool) {
787 match &type_.layout {
788 fdecl::ConfigTypeLayout::Bool
789 | fdecl::ConfigTypeLayout::Uint8
790 | fdecl::ConfigTypeLayout::Uint16
791 | fdecl::ConfigTypeLayout::Uint32
792 | fdecl::ConfigTypeLayout::Uint64
793 | fdecl::ConfigTypeLayout::Int8
794 | fdecl::ConfigTypeLayout::Int16
795 | fdecl::ConfigTypeLayout::Int32
796 | fdecl::ConfigTypeLayout::Int64 => {
797 if let Some(parameters) = &type_.parameters {
799 if !parameters.is_empty() {
800 self.errors
801 .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
802 }
803 } else {
804 self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"));
805 }
806
807 if !type_.constraints.is_empty() {
808 self.errors.push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
809 }
810 }
811 fdecl::ConfigTypeLayout::String => {
812 if let Some(parameters) = &type_.parameters {
814 if !parameters.is_empty() {
815 self.errors
816 .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
817 }
818 } else {
819 self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"));
820 }
821
822 if type_.constraints.is_empty() {
823 self.errors.push(Error::missing_field(DeclType::ConfigType, "constraints"));
824 } else if type_.constraints.len() > 1 {
825 self.errors.push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
826 } else if let fdecl::LayoutConstraint::MaxSize(_) = &type_.constraints[0] {
827 } else {
828 self.errors.push(Error::invalid_field(DeclType::ConfigType, "constraints"));
829 }
830 }
831 fdecl::ConfigTypeLayout::Vector => {
832 if accept_vectors {
833 if let Some(parameters) = &type_.parameters {
835 if parameters.is_empty() {
836 self.errors
837 .push(Error::missing_field(DeclType::ConfigType, "parameters"));
838 } else if parameters.len() > 1 {
839 self.errors
840 .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
841 } else if let fdecl::LayoutParameter::NestedType(nested_type) =
842 ¶meters[0]
843 {
844 self.validate_config_type(nested_type, false);
845 } else {
846 self.errors
847 .push(Error::invalid_field(DeclType::ConfigType, "parameters"));
848 }
849 } else {
850 self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"))
851 }
852
853 if type_.constraints.is_empty() {
854 self.errors.push(Error::missing_field(DeclType::ConfigType, "constraints"));
855 } else if type_.constraints.len() > 1 {
856 self.errors
857 .push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
858 } else if let fdecl::LayoutConstraint::MaxSize(_) = &type_.constraints[0] {
859 } else {
860 self.errors.push(Error::invalid_field(DeclType::ConfigType, "constraints"));
861 }
862 } else {
863 self.errors.push(Error::nested_vector());
864 }
865 }
866 _ => self.errors.push(Error::invalid_field(DeclType::ConfigType, "layout")),
867 }
868 }
869
870 fn validate_capability_decls(
871 &mut self,
872 capabilities: &'a [fdecl::Capability],
873 as_builtin: bool,
874 ) {
875 for capability in capabilities {
876 self.validate_capability_decl(capability, as_builtin);
877 }
878 }
879
880 fn validate_capability_decl(&mut self, capability: &'a fdecl::Capability, as_builtin: bool) {
885 match capability {
886 fdecl::Capability::Service(service) => self.validate_service_decl(&service, as_builtin),
887 fdecl::Capability::Protocol(protocol) => {
888 self.validate_protocol_decl(&protocol, as_builtin)
889 }
890 fdecl::Capability::Directory(directory) => {
891 self.validate_directory_decl(&directory, as_builtin)
892 }
893 fdecl::Capability::Storage(storage) => {
894 if as_builtin {
895 self.errors.push(Error::CapabilityCannotBeBuiltin(DeclType::Storage))
896 } else {
897 self.validate_storage_decl(&storage)
898 }
899 }
900 fdecl::Capability::Runner(runner) => self.validate_runner_decl(&runner, as_builtin),
901 fdecl::Capability::Resolver(resolver) => {
902 self.validate_resolver_decl(&resolver, as_builtin)
903 }
904 fdecl::Capability::EventStream(event) => {
905 if as_builtin {
906 self.validate_event_stream_decl(&event)
907 } else {
908 self.errors.push(Error::CapabilityMustBeBuiltin(DeclType::EventStream))
909 }
910 }
911 #[cfg(fuchsia_api_level_at_least = "25")]
912 fdecl::Capability::Dictionary(dictionary) => {
913 self.validate_dictionary_decl(&dictionary);
914 }
915 #[cfg(fuchsia_api_level_at_least = "HEAD")]
916 fdecl::Capability::Config(config) => {
917 self.validate_configuration_decl(&config);
918 }
919 fdecl::CapabilityUnknown!() => self.errors.push(Error::UnknownCapability),
920 }
921 }
922
923 fn validate_use_decls(
925 &mut self,
926 uses: &'a [fdecl::Use],
927 ) -> (Option<&'a String>, Option<&'a fdecl::Ref>) {
928 for use_ in uses.iter() {
930 self.validate_use_decl(&use_);
931 }
932 self.validate_use_paths(&uses);
933
934 #[cfg(fuchsia_api_level_at_least = "HEAD")]
935 {
936 let mut use_runner_name = None;
937 let mut use_runner_source = None;
938 for use_ in uses.iter() {
939 if let fdecl::Use::Runner(use_runner) = use_ {
940 if use_runner_name.is_some() {
941 self.errors.push(Error::MultipleRunnersUsed);
942 }
943
944 use_runner_name = use_runner.source_name.as_ref();
945 use_runner_source = use_runner.source.as_ref();
946 }
947 }
948 return (use_runner_name, use_runner_source);
949 }
950 #[cfg(fuchsia_api_level_less_than = "HEAD")]
951 return (None, None);
952 }
953
954 fn validate_use_decl(&mut self, use_: &'a fdecl::Use) {
955 match use_ {
956 fdecl::Use::Service(u) => {
957 let decl = DeclType::UseService;
958 self.validate_use_fields(
959 decl,
960 Self::service_checker,
961 u.source.as_ref(),
962 u.source_name.as_ref(),
963 get_source_dictionary!(u),
964 u.target_path.as_ref(),
965 u.dependency_type.as_ref(),
966 u.availability.as_ref(),
967 );
968 if u.dependency_type.is_none() {
969 self.errors.push(Error::missing_field(decl, "dependency_type"));
970 }
971 }
972 fdecl::Use::Protocol(u) => {
973 let decl = DeclType::UseProtocol;
974 self.validate_use_fields(
975 decl,
976 Self::protocol_checker,
977 u.source.as_ref(),
978 u.source_name.as_ref(),
979 get_source_dictionary!(u),
980 u.target_path.as_ref(),
981 u.dependency_type.as_ref(),
982 u.availability.as_ref(),
983 );
984 if u.dependency_type.is_none() {
985 self.errors.push(Error::missing_field(decl, "dependency_type"));
986 }
987 }
988 fdecl::Use::Directory(u) => {
989 let decl = DeclType::UseDirectory;
990 self.validate_use_fields(
991 decl,
992 Self::directory_checker,
993 u.source.as_ref(),
994 u.source_name.as_ref(),
995 get_source_dictionary!(u),
996 u.target_path.as_ref(),
997 u.dependency_type.as_ref(),
998 u.availability.as_ref(),
999 );
1000 if u.dependency_type.is_none() {
1001 self.errors.push(Error::missing_field(decl, "dependency_type"));
1002 }
1003 if u.rights.is_none() {
1004 self.errors.push(Error::missing_field(DeclType::UseDirectory, "rights"));
1005 }
1006 if let Some(subdir) = u.subdir.as_ref() {
1007 check_relative_path(
1008 Some(subdir),
1009 DeclType::UseDirectory,
1010 "subdir",
1011 &mut self.errors,
1012 );
1013 }
1014 }
1015 fdecl::Use::Storage(u) => {
1016 const SOURCE: Option<fdecl::Ref> = Some(fdecl::Ref::Parent(fdecl::ParentRef {}));
1017 const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
1018 Some(fdecl::DependencyType::Strong);
1019 self.validate_use_fields(
1020 DeclType::UseStorage,
1021 Self::storage_checker,
1022 SOURCE.as_ref(),
1023 u.source_name.as_ref(),
1024 None,
1025 u.target_path.as_ref(),
1026 DEPENDENCY_TYPE.as_ref(),
1027 u.availability.as_ref(),
1028 );
1029 }
1030 fdecl::Use::EventStream(u) => {
1031 const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
1032 Some(fdecl::DependencyType::Strong);
1033 let decl = DeclType::UseEventStream;
1034 self.validate_use_fields(
1035 decl,
1036 Self::event_stream_checker,
1037 u.source.as_ref(),
1038 u.source_name.as_ref(),
1039 None,
1040 u.target_path.as_ref(),
1041 DEPENDENCY_TYPE.as_ref(),
1042 u.availability.as_ref(),
1043 );
1044 match u.source {
1046 Some(fdecl::Ref::Child(_)) | Some(fdecl::Ref::Parent(_)) => {
1047 }
1049 Some(fdecl::Ref::Framework(_))
1050 | Some(fdecl::Ref::Self_(_))
1051 | Some(fdecl::Ref::Debug(_)) => {
1052 self.errors.push(Error::invalid_field(decl, "source"));
1054 }
1055 Some(fdecl::Ref::Collection(_)) | Some(fdecl::RefUnknown!()) | None => {
1056 }
1058 }
1059 if let Some(scope) = &u.scope {
1060 for reference in scope {
1061 if !matches!(reference, fdecl::Ref::Child(_) | fdecl::Ref::Collection(_)) {
1062 self.errors.push(Error::invalid_field(decl, "scope"));
1063 }
1064 }
1065 }
1066 }
1067 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1068 fdecl::Use::Runner(u) => {
1069 const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
1070 Some(fdecl::DependencyType::Strong);
1071 const AVAILABILITY: Option<fdecl::Availability> =
1072 Some(fdecl::Availability::Required);
1073 let decl = DeclType::UseRunner;
1074 self.validate_use_fields(
1075 decl,
1076 Self::runner_checker,
1077 u.source.as_ref(),
1078 u.source_name.as_ref(),
1079 get_source_dictionary!(u),
1080 None,
1081 DEPENDENCY_TYPE.as_ref(),
1082 AVAILABILITY.as_ref(),
1083 );
1084 }
1085 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1086 fdecl::Use::Config(u) => {
1087 const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
1088 Some(fdecl::DependencyType::Strong);
1089 let decl = DeclType::UseConfiguration;
1090 self.validate_use_fields(
1091 decl,
1092 Self::config_checker,
1093 u.source.as_ref(),
1094 u.source_name.as_ref(),
1095 None,
1096 None,
1097 DEPENDENCY_TYPE.as_ref(),
1098 u.availability.as_ref(),
1099 );
1100 }
1101 fdecl::UseUnknown!() => {
1102 self.errors.push(Error::invalid_field(DeclType::Component, "use"));
1103 }
1104 }
1105 }
1106
1107 fn validate_program(
1110 &mut self,
1111 program: &fdecl::Program,
1112 use_runner_name: Option<&String>,
1113 _use_runner_source: Option<&fdecl::Ref>,
1114 ) {
1115 match &program.runner {
1116 Some(_) =>
1117 {
1118 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1119 if use_runner_name.is_some() {
1120 if use_runner_name != program.runner.as_ref()
1121 || _use_runner_source
1122 != Some(&fdecl::Ref::Environment(fdecl::EnvironmentRef))
1123 {
1124 self.errors.push(Error::ConflictingRunners);
1125 }
1126 }
1127 }
1128 None => {
1129 if use_runner_name.is_none() {
1130 self.errors.push(Error::MissingRunner);
1131 }
1132 }
1133 }
1134
1135 if program.info.is_none() {
1136 self.errors.push(Error::missing_field(DeclType::Program, "info"));
1137 }
1138 }
1139
1140 fn validate_use_paths(&mut self, uses: &[fdecl::Use]) {
1143 #[derive(Debug, PartialEq, Clone, Copy)]
1144 struct PathCapability<'a> {
1145 decl: DeclType,
1146 dir: &'a Path,
1147 use_: &'a fdecl::Use,
1148 }
1149 let mut used_paths = HashMap::new();
1150 for use_ in uses.iter() {
1151 match use_ {
1152 fdecl::Use::Service(fdecl::UseService { target_path: Some(path), .. })
1153 | fdecl::Use::Protocol(fdecl::UseProtocol { target_path: Some(path), .. })
1154 | fdecl::Use::Directory(fdecl::UseDirectory { target_path: Some(path), .. })
1155 | fdecl::Use::Storage(fdecl::UseStorage { target_path: Some(path), .. }) => {
1156 let capability = match use_ {
1157 fdecl::Use::Service(_) => {
1158 let dir = match Path::new(path).parent() {
1159 Some(p) => p,
1160 None => continue, };
1162 PathCapability { decl: DeclType::UseService, dir, use_ }
1163 }
1164 fdecl::Use::Protocol(_) => {
1165 let dir = match Path::new(path).parent() {
1166 Some(p) => p,
1167 None => continue, };
1169 PathCapability { decl: DeclType::UseProtocol, dir, use_ }
1170 }
1171 fdecl::Use::Directory(_) => PathCapability {
1172 decl: DeclType::UseDirectory,
1173 dir: Path::new(path),
1174 use_,
1175 },
1176 fdecl::Use::Storage(_) => PathCapability {
1177 decl: DeclType::UseStorage,
1178 dir: Path::new(path),
1179 use_,
1180 },
1181 _ => unreachable!(),
1182 };
1183 if used_paths.insert(path, capability).is_some() {
1184 self.errors.push(Error::duplicate_field(
1186 capability.decl,
1187 "target_path",
1188 path,
1189 ));
1190 }
1191 }
1192 _ => {}
1193 }
1194 }
1195 for ((&path_a, capability_a), (&path_b, capability_b)) in
1196 used_paths.iter().tuple_combinations()
1197 {
1198 if match (capability_a.use_, capability_b.use_) {
1199 (fdecl::Use::Directory(_), fdecl::Use::Directory(_))
1201 | (fdecl::Use::Storage(_), fdecl::Use::Directory(_))
1202 | (fdecl::Use::Directory(_), fdecl::Use::Storage(_))
1203 | (fdecl::Use::Storage(_), fdecl::Use::Storage(_)) => {
1204 capability_b.dir == capability_a.dir
1205 || capability_b.dir.starts_with(capability_a.dir)
1206 || capability_a.dir.starts_with(capability_b.dir)
1207 }
1208
1209 (_, fdecl::Use::Directory(_)) | (fdecl::Use::Directory(_), _) => {
1211 capability_b.dir == capability_a.dir
1212 || capability_b.dir.starts_with(capability_a.dir)
1213 || capability_a.dir.starts_with(capability_b.dir)
1214 }
1215
1216 (_, _) => {
1219 capability_b.dir != capability_a.dir
1220 && (capability_b.dir.starts_with(capability_a.dir)
1221 || capability_a.dir.starts_with(capability_b.dir))
1222 }
1223 } {
1224 self.errors.push(Error::invalid_path_overlap(
1225 capability_a.decl,
1226 path_a,
1227 capability_b.decl,
1228 path_b,
1229 ));
1230 }
1231 }
1232 for (used_path, capability) in used_paths.iter() {
1233 if used_path.as_str() == "/pkg" || used_path.starts_with("/pkg/") {
1234 self.errors.push(Error::pkg_path_overlap(capability.decl, *used_path));
1235 }
1236 }
1237 }
1238
1239 fn validate_use_fields(
1240 &mut self,
1241 decl: DeclType,
1242 capability_checker: impl Fn(&Self) -> &dyn Container,
1246 source: Option<&'a fdecl::Ref>,
1247 source_name: Option<&'a String>,
1248 source_dictionary: Option<&'a String>,
1249 target_path: Option<&'a String>,
1250 dependency_type: Option<&fdecl::DependencyType>,
1251 availability: Option<&'a fdecl::Availability>,
1252 ) {
1253 self.validate_use_source(decl, source, source_dictionary);
1254
1255 check_name(source_name, decl, "source_name", &mut self.errors);
1256 if source_dictionary.is_some() {
1257 check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
1258 }
1259 if decl != DeclType::UseRunner && decl != DeclType::UseConfiguration {
1260 check_path(target_path, decl, "target_path", &mut self.errors);
1261 }
1262 check_use_availability(decl, availability, &mut self.errors);
1263
1264 let is_use_from_child = match source {
1266 Some(fdecl::Ref::Child(_)) => true,
1267 _ => false,
1268 };
1269 match (is_use_from_child, dependency_type) {
1270 (false, Some(fdecl::DependencyType::Weak)) => {
1271 self.errors.push(Error::invalid_field(decl, "dependency_type"));
1272 }
1273 _ => {}
1274 }
1275
1276 self.validate_route_from_self(
1277 decl,
1278 source,
1279 source_name,
1280 source_dictionary,
1281 capability_checker,
1282 );
1283 }
1284
1285 fn validate_use_source(
1286 &mut self,
1287 decl: DeclType,
1288 source: Option<&'a fdecl::Ref>,
1289 source_dictionary: Option<&'a String>,
1290 ) {
1291 match (source, source_dictionary) {
1292 (Some(fdecl::Ref::Parent(_)), _) => {}
1294 (Some(fdecl::Ref::Self_(_)), _) => {}
1295 (Some(fdecl::Ref::Child(child)), _) => {
1296 self.validate_child_ref(decl, "source", &child, OfferType::Static);
1297 return;
1298 }
1299 (Some(fdecl::Ref::Framework(_)), None) => {}
1301 (Some(fdecl::Ref::Debug(_)), None) => {}
1302 (Some(fdecl::Ref::Capability(c)), None) => {
1303 self.validate_source_capability(&c, decl, "source");
1304 return;
1305 }
1306 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1307 (Some(fdecl::Ref::Environment(_)), None) => {}
1308 (Some(fdecl::Ref::Collection(collection)), None) if decl == DeclType::UseService => {
1309 self.validate_collection_ref(decl, "source", &collection);
1310 return;
1311 }
1312 (None, _) => self.errors.push(Error::missing_field(decl, "source")),
1314 (_, _) => self.errors.push(Error::invalid_field(decl, "source")),
1316 }
1317 }
1318
1319 fn validate_child_decl(&mut self, child: &'a fdecl::Child) {
1320 if let Err(mut e) = validate_child(child, check_name) {
1321 self.errors.append(&mut e.errs);
1322 }
1323 if let Some(name) = child.name.as_ref() {
1324 let name: &str = name;
1325 if self.all_children.insert(name, child).is_some() {
1326 self.errors.push(Error::duplicate_field(DeclType::Child, "name", name));
1327 }
1328 }
1329 if let Some(environment) = child.environment.as_ref() {
1330 if !self.all_environment_names.contains(environment.as_str()) {
1331 self.errors.push(Error::invalid_environment(
1332 DeclType::Child,
1333 "environment",
1334 environment,
1335 ));
1336 }
1337 }
1338 }
1339
1340 fn validate_collection_decl(&mut self, collection: &'a fdecl::Collection) {
1341 let name = collection.name.as_ref();
1342 if check_name(name, DeclType::Collection, "name", &mut self.errors) {
1343 let name: &str = name.unwrap();
1344 if !self.all_collections.insert(name) {
1345 self.errors.push(Error::duplicate_field(DeclType::Collection, "name", name));
1346 }
1347 }
1348 if collection.durability.is_none() {
1349 self.errors.push(Error::missing_field(DeclType::Collection, "durability"));
1350 }
1351 if let Some(environment) = collection.environment.as_ref() {
1352 if !self.all_environment_names.contains(environment.as_str()) {
1353 self.errors.push(Error::invalid_environment(
1354 DeclType::Collection,
1355 "environment",
1356 environment,
1357 ));
1358 }
1359 }
1360 }
1362
1363 fn validate_environment_decl(&mut self, environment: &'a fdecl::Environment) {
1364 let name = environment.name.as_ref();
1365 check_name(name, DeclType::Environment, "name", &mut self.errors);
1366 if environment.extends.is_none() {
1367 self.errors.push(Error::missing_field(DeclType::Environment, "extends"));
1368 }
1369 if let Some(runners) = environment.runners.as_ref() {
1370 let mut registered_runners = HashSet::new();
1371 for runner in runners {
1372 self.validate_runner_registration(runner, name.clone(), &mut registered_runners);
1373 }
1374 }
1375 if let Some(resolvers) = environment.resolvers.as_ref() {
1376 let mut registered_schemes = HashSet::new();
1377 for resolver in resolvers {
1378 self.validate_resolver_registration(
1379 resolver,
1380 name.clone(),
1381 &mut registered_schemes,
1382 );
1383 }
1384 }
1385
1386 match environment.extends.as_ref() {
1387 Some(fdecl::EnvironmentExtends::None) => {
1388 if environment.stop_timeout_ms.is_none() {
1389 self.errors
1390 .push(Error::missing_field(DeclType::Environment, "stop_timeout_ms"));
1391 }
1392 }
1393 None | Some(fdecl::EnvironmentExtends::Realm) => {}
1394 }
1395
1396 if let Some(debugs) = environment.debug_capabilities.as_ref() {
1397 for debug in debugs {
1398 self.validate_environment_debug_registration(debug);
1399 }
1400 }
1401 }
1402
1403 fn validate_runner_registration(
1404 &mut self,
1405 runner_registration: &'a fdecl::RunnerRegistration,
1406 environment_name: Option<&'a String>,
1407 runner_names: &mut HashSet<&'a str>,
1408 ) {
1409 check_name(
1410 runner_registration.source_name.as_ref(),
1411 DeclType::RunnerRegistration,
1412 "source_name",
1413 &mut self.errors,
1414 );
1415 self.validate_registration_source(
1416 environment_name,
1417 runner_registration.source.as_ref(),
1418 DeclType::RunnerRegistration,
1419 );
1420 if let (Some(fdecl::Ref::Self_(_)), Some(ref name)) =
1422 (&runner_registration.source, &runner_registration.source_name)
1423 {
1424 if !self.all_runners.contains(name as &str) {
1425 self.errors.push(Error::invalid_runner(
1426 DeclType::RunnerRegistration,
1427 "source_name",
1428 name,
1429 ));
1430 }
1431 }
1432
1433 check_name(
1434 runner_registration.target_name.as_ref(),
1435 DeclType::RunnerRegistration,
1436 "target_name",
1437 &mut self.errors,
1438 );
1439 if let Some(name) = runner_registration.target_name.as_ref() {
1440 if !runner_names.insert(name.as_str()) {
1441 self.errors.push(Error::duplicate_field(
1442 DeclType::RunnerRegistration,
1443 "target_name",
1444 name,
1445 ));
1446 }
1447 }
1448 }
1449
1450 fn validate_resolver_registration(
1451 &mut self,
1452 resolver_registration: &'a fdecl::ResolverRegistration,
1453 environment_name: Option<&'a String>,
1454 schemes: &mut HashSet<&'a str>,
1455 ) {
1456 check_name(
1457 resolver_registration.resolver.as_ref(),
1458 DeclType::ResolverRegistration,
1459 "resolver",
1460 &mut self.errors,
1461 );
1462 self.validate_registration_source(
1463 environment_name,
1464 resolver_registration.source.as_ref(),
1465 DeclType::ResolverRegistration,
1466 );
1467 check_url_scheme(
1468 resolver_registration.scheme.as_ref(),
1469 DeclType::ResolverRegistration,
1470 "scheme",
1471 &mut self.errors,
1472 );
1473 if let Some(scheme) = resolver_registration.scheme.as_ref() {
1474 if !schemes.insert(scheme.as_str()) {
1475 self.errors.push(Error::duplicate_field(
1476 DeclType::ResolverRegistration,
1477 "scheme",
1478 scheme,
1479 ));
1480 }
1481 }
1482 }
1483
1484 fn validate_registration_source(
1485 &mut self,
1486 environment_name: Option<&'a String>,
1487 source: Option<&'a fdecl::Ref>,
1488 ty: DeclType,
1489 ) {
1490 match source {
1491 Some(fdecl::Ref::Parent(_)) => {}
1492 Some(fdecl::Ref::Self_(_)) => {}
1493 Some(fdecl::Ref::Child(child_ref)) => {
1494 self.validate_child_ref(ty, "source", &child_ref, OfferType::Static);
1496 }
1497 Some(_) => {
1498 self.errors.push(Error::invalid_field(ty, "source"));
1499 }
1500 None => {
1501 self.errors.push(Error::missing_field(ty, "source"));
1502 }
1503 }
1504
1505 let source = self.source_dependency_from_ref(None, None, source);
1506 if let Some(source) = source {
1507 if let Some(env_name) = &environment_name {
1508 let target = DependencyNode::Environment(env_name);
1509 self.strong_dependencies.add_edge(source, target);
1510 }
1511 }
1512 }
1513
1514 fn validate_service_decl(&mut self, service: &'a fdecl::Service, as_builtin: bool) {
1515 if check_name(service.name.as_ref(), DeclType::Service, "name", &mut self.errors) {
1516 let name = service.name.as_ref().unwrap();
1517 if !self.all_capability_ids.insert(name) {
1518 self.errors.push(Error::duplicate_field(DeclType::Service, "name", name.as_str()));
1519 }
1520 self.all_services.insert(name);
1521 }
1522 match as_builtin {
1523 true => {
1524 if let Some(path) = service.source_path.as_ref() {
1525 self.errors.push(Error::extraneous_source_path(DeclType::Service, path))
1526 }
1527 }
1528 false => {
1529 check_path(
1530 service.source_path.as_ref(),
1531 DeclType::Service,
1532 "source_path",
1533 &mut self.errors,
1534 );
1535 }
1536 }
1537 }
1538
1539 fn validate_protocol_decl(&mut self, protocol: &'a fdecl::Protocol, as_builtin: bool) {
1540 if check_name(protocol.name.as_ref(), DeclType::Protocol, "name", &mut self.errors) {
1541 let name = protocol.name.as_ref().unwrap();
1542 if !self.all_capability_ids.insert(name) {
1543 self.errors.push(Error::duplicate_field(DeclType::Protocol, "name", name.as_str()));
1544 }
1545 self.all_protocols.insert(name);
1546 }
1547 match as_builtin {
1548 true => {
1549 if let Some(path) = protocol.source_path.as_ref() {
1550 self.errors.push(Error::extraneous_source_path(DeclType::Protocol, path))
1551 }
1552 }
1553 false => {
1554 check_path(
1555 protocol.source_path.as_ref(),
1556 DeclType::Protocol,
1557 "source_path",
1558 &mut self.errors,
1559 );
1560 }
1561 }
1562
1563 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1564 match protocol.delivery {
1565 Some(delivery) => match cm_types::DeliveryType::try_from(delivery) {
1566 Ok(_) => {}
1567 Err(_) => self.errors.push(Error::invalid_field(DeclType::Protocol, "delivery")),
1568 },
1569 None => {}
1570 }
1571 }
1572
1573 fn validate_directory_decl(&mut self, directory: &'a fdecl::Directory, as_builtin: bool) {
1574 if check_name(directory.name.as_ref(), DeclType::Directory, "name", &mut self.errors) {
1575 let name = directory.name.as_ref().unwrap();
1576 if !self.all_capability_ids.insert(name) {
1577 self.errors.push(Error::duplicate_field(
1578 DeclType::Directory,
1579 "name",
1580 name.as_str(),
1581 ));
1582 }
1583 self.all_directories.insert(name);
1584 }
1585 match as_builtin {
1586 true => {
1587 if let Some(path) = directory.source_path.as_ref() {
1588 self.errors.push(Error::extraneous_source_path(DeclType::Directory, path))
1589 }
1590 }
1591 false => {
1592 check_path(
1593 directory.source_path.as_ref(),
1594 DeclType::Directory,
1595 "source_path",
1596 &mut self.errors,
1597 );
1598 }
1599 }
1600 if directory.rights.is_none() {
1601 self.errors.push(Error::missing_field(DeclType::Directory, "rights"));
1602 }
1603 }
1604
1605 fn validate_storage_decl(&mut self, storage: &'a fdecl::Storage) {
1606 match storage.source.as_ref() {
1607 Some(fdecl::Ref::Parent(_)) => {}
1608 Some(fdecl::Ref::Self_(_)) => {}
1609 Some(fdecl::Ref::Child(child)) => {
1610 let _ =
1611 self.validate_child_ref(DeclType::Storage, "source", &child, OfferType::Static);
1612 }
1613 Some(_) => {
1614 self.errors.push(Error::invalid_field(DeclType::Storage, "source"));
1615 }
1616 None => {
1617 self.errors.push(Error::missing_field(DeclType::Storage, "source"));
1618 }
1619 };
1620 if check_name(storage.name.as_ref(), DeclType::Storage, "name", &mut self.errors) {
1621 let name = storage.name.as_ref().unwrap();
1622 if !self.all_capability_ids.insert(name) {
1623 self.errors.push(Error::duplicate_field(DeclType::Storage, "name", name.as_str()));
1624 }
1625 self.all_storages.insert(name, storage.source.as_ref());
1626 }
1627 if storage.storage_id.is_none() {
1628 self.errors.push(Error::missing_field(DeclType::Storage, "storage_id"));
1629 }
1630 check_name(
1631 storage.backing_dir.as_ref(),
1632 DeclType::Storage,
1633 "backing_dir",
1634 &mut self.errors,
1635 );
1636 }
1637
1638 fn validate_runner_decl(&mut self, runner: &'a fdecl::Runner, as_builtin: bool) {
1639 if check_name(runner.name.as_ref(), DeclType::Runner, "name", &mut self.errors) {
1640 let name = runner.name.as_ref().unwrap();
1641 if !self.all_capability_ids.insert(name) {
1642 self.errors.push(Error::duplicate_field(DeclType::Runner, "name", name.as_str()));
1643 }
1644 self.all_runners.insert(name);
1645 }
1646 match as_builtin {
1647 true => {
1648 if let Some(path) = runner.source_path.as_ref() {
1649 self.errors.push(Error::extraneous_source_path(DeclType::Runner, path))
1650 }
1651 }
1652 false => {
1653 check_path(
1654 runner.source_path.as_ref(),
1655 DeclType::Runner,
1656 "source_path",
1657 &mut self.errors,
1658 );
1659 }
1660 }
1661 }
1662
1663 fn validate_resolver_decl(&mut self, resolver: &'a fdecl::Resolver, as_builtin: bool) {
1664 if check_name(resolver.name.as_ref(), DeclType::Resolver, "name", &mut self.errors) {
1665 let name = resolver.name.as_ref().unwrap();
1666 if !self.all_capability_ids.insert(name) {
1667 self.errors.push(Error::duplicate_field(DeclType::Resolver, "name", name.as_str()));
1668 }
1669 self.all_resolvers.insert(name);
1670 }
1671 match as_builtin {
1672 true => {
1673 if let Some(path) = resolver.source_path.as_ref() {
1674 self.errors.push(Error::extraneous_source_path(DeclType::Resolver, path))
1675 }
1676 }
1677 false => {
1678 check_path(
1679 resolver.source_path.as_ref(),
1680 DeclType::Resolver,
1681 "source_path",
1682 &mut self.errors,
1683 );
1684 }
1685 }
1686 }
1687
1688 #[cfg(fuchsia_api_level_at_least = "25")]
1692 fn load_dictionary_names(&mut self, dictionaries: impl Iterator<Item = &'a fdecl::Dictionary>) {
1693 for dictionary in dictionaries {
1694 let decl = DeclType::Dictionary;
1695 if check_name(dictionary.name.as_ref(), decl, "name", &mut self.errors) {
1696 let name = dictionary.name.as_ref().unwrap();
1697 if !self.all_capability_ids.insert(name) {
1698 self.errors.push(Error::duplicate_field(decl, "name", name.as_str()));
1699 }
1700 self.all_dictionaries.insert(name, &dictionary);
1701 }
1702 }
1703 }
1704
1705 #[cfg(fuchsia_api_level_at_least = "25")]
1706 fn validate_dictionary_decl(&mut self, dictionary: &'a fdecl::Dictionary) {
1707 let decl = DeclType::Dictionary;
1708 if let Some(path) = dictionary.source_path.as_ref() {
1709 if dictionary.source.is_some() {
1710 self.errors.push(Error::extraneous_field(decl, "source"));
1711 }
1712 check_path(Some(path), DeclType::Dictionary, "source_path", &mut self.errors);
1713 }
1714 }
1715
1716 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1717 fn validate_configuration_decl(&mut self, config: &'a fdecl::Configuration) {
1718 let decl = DeclType::Configuration;
1719 if check_name(config.name.as_ref(), decl, "name", &mut self.errors) {
1720 let name = config.name.as_ref().unwrap();
1721 if !self.all_capability_ids.insert(name) {
1722 self.errors.push(Error::duplicate_field(decl, "name", name.as_str()));
1723 }
1724 self.all_configs.insert(name);
1725 }
1726 }
1727
1728 fn validate_environment_debug_registration(&mut self, debug: &'a fdecl::DebugRegistration) {
1729 match debug {
1730 fdecl::DebugRegistration::Protocol(o) => {
1731 let decl = DeclType::DebugProtocolRegistration;
1732 self.validate_environment_debug_fields(
1733 decl,
1734 o.source.as_ref(),
1735 o.source_name.as_ref(),
1736 o.target_name.as_ref(),
1737 );
1738
1739 if let (Some(fdecl::Ref::Self_(_)), Some(ref name)) = (&o.source, &o.source_name) {
1740 if !self.all_protocols.contains(&name as &str) {
1741 self.errors.push(Error::invalid_field(decl, "source"));
1742 }
1743 }
1744 }
1745 _ => {
1746 self.errors.push(Error::invalid_field(DeclType::Environment, "debug"));
1747 }
1748 }
1749 }
1750
1751 fn validate_environment_debug_fields(
1752 &mut self,
1753 decl: DeclType,
1754 source: Option<&fdecl::Ref>,
1755 source_name: Option<&String>,
1756 target_name: Option<&'a String>,
1757 ) {
1758 match source {
1760 Some(fdecl::Ref::Parent(_)) => {}
1761 Some(fdecl::Ref::Self_(_)) => {}
1762 Some(fdecl::Ref::Framework(_)) => {}
1763 Some(fdecl::Ref::Child(child)) => {
1764 let _ = self.validate_child_ref(decl, "source", &child, OfferType::Static);
1765 }
1766 Some(_) => self.errors.push(Error::invalid_field(decl, "source")),
1767 None => self.errors.push(Error::missing_field(decl, "source")),
1768 }
1769 check_name(source_name, decl, "source_name", &mut self.errors);
1770 check_name(target_name, decl, "target_name", &mut self.errors);
1771 }
1772
1773 fn validate_event_stream_decl(&mut self, event: &'a fdecl::EventStream) {
1774 if check_name(event.name.as_ref(), DeclType::EventStream, "name", &mut self.errors) {
1775 let name = event.name.as_ref().unwrap();
1776 if !self.all_capability_ids.insert(name) {
1777 self.errors.push(Error::duplicate_field(
1778 DeclType::EventStream,
1779 "name",
1780 name.as_str(),
1781 ));
1782 }
1783 }
1784 }
1785
1786 fn validate_source_collection(
1787 &mut self,
1788 collection: &fdecl::CollectionRef,
1789 decl_type: DeclType,
1790 ) -> bool {
1791 let num_errors = self.errors.len();
1792 if check_name(Some(&collection.name), decl_type, "source.collection.name", &mut self.errors)
1793 && !self.all_collections.contains(&collection.name as &str)
1794 {
1795 self.errors.push(Error::invalid_collection(
1796 decl_type,
1797 "source",
1798 &collection.name as &str,
1799 ));
1800 }
1801 num_errors == self.errors.len()
1802 }
1803
1804 fn validate_filtered_service_fields(
1805 &mut self,
1806 decl_type: DeclType,
1807 source_instance_filter: Option<&Vec<String>>,
1808 renamed_instances: Option<&Vec<fdecl::NameMapping>>,
1809 ) {
1810 if let Some(source_instance_filter) = source_instance_filter {
1811 if source_instance_filter.is_empty() {
1812 self.errors.push(Error::invalid_field(decl_type, "source_instance_filter"));
1815 }
1816 for name in source_instance_filter {
1817 check_name(Some(name), decl_type, "source_instance_filter", &mut self.errors);
1818 }
1819 }
1820 if let Some(renamed_instances) = renamed_instances {
1821 let mut seen_target_names = HashSet::<String>::new();
1823 for mapping in renamed_instances {
1824 check_name(
1825 Some(&mapping.source_name),
1826 decl_type,
1827 "renamed_instances.source_name",
1828 &mut self.errors,
1829 );
1830 check_name(
1831 Some(&mapping.target_name),
1832 decl_type,
1833 "renamed_instances.target_name",
1834 &mut self.errors,
1835 );
1836 if !seen_target_names.insert(mapping.target_name.clone()) {
1837 self.errors.push(Error::invalid_field(decl_type, "renamed_instances"));
1838 break;
1839 }
1840 }
1841 }
1842 }
1843
1844 fn validate_source_capability(
1845 &mut self,
1846 capability: &fdecl::CapabilityRef,
1847 decl_type: DeclType,
1848 field: &str,
1849 ) -> bool {
1850 let num_errors = self.errors.len();
1851 if check_name(Some(&capability.name), decl_type, "source.capability.name", &mut self.errors)
1852 && !self.all_capability_ids.contains(capability.name.as_str())
1853 {
1854 self.errors.push(Error::invalid_capability(decl_type, field, &capability.name));
1855 }
1856 num_errors == self.errors.len()
1857 }
1858
1859 fn make_group_key(
1863 target_name: Option<&'a String>,
1864 target: Option<&'a fdecl::Ref>,
1865 ) -> Option<(&'a str, RefKey<'a>)> {
1866 if target_name.is_none() {
1867 return None;
1868 }
1869 let target_name = target_name.unwrap().as_str();
1870 if target.is_none() {
1871 return None;
1872 }
1873 let target = match target.unwrap() {
1874 fdecl::Ref::Parent(_) => RefKey::Parent,
1875 fdecl::Ref::Self_(_) => RefKey::Self_,
1876 fdecl::Ref::Child(r) => RefKey::Child(r.name.as_str()),
1877 fdecl::Ref::Collection(r) => RefKey::Collection(r.name.as_str()),
1878 fdecl::Ref::Framework(_) => RefKey::Framework,
1879 fdecl::Ref::Capability(_) => RefKey::Capability,
1880 fdecl::Ref::Debug(_) => RefKey::Debug,
1881 fdecl::RefUnknown!() => {
1882 return None;
1883 }
1884 };
1885 Some((target_name, target))
1886 }
1887
1888 fn validate_aggregation_has_same_availability(
1889 &mut self,
1890 route_group: &Vec<impl HasAvailability>,
1891 ) {
1892 let availability_of_sources: BTreeSet<_> =
1894 route_group.iter().map(|r| r.availability()).collect();
1895
1896 if availability_of_sources.len() > 1 {
1898 self.errors.push(Error::different_availability_in_aggregation(
1899 availability_of_sources.into_iter().collect(),
1900 ));
1901 }
1902 }
1903
1904 fn validate_expose_group(&mut self, exposes: &'a Vec<fdecl::Expose>) {
1907 let mut expose_groups: HashMap<_, Vec<fdecl::ExposeService>> = HashMap::new();
1908 let service_exposes = exposes.into_iter().filter_map(|o| {
1909 if let fdecl::Expose::Service(s) = o {
1910 Some(s)
1911 } else {
1912 None
1913 }
1914 });
1915 for expose in service_exposes {
1916 let key = Self::make_group_key(expose.target_name.as_ref(), expose.target.as_ref());
1917 if let Some(key) = key {
1918 expose_groups.entry(key).or_insert_with(|| vec![]).push(expose.clone());
1919 }
1920 }
1921 for expose_group in expose_groups.into_values() {
1922 if expose_group.len() == 1 {
1923 continue;
1926 }
1927
1928 self.validate_aggregation_has_same_availability(&expose_group);
1929 }
1930 }
1931
1932 fn validate_expose_decl(
1933 &mut self,
1934 expose: &'a fdecl::Expose,
1935 expose_to_parent_ids: &mut HashMap<&'a str, AllowableIds>,
1936 expose_to_framework_ids: &mut HashMap<&'a str, AllowableIds>,
1937 ) {
1938 match expose {
1939 fdecl::Expose::Service(e) => {
1940 let decl = DeclType::ExposeService;
1941 self.validate_expose_fields(
1942 decl,
1943 AllowableIds::Many,
1944 CollectionSource::Allow,
1945 Self::service_checker,
1946 e.source.as_ref(),
1947 e.source_name.as_ref(),
1948 get_source_dictionary!(e),
1949 e.target.as_ref(),
1950 e.target_name.as_ref(),
1951 e.availability.as_ref(),
1952 expose_to_parent_ids,
1953 expose_to_framework_ids,
1954 );
1955 }
1956 fdecl::Expose::Protocol(e) => {
1957 let decl = DeclType::ExposeProtocol;
1958 self.validate_expose_fields(
1959 decl,
1960 AllowableIds::One,
1961 CollectionSource::Deny,
1962 Self::protocol_checker,
1963 e.source.as_ref(),
1964 e.source_name.as_ref(),
1965 get_source_dictionary!(e),
1966 e.target.as_ref(),
1967 e.target_name.as_ref(),
1968 e.availability.as_ref(),
1969 expose_to_parent_ids,
1970 expose_to_framework_ids,
1971 );
1972 }
1973 fdecl::Expose::Directory(e) => {
1974 let decl = DeclType::ExposeDirectory;
1975 self.validate_expose_fields(
1976 decl,
1977 AllowableIds::One,
1978 CollectionSource::Deny,
1979 Self::directory_checker,
1980 e.source.as_ref(),
1981 e.source_name.as_ref(),
1982 get_source_dictionary!(e),
1983 e.target.as_ref(),
1984 e.target_name.as_ref(),
1985 e.availability.as_ref(),
1986 expose_to_parent_ids,
1987 expose_to_framework_ids,
1988 );
1989
1990 match e.target.as_ref() {
1993 Some(fdecl::Ref::Framework(_)) => {
1994 if e.subdir.is_some() {
1995 self.errors.push(Error::invalid_field(decl, "subdir"));
1996 }
1997 }
1998 _ => {}
1999 }
2000
2001 if let Some(subdir) = e.subdir.as_ref() {
2002 check_relative_path(Some(subdir), decl, "subdir", &mut self.errors);
2003 }
2004 }
2005 fdecl::Expose::Runner(e) => {
2006 let decl = DeclType::ExposeRunner;
2007 self.validate_expose_fields(
2008 decl,
2009 AllowableIds::One,
2010 CollectionSource::Deny,
2011 Self::runner_checker,
2012 e.source.as_ref(),
2013 e.source_name.as_ref(),
2014 get_source_dictionary!(e),
2015 e.target.as_ref(),
2016 e.target_name.as_ref(),
2017 Some(&fdecl::Availability::Required),
2018 expose_to_parent_ids,
2019 expose_to_framework_ids,
2020 );
2021 }
2022 fdecl::Expose::Resolver(e) => {
2023 let decl = DeclType::ExposeResolver;
2024 self.validate_expose_fields(
2025 decl,
2026 AllowableIds::One,
2027 CollectionSource::Deny,
2028 Self::resolver_checker,
2029 e.source.as_ref(),
2030 e.source_name.as_ref(),
2031 get_source_dictionary!(e),
2032 e.target.as_ref(),
2033 e.target_name.as_ref(),
2034 Some(&fdecl::Availability::Required),
2035 expose_to_parent_ids,
2036 expose_to_framework_ids,
2037 );
2038 }
2039 #[cfg(fuchsia_api_level_at_least = "25")]
2040 fdecl::Expose::Dictionary(e) => {
2041 let decl = DeclType::ExposeDictionary;
2042 self.validate_expose_fields(
2043 decl,
2044 AllowableIds::One,
2045 CollectionSource::Deny,
2046 Self::dictionary_checker,
2047 e.source.as_ref(),
2048 e.source_name.as_ref(),
2049 get_source_dictionary!(e),
2050 e.target.as_ref(),
2051 e.target_name.as_ref(),
2052 Some(&fdecl::Availability::Required),
2053 expose_to_parent_ids,
2054 expose_to_framework_ids,
2055 );
2056 }
2057 #[cfg(fuchsia_api_level_at_least = "HEAD")]
2058 fdecl::Expose::Config(e) => {
2059 let decl = DeclType::ExposeConfig;
2060 self.validate_expose_fields(
2061 decl,
2062 AllowableIds::One,
2063 CollectionSource::Deny,
2064 Self::config_checker,
2065 e.source.as_ref(),
2066 e.source_name.as_ref(),
2067 None,
2068 e.target.as_ref(),
2069 e.target_name.as_ref(),
2070 e.availability.as_ref(),
2071 expose_to_parent_ids,
2072 expose_to_framework_ids,
2073 );
2074 }
2075 _ => {
2076 self.errors.push(Error::invalid_field(DeclType::Component, "expose"));
2077 }
2078 }
2079 }
2080
2081 fn validate_expose_fields(
2082 &mut self,
2083 decl: DeclType,
2084 allowable_ids: AllowableIds,
2085 collection_source: CollectionSource,
2086 capability_checker: impl Fn(&Self) -> &dyn Container,
2090 source: Option<&fdecl::Ref>,
2091 source_name: Option<&String>,
2092 source_dictionary: Option<&String>,
2093 target: Option<&fdecl::Ref>,
2094 target_name: Option<&'a String>,
2095 availability: Option<&fdecl::Availability>,
2096 expose_to_parent_ids: &mut HashMap<&'a str, AllowableIds>,
2097 expose_to_framework_ids: &mut HashMap<&'a str, AllowableIds>,
2098 ) {
2099 self.validate_expose_source(decl, collection_source, source, source_dictionary);
2100 check_route_availability(decl, availability, source, source_name, &mut self.errors);
2101 match target {
2102 Some(r) => match r {
2103 fdecl::Ref::Parent(_) => {}
2104 fdecl::Ref::Framework(_) => {}
2105 _ => {
2106 self.errors.push(Error::invalid_field(decl, "target"));
2107 }
2108 },
2109 None => {
2110 self.errors.push(Error::missing_field(decl, "target"));
2111 }
2112 }
2113 check_name(source_name, decl, "source_name", &mut self.errors);
2114 if source_dictionary.is_some() {
2115 check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
2116 }
2117 if check_name(target_name, decl, "target_name", &mut self.errors) {
2118 let maybe_ids_set = match target {
2119 Some(fdecl::Ref::Parent(_)) => Some(expose_to_parent_ids),
2120 Some(fdecl::Ref::Framework(_)) => Some(expose_to_framework_ids),
2121 _ => None,
2122 };
2123 if let Some(ids_set) = maybe_ids_set {
2124 let target_name = target_name.unwrap();
2125 if let Some(prev_state) = ids_set.insert(target_name, allowable_ids) {
2126 if prev_state == AllowableIds::One || prev_state != allowable_ids {
2127 self.errors.push(Error::duplicate_field(decl, "target_name", target_name));
2128 }
2129 }
2130 }
2131 }
2132
2133 self.validate_route_from_self(
2134 decl,
2135 source,
2136 source_name,
2137 source_dictionary,
2138 capability_checker,
2139 );
2140 }
2141
2142 fn validate_expose_source(
2143 &mut self,
2144 decl: DeclType,
2145 collection_source: CollectionSource,
2146 source: Option<&fdecl::Ref>,
2147 source_dictionary: Option<&String>,
2148 ) {
2149 match (source, source_dictionary) {
2150 (Some(fdecl::Ref::Self_(_)), _) => {}
2152 (Some(fdecl::Ref::Child(child)), _) => {
2153 let _ = self.validate_child_ref(decl, "source", &child, OfferType::Static);
2154 }
2155 (Some(fdecl::Ref::VoidType(_)), None) => {}
2157 (Some(fdecl::Ref::Framework(_)), None) => {}
2158 (Some(fdecl::Ref::Capability(c)), None) => {
2159 self.validate_source_capability(c, decl, "source");
2160 }
2161 (Some(fdecl::Ref::Collection(c)), None)
2162 if collection_source == CollectionSource::Allow =>
2163 {
2164 self.validate_source_collection(c, decl);
2165 }
2166 (None, _) => {
2168 self.errors.push(Error::missing_field(decl, "source"));
2169 }
2170 (_, _) => {
2172 self.errors.push(Error::invalid_field(decl, "source"));
2173 }
2174 }
2175 }
2176
2177 fn add_strong_dep(
2184 &mut self,
2185 source: Option<DependencyNode<'a>>,
2186 target: Option<DependencyNode<'a>>,
2187 ) {
2188 if source.is_none() || target.is_none() {
2189 return;
2190 }
2191 let source = source.unwrap();
2192 let target = target.unwrap();
2193 match (source, target) {
2194 (DependencyNode::Self_, DependencyNode::Self_) => {
2195 }
2197 (source, target) => {
2198 self.strong_dependencies.add_edge(source, target);
2199 }
2200 }
2201 }
2202
2203 fn validate_offer_group(&mut self, offers: &'a Vec<fdecl::Offer>, offer_type: OfferType) {
2206 let mut offer_groups: HashMap<_, Vec<fdecl::OfferService>> = HashMap::new();
2207 let service_offers = offers.into_iter().filter_map(|o| {
2208 if let fdecl::Offer::Service(s) = o {
2209 Some(s)
2210 } else {
2211 None
2212 }
2213 });
2214 for offer in service_offers {
2215 let key = Self::make_group_key(offer.target_name.as_ref(), offer.target.as_ref());
2216 if let Some(key) = key {
2217 offer_groups.entry(key).or_insert_with(|| vec![]).push(offer.clone());
2218 }
2219 }
2220 for offer_group in offer_groups.into_values() {
2221 if offer_group.len() == 1 {
2222 continue;
2225 }
2226
2227 self.validate_aggregation_has_same_availability(&offer_group);
2228
2229 let mut source_instance_filter_entries: HashSet<String> = HashSet::new();
2230 let mut service_source_names: HashSet<String> = HashSet::new();
2231 for o in offer_group {
2232 match (o.source_instance_filter, offer_type) {
2234 (Some(source_instance_filter), _) => {
2235 for instance_name in source_instance_filter {
2236 if !source_instance_filter_entries.insert(instance_name.clone()) {
2237 self.errors.push(Error::invalid_aggregate_offer(format!(
2240 "Conflicting source_instance_filter in aggregate service \
2241 offer, instance_name '{}' seen in filter lists multiple times",
2242 instance_name,
2243 )));
2244 }
2245 }
2246 }
2247 (None, OfferType::Static) => {}
2248 (None, OfferType::Dynamic) => {
2249 self.errors.push(Error::invalid_aggregate_offer(
2251 "source_instance_filter must be set for dynamic aggregate service \
2252 offers",
2253 ));
2254 }
2255 }
2256 service_source_names.insert(
2257 o.source_name
2258 .expect("Offer Service declarations must always contain source_name"),
2259 );
2260 }
2261
2262 if service_source_names.len() > 1 {
2263 self.errors.push(Error::invalid_aggregate_offer(format!(
2264 "All aggregate service offers must have the same source_name, saw {}. Use \
2265 renamed_instances to rename instance names to avoid conflict.",
2266 service_source_names.into_iter().sorted().collect::<Vec<String>>().join(", ")
2267 )));
2268 }
2269 }
2270 }
2271
2272 fn validate_offer_decl(&mut self, offer: &'a fdecl::Offer, offer_type: OfferType) {
2273 match offer {
2274 fdecl::Offer::Service(o) => {
2275 let decl = DeclType::OfferService;
2276 self.validate_offer_fields(
2277 decl,
2278 AllowableIds::Many,
2279 CollectionSource::Allow,
2280 Self::service_checker,
2281 o.source.as_ref(),
2282 o.source_name.as_ref(),
2283 get_source_dictionary!(o),
2284 o.target.as_ref(),
2285 o.target_name.as_ref(),
2286 #[cfg(fuchsia_api_level_at_least = "HEAD")]
2287 Some(o.dependency_type.as_ref().unwrap_or(&fdecl::DependencyType::Strong)),
2288 #[cfg(fuchsia_api_level_less_than = "HEAD")]
2289 Some(&fdecl::DependencyType::Strong),
2290 o.availability.as_ref(),
2291 offer_type,
2292 );
2293 self.validate_filtered_service_fields(
2294 decl,
2295 o.source_instance_filter.as_ref(),
2296 o.renamed_instances.as_ref(),
2297 );
2298 }
2299 fdecl::Offer::Protocol(o) => {
2300 let decl = DeclType::OfferProtocol;
2301 self.validate_offer_fields(
2302 decl,
2303 AllowableIds::One,
2304 CollectionSource::Deny,
2305 Self::protocol_checker,
2306 o.source.as_ref(),
2307 o.source_name.as_ref(),
2308 get_source_dictionary!(o),
2309 o.target.as_ref(),
2310 o.target_name.as_ref(),
2311 o.dependency_type.as_ref(),
2312 o.availability.as_ref(),
2313 offer_type,
2314 );
2315 }
2316 fdecl::Offer::Directory(o) => {
2317 let decl = DeclType::OfferDirectory;
2318 self.validate_offer_fields(
2319 decl,
2320 AllowableIds::One,
2321 CollectionSource::Deny,
2322 Self::directory_checker,
2323 o.source.as_ref(),
2324 o.source_name.as_ref(),
2325 get_source_dictionary!(o),
2326 o.target.as_ref(),
2327 o.target_name.as_ref(),
2328 o.dependency_type.as_ref(),
2329 o.availability.as_ref(),
2330 offer_type,
2331 );
2332 if let Some(subdir) = o.subdir.as_ref() {
2333 check_relative_path(
2334 Some(subdir),
2335 DeclType::OfferDirectory,
2336 "subdir",
2337 &mut self.errors,
2338 );
2339 }
2340 }
2341 fdecl::Offer::Storage(o) => {
2342 let decl = DeclType::OfferStorage;
2343 self.validate_storage_offer_fields(
2344 decl,
2345 Self::storage_checker,
2346 o.source.as_ref(),
2347 o.source_name.as_ref(),
2348 o.target.as_ref(),
2349 o.target_name.as_ref(),
2350 o.availability.as_ref(),
2351 offer_type,
2352 );
2353 self.add_strong_dep(
2354 self.source_dependency_from_ref(
2355 o.source_name.as_ref(),
2356 None,
2357 o.source.as_ref(),
2358 ),
2359 self.target_dependency_from_ref(o.target.as_ref()),
2360 );
2361 }
2362 fdecl::Offer::Runner(o) => {
2363 let decl = DeclType::OfferRunner;
2364 self.validate_offer_fields(
2365 decl,
2366 AllowableIds::One,
2367 CollectionSource::Deny,
2368 Self::runner_checker,
2369 o.source.as_ref(),
2370 o.source_name.as_ref(),
2371 get_source_dictionary!(o),
2372 o.target.as_ref(),
2373 o.target_name.as_ref(),
2374 Some(&fdecl::DependencyType::Strong),
2375 Some(&fdecl::Availability::Required),
2376 offer_type,
2377 );
2378 }
2379 fdecl::Offer::Resolver(o) => {
2380 let decl = DeclType::OfferResolver;
2381 self.validate_offer_fields(
2382 decl,
2383 AllowableIds::One,
2384 CollectionSource::Deny,
2385 Self::resolver_checker,
2386 o.source.as_ref(),
2387 o.source_name.as_ref(),
2388 get_source_dictionary!(o),
2389 o.target.as_ref(),
2390 o.target_name.as_ref(),
2391 Some(&fdecl::DependencyType::Strong),
2392 Some(&fdecl::Availability::Required),
2393 offer_type,
2394 );
2395 }
2396 fdecl::Offer::EventStream(e) => {
2397 self.validate_event_stream_offer_fields(e, offer_type);
2398 }
2399 #[cfg(fuchsia_api_level_at_least = "25")]
2400 fdecl::Offer::Dictionary(o) => {
2401 let decl = DeclType::OfferDictionary;
2402 self.validate_offer_fields(
2403 decl,
2404 AllowableIds::One,
2405 CollectionSource::Deny,
2406 Self::dictionary_checker,
2407 o.source.as_ref(),
2408 o.source_name.as_ref(),
2409 get_source_dictionary!(o),
2410 o.target.as_ref(),
2411 o.target_name.as_ref(),
2412 o.dependency_type.as_ref(),
2413 o.availability.as_ref(),
2414 offer_type,
2415 );
2416 }
2417 #[cfg(fuchsia_api_level_at_least = "HEAD")]
2418 fdecl::Offer::Config(o) => {
2419 let decl = DeclType::OfferConfig;
2420 self.validate_offer_fields(
2421 decl,
2422 AllowableIds::One,
2423 CollectionSource::Deny,
2424 Self::config_checker,
2425 o.source.as_ref(),
2426 o.source_name.as_ref(),
2427 None,
2428 o.target.as_ref(),
2429 o.target_name.as_ref(),
2430 Some(&fdecl::DependencyType::Strong),
2431 o.availability.as_ref(),
2432 offer_type,
2433 );
2434 }
2435 fdecl::OfferUnknown!() => {
2436 self.errors.push(Error::invalid_field(DeclType::Component, "offer"));
2437 }
2438 }
2439 }
2440
2441 fn validate_offer_fields(
2442 &mut self,
2443 decl: DeclType,
2444 allowable_names: AllowableIds,
2445 collection_source: CollectionSource,
2446 capability_checker: impl Fn(&Self) -> &dyn Container,
2450 source: Option<&'a fdecl::Ref>,
2451 source_name: Option<&'a String>,
2452 source_dictionary: Option<&'a String>,
2453 target: Option<&'a fdecl::Ref>,
2454 target_name: Option<&'a String>,
2455 dependency_type: Option<&'a fdecl::DependencyType>,
2456 availability: Option<&'a fdecl::Availability>,
2457 offer_type: OfferType,
2458 ) {
2459 self.validate_offer_source(decl, collection_source, source, source_dictionary, offer_type);
2460 check_route_availability(decl, availability, source, source_name, &mut self.errors);
2461 check_offer_name(source_name, decl, "source_name", offer_type, &mut self.errors);
2462 if source_dictionary.is_some() {
2463 check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
2464 }
2465 self.validate_offer_target(decl, allowable_names, target, target_name, offer_type);
2466 check_offer_name(target_name, decl, "target_name", offer_type, &mut self.errors);
2467
2468 if dependency_type.is_none() {
2469 self.errors.push(Error::missing_field(decl, "dependency_type"));
2470 } else if dependency_type == Some(&fdecl::DependencyType::Strong) {
2471 self.add_strong_dep(
2472 self.source_dependency_from_ref(source_name, source_dictionary, source),
2473 self.target_dependency_from_ref(target),
2474 );
2475
2476 if let Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: collection })) =
2479 target.as_ref()
2480 {
2481 for name in
2482 Self::dynamic_children_in_collection(&self.dynamic_children, &collection)
2483 {
2484 self.add_strong_dep(
2485 self.source_dependency_from_ref(source_name, source_dictionary, source),
2486 Some(DependencyNode::Child(name, Some(&collection))),
2487 );
2488 }
2489 }
2490 }
2491
2492 self.validate_route_from_self(
2493 decl,
2494 source,
2495 source_name,
2496 source_dictionary,
2497 capability_checker,
2498 );
2499 }
2500
2501 fn validate_offer_source(
2502 &mut self,
2503 decl: DeclType,
2504 collection_source: CollectionSource,
2505 source: Option<&'a fdecl::Ref>,
2506 source_dictionary: Option<&'a String>,
2507 offer_type: OfferType,
2508 ) {
2509 match (source, source_dictionary) {
2510 (Some(fdecl::Ref::Parent(_)), _) => {}
2512 (Some(fdecl::Ref::Self_(_)), _) => {}
2513 (Some(fdecl::Ref::Child(child)), _) => {
2514 self.validate_child_ref(decl, "source", &child, offer_type);
2515 }
2516 (Some(fdecl::Ref::VoidType(_)), None) => {}
2518 (Some(fdecl::Ref::Framework(_)), None) => {}
2519 (Some(fdecl::Ref::Capability(c)), None) => {
2520 self.validate_source_capability(c, decl, "source");
2521 }
2522 (Some(fdecl::Ref::Collection(c)), None)
2523 if collection_source == CollectionSource::Allow =>
2524 {
2525 self.validate_source_collection(c, decl);
2526 }
2527 (None, _) => self.errors.push(Error::missing_field(decl, "source")),
2529 (_, _) => self.errors.push(Error::invalid_field(decl, "source")),
2531 }
2532 }
2533
2534 fn validate_storage_offer_fields(
2535 &mut self,
2536 decl: DeclType,
2537 capability_checker: impl Fn(&Self) -> &dyn Container,
2541 source: Option<&'a fdecl::Ref>,
2542 source_name: Option<&'a String>,
2543 target: Option<&'a fdecl::Ref>,
2544 target_name: Option<&'a String>,
2545 availability: Option<&fdecl::Availability>,
2546 offer_type: OfferType,
2547 ) {
2548 match source {
2549 Some(fdecl::Ref::Parent(_) | fdecl::Ref::VoidType(_) | fdecl::Ref::Self_(_)) => {}
2550 Some(_) => {
2551 self.errors.push(Error::invalid_field(decl, "source"));
2552 }
2553 None => {
2554 self.errors.push(Error::missing_field(decl, "source"));
2555 }
2556 }
2557 check_route_availability(decl, availability, source, source_name, &mut self.errors);
2558 check_offer_name(source_name, decl, "source_name", offer_type, &mut self.errors);
2559 self.validate_offer_target(decl, AllowableIds::One, target, target_name, offer_type);
2560 check_offer_name(target_name, decl, "target_name", offer_type, &mut self.errors);
2561
2562 if let (Some(fdecl::Ref::Self_(_)), Some(name)) = (source, source_name) {
2563 if !(capability_checker)(self).contains(name) {
2564 self.errors.push(Error::invalid_capability(decl, "source", name));
2565 }
2566 }
2567 }
2568
2569 fn validate_event_stream_offer_fields(
2570 &mut self,
2571 event_stream: &'a fdecl::OfferEventStream,
2572 offer_type: OfferType,
2573 ) {
2574 let decl = DeclType::OfferEventStream;
2575 check_name(event_stream.source_name.as_ref(), decl, "source_name", &mut self.errors);
2576 if event_stream.target == Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})) {
2577 self.errors.push(Error::invalid_field(DeclType::OfferEventStream, "target"));
2579 }
2580 if let Some(scope) = &event_stream.scope {
2581 if scope.is_empty() {
2582 self.errors.push(Error::invalid_field(decl, "scope"));
2583 }
2584 for value in scope {
2585 match value {
2586 fdecl::Ref::Child(child) => {
2587 self.validate_child_ref(
2588 DeclType::OfferEventStream,
2589 "scope",
2590 &child,
2591 offer_type,
2592 );
2593 }
2594 fdecl::Ref::Collection(collection) => {
2595 self.validate_collection_ref(
2596 DeclType::OfferEventStream,
2597 "scope",
2598 &collection,
2599 );
2600 }
2601 _ => {
2602 self.errors.push(Error::invalid_field(DeclType::OfferEventStream, "scope"));
2603 }
2604 }
2605 }
2606 }
2607 match event_stream.source {
2609 Some(
2610 fdecl::Ref::Parent(_)
2611 | fdecl::Ref::Framework(_)
2612 | fdecl::Ref::Child(_)
2613 | fdecl::Ref::VoidType(_),
2614 ) => {}
2615 Some(_) => {
2616 self.errors.push(Error::invalid_field(decl, "source"));
2617 }
2618 None => {
2619 self.errors.push(Error::missing_field(decl, "source"));
2620 }
2621 };
2622
2623 check_route_availability(
2624 decl,
2625 event_stream.availability.as_ref(),
2626 event_stream.source.as_ref(),
2627 event_stream.source_name.as_ref(),
2628 &mut self.errors,
2629 );
2630
2631 self.validate_offer_target(
2632 decl,
2633 AllowableIds::One,
2634 event_stream.target.as_ref(),
2635 event_stream.target_name.as_ref(),
2636 offer_type,
2637 );
2638 check_name(event_stream.target_name.as_ref(), decl, "target_name", &mut self.errors);
2639 }
2640
2641 fn validate_child_ref(
2643 &mut self,
2644 decl: DeclType,
2645 field_name: &str,
2646 child: &fdecl::ChildRef,
2647 offer_type: OfferType,
2648 ) -> bool {
2649 if offer_type == OfferType::Dynamic && child.collection.is_some() {
2650 return self.validate_dynamic_child_ref(decl, field_name, child);
2651 }
2652 let mut valid = true;
2656 if !check_name(
2657 Some(&child.name),
2658 decl,
2659 &format!("{}.child.name", field_name),
2660 &mut self.errors,
2661 ) {
2662 valid = false;
2663 }
2664 if child.collection.is_some() {
2665 self.errors
2666 .push(Error::extraneous_field(decl, format!("{}.child.collection", field_name)));
2667 valid = false;
2668 }
2669 if !valid {
2670 return false;
2671 }
2672
2673 let name: &str = &child.name;
2675 if !self.all_children.contains_key(name) {
2676 self.errors.push(Error::invalid_child(decl, field_name, name));
2677 return false;
2678 }
2679
2680 true
2681 }
2682
2683 fn validate_dynamic_child_ref(
2688 &mut self,
2689 decl: DeclType,
2690 field_name: &str,
2691 child: &fdecl::ChildRef,
2692 ) -> bool {
2693 let mut valid = true;
2697 if !check_dynamic_name(
2698 Some(&child.name),
2699 decl,
2700 &format!("{}.child.name", field_name),
2701 &mut self.errors,
2702 ) {
2703 valid = false;
2704 }
2705 if !check_name(
2706 child.collection.as_ref(),
2707 decl,
2708 &format!("{}.child.collection", field_name),
2709 &mut self.errors,
2710 ) {
2711 valid = false;
2712 }
2713 valid
2714 }
2715
2716 fn validate_collection_ref(
2718 &mut self,
2719 decl: DeclType,
2720 field_name: &str,
2721 collection: &fdecl::CollectionRef,
2722 ) -> bool {
2723 if !check_name(
2725 Some(&collection.name),
2726 decl,
2727 &format!("{}.collection.name", field_name),
2728 &mut self.errors,
2729 ) {
2730 return false;
2731 }
2732
2733 if !self.all_collections.contains(&collection.name as &str) {
2735 self.errors.push(Error::invalid_collection(decl, field_name, &collection.name as &str));
2736 return false;
2737 }
2738
2739 true
2740 }
2741
2742 fn validate_offer_target(
2743 &mut self,
2744 decl: DeclType,
2745 allowable_names: AllowableIds,
2746 target: Option<&'a fdecl::Ref>,
2747 target_name: Option<&'a String>,
2748 offer_type: OfferType,
2749 ) {
2750 match target {
2751 Some(fdecl::Ref::Child(c)) => {
2752 self.validate_target_child(decl, allowable_names, c, target_name, offer_type);
2753 }
2754 Some(fdecl::Ref::Collection(c)) => {
2755 self.validate_target_collection(decl, allowable_names, c, target_name);
2756 }
2757 Some(fdecl::Ref::Capability(c)) => {
2758 #[cfg(fuchsia_api_level_at_least = "25")]
2760 if let Some(d) = self.all_dictionaries.get(&c.name.as_str()) {
2761 if d.source_path.is_some() {
2762 self.errors.push(Error::invalid_field(decl, "target"));
2765 }
2766 } else {
2767 self.errors.push(Error::invalid_field(decl, "target"));
2768 }
2769 #[cfg(not(fuchsia_api_level_at_least = "25"))]
2770 {
2771 let _ = c;
2772 self.errors.push(Error::invalid_field(decl, "target"));
2773 }
2774 }
2775 Some(_) => {
2776 self.errors.push(Error::invalid_field(decl, "target"));
2777 }
2778 None => {
2779 self.errors.push(Error::missing_field(decl, "target"));
2780 }
2781 }
2782 }
2783
2784 fn validate_target_child(
2785 &mut self,
2786 decl: DeclType,
2787 allowable_names: AllowableIds,
2788 child: &'a fdecl::ChildRef,
2789 target_name: Option<&'a String>,
2790 offer_type: OfferType,
2791 ) {
2792 if !self.validate_child_ref(decl, "target", child, offer_type) {
2793 return;
2794 }
2795 if let Some(target_name) = target_name {
2796 let names_for_target = self
2797 .target_ids
2798 .entry(TargetId::Component(
2799 &child.name,
2800 child.collection.as_ref().map(|s| s.as_str()),
2801 ))
2802 .or_insert(HashMap::new());
2803 if let Some(prev_state) = names_for_target.insert(target_name, allowable_names) {
2804 if prev_state == AllowableIds::One || prev_state != allowable_names {
2805 self.errors.push(Error::duplicate_field(
2806 decl,
2807 "target_name",
2808 target_name as &str,
2809 ));
2810 }
2811 }
2812 if let Some(collection) = child.collection.as_ref() {
2813 if let Some(names_for_target) =
2814 self.target_ids.get(&TargetId::Collection(&collection))
2815 {
2816 if names_for_target.contains_key(&target_name.as_str()) {
2817 self.errors.push(Error::duplicate_field(
2819 decl,
2820 "target_name",
2821 target_name as &str,
2822 ));
2823 }
2824 }
2825 }
2826 }
2827 }
2828
2829 fn validate_target_collection(
2830 &mut self,
2831 decl: DeclType,
2832 allowable_names: AllowableIds,
2833 collection: &'a fdecl::CollectionRef,
2834 target_name: Option<&'a String>,
2835 ) {
2836 if !self.validate_collection_ref(decl, "target", &collection) {
2837 return;
2838 }
2839 if let Some(target_name) = target_name {
2840 let names_for_target = self
2841 .target_ids
2842 .entry(TargetId::Collection(&collection.name))
2843 .or_insert(HashMap::new());
2844 if let Some(prev_state) = names_for_target.insert(target_name, allowable_names) {
2845 if prev_state == AllowableIds::One || prev_state != allowable_names {
2846 self.errors.push(Error::duplicate_field(
2847 decl,
2848 "target_name",
2849 target_name as &str,
2850 ));
2851 }
2852 }
2853 }
2854 }
2855
2856 fn validate_route_from_self(
2857 &mut self,
2858 decl: DeclType,
2859 source: Option<&fdecl::Ref>,
2860 source_name: Option<&String>,
2861 source_dictionary: Option<&String>,
2862 capability_checker: impl Fn(&Self) -> &dyn Container,
2863 ) {
2864 let (Some(fdecl::Ref::Self_(_)), Some(name)) = (source, source_name) else {
2865 return;
2866 };
2867 match source_dictionary {
2868 Some(source_dictionary) => {
2869 #[cfg(fuchsia_api_level_at_least = "25")]
2870 {
2871 use cm_types::IterablePath;
2872 if let Ok(path) = cm_types::RelativePath::new(source_dictionary) {
2873 if let Some(first_segment) = path.iter_segments().next().map(|s| s.as_str())
2874 {
2875 if !self.all_dictionaries.contains_key(first_segment) {
2876 self.errors.push(Error::invalid_capability(
2877 decl,
2878 "source",
2879 first_segment,
2880 ));
2881 }
2882 }
2883 }
2884 }
2885 #[cfg(not(fuchsia_api_level_at_least = "25"))]
2886 let _ = source_dictionary;
2887 }
2888 None => {
2889 if !(capability_checker)(self).contains(name) {
2890 self.errors.push(Error::invalid_capability(decl, "source", name));
2891 }
2892 }
2893 }
2894 }
2895
2896 fn source_dependency_from_ref(
2897 &self,
2898 source_name: Option<&'a String>,
2899 source_dictionary: Option<&'a String>,
2900 ref_: Option<&'a fdecl::Ref>,
2901 ) -> Option<DependencyNode<'a>> {
2902 if ref_.is_none() {
2903 return None;
2904 }
2905 match ref_.unwrap() {
2906 fdecl::Ref::Child(fdecl::ChildRef { name, collection }) => {
2907 Some(DependencyNode::Child(name.as_str(), collection.as_ref().map(|s| s.as_str())))
2908 }
2909 fdecl::Ref::Collection(fdecl::CollectionRef { name, .. }) => {
2910 Some(DependencyNode::Collection(name.as_str()))
2911 }
2912 fdecl::Ref::Capability(fdecl::CapabilityRef { name, .. }) => {
2913 Some(DependencyNode::Capability(name.as_str()))
2914 }
2915 fdecl::Ref::Self_(_) => {
2916 #[cfg(fuchsia_api_level_at_least = "25")]
2917 if let Some(source_dictionary) = source_dictionary {
2918 let root_dict = source_dictionary.split('/').next().unwrap();
2919 if self.all_dictionaries.contains_key(root_dict) {
2920 Some(DependencyNode::Capability(root_dict))
2921 } else {
2922 Some(DependencyNode::Self_)
2923 }
2924 } else if let Some(source_name) = source_name {
2925 if self.all_storages.contains_key(source_name.as_str())
2926 || self.all_dictionaries.contains_key(source_name.as_str())
2927 {
2928 Some(DependencyNode::Capability(source_name))
2929 } else {
2930 Some(DependencyNode::Self_)
2931 }
2932 } else {
2933 Some(DependencyNode::Self_)
2934 }
2935 #[cfg(not(fuchsia_api_level_at_least = "25"))]
2936 {
2937 let _ = source_dictionary;
2938 if let Some(source_name) = source_name {
2939 if self.all_storages.contains_key(source_name.as_str()) {
2940 Some(DependencyNode::Capability(source_name))
2941 } else {
2942 Some(DependencyNode::Self_)
2943 }
2944 } else {
2945 Some(DependencyNode::Self_)
2946 }
2947 }
2948 }
2949 fdecl::Ref::Parent(_) => {
2950 None
2953 }
2954 fdecl::Ref::Framework(_) => {
2955 None
2958 }
2959 fdecl::Ref::Debug(_) => {
2960 None
2964 }
2965 fdecl::Ref::VoidType(_) => None,
2966 fdecl::RefUnknown!() => {
2967 None
2969 }
2970 }
2971 }
2972
2973 fn target_dependency_from_ref(
2974 &self,
2975 ref_: Option<&'a fdecl::Ref>,
2976 ) -> Option<DependencyNode<'a>> {
2977 self.source_dependency_from_ref(None, None, ref_)
2978 }
2979
2980 fn dynamic_children_in_collection(
2981 dynamic_children: &Vec<(&'a str, &'a str)>,
2982 collection: &'a str,
2983 ) -> Vec<&'a str> {
2984 dynamic_children
2985 .iter()
2986 .filter_map(|(n, c)| if *c == collection { Some(*n) } else { None })
2987 .collect()
2988 }
2989
2990 fn service_checker(&self) -> &dyn Container {
2993 &self.all_services
2994 }
2995 fn protocol_checker(&self) -> &dyn Container {
2996 &self.all_protocols
2997 }
2998 fn directory_checker(&self) -> &dyn Container {
2999 &self.all_directories
3000 }
3001 fn runner_checker(&self) -> &dyn Container {
3002 &self.all_runners
3003 }
3004 fn resolver_checker(&self) -> &dyn Container {
3005 &self.all_resolvers
3006 }
3007
3008 #[cfg(fuchsia_api_level_at_least = "25")]
3009 fn dictionary_checker(&self) -> &dyn Container {
3010 &self.all_dictionaries
3011 }
3012
3013 #[cfg(fuchsia_api_level_at_least = "HEAD")]
3014 fn config_checker(&self) -> &dyn Container {
3015 &self.all_configs
3016 }
3017 fn storage_checker(&self) -> &dyn Container {
3018 &self.all_storages
3019 }
3020 fn event_stream_checker(&self) -> &dyn Container {
3021 struct AlwaysTrueContainer {}
3025 impl Container for AlwaysTrueContainer {
3026 fn contains(&self, _key: &str) -> bool {
3027 true
3028 }
3029 }
3030 static CONTAINER: AlwaysTrueContainer = AlwaysTrueContainer {};
3031 &CONTAINER
3032 }
3033}
3034
3035#[cfg(test)]
3036mod tests {
3037 use super::*;
3038 use cm_types::MAX_LONG_NAME_LENGTH;
3039 use test_case::test_case;
3040 use {
3041 fidl_fuchsia_component_decl as fdecl, fidl_fuchsia_data as fdata, fidl_fuchsia_io as fio,
3042 };
3043
3044 macro_rules! test_validate {
3045 (
3046 $(
3047 $test_name:ident => {
3048 input = $input:expr,
3049 result = $result:expr,
3050 },
3051 )+
3052 ) => {
3053 $(
3054 #[test]
3055 fn $test_name() {
3056 validate_test($input, $result);
3057 }
3058 )+
3059 }
3060 }
3061
3062 macro_rules! test_validate_any_result {
3063 (
3064 $(
3065 $test_name:ident => {
3066 input = $input:expr,
3067 results = $results:expr,
3068 },
3069 )+
3070 ) => {
3071 $(
3072 #[test]
3073 fn $test_name() {
3074 validate_test_any_result($input, $results);
3075 }
3076 )+
3077 }
3078 }
3079
3080 macro_rules! test_validate_values_data {
3081 (
3082 $(
3083 $test_name:ident => {
3084 input = $input:expr,
3085 result = $result:expr,
3086 },
3087 )+
3088 ) => {
3089 $(
3090 #[test]
3091 fn $test_name() {
3092 validate_values_data_test($input, $result);
3093 }
3094 )+
3095 }
3096 }
3097
3098 macro_rules! test_validate_capabilities {
3099 (
3100 $(
3101 $test_name:ident => {
3102 input = $input:expr,
3103 as_builtin = $as_builtin:expr,
3104 result = $result:expr,
3105 },
3106 )+
3107 ) => {
3108 $(
3109 #[test]
3110 fn $test_name() {
3111 validate_capabilities_test($input, $as_builtin, $result);
3112 }
3113 )+
3114 }
3115 }
3116
3117 macro_rules! test_dependency {
3118 (
3119 $(
3120 ($test_name:ident) => {
3121 ty = $ty:expr,
3122 offer_decl = $offer_decl:expr,
3123 },
3124 )+
3125 ) => {
3126 $(
3127 #[test]
3128 fn $test_name() {
3129 let mut decl = new_component_decl();
3130 let dependencies = vec![
3131 ("a", "b"),
3132 ("b", "a"),
3133 ];
3134 let offers = dependencies.into_iter().map(|(from,to)| {
3135 let mut offer_decl = $offer_decl;
3136 offer_decl.source = Some(fdecl::Ref::Child(
3137 fdecl::ChildRef { name: from.to_string(), collection: None },
3138 ));
3139 offer_decl.target = Some(fdecl::Ref::Child(
3140 fdecl::ChildRef { name: to.to_string(), collection: None },
3141 ));
3142 $ty(offer_decl)
3143 }).collect();
3144 let children = ["a", "b"].iter().map(|name| {
3145 fdecl::Child {
3146 name: Some(name.to_string()),
3147 url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
3148 startup: Some(fdecl::StartupMode::Lazy),
3149 on_terminate: None,
3150 environment: None,
3151 ..Default::default()
3152 }
3153 }).collect();
3154 decl.offers = Some(offers);
3155 decl.children = Some(children);
3156 let result = Err(ErrorList::new(vec![
3157 Error::dependency_cycle(
3158 directed_graph::Error::CyclesDetected([vec!["child a", "child b", "child a"]].iter().cloned().collect()).format_cycle()),
3159 ]));
3160 validate_test(decl, result);
3161 }
3162 )+
3163 }
3164 }
3165
3166 macro_rules! test_weak_dependency {
3167 (
3168 $(
3169 ($test_name:ident) => {
3170 ty = $ty:expr,
3171 offer_decl = $offer_decl:expr,
3172 },
3173 )+
3174 ) => {
3175 $(
3176 #[test_case(fdecl::DependencyType::Weak)]
3177 fn $test_name(weak_dep: fdecl::DependencyType) {
3178 let mut decl = new_component_decl();
3179 let offers = vec![
3180 {
3181 let mut offer_decl = $offer_decl;
3182 offer_decl.source = Some(fdecl::Ref::Child(
3183 fdecl::ChildRef { name: "a".to_string(), collection: None },
3184 ));
3185 offer_decl.target = Some(fdecl::Ref::Child(
3186 fdecl::ChildRef { name: "b".to_string(), collection: None },
3187 ));
3188 offer_decl.dependency_type = Some(fdecl::DependencyType::Strong);
3189 $ty(offer_decl)
3190 },
3191 {
3192 let mut offer_decl = $offer_decl;
3193 offer_decl.source = Some(fdecl::Ref::Child(
3194 fdecl::ChildRef { name: "b".to_string(), collection: None },
3195 ));
3196 offer_decl.target = Some(fdecl::Ref::Child(
3197 fdecl::ChildRef { name: "a".to_string(), collection: None },
3198 ));
3199 offer_decl.dependency_type = Some(weak_dep);
3200 $ty(offer_decl)
3201 },
3202 ];
3203 let children = ["a", "b"].iter().map(|name| {
3204 fdecl::Child {
3205 name: Some(name.to_string()),
3206 url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
3207 startup: Some(fdecl::StartupMode::Lazy),
3208 on_terminate: None,
3209 environment: None,
3210 ..Default::default()
3211 }
3212 }).collect();
3213 decl.offers = Some(offers);
3214 decl.children = Some(children);
3215 let result = Ok(());
3216 validate_test(decl, result);
3217 }
3218 )+
3219 }
3220 }
3221
3222 #[track_caller]
3223 fn validate_test(input: fdecl::Component, expected_res: Result<(), ErrorList>) {
3224 let res = validate(&input);
3225 assert_eq!(res, expected_res);
3226 }
3227
3228 #[track_caller]
3229 fn validate_test_any_result(input: fdecl::Component, expected_res: Vec<Result<(), ErrorList>>) {
3230 let res = format!("{:?}", validate(&input));
3231 let expected_res_debug = format!("{:?}", expected_res);
3232
3233 let matched_exp =
3234 expected_res.into_iter().find(|expected| res == format!("{:?}", expected));
3235
3236 assert!(
3237 matched_exp.is_some(),
3238 "assertion failed: Expected one of:\n{:?}\nActual:\n{:?}",
3239 expected_res_debug,
3240 res
3241 );
3242 }
3243
3244 #[track_caller]
3245 fn validate_values_data_test(
3246 input: fdecl::ConfigValuesData,
3247 expected_res: Result<(), ErrorList>,
3248 ) {
3249 let res = validate_values_data(&input);
3250 assert_eq!(res, expected_res);
3251 }
3252
3253 #[track_caller]
3254 fn validate_capabilities_test(
3255 input: Vec<fdecl::Capability>,
3256 as_builtin: bool,
3257 expected_res: Result<(), ErrorList>,
3258 ) {
3259 let res = validate_capabilities(&input, as_builtin);
3260 assert_eq!(res, expected_res);
3261 }
3262
3263 fn new_component_decl() -> fdecl::Component {
3264 fdecl::Component {
3265 program: None,
3266 uses: None,
3267 exposes: None,
3268 offers: None,
3269 facets: None,
3270 capabilities: None,
3271 children: None,
3272 collections: None,
3273 environments: None,
3274 ..Default::default()
3275 }
3276 }
3277
3278 test_validate_any_result! {
3279 test_validate_use_disallows_nested_dirs => {
3280 input = {
3281 let mut decl = new_component_decl();
3282 decl.uses = Some(vec![
3283 fdecl::Use::Directory(fdecl::UseDirectory {
3284 dependency_type: Some(fdecl::DependencyType::Strong),
3285 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3286 source_name: Some("abc".to_string()),
3287 target_path: Some("/foo/bar".to_string()),
3288 rights: Some(fio::Operations::CONNECT),
3289 subdir: None,
3290 ..Default::default()
3291 }),
3292 fdecl::Use::Directory(fdecl::UseDirectory {
3293 dependency_type: Some(fdecl::DependencyType::Strong),
3294 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3295 source_name: Some("abc".to_string()),
3296 target_path: Some("/foo/bar/baz".to_string()),
3297 rights: Some(fio::Operations::CONNECT),
3298 subdir: None,
3299 ..Default::default()
3300 }),
3301 ]);
3302 decl
3303 },
3304 results = vec![
3305 Err(ErrorList::new(vec![
3306 Error::invalid_path_overlap(
3307 DeclType::UseDirectory, "/foo/bar/baz", DeclType::UseDirectory, "/foo/bar"),
3308 ])),
3309 Err(ErrorList::new(vec![
3310 Error::invalid_path_overlap(
3311 DeclType::UseDirectory, "/foo/bar", DeclType::UseDirectory, "/foo/bar/baz"),
3312 ])),
3313 ],
3314 },
3315 test_validate_use_disallows_nested_dirs_storage => {
3316 input = {
3317 let mut decl = new_component_decl();
3318 decl.uses = Some(vec![
3319 fdecl::Use::Storage(fdecl::UseStorage {
3320 source_name: Some("abc".to_string()),
3321 target_path: Some("/foo/bar".to_string()),
3322 ..Default::default()
3323 }),
3324 fdecl::Use::Storage(fdecl::UseStorage {
3325 source_name: Some("abc".to_string()),
3326 target_path: Some("/foo/bar/baz".to_string()),
3327 ..Default::default()
3328 }),
3329 ]);
3330 decl
3331 },
3332 results = vec![
3333 Err(ErrorList::new(vec![
3334 Error::invalid_path_overlap(
3335 DeclType::UseStorage, "/foo/bar/baz", DeclType::UseStorage, "/foo/bar"),
3336 ])),
3337 Err(ErrorList::new(vec![
3338 Error::invalid_path_overlap(
3339 DeclType::UseStorage, "/foo/bar", DeclType::UseStorage, "/foo/bar/baz"),
3340 ])),
3341 ],
3342 },
3343 test_validate_use_disallows_nested_dirs_directory_and_storage => {
3344 input = {
3345 let mut decl = new_component_decl();
3346 decl.uses = Some(vec![
3347 fdecl::Use::Directory(fdecl::UseDirectory {
3348 dependency_type: Some(fdecl::DependencyType::Strong),
3349 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3350 source_name: Some("abc".to_string()),
3351 target_path: Some("/foo/bar".to_string()),
3352 rights: Some(fio::Operations::CONNECT),
3353 subdir: None,
3354 ..Default::default()
3355 }),
3356 fdecl::Use::Storage(fdecl::UseStorage {
3357 source_name: Some("abc".to_string()),
3358 target_path: Some("/foo/bar/baz".to_string()),
3359 ..Default::default()
3360 }),
3361 ]);
3362 decl
3363 },
3364 results = vec![
3365 Err(ErrorList::new(vec![
3366 Error::invalid_path_overlap(
3367 DeclType::UseStorage, "/foo/bar/baz", DeclType::UseDirectory, "/foo/bar"),
3368 ])),
3369 Err(ErrorList::new(vec![
3370 Error::invalid_path_overlap(
3371 DeclType::UseDirectory, "/foo/bar", DeclType::UseStorage, "/foo/bar/baz"),
3372 ])),
3373 ],
3374 },
3375 test_validate_use_disallows_common_prefixes_protocol => {
3376 input = {
3377 let mut decl = new_component_decl();
3378 decl.uses = Some(vec![
3379 fdecl::Use::Directory(fdecl::UseDirectory {
3380 dependency_type: Some(fdecl::DependencyType::Strong),
3381 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3382 source_name: Some("abc".to_string()),
3383 target_path: Some("/foo/bar".to_string()),
3384 rights: Some(fio::Operations::CONNECT),
3385 subdir: None,
3386 ..Default::default()
3387 }),
3388 fdecl::Use::Protocol(fdecl::UseProtocol {
3389 dependency_type: Some(fdecl::DependencyType::Strong),
3390 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3391 source_name: Some("crow".to_string()),
3392 target_path: Some("/foo/bar/fuchsia.2".to_string()),
3393 ..Default::default()
3394 }),
3395 ]);
3396 decl
3397 },
3398 results = vec![
3399 Err(ErrorList::new(vec![
3400 Error::invalid_path_overlap(
3401 DeclType::UseProtocol, "/foo/bar/fuchsia.2", DeclType::UseDirectory, "/foo/bar"),
3402 ])),
3403 Err(ErrorList::new(vec![
3404 Error::invalid_path_overlap(
3405 DeclType::UseDirectory, "/foo/bar", DeclType::UseProtocol, "/foo/bar/fuchsia.2"),
3406 ])),
3407 ],
3408 },
3409 test_validate_use_disallows_common_prefixes_service => {
3410 input = {
3411 let mut decl = new_component_decl();
3412 decl.uses = Some(vec![
3413 fdecl::Use::Directory(fdecl::UseDirectory {
3414 dependency_type: Some(fdecl::DependencyType::Strong),
3415 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3416 source_name: Some("abc".to_string()),
3417 target_path: Some("/foo/bar".to_string()),
3418 rights: Some(fio::Operations::CONNECT),
3419 subdir: None,
3420 ..Default::default()
3421 }),
3422 fdecl::Use::Service(fdecl::UseService {
3423 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3424 source_name: Some("space".to_string()),
3425 target_path: Some("/foo/bar/baz/fuchsia.logger.Log".to_string()),
3426 dependency_type: Some(fdecl::DependencyType::Strong),
3427 ..Default::default()
3428 }),
3429 ]);
3430 decl
3431 },
3432 results = vec![
3433 Err(ErrorList::new(vec![
3434 Error::invalid_path_overlap(
3435 DeclType::UseService, "/foo/bar/baz/fuchsia.logger.Log", DeclType::UseDirectory, "/foo/bar"),
3436 ])),
3437 Err(ErrorList::new(vec![
3438 Error::invalid_path_overlap(
3439 DeclType::UseDirectory, "/foo/bar", DeclType::UseService, "/foo/bar/baz/fuchsia.logger.Log"),
3440 ])),
3441 ],
3442 },
3443 test_validate_use_disallows_pkg => {
3444 input = {
3445 let mut decl = new_component_decl();
3446 decl.uses = Some(vec![
3447 fdecl::Use::Directory(fdecl::UseDirectory {
3448 dependency_type: Some(fdecl::DependencyType::Strong),
3449 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3450 source_name: Some("abc".to_string()),
3451 target_path: Some("/pkg".to_string()),
3452 rights: Some(fio::Operations::CONNECT),
3453 subdir: None,
3454 ..Default::default()
3455 }),
3456 ]);
3457 decl
3458 },
3459 results = vec![
3460 Err(ErrorList::new(vec![
3461 Error::pkg_path_overlap(DeclType::UseDirectory, "/pkg"),
3462 ])),
3463 ],
3464 },
3465 test_validate_use_disallows_pkg_overlap => {
3466 input = {
3467 let mut decl = new_component_decl();
3468 decl.uses = Some(vec![
3469 fdecl::Use::Directory(fdecl::UseDirectory {
3470 dependency_type: Some(fdecl::DependencyType::Strong),
3471 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3472 source_name: Some("abc".to_string()),
3473 target_path: Some("/pkg/foo".to_string()),
3474 rights: Some(fio::Operations::CONNECT),
3475 subdir: None,
3476 ..Default::default()
3477 }),
3478 ]);
3479 decl
3480 },
3481 results = vec![
3482 Err(ErrorList::new(vec![
3483 Error::pkg_path_overlap(DeclType::UseDirectory, "/pkg/foo"),
3484 ])),
3485 ],
3486 },
3487 test_validate_use_optional_config_correct => {
3488 input = {
3489 let mut decl = new_component_decl();
3490 decl.uses = Some(vec![
3491 fdecl::Use::Config(fdecl::UseConfiguration {
3492 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3493 source_name: Some("abc".to_string()),
3494 target_name: Some("foo".to_string()),
3495 availability: Some(fdecl::Availability::Optional),
3496 type_: Some(fdecl::ConfigType {
3497 layout: fdecl::ConfigTypeLayout::Bool,
3498 parameters: Some(Vec::new()),
3499 constraints: Vec::new(),
3500 }),
3501 ..Default::default()
3502 }),
3503 ]);
3504 decl.config = Some(fdecl::ConfigSchema {
3505 fields: Some(vec![fdecl::ConfigField {
3506 key: Some("foo".into()),
3507 type_: Some(fdecl::ConfigType {
3508 layout: fdecl::ConfigTypeLayout::Bool,
3509 parameters: Some(Vec::new()),
3510 constraints: Vec::new(),
3511 }),
3512 mutability: None,
3513 ..Default::default()}]),
3514 checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3515 value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3516 ..Default::default()
3517 });
3518 decl
3519 },
3520 results = vec![Ok(())],
3521 },
3522 test_validate_use_optional_config_no_config_schema => {
3523 input = {
3524 let mut decl = new_component_decl();
3525 decl.uses = Some(vec![
3526 fdecl::Use::Config(fdecl::UseConfiguration {
3527 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3528 source_name: Some("abc".to_string()),
3529 target_name: Some("foo".to_string()),
3530 availability: Some(fdecl::Availability::Optional),
3531 type_: Some(fdecl::ConfigType {
3532 layout: fdecl::ConfigTypeLayout::Bool,
3533 parameters: None,
3534 constraints: Vec::new(),
3535 }),
3536 ..Default::default()
3537 }),
3538 ]);
3539 decl
3540 },
3541 results = vec![
3542 Err(ErrorList::new(vec![
3543 Error::missing_field(DeclType::ConfigField, "config"),
3544 ])),
3545 ],
3546 },
3547 test_validate_use_optional_config_no_config_field => {
3548 input = {
3549 let mut decl = new_component_decl();
3550 decl.uses = Some(vec![
3551 fdecl::Use::Config(fdecl::UseConfiguration {
3552 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3553 source_name: Some("abc".to_string()),
3554 target_name: Some("foo".to_string()),
3555 availability: Some(fdecl::Availability::Optional),
3556 type_: Some(fdecl::ConfigType {
3557 layout: fdecl::ConfigTypeLayout::Bool,
3558 parameters: None,
3559 constraints: Vec::new(),
3560 }),
3561 ..Default::default()
3562 }),
3563 ]);
3564 decl.config = Some(fdecl::ConfigSchema {
3565 fields: Some(vec![]),
3566 checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3567 value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3568 ..Default::default()
3569 });
3570 decl
3571 },
3572 results = vec![
3573 Err(ErrorList::new(vec![
3574 Error::missing_field(DeclType::ConfigField, "foo"),
3575 ])),
3576 ],
3577 },
3578 test_validate_use_optional_config_bad_type => {
3579 input = {
3580 let mut decl = new_component_decl();
3581 decl.uses = Some(vec![
3582 fdecl::Use::Config(fdecl::UseConfiguration {
3583 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3584 source_name: Some("abc".to_string()),
3585 target_name: Some("foo".to_string()),
3586 availability: Some(fdecl::Availability::Optional),
3587 type_: Some(fdecl::ConfigType {
3588 layout: fdecl::ConfigTypeLayout::Bool,
3589 parameters: None,
3590 constraints: Vec::new(),
3591 }),
3592 ..Default::default()
3593 }),
3594 ]);
3595 decl.config = Some(fdecl::ConfigSchema {
3596 fields: Some(vec![fdecl::ConfigField {
3597 key: Some("foo".into()),
3598 type_: Some(fdecl::ConfigType {
3599 layout: fdecl::ConfigTypeLayout::Int16,
3600 parameters: Some(Vec::new()),
3601 constraints: Vec::new(),
3602 }),
3603 mutability: None,
3604 ..Default::default()}]),
3605 checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3606 value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3607 ..Default::default()
3608 });
3609 decl
3610 },
3611 results = vec![
3612 Err(ErrorList::new(vec![
3613 Error::invalid_field(DeclType::ConfigField, "foo"),
3614 ])),
3615 ],
3616 },
3617 }
3618
3619 test_validate_values_data! {
3620 test_values_data_ok => {
3621 input = fdecl::ConfigValuesData {
3622 values: Some(vec![
3623 fdecl::ConfigValueSpec {
3624 value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(true))),
3625 ..Default::default()
3626 }
3627 ]),
3628 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3629 ..Default::default()
3630 },
3631 result = Ok(()),
3632 },
3633 test_values_data_no_checksum => {
3634 input = fdecl::ConfigValuesData {
3635 values: Some(vec![]),
3636 checksum: None,
3637 ..Default::default()
3638 },
3639 result = Err(ErrorList::new(vec![
3640 Error::missing_field(DeclType::ConfigValuesData, "checksum")
3641 ])),
3642 },
3643 test_values_data_unknown_checksum => {
3644 input = fdecl::ConfigValuesData {
3645 values: Some(vec![]),
3646 checksum: Some(fdecl::ConfigChecksum::unknown_variant_for_testing()),
3647 ..Default::default()
3648 },
3649 result = Err(ErrorList::new(vec![
3650 Error::invalid_field(DeclType::ConfigValuesData, "checksum")
3651 ])),
3652 },
3653 test_values_data_no_values => {
3654 input = fdecl::ConfigValuesData {
3655 values: None,
3656 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3657 ..Default::default()
3658 },
3659 result = Err(ErrorList::new(vec![
3660 Error::missing_field(DeclType::ConfigValuesData, "values")
3661 ])),
3662 },
3663 test_values_data_no_inner_value => {
3664 input = fdecl::ConfigValuesData {
3665 values: Some(vec![
3666 fdecl::ConfigValueSpec::default()
3667 ]),
3668 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3669 ..Default::default()
3670 },
3671 result = Err(ErrorList::new(vec![
3672 Error::missing_field(DeclType::ConfigValueSpec, "value")
3673 ])),
3674 },
3675 test_values_data_unknown_inner_value => {
3676 input = fdecl::ConfigValuesData {
3677 values: Some(vec![
3678 fdecl::ConfigValueSpec {
3679 value: Some(fdecl::ConfigValue::unknown_variant_for_testing()),
3680 ..Default::default()
3681 }
3682 ]),
3683 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3684 ..Default::default()
3685 },
3686 result = Err(ErrorList::new(vec![
3687 Error::invalid_field(DeclType::ConfigValueSpec, "value")
3688 ])),
3689 },
3690 test_values_data_unknown_single_value => {
3691 input = fdecl::ConfigValuesData {
3692 values: Some(vec![
3693 fdecl::ConfigValueSpec {
3694 value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::unknown_variant_for_testing())),
3695 ..Default::default()
3696 }
3697 ]),
3698 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3699 ..Default::default()
3700 },
3701 result = Err(ErrorList::new(vec![
3702 Error::invalid_field(DeclType::ConfigValueSpec, "value")
3703 ])),
3704 },
3705 test_values_data_unknown_list_value => {
3706 input = fdecl::ConfigValuesData {
3707 values: Some(vec![
3708 fdecl::ConfigValueSpec {
3709 value: Some(fdecl::ConfigValue::Vector(fdecl::ConfigVectorValue::unknown_variant_for_testing())),
3710 ..Default::default()
3711 }
3712 ]),
3713 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3714 ..Default::default()
3715 },
3716 result = Err(ErrorList::new(vec![
3717 Error::invalid_field(DeclType::ConfigValueSpec, "value")
3718 ])),
3719 },
3720 }
3721
3722 test_validate! {
3723 test_validate_uses_empty => {
3725 input = {
3726 let mut decl = new_component_decl();
3727 decl.program = Some(fdecl::Program {
3728 runner: Some("elf".to_string()),
3729 info: Some(fdata::Dictionary {
3730 entries: None,
3731 ..Default::default()
3732 }),
3733 ..Default::default()
3734 });
3735 decl.uses = Some(vec![
3736 fdecl::Use::Service(fdecl::UseService {
3737 source: None,
3738 source_name: None,
3739 target_path: None,
3740 dependency_type: None,
3741 ..Default::default()
3742 }),
3743 fdecl::Use::Protocol(fdecl::UseProtocol {
3744 dependency_type: None,
3745 source: None,
3746 source_name: None,
3747 target_path: None,
3748 ..Default::default()
3749 }),
3750 fdecl::Use::Directory(fdecl::UseDirectory {
3751 dependency_type: None,
3752 source: None,
3753 source_name: None,
3754 target_path: None,
3755 rights: None,
3756 subdir: None,
3757 ..Default::default()
3758 }),
3759 fdecl::Use::Storage(fdecl::UseStorage {
3760 source_name: None,
3761 target_path: None,
3762 ..Default::default()
3763 }),
3764 fdecl::Use::EventStream(fdecl::UseEventStream {
3765 source_name: None,
3766 source: None,
3767 target_path: None,
3768 ..Default::default()
3769 }),
3770 fdecl::Use::Runner(fdecl::UseRunner {
3771 source_name: None,
3772 source: None,
3773 ..Default::default()
3774 }),
3775 ]);
3776 decl
3777 },
3778 result = Err(ErrorList::new(vec![
3779 Error::missing_field(DeclType::UseService, "source"),
3780 Error::missing_field(DeclType::UseService, "source_name"),
3781 Error::missing_field(DeclType::UseService, "target_path"),
3782 Error::missing_field(DeclType::UseService, "dependency_type"),
3783 Error::missing_field(DeclType::UseProtocol, "source"),
3784 Error::missing_field(DeclType::UseProtocol, "source_name"),
3785 Error::missing_field(DeclType::UseProtocol, "target_path"),
3786 Error::missing_field(DeclType::UseProtocol, "dependency_type"),
3787 Error::missing_field(DeclType::UseDirectory, "source"),
3788 Error::missing_field(DeclType::UseDirectory, "source_name"),
3789 Error::missing_field(DeclType::UseDirectory, "target_path"),
3790 Error::missing_field(DeclType::UseDirectory, "dependency_type"),
3791 Error::missing_field(DeclType::UseDirectory, "rights"),
3792 Error::missing_field(DeclType::UseStorage, "source_name"),
3793 Error::missing_field(DeclType::UseStorage, "target_path"),
3794 Error::missing_field(DeclType::UseEventStream, "source"),
3795 Error::missing_field(DeclType::UseEventStream, "source_name"),
3796 Error::missing_field(DeclType::UseEventStream, "target_path"),
3797 Error::missing_field(DeclType::UseRunner, "source"),
3798 Error::missing_field(DeclType::UseRunner, "source_name"),
3799 ])),
3800 },
3801 test_validate_missing_program_info => {
3802 input = {
3803 let mut decl = new_component_decl();
3804 decl.program = Some(fdecl::Program {
3805 runner: Some("runner".to_string()),
3806 info: None,
3807 ..Default::default()
3808 });
3809 decl
3810 },
3811 result = Err(ErrorList::new(vec![
3812 Error::missing_field(DeclType::Program, "info")
3813 ])),
3814 },
3815 test_validate_uses_invalid_identifiers => {
3816 input = {
3817 let mut decl = new_component_decl();
3818 decl.uses = Some(vec![
3819 fdecl::Use::Service(fdecl::UseService {
3820 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
3821 name: "^bad".to_string(),
3822 })),
3823 source_name: Some("foo/".to_string()),
3824 target_path: Some("a/foo".to_string()),
3825 dependency_type: Some(fdecl::DependencyType::Strong),
3826 ..Default::default()
3827 }),
3828 fdecl::Use::Protocol(fdecl::UseProtocol {
3829 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3830 name: "^bad".to_string(),
3831 collection: None,
3832 })),
3833 source_name: Some("foo/".to_string()),
3834 target_path: Some("b/foo".to_string()),
3835 dependency_type: Some(fdecl::DependencyType::Strong),
3836 ..Default::default()
3837 }),
3838 fdecl::Use::Directory(fdecl::UseDirectory {
3839 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3840 name: "^bad".to_string(),
3841 collection: None,
3842 })),
3843 source_name: Some("foo/".to_string()),
3844 target_path: Some("c".to_string()),
3845 rights: Some(fio::Operations::CONNECT),
3846 subdir: Some("/foo".to_string()),
3847 dependency_type: Some(fdecl::DependencyType::Strong),
3848 ..Default::default()
3849 }),
3850 fdecl::Use::Storage(fdecl::UseStorage {
3851 source_name: Some("foo/".to_string()),
3852 target_path: Some("d".to_string()),
3853 ..Default::default()
3854 }),
3855 fdecl::Use::EventStream(fdecl::UseEventStream {
3856 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3857 name: "^bad".to_string(),
3858 collection: None,
3859 })),
3860 source_name: Some("foo/".to_string()),
3861 target_path: Some("e".to_string()),
3862 ..Default::default()
3863 }),
3864 fdecl::Use::Runner(fdecl::UseRunner {
3865 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3866 name: "^bad".to_string(),
3867 collection: None,
3868 })),
3869 source_name: Some("foo/".to_string()),
3870 ..Default::default()
3871 }),
3872 ]);
3873 decl
3874 },
3875 result = Err(ErrorList::new(vec![
3876 Error::invalid_field(DeclType::UseService, "source.capability.name"),
3877 Error::invalid_field(DeclType::UseService, "source_name"),
3878 Error::invalid_field(DeclType::UseService, "target_path"),
3879 Error::invalid_field(DeclType::UseProtocol, "source.child.name"),
3880 Error::invalid_field(DeclType::UseProtocol, "source_name"),
3881 Error::invalid_field(DeclType::UseProtocol, "target_path"),
3882 Error::invalid_field(DeclType::UseDirectory, "source.child.name"),
3883 Error::invalid_field(DeclType::UseDirectory, "source_name"),
3884 Error::invalid_field(DeclType::UseDirectory, "target_path"),
3885 Error::invalid_field(DeclType::UseDirectory, "subdir"),
3886 Error::invalid_field(DeclType::UseStorage, "source_name"),
3887 Error::invalid_field(DeclType::UseStorage, "target_path"),
3888 Error::invalid_field(DeclType::UseEventStream, "source.child.name"),
3889 Error::invalid_field(DeclType::UseEventStream, "source_name"),
3890 Error::invalid_field(DeclType::UseEventStream, "target_path"),
3891 Error::invalid_field(DeclType::UseRunner, "source.child.name"),
3892 Error::invalid_field(DeclType::UseRunner, "source_name"),
3893 ])),
3894 },
3895 test_validate_uses_missing_source => {
3896 input = {
3897 fdecl::Component {
3898 uses: Some(vec![
3899 fdecl::Use::Protocol(fdecl::UseProtocol {
3900 dependency_type: Some(fdecl::DependencyType::Strong),
3901 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
3902 name: "this-storage-doesnt-exist".to_string(),
3903 })),
3904 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3905 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3906 ..Default::default()
3907 })
3908 ]),
3909 ..new_component_decl()
3910 }
3911 },
3912 result = Err(ErrorList::new(vec![
3913 Error::invalid_capability(DeclType::UseProtocol, "source", "this-storage-doesnt-exist"),
3914 ])),
3915 },
3916 test_validate_uses_invalid_child => {
3917 input = {
3918 fdecl::Component {
3919 uses: Some(vec![
3920 fdecl::Use::Protocol(fdecl::UseProtocol {
3921 dependency_type: Some(fdecl::DependencyType::Strong),
3922 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3923 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3924 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3925 ..Default::default()
3926 }),
3927 fdecl::Use::Service(fdecl::UseService {
3928 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3929 source_name: Some("service_name".to_string()),
3930 target_path: Some("/svc/service_name".to_string()),
3931 dependency_type: Some(fdecl::DependencyType::Strong),
3932 ..Default::default()
3933 }),
3934 fdecl::Use::Directory(fdecl::UseDirectory {
3935 dependency_type: Some(fdecl::DependencyType::Strong),
3936 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3937 source_name: Some("DirectoryName".to_string()),
3938 target_path: Some("/data/DirectoryName".to_string()),
3939 rights: Some(fio::Operations::CONNECT),
3940 subdir: None,
3941 ..Default::default()
3942 }),
3943 fdecl::Use::Runner(fdecl::UseRunner {
3944 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3945 source_name: Some("RunnerName".to_string()),
3946 ..Default::default()
3947 }),
3948 ]),
3949 ..new_component_decl()
3950 }
3951 },
3952 result = Err(ErrorList::new(vec![
3953 Error::invalid_child(DeclType::UseProtocol, "source", "no-such-child"),
3954 Error::invalid_child(DeclType::UseService, "source", "no-such-child"),
3955 Error::invalid_child(DeclType::UseDirectory, "source", "no-such-child"),
3956 Error::invalid_child(DeclType::UseRunner, "source", "no-such-child"),
3957 ])),
3958 },
3959 test_validate_uses_invalid_capability_from_self => {
3960 input = {
3961 let mut decl = new_component_decl();
3962 decl.uses = Some(vec![
3963 fdecl::Use::Service(fdecl::UseService {
3964 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3965 source_name: Some("fuchsia.some.library.SomeService".into()),
3966 target_path: Some("/svc/foo".into()),
3967 dependency_type: Some(fdecl::DependencyType::Strong),
3968 ..Default::default()
3969 }),
3970 fdecl::Use::Protocol(fdecl::UseProtocol {
3971 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3972 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
3973 target_path: Some("/svc/bar".into()),
3974 dependency_type: Some(fdecl::DependencyType::Strong),
3975 ..Default::default()
3976 }),
3977 fdecl::Use::Directory(fdecl::UseDirectory {
3978 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3979 source_name: Some("dir".into()),
3980 target_path: Some("/assets".into()),
3981 dependency_type: Some(fdecl::DependencyType::Strong),
3982 rights: Some(fio::Operations::CONNECT),
3983 ..Default::default()
3984 }),
3985 fdecl::Use::Runner(fdecl::UseRunner {
3986 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3987 source_name: Some("source_elf".into()),
3988 ..Default::default()
3989 }),
3990 fdecl::Use::Config(fdecl::UseConfiguration {
3991 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3992 source_name: Some("source_config".into()),
3993 target_name: Some("config".into()),
3994 type_: Some(fdecl::ConfigType {
3995 layout: fdecl::ConfigTypeLayout::Bool,
3996 parameters: Some(Vec::new()),
3997 constraints: Vec::new(),
3998 }),
3999 ..Default::default()
4000 }),
4001 fdecl::Use::Protocol(fdecl::UseProtocol {
4002 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4003 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
4004 source_dictionary: Some("dict/inner".into()),
4005 target_path: Some("/svc/baz".into()),
4006 dependency_type: Some(fdecl::DependencyType::Strong),
4007 ..Default::default()
4008 }),
4009 ]);
4010 decl
4011 },
4012 result = Err(ErrorList::new(vec![
4013 Error::invalid_capability(
4014 DeclType::UseService,
4015 "source",
4016 "fuchsia.some.library.SomeService"),
4017 Error::invalid_capability(
4018 DeclType::UseProtocol,
4019 "source",
4020 "fuchsia.some.library.SomeProtocol"),
4021 Error::invalid_capability(DeclType::UseDirectory, "source", "dir"),
4022 Error::invalid_capability(DeclType::UseRunner, "source", "source_elf"),
4023 Error::invalid_capability(DeclType::UseConfiguration, "source", "source_config"),
4024 Error::invalid_capability(DeclType::UseProtocol, "source", "dict"),
4025 ])),
4026 },
4027 test_validate_use_from_child_offer_to_child_strong_cycle => {
4028 input = {
4029 fdecl::Component {
4030 capabilities: Some(vec![
4031 fdecl::Capability::Service(fdecl::Service {
4032 name: Some("a".to_string()),
4033 source_path: Some("/a".to_string()),
4034 ..Default::default()
4035 })]),
4036 uses: Some(vec![
4037 fdecl::Use::Protocol(fdecl::UseProtocol {
4038 dependency_type: Some(fdecl::DependencyType::Strong),
4039 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4040 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4041 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
4042 ..Default::default()
4043 }),
4044 fdecl::Use::Service(fdecl::UseService {
4045 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4046 source_name: Some("service_name".to_string()),
4047 target_path: Some("/svc/service_name".to_string()),
4048 dependency_type: Some(fdecl::DependencyType::Strong),
4049 ..Default::default()
4050 }),
4051 fdecl::Use::Directory(fdecl::UseDirectory {
4052 dependency_type: Some(fdecl::DependencyType::Strong),
4053 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4054 source_name: Some("DirectoryName".to_string()),
4055 target_path: Some("/data/DirectoryName".to_string()),
4056 rights: Some(fio::Operations::CONNECT),
4057 subdir: None,
4058 ..Default::default()
4059 }),
4060 ]),
4061 offers: Some(vec![
4062 fdecl::Offer::Service(fdecl::OfferService {
4063 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4064 source_name: Some("a".to_string()),
4065 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4066 target_name: Some("a".to_string()),
4067 ..Default::default()
4068 })
4069 ]),
4070 children: Some(vec![
4071 fdecl::Child {
4072 name: Some("child".to_string()),
4073 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4074 startup: Some(fdecl::StartupMode::Lazy),
4075 on_terminate: None,
4076 ..Default::default()
4077 }
4078 ]),
4079 ..new_component_decl()
4080 }
4081 },
4082 result = Err(ErrorList::new(vec![
4083 Error::dependency_cycle("{{self -> child child -> self}}".to_string()),
4084 ])),
4085 },
4086 test_validate_use_from_child_storage_no_cycle => {
4087 input = {
4088 fdecl::Component {
4089 capabilities: Some(vec![
4090 fdecl::Capability::Storage(fdecl::Storage {
4091 name: Some("cdata".to_string()),
4092 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None } )),
4093 backing_dir: Some("minfs".to_string()),
4094 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4095 ..Default::default()
4096 }),
4097 fdecl::Capability::Storage(fdecl::Storage {
4098 name: Some("pdata".to_string()),
4099 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4100 backing_dir: Some("minfs".to_string()),
4101 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4102 ..Default::default()
4103 }),
4104 ]),
4105 uses: Some(vec![
4106 fdecl::Use::Protocol(fdecl::UseProtocol {
4107 dependency_type: Some(fdecl::DependencyType::Strong),
4108 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child1".to_string(), collection: None})),
4109 source_name: Some("a".to_string()),
4110 target_path: Some("/svc/a".to_string()),
4111 ..Default::default()
4112 }),
4113 ]),
4114 offers: Some(vec![
4115 fdecl::Offer::Storage(fdecl::OfferStorage {
4116 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4117 source_name: Some("cdata".to_string()),
4118 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4119 target_name: Some("cdata".to_string()),
4120 ..Default::default()
4121 }),
4122 fdecl::Offer::Storage(fdecl::OfferStorage {
4123 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4124 source_name: Some("pdata".to_string()),
4125 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4126 target_name: Some("pdata".to_string()),
4127 ..Default::default()
4128 }),
4129 ]),
4130 children: Some(vec![
4131 fdecl::Child {
4132 name: Some("child1".to_string()),
4133 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4134 startup: Some(fdecl::StartupMode::Lazy),
4135 on_terminate: None,
4136 ..Default::default()
4137 },
4138 fdecl::Child {
4139 name: Some("child2".to_string()),
4140 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4141 startup: Some(fdecl::StartupMode::Lazy),
4142 on_terminate: None,
4143 ..Default::default()
4144 }
4145 ]),
4146 ..new_component_decl()
4147 }
4148 },
4149 result = Ok(()),
4150 },
4151 test_validate_use_from_child_storage_cycle => {
4152 input = {
4153 fdecl::Component {
4154 capabilities: Some(vec![
4155 fdecl::Capability::Storage(fdecl::Storage {
4156 name: Some("data".to_string()),
4157 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4158 backing_dir: Some("minfs".to_string()),
4159 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4160 ..Default::default()
4161 }),
4162 ]),
4163 uses: Some(vec![
4164 fdecl::Use::Protocol(fdecl::UseProtocol {
4165 dependency_type: Some(fdecl::DependencyType::Strong),
4166 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4167 source_name: Some("a".to_string()),
4168 target_path: Some("/svc/a".to_string()),
4169 ..Default::default()
4170 }),
4171 ]),
4172 offers: Some(vec![
4173 fdecl::Offer::Storage(fdecl::OfferStorage {
4174 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4175 source_name: Some("data".to_string()),
4176 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4177 target_name: Some("data".to_string()),
4178 ..Default::default()
4179 }),
4180 ]),
4181 children: Some(vec![
4182 fdecl::Child {
4183 name: Some("child".to_string()),
4184 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4185 startup: Some(fdecl::StartupMode::Lazy),
4186 on_terminate: None,
4187 ..Default::default()
4188 },
4189 ]),
4190 ..new_component_decl()
4191 }
4192 },
4193 result = Err(ErrorList::new(vec![
4194 Error::dependency_cycle("{{self -> capability data -> child child -> self}}".to_string()),
4195 ])),
4196 },
4197 test_validate_storage_strong_cycle_between_children => {
4198 input = {
4199 fdecl::Component {
4200 capabilities: Some(vec![
4201 fdecl::Capability::Storage(fdecl::Storage {
4202 name: Some("data".to_string()),
4203 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None } )),
4204 backing_dir: Some("minfs".to_string()),
4205 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4206 ..Default::default()
4207 })
4208 ]),
4209 offers: Some(vec![
4210 fdecl::Offer::Storage(fdecl::OfferStorage {
4211 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4212 source_name: Some("data".to_string()),
4213 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4214 target_name: Some("data".to_string()),
4215 ..Default::default()
4216 }),
4217 fdecl::Offer::Service(fdecl::OfferService {
4218 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4219 source_name: Some("a".to_string()),
4220 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4221 target_name: Some("a".to_string()),
4222 ..Default::default()
4223 }),
4224 ]),
4225 children: Some(vec![
4226 fdecl::Child {
4227 name: Some("child1".to_string()),
4228 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4229 startup: Some(fdecl::StartupMode::Lazy),
4230 on_terminate: None,
4231 ..Default::default()
4232 },
4233 fdecl::Child {
4234 name: Some("child2".to_string()),
4235 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4236 startup: Some(fdecl::StartupMode::Lazy),
4237 on_terminate: None,
4238 ..Default::default()
4239 }
4240 ]),
4241 ..new_component_decl()
4242 }
4243 },
4244 result = Err(ErrorList::new(vec![
4245 Error::dependency_cycle("{{child child1 -> capability data -> child child2 -> child child1}}".to_string()),
4246 ])),
4247 },
4248 test_validate_strong_cycle_between_children_through_environment_debug => {
4249 input = {
4250 fdecl::Component {
4251 environments: Some(vec![
4252 fdecl::Environment {
4253 name: Some("env".to_string()),
4254 extends: Some(fdecl::EnvironmentExtends::Realm),
4255 debug_capabilities: Some(vec![
4256 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
4257 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4258 source_name: Some("fuchsia.foo.Bar".to_string()),
4259 target_name: Some("fuchsia.foo.Bar".to_string()),
4260 ..Default::default()
4261 }),
4262 ]),
4263 ..Default::default()
4264 },
4265 ]),
4266 offers: Some(vec![
4267 fdecl::Offer::Service(fdecl::OfferService {
4268 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4269 source_name: Some("a".to_string()),
4270 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4271 target_name: Some("a".to_string()),
4272 ..Default::default()
4273 }),
4274 ]),
4275 children: Some(vec![
4276 fdecl::Child {
4277 name: Some("child1".to_string()),
4278 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4279 startup: Some(fdecl::StartupMode::Lazy),
4280 on_terminate: None,
4281 ..Default::default()
4282 },
4283 fdecl::Child {
4284 name: Some("child2".to_string()),
4285 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4286 startup: Some(fdecl::StartupMode::Lazy),
4287 environment: Some("env".to_string()),
4288 on_terminate: None,
4289 ..Default::default()
4290 }
4291 ]),
4292 ..new_component_decl()
4293 }
4294 },
4295 result = Err(ErrorList::new(vec![
4296 Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}".to_string()),
4297 ])),
4298 },
4299 test_validate_strong_cycle_between_children_through_environment_runner => {
4300 input = {
4301 fdecl::Component {
4302 environments: Some(vec![
4303 fdecl::Environment {
4304 name: Some("env".to_string()),
4305 extends: Some(fdecl::EnvironmentExtends::Realm),
4306 runners: Some(vec![
4307 fdecl::RunnerRegistration {
4308 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4309 source_name: Some("coff".to_string()),
4310 target_name: Some("coff".to_string()),
4311 ..Default::default()
4312 }
4313 ]),
4314 ..Default::default()
4315 },
4316 ]),
4317 offers: Some(vec![
4318 fdecl::Offer::Service(fdecl::OfferService {
4319 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4320 source_name: Some("a".to_string()),
4321 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4322 target_name: Some("a".to_string()),
4323 ..Default::default()
4324 }),
4325 ]),
4326 children: Some(vec![
4327 fdecl::Child {
4328 name: Some("child1".to_string()),
4329 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4330 startup: Some(fdecl::StartupMode::Lazy),
4331 on_terminate: None,
4332 ..Default::default()
4333 },
4334 fdecl::Child {
4335 name: Some("child2".to_string()),
4336 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4337 startup: Some(fdecl::StartupMode::Lazy),
4338 environment: Some("env".to_string()),
4339 on_terminate: None,
4340 ..Default::default()
4341 }
4342 ]),
4343 ..new_component_decl()
4344 }
4345 },
4346 result = Err(ErrorList::new(vec![
4347 Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}".to_string()),
4348 ])),
4349 },
4350 test_validate_strong_cycle_between_children_through_environment_resolver => {
4351 input = {
4352 fdecl::Component {
4353 environments: Some(vec![
4354 fdecl::Environment {
4355 name: Some("env".to_string()),
4356 extends: Some(fdecl::EnvironmentExtends::Realm),
4357 resolvers: Some(vec![
4358 fdecl::ResolverRegistration {
4359 resolver: Some("gopher".to_string()),
4360 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4361 scheme: Some("gopher".to_string()),
4362 ..Default::default()
4363 }
4364 ]),
4365 ..Default::default()
4366 },
4367 ]),
4368 offers: Some(vec![
4369 fdecl::Offer::Service(fdecl::OfferService {
4370 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4371 source_name: Some("a".to_string()),
4372 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4373 target_name: Some("a".to_string()),
4374 ..Default::default()
4375 }),
4376 ]),
4377 children: Some(vec![
4378 fdecl::Child {
4379 name: Some("child1".to_string()),
4380 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4381 startup: Some(fdecl::StartupMode::Lazy),
4382 on_terminate: None,
4383 ..Default::default()
4384 },
4385 fdecl::Child {
4386 name: Some("child2".to_string()),
4387 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4388 startup: Some(fdecl::StartupMode::Lazy),
4389 environment: Some("env".to_string()),
4390 on_terminate: None,
4391 ..Default::default()
4392 }
4393 ]),
4394 ..new_component_decl()
4395 }
4396 },
4397 result = Err(ErrorList::new(vec![
4398 Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}".to_string()),
4399 ])),
4400 },
4401 test_validate_strong_cycle_between_self_and_two_children => {
4402 input = {
4403 fdecl::Component {
4404 capabilities: Some(vec![
4405 fdecl::Capability::Protocol(fdecl::Protocol {
4406 name: Some("fuchsia.foo.Bar".to_string()),
4407 source_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4408 ..Default::default()
4409 })
4410 ]),
4411 offers: Some(vec![
4412 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4413 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4414 source_name: Some("fuchsia.foo.Bar".to_string()),
4415 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4416 target_name: Some("fuchsia.foo.Bar".to_string()),
4417 dependency_type: Some(fdecl::DependencyType::Strong),
4418 ..Default::default()
4419 }),
4420 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4421 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4422 source_name: Some("fuchsia.bar.Baz".to_string()),
4423 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4424 target_name: Some("fuchsia.bar.Baz".to_string()),
4425 dependency_type: Some(fdecl::DependencyType::Strong),
4426 ..Default::default()
4427 }),
4428 ]),
4429 uses: Some(vec![
4430 fdecl::Use::Protocol(fdecl::UseProtocol {
4431 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child2".to_string(), collection: None})),
4432 source_name: Some("fuchsia.baz.Foo".to_string()),
4433 target_path: Some("/svc/fuchsia.baz.Foo".to_string()),
4434 dependency_type: Some(fdecl::DependencyType::Strong),
4435 ..Default::default()
4436 }),
4437 ]),
4438 children: Some(vec![
4439 fdecl::Child {
4440 name: Some("child1".to_string()),
4441 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4442 startup: Some(fdecl::StartupMode::Lazy),
4443 on_terminate: None,
4444 ..Default::default()
4445 },
4446 fdecl::Child {
4447 name: Some("child2".to_string()),
4448 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4449 startup: Some(fdecl::StartupMode::Lazy),
4450 on_terminate: None,
4451 ..Default::default()
4452 }
4453 ]),
4454 ..new_component_decl()
4455 }
4456 },
4457 result = Err(ErrorList::new(vec![
4458 Error::dependency_cycle("{{self -> child child1 -> child child2 -> self}}".to_string()),
4459 ])),
4460 },
4461 test_validate_strong_cycle_with_self_storage => {
4462 input = {
4463 fdecl::Component {
4464 capabilities: Some(vec![
4465 fdecl::Capability::Storage(fdecl::Storage {
4466 name: Some("data".to_string()),
4467 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4468 backing_dir: Some("minfs".to_string()),
4469 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4470 ..Default::default()
4471 }),
4472 fdecl::Capability::Directory(fdecl::Directory {
4473 name: Some("minfs".to_string()),
4474 source_path: Some("/minfs".to_string()),
4475 rights: Some(fio::RW_STAR_DIR),
4476 ..Default::default()
4477 }),
4478 ]),
4479 offers: Some(vec![
4480 fdecl::Offer::Storage(fdecl::OfferStorage {
4481 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4482 source_name: Some("data".to_string()),
4483 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4484 target_name: Some("data".to_string()),
4485 ..Default::default()
4486 }),
4487 ]),
4488 uses: Some(vec![
4489 fdecl::Use::Protocol(fdecl::UseProtocol {
4490 dependency_type: Some(fdecl::DependencyType::Strong),
4491 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4492 source_name: Some("fuchsia.foo.Bar".to_string()),
4493 target_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4494 ..Default::default()
4495 }),
4496 ]),
4497 children: Some(vec![
4498 fdecl::Child {
4499 name: Some("child".to_string()),
4500 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4501 startup: Some(fdecl::StartupMode::Lazy),
4502 ..Default::default()
4503 },
4504 ]),
4505 ..new_component_decl()
4506 }
4507 },
4508 result = Err(ErrorList::new(vec![
4509 Error::dependency_cycle("{{self -> capability data -> child child -> self}}".to_string()),
4510 ])),
4511 },
4512 test_validate_strong_cycle_with_self_storage_admin_protocol => {
4513 input = {
4514 fdecl::Component {
4515 capabilities: Some(vec![
4516 fdecl::Capability::Storage(fdecl::Storage {
4517 name: Some("data".to_string()),
4518 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4519 backing_dir: Some("minfs".to_string()),
4520 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4521 ..Default::default()
4522 }),
4523 fdecl::Capability::Directory(fdecl::Directory {
4524 name: Some("minfs".to_string()),
4525 source_path: Some("/minfs".to_string()),
4526 rights: Some(fio::RW_STAR_DIR),
4527 ..Default::default()
4528 }),
4529 ]),
4530 offers: Some(vec![
4531 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4532 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef { name: "data".to_string() })),
4533 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4534 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4535 target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4536 dependency_type: Some(fdecl::DependencyType::Strong),
4537 ..Default::default()
4538 }),
4539 ]),
4540 uses: Some(vec![
4541 fdecl::Use::Protocol(fdecl::UseProtocol {
4542 dependency_type: Some(fdecl::DependencyType::Strong),
4543 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4544 source_name: Some("fuchsia.foo.Bar".to_string()),
4545 target_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4546 ..Default::default()
4547 }),
4548 ]),
4549 children: Some(vec![
4550 fdecl::Child {
4551 name: Some("child".to_string()),
4552 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4553 startup: Some(fdecl::StartupMode::Lazy),
4554 ..Default::default()
4555 },
4556 ]),
4557 ..new_component_decl()
4558 }
4559 },
4560 result = Err(ErrorList::new(vec![
4561 Error::dependency_cycle("{{self -> capability data -> child child -> self}}".to_string()),
4562 ])),
4563 },
4564 test_validate_strong_cycle_with_dictionary => {
4565 input = fdecl::Component {
4566 offers: Some(vec![
4567 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
4568 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4569 source_name: Some("dict".into()),
4570 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4571 name: "a".into(),
4572 collection: None,
4573 })),
4574 target_name: Some("dict".into()),
4575 dependency_type: Some(fdecl::DependencyType::Strong),
4576 ..Default::default()
4577 }),
4578 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4579 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4580 name: "b".into(),
4581 collection: None,
4582 })),
4583 source_name: Some("1".into()),
4584 target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4585 name: "dict".into(),
4586 })),
4587 target_name: Some("1".into()),
4588 dependency_type: Some(fdecl::DependencyType::Strong),
4589 ..Default::default()
4590 }),
4591 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4592 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4593 name: "a".into(),
4594 collection: None,
4595 })),
4596 source_name: Some("2".into()),
4597 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4598 name: "b".into(),
4599 collection: None,
4600 })),
4601 target_name: Some("2".into()),
4602 dependency_type: Some(fdecl::DependencyType::Strong),
4603 ..Default::default()
4604 }),
4605 ]),
4606 children: Some(vec![
4607 fdecl::Child {
4608 name: Some("a".into()),
4609 url: Some("fuchsia-pkg://child".into()),
4610 startup: Some(fdecl::StartupMode::Lazy),
4611 ..Default::default()
4612 },
4613 fdecl::Child {
4614 name: Some("b".into()),
4615 url: Some("fuchsia-pkg://child".into()),
4616 startup: Some(fdecl::StartupMode::Lazy),
4617 ..Default::default()
4618 },
4619 ]),
4620 capabilities: Some(vec![
4621 fdecl::Capability::Dictionary(fdecl::Dictionary {
4622 name: Some("dict".into()),
4623 ..Default::default()
4624 }),
4625 ]),
4626 ..Default::default()
4627 },
4628 result = Err(ErrorList::new(vec![
4629 Error::dependency_cycle("{{child a -> child b -> capability dict -> child a}}".to_string()),
4630 ])),
4631 },
4632 test_validate_strong_cycle_with_dictionary_indirect => {
4633 input = fdecl::Component {
4634 offers: Some(vec![
4635 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4636 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4637 source_name: Some("3".into()),
4638 source_dictionary: Some("dict".into()),
4639 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4640 name: "a".into(),
4641 collection: None,
4642 })),
4643 target_name: Some("3".into()),
4644 dependency_type: Some(fdecl::DependencyType::Strong),
4645 ..Default::default()
4646 }),
4647 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4648 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4649 name: "b".into(),
4650 collection: None,
4651 })),
4652 source_name: Some("1".into()),
4653 target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4654 name: "dict".into(),
4655 })),
4656 target_name: Some("1".into()),
4657 dependency_type: Some(fdecl::DependencyType::Strong),
4658 ..Default::default()
4659 }),
4660 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4661 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4662 name: "a".into(),
4663 collection: None,
4664 })),
4665 source_name: Some("2".into()),
4666 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4667 name: "b".into(),
4668 collection: None,
4669 })),
4670 target_name: Some("2".into()),
4671 dependency_type: Some(fdecl::DependencyType::Strong),
4672 ..Default::default()
4673 }),
4674 ]),
4675 children: Some(vec![
4676 fdecl::Child {
4677 name: Some("a".into()),
4678 url: Some("fuchsia-pkg://child".into()),
4679 startup: Some(fdecl::StartupMode::Lazy),
4680 ..Default::default()
4681 },
4682 fdecl::Child {
4683 name: Some("b".into()),
4684 url: Some("fuchsia-pkg://child".into()),
4685 startup: Some(fdecl::StartupMode::Lazy),
4686 ..Default::default()
4687 },
4688 ]),
4689 capabilities: Some(vec![
4690 fdecl::Capability::Dictionary(fdecl::Dictionary {
4691 name: Some("dict".into()),
4692 ..Default::default()
4693 }),
4694 ]),
4695 ..Default::default()
4696 },
4697 result = Err(ErrorList::new(vec![
4698 Error::dependency_cycle("{{child a -> child b -> capability dict -> child a}}".to_string()),
4699 ])),
4700 },
4701 test_validate_use_from_child_offer_to_child_weak_cycle => {
4702 input = {
4703 fdecl::Component {
4704 capabilities: Some(vec![
4705 fdecl::Capability::Service(fdecl::Service {
4706 name: Some("a".to_string()),
4707 source_path: Some("/a".to_string()),
4708 ..Default::default()
4709 })]),
4710 uses: Some(vec![
4711 fdecl::Use::Protocol(fdecl::UseProtocol {
4712 dependency_type: Some(fdecl::DependencyType::Weak),
4713 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4714 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4715 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
4716 ..Default::default()
4717 }),
4718 fdecl::Use::Service(fdecl::UseService {
4719 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4720 source_name: Some("service_name".to_string()),
4721 target_path: Some("/svc/service_name".to_string()),
4722 dependency_type: Some(fdecl::DependencyType::Weak),
4723 ..Default::default()
4724 }),
4725 fdecl::Use::Directory(fdecl::UseDirectory {
4726 dependency_type: Some(fdecl::DependencyType::Weak),
4727 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4728 source_name: Some("DirectoryName".to_string()),
4729 target_path: Some("/data/DirectoryName".to_string()),
4730 rights: Some(fio::Operations::CONNECT),
4731 subdir: None,
4732 ..Default::default()
4733 }),
4734 ]),
4735 offers: Some(vec![
4736 fdecl::Offer::Service(fdecl::OfferService {
4737 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4738 source_name: Some("a".to_string()),
4739 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4740 target_name: Some("a".to_string()),
4741 ..Default::default()
4742 })
4743 ]),
4744 children: Some(vec![
4745 fdecl::Child {
4746 name: Some("child".to_string()),
4747 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4748 startup: Some(fdecl::StartupMode::Lazy),
4749 on_terminate: None,
4750 ..Default::default()
4751 }
4752 ]),
4753 ..new_component_decl()
4754 }
4755 },
4756 result = Ok(()),
4757 },
4758 test_validate_expose_from_self_to_framework_and_parent => {
4759 input = {
4760 fdecl::Component {
4761 capabilities: Some(vec![
4762 fdecl::Capability::Protocol(fdecl::Protocol {
4763 name: Some("a".to_string()),
4764 source_path: Some("/a".to_string()),
4765 ..Default::default()
4766 }),
4767 ]),
4768 exposes: Some(vec![
4769 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
4770 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4771 source_name: Some("a".to_string()),
4772 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4773 target_name: Some("a".to_string()),
4774 ..Default::default()
4775 }),
4776 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
4777 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4778 source_name: Some("a".to_string()),
4779 target: Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})),
4780 target_name: Some("a".to_string()),
4781 ..Default::default()
4782 }),
4783 ]),
4784 ..new_component_decl()
4785 }
4786 },
4787 result = Ok(()),
4788 },
4789 test_validate_use_from_not_child_weak => {
4790 input = {
4791 fdecl::Component {
4792 uses: Some(vec![
4793 fdecl::Use::Protocol(fdecl::UseProtocol {
4794 dependency_type: Some(fdecl::DependencyType::Weak),
4795 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4796 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4797 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
4798 ..Default::default()
4799 }),
4800 ]),
4801 ..new_component_decl()
4802 }
4803 },
4804 result = Err(ErrorList::new(vec![
4805 Error::invalid_field(DeclType::UseProtocol, "dependency_type"),
4806 ])),
4807 },
4808 test_validate_event_stream_offer_valid_decls => {
4809 input = {
4810 let mut decl = new_component_decl();
4811 decl.offers = Some(vec![
4812 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4813 source_name: Some("stopped".to_string()),
4814 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4815 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4816 target_name: Some("stopped".to_string()),
4817 ..Default::default()
4818 }),
4819 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4820 source_name: Some("started".to_string()),
4821 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4822 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4823 target_name: Some("started".to_string()),
4824 ..Default::default()
4825 }),
4826 ]);
4827 decl.children = Some(vec![fdecl::Child{
4828 name: Some("test".to_string()),
4829 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4830 startup: Some(fdecl::StartupMode::Lazy),
4831 on_terminate: None,
4832 environment: None,
4833 ..Default::default()
4834 },
4835 fdecl::Child{
4836 name: Some("test2".to_string()),
4837 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4838 startup: Some(fdecl::StartupMode::Lazy),
4839 on_terminate: None,
4840 environment: None,
4841 ..Default::default()
4842 }
4843 ]);
4844 decl
4845 },
4846 result = Ok(()),
4847 },
4848 test_validate_event_stream_offer_to_framework_invalid => {
4849 input = {
4850 let mut decl = new_component_decl();
4851 decl.offers = Some(vec![
4852 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4853 source_name: Some("stopped".to_string()),
4854 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4855 target: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
4856 target_name: Some("stopped".to_string()),
4857 ..Default::default()
4858 }),
4859 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4860 source_name: Some("started".to_string()),
4861 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4862 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4863 target_name: Some("started".to_string()),
4864 ..Default::default()
4865 }),
4866 ]);
4867 decl.children = Some(vec![fdecl::Child{
4868 name: Some("test".to_string()),
4869 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4870 startup: Some(fdecl::StartupMode::Lazy),
4871 on_terminate: None,
4872 environment: None,
4873 ..Default::default()
4874 },
4875 fdecl::Child{
4876 name: Some("test2".to_string()),
4877 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4878 startup: Some(fdecl::StartupMode::Lazy),
4879 on_terminate: None,
4880 environment: None,
4881 ..Default::default()
4882 }
4883 ]);
4884 decl
4885 },
4886 result = Err(ErrorList::new(vec![
4887 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "target".to_string() }),
4888 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "target".to_string() }),
4889 ])),
4890 },
4891 test_validate_event_stream_offer_to_scope_zero_length_invalid => {
4892 input = {
4893 let mut decl = new_component_decl();
4894 decl.offers = Some(vec![
4895 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4896 source_name: Some("started".to_string()),
4897 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4898 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4899 scope: Some(vec![]),
4900 target_name: Some("started".to_string()),
4901 ..Default::default()
4902 }),
4903 ]);
4904 decl.children = Some(vec![fdecl::Child{
4905 name: Some("test".to_string()),
4906 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4907 startup: Some(fdecl::StartupMode::Lazy),
4908 on_terminate: None,
4909 environment: None,
4910 ..Default::default()
4911 },
4912 fdecl::Child{
4913 name: Some("test2".to_string()),
4914 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4915 startup: Some(fdecl::StartupMode::Lazy),
4916 on_terminate: None,
4917 environment: None,
4918 ..Default::default()
4919 }
4920 ]);
4921 decl
4922 },
4923 result = Err(ErrorList::new(vec![
4924 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "scope".to_string() }),
4925 ])),
4926 },
4927 test_validate_event_stream_offer_to_scope_framework_invalid => {
4928 input = {
4929 let mut decl = new_component_decl();
4930 decl.offers = Some(vec![
4931 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4932 source_name: Some("started".to_string()),
4933 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4934 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4935 scope: Some(vec![fdecl::Ref::Framework(fdecl::FrameworkRef{})]),
4936 target_name: Some("started".to_string()),
4937 ..Default::default()
4938 }),
4939 ]);
4940 decl.children = Some(vec![fdecl::Child{
4941 name: Some("test".to_string()),
4942 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4943 startup: Some(fdecl::StartupMode::Lazy),
4944 on_terminate: None,
4945 environment: None,
4946 ..Default::default()
4947 },
4948 fdecl::Child{
4949 name: Some("test2".to_string()),
4950 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4951 startup: Some(fdecl::StartupMode::Lazy),
4952 on_terminate: None,
4953 environment: None,
4954 ..Default::default()
4955 }
4956 ]);
4957 decl
4958 },
4959 result = Err(ErrorList::new(vec![
4960 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "scope".to_string() }),
4961 ])),
4962 },
4963 test_validate_event_stream_offer_to_scope_valid => {
4964 input = {
4965 let mut decl = new_component_decl();
4966 decl.offers = Some(vec![
4967 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4968 source_name: Some("started".to_string()),
4969 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4970 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4971 scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
4972 target_name: Some("started".to_string()),
4973 ..Default::default()
4974 }),
4975 ]);
4976 decl.children = Some(vec![fdecl::Child{
4977 name: Some("test".to_string()),
4978 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4979 startup: Some(fdecl::StartupMode::Lazy),
4980 on_terminate: None,
4981 environment: None,
4982 ..Default::default()
4983 },
4984 fdecl::Child{
4985 name: Some("test2".to_string()),
4986 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4987 startup: Some(fdecl::StartupMode::Lazy),
4988 on_terminate: None,
4989 environment: None,
4990 ..Default::default()
4991 }
4992 ]);
4993 decl
4994 },
4995 result = Ok(()),
4996 },
4997 test_validate_event_stream_offer_to_scope_with_capability_requested => {
4998 input = {
4999 let mut decl = new_component_decl();
5000 decl.offers = Some(vec![
5001 fdecl::Offer::EventStream(fdecl::OfferEventStream {
5002 source_name: Some("capability_requested".to_string()),
5003 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5004 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5005 scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
5006 target_name: Some("started".to_string()),
5007 ..Default::default()
5008 }),
5009 ]);
5010 decl.children = Some(vec![fdecl::Child{
5011 name: Some("test".to_string()),
5012 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5013 startup: Some(fdecl::StartupMode::Lazy),
5014 on_terminate: None,
5015 environment: None,
5016 ..Default::default()
5017 },
5018 fdecl::Child{
5019 name: Some("test2".to_string()),
5020 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
5021 startup: Some(fdecl::StartupMode::Lazy),
5022 on_terminate: None,
5023 environment: None,
5024 ..Default::default()
5025 }
5026 ]);
5027 decl
5028 },
5029 result = Ok(()),
5030 },
5031 test_validate_event_stream_offer_with_no_source_name_invalid => {
5032 input = {
5033 let mut decl = new_component_decl();
5034 decl.offers = Some(vec![
5035 fdecl::Offer::EventStream(fdecl::OfferEventStream {
5036 source_name: None,
5037 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5038 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5039 scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
5040 target_name: Some("started".to_string()),
5041 ..Default::default()
5042 }),
5043 ]);
5044 decl.children = Some(vec![fdecl::Child{
5045 name: Some("test".to_string()),
5046 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5047 startup: Some(fdecl::StartupMode::Lazy),
5048 on_terminate: None,
5049 environment: None,
5050 ..Default::default()
5051 },
5052 fdecl::Child{
5053 name: Some("test2".to_string()),
5054 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
5055 startup: Some(fdecl::StartupMode::Lazy),
5056 on_terminate: None,
5057 environment: None,
5058 ..Default::default()
5059 }
5060 ]);
5061 decl
5062 },
5063 result = Err(ErrorList::new(vec![
5064 Error::MissingField(DeclField { decl: DeclType::OfferEventStream, field: "source_name".to_string() }),
5065 ])),
5066 },
5067 test_validate_event_stream_offer_invalid_source => {
5068 input = {
5069 let mut decl = new_component_decl();
5070 decl.offers = Some(vec![
5071 fdecl::Offer::EventStream(fdecl::OfferEventStream {
5072 source_name: Some("stopped".to_string()),
5073 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
5074 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5075 target_name: Some("stopped".to_string()),
5076 ..Default::default()
5077 }),
5078 fdecl::Offer::EventStream(fdecl::OfferEventStream {
5079 source_name: Some("started".to_string()),
5080 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5081 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5082 target_name: Some("started".to_string()),
5083 ..Default::default()
5084 }),
5085 fdecl::Offer::EventStream(fdecl::OfferEventStream {
5086 source_name: Some("capability_requested".to_string()),
5087 source: Some(fdecl::Ref::Debug(fdecl::DebugRef{})),
5088 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5089 target_name: Some("capability_requested".to_string()),
5090 ..Default::default()
5091 }),
5092 ]);
5093 decl.children = Some(vec![fdecl::Child{
5094 name: Some("test".to_string()),
5095 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5096 startup: Some(fdecl::StartupMode::Lazy),
5097 on_terminate: None,
5098 environment: None,
5099 ..Default::default()
5100 },
5101 fdecl::Child{
5102 name: Some("test2".to_string()),
5103 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
5104 startup: Some(fdecl::StartupMode::Lazy),
5105 on_terminate: None,
5106 environment: None,
5107 ..Default::default()
5108 }
5109 ]);
5110 decl
5111 },
5112 result = Err(ErrorList::new(vec![
5113 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "source".to_string() }),
5114 ])),
5115 },
5116
5117 test_validate_event_stream_offer_missing_source => {
5118 input = {
5119 let mut decl = new_component_decl();
5120 decl.offers = Some(vec![
5121 fdecl::Offer::EventStream(fdecl::OfferEventStream {
5122 source_name: Some("stopped".to_string()),
5123 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
5124 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5125 target_name: Some("stopped".to_string()),
5126 ..Default::default()
5127 }),
5128 fdecl::Offer::EventStream(fdecl::OfferEventStream {
5129 source_name: Some("started".to_string()),
5130 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5131 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5132 target_name: Some("started".to_string()),
5133 ..Default::default()
5134 }),
5135 fdecl::Offer::EventStream(fdecl::OfferEventStream {
5136 source_name: Some("capability_requested".to_string()),
5137 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5138 target_name: Some("capability_requested".to_string()),
5139 ..Default::default()
5140 }),
5141 ]);
5142 decl.children = Some(vec![fdecl::Child{
5143 name: Some("test".to_string()),
5144 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5145 startup: Some(fdecl::StartupMode::Lazy),
5146 on_terminate: None,
5147 environment: None,
5148 ..Default::default()
5149 },
5150 fdecl::Child{
5151 name: Some("test2".to_string()),
5152 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
5153 startup: Some(fdecl::StartupMode::Lazy),
5154 on_terminate: None,
5155 environment: None,
5156 ..Default::default()
5157 }
5158 ]);
5159 decl
5160 },
5161 result = Err(ErrorList::new(vec![
5162 Error::MissingField(DeclField { decl: DeclType::OfferEventStream, field: "source".to_string() }),
5163 ])),
5164 },
5165 test_validate_event_stream_must_have_target_path => {
5166 input = {
5167 let mut decl = new_component_decl();
5168 decl.uses = Some(vec![
5169 fdecl::Use::EventStream(fdecl::UseEventStream {
5170 source_name: Some("bar".to_string()),
5171 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5172 ..Default::default()
5173 }),
5174 ]);
5175 decl
5176 },
5177 result = Err(ErrorList::new(vec![
5178 Error::MissingField(DeclField { decl: DeclType::UseEventStream, field: "target_path".to_string() })
5179 ])),
5180 },
5181 test_validate_event_stream_must_have_source_names => {
5182 input = {
5183 let mut decl = new_component_decl();
5184 decl.uses = Some(vec![
5185 fdecl::Use::EventStream(fdecl::UseEventStream {
5186 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5187 target_path: Some("/svc/something".to_string()),
5188 ..Default::default()
5189 }),
5190 ]);
5191 decl
5192 },
5193 result = Err(ErrorList::new(vec![
5194 Error::MissingField(DeclField { decl: DeclType::UseEventStream, field: "source_name".to_string() })
5195 ])),
5196 },
5197 test_validate_event_stream_scope_must_be_child_or_collection => {
5198 input = {
5199 let mut decl = new_component_decl();
5200 decl.uses = Some(vec![
5201 fdecl::Use::EventStream(fdecl::UseEventStream {
5202 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5203 target_path: Some("/svc/something".to_string()),
5204 source_name: Some("some_source".to_string()),
5205 scope: Some(vec![fdecl::Ref::Framework(fdecl::FrameworkRef{})]),
5206 ..Default::default()
5207 }),
5208 ]);
5209 decl
5210 },
5211 result = Err(ErrorList::new(vec![
5212 Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "scope".to_string() })
5213 ])),
5214 },
5215 test_validate_event_stream_source_must_be_parent_or_child => {
5216 input = {
5217 let mut decl = new_component_decl();
5218 decl.uses = Some(vec![
5219 fdecl::Use::EventStream(fdecl::UseEventStream {
5220 source: Some(fdecl::Ref::Debug(fdecl::DebugRef{})),
5221 target_path: Some("/svc/something".to_string()),
5222 source_name: Some("some_source".to_string()),
5223 scope: Some(vec![]),
5224 ..Default::default()
5225 }),
5226 fdecl::Use::EventStream(fdecl::UseEventStream {
5227 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
5228 target_path: Some("/svc/something_else".to_string()),
5229 source_name: Some("some_source".to_string()),
5230 scope: Some(vec![]),
5231 ..Default::default()
5232 }),
5233 fdecl::Use::EventStream(fdecl::UseEventStream {
5234 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5235 target_path: Some("/svc/yet_something_else".to_string()),
5236 source_name: Some("some_source".to_string()),
5237 scope: Some(vec![]),
5238 ..Default::default()
5239 }),
5240 ]);
5241 decl
5242 },
5243 result = Err(ErrorList::new(vec![
5244 Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() }),
5245 Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() }),
5246 Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() })
5247 ])),
5248 },
5249 test_validate_no_runner => {
5250 input = {
5251 let mut decl = new_component_decl();
5252 decl.program = Some(fdecl::Program {
5253 runner: None,
5254 info: Some(fdata::Dictionary {
5255 entries: None,
5256 ..Default::default()
5257 }),
5258 ..Default::default()
5259 });
5260 decl
5261 },
5262 result = Err(ErrorList::new(vec![
5263 Error::MissingRunner,
5264 ])),
5265 },
5266 test_validate_uses_runner => {
5267 input = {
5268 let mut decl = new_component_decl();
5269 decl.program = Some(fdecl::Program {
5270 runner: None,
5271 info: Some(fdata::Dictionary {
5272 entries: None,
5273 ..Default::default()
5274 }),
5275 ..Default::default()
5276 });
5277 decl.uses = Some(vec![
5278 fdecl::Use::Runner(fdecl::UseRunner {
5279 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5280 source_name: Some("runner".to_string()),
5281 ..Default::default()
5282 }),
5283 ]);
5284 decl
5285 },
5286 result = Ok(()),
5287 },
5288 test_validate_program_and_uses_runner_match => {
5289 input = {
5290 let mut decl = new_component_decl();
5291 decl.program = Some(fdecl::Program {
5292 runner: Some("runner".to_string()),
5293 info: Some(fdata::Dictionary {
5294 entries: None,
5295 ..Default::default()
5296 }),
5297 ..Default::default()
5298 });
5299 decl.uses = Some(vec![
5300 fdecl::Use::Runner(fdecl::UseRunner {
5301 source: Some(fdecl::Ref::Environment(fdecl::EnvironmentRef {})),
5302 source_name: Some("runner".to_string()),
5303 ..Default::default()
5304 }),
5305 ]);
5306 decl
5307 },
5308 result = Ok(()),
5309 },
5310 test_validate_runner_names_conflict => {
5311 input = {
5312 let mut decl = new_component_decl();
5313 decl.program = Some(fdecl::Program {
5314 runner: Some("runner".to_string()),
5315 info: Some(fdata::Dictionary {
5316 entries: None,
5317 ..Default::default()
5318 }),
5319 ..Default::default()
5320 });
5321 decl.uses = Some(vec![
5322 fdecl::Use::Runner(fdecl::UseRunner {
5323 source: Some(fdecl::Ref::Environment(fdecl::EnvironmentRef {})),
5324 source_name: Some("other.runner".to_string()),
5325 ..Default::default()
5326 }),
5327 ]);
5328 decl
5329 },
5330 result = Err(ErrorList::new(vec![
5331 Error::ConflictingRunners,
5332 ])),
5333 },
5334 test_validate_uses_runner_not_environement => {
5335 input = {
5336 let mut decl = new_component_decl();
5337 decl.program = Some(fdecl::Program {
5338 runner: Some("runner".to_string()),
5339 info: Some(fdata::Dictionary {
5340 entries: None,
5341 ..Default::default()
5342 }),
5343 ..Default::default()
5344 });
5345 decl.uses = Some(vec![
5346 fdecl::Use::Runner(fdecl::UseRunner {
5347 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5348 source_name: Some("runner".to_string()),
5349 ..Default::default()
5350 }),
5351 ]);
5352 decl
5353 },
5354 result = Err(ErrorList::new(vec![
5355 Error::ConflictingRunners,
5356 ])),
5357 },
5358 test_validate_uses_long_identifiers => {
5359 input = {
5360 let mut decl = new_component_decl();
5361 decl.program = Some(fdecl::Program {
5362 runner: Some("elf".to_string()),
5363 info: Some(fdata::Dictionary {
5364 entries: None,
5365 ..Default::default()
5366 }),
5367 ..Default::default()
5368 });
5369 decl.uses = Some(vec![
5370 fdecl::Use::Service(fdecl::UseService {
5371 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5372 source_name: Some(format!("{}", "a".repeat(256))),
5373 target_path: Some("/a".repeat(2048)),
5374 dependency_type: Some(fdecl::DependencyType::Strong),
5375 ..Default::default()
5376 }),
5377 fdecl::Use::Protocol(fdecl::UseProtocol {
5378 dependency_type: Some(fdecl::DependencyType::Strong),
5379 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5380 source_name: Some(format!("{}", "a".repeat(256))),
5381 target_path: Some("/b".repeat(2048)),
5382 ..Default::default()
5383 }),
5384 fdecl::Use::Directory(fdecl::UseDirectory {
5385 dependency_type: Some(fdecl::DependencyType::Strong),
5386 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5387 source_name: Some(format!("{}", "a".repeat(256))),
5388 target_path: Some("/c".repeat(2048)),
5389 rights: Some(fio::Operations::CONNECT),
5390 subdir: None,
5391 ..Default::default()
5392 }),
5393 fdecl::Use::Storage(fdecl::UseStorage {
5394 source_name: Some("cache".to_string()),
5395 target_path: Some("/d".repeat(2048)),
5396 ..Default::default()
5397 }),
5398 ]);
5399 decl
5400 },
5401 result = Err(ErrorList::new(vec![
5402 Error::field_too_long(DeclType::UseService, "source_name"),
5403 Error::field_too_long(DeclType::UseService, "target_path"),
5404 Error::field_too_long(DeclType::UseProtocol, "source_name"),
5405 Error::field_too_long(DeclType::UseProtocol, "target_path"),
5406 Error::field_too_long(DeclType::UseDirectory, "source_name"),
5407 Error::field_too_long(DeclType::UseDirectory, "target_path"),
5408 Error::field_too_long(DeclType::UseStorage, "target_path"),
5409 ])),
5410 },
5411 test_validate_conflicting_paths => {
5412 input = {
5413 let mut decl = new_component_decl();
5414 decl.uses = Some(vec![
5415 fdecl::Use::Service(fdecl::UseService {
5416 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5417 source_name: Some("foo".to_string()),
5418 target_path: Some("/bar".to_string()),
5419 dependency_type: Some(fdecl::DependencyType::Strong),
5420 ..Default::default()
5421 }),
5422 fdecl::Use::Protocol(fdecl::UseProtocol {
5423 dependency_type: Some(fdecl::DependencyType::Strong),
5424 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5425 source_name: Some("space".to_string()),
5426 target_path: Some("/bar".to_string()),
5427 ..Default::default()
5428 }),
5429 fdecl::Use::Directory(fdecl::UseDirectory {
5430 dependency_type: Some(fdecl::DependencyType::Strong),
5431 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5432 source_name: Some("crow".to_string()),
5433 target_path: Some("/bar".to_string()),
5434 rights: Some(fio::Operations::CONNECT),
5435 subdir: None,
5436 ..Default::default()
5437 }),
5438 ]);
5439 decl
5440 },
5441 result = Err(ErrorList::new(vec![
5442 Error::duplicate_field(DeclType::UseProtocol, "target_path", "/bar"),
5443 Error::duplicate_field(DeclType::UseDirectory, "target_path", "/bar"),
5444 ])),
5445 },
5446 test_validate_exposes_empty => {
5448 input = {
5449 let mut decl = new_component_decl();
5450 decl.exposes = Some(vec![
5451 fdecl::Expose::Service(fdecl::ExposeService {
5452 source: None,
5453 source_name: None,
5454 target_name: None,
5455 target: None,
5456 ..Default::default()
5457 }),
5458 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5459 source: None,
5460 source_name: None,
5461 target_name: None,
5462 target: None,
5463 ..Default::default()
5464 }),
5465 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5466 source: None,
5467 source_name: None,
5468 target_name: None,
5469 target: None,
5470 rights: None,
5471 subdir: None,
5472 ..Default::default()
5473 }),
5474 fdecl::Expose::Runner(fdecl::ExposeRunner {
5475 source: None,
5476 source_name: None,
5477 target: None,
5478 target_name: None,
5479 ..Default::default()
5480 }),
5481 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5482 source: None,
5483 source_name: None,
5484 target: None,
5485 target_name: None,
5486 ..Default::default()
5487 }),
5488 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5489 ..Default::default()
5490 }),
5491 ]);
5492 decl
5493 },
5494 result = Err(ErrorList::new(vec![
5495 Error::missing_field(DeclType::ExposeService, "source"),
5496 Error::missing_field(DeclType::ExposeService, "target"),
5497 Error::missing_field(DeclType::ExposeService, "source_name"),
5498 Error::missing_field(DeclType::ExposeService, "target_name"),
5499 Error::missing_field(DeclType::ExposeProtocol, "source"),
5500 Error::missing_field(DeclType::ExposeProtocol, "target"),
5501 Error::missing_field(DeclType::ExposeProtocol, "source_name"),
5502 Error::missing_field(DeclType::ExposeProtocol, "target_name"),
5503 Error::missing_field(DeclType::ExposeDirectory, "source"),
5504 Error::missing_field(DeclType::ExposeDirectory, "target"),
5505 Error::missing_field(DeclType::ExposeDirectory, "source_name"),
5506 Error::missing_field(DeclType::ExposeDirectory, "target_name"),
5507 Error::missing_field(DeclType::ExposeRunner, "source"),
5508 Error::missing_field(DeclType::ExposeRunner, "target"),
5509 Error::missing_field(DeclType::ExposeRunner, "source_name"),
5510 Error::missing_field(DeclType::ExposeRunner, "target_name"),
5511 Error::missing_field(DeclType::ExposeResolver, "source"),
5512 Error::missing_field(DeclType::ExposeResolver, "target"),
5513 Error::missing_field(DeclType::ExposeResolver, "source_name"),
5514 Error::missing_field(DeclType::ExposeResolver, "target_name"),
5515 Error::missing_field(DeclType::ExposeDictionary, "source"),
5516 Error::missing_field(DeclType::ExposeDictionary, "target"),
5517 Error::missing_field(DeclType::ExposeDictionary, "source_name"),
5518 Error::missing_field(DeclType::ExposeDictionary, "target_name"),
5519 ])),
5520 },
5521 test_validate_exposes_extraneous => {
5522 input = {
5523 let mut decl = new_component_decl();
5524 decl.exposes = Some(vec![
5525 fdecl::Expose::Service(fdecl::ExposeService {
5526 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5527 name: "logger".to_string(),
5528 collection: Some("modular".to_string()),
5529 })),
5530 source_name: Some("logger".to_string()),
5531 target_name: Some("logger".to_string()),
5532 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5533 ..Default::default()
5534 }),
5535 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5536 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5537 name: "logger".to_string(),
5538 collection: Some("modular".to_string()),
5539 })),
5540 source_name: Some("legacy_logger".to_string()),
5541 target_name: Some("legacy_logger".to_string()),
5542 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5543 ..Default::default()
5544 }),
5545 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5546 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5547 name: "netstack".to_string(),
5548 collection: Some("modular".to_string()),
5549 })),
5550 source_name: Some("data".to_string()),
5551 target_name: Some("data".to_string()),
5552 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5553 rights: Some(fio::Operations::CONNECT),
5554 subdir: None,
5555 ..Default::default()
5556 }),
5557 fdecl::Expose::Runner(fdecl::ExposeRunner {
5558 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5559 name: "netstack".to_string(),
5560 collection: Some("modular".to_string()),
5561 })),
5562 source_name: Some("elf".to_string()),
5563 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5564 target_name: Some("elf".to_string()),
5565 ..Default::default()
5566 }),
5567 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5568 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5569 name: "netstack".to_string(),
5570 collection: Some("modular".to_string()),
5571 })),
5572 source_name: Some("pkg".to_string()),
5573 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5574 target_name: Some("pkg".to_string()),
5575 ..Default::default()
5576 }),
5577 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5578 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5579 name: "netstack".to_string(),
5580 collection: Some("modular".to_string()),
5581 })),
5582 source_name: Some("dict".to_string()),
5583 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5584 target_name: Some("dict".to_string()),
5585 ..Default::default()
5586 }),
5587 ]);
5588 decl
5589 },
5590 result = Err(ErrorList::new(vec![
5591 Error::extraneous_field(DeclType::ExposeService, "source.child.collection"),
5592 Error::extraneous_field(DeclType::ExposeProtocol, "source.child.collection"),
5593 Error::extraneous_field(DeclType::ExposeDirectory, "source.child.collection"),
5594 Error::extraneous_field(DeclType::ExposeRunner, "source.child.collection"),
5595 Error::extraneous_field(DeclType::ExposeResolver, "source.child.collection"),
5596 Error::extraneous_field(DeclType::ExposeDictionary, "source.child.collection"),
5597 ])),
5598 },
5599 test_validate_exposes_invalid_identifiers => {
5600 input = {
5601 let mut decl = new_component_decl();
5602 decl.exposes = Some(vec![
5603 fdecl::Expose::Service(fdecl::ExposeService {
5604 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5605 name: "^bad".to_string(),
5606 collection: None,
5607 })),
5608 source_name: Some("foo/".to_string()),
5609 target_name: Some("/".to_string()),
5610 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5611 ..Default::default()
5612 }),
5613 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5614 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5615 name: "^bad".to_string(),
5616 collection: None,
5617 })),
5618 source_name: Some("foo/".to_string()),
5619 target_name: Some("/".to_string()),
5620 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5621 ..Default::default()
5622 }),
5623 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5624 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5625 name: "^bad".to_string(),
5626 collection: None,
5627 })),
5628 source_name: Some("foo/".to_string()),
5629 target_name: Some("/".to_string()),
5630 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5631 rights: Some(fio::Operations::CONNECT),
5632 subdir: Some("/foo".to_string()),
5633 ..Default::default()
5634 }),
5635 fdecl::Expose::Runner(fdecl::ExposeRunner {
5636 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5637 name: "^bad".to_string(),
5638 collection: None,
5639 })),
5640 source_name: Some("/path".to_string()),
5641 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5642 target_name: Some("elf!".to_string()),
5643 ..Default::default()
5644 }),
5645 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5646 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5647 name: "^bad".to_string(),
5648 collection: None,
5649 })),
5650 source_name: Some("/path".to_string()),
5651 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5652 target_name: Some("pkg!".to_string()),
5653 ..Default::default()
5654 }),
5655 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5656 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5657 name: "^bad".to_string(),
5658 collection: None,
5659 })),
5660 source_name: Some("/path".to_string()),
5661 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5662 target_name: Some("pkg!".to_string()),
5663 ..Default::default()
5664 }),
5665 ]);
5666 decl
5667 },
5668 result = Err(ErrorList::new(vec![
5669 Error::invalid_field(DeclType::ExposeService, "source.child.name"),
5670 Error::invalid_field(DeclType::ExposeService, "source_name"),
5671 Error::invalid_field(DeclType::ExposeService, "target_name"),
5672 Error::invalid_field(DeclType::ExposeProtocol, "source.child.name"),
5673 Error::invalid_field(DeclType::ExposeProtocol, "source_name"),
5674 Error::invalid_field(DeclType::ExposeProtocol, "target_name"),
5675 Error::invalid_field(DeclType::ExposeDirectory, "source.child.name"),
5676 Error::invalid_field(DeclType::ExposeDirectory, "source_name"),
5677 Error::invalid_field(DeclType::ExposeDirectory, "target_name"),
5678 Error::invalid_field(DeclType::ExposeDirectory, "subdir"),
5679 Error::invalid_field(DeclType::ExposeRunner, "source.child.name"),
5680 Error::invalid_field(DeclType::ExposeRunner, "source_name"),
5681 Error::invalid_field(DeclType::ExposeRunner, "target_name"),
5682 Error::invalid_field(DeclType::ExposeResolver, "source.child.name"),
5683 Error::invalid_field(DeclType::ExposeResolver, "source_name"),
5684 Error::invalid_field(DeclType::ExposeResolver, "target_name"),
5685 Error::invalid_field(DeclType::ExposeDictionary, "source.child.name"),
5686 Error::invalid_field(DeclType::ExposeDictionary, "source_name"),
5687 Error::invalid_field(DeclType::ExposeDictionary, "target_name"),
5688 ])),
5689 },
5690 test_validate_exposes_invalid_source_target => {
5691 input = {
5692 let mut decl = new_component_decl();
5693 decl.children = Some(vec![fdecl::Child{
5694 name: Some("logger".to_string()),
5695 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5696 startup: Some(fdecl::StartupMode::Lazy),
5697 on_terminate: None,
5698 environment: None,
5699 ..Default::default()
5700 }]);
5701 decl.exposes = Some(vec![
5702 fdecl::Expose::Service(fdecl::ExposeService {
5703 source: None,
5704 source_name: Some("a".to_string()),
5705 target_name: Some("b".to_string()),
5706 target: None,
5707 ..Default::default()
5708 }),
5709 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5710 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5711 source_name: Some("c".to_string()),
5712 target_name: Some("d".to_string()),
5713 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5714 ..Default::default()
5715 }),
5716 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5717 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "z".to_string()})),
5718 source_name: Some("e".to_string()),
5719 target_name: Some("f".to_string()),
5720 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "z".to_string()})),
5721 rights: Some(fio::Operations::CONNECT),
5722 subdir: None,
5723 ..Default::default()
5724 }),
5725 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5726 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5727 source_name: Some("g".to_string()),
5728 target_name: Some("h".to_string()),
5729 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5730 rights: Some(fio::Operations::CONNECT),
5731 subdir: None,
5732 ..Default::default()
5733 }),
5734 fdecl::Expose::Runner(fdecl::ExposeRunner {
5735 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5736 source_name: Some("i".to_string()),
5737 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5738 target_name: Some("j".to_string()),
5739 ..Default::default()
5740 }),
5741 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5742 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5743 source_name: Some("k".to_string()),
5744 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5745 target_name: Some("l".to_string()),
5746 ..Default::default()
5747 }),
5748 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5749 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5750 name: "logger".to_string(),
5751 collection: None,
5752 })),
5753 source_name: Some("m".to_string()),
5754 target_name: Some("n".to_string()),
5755 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5756 ..Default::default()
5757 }),
5758 ]);
5759 decl
5760 },
5761 result = Err(ErrorList::new(vec![
5762 Error::missing_field(DeclType::ExposeService, "source"),
5763 Error::missing_field(DeclType::ExposeService, "target"),
5764 Error::invalid_field(DeclType::ExposeProtocol, "source"),
5765 Error::invalid_field(DeclType::ExposeProtocol, "target"),
5766 Error::invalid_field(DeclType::ExposeDirectory, "source"),
5767 Error::invalid_field(DeclType::ExposeDirectory, "target"),
5768 Error::invalid_field(DeclType::ExposeDirectory, "source"),
5769 Error::invalid_field(DeclType::ExposeDirectory, "target"),
5770 Error::invalid_field(DeclType::ExposeRunner, "source"),
5771 Error::invalid_field(DeclType::ExposeRunner, "target"),
5772 Error::invalid_field(DeclType::ExposeResolver, "source"),
5773 Error::invalid_field(DeclType::ExposeResolver, "target"),
5774 Error::invalid_field(DeclType::ExposeDictionary, "target"),
5775 ])),
5776 },
5777 test_validate_exposes_invalid_source_collection => {
5778 input = {
5779 let mut decl = new_component_decl();
5780 decl.collections = Some(vec![fdecl::Collection{
5781 name: Some("col".to_string()),
5782 durability: Some(fdecl::Durability::Transient),
5783 allowed_offers: None,
5784 allow_long_names: None,
5785 ..Default::default()
5786 }]);
5787 decl.exposes = Some(vec![
5788 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5789 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
5790 source_name: Some("a".to_string()),
5791 target_name: Some("a".to_string()),
5792 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5793 ..Default::default()
5794 }),
5795 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5796 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5797 source_name: Some("b".to_string()),
5798 target_name: Some("b".to_string()),
5799 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5800 rights: Some(fio::Operations::CONNECT),
5801 subdir: None,
5802 ..Default::default()
5803 }),
5804 fdecl::Expose::Runner(fdecl::ExposeRunner {
5805 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5806 source_name: Some("c".to_string()),
5807 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5808 target_name: Some("c".to_string()),
5809 ..Default::default()
5810 }),
5811 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5812 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5813 source_name: Some("d".to_string()),
5814 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5815 target_name: Some("d".to_string()),
5816 ..Default::default()
5817 }),
5818 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5819 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5820 source_name: Some("e".to_string()),
5821 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5822 target_name: Some("e".to_string()),
5823 ..Default::default()
5824 }),
5825 ]);
5826 decl
5827 },
5828 result = Err(ErrorList::new(vec![
5829 Error::invalid_field(DeclType::ExposeProtocol, "source"),
5830 Error::invalid_field(DeclType::ExposeDirectory, "source"),
5831 Error::invalid_field(DeclType::ExposeRunner, "source"),
5832 Error::invalid_field(DeclType::ExposeResolver, "source"),
5833 Error::invalid_field(DeclType::ExposeDictionary, "source"),
5834 ])),
5835 },
5836 test_validate_exposes_sources_collection => {
5837 input = {
5838 let mut decl = new_component_decl();
5839 decl.collections = Some(vec![
5840 fdecl::Collection {
5841 name: Some("col".to_string()),
5842 durability: Some(fdecl::Durability::Transient),
5843 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
5844 allow_long_names: None,
5845 ..Default::default()
5846 }
5847 ]);
5848 decl.exposes = Some(vec![
5849 fdecl::Expose::Service(fdecl::ExposeService {
5850 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
5851 source_name: Some("a".to_string()),
5852 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5853 target_name: Some("a".to_string()),
5854 ..Default::default()
5855 })
5856 ]);
5857 decl
5858 },
5859 result = Ok(()),
5860 },
5861 test_validate_exposes_long_identifiers => {
5862 input = {
5863 let mut decl = new_component_decl();
5864 decl.exposes = Some(vec![
5865 fdecl::Expose::Service(fdecl::ExposeService {
5866 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5867 name: "b".repeat(256),
5868 collection: None,
5869 })),
5870 source_name: Some(format!("{}", "a".repeat(1025))),
5871 target_name: Some(format!("{}", "b".repeat(1025))),
5872 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5873 ..Default::default()
5874 }),
5875 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5876 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5877 name: "b".repeat(256),
5878 collection: None,
5879 })),
5880 source_name: Some(format!("{}", "a".repeat(256))),
5881 target_name: Some(format!("{}", "b".repeat(256))),
5882 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5883 ..Default::default()
5884 }),
5885 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5886 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5887 name: "b".repeat(256),
5888 collection: None,
5889 })),
5890 source_name: Some(format!("{}", "a".repeat(256))),
5891 target_name: Some(format!("{}", "b".repeat(256))),
5892 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5893 rights: Some(fio::Operations::CONNECT),
5894 subdir: None,
5895 ..Default::default()
5896 }),
5897 fdecl::Expose::Runner(fdecl::ExposeRunner {
5898 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5899 name: "b".repeat(256),
5900 collection: None,
5901 })),
5902 source_name: Some("a".repeat(256)),
5903 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5904 target_name: Some("b".repeat(256)),
5905 ..Default::default()
5906 }),
5907 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5908 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5909 name: "b".repeat(256),
5910 collection: None,
5911 })),
5912 source_name: Some("a".repeat(256)),
5913 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5914 target_name: Some("b".repeat(256)),
5915 ..Default::default()
5916 }),
5917 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5918 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5919 name: "b".repeat(256),
5920 collection: None,
5921 })),
5922 source_name: Some("a".repeat(256)),
5923 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5924 target_name: Some("b".repeat(256)),
5925 ..Default::default()
5926 }),
5927 ]);
5928 decl
5929 },
5930 result = Err(ErrorList::new(vec![
5931 Error::field_too_long(DeclType::ExposeService, "source.child.name"),
5932 Error::field_too_long(DeclType::ExposeService, "source_name"),
5933 Error::field_too_long(DeclType::ExposeService, "target_name"),
5934 Error::field_too_long(DeclType::ExposeProtocol, "source.child.name"),
5935 Error::field_too_long(DeclType::ExposeProtocol, "source_name"),
5936 Error::field_too_long(DeclType::ExposeProtocol, "target_name"),
5937 Error::field_too_long(DeclType::ExposeDirectory, "source.child.name"),
5938 Error::field_too_long(DeclType::ExposeDirectory, "source_name"),
5939 Error::field_too_long(DeclType::ExposeDirectory, "target_name"),
5940 Error::field_too_long(DeclType::ExposeRunner, "source.child.name"),
5941 Error::field_too_long(DeclType::ExposeRunner, "source_name"),
5942 Error::field_too_long(DeclType::ExposeRunner, "target_name"),
5943 Error::field_too_long(DeclType::ExposeResolver, "source.child.name"),
5944 Error::field_too_long(DeclType::ExposeResolver, "source_name"),
5945 Error::field_too_long(DeclType::ExposeResolver, "target_name"),
5946 Error::field_too_long(DeclType::ExposeDictionary, "source.child.name"),
5947 Error::field_too_long(DeclType::ExposeDictionary, "source_name"),
5948 Error::field_too_long(DeclType::ExposeDictionary, "target_name"),
5949 ])),
5950 },
5951 test_validate_exposes_invalid_child => {
5952 input = {
5953 let mut decl = new_component_decl();
5954 decl.exposes = Some(vec![
5955 fdecl::Expose::Service(fdecl::ExposeService {
5956 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5957 name: "netstack".to_string(),
5958 collection: None,
5959 })),
5960 source_name: Some("fuchsia.logger.Log".to_string()),
5961 target_name: Some("fuchsia.logger.Log".to_string()),
5962 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5963 ..Default::default()
5964 }),
5965 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5966 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5967 name: "netstack".to_string(),
5968 collection: None,
5969 })),
5970 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
5971 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
5972 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5973 ..Default::default()
5974 }),
5975 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5976 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5977 name: "netstack".to_string(),
5978 collection: None,
5979 })),
5980 source_name: Some("data".to_string()),
5981 target_name: Some("data".to_string()),
5982 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5983 rights: Some(fio::Operations::CONNECT),
5984 subdir: None,
5985 ..Default::default()
5986 }),
5987 fdecl::Expose::Runner(fdecl::ExposeRunner {
5988 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5989 name: "netstack".to_string(),
5990 collection: None,
5991 })),
5992 source_name: Some("elf".to_string()),
5993 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5994 target_name: Some("elf".to_string()),
5995 ..Default::default()
5996 }),
5997 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5998 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5999 name: "netstack".to_string(),
6000 collection: None,
6001 })),
6002 source_name: Some("pkg".to_string()),
6003 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6004 target_name: Some("pkg".to_string()),
6005 ..Default::default()
6006 }),
6007 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
6008 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6009 name: "netstack".to_string(),
6010 collection: None,
6011 })),
6012 source_name: Some("dict".to_string()),
6013 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6014 target_name: Some("dict".to_string()),
6015 ..Default::default()
6016 }),
6017 ]);
6018 decl
6019 },
6020 result = Err(ErrorList::new(vec![
6021 Error::invalid_child(DeclType::ExposeService, "source", "netstack"),
6022 Error::invalid_child(DeclType::ExposeProtocol, "source", "netstack"),
6023 Error::invalid_child(DeclType::ExposeDirectory, "source", "netstack"),
6024 Error::invalid_child(DeclType::ExposeRunner, "source", "netstack"),
6025 Error::invalid_child(DeclType::ExposeResolver, "source", "netstack"),
6026 Error::invalid_child(DeclType::ExposeDictionary, "source", "netstack"),
6027 ])),
6028 },
6029 test_validate_exposes_invalid_source_capability => {
6030 input = {
6031 fdecl::Component {
6032 exposes: Some(vec![
6033 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6034 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
6035 name: "this-storage-doesnt-exist".to_string(),
6036 })),
6037 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
6038 target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
6039 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6040 ..Default::default()
6041 }),
6042 ]),
6043 ..new_component_decl()
6044 }
6045 },
6046 result = Err(ErrorList::new(vec![
6047 Error::invalid_capability(DeclType::ExposeProtocol, "source", "this-storage-doesnt-exist"),
6048 ])),
6049 },
6050 test_validate_exposes_duplicate_target => {
6051 input = {
6052 let mut decl = new_component_decl();
6053 decl.exposes = Some(vec![
6054 fdecl::Expose::Service(fdecl::ExposeService {
6055 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6056 name: "coll".into(),
6057 })),
6058 source_name: Some("netstack".to_string()),
6059 target_name: Some("fuchsia.net.Stack".to_string()),
6060 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6061 ..Default::default()
6062 }),
6063 fdecl::Expose::Service(fdecl::ExposeService {
6064 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6065 name: "coll2".into(),
6066 })),
6067 source_name: Some("netstack2".to_string()),
6068 target_name: Some("fuchsia.net.Stack".to_string()),
6069 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6070 ..Default::default()
6071 }),
6072 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6073 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6074 source_name: Some("fonts".to_string()),
6075 target_name: Some("fuchsia.fonts.Provider".to_string()),
6076 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6077 ..Default::default()
6078 }),
6079 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6080 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6081 source_name: Some("fonts2".to_string()),
6082 target_name: Some("fuchsia.fonts.Provider".to_string()),
6083 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6084 ..Default::default()
6085 }),
6086 fdecl::Expose::Directory(fdecl::ExposeDirectory {
6087 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6088 source_name: Some("assets".to_string()),
6089 target_name: Some("stuff".to_string()),
6090 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6091 rights: None,
6092 subdir: None,
6093 ..Default::default()
6094 }),
6095 fdecl::Expose::Directory(fdecl::ExposeDirectory {
6096 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6097 source_name: Some("assets2".to_string()),
6098 target_name: Some("stuff".to_string()),
6099 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6100 rights: None,
6101 subdir: None,
6102 ..Default::default()
6103 }),
6104 fdecl::Expose::Runner(fdecl::ExposeRunner {
6105 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6106 source_name: Some("source_elf".to_string()),
6107 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6108 target_name: Some("elf".to_string()),
6109 ..Default::default()
6110 }),
6111 fdecl::Expose::Runner(fdecl::ExposeRunner {
6112 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6113 source_name: Some("source_elf".to_string()),
6114 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6115 target_name: Some("elf".to_string()),
6116 ..Default::default()
6117 }),
6118 fdecl::Expose::Resolver(fdecl::ExposeResolver {
6119 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6120 source_name: Some("source_pkg".to_string()),
6121 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6122 target_name: Some("pkg".to_string()),
6123 ..Default::default()
6124 }),
6125 fdecl::Expose::Resolver(fdecl::ExposeResolver {
6126 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6127 source_name: Some("source_pkg".to_string()),
6128 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6129 target_name: Some("pkg".to_string()),
6130 ..Default::default()
6131 }),
6132 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
6133 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6134 source_name: Some("source_dict".to_string()),
6135 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6136 target_name: Some("dict".to_string()),
6137 ..Default::default()
6138 }),
6139 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
6140 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6141 source_name: Some("source_dict".to_string()),
6142 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6143 target_name: Some("dict".to_string()),
6144 ..Default::default()
6145 }),
6146 ]);
6147 decl.collections = Some(vec![
6148 fdecl::Collection {
6149 name: Some("coll".into()),
6150 durability: Some(fdecl::Durability::Transient),
6151 ..Default::default()
6152 },
6153 fdecl::Collection {
6154 name: Some("coll2".into()),
6155 durability: Some(fdecl::Durability::Transient),
6156 ..Default::default()
6157 },
6158 ]);
6159 decl.capabilities = Some(vec![
6160 fdecl::Capability::Service(fdecl::Service {
6161 name: Some("netstack".to_string()),
6162 source_path: Some("/path".to_string()),
6163 ..Default::default()
6164 }),
6165 fdecl::Capability::Service(fdecl::Service {
6166 name: Some("netstack2".to_string()),
6167 source_path: Some("/path".to_string()),
6168 ..Default::default()
6169 }),
6170 fdecl::Capability::Protocol(fdecl::Protocol {
6171 name: Some("fonts".to_string()),
6172 source_path: Some("/path".to_string()),
6173 ..Default::default()
6174 }),
6175 fdecl::Capability::Protocol(fdecl::Protocol {
6176 name: Some("fonts2".to_string()),
6177 source_path: Some("/path".to_string()),
6178 ..Default::default()
6179 }),
6180 fdecl::Capability::Directory(fdecl::Directory {
6181 name: Some("assets".to_string()),
6182 source_path: Some("/path".to_string()),
6183 rights: Some(fio::Operations::CONNECT),
6184 ..Default::default()
6185 }),
6186 fdecl::Capability::Directory(fdecl::Directory {
6187 name: Some("assets2".to_string()),
6188 source_path: Some("/path".to_string()),
6189 rights: Some(fio::Operations::CONNECT),
6190 ..Default::default()
6191 }),
6192 fdecl::Capability::Runner(fdecl::Runner {
6193 name: Some("source_elf".to_string()),
6194 source_path: Some("/path".to_string()),
6195 ..Default::default()
6196 }),
6197 fdecl::Capability::Resolver(fdecl::Resolver {
6198 name: Some("source_pkg".to_string()),
6199 source_path: Some("/path".to_string()),
6200 ..Default::default()
6201 }),
6202 fdecl::Capability::Dictionary(fdecl::Dictionary {
6203 name: Some("source_dict".to_string()),
6204 ..Default::default()
6205 }),
6206 ]);
6207 decl
6208 },
6209 result = Err(ErrorList::new(vec![
6210 Error::duplicate_field(DeclType::ExposeProtocol, "target_name",
6212 "fuchsia.fonts.Provider"),
6213 Error::duplicate_field(DeclType::ExposeDirectory, "target_name",
6214 "stuff"),
6215 Error::duplicate_field(DeclType::ExposeRunner, "target_name",
6216 "elf"),
6217 Error::duplicate_field(DeclType::ExposeResolver, "target_name", "pkg"),
6218 Error::duplicate_field(DeclType::ExposeDictionary, "target_name", "dict"),
6219 ])),
6220 },
6221 test_validate_exposes_invalid_capability_from_self => {
6222 input = {
6223 let mut decl = new_component_decl();
6224 decl.exposes = Some(vec![
6225 fdecl::Expose::Service(fdecl::ExposeService {
6226 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6227 source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
6228 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6229 target_name: Some("foo".to_string()),
6230 ..Default::default()
6231 }),
6232 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6233 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6234 source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
6235 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6236 target_name: Some("bar".to_string()),
6237 ..Default::default()
6238 }),
6239 fdecl::Expose::Directory(fdecl::ExposeDirectory {
6240 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6241 source_name: Some("dir".to_string()),
6242 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6243 target_name: Some("assets".to_string()),
6244 rights: None,
6245 subdir: None,
6246 ..Default::default()
6247 }),
6248 fdecl::Expose::Runner(fdecl::ExposeRunner {
6249 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6250 source_name: Some("source_elf".to_string()),
6251 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6252 target_name: Some("elf".to_string()),
6253 ..Default::default()
6254 }),
6255 fdecl::Expose::Resolver(fdecl::ExposeResolver {
6256 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6257 source_name: Some("source_pkg".to_string()),
6258 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6259 target_name: Some("pkg".to_string()),
6260 ..Default::default()
6261 }),
6262 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
6263 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6264 source_name: Some("source_dict".to_string()),
6265 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6266 target_name: Some("dict".to_string()),
6267 ..Default::default()
6268 }),
6269 fdecl::Expose::Config(fdecl::ExposeConfiguration {
6270 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6271 source_name: Some("source_config".to_string()),
6272 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6273 target_name: Some("config".to_string()),
6274 ..Default::default()
6275 }),
6276 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6277 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6278 source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
6279 source_dictionary: Some("dict/inner".to_string()),
6280 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6281 target_name: Some("baz".to_string()),
6282 ..Default::default()
6283 }),
6284 ]);
6285 decl
6286 },
6287 result = Err(ErrorList::new(vec![
6288 Error::invalid_capability(
6289 DeclType::ExposeService,
6290 "source",
6291 "fuchsia.some.library.SomeProtocol"),
6292 Error::invalid_capability(
6293 DeclType::ExposeProtocol,
6294 "source",
6295 "fuchsia.some.library.SomeProtocol"),
6296 Error::invalid_capability(DeclType::ExposeDirectory, "source", "dir"),
6297 Error::invalid_capability(DeclType::ExposeRunner, "source", "source_elf"),
6298 Error::invalid_capability(DeclType::ExposeResolver, "source", "source_pkg"),
6299 Error::invalid_capability(DeclType::ExposeDictionary, "source", "source_dict"),
6300 Error::invalid_capability(DeclType::ExposeConfig, "source", "source_config"),
6301 Error::invalid_capability(DeclType::ExposeProtocol, "source", "dict"),
6302 ])),
6303 },
6304
6305 test_validate_exposes_availability_service => {
6306 input = {
6307 let mut decl = generate_expose_different_source_and_availability_decl(
6308 |source, availability, target_name|
6309 fdecl::Expose::Service(fdecl::ExposeService {
6310 source: Some(source),
6311 source_name: Some("fuchsia.examples.Echo".to_string()),
6312 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6313 target_name: Some(target_name.to_string()),
6314 availability: Some(availability),
6315 ..Default::default()
6316 })
6317 );
6318 decl.capabilities = Some(vec![
6319 fdecl::Capability::Service(fdecl::Service {
6320 name: Some("fuchsia.examples.Echo".to_string()),
6321 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
6322 ..Default::default()
6323 }),
6324 ]);
6325 decl
6326 },
6327 result = {
6328 Err(ErrorList::new(vec![
6329 Error::availability_must_be_optional(
6330 DeclType::ExposeService,
6331 "availability",
6332 Some(&"fuchsia.examples.Echo".to_string()),
6333 ),
6334 Error::availability_must_be_optional(
6335 DeclType::ExposeService,
6336 "availability",
6337 Some(&"fuchsia.examples.Echo".to_string()),
6338 ),
6339 ]))
6340 },
6341 },
6342 test_validate_exposes_availability_protocol => {
6343 input = {
6344 let mut decl = generate_expose_different_source_and_availability_decl(
6345 |source, availability, target_name|
6346 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6347 source: Some(source),
6348 source_name: Some("fuchsia.examples.Echo".to_string()),
6349 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6350 target_name: Some(target_name.to_string()),
6351 availability: Some(availability),
6352 ..Default::default()
6353 })
6354 );
6355 decl.capabilities = Some(vec![
6356 fdecl::Capability::Protocol(fdecl::Protocol {
6357 name: Some("fuchsia.examples.Echo".to_string()),
6358 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
6359 ..Default::default()
6360 }),
6361 ]);
6362 decl
6363 },
6364 result = {
6365 Err(ErrorList::new(vec![
6366 Error::availability_must_be_optional(
6367 DeclType::ExposeProtocol,
6368 "availability",
6369 Some(&"fuchsia.examples.Echo".to_string()),
6370 ),
6371 Error::availability_must_be_optional(
6372 DeclType::ExposeProtocol,
6373 "availability",
6374 Some(&"fuchsia.examples.Echo".to_string()),
6375 ),
6376 ]))
6377 },
6378 },
6379 test_validate_exposes_availability_directory => {
6380 input = {
6381 let mut decl = generate_expose_different_source_and_availability_decl(
6382 |source, availability, target_name|
6383 fdecl::Expose::Directory(fdecl::ExposeDirectory {
6384 source: Some(source),
6385 source_name: Some("fuchsia.examples.Echo".to_string()),
6386 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6387 target_name: Some(target_name.to_string()),
6388 availability: Some(availability),
6389 ..Default::default()
6390 })
6391 );
6392 decl.capabilities = Some(vec![
6393 fdecl::Capability::Directory(fdecl::Directory {
6394 name: Some("fuchsia.examples.Echo".to_string()),
6395 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
6396 rights: Some(fio::Operations::READ_BYTES),
6397 ..Default::default()
6398 }),
6399 ]);
6400 decl
6401 },
6402 result = {
6403 Err(ErrorList::new(vec![
6404 Error::availability_must_be_optional(
6405 DeclType::ExposeDirectory,
6406 "availability",
6407 Some(&"fuchsia.examples.Echo".to_string()),
6408 ),
6409 Error::availability_must_be_optional(
6410 DeclType::ExposeDirectory,
6411 "availability",
6412 Some(&"fuchsia.examples.Echo".to_string()),
6413 ),
6414 ]))
6415 },
6416 },
6417
6418 test_validate_offers_empty => {
6420 input = {
6421 let mut decl = new_component_decl();
6422 decl.offers = Some(vec![
6423 fdecl::Offer::Service(fdecl::OfferService {
6424 source: None,
6425 source_name: None,
6426 target: None,
6427 target_name: None,
6428 ..Default::default()
6429 }),
6430 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6431 source: None,
6432 source_name: None,
6433 target: None,
6434 target_name: None,
6435 dependency_type: None,
6436 ..Default::default()
6437 }),
6438 fdecl::Offer::Directory(fdecl::OfferDirectory {
6439 source: None,
6440 source_name: None,
6441 target: None,
6442 target_name: None,
6443 rights: None,
6444 subdir: None,
6445 dependency_type: None,
6446 ..Default::default()
6447 }),
6448 fdecl::Offer::Storage(fdecl::OfferStorage {
6449 source_name: None,
6450 source: None,
6451 target: None,
6452 target_name: None,
6453 ..Default::default()
6454 }),
6455 fdecl::Offer::Runner(fdecl::OfferRunner {
6456 source: None,
6457 source_name: None,
6458 target: None,
6459 target_name: None,
6460 ..Default::default()
6461 }),
6462 fdecl::Offer::Resolver(fdecl::OfferResolver {
6463 ..Default::default()
6464 }),
6465 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6466 ..Default::default()
6467 }),
6468 ]);
6469 decl
6470 },
6471 result = Err(ErrorList::new(vec![
6474 Error::missing_field(DeclType::OfferService, "source"),
6475 Error::missing_field(DeclType::OfferService, "source_name"),
6476 Error::missing_field(DeclType::OfferService, "target"),
6477 Error::missing_field(DeclType::OfferService, "target_name"),
6478 Error::missing_field(DeclType::OfferProtocol, "source"),
6480 Error::missing_field(DeclType::OfferProtocol, "source_name"),
6481 Error::missing_field(DeclType::OfferProtocol, "target"),
6482 Error::missing_field(DeclType::OfferProtocol, "target_name"),
6483 Error::missing_field(DeclType::OfferProtocol, "dependency_type"),
6484 Error::missing_field(DeclType::OfferDirectory, "source"),
6486 Error::missing_field(DeclType::OfferDirectory, "source_name"),
6487 Error::missing_field(DeclType::OfferDirectory, "target"),
6488 Error::missing_field(DeclType::OfferDirectory, "target_name"),
6489 Error::missing_field(DeclType::OfferDirectory, "dependency_type"),
6490 Error::missing_field(DeclType::OfferStorage, "source"),
6492 Error::missing_field(DeclType::OfferStorage, "source_name"),
6493 Error::missing_field(DeclType::OfferStorage, "target"),
6494 Error::missing_field(DeclType::OfferStorage, "target_name"),
6495 Error::missing_field(DeclType::OfferRunner, "source"),
6497 Error::missing_field(DeclType::OfferRunner, "source_name"),
6498 Error::missing_field(DeclType::OfferRunner, "target"),
6499 Error::missing_field(DeclType::OfferRunner, "target_name"),
6500 Error::missing_field(DeclType::OfferResolver, "source"),
6502 Error::missing_field(DeclType::OfferResolver, "source_name"),
6503 Error::missing_field(DeclType::OfferResolver, "target"),
6504 Error::missing_field(DeclType::OfferResolver, "target_name"),
6505 Error::missing_field(DeclType::OfferDictionary, "source"),
6506 Error::missing_field(DeclType::OfferDictionary, "source_name"),
6507 Error::missing_field(DeclType::OfferDictionary, "target"),
6508 Error::missing_field(DeclType::OfferDictionary, "target_name"),
6509 Error::missing_field(DeclType::OfferDictionary, "dependency_type"),
6510 ])),
6511 },
6512 test_validate_offers_long_identifiers => {
6513 input = {
6514 let mut decl = new_component_decl();
6515 decl.offers = Some(vec![
6516 fdecl::Offer::Service(fdecl::OfferService {
6517 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6518 name: "a".repeat(256),
6519 collection: None,
6520 })),
6521 source_name: Some(format!("{}", "a".repeat(256))),
6522 target: Some(fdecl::Ref::Child(
6523 fdecl::ChildRef {
6524 name: "b".repeat(256),
6525 collection: None,
6526 }
6527 )),
6528 target_name: Some(format!("{}", "b".repeat(256))),
6529 ..Default::default()
6530 }),
6531 fdecl::Offer::Service(fdecl::OfferService {
6532 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6533 source_name: Some("a".to_string()),
6534 target: Some(fdecl::Ref::Collection(
6535 fdecl::CollectionRef {
6536 name: "b".repeat(256),
6537 }
6538 )),
6539 target_name: Some(format!("{}", "b".repeat(256))),
6540 ..Default::default()
6541 }),
6542 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6543 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6544 name: "a".repeat(256),
6545 collection: None,
6546 })),
6547 source_name: Some(format!("{}", "a".repeat(256))),
6548 target: Some(fdecl::Ref::Child(
6549 fdecl::ChildRef {
6550 name: "b".repeat(256),
6551 collection: None,
6552 }
6553 )),
6554 target_name: Some(format!("{}", "b".repeat(256))),
6555 dependency_type: Some(fdecl::DependencyType::Strong),
6556 ..Default::default()
6557 }),
6558 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6559 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6560 source_name: Some("a".to_string()),
6561 target: Some(fdecl::Ref::Collection(
6562 fdecl::CollectionRef {
6563 name: "b".repeat(256),
6564 }
6565 )),
6566 target_name: Some(format!("{}", "b".repeat(256))),
6567 dependency_type: Some(fdecl::DependencyType::Weak),
6568 ..Default::default()
6569 }),
6570 fdecl::Offer::Directory(fdecl::OfferDirectory {
6571 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6572 name: "a".repeat(256),
6573 collection: None,
6574 })),
6575 source_name: Some(format!("{}", "a".repeat(256))),
6576 target: Some(fdecl::Ref::Child(
6577 fdecl::ChildRef {
6578 name: "b".repeat(256),
6579 collection: None,
6580 }
6581 )),
6582 target_name: Some(format!("{}", "b".repeat(256))),
6583 rights: Some(fio::Operations::CONNECT),
6584 subdir: None,
6585 dependency_type: Some(fdecl::DependencyType::Strong),
6586 ..Default::default()
6587 }),
6588 fdecl::Offer::Directory(fdecl::OfferDirectory {
6589 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6590 source_name: Some("a".to_string()),
6591 target: Some(fdecl::Ref::Collection(
6592 fdecl::CollectionRef {
6593 name: "b".repeat(256),
6594 }
6595 )),
6596 target_name: Some(format!("{}", "b".repeat(256))),
6597 rights: Some(fio::Operations::CONNECT),
6598 subdir: None,
6599 dependency_type: Some(fdecl::DependencyType::Weak),
6600 ..Default::default()
6601 }),
6602 fdecl::Offer::Storage(fdecl::OfferStorage {
6603 source_name: Some("data".to_string()),
6604 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6605 target: Some(fdecl::Ref::Child(
6606 fdecl::ChildRef {
6607 name: "b".repeat(256),
6608 collection: None,
6609 }
6610 )),
6611 target_name: Some("data".to_string()),
6612 ..Default::default()
6613 }),
6614 fdecl::Offer::Storage(fdecl::OfferStorage {
6615 source_name: Some("data".to_string()),
6616 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6617 target: Some(fdecl::Ref::Collection(
6618 fdecl::CollectionRef { name: "b".repeat(256) }
6619 )),
6620 target_name: Some("data".to_string()),
6621 ..Default::default()
6622 }),
6623 fdecl::Offer::Runner(fdecl::OfferRunner {
6624 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6625 name: "a".repeat(256),
6626 collection: None,
6627 })),
6628 source_name: Some("b".repeat(256)),
6629 target: Some(fdecl::Ref::Collection(
6630 fdecl::CollectionRef {
6631 name: "c".repeat(256),
6632 }
6633 )),
6634 target_name: Some("d".repeat(256)),
6635 ..Default::default()
6636 }),
6637 fdecl::Offer::Resolver(fdecl::OfferResolver {
6638 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6639 name: "a".repeat(256),
6640 collection: None,
6641 })),
6642 source_name: Some("b".repeat(256)),
6643 target: Some(fdecl::Ref::Collection(
6644 fdecl::CollectionRef {
6645 name: "c".repeat(256),
6646 }
6647 )),
6648 target_name: Some("d".repeat(256)),
6649 ..Default::default()
6650 }),
6651 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6652 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6653 name: "a".repeat(256),
6654 collection: None,
6655 })),
6656 source_name: Some("b".repeat(256)),
6657 target: Some(fdecl::Ref::Collection(
6658 fdecl::CollectionRef {
6659 name: "c".repeat(256),
6660 }
6661 )),
6662 target_name: Some("d".repeat(256)),
6663 dependency_type: Some(fdecl::DependencyType::Strong),
6664 ..Default::default()
6665 }),
6666 ]);
6667 decl
6668 },
6669 result = Err(ErrorList::new(vec![
6670 Error::field_too_long(DeclType::OfferService, "source.child.name"),
6671 Error::field_too_long(DeclType::OfferService, "source_name"),
6672 Error::field_too_long(DeclType::OfferService, "target.child.name"),
6673 Error::field_too_long(DeclType::OfferService, "target_name"),
6674 Error::field_too_long(DeclType::OfferService, "target.collection.name"),
6675 Error::field_too_long(DeclType::OfferService, "target_name"),
6676 Error::field_too_long(DeclType::OfferProtocol, "source.child.name"),
6677 Error::field_too_long(DeclType::OfferProtocol, "source_name"),
6678 Error::field_too_long(DeclType::OfferProtocol, "target.child.name"),
6679 Error::field_too_long(DeclType::OfferProtocol, "target_name"),
6680 Error::field_too_long(DeclType::OfferProtocol, "target.collection.name"),
6681 Error::field_too_long(DeclType::OfferProtocol, "target_name"),
6682 Error::field_too_long(DeclType::OfferDirectory, "source.child.name"),
6683 Error::field_too_long(DeclType::OfferDirectory, "source_name"),
6684 Error::field_too_long(DeclType::OfferDirectory, "target.child.name"),
6685 Error::field_too_long(DeclType::OfferDirectory, "target_name"),
6686 Error::field_too_long(DeclType::OfferDirectory, "target.collection.name"),
6687 Error::field_too_long(DeclType::OfferDirectory, "target_name"),
6688 Error::field_too_long(DeclType::OfferStorage, "target.child.name"),
6689 Error::field_too_long(DeclType::OfferStorage, "target.collection.name"),
6690 Error::field_too_long(DeclType::OfferRunner, "source.child.name"),
6691 Error::field_too_long(DeclType::OfferRunner, "source_name"),
6692 Error::field_too_long(DeclType::OfferRunner, "target.collection.name"),
6693 Error::field_too_long(DeclType::OfferRunner, "target_name"),
6694 Error::field_too_long(DeclType::OfferResolver, "source.child.name"),
6695 Error::field_too_long(DeclType::OfferResolver, "source_name"),
6696 Error::field_too_long(DeclType::OfferResolver, "target.collection.name"),
6697 Error::field_too_long(DeclType::OfferResolver, "target_name"),
6698 Error::field_too_long(DeclType::OfferDictionary, "source.child.name"),
6699 Error::field_too_long(DeclType::OfferDictionary, "source_name"),
6700 Error::field_too_long(DeclType::OfferDictionary, "target.collection.name"),
6701 Error::field_too_long(DeclType::OfferDictionary, "target_name"),
6702 ])),
6703 },
6704 test_validate_offers_extraneous => {
6705 input = {
6706 let mut decl = new_component_decl();
6707 decl.offers = Some(vec![
6708 fdecl::Offer::Service(fdecl::OfferService {
6709 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6710 name: "logger".to_string(),
6711 collection: Some("modular".to_string()),
6712 })),
6713 source_name: Some("fuchsia.logger.Log".to_string()),
6714 target: Some(fdecl::Ref::Child(
6715 fdecl::ChildRef {
6716 name: "netstack".to_string(),
6717 collection: Some("modular".to_string()),
6718 }
6719 )),
6720 target_name: Some("fuchsia.logger.Log".to_string()),
6721 ..Default::default()
6722 }),
6723 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6724 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6725 name: "logger".to_string(),
6726 collection: Some("modular".to_string()),
6727 })),
6728 source_name: Some("fuchsia.logger.Log".to_string()),
6729 target: Some(fdecl::Ref::Child(
6730 fdecl::ChildRef {
6731 name: "netstack".to_string(),
6732 collection: Some("modular".to_string()),
6733 }
6734 )),
6735 target_name: Some("fuchsia.logger.Log".to_string()),
6736 dependency_type: Some(fdecl::DependencyType::Strong),
6737 ..Default::default()
6738 }),
6739 fdecl::Offer::Directory(fdecl::OfferDirectory {
6740 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6741 name: "logger".to_string(),
6742 collection: Some("modular".to_string()),
6743 })),
6744 source_name: Some("assets".to_string()),
6745 target: Some(fdecl::Ref::Child(
6746 fdecl::ChildRef {
6747 name: "netstack".to_string(),
6748 collection: Some("modular".to_string()),
6749 }
6750 )),
6751 target_name: Some("assets".to_string()),
6752 rights: Some(fio::Operations::CONNECT),
6753 subdir: None,
6754 dependency_type: Some(fdecl::DependencyType::Weak),
6755 ..Default::default()
6756 }),
6757 fdecl::Offer::Storage(fdecl::OfferStorage {
6758 source_name: Some("data".to_string()),
6759 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{ })),
6760 target: Some(fdecl::Ref::Child(
6761 fdecl::ChildRef {
6762 name: "netstack".to_string(),
6763 collection: Some("modular".to_string()),
6764 }
6765 )),
6766 target_name: Some("data".to_string()),
6767 ..Default::default()
6768 }),
6769 fdecl::Offer::Runner(fdecl::OfferRunner {
6770 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6771 name: "logger".to_string(),
6772 collection: Some("modular".to_string()),
6773 })),
6774 source_name: Some("elf".to_string()),
6775 target: Some(fdecl::Ref::Child(
6776 fdecl::ChildRef {
6777 name: "netstack".to_string(),
6778 collection: Some("modular".to_string()),
6779 }
6780 )),
6781 target_name: Some("elf".to_string()),
6782 ..Default::default()
6783 }),
6784 fdecl::Offer::Resolver(fdecl::OfferResolver {
6785 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6786 name: "logger".to_string(),
6787 collection: Some("modular".to_string()),
6788 })),
6789 source_name: Some("pkg".to_string()),
6790 target: Some(fdecl::Ref::Child(
6791 fdecl::ChildRef {
6792 name: "netstack".to_string(),
6793 collection: Some("modular".to_string()),
6794 }
6795 )),
6796 target_name: Some("pkg".to_string()),
6797 ..Default::default()
6798 }),
6799 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6800 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6801 name: "logger".to_string(),
6802 collection: Some("modular".to_string()),
6803 })),
6804 source_name: Some("dict".to_string()),
6805 target: Some(fdecl::Ref::Child(
6806 fdecl::ChildRef {
6807 name: "netstack".to_string(),
6808 collection: Some("modular".to_string()),
6809 }
6810 )),
6811 target_name: Some("dict".to_string()),
6812 dependency_type: Some(fdecl::DependencyType::Strong),
6813 ..Default::default()
6814 }),
6815 ]);
6816 decl.capabilities = Some(vec![
6817 fdecl::Capability::Protocol(fdecl::Protocol {
6818 name: Some("fuchsia.logger.Log".to_string()),
6819 source_path: Some("/svc/logger".to_string()),
6820 ..Default::default()
6821 }),
6822 fdecl::Capability::Directory(fdecl::Directory {
6823 name: Some("assets".to_string()),
6824 source_path: Some("/data/assets".to_string()),
6825 rights: Some(fio::Operations::CONNECT),
6826 ..Default::default()
6827 }),
6828 ]);
6829 decl
6830 },
6831 result = Err(ErrorList::new(vec![
6832 Error::extraneous_field(DeclType::OfferService, "source.child.collection"),
6833 Error::extraneous_field(DeclType::OfferService, "target.child.collection"),
6834 Error::extraneous_field(DeclType::OfferProtocol, "source.child.collection"),
6835 Error::extraneous_field(DeclType::OfferProtocol, "target.child.collection"),
6836 Error::extraneous_field(DeclType::OfferDirectory, "source.child.collection"),
6837 Error::extraneous_field(DeclType::OfferDirectory, "target.child.collection"),
6838 Error::extraneous_field(DeclType::OfferStorage, "target.child.collection"),
6839 Error::extraneous_field(DeclType::OfferRunner, "source.child.collection"),
6840 Error::extraneous_field(DeclType::OfferRunner, "target.child.collection"),
6841 Error::extraneous_field(DeclType::OfferResolver, "source.child.collection"),
6842 Error::extraneous_field(DeclType::OfferResolver, "target.child.collection"),
6843 Error::extraneous_field(DeclType::OfferDictionary, "source.child.collection"),
6844 Error::extraneous_field(DeclType::OfferDictionary, "target.child.collection"),
6845 ])),
6846 },
6847 test_validate_offers_invalid_filtered_service_fields => {
6848 input = {
6849 let mut decl = new_component_decl();
6850 decl.offers = Some(vec![
6851 fdecl::Offer::Service(fdecl::OfferService {
6852 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6853 source_name: Some("fuchsia.logger.Log".to_string()),
6854 target: Some(fdecl::Ref::Child(
6855 fdecl::ChildRef {
6856 name: "logger".to_string(),
6857 collection: None,
6858 }
6859 )),
6860 target_name: Some("fuchsia.logger.Log".to_string()),
6861 source_instance_filter: Some(vec![]),
6862 ..Default::default()
6863 }),
6864 fdecl::Offer::Service(fdecl::OfferService {
6865 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6866 source_name: Some("fuchsia.logger.Log".to_string()),
6867 target: Some(fdecl::Ref::Child(
6868 fdecl::ChildRef {
6869 name: "logger".to_string(),
6870 collection: None,
6871 }
6872 )),
6873 target_name: Some("fuchsia.logger.Log2".to_string()),
6874 source_instance_filter: Some(vec!["^badname".to_string()]),
6875 ..Default::default()
6876 }),
6877 fdecl::Offer::Service(fdecl::OfferService {
6878 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6879 source_name: Some("fuchsia.logger.Log".to_string()),
6880 target: Some(fdecl::Ref::Child(
6881 fdecl::ChildRef {
6882 name: "logger".to_string(),
6883 collection: None,
6884 }
6885 )),
6886 target_name: Some("fuchsia.logger.Log1".to_string()),
6887 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()}]),
6888 ..Default::default()
6889 }),
6890 fdecl::Offer::Service(fdecl::OfferService {
6891 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6892 source_name: Some("fuchsia.logger.Log".to_string()),
6893 target: Some(fdecl::Ref::Child(
6894 fdecl::ChildRef {
6895 name: "logger".to_string(),
6896 collection: None,
6897 }
6898 )),
6899 target_name: Some("fuchsia.logger.Log3".to_string()),
6900 renamed_instances: Some(vec![
6901 fdecl::NameMapping {
6902 source_name: "^badname".to_string(),
6903 target_name: "^badname".to_string(),
6904 }
6905 ]),
6906 ..Default::default()
6907 })
6908 ]);
6909 decl.children = Some(vec![
6910 fdecl::Child {
6911 name: Some("logger".to_string()),
6912 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
6913 startup: Some(fdecl::StartupMode::Lazy),
6914 on_terminate: None,
6915 environment: None,
6916 ..Default::default()
6917 },
6918 ]);
6919 decl
6920 },
6921 result = Err(ErrorList::new(vec![
6922 Error::invalid_field(DeclType::OfferService, "source_instance_filter"),
6923 Error::invalid_field(DeclType::OfferService, "source_instance_filter"),
6924 Error::invalid_field(DeclType::OfferService, "renamed_instances"),
6925 Error::invalid_field(DeclType::OfferService, "renamed_instances.source_name"),
6926 Error::invalid_field(DeclType::OfferService, "renamed_instances.target_name"),
6927 ])),
6928 },
6929 test_validate_offers_invalid_identifiers => {
6930 input = {
6931 let mut decl = new_component_decl();
6932 decl.offers = Some(vec![
6933 fdecl::Offer::Service(fdecl::OfferService {
6934 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6935 name: "^bad".to_string(),
6936 collection: None,
6937 })),
6938 source_name: Some("foo/".to_string()),
6939 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6940 name: "%bad".to_string(),
6941 collection: None,
6942 })),
6943 target_name: Some("/".to_string()),
6944 ..Default::default()
6945 }),
6946 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6947 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6948 name: "^bad".to_string(),
6949 collection: None,
6950 })),
6951 source_name: Some("foo/".to_string()),
6952 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6953 name: "%bad".to_string(),
6954 collection: None,
6955 })),
6956 target_name: Some("/".to_string()),
6957 dependency_type: Some(fdecl::DependencyType::Strong),
6958 ..Default::default()
6959 }),
6960 fdecl::Offer::Directory(fdecl::OfferDirectory {
6961 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6962 name: "^bad".to_string(),
6963 collection: None,
6964 })),
6965 source_name: Some("foo/".to_string()),
6966 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6967 name: "%bad".to_string(),
6968 collection: None,
6969 })),
6970 target_name: Some("/".to_string()),
6971 rights: Some(fio::Operations::CONNECT),
6972 subdir: Some("/foo".to_string()),
6973 dependency_type: Some(fdecl::DependencyType::Strong),
6974 ..Default::default()
6975 }),
6976 fdecl::Offer::Runner(fdecl::OfferRunner {
6977 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6978 name: "^bad".to_string(),
6979 collection: None,
6980 })),
6981 source_name: Some("/path".to_string()),
6982 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6983 name: "%bad".to_string(),
6984 collection: None,
6985 })),
6986 target_name: Some("elf!".to_string()),
6987 ..Default::default()
6988 }),
6989 fdecl::Offer::Resolver(fdecl::OfferResolver {
6990 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6991 name: "^bad".to_string(),
6992 collection: None,
6993 })),
6994 source_name: Some("/path".to_string()),
6995 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6996 name: "%bad".to_string(),
6997 collection: None,
6998 })),
6999 target_name: Some("pkg!".to_string()),
7000 ..Default::default()
7001 }),
7002 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7003 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7004 name: "^bad".to_string(),
7005 collection: None,
7006 })),
7007 source_name: Some("/path".to_string()),
7008 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7009 name: "%bad".to_string(),
7010 collection: None,
7011 })),
7012 target_name: Some("pkg!".to_string()),
7013 dependency_type: Some(fdecl::DependencyType::Strong),
7014 ..Default::default()
7015 }),
7016 ]);
7017 decl
7018 },
7019 result = Err(ErrorList::new(vec![
7020 Error::invalid_field(DeclType::OfferService, "source.child.name"),
7021 Error::invalid_field(DeclType::OfferService, "source_name"),
7022 Error::invalid_field(DeclType::OfferService, "target.child.name"),
7023 Error::invalid_field(DeclType::OfferService, "target_name"),
7024 Error::invalid_field(DeclType::OfferProtocol, "source.child.name"),
7025 Error::invalid_field(DeclType::OfferProtocol, "source_name"),
7026 Error::invalid_field(DeclType::OfferProtocol, "target.child.name"),
7027 Error::invalid_field(DeclType::OfferProtocol, "target_name"),
7028 Error::invalid_field(DeclType::OfferDirectory, "source.child.name"),
7029 Error::invalid_field(DeclType::OfferDirectory, "source_name"),
7030 Error::invalid_field(DeclType::OfferDirectory, "target.child.name"),
7031 Error::invalid_field(DeclType::OfferDirectory, "target_name"),
7032 Error::invalid_field(DeclType::OfferDirectory, "subdir"),
7033 Error::invalid_field(DeclType::OfferRunner, "source.child.name"),
7034 Error::invalid_field(DeclType::OfferRunner, "source_name"),
7035 Error::invalid_field(DeclType::OfferRunner, "target.child.name"),
7036 Error::invalid_field(DeclType::OfferRunner, "target_name"),
7037 Error::invalid_field(DeclType::OfferResolver, "source.child.name"),
7038 Error::invalid_field(DeclType::OfferResolver, "source_name"),
7039 Error::invalid_field(DeclType::OfferResolver, "target.child.name"),
7040 Error::invalid_field(DeclType::OfferResolver, "target_name"),
7041 Error::invalid_field(DeclType::OfferDictionary, "source.child.name"),
7042 Error::invalid_field(DeclType::OfferDictionary, "source_name"),
7043 Error::invalid_field(DeclType::OfferDictionary, "target.child.name"),
7044 Error::invalid_field(DeclType::OfferDictionary, "target_name"),
7045 ])),
7046 },
7047 test_validate_offers_target_equals_source => {
7048 input = {
7049 let mut decl = new_component_decl();
7050 decl.offers = Some(vec![
7051 fdecl::Offer::Service(fdecl::OfferService {
7052 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7053 name: "logger".to_string(),
7054 collection: None,
7055 })),
7056 source_name: Some("logger".to_string()),
7057 target: Some(fdecl::Ref::Child(
7058 fdecl::ChildRef {
7059 name: "logger".to_string(),
7060 collection: None,
7061 }
7062 )),
7063 target_name: Some("logger".to_string()),
7064 ..Default::default()
7065 }),
7066 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7067 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7068 name: "logger".to_string(),
7069 collection: None,
7070 })),
7071 source_name: Some("legacy_logger".to_string()),
7072 target: Some(fdecl::Ref::Child(
7073 fdecl::ChildRef {
7074 name: "logger".to_string(),
7075 collection: None,
7076 }
7077 )),
7078 target_name: Some("weak_legacy_logger".to_string()),
7079 dependency_type: Some(fdecl::DependencyType::Weak),
7080 ..Default::default()
7081 }),
7082 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7083 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7084 name: "logger".to_string(),
7085 collection: None,
7086 })),
7087 source_name: Some("legacy_logger".to_string()),
7088 target: Some(fdecl::Ref::Child(
7089 fdecl::ChildRef {
7090 name: "logger".to_string(),
7091 collection: None,
7092 }
7093 )),
7094 target_name: Some("strong_legacy_logger".to_string()),
7095 dependency_type: Some(fdecl::DependencyType::Strong),
7096 ..Default::default()
7097 }),
7098 fdecl::Offer::Directory(fdecl::OfferDirectory {
7099 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7100 name: "logger".to_string(),
7101 collection: None,
7102 })),
7103 source_name: Some("assets".to_string()),
7104 target: Some(fdecl::Ref::Child(
7105 fdecl::ChildRef {
7106 name: "logger".to_string(),
7107 collection: None,
7108 }
7109 )),
7110 target_name: Some("assets".to_string()),
7111 rights: Some(fio::Operations::CONNECT),
7112 subdir: None,
7113 dependency_type: Some(fdecl::DependencyType::Strong),
7114 ..Default::default()
7115 }),
7116 fdecl::Offer::Runner(fdecl::OfferRunner {
7117 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7118 name: "logger".to_string(),
7119 collection: None,
7120 })),
7121 source_name: Some("web".to_string()),
7122 target: Some(fdecl::Ref::Child(
7123 fdecl::ChildRef {
7124 name: "logger".to_string(),
7125 collection: None,
7126 }
7127 )),
7128 target_name: Some("web".to_string()),
7129 ..Default::default()
7130 }),
7131 fdecl::Offer::Resolver(fdecl::OfferResolver {
7132 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7133 name: "logger".to_string(),
7134 collection: None,
7135 })),
7136 source_name: Some("pkg".to_string()),
7137 target: Some(fdecl::Ref::Child(
7138 fdecl::ChildRef {
7139 name: "logger".to_string(),
7140 collection: None,
7141 }
7142 )),
7143 target_name: Some("pkg".to_string()),
7144 ..Default::default()
7145 }),
7146 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7147 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7148 name: "logger".to_string(),
7149 collection: None,
7150 })),
7151 source_name: Some("dict".to_string()),
7152 target: Some(fdecl::Ref::Child(
7153 fdecl::ChildRef {
7154 name: "logger".to_string(),
7155 collection: None,
7156 }
7157 )),
7158 target_name: Some("dict".to_string()),
7159 dependency_type: Some(fdecl::DependencyType::Strong),
7160 ..Default::default()
7161 }),
7162 ]);
7163 decl.children = Some(vec![fdecl::Child{
7164 name: Some("logger".to_string()),
7165 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
7166 startup: Some(fdecl::StartupMode::Lazy),
7167 on_terminate: None,
7168 environment: None,
7169 ..Default::default()
7170 }]);
7171 decl
7172 },
7173 result = Err(ErrorList::new(vec![
7174 Error::dependency_cycle("{{child logger -> child logger}}".to_string()),
7175 ])),
7176 },
7177 test_validate_offers_storage_target_equals_source => {
7178 input = fdecl::Component {
7179 offers: Some(vec![
7180 fdecl::Offer::Storage(fdecl::OfferStorage {
7181 source_name: Some("data".to_string()),
7182 source: Some(fdecl::Ref::Self_(fdecl::SelfRef { })),
7183 target: Some(fdecl::Ref::Child(
7184 fdecl::ChildRef {
7185 name: "logger".to_string(),
7186 collection: None,
7187 }
7188 )),
7189 target_name: Some("data".to_string()),
7190 ..Default::default()
7191 })
7192 ]),
7193 capabilities: Some(vec![
7194 fdecl::Capability::Storage(fdecl::Storage {
7195 name: Some("data".to_string()),
7196 backing_dir: Some("minfs".to_string()),
7197 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7198 name: "logger".to_string(),
7199 collection: None,
7200 })),
7201 subdir: None,
7202 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
7203 ..Default::default()
7204 }),
7205 ]),
7206 children: Some(vec![
7207 fdecl::Child {
7208 name: Some("logger".to_string()),
7209 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
7210 startup: Some(fdecl::StartupMode::Lazy),
7211 on_terminate: None,
7212 environment: None,
7213 ..Default::default()
7214 },
7215 ]),
7216 ..new_component_decl()
7217 },
7218 result = Err(ErrorList::new(vec![
7219 Error::dependency_cycle("{{child logger -> capability data -> child logger}}".to_string()),
7220 ])),
7221 },
7222 test_validate_offers_invalid_child => {
7223 input = {
7224 let mut decl = new_component_decl();
7225 decl.offers = Some(vec![
7226 fdecl::Offer::Service(fdecl::OfferService {
7227 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7228 name: "logger".to_string(),
7229 collection: None,
7230 })),
7231 source_name: Some("fuchsia.logger.Log".to_string()),
7232 target: Some(fdecl::Ref::Child(
7233 fdecl::ChildRef {
7234 name: "netstack".to_string(),
7235 collection: None,
7236 }
7237 )),
7238 target_name: Some("fuchsia.logger.Log".to_string()),
7239 ..Default::default()
7240 }),
7241 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7242 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7243 name: "logger".to_string(),
7244 collection: None,
7245 })),
7246 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
7247 target: Some(fdecl::Ref::Child(
7248 fdecl::ChildRef {
7249 name: "netstack".to_string(),
7250 collection: None,
7251 }
7252 )),
7253 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7254 dependency_type: Some(fdecl::DependencyType::Strong),
7255 ..Default::default()
7256 }),
7257 fdecl::Offer::Directory(fdecl::OfferDirectory {
7258 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7259 name: "logger".to_string(),
7260 collection: None,
7261 })),
7262 source_name: Some("assets".to_string()),
7263 target: Some(fdecl::Ref::Collection(
7264 fdecl::CollectionRef { name: "modular".to_string() }
7265 )),
7266 target_name: Some("assets".to_string()),
7267 rights: Some(fio::Operations::CONNECT),
7268 subdir: None,
7269 dependency_type: Some(fdecl::DependencyType::Weak),
7270 ..Default::default()
7271 }),
7272 ]);
7273 decl.capabilities = Some(vec![
7274 fdecl::Capability::Storage(fdecl::Storage {
7275 name: Some("memfs".to_string()),
7276 backing_dir: Some("memfs".to_string()),
7277 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7278 name: "logger".to_string(),
7279 collection: None,
7280 })),
7281 subdir: None,
7282 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
7283 ..Default::default()
7284 }),
7285 ]);
7286 decl.children = Some(vec![
7287 fdecl::Child {
7288 name: Some("netstack".to_string()),
7289 url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
7290 startup: Some(fdecl::StartupMode::Lazy),
7291 on_terminate: None,
7292 environment: None,
7293 ..Default::default()
7294 },
7295 ]);
7296 decl.collections = Some(vec![
7297 fdecl::Collection {
7298 name: Some("modular".to_string()),
7299 durability: Some(fdecl::Durability::Transient),
7300 environment: None,
7301 allowed_offers: Some(fdecl::AllowedOffers::StaticAndDynamic),
7302 allow_long_names: None,
7303 ..Default::default()
7304 },
7305 ]);
7306 decl
7307 },
7308 result = Err(ErrorList::new(vec![
7309 Error::invalid_child(DeclType::Storage, "source", "logger"),
7310 Error::invalid_child(DeclType::OfferService, "source", "logger"),
7311 Error::invalid_child(DeclType::OfferProtocol, "source", "logger"),
7312 Error::invalid_child(DeclType::OfferDirectory, "source", "logger"),
7313 ])),
7314 },
7315 test_validate_offers_invalid_source_capability => {
7316 input = {
7317 fdecl::Component {
7318 offers: Some(vec![
7319 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7320 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
7321 name: "this-storage-doesnt-exist".to_string(),
7322 })),
7323 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
7324 target: Some(fdecl::Ref::Child(
7325 fdecl::ChildRef {
7326 name: "netstack".to_string(),
7327 collection: None,
7328 }
7329 )),
7330 target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
7331 dependency_type: Some(fdecl::DependencyType::Strong),
7332 ..Default::default()
7333 }),
7334 ]),
7335 ..new_component_decl()
7336 }
7337 },
7338 result = Err(ErrorList::new(vec![
7339 Error::invalid_capability(DeclType::OfferProtocol, "source", "this-storage-doesnt-exist"),
7340 Error::invalid_child(DeclType::OfferProtocol, "target", "netstack"),
7341 ])),
7342 },
7343 test_validate_offers_target => {
7344 input = {
7345 let mut decl = new_component_decl();
7346 decl.offers = Some(vec![
7347 fdecl::Offer::Service(fdecl::OfferService {
7348 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
7349 name: "modular".into()
7350 })),
7351 source_name: Some("logger".to_string()),
7352 target: Some(fdecl::Ref::Child(
7353 fdecl::ChildRef {
7354 name: "netstack".to_string(),
7355 collection: None,
7356 }
7357 )),
7358 target_name: Some("fuchsia.logger.Log".to_string()),
7359 ..Default::default()
7360 }),
7361 fdecl::Offer::Service(fdecl::OfferService {
7362 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
7363 name: "modular".into()
7364 })),
7365 source_name: Some("logger".to_string()),
7366 target: Some(fdecl::Ref::Child(
7367 fdecl::ChildRef {
7368 name: "netstack".to_string(),
7369 collection: None,
7370 }
7371 )),
7372 target_name: Some("fuchsia.logger.Log".to_string()),
7373 ..Default::default()
7374 }),
7375 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7376 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7377 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
7378 target: Some(fdecl::Ref::Child(
7379 fdecl::ChildRef {
7380 name: "netstack".to_string(),
7381 collection: None,
7382 }
7383 )),
7384 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7385 dependency_type: Some(fdecl::DependencyType::Strong),
7386 ..Default::default()
7387 }),
7388 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7389 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7390 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
7391 target: Some(fdecl::Ref::Child(
7392 fdecl::ChildRef {
7393 name: "netstack".to_string(),
7394 collection: None,
7395 }
7396 )),
7397 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7398 dependency_type: Some(fdecl::DependencyType::Strong),
7399 ..Default::default()
7400 }),
7401 fdecl::Offer::Directory(fdecl::OfferDirectory {
7402 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7403 source_name: Some("assets".to_string()),
7404 target: Some(fdecl::Ref::Collection(
7405 fdecl::CollectionRef { name: "modular".to_string() }
7406 )),
7407 target_name: Some("assets".to_string()),
7408 rights: Some(fio::Operations::CONNECT),
7409 subdir: None,
7410 dependency_type: Some(fdecl::DependencyType::Strong),
7411 ..Default::default()
7412 }),
7413 fdecl::Offer::Directory(fdecl::OfferDirectory {
7414 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7415 source_name: Some("assets".to_string()),
7416 target: Some(fdecl::Ref::Collection(
7417 fdecl::CollectionRef { name: "modular".to_string() }
7418 )),
7419 target_name: Some("assets".to_string()),
7420 rights: Some(fio::Operations::CONNECT),
7421 subdir: None,
7422 dependency_type: Some(fdecl::DependencyType::Weak),
7423 ..Default::default()
7424 }),
7425 fdecl::Offer::Storage(fdecl::OfferStorage {
7426 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7427 source_name: Some("data".to_string()),
7428 target: Some(fdecl::Ref::Collection(
7429 fdecl::CollectionRef { name: "modular".to_string() }
7430 )),
7431 target_name: Some("data".to_string()),
7432 ..Default::default()
7433 }),
7434 fdecl::Offer::Storage(fdecl::OfferStorage {
7435 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7436 source_name: Some("data".to_string()),
7437 target: Some(fdecl::Ref::Collection(
7438 fdecl::CollectionRef { name: "modular".to_string() }
7439 )),
7440 target_name: Some("data".to_string()),
7441 ..Default::default()
7442 }),
7443 fdecl::Offer::Runner(fdecl::OfferRunner {
7444 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7445 source_name: Some("elf".to_string()),
7446 target: Some(fdecl::Ref::Collection(
7447 fdecl::CollectionRef { name: "modular".to_string() }
7448 )),
7449 target_name: Some("duplicated".to_string()),
7450 ..Default::default()
7451 }),
7452 fdecl::Offer::Runner(fdecl::OfferRunner {
7453 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7454 source_name: Some("elf".to_string()),
7455 target: Some(fdecl::Ref::Collection(
7456 fdecl::CollectionRef { name: "modular".to_string() }
7457 )),
7458 target_name: Some("duplicated".to_string()),
7459 ..Default::default()
7460 }),
7461 fdecl::Offer::Resolver(fdecl::OfferResolver {
7462 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7463 source_name: Some("pkg".to_string()),
7464 target: Some(fdecl::Ref::Collection(
7465 fdecl::CollectionRef { name: "modular".to_string() }
7466 )),
7467 target_name: Some("duplicated".to_string()),
7468 ..Default::default()
7469 }),
7470 fdecl::Offer::EventStream(fdecl::OfferEventStream {
7471 source_name: Some("started".to_string()),
7472 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7473 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "netstack".to_string(), collection: None})),
7474 target_name: Some("started".to_string()),
7475 ..Default::default()
7476 }),
7477 fdecl::Offer::EventStream(fdecl::OfferEventStream {
7478 source_name: Some("started".to_string()),
7479 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7480 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "netstack".to_string(), collection: None})),
7481 target_name: Some("started".to_string()),
7482 ..Default::default()
7483 }),
7484 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7485 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7486 source_name: Some("a".to_string()),
7487 target: Some(fdecl::Ref::Collection(
7488 fdecl::CollectionRef { name: "modular".to_string() }
7489 )),
7490 target_name: Some("dict".to_string()),
7491 dependency_type: Some(fdecl::DependencyType::Strong),
7492 ..Default::default()
7493 }),
7494 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7495 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7496 source_name: Some("b".to_string()),
7497 target: Some(fdecl::Ref::Collection(
7498 fdecl::CollectionRef { name: "modular".to_string() }
7499 )),
7500 target_name: Some("dict".to_string()),
7501 dependency_type: Some(fdecl::DependencyType::Strong),
7502 ..Default::default()
7503 }),
7504 ]);
7505 decl.children = Some(vec![
7506 fdecl::Child{
7507 name: Some("netstack".to_string()),
7508 url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
7509 startup: Some(fdecl::StartupMode::Eager),
7510 on_terminate: None,
7511 environment: None,
7512 ..Default::default()
7513 },
7514 ]);
7515 decl.collections = Some(vec![
7516 fdecl::Collection{
7517 name: Some("modular".to_string()),
7518 durability: Some(fdecl::Durability::Transient),
7519 environment: None,
7520 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7521 allow_long_names: None,
7522 ..Default::default()
7523 },
7524 ]);
7525 decl
7526 },
7527 result = Err(ErrorList::new(vec![
7528 Error::duplicate_field(DeclType::OfferProtocol, "target_name", "fuchsia.logger.LegacyLog"),
7530 Error::duplicate_field(DeclType::OfferDirectory, "target_name", "assets"),
7531 Error::duplicate_field(DeclType::OfferStorage, "target_name", "data"),
7532 Error::duplicate_field(DeclType::OfferRunner, "target_name", "duplicated"),
7533 Error::duplicate_field(DeclType::OfferResolver, "target_name", "duplicated"),
7534 Error::duplicate_field(DeclType::OfferEventStream, "target_name", "started"),
7535 Error::duplicate_field(DeclType::OfferDictionary, "target_name", "dict"),
7536 ])),
7537 },
7538 test_validate_offers_target_invalid => {
7539 input = {
7540 let mut decl = new_component_decl();
7541 decl.offers = Some(vec![
7542 fdecl::Offer::Service(fdecl::OfferService {
7543 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7544 source_name: Some("logger".to_string()),
7545 target: Some(fdecl::Ref::Child(
7546 fdecl::ChildRef {
7547 name: "netstack".to_string(),
7548 collection: None,
7549 }
7550 )),
7551 target_name: Some("fuchsia.logger.Log".to_string()),
7552 ..Default::default()
7553 }),
7554 fdecl::Offer::Service(fdecl::OfferService {
7555 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7556 source_name: Some("logger".to_string()),
7557 target: Some(fdecl::Ref::Collection(
7558 fdecl::CollectionRef { name: "modular".to_string(), }
7559 )),
7560 target_name: Some("fuchsia.logger.Log".to_string()),
7561 ..Default::default()
7562 }),
7563 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7564 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7565 source_name: Some("legacy_logger".to_string()),
7566 target: Some(fdecl::Ref::Child(
7567 fdecl::ChildRef {
7568 name: "netstack".to_string(),
7569 collection: None,
7570 }
7571 )),
7572 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7573 dependency_type: Some(fdecl::DependencyType::Weak),
7574 ..Default::default()
7575 }),
7576 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7577 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7578 source_name: Some("legacy_logger".to_string()),
7579 target: Some(fdecl::Ref::Collection(
7580 fdecl::CollectionRef { name: "modular".to_string(), }
7581 )),
7582 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7583 dependency_type: Some(fdecl::DependencyType::Strong),
7584 ..Default::default()
7585 }),
7586 fdecl::Offer::Directory(fdecl::OfferDirectory {
7587 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7588 source_name: Some("assets".to_string()),
7589 target: Some(fdecl::Ref::Child(
7590 fdecl::ChildRef {
7591 name: "netstack".to_string(),
7592 collection: None,
7593 }
7594 )),
7595 target_name: Some("data".to_string()),
7596 rights: Some(fio::Operations::CONNECT),
7597 subdir: None,
7598 dependency_type: Some(fdecl::DependencyType::Strong),
7599 ..Default::default()
7600 }),
7601 fdecl::Offer::Directory(fdecl::OfferDirectory {
7602 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7603 source_name: Some("assets".to_string()),
7604 target: Some(fdecl::Ref::Collection(
7605 fdecl::CollectionRef { name: "modular".to_string(), }
7606 )),
7607 target_name: Some("data".to_string()),
7608 rights: Some(fio::Operations::CONNECT),
7609 subdir: None,
7610 dependency_type: Some(fdecl::DependencyType::Weak),
7611 ..Default::default()
7612 }),
7613 fdecl::Offer::Storage(fdecl::OfferStorage {
7614 source_name: Some("data".to_string()),
7615 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7616 target: Some(fdecl::Ref::Child(
7617 fdecl::ChildRef {
7618 name: "netstack".to_string(),
7619 collection: None,
7620 }
7621 )),
7622 target_name: Some("data".to_string()),
7623 ..Default::default()
7624 }),
7625 fdecl::Offer::Storage(fdecl::OfferStorage {
7626 source_name: Some("data".to_string()),
7627 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7628 target: Some(fdecl::Ref::Collection(
7629 fdecl::CollectionRef { name: "modular".to_string(), }
7630 )),
7631 target_name: Some("data".to_string()),
7632 ..Default::default()
7633 }),
7634 fdecl::Offer::Runner(fdecl::OfferRunner {
7635 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7636 source_name: Some("elf".to_string()),
7637 target: Some(fdecl::Ref::Child(
7638 fdecl::ChildRef {
7639 name: "netstack".to_string(),
7640 collection: None,
7641 }
7642 )),
7643 target_name: Some("elf".to_string()),
7644 ..Default::default()
7645 }),
7646 fdecl::Offer::Runner(fdecl::OfferRunner {
7647 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7648 source_name: Some("elf".to_string()),
7649 target: Some(fdecl::Ref::Collection(
7650 fdecl::CollectionRef { name: "modular".to_string(), }
7651 )),
7652 target_name: Some("elf".to_string()),
7653 ..Default::default()
7654 }),
7655 fdecl::Offer::Resolver(fdecl::OfferResolver {
7656 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7657 source_name: Some("pkg".to_string()),
7658 target: Some(fdecl::Ref::Child(
7659 fdecl::ChildRef {
7660 name: "netstack".to_string(),
7661 collection: None,
7662 }
7663 )),
7664 target_name: Some("pkg".to_string()),
7665 ..Default::default()
7666 }),
7667 fdecl::Offer::Resolver(fdecl::OfferResolver {
7668 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7669 source_name: Some("pkg".to_string()),
7670 target: Some(fdecl::Ref::Collection(
7671 fdecl::CollectionRef { name: "modular".to_string(), }
7672 )),
7673 target_name: Some("pkg".to_string()),
7674 ..Default::default()
7675 }),
7676 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7677 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7678 source_name: Some("pkg".to_string()),
7679 target: Some(fdecl::Ref::Child(
7680 fdecl::ChildRef {
7681 name: "netstack".to_string(),
7682 collection: None,
7683 }
7684 )),
7685 target_name: Some("pkg".to_string()),
7686 dependency_type: Some(fdecl::DependencyType::Strong),
7687 ..Default::default()
7688 }),
7689 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7690 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7691 source_name: Some("pkg".to_string()),
7692 target: Some(fdecl::Ref::Collection(
7693 fdecl::CollectionRef { name: "modular".to_string(), }
7694 )),
7695 target_name: Some("pkg".to_string()),
7696 dependency_type: Some(fdecl::DependencyType::Strong),
7697 ..Default::default()
7698 }),
7699 ]);
7700 decl
7701 },
7702 result = Err(ErrorList::new(vec![
7703 Error::invalid_child(DeclType::OfferService, "target", "netstack"),
7704 Error::invalid_collection(DeclType::OfferService, "target", "modular"),
7705 Error::invalid_child(DeclType::OfferProtocol, "target", "netstack"),
7706 Error::invalid_collection(DeclType::OfferProtocol, "target", "modular"),
7707 Error::invalid_child(DeclType::OfferDirectory, "target", "netstack"),
7708 Error::invalid_collection(DeclType::OfferDirectory, "target", "modular"),
7709 Error::invalid_child(DeclType::OfferStorage, "target", "netstack"),
7710 Error::invalid_collection(DeclType::OfferStorage, "target", "modular"),
7711 Error::invalid_child(DeclType::OfferRunner, "target", "netstack"),
7712 Error::invalid_collection(DeclType::OfferRunner, "target", "modular"),
7713 Error::invalid_child(DeclType::OfferResolver, "target", "netstack"),
7714 Error::invalid_collection(DeclType::OfferResolver, "target", "modular"),
7715 Error::invalid_child(DeclType::OfferDictionary, "target", "netstack"),
7716 Error::invalid_collection(DeclType::OfferDictionary, "target", "modular"),
7717 ])),
7718 },
7719 test_validate_offers_target_dictionary => {
7720 input = fdecl::Component {
7721 offers: Some(vec![
7722 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7724 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7725 source_name: Some("p".to_string()),
7726 target: Some(fdecl::Ref::Capability(
7727 fdecl::CapabilityRef {
7728 name: "dict".into(),
7729 },
7730 )),
7731 target_name: Some("p".into()),
7732 dependency_type: Some(fdecl::DependencyType::Strong),
7733 ..Default::default()
7734 }),
7735 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7737 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7738 source_name: Some("p".to_string()),
7739 target: Some(fdecl::Ref::Capability(
7740 fdecl::CapabilityRef {
7741 name: "dynamic".into(),
7742 },
7743 )),
7744 target_name: Some("p".into()),
7745 dependency_type: Some(fdecl::DependencyType::Strong),
7746 ..Default::default()
7747 }),
7748 ]),
7749 capabilities: Some(vec![
7750 fdecl::Capability::Dictionary(fdecl::Dictionary {
7751 name: Some("dict".into()),
7752 ..Default::default()
7753 }),
7754 fdecl::Capability::Dictionary(fdecl::Dictionary {
7755 name: Some("dynamic".into()),
7756 source_path: Some("/out/dir".into()),
7757 ..Default::default()
7758 }),
7759 ]),
7760 ..Default::default()
7761 },
7762 result = Err(ErrorList::new(vec![
7763 Error::invalid_field(DeclType::OfferProtocol, "target"),
7764 ])),
7765 },
7766 test_validate_offers_invalid_source_collection => {
7767 input = {
7768 let mut decl = new_component_decl();
7769 decl.collections = Some(vec![
7770 fdecl::Collection {
7771 name: Some("col".to_string()),
7772 durability: Some(fdecl::Durability::Transient),
7773 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7774 allow_long_names: None,
7775 ..Default::default()
7776 }
7777 ]);
7778 decl.children = Some(vec![
7779 fdecl::Child {
7780 name: Some("child".to_string()),
7781 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7782 startup: Some(fdecl::StartupMode::Lazy),
7783 on_terminate: None,
7784 ..Default::default()
7785 }
7786 ]);
7787 decl.offers = Some(vec![
7788 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7789 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7790 source_name: Some("a".to_string()),
7791 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7792 target_name: Some("a".to_string()),
7793 dependency_type: Some(fdecl::DependencyType::Strong),
7794 ..Default::default()
7795 }),
7796 fdecl::Offer::Directory(fdecl::OfferDirectory {
7797 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7798 source_name: Some("b".to_string()),
7799 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7800 target_name: Some("b".to_string()),
7801 rights: Some(fio::Operations::CONNECT),
7802 subdir: None,
7803 dependency_type: Some(fdecl::DependencyType::Strong),
7804 ..Default::default()
7805 }),
7806 fdecl::Offer::Storage(fdecl::OfferStorage {
7807 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7808 source_name: Some("c".to_string()),
7809 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7810 target_name: Some("c".to_string()),
7811 ..Default::default()
7812 }),
7813 fdecl::Offer::Runner(fdecl::OfferRunner {
7814 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7815 source_name: Some("d".to_string()),
7816 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7817 target_name: Some("d".to_string()),
7818 ..Default::default()
7819 }),
7820 fdecl::Offer::Resolver(fdecl::OfferResolver {
7821 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7822 source_name: Some("e".to_string()),
7823 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7824 target_name: Some("e".to_string()),
7825 ..Default::default()
7826 }),
7827 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7828 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7829 source_name: Some("f".to_string()),
7830 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7831 target_name: Some("f".to_string()),
7832 dependency_type: Some(fdecl::DependencyType::Strong),
7833 ..Default::default()
7834 }),
7835 ]);
7836 decl
7837 },
7838 result = Err(ErrorList::new(vec![
7839 Error::invalid_field(DeclType::OfferProtocol, "source"),
7840 Error::invalid_field(DeclType::OfferDirectory, "source"),
7841 Error::invalid_field(DeclType::OfferStorage, "source"),
7842 Error::invalid_field(DeclType::OfferRunner, "source"),
7843 Error::invalid_field(DeclType::OfferResolver, "source"),
7844 Error::invalid_field(DeclType::OfferDictionary, "source"),
7845 ])),
7846 },
7847 test_validate_offers_source_collection => {
7848 input = {
7849 let mut decl = new_component_decl();
7850 decl.collections = Some(vec![
7851 fdecl::Collection {
7852 name: Some("col".to_string()),
7853 durability: Some(fdecl::Durability::Transient),
7854 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7855 allow_long_names: None,
7856 ..Default::default()
7857 }
7858 ]);
7859 decl.children = Some(vec![
7860 fdecl::Child {
7861 name: Some("child".to_string()),
7862 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7863 startup: Some(fdecl::StartupMode::Lazy),
7864 on_terminate: None,
7865 ..Default::default()
7866 }
7867 ]);
7868 decl.offers = Some(vec![
7869 fdecl::Offer::Service(fdecl::OfferService {
7870 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7871 source_name: Some("a".to_string()),
7872 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7873 target_name: Some("a".to_string()),
7874 ..Default::default()
7875 })
7876 ]);
7877 decl
7878 },
7879 result = Ok(()),
7880 },
7881 test_validate_offers_invalid_capability_from_self => {
7882 input = {
7883 let mut decl = new_component_decl();
7884 decl.children = Some(vec![
7885 fdecl::Child {
7886 name: Some("child".to_string()),
7887 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7888 startup: Some(fdecl::StartupMode::Lazy),
7889 ..Default::default()
7890 }
7891 ]);
7892 decl.offers = Some(vec![
7893 fdecl::Offer::Service(fdecl::OfferService {
7894 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7895 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7896 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7897 name: "child".into(),
7898 collection: None
7899 })),
7900 target_name: Some("foo".into()),
7901 ..Default::default()
7902 }),
7903 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7904 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7905 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7906 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7907 name: "child".into(),
7908 collection: None
7909 })),
7910 target_name: Some("bar".into()),
7911 dependency_type: Some(fdecl::DependencyType::Strong),
7912 ..Default::default()
7913 }),
7914 fdecl::Offer::Directory(fdecl::OfferDirectory {
7915 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7916 source_name: Some("dir".into()),
7917 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7918 name: "child".into(),
7919 collection: None
7920 })),
7921 target_name: Some("assets".into()),
7922 dependency_type: Some(fdecl::DependencyType::Strong),
7923 ..Default::default()
7924 }),
7925 fdecl::Offer::Runner(fdecl::OfferRunner {
7926 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7927 source_name: Some("source_elf".into()),
7928 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7929 name: "child".into(),
7930 collection: None
7931 })),
7932 target_name: Some("elf".into()),
7933 ..Default::default()
7934 }),
7935 fdecl::Offer::Resolver(fdecl::OfferResolver {
7936 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7937 source_name: Some("source_pkg".into()),
7938 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7939 name: "child".into(),
7940 collection: None
7941 })),
7942 target_name: Some("pkg".into()),
7943 ..Default::default()
7944 }),
7945 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7946 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7947 source_name: Some("source_dict".into()),
7948 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7949 name: "child".into(),
7950 collection: None
7951 })),
7952 target_name: Some("dict".into()),
7953 dependency_type: Some(fdecl::DependencyType::Strong),
7954 ..Default::default()
7955 }),
7956 fdecl::Offer::Storage(fdecl::OfferStorage {
7957 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7958 source_name: Some("source_storage".into()),
7959 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7960 name: "child".into(),
7961 collection: None
7962 })),
7963 target_name: Some("storage".into()),
7964 ..Default::default()
7965 }),
7966 fdecl::Offer::Config(fdecl::OfferConfiguration {
7967 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7968 source_name: Some("source_config".into()),
7969 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7970 name: "child".into(),
7971 collection: None
7972 })),
7973 target_name: Some("config".into()),
7974 ..Default::default()
7975 }),
7976 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7977 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7978 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7979 source_dictionary: Some("dict/inner".into()),
7980 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7981 name: "child".into(),
7982 collection: None
7983 })),
7984 target_name: Some("baz".into()),
7985 dependency_type: Some(fdecl::DependencyType::Strong),
7986 ..Default::default()
7987 }),
7988 ]);
7989 decl
7990 },
7991 result = Err(ErrorList::new(vec![
7992 Error::invalid_capability(
7993 DeclType::OfferService,
7994 "source",
7995 "fuchsia.some.library.SomeProtocol"),
7996 Error::invalid_capability(
7997 DeclType::OfferProtocol,
7998 "source",
7999 "fuchsia.some.library.SomeProtocol"),
8000 Error::invalid_capability(DeclType::OfferDirectory, "source", "dir"),
8001 Error::invalid_capability(DeclType::OfferRunner, "source", "source_elf"),
8002 Error::invalid_capability(DeclType::OfferResolver, "source", "source_pkg"),
8003 Error::invalid_capability(DeclType::OfferDictionary, "source", "source_dict"),
8004 Error::invalid_capability(DeclType::OfferStorage, "source", "source_storage"),
8005 Error::invalid_capability(DeclType::OfferConfig, "source", "source_config"),
8006 Error::invalid_capability(DeclType::OfferProtocol, "source", "dict"),
8007 ])),
8008 },
8009 test_validate_offers_long_dependency_cycle => {
8010 input = {
8011 let mut decl = new_component_decl();
8012 let dependencies = vec![
8013 ("d", "b"),
8014 ("a", "b"),
8015 ("b", "c"),
8016 ("b", "d"),
8017 ("c", "a"),
8018 ];
8019 let offers = dependencies.into_iter().map(|(from,to)|
8020 fdecl::Offer::Protocol(fdecl::OfferProtocol {
8021 source: Some(fdecl::Ref::Child(
8022 fdecl::ChildRef { name: from.to_string(), collection: None },
8023 )),
8024 source_name: Some(format!("thing_{}", from)),
8025 target: Some(fdecl::Ref::Child(
8026 fdecl::ChildRef { name: to.to_string(), collection: None },
8027 )),
8028 target_name: Some(format!("thing_{}", from)),
8029 dependency_type: Some(fdecl::DependencyType::Strong),
8030 ..Default::default()
8031 })).collect();
8032 let children = ["a", "b", "c", "d"].iter().map(|name| {
8033 fdecl::Child {
8034 name: Some(name.to_string()),
8035 url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
8036 startup: Some(fdecl::StartupMode::Lazy),
8037 on_terminate: None,
8038 environment: None,
8039 ..Default::default()
8040 }
8041 }).collect();
8042 decl.offers = Some(offers);
8043 decl.children = Some(children);
8044 decl
8045 },
8046 result = Err(ErrorList::new(vec![
8047 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()),
8048 ])),
8049 },
8050 test_validate_offers_not_required_invalid_source_service => {
8051 input = {
8052 let mut decl = generate_offer_different_source_and_availability_decl(
8053 |source, availability, target_name|
8054 fdecl::Offer::Service(fdecl::OfferService {
8055 source: Some(source),
8056 source_name: Some("fuchsia.examples.Echo".to_string()),
8057 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8058 name: "sink".to_string(),
8059 collection: None,
8060 })),
8061 target_name: Some(target_name.into()),
8062 availability: Some(availability),
8063 ..Default::default()
8064 })
8065 );
8066 decl.capabilities = Some(vec![
8067 fdecl::Capability::Service(fdecl::Service {
8068 name: Some("fuchsia.examples.Echo".to_string()),
8069 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
8070 ..Default::default()
8071 }),
8072 ]);
8073 decl
8074 },
8075 result = {
8076 Err(ErrorList::new(vec![
8077 Error::availability_must_be_optional(
8078 DeclType::OfferService,
8079 "availability",
8080 Some(&"fuchsia.examples.Echo".to_string()),
8081 ),
8082 Error::availability_must_be_optional(
8083 DeclType::OfferService,
8084 "availability",
8085 Some(&"fuchsia.examples.Echo".to_string()),
8086 ),
8087 ]))
8088 },
8089 },
8090 test_validate_offers_not_required_invalid_source_protocol => {
8091 input = {
8092 let mut decl = generate_offer_different_source_and_availability_decl(
8093 |source, availability, target_name|
8094 fdecl::Offer::Protocol(fdecl::OfferProtocol {
8095 source: Some(source),
8096 source_name: Some("fuchsia.examples.Echo".to_string()),
8097 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8098 name: "sink".to_string(),
8099 collection: None,
8100 })),
8101 target_name: Some(target_name.into()),
8102 dependency_type: Some(fdecl::DependencyType::Strong),
8103 availability: Some(availability),
8104 ..Default::default()
8105 })
8106 );
8107 decl.capabilities = Some(vec![
8108 fdecl::Capability::Protocol(fdecl::Protocol {
8109 name: Some("fuchsia.examples.Echo".to_string()),
8110 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
8111 ..Default::default()
8112 }),
8113 ]);
8114 decl
8115 },
8116 result = {
8117 Err(ErrorList::new(vec![
8118 Error::availability_must_be_optional(
8119 DeclType::OfferProtocol,
8120 "availability",
8121 Some(&"fuchsia.examples.Echo".to_string()),
8122 ),
8123 Error::availability_must_be_optional(
8124 DeclType::OfferProtocol,
8125 "availability",
8126 Some(&"fuchsia.examples.Echo".to_string()),
8127 ),
8128 ]))
8129 },
8130 },
8131 test_validate_offers_not_required_invalid_source_directory => {
8132 input = {
8133 let mut decl = generate_offer_different_source_and_availability_decl(
8134 |source, availability, target_name|
8135 fdecl::Offer::Directory(fdecl::OfferDirectory {
8136 source: Some(source),
8137 source_name: Some("assets".to_string()),
8138 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8139 name: "sink".to_string(),
8140 collection: None,
8141 })),
8142 target_name: Some(target_name.into()),
8143 rights: Some(fio::Operations::CONNECT),
8144 subdir: None,
8145 dependency_type: Some(fdecl::DependencyType::Weak),
8146 availability: Some(availability),
8147 ..Default::default()
8148 })
8149 );
8150 decl.capabilities = Some(vec![
8151 fdecl::Capability::Directory(fdecl::Directory {
8152 name: Some("assets".to_string()),
8153 source_path: Some("/assets".to_string()),
8154 rights: Some(fio::Operations::CONNECT),
8155 ..Default::default()
8156 }),
8157 ]);
8158 decl
8159 },
8160 result = {
8161 Err(ErrorList::new(vec![
8162 Error::availability_must_be_optional(
8163 DeclType::OfferDirectory,
8164 "availability",
8165 Some(&"assets".to_string()),
8166 ),
8167 Error::availability_must_be_optional(
8168 DeclType::OfferDirectory,
8169 "availability",
8170 Some(&"assets".to_string()),
8171 ),
8172 ]))
8173 },
8174 },
8175 test_validate_offers_not_required_invalid_source_storage => {
8176 input = {
8177 let mut decl = new_component_decl();
8178 decl.children = Some(vec![
8179 fdecl::Child {
8180 name: Some("sink".to_string()),
8181 url: Some("fuchsia-pkg://fuchsia.com/sink#meta/sink.cm".to_string()),
8182 startup: Some(fdecl::StartupMode::Lazy),
8183 on_terminate: None,
8184 environment: None,
8185 ..Default::default()
8186 },
8187 ]);
8188 decl.capabilities = Some(vec![
8189 fdecl::Capability::Storage(fdecl::Storage {
8190 name: Some("data".to_string()),
8191 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8192 backing_dir: Some("minfs".to_string()),
8193 subdir: None,
8194 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
8195 ..Default::default()
8196 }),
8197 ]);
8198 let new_offer = |source: fdecl::Ref, availability: fdecl::Availability,
8199 target_name: &str|
8200 {
8201 fdecl::Offer::Storage(fdecl::OfferStorage {
8202 source: Some(source),
8203 source_name: Some("data".to_string()),
8204 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8205 name: "sink".to_string(),
8206 collection: None,
8207 })),
8208 target_name: Some(target_name.into()),
8209 availability: Some(availability),
8210 ..Default::default()
8211 })
8212 };
8213 decl.offers = Some(vec![
8214 new_offer(
8217 fdecl::Ref::Parent(fdecl::ParentRef {}),
8218 fdecl::Availability::Required,
8219 "data0",
8220 ),
8221 new_offer(
8222 fdecl::Ref::Parent(fdecl::ParentRef {}),
8223 fdecl::Availability::Optional,
8224 "data1",
8225 ),
8226 new_offer(
8227 fdecl::Ref::Parent(fdecl::ParentRef {}),
8228 fdecl::Availability::SameAsTarget,
8229 "data2",
8230 ),
8231 new_offer(
8232 fdecl::Ref::VoidType(fdecl::VoidRef {}),
8233 fdecl::Availability::Optional,
8234 "data3",
8235 ),
8236 new_offer(
8239 fdecl::Ref::Self_(fdecl::SelfRef {}),
8240 fdecl::Availability::Optional,
8241 "data4",
8242 ),
8243 new_offer(
8244 fdecl::Ref::Self_(fdecl::SelfRef {}),
8245 fdecl::Availability::SameAsTarget,
8246 "data5",
8247 ),
8248 new_offer(
8250 fdecl::Ref::VoidType(fdecl::VoidRef {}),
8251 fdecl::Availability::Required,
8252 "data6",
8253 ),
8254 new_offer(
8255 fdecl::Ref::VoidType(fdecl::VoidRef {}),
8256 fdecl::Availability::SameAsTarget,
8257 "data7",
8258 ),
8259 ]);
8260 decl
8261 },
8262 result = {
8263 Err(ErrorList::new(vec![
8264 Error::availability_must_be_optional(
8265 DeclType::OfferStorage,
8266 "availability",
8267 Some(&"data".to_string()),
8268 ),
8269 Error::availability_must_be_optional(
8270 DeclType::OfferStorage,
8271 "availability",
8272 Some(&"data".to_string()),
8273 ),
8274 ]))
8275 },
8276 },
8277
8278 test_validate_offers_valid_service_aggregation => {
8279 input = {
8280 let mut decl = new_component_decl();
8281 decl.offers = Some(vec![
8282 fdecl::Offer::Service(fdecl::OfferService {
8283 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
8284 name: "coll_a".to_string()
8285 })),
8286 source_name: Some("fuchsia.logger.Log".to_string()),
8287 target: Some(fdecl::Ref::Child(
8288 fdecl::ChildRef {
8289 name: "child_c".to_string(),
8290 collection: None,
8291 }
8292 )),
8293 target_name: Some("fuchsia.logger.Log".to_string()),
8294 source_instance_filter: None,
8295 ..Default::default()
8296 }),
8297 fdecl::Offer::Service(fdecl::OfferService {
8298 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
8299 name: "coll_b".to_string()
8300 })),
8301 source_name: Some("fuchsia.logger.Log".to_string()),
8302 target: Some(fdecl::Ref::Child(
8303 fdecl::ChildRef {
8304 name: "child_c".to_string(),
8305 collection: None,
8306 }
8307 )),
8308 target_name: Some("fuchsia.logger.Log".to_string()),
8309 source_instance_filter: Some(vec!["a_different_default".to_string()]),
8310 ..Default::default()
8311 })
8312 ]);
8313 decl.children = Some(vec![
8314 fdecl::Child {
8315 name: Some("child_c".to_string()),
8316 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
8317 startup: Some(fdecl::StartupMode::Lazy),
8318 ..Default::default()
8319 },
8320 ]);
8321 decl.collections = Some(vec![
8322 fdecl::Collection {
8323 name: Some("coll_a".into()),
8324 durability: Some(fdecl::Durability::Transient),
8325 ..Default::default()
8326 },
8327 fdecl::Collection {
8328 name: Some("coll_b".into()),
8329 durability: Some(fdecl::Durability::Transient),
8330 ..Default::default()
8331 },
8332 ]);
8333 decl
8334 },
8335 result = Ok(()),
8336 },
8337
8338 test_validate_source_dictionary => {
8340 input = fdecl::Component {
8341 program: Some(fdecl::Program {
8342 runner: Some("elf".into()),
8343 info: Some(fdata::Dictionary {
8344 entries: None,
8345 ..Default::default()
8346 }),
8347 ..Default::default()
8348 }),
8349 uses: Some(vec![
8350 fdecl::Use::Protocol(fdecl::UseProtocol {
8351 dependency_type: Some(fdecl::DependencyType::Strong),
8352 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8353 source_dictionary: Some("bad//".into()),
8354 source_name: Some("foo".into()),
8355 target_path: Some("/svc/foo".into()),
8356 ..Default::default()
8357 }),
8358 ]),
8359 exposes: Some(vec![
8360 fdecl::Expose::Directory(fdecl::ExposeDirectory {
8361 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8362 name: "missing".into(),
8363 collection: None,
8364 })),
8365 source_dictionary: Some("in/dict".into()),
8366 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8367 source_name: Some("foo".into()),
8368 target_name: Some("bar".into()),
8369 ..Default::default()
8370 }),
8371 ]),
8372 offers: Some(vec![
8373 fdecl::Offer::Service(fdecl::OfferService {
8374 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8375 source_dictionary: Some("bad//".into()),
8376 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8377 name: "child".into(),
8378 collection: None,
8379 })),
8380 source_name: Some("foo".into()),
8381 target_name: Some("bar".into()),
8382 ..Default::default()
8383 }),
8384 ]),
8385 children: Some(vec![
8386 fdecl::Child {
8387 name: Some("child".into()),
8388 url: Some("fuchsia-pkg://child".into()),
8389 startup: Some(fdecl::StartupMode::Lazy),
8390 ..Default::default()
8391 },
8392 ]),
8393 ..Default::default()
8394 },
8395 result = Err(ErrorList::new(vec![
8396 Error::invalid_field(DeclType::UseProtocol, "source_dictionary"),
8397 Error::invalid_child(DeclType::ExposeDirectory, "source", "missing"),
8398 Error::invalid_field(DeclType::OfferService, "source_dictionary"),
8399 ])),
8400 },
8401 test_validate_dictionary_too_long => {
8402 input = fdecl::Component {
8403 program: Some(fdecl::Program {
8404 runner: Some("elf".into()),
8405 info: Some(fdata::Dictionary {
8406 entries: None,
8407 ..Default::default()
8408 }),
8409 ..Default::default()
8410 }),
8411 uses: Some(vec![
8412 fdecl::Use::Protocol(fdecl::UseProtocol {
8413 dependency_type: Some(fdecl::DependencyType::Strong),
8414 source: Some(fdecl::Ref::Parent( fdecl::ParentRef {} )),
8415 source_dictionary: Some("a".repeat(4096)),
8416 source_name: Some("foo".into()),
8417 target_path: Some("/svc/foo".into()),
8418 ..Default::default()
8419 }),
8420 ]),
8421 ..Default::default()
8422 },
8423 result = Err(ErrorList::new(vec![
8424 Error::field_too_long(DeclType::UseProtocol, "source_dictionary"),
8425 ])),
8426 },
8427
8428 test_validate_environment_empty => {
8430 input = {
8431 let mut decl = new_component_decl();
8432 decl.environments = Some(vec![fdecl::Environment {
8433 name: None,
8434 extends: None,
8435 runners: None,
8436 resolvers: None,
8437 stop_timeout_ms: None,
8438 debug_capabilities: None,
8439 ..Default::default()
8440 }]);
8441 decl
8442 },
8443 result = Err(ErrorList::new(vec![
8444 Error::missing_field(DeclType::Environment, "name"),
8445 Error::missing_field(DeclType::Environment, "extends"),
8446 ])),
8447 },
8448
8449 test_validate_environment_no_stop_timeout => {
8450 input = {
8451 let mut decl = new_component_decl();
8452 decl.environments = Some(vec![fdecl::Environment {
8453 name: Some("env".to_string()),
8454 extends: Some(fdecl::EnvironmentExtends::None),
8455 runners: None,
8456 resolvers: None,
8457 stop_timeout_ms: None,
8458 ..Default::default()
8459 }]);
8460 decl
8461 },
8462 result = Err(ErrorList::new(vec![Error::missing_field(DeclType::Environment, "stop_timeout_ms")])),
8463 },
8464
8465 test_validate_environment_extends_stop_timeout => {
8466 input = { let mut decl = new_component_decl();
8467 decl.environments = Some(vec![fdecl::Environment {
8468 name: Some("env".to_string()),
8469 extends: Some(fdecl::EnvironmentExtends::Realm),
8470 runners: None,
8471 resolvers: None,
8472 stop_timeout_ms: None,
8473 ..Default::default()
8474 }]);
8475 decl
8476 },
8477 result = Ok(()),
8478 },
8479 test_validate_environment_long_identifiers => {
8480 input = {
8481 let mut decl = new_component_decl();
8482 decl.environments = Some(vec![fdecl::Environment {
8483 name: Some("a".repeat(256)),
8484 extends: Some(fdecl::EnvironmentExtends::None),
8485 runners: Some(vec![
8486 fdecl::RunnerRegistration {
8487 source_name: Some("a".repeat(256)),
8488 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8489 target_name: Some("a".repeat(256)),
8490 ..Default::default()
8491 },
8492 ]),
8493 resolvers: Some(vec![
8494 fdecl::ResolverRegistration {
8495 resolver: Some("a".repeat(256)),
8496 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8497 scheme: Some("a".repeat(256)),
8498 ..Default::default()
8499 },
8500 ]),
8501 stop_timeout_ms: Some(1234),
8502 ..Default::default()
8503 }]);
8504 decl
8505 },
8506 result = Err(ErrorList::new(vec![
8507 Error::field_too_long(DeclType::Environment, "name"),
8508 Error::field_too_long(DeclType::RunnerRegistration, "source_name"),
8509 Error::field_too_long(DeclType::RunnerRegistration, "target_name"),
8510 Error::field_too_long(DeclType::ResolverRegistration, "resolver"),
8511 Error::field_too_long(DeclType::ResolverRegistration, "scheme"),
8512 ])),
8513 },
8514 test_validate_environment_empty_runner_resolver_fields => {
8515 input = {
8516 let mut decl = new_component_decl();
8517 decl.environments = Some(vec![fdecl::Environment {
8518 name: Some("a".to_string()),
8519 extends: Some(fdecl::EnvironmentExtends::None),
8520 runners: Some(vec![
8521 fdecl::RunnerRegistration {
8522 source_name: None,
8523 source: None,
8524 target_name: None,
8525 ..Default::default()
8526 },
8527 ]),
8528 resolvers: Some(vec![
8529 fdecl::ResolverRegistration {
8530 resolver: None,
8531 source: None,
8532 scheme: None,
8533 ..Default::default()
8534 },
8535 ]),
8536 stop_timeout_ms: Some(1234),
8537 ..Default::default()
8538 }]);
8539 decl
8540 },
8541 result = Err(ErrorList::new(vec![
8542 Error::missing_field(DeclType::RunnerRegistration, "source_name"),
8543 Error::missing_field(DeclType::RunnerRegistration, "source"),
8544 Error::missing_field(DeclType::RunnerRegistration, "target_name"),
8545 Error::missing_field(DeclType::ResolverRegistration, "resolver"),
8546 Error::missing_field(DeclType::ResolverRegistration, "source"),
8547 Error::missing_field(DeclType::ResolverRegistration, "scheme"),
8548 ])),
8549 },
8550 test_validate_environment_invalid_fields => {
8551 input = {
8552 let mut decl = new_component_decl();
8553 decl.environments = Some(vec![fdecl::Environment {
8554 name: Some("a".to_string()),
8555 extends: Some(fdecl::EnvironmentExtends::None),
8556 runners: Some(vec![
8557 fdecl::RunnerRegistration {
8558 source_name: Some("^a".to_string()),
8559 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
8560 target_name: Some("%a".to_string()),
8561 ..Default::default()
8562 },
8563 ]),
8564 resolvers: Some(vec![
8565 fdecl::ResolverRegistration {
8566 resolver: Some("^a".to_string()),
8567 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
8568 scheme: Some("9scheme".to_string()),
8569 ..Default::default()
8570 },
8571 ]),
8572 stop_timeout_ms: Some(1234),
8573 ..Default::default()
8574 }]);
8575 decl
8576 },
8577 result = Err(ErrorList::new(vec![
8578 Error::invalid_field(DeclType::RunnerRegistration, "source_name"),
8579 Error::invalid_field(DeclType::RunnerRegistration, "source"),
8580 Error::invalid_field(DeclType::RunnerRegistration, "target_name"),
8581 Error::invalid_field(DeclType::ResolverRegistration, "resolver"),
8582 Error::invalid_field(DeclType::ResolverRegistration, "source"),
8583 Error::invalid_field(DeclType::ResolverRegistration, "scheme"),
8584 ])),
8585 },
8586 test_validate_environment_missing_runner => {
8587 input = {
8588 let mut decl = new_component_decl();
8589 decl.environments = Some(vec![fdecl::Environment {
8590 name: Some("a".to_string()),
8591 extends: Some(fdecl::EnvironmentExtends::None),
8592 runners: Some(vec![
8593 fdecl::RunnerRegistration {
8594 source_name: Some("dart".to_string()),
8595 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
8596 target_name: Some("dart".to_string()),
8597 ..Default::default()
8598 },
8599 ]),
8600 resolvers: None,
8601 stop_timeout_ms: Some(1234),
8602 ..Default::default()
8603 }]);
8604 decl
8605 },
8606 result = Err(ErrorList::new(vec![
8607 Error::invalid_runner(DeclType::RunnerRegistration, "source_name", "dart"),
8608 ])),
8609 },
8610 test_validate_environment_duplicate_registrations => {
8611 input = {
8612 let mut decl = new_component_decl();
8613 decl.environments = Some(vec![fdecl::Environment {
8614 name: Some("a".to_string()),
8615 extends: Some(fdecl::EnvironmentExtends::None),
8616 runners: Some(vec![
8617 fdecl::RunnerRegistration {
8618 source_name: Some("dart".to_string()),
8619 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8620 target_name: Some("dart".to_string()),
8621 ..Default::default()
8622 },
8623 fdecl::RunnerRegistration {
8624 source_name: Some("other-dart".to_string()),
8625 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8626 target_name: Some("dart".to_string()),
8627 ..Default::default()
8628 },
8629 ]),
8630 resolvers: Some(vec![
8631 fdecl::ResolverRegistration {
8632 resolver: Some("pkg_resolver".to_string()),
8633 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8634 scheme: Some("fuchsia-pkg".to_string()),
8635 ..Default::default()
8636 },
8637 fdecl::ResolverRegistration {
8638 resolver: Some("base_resolver".to_string()),
8639 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8640 scheme: Some("fuchsia-pkg".to_string()),
8641 ..Default::default()
8642 },
8643 ]),
8644 stop_timeout_ms: Some(1234),
8645 ..Default::default()
8646 }]);
8647 decl
8648 },
8649 result = Err(ErrorList::new(vec![
8650 Error::duplicate_field(DeclType::RunnerRegistration, "target_name", "dart"),
8651 Error::duplicate_field(DeclType::ResolverRegistration, "scheme", "fuchsia-pkg"),
8652 ])),
8653 },
8654 test_validate_environment_from_missing_child => {
8655 input = {
8656 let mut decl = new_component_decl();
8657 decl.environments = Some(vec![fdecl::Environment {
8658 name: Some("a".to_string()),
8659 extends: Some(fdecl::EnvironmentExtends::None),
8660 runners: Some(vec![
8661 fdecl::RunnerRegistration {
8662 source_name: Some("elf".to_string()),
8663 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8664 name: "missing".to_string(),
8665 collection: None,
8666 })),
8667 target_name: Some("elf".to_string()),
8668 ..Default::default()
8669 },
8670 ]),
8671 resolvers: Some(vec![
8672 fdecl::ResolverRegistration {
8673 resolver: Some("pkg_resolver".to_string()),
8674 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8675 name: "missing".to_string(),
8676 collection: None,
8677 })),
8678 scheme: Some("fuchsia-pkg".to_string()),
8679 ..Default::default()
8680 },
8681 ]),
8682 stop_timeout_ms: Some(1234),
8683 ..Default::default()
8684 }]);
8685 decl
8686 },
8687 result = Err(ErrorList::new(vec![
8688 Error::invalid_child(DeclType::RunnerRegistration, "source", "missing"),
8689 Error::invalid_child(DeclType::ResolverRegistration, "source", "missing"),
8690 ])),
8691 },
8692 test_validate_environment_runner_child_cycle => {
8693 input = {
8694 let mut decl = new_component_decl();
8695 decl.environments = Some(vec![fdecl::Environment {
8696 name: Some("env".to_string()),
8697 extends: Some(fdecl::EnvironmentExtends::None),
8698 runners: Some(vec![
8699 fdecl::RunnerRegistration {
8700 source_name: Some("elf".to_string()),
8701 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8702 name: "child".to_string(),
8703 collection: None,
8704 })),
8705 target_name: Some("elf".to_string()),
8706 ..Default::default()
8707 },
8708 ]),
8709 resolvers: None,
8710 stop_timeout_ms: Some(1234),
8711 ..Default::default()
8712 }]);
8713 decl.children = Some(vec![fdecl::Child {
8714 name: Some("child".to_string()),
8715 startup: Some(fdecl::StartupMode::Lazy),
8716 on_terminate: None,
8717 url: Some("fuchsia-pkg://child".to_string()),
8718 environment: Some("env".to_string()),
8719 ..Default::default()
8720 }]);
8721 decl
8722 },
8723 result = Err(ErrorList::new(vec![
8724 Error::dependency_cycle(
8725 directed_graph::Error::CyclesDetected([vec!["child child", "environment env", "child child"]].iter().cloned().collect()).format_cycle()
8726 ),
8727 ])),
8728 },
8729 test_validate_environment_resolver_child_cycle => {
8730 input = {
8731 let mut decl = new_component_decl();
8732 decl.environments = Some(vec![fdecl::Environment {
8733 name: Some("env".to_string()),
8734 extends: Some(fdecl::EnvironmentExtends::None),
8735 runners: None,
8736 resolvers: Some(vec![
8737 fdecl::ResolverRegistration {
8738 resolver: Some("pkg_resolver".to_string()),
8739 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8740 name: "child".to_string(),
8741 collection: None,
8742 })),
8743 scheme: Some("fuchsia-pkg".to_string()),
8744 ..Default::default()
8745 },
8746 ]),
8747 stop_timeout_ms: Some(1234),
8748 ..Default::default()
8749 }]);
8750 decl.children = Some(vec![fdecl::Child {
8751 name: Some("child".to_string()),
8752 startup: Some(fdecl::StartupMode::Lazy),
8753 on_terminate: None,
8754 url: Some("fuchsia-pkg://child".to_string()),
8755 environment: Some("env".to_string()),
8756 ..Default::default()
8757 }]);
8758 decl
8759 },
8760 result = Err(ErrorList::new(vec![
8761 Error::dependency_cycle(
8762 directed_graph::Error::CyclesDetected([vec!["child child", "environment env", "child child"]].iter().cloned().collect()).format_cycle()
8763 ),
8764 ])),
8765 },
8766 test_validate_environment_resolver_multiple_children_cycle => {
8767 input = {
8768 let mut decl = new_component_decl();
8769 decl.environments = Some(vec![fdecl::Environment {
8770 name: Some("env".to_string()),
8771 extends: Some(fdecl::EnvironmentExtends::None),
8772 runners: None,
8773 resolvers: Some(vec![
8774 fdecl::ResolverRegistration {
8775 resolver: Some("pkg_resolver".to_string()),
8776 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8777 name: "a".to_string(),
8778 collection: None,
8779 })),
8780 scheme: Some("fuchsia-pkg".to_string()),
8781 ..Default::default()
8782 },
8783 ]),
8784 stop_timeout_ms: Some(1234),
8785 ..Default::default()
8786 }]);
8787 decl.children = Some(vec![
8788 fdecl::Child {
8789 name: Some("a".to_string()),
8790 startup: Some(fdecl::StartupMode::Lazy),
8791 on_terminate: None,
8792 url: Some("fuchsia-pkg://child-a".to_string()),
8793 environment: None,
8794 ..Default::default()
8795 },
8796 fdecl::Child {
8797 name: Some("b".to_string()),
8798 startup: Some(fdecl::StartupMode::Lazy),
8799 on_terminate: None,
8800 url: Some("fuchsia-pkg://child-b".to_string()),
8801 environment: Some("env".to_string()),
8802 ..Default::default()
8803 },
8804 ]);
8805 decl.offers = Some(vec![fdecl::Offer::Service(fdecl::OfferService {
8806 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8807 name: "b".to_string(),
8808 collection: None,
8809 })),
8810 source_name: Some("thing".to_string()),
8811 target: Some(fdecl::Ref::Child(
8812 fdecl::ChildRef {
8813 name: "a".to_string(),
8814 collection: None,
8815 }
8816 )),
8817 target_name: Some("thing".to_string()),
8818 ..Default::default()
8819 })]);
8820 decl
8821 },
8822 result = Err(ErrorList::new(vec![
8823 Error::dependency_cycle(
8824 directed_graph::Error::CyclesDetected([vec!["child a", "environment env", "child b", "child a"]].iter().cloned().collect()).format_cycle()
8825 ),
8826 ])),
8827 },
8828 test_validate_environment_debug_empty => {
8829 input = {
8830 let mut decl = new_component_decl();
8831 decl.environments = Some(vec![
8832 fdecl::Environment {
8833 name: Some("a".to_string()),
8834 extends: Some(fdecl::EnvironmentExtends::None),
8835 stop_timeout_ms: Some(2),
8836 debug_capabilities:Some(vec![
8837 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8838 source: None,
8839 source_name: None,
8840 target_name: None,
8841 ..Default::default()
8842 }),
8843 ]),
8844 ..Default::default()
8845 }]);
8846 decl
8847 },
8848 result = Err(ErrorList::new(vec![
8849 Error::missing_field(DeclType::DebugProtocolRegistration, "source"),
8850 Error::missing_field(DeclType::DebugProtocolRegistration, "source_name"),
8851 Error::missing_field(DeclType::DebugProtocolRegistration, "target_name"),
8852 ])),
8853 },
8854 test_validate_environment_debug_log_identifier => {
8855 input = {
8856 let mut decl = new_component_decl();
8857 decl.environments = Some(vec![
8858 fdecl::Environment {
8859 name: Some("a".to_string()),
8860 extends: Some(fdecl::EnvironmentExtends::None),
8861 stop_timeout_ms: Some(2),
8862 debug_capabilities:Some(vec![
8863 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8864 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8865 name: "a".repeat(256),
8866 collection: None,
8867 })),
8868 source_name: Some(format!("{}", "a".repeat(256))),
8869 target_name: Some(format!("{}", "b".repeat(256))),
8870 ..Default::default()
8871 }),
8872 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8873 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8874 source_name: Some("a".to_string()),
8875 target_name: Some(format!("{}", "b".repeat(256))),
8876 ..Default::default()
8877 }),
8878 ]),
8879 ..Default::default()
8880 }]);
8881 decl
8882 },
8883 result = Err(ErrorList::new(vec![
8884 Error::field_too_long(DeclType::DebugProtocolRegistration, "source.child.name"),
8885 Error::field_too_long(DeclType::DebugProtocolRegistration, "source_name"),
8886 Error::field_too_long(DeclType::DebugProtocolRegistration, "target_name"),
8887 Error::field_too_long(DeclType::DebugProtocolRegistration, "target_name"),
8888 ])),
8889 },
8890 test_validate_environment_debug_log_extraneous => {
8891 input = {
8892 let mut decl = new_component_decl();
8893 decl.environments = Some(vec![
8894 fdecl::Environment {
8895 name: Some("a".to_string()),
8896 extends: Some(fdecl::EnvironmentExtends::None),
8897 stop_timeout_ms: Some(2),
8898 debug_capabilities:Some(vec![
8899 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8900 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8901 name: "logger".to_string(),
8902 collection: Some("modular".to_string()),
8903 })),
8904 source_name: Some("fuchsia.logger.Log".to_string()),
8905 target_name: Some("fuchsia.logger.Log".to_string()),
8906 ..Default::default()
8907 }),
8908 ]),
8909 ..Default::default()
8910 }]);
8911 decl
8912 },
8913 result = Err(ErrorList::new(vec![
8914 Error::extraneous_field(DeclType::DebugProtocolRegistration, "source.child.collection"),
8915 ])),
8916 },
8917 test_validate_environment_debug_log_invalid_identifiers => {
8918 input = {
8919 let mut decl = new_component_decl();
8920 decl.environments = Some(vec![
8921 fdecl::Environment {
8922 name: Some("a".to_string()),
8923 extends: Some(fdecl::EnvironmentExtends::None),
8924 stop_timeout_ms: Some(2),
8925 debug_capabilities:Some(vec![
8926 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8927 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8928 name: "^bad".to_string(),
8929 collection: None,
8930 })),
8931 source_name: Some("foo/".to_string()),
8932 target_name: Some("/".to_string()),
8933 ..Default::default()
8934 }),
8935 ]),
8936 ..Default::default()
8937 }]);
8938 decl
8939 },
8940 result = Err(ErrorList::new(vec![
8941 Error::invalid_field(DeclType::DebugProtocolRegistration, "source.child.name"),
8942 Error::invalid_field(DeclType::DebugProtocolRegistration, "source_name"),
8943 Error::invalid_field(DeclType::DebugProtocolRegistration, "target_name"),
8944 ])),
8945 },
8946 test_validate_environment_debug_log_invalid_child => {
8947 input = {
8948 let mut decl = new_component_decl();
8949 decl.environments = Some(vec![
8950 fdecl::Environment {
8951 name: Some("a".to_string()),
8952 extends: Some(fdecl::EnvironmentExtends::None),
8953 stop_timeout_ms: Some(2),
8954 debug_capabilities:Some(vec![
8955 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8956 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8957 name: "logger".to_string(),
8958 collection: None,
8959 })),
8960 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
8961 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
8962 ..Default::default()
8963 }),
8964 ]),
8965 ..Default::default()
8966 }]);
8967 decl.children = Some(vec![
8968 fdecl::Child {
8969 name: Some("netstack".to_string()),
8970 url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
8971 startup: Some(fdecl::StartupMode::Lazy),
8972 on_terminate: None,
8973 environment: None,
8974 ..Default::default()
8975 },
8976 ]);
8977 decl
8978 },
8979 result = Err(ErrorList::new(vec![
8980 Error::invalid_child(DeclType::DebugProtocolRegistration, "source", "logger"),
8981
8982 ])),
8983 },
8984 test_validate_environment_debug_source_capability => {
8985 input = {
8986 let mut decl = new_component_decl();
8987 decl.environments = Some(vec![
8988 fdecl::Environment {
8989 name: Some("a".to_string()),
8990 extends: Some(fdecl::EnvironmentExtends::None),
8991 stop_timeout_ms: Some(2),
8992 debug_capabilities:Some(vec![
8993 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8994 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
8995 name: "storage".to_string(),
8996 })),
8997 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
8998 target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
8999 ..Default::default()
9000 }),
9001 ]),
9002 ..Default::default()
9003 }]);
9004 decl
9005 },
9006 result = Err(ErrorList::new(vec![
9007 Error::invalid_field(DeclType::DebugProtocolRegistration, "source"),
9008 ])),
9009 },
9010
9011 test_validate_children_empty => {
9013 input = {
9014 let mut decl = new_component_decl();
9015 decl.children = Some(vec![fdecl::Child{
9016 name: None,
9017 url: None,
9018 startup: None,
9019 on_terminate: None,
9020 environment: None,
9021 ..Default::default()
9022 }]);
9023 decl
9024 },
9025 result = Err(ErrorList::new(vec![
9026 Error::missing_field(DeclType::Child, "name"),
9027 Error::missing_field(DeclType::Child, "url"),
9028 Error::missing_field(DeclType::Child, "startup"),
9029 ])),
9031 },
9032 test_validate_children_invalid_identifiers => {
9033 input = {
9034 let mut decl = new_component_decl();
9035 decl.children = Some(vec![fdecl::Child{
9036 name: Some("^bad".to_string()),
9037 url: Some("scheme://invalid-port:99999999/path#frag".to_string()),
9038 startup: Some(fdecl::StartupMode::Lazy),
9039 on_terminate: None,
9040 environment: None,
9041 ..Default::default()
9042 }]);
9043 decl
9044 },
9045 result = Err(ErrorList::new(vec![
9046 Error::invalid_field(DeclType::Child, "name"),
9047 Error::invalid_url(DeclType::Child, "url", "\"scheme://invalid-port:99999999/path#frag\": Malformed URL: InvalidPort."),
9048 ])),
9049 },
9050 test_validate_children_long_identifiers => {
9051 input = {
9052 let mut decl = new_component_decl();
9053 decl.children = Some(vec![fdecl::Child{
9054 name: Some("a".repeat(1025)),
9055 url: Some(format!("fuchsia-pkg://{}", "a".repeat(4083))),
9056 startup: Some(fdecl::StartupMode::Lazy),
9057 on_terminate: None,
9058 environment: Some("a".repeat(1025)),
9059 ..Default::default()
9060 }]);
9061 decl
9062 },
9063 result = Err(ErrorList::new(vec![
9064 Error::field_too_long(DeclType::Child, "name"),
9065 Error::field_too_long(DeclType::Child, "url"),
9066 Error::field_too_long(DeclType::Child, "environment"),
9067 Error::invalid_environment(DeclType::Child, "environment", "a".repeat(1025)),
9068 ])),
9069 },
9070 test_validate_child_references_unknown_env => {
9071 input = {
9072 let mut decl = new_component_decl();
9073 decl.children = Some(vec![fdecl::Child{
9074 name: Some("foo".to_string()),
9075 url: Some("fuchsia-pkg://foo".to_string()),
9076 startup: Some(fdecl::StartupMode::Lazy),
9077 on_terminate: None,
9078 environment: Some("test_env".to_string()),
9079 ..Default::default()
9080 }]);
9081 decl
9082 },
9083 result = Err(ErrorList::new(vec![
9084 Error::invalid_environment(DeclType::Child, "environment", "test_env"),
9085 ])),
9086 },
9087
9088 test_validate_collections_empty => {
9090 input = {
9091 let mut decl = new_component_decl();
9092 decl.collections = Some(vec![fdecl::Collection{
9093 name: None,
9094 durability: None,
9095 environment: None,
9096 allowed_offers: None,
9097 allow_long_names: None,
9098 ..Default::default()
9099 }]);
9100 decl
9101 },
9102 result = Err(ErrorList::new(vec![
9103 Error::missing_field(DeclType::Collection, "name"),
9104 Error::missing_field(DeclType::Collection, "durability"),
9105 ])),
9106 },
9107 test_validate_collections_invalid_identifiers => {
9108 input = {
9109 let mut decl = new_component_decl();
9110 decl.collections = Some(vec![fdecl::Collection{
9111 name: Some("^bad".to_string()),
9112 durability: Some(fdecl::Durability::Transient),
9113 environment: None,
9114 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
9115 allow_long_names: None,
9116 ..Default::default()
9117 }]);
9118 decl
9119 },
9120 result = Err(ErrorList::new(vec![
9121 Error::invalid_field(DeclType::Collection, "name"),
9122 ])),
9123 },
9124 test_validate_collections_long_identifiers => {
9125 input = {
9126 let mut decl = new_component_decl();
9127 decl.collections = Some(vec![fdecl::Collection{
9128 name: Some("a".repeat(1025)),
9129 durability: Some(fdecl::Durability::Transient),
9130 environment: None,
9131 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
9132 allow_long_names: None,
9133 ..Default::default()
9134 }]);
9135 decl
9136 },
9137 result = Err(ErrorList::new(vec![
9138 Error::field_too_long(DeclType::Collection, "name"),
9139 ])),
9140 },
9141 test_validate_collection_references_unknown_env => {
9142 input = {
9143 let mut decl = new_component_decl();
9144 decl.collections = Some(vec![fdecl::Collection {
9145 name: Some("foo".to_string()),
9146 durability: Some(fdecl::Durability::Transient),
9147 environment: Some("test_env".to_string()),
9148 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
9149 allow_long_names: None,
9150 ..Default::default()
9151 }]);
9152 decl
9153 },
9154 result = Err(ErrorList::new(vec![
9155 Error::invalid_environment(DeclType::Collection, "environment", "test_env"),
9156 ])),
9157 },
9158
9159 test_validate_capabilities_empty => {
9161 input = {
9162 let mut decl = new_component_decl();
9163 decl.capabilities = Some(vec![
9164 fdecl::Capability::Service(fdecl::Service {
9165 name: None,
9166 source_path: None,
9167 ..Default::default()
9168 }),
9169 fdecl::Capability::Protocol(fdecl::Protocol {
9170 name: None,
9171 source_path: None,
9172 ..Default::default()
9173 }),
9174 fdecl::Capability::Directory(fdecl::Directory {
9175 name: None,
9176 source_path: None,
9177 rights: None,
9178 ..Default::default()
9179 }),
9180 fdecl::Capability::Storage(fdecl::Storage {
9181 name: None,
9182 source: None,
9183 backing_dir: None,
9184 subdir: None,
9185 storage_id: None,
9186 ..Default::default()
9187 }),
9188 fdecl::Capability::Runner(fdecl::Runner {
9189 name: None,
9190 source_path: None,
9191 ..Default::default()
9192 }),
9193 fdecl::Capability::Resolver(fdecl::Resolver {
9194 name: None,
9195 source_path: None,
9196 ..Default::default()
9197 }),
9198 fdecl::Capability::Dictionary(fdecl::Dictionary {
9199 ..Default::default()
9200 }),
9201 ]);
9202 decl
9203 },
9204 result = Err(ErrorList::new(vec![
9205 Error::missing_field(DeclType::Dictionary, "name"),
9206 Error::missing_field(DeclType::Service, "name"),
9207 Error::missing_field(DeclType::Service, "source_path"),
9208 Error::missing_field(DeclType::Protocol, "name"),
9209 Error::missing_field(DeclType::Protocol, "source_path"),
9210 Error::missing_field(DeclType::Directory, "name"),
9211 Error::missing_field(DeclType::Directory, "source_path"),
9212 Error::missing_field(DeclType::Directory, "rights"),
9213 Error::missing_field(DeclType::Storage, "source"),
9214 Error::missing_field(DeclType::Storage, "name"),
9215 Error::missing_field(DeclType::Storage, "storage_id"),
9216 Error::missing_field(DeclType::Storage, "backing_dir"),
9217 Error::missing_field(DeclType::Runner, "name"),
9218 Error::missing_field(DeclType::Runner, "source_path"),
9219 Error::missing_field(DeclType::Resolver, "name"),
9220 Error::missing_field(DeclType::Resolver, "source_path"),
9221 ])),
9222 },
9223 test_validate_capabilities_invalid_identifiers => {
9224 input = {
9225 let mut decl = new_component_decl();
9226 decl.capabilities = Some(vec![
9227 fdecl::Capability::Service(fdecl::Service {
9228 name: Some("^bad".to_string()),
9229 source_path: Some("&bad".to_string()),
9230 ..Default::default()
9231 }),
9232 fdecl::Capability::Protocol(fdecl::Protocol {
9233 name: Some("^bad".to_string()),
9234 source_path: Some("&bad".to_string()),
9235 ..Default::default()
9236 }),
9237 fdecl::Capability::Directory(fdecl::Directory {
9238 name: Some("^bad".to_string()),
9239 source_path: Some("&bad".to_string()),
9240 rights: Some(fio::Operations::CONNECT),
9241 ..Default::default()
9242 }),
9243 fdecl::Capability::Storage(fdecl::Storage {
9244 name: Some("^bad".to_string()),
9245 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9246 name: "/bad".to_string()
9247 })),
9248 backing_dir: Some("&bad".to_string()),
9249 subdir: None,
9250 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9251 ..Default::default()
9252 }),
9253 fdecl::Capability::Runner(fdecl::Runner {
9254 name: Some("^bad".to_string()),
9255 source_path: Some("&bad".to_string()),
9256 ..Default::default()
9257 }),
9258 fdecl::Capability::Resolver(fdecl::Resolver {
9259 name: Some("^bad".to_string()),
9260 source_path: Some("&bad".to_string()),
9261 ..Default::default()
9262 }),
9263 fdecl::Capability::Dictionary(fdecl::Dictionary {
9264 name: Some("^bad".to_string()),
9265 ..Default::default()
9266 }),
9267 ]);
9268 decl
9269 },
9270 result = Err(ErrorList::new(vec![
9271 Error::invalid_field(DeclType::Dictionary, "name"),
9272 Error::invalid_field(DeclType::Service, "name"),
9273 Error::invalid_field(DeclType::Service, "source_path"),
9274 Error::invalid_field(DeclType::Protocol, "name"),
9275 Error::invalid_field(DeclType::Protocol, "source_path"),
9276 Error::invalid_field(DeclType::Directory, "name"),
9277 Error::invalid_field(DeclType::Directory, "source_path"),
9278 Error::invalid_field(DeclType::Storage, "source"),
9279 Error::invalid_field(DeclType::Storage, "name"),
9280 Error::invalid_field(DeclType::Storage, "backing_dir"),
9281 Error::invalid_field(DeclType::Runner, "name"),
9282 Error::invalid_field(DeclType::Runner, "source_path"),
9283 Error::invalid_field(DeclType::Resolver, "name"),
9284 Error::invalid_field(DeclType::Resolver, "source_path"),
9285 ])),
9286 },
9287 test_validate_capabilities_invalid_child => {
9288 input = {
9289 let mut decl = new_component_decl();
9290 decl.capabilities = Some(vec![
9291 fdecl::Capability::Storage(fdecl::Storage {
9292 name: Some("foo".to_string()),
9293 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9294 name: "invalid".to_string(),
9295 })),
9296 backing_dir: Some("foo".to_string()),
9297 subdir: None,
9298 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9299 ..Default::default()
9300 }),
9301 ]);
9302 decl
9303 },
9304 result = Err(ErrorList::new(vec![
9305 Error::invalid_field(DeclType::Storage, "source"),
9306 ])),
9307 },
9308 test_validate_capabilities_long_identifiers => {
9309 input = {
9310 let mut decl = new_component_decl();
9311 decl.capabilities = Some(vec![
9312 fdecl::Capability::Service(fdecl::Service {
9313 name: Some("a".repeat(256)),
9314 source_path: Some("/c".repeat(2048)),
9315 ..Default::default()
9316 }),
9317 fdecl::Capability::Protocol(fdecl::Protocol {
9318 name: Some("a".repeat(256)),
9319 source_path: Some("/c".repeat(2048)),
9320 ..Default::default()
9321 }),
9322 fdecl::Capability::Directory(fdecl::Directory {
9323 name: Some("a".repeat(256)),
9324 source_path: Some("/c".repeat(2048)),
9325 rights: Some(fio::Operations::CONNECT),
9326 ..Default::default()
9327 }),
9328 fdecl::Capability::Storage(fdecl::Storage {
9329 name: Some("a".repeat(256)),
9330 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
9331 name: "b".repeat(256),
9332 collection: None,
9333 })),
9334 backing_dir: Some(format!("{}", "c".repeat(256))),
9335 subdir: None,
9336 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9337 ..Default::default()
9338 }),
9339 fdecl::Capability::Runner(fdecl::Runner {
9340 name: Some("a".repeat(256)),
9341 source_path: Some("/c".repeat(2048)),
9342 ..Default::default()
9343 }),
9344 fdecl::Capability::Resolver(fdecl::Resolver {
9345 name: Some("a".repeat(256)),
9346 source_path: Some("/c".repeat(2048)),
9347 ..Default::default()
9348 }),
9349 fdecl::Capability::Dictionary(fdecl::Dictionary {
9350 name: Some("a".repeat(256)),
9351 ..Default::default()
9352 }),
9353 ]);
9354 decl
9355 },
9356 result = Err(ErrorList::new(vec![
9357 Error::field_too_long(DeclType::Dictionary, "name"),
9358 Error::field_too_long(DeclType::Service, "name"),
9359 Error::field_too_long(DeclType::Service, "source_path"),
9360 Error::field_too_long(DeclType::Protocol, "name"),
9361 Error::field_too_long(DeclType::Protocol, "source_path"),
9362 Error::field_too_long(DeclType::Directory, "name"),
9363 Error::field_too_long(DeclType::Directory, "source_path"),
9364 Error::field_too_long(DeclType::Storage, "source.child.name"),
9365 Error::field_too_long(DeclType::Storage, "name"),
9366 Error::field_too_long(DeclType::Storage, "backing_dir"),
9367 Error::field_too_long(DeclType::Runner, "name"),
9368 Error::field_too_long(DeclType::Runner, "source_path"),
9369 Error::field_too_long(DeclType::Resolver, "name"),
9370 Error::field_too_long(DeclType::Resolver, "source_path"),
9371 ])),
9372 },
9373 test_validate_capabilities_duplicate_name => {
9374 input = {
9375 let mut decl = new_component_decl();
9376 decl.capabilities = Some(vec![
9377 fdecl::Capability::Service(fdecl::Service {
9378 name: Some("service".to_string()),
9379 source_path: Some("/service".to_string()),
9380 ..Default::default()
9381 }),
9382 fdecl::Capability::Service(fdecl::Service {
9383 name: Some("service".to_string()),
9384 source_path: Some("/service".to_string()),
9385 ..Default::default()
9386 }),
9387 fdecl::Capability::Protocol(fdecl::Protocol {
9388 name: Some("protocol".to_string()),
9389 source_path: Some("/protocol".to_string()),
9390 ..Default::default()
9391 }),
9392 fdecl::Capability::Protocol(fdecl::Protocol {
9393 name: Some("protocol".to_string()),
9394 source_path: Some("/protocol".to_string()),
9395 ..Default::default()
9396 }),
9397 fdecl::Capability::Directory(fdecl::Directory {
9398 name: Some("directory".to_string()),
9399 source_path: Some("/directory".to_string()),
9400 rights: Some(fio::Operations::CONNECT),
9401 ..Default::default()
9402 }),
9403 fdecl::Capability::Directory(fdecl::Directory {
9404 name: Some("directory".to_string()),
9405 source_path: Some("/directory".to_string()),
9406 rights: Some(fio::Operations::CONNECT),
9407 ..Default::default()
9408 }),
9409 fdecl::Capability::Storage(fdecl::Storage {
9410 name: Some("storage".to_string()),
9411 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
9412 backing_dir: Some("directory".to_string()),
9413 subdir: None,
9414 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9415 ..Default::default()
9416 }),
9417 fdecl::Capability::Storage(fdecl::Storage {
9418 name: Some("storage".to_string()),
9419 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
9420 backing_dir: Some("directory".to_string()),
9421 subdir: None,
9422 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9423 ..Default::default()
9424 }),
9425 fdecl::Capability::Runner(fdecl::Runner {
9426 name: Some("runner".to_string()),
9427 source_path: Some("/runner".to_string()),
9428 ..Default::default()
9429 }),
9430 fdecl::Capability::Runner(fdecl::Runner {
9431 name: Some("runner".to_string()),
9432 source_path: Some("/runner".to_string()),
9433 ..Default::default()
9434 }),
9435 fdecl::Capability::Resolver(fdecl::Resolver {
9436 name: Some("resolver".to_string()),
9437 source_path: Some("/resolver".to_string()),
9438 ..Default::default()
9439 }),
9440 fdecl::Capability::Resolver(fdecl::Resolver {
9441 name: Some("resolver".to_string()),
9442 source_path: Some("/resolver".to_string()),
9443 ..Default::default()
9444 }),
9445 fdecl::Capability::Dictionary(fdecl::Dictionary {
9446 name: Some("dictionary".to_string()),
9447 ..Default::default()
9448 }),
9449 fdecl::Capability::Dictionary(fdecl::Dictionary {
9450 name: Some("dictionary".to_string()),
9451 ..Default::default()
9452 }),
9453 ]);
9454 decl
9455 },
9456 result = Err(ErrorList::new(vec![
9457 Error::duplicate_field(DeclType::Dictionary, "name", "dictionary"),
9458 Error::duplicate_field(DeclType::Service, "name", "service"),
9459 Error::duplicate_field(DeclType::Protocol, "name", "protocol"),
9460 Error::duplicate_field(DeclType::Directory, "name", "directory"),
9461 Error::duplicate_field(DeclType::Storage, "name", "storage"),
9462 Error::duplicate_field(DeclType::Runner, "name", "runner"),
9463 Error::duplicate_field(DeclType::Resolver, "name", "resolver"),
9464 ])),
9465 },
9466 test_validate_invalid_service_aggregation_conflicting_filter => {
9467 input = {
9468 let mut decl = new_component_decl();
9469 decl.offers = Some(vec![
9470 fdecl::Offer::Service(fdecl::OfferService {
9471 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9472 name: "coll_a".to_string()
9473 })),
9474 source_name: Some("fuchsia.logger.Log".to_string()),
9475 target: Some(fdecl::Ref::Child(
9476 fdecl::ChildRef {
9477 name: "child_c".to_string(),
9478 collection: None,
9479 }
9480 )),
9481 target_name: Some("fuchsia.logger.Log1".to_string()),
9482 source_instance_filter: Some(vec!["default".to_string()]),
9483 ..Default::default()
9484 }),
9485 fdecl::Offer::Service(fdecl::OfferService {
9486 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9487 name: "coll_b".to_string()
9488 })),
9489 source_name: Some("fuchsia.logger.Log".to_string()),
9490 target: Some(fdecl::Ref::Child(
9491 fdecl::ChildRef {
9492 name: "child_c".to_string(),
9493 collection: None,
9494 }
9495 )),
9496 target_name: Some("fuchsia.logger.Log1".to_string()),
9497 source_instance_filter: Some(vec!["default".to_string()]),
9498 ..Default::default()
9499 }),
9500 ]);
9501 decl.collections = Some(vec![
9502 fdecl::Collection {
9503 name: Some("coll_a".to_string()),
9504 durability: Some(fdecl::Durability::Transient),
9505 ..Default::default()
9506 },
9507 fdecl::Collection {
9508 name: Some("coll_b".to_string()),
9509 durability: Some(fdecl::Durability::Transient),
9510 ..Default::default()
9511 },
9512 ]);
9513 decl.children = Some(vec![
9514 fdecl::Child {
9515 name: Some("child_c".to_string()),
9516 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9517 startup: Some(fdecl::StartupMode::Lazy),
9518 ..Default::default()
9519 },
9520 ]);
9521 decl
9522 },
9523 result = Err(ErrorList::new(vec![
9524 Error::invalid_aggregate_offer("Conflicting source_instance_filter in aggregate \
9525 service offer, instance_name 'default' seen in filter lists multiple times"),
9526 ])),
9527 },
9528
9529 test_validate_invalid_service_aggregation_conflicting_source_name => {
9530 input = {
9531 let mut decl = new_component_decl();
9532 decl.offers = Some(vec![
9533 fdecl::Offer::Service(fdecl::OfferService {
9534 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9535 name: "coll_a".into()
9536 })),
9537 source_name: Some("fuchsia.logger.Log".to_string()),
9538 target: Some(fdecl::Ref::Child(
9539 fdecl::ChildRef {
9540 name: "child_c".to_string(),
9541 collection: None,
9542 }
9543 )),
9544 target_name: Some("fuchsia.logger.Log2".to_string()),
9545 source_instance_filter: Some(vec!["default2".to_string()]),
9546 ..Default::default()
9547 }),
9548 fdecl::Offer::Service(fdecl::OfferService {
9549 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9550 name: "coll_b".into()
9551 })),
9552 source_name: Some("fuchsia.logger.LogAlt".to_string()),
9553 target: Some(fdecl::Ref::Child(
9554 fdecl::ChildRef {
9555 name: "child_c".to_string(),
9556 collection: None,
9557 }
9558 )),
9559 target_name: Some("fuchsia.logger.Log2".to_string()),
9560 source_instance_filter: Some(vec!["default".to_string()]),
9561 ..Default::default()
9562 })
9563 ]);
9564 decl.collections = Some(vec![
9565 fdecl::Collection {
9566 name: Some("coll_a".to_string()),
9567 durability: Some(fdecl::Durability::Transient),
9568 ..Default::default()
9569 },
9570 fdecl::Collection {
9571 name: Some("coll_b".to_string()),
9572 durability: Some(fdecl::Durability::Transient),
9573 ..Default::default()
9574 },
9575 ]);
9576 decl.children = Some(vec![
9577 fdecl::Child {
9578 name: Some("child_c".to_string()),
9579 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9580 startup: Some(fdecl::StartupMode::Lazy),
9581 on_terminate: None,
9582 environment: None,
9583 ..Default::default()
9584 },
9585 ]);
9586 decl
9587 },
9588 result = Err(ErrorList::new(vec![
9589 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."),
9590 ])),
9591 },
9592
9593 test_validate_resolvers_missing_from_offer => {
9594 input = {
9595 let mut decl = new_component_decl();
9596 decl.offers = Some(vec![fdecl::Offer::Resolver(fdecl::OfferResolver {
9597 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
9598 source_name: Some("a".to_string()),
9599 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
9600 target_name: Some("a".to_string()),
9601 ..Default::default()
9602 })]);
9603 decl.children = Some(vec![fdecl::Child {
9604 name: Some("child".to_string()),
9605 url: Some("test:///child".to_string()),
9606 startup: Some(fdecl::StartupMode::Eager),
9607 on_terminate: None,
9608 environment: None,
9609 ..Default::default()
9610 }]);
9611 decl
9612 },
9613 result = Err(ErrorList::new(vec![
9614 Error::invalid_capability(DeclType::OfferResolver, "source", "a"),
9615 ])),
9616 },
9617 test_validate_resolvers_missing_from_expose => {
9618 input = {
9619 let mut decl = new_component_decl();
9620 decl.exposes = Some(vec![fdecl::Expose::Resolver(fdecl::ExposeResolver {
9621 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
9622 source_name: Some("a".to_string()),
9623 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
9624 target_name: Some("a".to_string()),
9625 ..Default::default()
9626 })]);
9627 decl
9628 },
9629 result = Err(ErrorList::new(vec![
9630 Error::invalid_capability(DeclType::ExposeResolver, "source", "a"),
9631 ])),
9632 },
9633
9634 test_validate_config_missing_config => {
9635 input = {
9636 let mut decl = new_component_decl();
9637 decl.config = Some(fdecl::ConfigSchema{
9638 fields: None,
9639 checksum: None,
9640 value_source: None,
9641 ..Default::default()
9642 });
9643 decl
9644 },
9645 result = Err(ErrorList::new(vec![
9646 Error::missing_field(DeclType::ConfigSchema, "fields"),
9647 Error::missing_field(DeclType::ConfigSchema, "checksum"),
9648 Error::missing_field(DeclType::ConfigSchema, "value_source"),
9649 ])),
9650 },
9651
9652 test_validate_config_missing_config_field => {
9653 input = {
9654 let mut decl = new_component_decl();
9655 decl.config = Some(fdecl::ConfigSchema{
9656 fields: Some(vec![
9657 fdecl::ConfigField {
9658 key: None,
9659 type_: None,
9660 ..Default::default()
9661 }
9662 ]),
9663 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9664 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9665 ..Default::default()
9666 });
9667 decl
9668 },
9669 result = Err(ErrorList::new(vec![
9670 Error::missing_field(DeclType::ConfigField, "key"),
9671 Error::missing_field(DeclType::ConfigField, "value_type"),
9672 ])),
9673 },
9674
9675 test_validate_config_bool => {
9676 input = {
9677 let mut decl = new_component_decl();
9678 decl.config = Some(fdecl::ConfigSchema{
9679 fields: Some(vec![
9680 fdecl::ConfigField {
9681 key: Some("test".to_string()),
9682 type_: Some(fdecl::ConfigType {
9683 layout: fdecl::ConfigTypeLayout::Bool,
9684 parameters: Some(vec![]),
9685 constraints: vec![]
9686 }),
9687 ..Default::default()
9688 }
9689 ]),
9690 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9691 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9692 ..Default::default()
9693 });
9694 decl
9695 },
9696 result = Ok(()),
9697 },
9698
9699 test_validate_config_bool_extra_constraint => {
9700 input = {
9701 let mut decl = new_component_decl();
9702 decl.config = Some(fdecl::ConfigSchema{
9703 fields: Some(vec![
9704 fdecl::ConfigField {
9705 key: Some("test".to_string()),
9706 type_: Some(fdecl::ConfigType {
9707 layout: fdecl::ConfigTypeLayout::Bool,
9708 parameters: Some(vec![]),
9709 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9710 }),
9711 ..Default::default()
9712 }
9713 ]),
9714 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9715 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9716 ..Default::default()
9717 });
9718 decl
9719 },
9720 result = Err(ErrorList::new(vec![
9721 Error::extraneous_field(DeclType::ConfigType, "constraints")
9722 ])),
9723 },
9724
9725 test_validate_config_bool_missing_parameters => {
9726 input = {
9727 let mut decl = new_component_decl();
9728 decl.config = Some(fdecl::ConfigSchema{
9729 fields: Some(vec![
9730 fdecl::ConfigField {
9731 key: Some("test".to_string()),
9732 type_: Some(fdecl::ConfigType {
9733 layout: fdecl::ConfigTypeLayout::Bool,
9734 parameters: None,
9735 constraints: vec![]
9736 }),
9737 ..Default::default()
9738 }
9739 ]),
9740 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9741 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9742 ..Default::default()
9743 });
9744 decl
9745 },
9746 result = Err(ErrorList::new(vec![
9747 Error::missing_field(DeclType::ConfigType, "parameters")
9748 ])),
9749 },
9750
9751 test_validate_config_string => {
9752 input = {
9753 let mut decl = new_component_decl();
9754 decl.config = Some(fdecl::ConfigSchema{
9755 fields: Some(vec![
9756 fdecl::ConfigField {
9757 key: Some("test".to_string()),
9758 type_: Some(fdecl::ConfigType {
9759 layout: fdecl::ConfigTypeLayout::String,
9760 parameters: Some(vec![]),
9761 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9762 }),
9763 ..Default::default()
9764 }
9765 ]),
9766 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9767 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9768 ..Default::default()
9769 });
9770 decl
9771 },
9772 result = Ok(()),
9773 },
9774
9775 test_validate_config_string_missing_parameter => {
9776 input = {
9777 let mut decl = new_component_decl();
9778 decl.config = Some(fdecl::ConfigSchema{
9779 fields: Some(vec![
9780 fdecl::ConfigField {
9781 key: Some("test".to_string()),
9782 type_: Some(fdecl::ConfigType {
9783 layout: fdecl::ConfigTypeLayout::String,
9784 parameters: None,
9785 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9786 }),
9787 ..Default::default()
9788 }
9789 ]),
9790 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9791 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9792 ..Default::default()
9793 });
9794 decl
9795 },
9796 result = Err(ErrorList::new(vec![
9797 Error::missing_field(DeclType::ConfigType, "parameters")
9798 ])),
9799 },
9800
9801 test_validate_config_string_missing_constraint => {
9802 input = {
9803 let mut decl = new_component_decl();
9804 decl.config = Some(fdecl::ConfigSchema{
9805 fields: Some(vec![
9806 fdecl::ConfigField {
9807 key: Some("test".to_string()),
9808 type_: Some(fdecl::ConfigType {
9809 layout: fdecl::ConfigTypeLayout::String,
9810 parameters: Some(vec![]),
9811 constraints: vec![]
9812 }),
9813 ..Default::default()
9814 }
9815 ]),
9816 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9817 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9818 ..Default::default()
9819 });
9820 decl
9821 },
9822 result = Err(ErrorList::new(vec![
9823 Error::missing_field(DeclType::ConfigType, "constraints")
9824 ])),
9825 },
9826
9827 test_validate_config_string_extra_constraint => {
9828 input = {
9829 let mut decl = new_component_decl();
9830 decl.config = Some(fdecl::ConfigSchema{
9831 fields: Some(vec![
9832 fdecl::ConfigField {
9833 key: Some("test".to_string()),
9834 type_: Some(fdecl::ConfigType {
9835 layout: fdecl::ConfigTypeLayout::String,
9836 parameters: Some(vec![]),
9837 constraints: vec![fdecl::LayoutConstraint::MaxSize(10), fdecl::LayoutConstraint::MaxSize(10)]
9838 }),
9839 ..Default::default()
9840 }
9841 ]),
9842 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9843 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9844 ..Default::default()
9845 });
9846 decl
9847 },
9848 result = Err(ErrorList::new(vec![
9849 Error::extraneous_field(DeclType::ConfigType, "constraints")
9850 ])),
9851 },
9852
9853 test_validate_config_vector_bool => {
9854 input = {
9855 let mut decl = new_component_decl();
9856 decl.config = Some(fdecl::ConfigSchema{
9857 fields: Some(vec![
9858 fdecl::ConfigField {
9859 key: Some("test".to_string()),
9860 type_: Some(fdecl::ConfigType {
9861 layout: fdecl::ConfigTypeLayout::Vector,
9862 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9863 layout: fdecl::ConfigTypeLayout::Bool,
9864 parameters: Some(vec![]),
9865 constraints: vec![],
9866 })]),
9867 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9868 }),
9869 ..Default::default()
9870 }
9871 ]),
9872 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9873 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9874 ..Default::default()
9875 });
9876 decl
9877 },
9878 result = Ok(()),
9879 },
9880
9881 test_validate_config_vector_extra_parameter => {
9882 input = {
9883 let mut decl = new_component_decl();
9884 decl.config = Some(fdecl::ConfigSchema{
9885 fields: Some(vec![
9886 fdecl::ConfigField {
9887 key: Some("test".to_string()),
9888 type_: Some(fdecl::ConfigType {
9889 layout: fdecl::ConfigTypeLayout::Vector,
9890 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9891 layout: fdecl::ConfigTypeLayout::Bool,
9892 parameters: Some(vec![]),
9893 constraints: vec![],
9894 }), fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9895 layout: fdecl::ConfigTypeLayout::Uint8,
9896 parameters: Some(vec![]),
9897 constraints: vec![],
9898 })]),
9899 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9900 }),
9901 ..Default::default()
9902 }
9903 ]),
9904 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9905 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9906 ..Default::default()
9907 });
9908 decl
9909 },
9910 result = Err(ErrorList::new(vec![
9911 Error::extraneous_field(DeclType::ConfigType, "parameters")
9912 ])),
9913 },
9914
9915 test_validate_config_vector_missing_parameter => {
9916 input = {
9917 let mut decl = new_component_decl();
9918 decl.config = Some(fdecl::ConfigSchema{
9919 fields: Some(vec![
9920 fdecl::ConfigField {
9921 key: Some("test".to_string()),
9922 type_: Some(fdecl::ConfigType {
9923 layout: fdecl::ConfigTypeLayout::Vector,
9924 parameters: Some(vec![]),
9925 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9926 }),
9927 ..Default::default()
9928 }
9929 ]),
9930 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9931 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9932 ..Default::default()
9933 });
9934 decl
9935 },
9936 result = Err(ErrorList::new(vec![
9937 Error::missing_field(DeclType::ConfigType, "parameters")
9938 ])),
9939 },
9940
9941 test_validate_config_vector_string => {
9942 input = {
9943 let mut decl = new_component_decl();
9944 decl.config = Some(fdecl::ConfigSchema{
9945 fields: Some(vec![
9946 fdecl::ConfigField {
9947 key: Some("test".to_string()),
9948 type_: Some(fdecl::ConfigType {
9949 layout: fdecl::ConfigTypeLayout::Vector,
9950 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9951 layout: fdecl::ConfigTypeLayout::String,
9952 parameters: Some(vec![]),
9953 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9954 })]),
9955 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9956 }),
9957 ..Default::default()
9958 }
9959 ]),
9960 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9961 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9962 ..Default::default()
9963 });
9964 decl
9965 },
9966 result = Ok(()),
9967 },
9968
9969 test_validate_config_vector_vector => {
9970 input = {
9971 let mut decl = new_component_decl();
9972 decl.config = Some(fdecl::ConfigSchema{
9973 fields: Some(vec![
9974 fdecl::ConfigField {
9975 key: Some("test".to_string()),
9976 type_: Some(fdecl::ConfigType {
9977 layout: fdecl::ConfigTypeLayout::Vector,
9978 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9979 layout: fdecl::ConfigTypeLayout::Vector,
9980 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9981 layout: fdecl::ConfigTypeLayout::String,
9982 parameters: Some(vec![]),
9983 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9984 })]),
9985 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9986 })]),
9987 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9988 }),
9989 ..Default::default()
9990 }
9991 ]),
9992 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9993 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9994 ..Default::default()
9995 });
9996 decl
9997 },
9998 result = Err(ErrorList::new(vec![
9999 Error::nested_vector()
10000 ])),
10001 },
10002
10003 test_validate_exposes_invalid_aggregation_different_availability => {
10004 input = {
10005 let mut decl = new_component_decl();
10006 decl.exposes = Some(vec![
10007 fdecl::Expose::Service(fdecl::ExposeService {
10008 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10009 name: "coll_a".into()
10010 })),
10011 source_name: Some("fuchsia.logger.Log".to_string()),
10012 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
10013 target_name: Some("fuchsia.logger.Log".to_string()),
10014 availability: Some(fdecl::Availability::Required),
10015 ..Default::default()
10016 }),
10017 fdecl::Expose::Service(fdecl::ExposeService {
10018 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10019 name: "coll_b".into()
10020 })),
10021 source_name: Some("fuchsia.logger.Log".to_string()),
10022 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
10023 target_name: Some("fuchsia.logger.Log".to_string()),
10024 availability: Some(fdecl::Availability::Optional),
10025 ..Default::default()
10026 })
10027 ]);
10028 decl.collections = Some(vec![
10029 fdecl::Collection {
10030 name: Some("coll_a".to_string()),
10031 durability: Some(fdecl::Durability::Transient),
10032 ..Default::default()
10033 },
10034 fdecl::Collection {
10035 name: Some("coll_b".to_string()),
10036 durability: Some(fdecl::Durability::Transient),
10037 ..Default::default()
10038 },
10039 ]);
10040 decl
10041 },
10042 result = Err(ErrorList::new(vec![
10043 Error::DifferentAvailabilityInAggregation(AvailabilityList(vec![
10044 fdecl::Availability::Required,
10045 fdecl::Availability::Optional,
10046 ]))
10047 ])),
10048 },
10049
10050 test_validate_offers_invalid_aggregation_different_availability => {
10051 input = {
10052 let mut decl = new_component_decl();
10053 decl.offers = Some(vec![
10054 fdecl::Offer::Service(fdecl::OfferService {
10055 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10056 name: "coll_a".into()
10057 })),
10058 source_name: Some("fuchsia.logger.Log".to_string()),
10059 target: Some(fdecl::Ref::Child(
10060 fdecl::ChildRef {
10061 name: "child_c".to_string(),
10062 collection: None,
10063 }
10064 )),
10065 target_name: Some("fuchsia.logger.Log".to_string()),
10066 source_instance_filter: Some(vec!["default".to_string()]),
10067 availability: Some(fdecl::Availability::Required),
10068 ..Default::default()
10069 }),
10070 fdecl::Offer::Service(fdecl::OfferService {
10071 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10072 name: "coll_b".into()
10073 })),
10074 source_name: Some("fuchsia.logger.Log".to_string()),
10075 target: Some(fdecl::Ref::Child(
10076 fdecl::ChildRef {
10077 name: "child_c".to_string(),
10078 collection: None,
10079 }
10080 )),
10081 target_name: Some("fuchsia.logger.Log".to_string()),
10082 source_instance_filter: Some(vec!["a_different_default".to_string()]),
10083 availability: Some(fdecl::Availability::Optional),
10084 ..Default::default()
10085 })
10086 ]);
10087 decl.collections = Some(vec![
10088 fdecl::Collection {
10089 name: Some("coll_a".to_string()),
10090 durability: Some(fdecl::Durability::Transient),
10091 ..Default::default()
10092 },
10093 fdecl::Collection {
10094 name: Some("coll_b".to_string()),
10095 durability: Some(fdecl::Durability::Transient),
10096 ..Default::default()
10097 },
10098 ]);
10099 decl.children = Some(vec![
10100 fdecl::Child {
10101 name: Some("child_c".to_string()),
10102 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
10103 startup: Some(fdecl::StartupMode::Lazy),
10104 on_terminate: None,
10105 environment: None,
10106 ..Default::default()
10107 },
10108 ]);
10109 decl
10110 },
10111 result = Err(ErrorList::new(vec![
10112 Error::DifferentAvailabilityInAggregation(AvailabilityList(vec![
10113 fdecl::Availability::Required,
10114 fdecl::Availability::Optional,
10115 ]))
10116 ])),
10117 },
10118 }
10119
10120 test_validate_capabilities! {
10121 test_validate_capabilities_individually_ok => {
10122 input = vec![
10123 fdecl::Capability::Protocol(fdecl::Protocol {
10124 name: Some("foo_svc".into()),
10125 source_path: Some("/svc/foo".into()),
10126 ..Default::default()
10127 }),
10128 fdecl::Capability::Directory(fdecl::Directory {
10129 name: Some("foo_dir".into()),
10130 source_path: Some("/foo".into()),
10131 rights: Some(fio::Operations::CONNECT),
10132 ..Default::default()
10133 }),
10134 ],
10135 as_builtin = false,
10136 result = Ok(()),
10137 },
10138 test_validate_capabilities_individually_err => {
10139 input = vec![
10140 fdecl::Capability::Protocol(fdecl::Protocol {
10141 name: None,
10142 source_path: None,
10143 ..Default::default()
10144 }),
10145 fdecl::Capability::Directory(fdecl::Directory {
10146 name: None,
10147 source_path: None,
10148 rights: None,
10149 ..Default::default()
10150 }),
10151 ],
10152 as_builtin = false,
10153 result = Err(ErrorList::new(vec![
10154 Error::missing_field(DeclType::Protocol, "name"),
10155 Error::missing_field(DeclType::Protocol, "source_path"),
10156 Error::missing_field(DeclType::Directory, "name"),
10157 Error::missing_field(DeclType::Directory, "source_path"),
10158 Error::missing_field(DeclType::Directory, "rights"),
10159 ])),
10160 },
10161 test_validate_builtin_capabilities_individually_ok => {
10162 input = vec![
10163 fdecl::Capability::Protocol(fdecl::Protocol {
10164 name: Some("foo_protocol".into()),
10165 source_path: None,
10166 ..Default::default()
10167 }),
10168 fdecl::Capability::Directory(fdecl::Directory {
10169 name: Some("foo_dir".into()),
10170 source_path: None,
10171 rights: Some(fio::Operations::CONNECT),
10172 ..Default::default()
10173 }),
10174 fdecl::Capability::Service(fdecl::Service {
10175 name: Some("foo_svc".into()),
10176 source_path: None,
10177 ..Default::default()
10178 }),
10179 fdecl::Capability::Runner(fdecl::Runner {
10180 name: Some("foo_runner".into()),
10181 source_path: None,
10182 ..Default::default()
10183 }),
10184 fdecl::Capability::Resolver(fdecl::Resolver {
10185 name: Some("foo_resolver".into()),
10186 source_path: None,
10187 ..Default::default()
10188 }),
10189 ],
10190 as_builtin = true,
10191 result = Ok(()),
10192 },
10193 test_validate_builtin_capabilities_individually_err => {
10194 input = vec![
10195 fdecl::Capability::Protocol(fdecl::Protocol {
10196 name: None,
10197 source_path: Some("/svc/foo".into()),
10198 ..Default::default()
10199 }),
10200 fdecl::Capability::Directory(fdecl::Directory {
10201 name: None,
10202 source_path: Some("/foo".into()),
10203 rights: None,
10204 ..Default::default()
10205 }),
10206 fdecl::Capability::Service(fdecl::Service {
10207 name: None,
10208 source_path: Some("/svc/foo".into()),
10209 ..Default::default()
10210 }),
10211 fdecl::Capability::Runner(fdecl::Runner {
10212 name: None,
10213 source_path: Some("/foo".into()),
10214 ..Default::default()
10215 }),
10216 fdecl::Capability::Resolver(fdecl::Resolver {
10217 name: None,
10218 source_path: Some("/foo".into()),
10219 ..Default::default()
10220 }),
10221 fdecl::Capability::Storage(fdecl::Storage {
10222 name: None,
10223 ..Default::default()
10224 }),
10225 ],
10226 as_builtin = true,
10227 result = Err(ErrorList::new(vec![
10228 Error::missing_field(DeclType::Protocol, "name"),
10229 Error::extraneous_source_path(DeclType::Protocol, "/svc/foo"),
10230 Error::missing_field(DeclType::Directory, "name"),
10231 Error::extraneous_source_path(DeclType::Directory, "/foo"),
10232 Error::missing_field(DeclType::Directory, "rights"),
10233 Error::missing_field(DeclType::Service, "name"),
10234 Error::extraneous_source_path(DeclType::Service, "/svc/foo"),
10235 Error::missing_field(DeclType::Runner, "name"),
10236 Error::extraneous_source_path(DeclType::Runner, "/foo"),
10237 Error::missing_field(DeclType::Resolver, "name"),
10238 Error::extraneous_source_path(DeclType::Resolver, "/foo"),
10239 Error::CapabilityCannotBeBuiltin(DeclType::Storage),
10240 ])),
10241 },
10242 test_validate_delivery_type_ok => {
10243 input = vec![
10244 fdecl::Capability::Protocol(fdecl::Protocol {
10245 name: Some("foo_svc1".into()),
10246 source_path: Some("/svc/foo1".into()),
10247 ..Default::default()
10248 }),
10249 fdecl::Capability::Protocol(fdecl::Protocol {
10250 name: Some("foo_svc2".into()),
10251 source_path: Some("/svc/foo2".into()),
10252 delivery: Some(fdecl::DeliveryType::Immediate),
10253 ..Default::default()
10254 }),
10255 fdecl::Capability::Protocol(fdecl::Protocol {
10256 name: Some("foo_svc3".into()),
10257 source_path: Some("/svc/foo3".into()),
10258 delivery: Some(fdecl::DeliveryType::OnReadable),
10259 ..Default::default()
10260 }),
10261 ],
10262 as_builtin = false,
10263 result = Ok(()),
10264 },
10265 test_validate_delivery_type_err => {
10266 input = vec![
10267 fdecl::Capability::Protocol(fdecl::Protocol {
10268 name: Some("foo_svc".into()),
10269 source_path: Some("/svc/foo".into()),
10270 delivery: Some(fdecl::DeliveryType::unknown()),
10271 ..Default::default()
10272 }),
10273 ],
10274 as_builtin = false,
10275 result = Err(ErrorList::new(vec![
10276 Error::invalid_field(DeclType::Protocol, "delivery"),
10277 ])),
10278 },
10279 }
10280
10281 fn generate_expose_different_source_and_availability_decl(
10284 new_expose: impl Fn(fdecl::Ref, fdecl::Availability, &str) -> fdecl::Expose,
10285 ) -> fdecl::Component {
10286 let mut decl = new_component_decl();
10287 let child = "child";
10288 decl.children = Some(vec![fdecl::Child {
10289 name: Some(child.to_string()),
10290 url: Some("fuchsia-pkg://fuchsia.com/source#meta/source.cm".to_string()),
10291 startup: Some(fdecl::StartupMode::Lazy),
10292 ..Default::default()
10293 }]);
10294 decl.exposes = Some(vec![
10295 new_expose(
10297 fdecl::Ref::Self_(fdecl::SelfRef {}),
10298 fdecl::Availability::Optional,
10299 "fuchsia.examples.Echo1",
10300 ),
10301 new_expose(
10303 fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
10304 fdecl::Availability::Optional,
10305 "fuchsia.examples.Echo2",
10306 ),
10307 new_expose(
10309 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10310 fdecl::Availability::Optional,
10311 "fuchsia.examples.Echo3",
10312 ),
10313 new_expose(
10315 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10316 fdecl::Availability::Optional,
10317 "fuchsia.examples.Echo4",
10318 ),
10319 new_expose(
10321 fdecl::Ref::Self_(fdecl::SelfRef {}),
10322 fdecl::Availability::Transitional,
10323 "fuchsia.examples.Echo5",
10324 ),
10325 new_expose(
10327 fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
10328 fdecl::Availability::Transitional,
10329 "fuchsia.examples.Echo6",
10330 ),
10331 new_expose(
10333 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10334 fdecl::Availability::Transitional,
10335 "fuchsia.examples.Echo7",
10336 ),
10337 new_expose(
10339 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10340 fdecl::Availability::Transitional,
10341 "fuchsia.examples.Echo8",
10342 ),
10343 new_expose(
10345 fdecl::Ref::Self_(fdecl::SelfRef {}),
10346 fdecl::Availability::SameAsTarget,
10347 "fuchsia.examples.Echo9",
10348 ),
10349 new_expose(
10351 fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
10352 fdecl::Availability::SameAsTarget,
10353 "fuchsia.examples.Echo10",
10354 ),
10355 new_expose(
10357 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10358 fdecl::Availability::SameAsTarget,
10359 "fuchsia.examples.Echo11",
10360 ),
10361 new_expose(
10363 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10364 fdecl::Availability::Required,
10365 "fuchsia.examples.Echo12",
10366 ),
10367 new_expose(
10369 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10370 fdecl::Availability::SameAsTarget,
10371 "fuchsia.examples.Echo13",
10372 ),
10373 ]);
10374 decl
10375 }
10376
10377 fn generate_offer_different_source_and_availability_decl(
10380 new_offer: impl Fn(fdecl::Ref, fdecl::Availability, &str) -> fdecl::Offer,
10381 ) -> fdecl::Component {
10382 let mut decl = new_component_decl();
10383 decl.children = Some(vec![
10384 fdecl::Child {
10385 name: Some("source".to_string()),
10386 url: Some("fuchsia-pkg://fuchsia.com/source#meta/source.cm".to_string()),
10387 startup: Some(fdecl::StartupMode::Lazy),
10388 on_terminate: None,
10389 environment: None,
10390 ..Default::default()
10391 },
10392 fdecl::Child {
10393 name: Some("sink".to_string()),
10394 url: Some("fuchsia-pkg://fuchsia.com/sink#meta/sink.cm".to_string()),
10395 startup: Some(fdecl::StartupMode::Lazy),
10396 on_terminate: None,
10397 environment: None,
10398 ..Default::default()
10399 },
10400 ]);
10401 decl.offers = Some(vec![
10402 new_offer(
10405 fdecl::Ref::Parent(fdecl::ParentRef {}),
10406 fdecl::Availability::Required,
10407 "fuchsia.examples.Echo0",
10408 ),
10409 new_offer(
10410 fdecl::Ref::Parent(fdecl::ParentRef {}),
10411 fdecl::Availability::Optional,
10412 "fuchsia.examples.Echo1",
10413 ),
10414 new_offer(
10415 fdecl::Ref::Parent(fdecl::ParentRef {}),
10416 fdecl::Availability::SameAsTarget,
10417 "fuchsia.examples.Echo2",
10418 ),
10419 new_offer(
10420 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10421 fdecl::Availability::Optional,
10422 "fuchsia.examples.Echo3",
10423 ),
10424 new_offer(
10427 fdecl::Ref::Self_(fdecl::SelfRef {}),
10428 fdecl::Availability::Optional,
10429 "fuchsia.examples.Echo4",
10430 ),
10431 new_offer(
10432 fdecl::Ref::Self_(fdecl::SelfRef {}),
10433 fdecl::Availability::SameAsTarget,
10434 "fuchsia.examples.Echo5",
10435 ),
10436 new_offer(
10437 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10438 fdecl::Availability::Optional,
10439 "fuchsia.examples.Echo6",
10440 ),
10441 new_offer(
10442 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10443 fdecl::Availability::SameAsTarget,
10444 "fuchsia.examples.Echo7",
10445 ),
10446 new_offer(
10447 fdecl::Ref::Child(fdecl::ChildRef { name: "source".to_string(), collection: None }),
10448 fdecl::Availability::Optional,
10449 "fuchsia.examples.Echo8",
10450 ),
10451 new_offer(
10452 fdecl::Ref::Child(fdecl::ChildRef { name: "source".to_string(), collection: None }),
10453 fdecl::Availability::SameAsTarget,
10454 "fuchsia.examples.Echo9",
10455 ),
10456 new_offer(
10458 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10459 fdecl::Availability::Required,
10460 "fuchsia.examples.Echo10",
10461 ),
10462 new_offer(
10463 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10464 fdecl::Availability::SameAsTarget,
10465 "fuchsia.examples.Echo11",
10466 ),
10467 ]);
10468 decl
10469 }
10470
10471 #[test]
10472 fn test_validate_dynamic_offers_empty() {
10473 assert_eq!(validate_dynamic_offers(vec![], &vec![], &fdecl::Component::default()), Ok(()));
10474 }
10475
10476 #[test]
10477 fn test_validate_dynamic_offers_okay() {
10478 assert_eq!(
10479 validate_dynamic_offers(
10480 vec![],
10481 &vec![
10482 fdecl::Offer::Protocol(fdecl::OfferProtocol {
10483 dependency_type: Some(fdecl::DependencyType::Strong),
10484 source: Some(fdecl::Ref::Self_(fdecl::SelfRef)),
10485 source_name: Some("thing".to_string()),
10486 target_name: Some("thing".repeat(26)),
10487 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10488 name: "foo".to_string(),
10489 collection: Some("foo".to_string()),
10490 })),
10491 ..Default::default()
10492 }),
10493 fdecl::Offer::Service(fdecl::OfferService {
10494 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10495 source_name: Some("thang".repeat(26)),
10496 target_name: Some("thang".repeat(26)),
10497 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10498 name: "foo".to_string(),
10499 collection: Some("foo".to_string()),
10500 })),
10501 ..Default::default()
10502 }),
10503 fdecl::Offer::Directory(fdecl::OfferDirectory {
10504 dependency_type: Some(fdecl::DependencyType::Strong),
10505 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10506 source_name: Some("thung1".repeat(26)),
10507 target_name: Some("thung1".repeat(26)),
10508 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10509 name: "foo".to_string(),
10510 collection: Some("foo".to_string()),
10511 })),
10512 ..Default::default()
10513 }),
10514 fdecl::Offer::Storage(fdecl::OfferStorage {
10515 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10516 source_name: Some("thung2".repeat(26)),
10517 target_name: Some("thung2".repeat(26)),
10518 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10519 name: "foo".to_string(),
10520 collection: Some("foo".to_string()),
10521 })),
10522 ..Default::default()
10523 }),
10524 fdecl::Offer::Runner(fdecl::OfferRunner {
10525 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10526 source_name: Some("thung3".repeat(26)),
10527 target_name: Some("thung3".repeat(26)),
10528 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10529 name: "foo".to_string(),
10530 collection: Some("foo".to_string()),
10531 })),
10532 ..Default::default()
10533 }),
10534 fdecl::Offer::Resolver(fdecl::OfferResolver {
10535 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10536 source_name: Some("thung4".repeat(26)),
10537 target_name: Some("thung4".repeat(26)),
10538 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10539 name: "foo".to_string(),
10540 collection: Some("foo".to_string()),
10541 })),
10542 ..Default::default()
10543 }),
10544 ],
10545 &fdecl::Component {
10546 capabilities: Some(vec![fdecl::Capability::Protocol(fdecl::Protocol {
10547 name: Some("thing".to_string()),
10548 source_path: Some("/svc/foo".into()),
10549 ..Default::default()
10550 }),]),
10551 ..Default::default()
10552 }
10553 ),
10554 Ok(())
10555 );
10556 }
10557
10558 #[test]
10559 fn test_validate_dynamic_offers_valid_service_aggregation() {
10560 assert_eq!(
10561 validate_dynamic_offers(
10562 vec![],
10563 &vec![
10564 fdecl::Offer::Service(fdecl::OfferService {
10565 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10566 name: "child_a".to_string(),
10567 collection: None
10568 })),
10569 source_name: Some("fuchsia.logger.Log".to_string()),
10570 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10571 name: "child_c".to_string(),
10572 collection: None,
10573 })),
10574 target_name: Some("fuchsia.logger.Log".to_string()),
10575 source_instance_filter: Some(vec!["default".to_string()]),
10576 ..Default::default()
10577 }),
10578 fdecl::Offer::Service(fdecl::OfferService {
10579 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10580 name: "child_b".to_string(),
10581 collection: None
10582 })),
10583 source_name: Some("fuchsia.logger.Log".to_string()),
10584 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10585 name: "child_c".to_string(),
10586 collection: None,
10587 })),
10588 target_name: Some("fuchsia.logger.Log".to_string()),
10589 source_instance_filter: Some(vec!["a_different_default".to_string()]),
10590 ..Default::default()
10591 })
10592 ],
10593 &fdecl::Component {
10594 children: Some(vec![
10595 fdecl::Child {
10596 name: Some("child_a".to_string()),
10597 url: Some(
10598 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10599 .to_string()
10600 ),
10601 startup: Some(fdecl::StartupMode::Lazy),
10602 on_terminate: None,
10603 environment: None,
10604 ..Default::default()
10605 },
10606 fdecl::Child {
10607 name: Some("child_b".to_string()),
10608 url: Some(
10609 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10610 .to_string()
10611 ),
10612 startup: Some(fdecl::StartupMode::Lazy),
10613 on_terminate: None,
10614 environment: None,
10615 ..Default::default()
10616 },
10617 fdecl::Child {
10618 name: Some("child_c".to_string()),
10619 url: Some(
10620 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10621 .to_string()
10622 ),
10623 startup: Some(fdecl::StartupMode::Lazy),
10624 on_terminate: None,
10625 environment: None,
10626 ..Default::default()
10627 },
10628 ]),
10629 ..Default::default()
10630 }
10631 ),
10632 Ok(())
10633 );
10634 }
10635
10636 #[test]
10637 fn test_validate_dynamic_service_aggregation_missing_filter() {
10638 assert_eq!(
10639 validate_dynamic_offers(
10640 vec![],
10641 &vec![
10642 fdecl::Offer::Service(fdecl::OfferService {
10643 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10644 name: "child_a".to_string(),
10645 collection: None
10646 })),
10647 source_name: Some("fuchsia.logger.Log".to_string()),
10648 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10649 name: "child_c".to_string(),
10650 collection: None,
10651 })),
10652 target_name: Some("fuchsia.logger.Log".to_string()),
10653 source_instance_filter: Some(vec!["default".to_string()]),
10654 ..Default::default()
10655 }),
10656 fdecl::Offer::Service(fdecl::OfferService {
10657 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10658 name: "child_b".to_string(),
10659 collection: None
10660 })),
10661 source_name: Some("fuchsia.logger.Log".to_string()),
10662 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10663 name: "child_c".to_string(),
10664 collection: None,
10665 })),
10666 target_name: Some("fuchsia.logger.Log".to_string()),
10667 source_instance_filter: None,
10668 ..Default::default()
10669 }),
10670 ],
10671 &fdecl::Component {
10672 children: Some(vec![
10673 fdecl::Child {
10674 name: Some("child_a".to_string()),
10675 url: Some(
10676 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10677 .to_string()
10678 ),
10679 startup: Some(fdecl::StartupMode::Lazy),
10680 ..Default::default()
10681 },
10682 fdecl::Child {
10683 name: Some("child_b".to_string()),
10684 url: Some(
10685 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10686 .to_string()
10687 ),
10688 startup: Some(fdecl::StartupMode::Lazy),
10689 ..Default::default()
10690 },
10691 fdecl::Child {
10692 name: Some("child_c".to_string()),
10693 url: Some(
10694 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10695 .to_string()
10696 ),
10697 startup: Some(fdecl::StartupMode::Lazy),
10698 ..Default::default()
10699 },
10700 ]),
10701 ..Default::default()
10702 },
10703 ),
10704 Err(ErrorList::new(vec![Error::invalid_aggregate_offer(
10705 "source_instance_filter must be set for dynamic aggregate service offers"
10706 ),]))
10707 );
10708 }
10709
10710 #[test]
10711 fn test_validate_dynamic_offers_omit_target() {
10712 assert_eq!(
10713 validate_dynamic_offers(
10714 vec![],
10715 &vec![
10716 fdecl::Offer::Protocol(fdecl::OfferProtocol {
10717 dependency_type: Some(fdecl::DependencyType::Strong),
10718 source: Some(fdecl::Ref::Self_(fdecl::SelfRef)),
10719 source_name: Some("thing".to_string()),
10720 target_name: Some("thing".to_string()),
10721 ..Default::default()
10722 }),
10723 fdecl::Offer::Service(fdecl::OfferService {
10724 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10725 source_name: Some("thang".to_string()),
10726 target_name: Some("thang".to_string()),
10727 ..Default::default()
10728 }),
10729 fdecl::Offer::Directory(fdecl::OfferDirectory {
10730 dependency_type: Some(fdecl::DependencyType::Strong),
10731 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10732 source_name: Some("thung1".to_string()),
10733 target_name: Some("thung1".to_string()),
10734 ..Default::default()
10735 }),
10736 fdecl::Offer::Storage(fdecl::OfferStorage {
10737 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10738 source_name: Some("thung2".to_string()),
10739 target_name: Some("thung2".to_string()),
10740 ..Default::default()
10741 }),
10742 fdecl::Offer::Runner(fdecl::OfferRunner {
10743 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10744 source_name: Some("thung3".to_string()),
10745 target_name: Some("thung3".to_string()),
10746 ..Default::default()
10747 }),
10748 fdecl::Offer::Resolver(fdecl::OfferResolver {
10749 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10750 source_name: Some("thung4".to_string()),
10751 target_name: Some("thung4".to_string()),
10752 ..Default::default()
10753 }),
10754 ],
10755 &fdecl::Component {
10756 capabilities: Some(vec![fdecl::Capability::Protocol(fdecl::Protocol {
10757 name: Some("thing".to_string()),
10758 source_path: Some("/svc/foo".into()),
10759 ..Default::default()
10760 }),]),
10761 ..Default::default()
10762 }
10763 ),
10764 Err(ErrorList::new(vec![
10765 Error::missing_field(DeclType::OfferProtocol, "target"),
10766 Error::missing_field(DeclType::OfferService, "target"),
10767 Error::missing_field(DeclType::OfferDirectory, "target"),
10768 Error::missing_field(DeclType::OfferStorage, "target"),
10769 Error::missing_field(DeclType::OfferRunner, "target"),
10770 Error::missing_field(DeclType::OfferResolver, "target"),
10771 ]))
10772 );
10773 }
10774
10775 #[test]
10776 fn test_validate_dynamic_offers_collection_collision() {
10777 assert_eq!(
10778 validate_dynamic_offers(
10779 vec![],
10780 &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10781 dependency_type: Some(fdecl::DependencyType::Strong),
10782 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10783 source_name: Some("thing".to_string()),
10784 target_name: Some("thing".to_string()),
10785 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10786 name: "child".to_string(),
10787 collection: Some("coll".to_string()),
10788 })),
10789 ..Default::default()
10790 }),],
10791 &fdecl::Component {
10792 offers: Some(vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10793 dependency_type: Some(fdecl::DependencyType::Strong),
10794 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10795 source_name: Some("thing".to_string()),
10796 target_name: Some("thing".to_string()),
10797 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10798 name: "coll".into()
10799 })),
10800 ..Default::default()
10801 }),]),
10802 collections: Some(vec![fdecl::Collection {
10803 name: Some("coll".to_string()),
10804 durability: Some(fdecl::Durability::Transient),
10805 ..Default::default()
10806 },]),
10807 ..Default::default()
10808 }
10809 ),
10810 Err(ErrorList::new(vec![Error::duplicate_field(
10811 DeclType::OfferProtocol,
10812 "target_name",
10813 "thing"
10814 ),]))
10815 );
10816 }
10817
10818 #[test]
10819 fn test_validate_dynamic_offers_cycle_collection_to_static_child() {
10820 assert_eq!(
10821 validate_dynamic_offers(
10822 vec![("dyn", "coll")],
10823 &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10824 source_name: Some("bar".to_string()),
10825 target_name: Some("bar".to_string()),
10826 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10827 name: "static_child".into(),
10828 collection: None,
10829 })),
10830 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10831 name: "dyn".to_string(),
10832 collection: Some("coll".to_string()),
10833 })),
10834 dependency_type: Some(fdecl::DependencyType::Strong),
10835 ..Default::default()
10836 }),],
10837 &fdecl::Component {
10838 offers: Some(vec![fdecl::Offer::Service(fdecl::OfferService {
10839 source_name: Some("foo".to_string()),
10840 target_name: Some("foo".to_string()),
10841 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10842 name: "coll".into(),
10843 })),
10844 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10845 name: "static_child".into(),
10846 collection: None,
10847 })),
10848 ..Default::default()
10849 })]),
10850 children: Some(vec![fdecl::Child {
10851 name: Some("static_child".into()),
10852 url: Some("url#child.cm".into()),
10853 startup: Some(fdecl::StartupMode::Lazy),
10854 ..Default::default()
10855 }]),
10856 collections: Some(vec![fdecl::Collection {
10857 name: Some("coll".into()),
10858 durability: Some(fdecl::Durability::Transient),
10859 ..Default::default()
10860 }]),
10861 ..Default::default()
10862 }
10863 ),
10864 Err(ErrorList::new(vec![Error::dependency_cycle(
10865 directed_graph::Error::CyclesDetected(
10866 [vec![
10867 "child coll:dyn",
10868 "collection coll",
10869 "child static_child",
10870 "child coll:dyn",
10871 ]]
10872 .iter()
10873 .cloned()
10874 .collect()
10875 )
10876 .format_cycle()
10877 )]))
10878 );
10879 }
10880
10881 #[test]
10882 fn test_validate_dynamic_offers_cycle_collection_to_dynamic_child() {
10883 assert_eq!(
10884 validate_dynamic_offers(
10885 vec![("dyn", "coll1"), ("dyn", "coll2")],
10886 &vec![
10887 fdecl::Offer::Service(fdecl::OfferService {
10888 source_name: Some("foo".to_string()),
10889 target_name: Some("foo".to_string()),
10890 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10891 name: "coll2".into(),
10892 })),
10893 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10894 name: "dyn".into(),
10895 collection: Some("coll1".into()),
10896 })),
10897 ..Default::default()
10898 }),
10899 fdecl::Offer::Protocol(fdecl::OfferProtocol {
10900 source_name: Some("bar".to_string()),
10901 target_name: Some("bar".to_string()),
10902 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10903 name: "dyn".into(),
10904 collection: Some("coll1".into()),
10905 })),
10906 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10907 name: "dyn".to_string(),
10908 collection: Some("coll2".to_string()),
10909 })),
10910 dependency_type: Some(fdecl::DependencyType::Strong),
10911 ..Default::default()
10912 }),
10913 ],
10914 &fdecl::Component {
10915 collections: Some(vec![
10916 fdecl::Collection {
10917 name: Some("coll1".into()),
10918 durability: Some(fdecl::Durability::Transient),
10919 ..Default::default()
10920 },
10921 fdecl::Collection {
10922 name: Some("coll2".into()),
10923 durability: Some(fdecl::Durability::Transient),
10924 ..Default::default()
10925 },
10926 ]),
10927 ..Default::default()
10928 }
10929 ),
10930 Err(ErrorList::new(vec![Error::dependency_cycle(
10931 directed_graph::Error::CyclesDetected(
10932 [vec![
10933 "child coll1:dyn",
10934 "child coll2:dyn",
10935 "collection coll2",
10936 "child coll1:dyn",
10937 ]]
10938 .iter()
10939 .cloned()
10940 .collect()
10941 )
10942 .format_cycle()
10943 )]))
10944 );
10945 }
10946
10947 #[test]
10948 fn test_validate_dynamic_offers_cycle_collection_to_collection() {
10949 assert_eq!(
10950 validate_dynamic_offers(
10951 vec![("dyn", "coll1"), ("dyn", "coll2")],
10952 &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10953 source_name: Some("bar".to_string()),
10954 target_name: Some("bar".to_string()),
10955 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10956 name: "dyn".into(),
10957 collection: Some("coll2".parse().unwrap()),
10958 })),
10959 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10960 name: "dyn".into(),
10961 collection: Some("coll1".parse().unwrap()),
10962 })),
10963 dependency_type: Some(fdecl::DependencyType::Strong),
10964 ..Default::default()
10965 }),],
10966 &fdecl::Component {
10967 offers: Some(vec![fdecl::Offer::Service(fdecl::OfferService {
10968 source_name: Some("foo".to_string()),
10969 target_name: Some("foo".to_string()),
10970 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10971 name: "coll1".into(),
10972 })),
10973 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10974 name: "coll2".into(),
10975 })),
10976 ..Default::default()
10977 })]),
10978 collections: Some(vec![
10979 fdecl::Collection {
10980 name: Some("coll1".into()),
10981 durability: Some(fdecl::Durability::Transient),
10982 ..Default::default()
10983 },
10984 fdecl::Collection {
10985 name: Some("coll2".into()),
10986 durability: Some(fdecl::Durability::Transient),
10987 ..Default::default()
10988 },
10989 ]),
10990 ..Default::default()
10991 }
10992 ),
10993 Err(ErrorList::new(vec![Error::dependency_cycle(
10994 directed_graph::Error::CyclesDetected(
10995 [vec![
10996 "child coll1:dyn",
10997 "collection coll1",
10998 "child coll2:dyn",
10999 "child coll1:dyn",
11000 ]]
11001 .iter()
11002 .cloned()
11003 .collect()
11004 )
11005 .format_cycle()
11006 )]))
11007 );
11008 }
11009
11010 #[test]
11011 fn test_validate_dynamic_child() {
11012 assert_eq!(
11013 Ok(()),
11014 validate_dynamic_child(&fdecl::Child {
11015 name: Some("a".repeat(MAX_LONG_NAME_LENGTH).to_string()),
11016 url: Some("test:///child".to_string()),
11017 startup: Some(fdecl::StartupMode::Lazy),
11018 on_terminate: None,
11019 environment: None,
11020 ..Default::default()
11021 })
11022 );
11023 }
11024
11025 #[test]
11026 fn test_validate_dynamic_child_environment_is_invalid() {
11027 assert_eq!(
11028 validate_dynamic_child(&fdecl::Child {
11029 name: Some("a".repeat(MAX_LONG_NAME_LENGTH).to_string()),
11030 url: Some("test:///child".to_string()),
11031 startup: Some(fdecl::StartupMode::Lazy),
11032 on_terminate: None,
11033 environment: Some("env".to_string()),
11034 ..Default::default()
11035 }),
11036 Err(ErrorList::new(vec![Error::DynamicChildWithEnvironment]))
11037 );
11038 }
11039
11040 #[test]
11041 fn test_validate_dynamic_offers_missing_stuff() {
11042 assert_eq!(
11043 validate_dynamic_offers(
11044 vec![],
11045 &vec![
11046 fdecl::Offer::Protocol(fdecl::OfferProtocol::default()),
11047 fdecl::Offer::Service(fdecl::OfferService::default()),
11048 fdecl::Offer::Directory(fdecl::OfferDirectory::default()),
11049 fdecl::Offer::Storage(fdecl::OfferStorage::default()),
11050 fdecl::Offer::Runner(fdecl::OfferRunner::default()),
11051 fdecl::Offer::Resolver(fdecl::OfferResolver::default()),
11052 ],
11053 &fdecl::Component::default()
11054 ),
11055 Err(ErrorList::new(vec![
11056 Error::missing_field(DeclType::OfferProtocol, "source"),
11057 Error::missing_field(DeclType::OfferProtocol, "source_name"),
11058 Error::missing_field(DeclType::OfferProtocol, "target"),
11059 Error::missing_field(DeclType::OfferProtocol, "target_name"),
11060 Error::missing_field(DeclType::OfferProtocol, "dependency_type"),
11061 Error::missing_field(DeclType::OfferService, "source"),
11062 Error::missing_field(DeclType::OfferService, "source_name"),
11063 Error::missing_field(DeclType::OfferService, "target"),
11064 Error::missing_field(DeclType::OfferService, "target_name"),
11065 Error::missing_field(DeclType::OfferDirectory, "source"),
11066 Error::missing_field(DeclType::OfferDirectory, "source_name"),
11067 Error::missing_field(DeclType::OfferDirectory, "target"),
11068 Error::missing_field(DeclType::OfferDirectory, "target_name"),
11069 Error::missing_field(DeclType::OfferDirectory, "dependency_type"),
11070 Error::missing_field(DeclType::OfferStorage, "source"),
11071 Error::missing_field(DeclType::OfferStorage, "source_name"),
11072 Error::missing_field(DeclType::OfferStorage, "target"),
11073 Error::missing_field(DeclType::OfferStorage, "target_name"),
11074 Error::missing_field(DeclType::OfferRunner, "source"),
11075 Error::missing_field(DeclType::OfferRunner, "source_name"),
11076 Error::missing_field(DeclType::OfferRunner, "target"),
11077 Error::missing_field(DeclType::OfferRunner, "target_name"),
11078 Error::missing_field(DeclType::OfferResolver, "source"),
11079 Error::missing_field(DeclType::OfferResolver, "source_name"),
11080 Error::missing_field(DeclType::OfferResolver, "target"),
11081 Error::missing_field(DeclType::OfferResolver, "target_name"),
11082 ]))
11083 );
11084 }
11085
11086 test_dependency! {
11087 (test_validate_offers_protocol_dependency_cycle) => {
11088 ty = fdecl::Offer::Protocol,
11089 offer_decl = fdecl::OfferProtocol {
11090 source: None, target: None, source_name: Some(format!("thing")),
11093 target_name: Some(format!("thing")),
11094 dependency_type: Some(fdecl::DependencyType::Strong),
11095 ..Default::default()
11096 },
11097 },
11098 (test_validate_offers_directory_dependency_cycle) => {
11099 ty = fdecl::Offer::Directory,
11100 offer_decl = fdecl::OfferDirectory {
11101 source: None, target: None, source_name: Some(format!("thing")),
11104 target_name: Some(format!("thing")),
11105 rights: Some(fio::Operations::CONNECT),
11106 subdir: None,
11107 dependency_type: Some(fdecl::DependencyType::Strong),
11108 ..Default::default()
11109 },
11110 },
11111 (test_validate_offers_service_dependency_cycle) => {
11112 ty = fdecl::Offer::Service,
11113 offer_decl = fdecl::OfferService {
11114 source: None, target: None, source_name: Some(format!("thing")),
11117 target_name: Some(format!("thing")),
11118 ..Default::default()
11119 },
11120 },
11121 (test_validate_offers_runner_dependency_cycle) => {
11122 ty = fdecl::Offer::Runner,
11123 offer_decl = fdecl::OfferRunner {
11124 source: None, target: None, source_name: Some(format!("thing")),
11127 target_name: Some(format!("thing")),
11128 ..Default::default()
11129 },
11130 },
11131 (test_validate_offers_resolver_dependency_cycle) => {
11132 ty = fdecl::Offer::Resolver,
11133 offer_decl = fdecl::OfferResolver {
11134 source: None, target: None, source_name: Some(format!("thing")),
11137 target_name: Some(format!("thing")),
11138 ..Default::default()
11139 },
11140 },
11141 }
11142 test_weak_dependency! {
11143 (test_validate_offers_protocol_weak_dependency_cycle) => {
11144 ty = fdecl::Offer::Protocol,
11145 offer_decl = fdecl::OfferProtocol {
11146 source: None, target: None, source_name: Some(format!("thing")),
11149 target_name: Some(format!("thing")),
11150 dependency_type: None, ..Default::default()
11152 },
11153 },
11154 (test_validate_offers_directory_weak_dependency_cycle) => {
11155 ty = fdecl::Offer::Directory,
11156 offer_decl = fdecl::OfferDirectory {
11157 source: None, target: None, source_name: Some(format!("thing")),
11160 target_name: Some(format!("thing")),
11161 rights: Some(fio::Operations::CONNECT),
11162 subdir: None,
11163 dependency_type: None, ..Default::default()
11165 },
11166 },
11167 (test_validate_offers_service_weak_dependency_cycle) => {
11168 ty = fdecl::Offer::Service,
11169 offer_decl = fdecl::OfferService {
11170 source: None, target: None, source_name: Some(format!("thing")),
11173 target_name: Some(format!("thing")),
11174 dependency_type: None, ..Default::default()
11176 },
11177 },
11178 }
11179}