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 generate_dependency_graph<'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
403impl<'a> ValidationContext<'a> {
404 fn validate(
405 mut self,
406 decl: &'a fdecl::Component,
407 dynamic_offers: Option<&'a Vec<fdecl::Offer>>,
408 ) -> Result<(), Vec<Error>> {
409 if let Some(envs) = &decl.environments {
411 self.collect_environment_names(&envs);
412 }
413
414 if let Some(children) = decl.children.as_ref() {
416 for child in children {
417 self.validate_child_decl(&child);
418 }
419 }
420
421 if let Some(collections) = decl.collections.as_ref() {
423 for collection in collections {
424 self.validate_collection_decl(&collection);
425 }
426 }
427
428 if let Some(capabilities) = decl.capabilities.as_ref() {
430 #[cfg(fuchsia_api_level_at_least = "25")]
431 self.load_dictionary_names(capabilities.iter().filter_map(
432 |capability| match capability {
433 fdecl::Capability::Dictionary(dictionary_decl) => Some(dictionary_decl),
434 _ => None,
435 },
436 ));
437 self.validate_capability_decls(capabilities, false);
438 }
439
440 let mut use_runner_name = None;
442 let mut use_runner_source = None;
443 if let Some(uses) = decl.uses.as_ref() {
444 (use_runner_name, use_runner_source) = self.validate_use_decls(uses);
445 }
446
447 if let Some(program) = decl.program.as_ref() {
449 self.validate_program(program, use_runner_name, use_runner_source);
450 }
451
452 if let Some(exposes) = decl.exposes.as_ref() {
454 let mut expose_to_parent_ids = HashMap::new();
455 let mut expose_to_framework_ids = HashMap::new();
456 for expose in exposes.iter() {
457 self.validate_expose_decl(
458 &expose,
459 &mut expose_to_parent_ids,
460 &mut expose_to_framework_ids,
461 );
462 }
463 self.validate_expose_group(&exposes);
464 }
465
466 if let Some(offers) = decl.offers.as_ref() {
468 for offer in offers.iter() {
469 self.validate_offer_decl(&offer, OfferType::Static);
470 }
471 self.validate_offer_group(&offers, OfferType::Static);
472 }
473
474 if let Some(dynamic_offers) = dynamic_offers.as_ref() {
475 for dynamic_offer in dynamic_offers.iter() {
476 self.validate_offer_decl(&dynamic_offer, OfferType::Dynamic);
477 }
478 self.validate_offer_group(&dynamic_offers, OfferType::Dynamic);
479 }
480
481 if let Some(environment) = decl.environments.as_ref() {
483 for environment in environment {
484 self.validate_environment_decl(&environment);
485 }
486 }
487
488 #[cfg(fuchsia_api_level_at_least = "20")]
490 self.validate_config(decl.config.as_ref(), decl.uses.as_ref());
491
492 generate_dependency_graph(&mut self.strong_dependencies, &decl);
494 if let Err(e) = self.strong_dependencies.topological_sort() {
495 self.errors.push(Error::dependency_cycle(e.format_cycle()));
496 }
497
498 if self.errors.is_empty() {
499 Ok(())
500 } else {
501 Err(self.errors)
502 }
503 }
504
505 fn collect_environment_names(&mut self, envs: &'a [fdecl::Environment]) {
507 for env in envs {
508 if let Some(name) = env.name.as_ref() {
509 if !self.all_environment_names.insert(name) {
510 self.errors.push(Error::duplicate_field(DeclType::Environment, "name", name));
511 }
512 }
513 }
514 }
515
516 #[cfg(fuchsia_api_level_at_least = "20")]
519 fn validate_config(
520 &mut self,
521 config: Option<&fdecl::ConfigSchema>,
522 uses: Option<&Vec<fdecl::Use>>,
523 ) {
524 use std::collections::BTreeMap;
525
526 let optional_use_keys: BTreeMap<String, fdecl::ConfigType> =
528 uses.map_or(BTreeMap::new(), |u| {
529 u.iter()
530 .map(|u| {
531 let fdecl::Use::Config(config) = u else {
532 return None;
533 };
534 if config.availability == Some(fdecl::Availability::Required)
535 || config.availability == None
536 {
537 return None;
538 }
539 if let Some(_) = config.default.as_ref() {
540 return None;
541 }
542 let Some(key) = config.target_name.clone() else {
543 return None;
544 };
545 let Some(value) = config.type_.clone() else {
546 return None;
547 };
548 Some((key, value))
549 })
550 .flatten()
551 .collect()
552 });
553
554 for u in uses.iter().flat_map(|x| x.iter()) {
556 let fdecl::Use::Config(config) = u else { continue };
557 let Some(default) = config.default.as_ref() else { continue };
558 validate_value_spec(&fdecl::ConfigValueSpec {
559 value: Some(default.clone()),
560 ..Default::default()
561 })
562 .map_err(|mut e| self.errors.append(&mut e.errs))
563 .ok();
564 }
565
566 let Some(config) = config else {
567 if !optional_use_keys.is_empty() {
568 self.errors.push(Error::missing_field(DeclType::ConfigField, "config"))
569 }
570 return;
571 };
572
573 if let Some(fields) = &config.fields {
574 for field in fields {
575 if field.key.is_none() {
576 self.errors.push(Error::missing_field(DeclType::ConfigField, "key"));
577 }
578 if let Some(type_) = &field.type_ {
579 self.validate_config_type(type_, true);
580 } else {
581 self.errors.push(Error::missing_field(DeclType::ConfigField, "value_type"));
582 }
583 }
584 } else {
585 self.errors.push(Error::missing_field(DeclType::ConfigSchema, "fields"));
586 }
587
588 if let Some(checksum) = &config.checksum {
589 match checksum {
590 fdecl::ConfigChecksum::Sha256(_) => {}
591 fdecl::ConfigChecksumUnknown!() => {
592 self.errors.push(Error::invalid_field(DeclType::ConfigSchema, "checksum"));
593 }
594 }
595 } else {
596 self.errors.push(Error::missing_field(DeclType::ConfigSchema, "checksum"));
597 }
598
599 'outer: for (key, value) in optional_use_keys.iter() {
600 for field in config.fields.iter().flatten() {
601 if field.key.as_ref() == Some(key) {
602 if field.type_.as_ref() != Some(value) {
603 self.errors.push(Error::invalid_field(DeclType::ConfigField, key.clone()));
604 }
605 continue 'outer;
606 }
607 }
608 self.errors.push(Error::missing_field(DeclType::ConfigField, key.clone()));
609 }
610
611 match config.value_source {
612 None => self.errors.push(Error::missing_field(DeclType::ConfigSchema, "value_source")),
613 #[cfg(fuchsia_api_level_at_least = "HEAD")]
614 Some(fdecl::ConfigValueSource::Capabilities(_)) => {
615 if !optional_use_keys.is_empty() {
616 self.errors
617 .push(Error::invalid_field(DeclType::ConfigValueSource, "ValueSource"))
618 }
619 }
620 Some(fdecl::ConfigValueSource::__SourceBreaking { .. }) => {
621 self.errors.push(Error::invalid_field(DeclType::ConfigValueSource, "ValueSource"))
622 }
623 _ => (),
624 };
625 }
626
627 #[cfg(fuchsia_api_level_at_least = "20")]
628 fn validate_config_type(&mut self, type_: &fdecl::ConfigType, accept_vectors: bool) {
629 match &type_.layout {
630 fdecl::ConfigTypeLayout::Bool
631 | fdecl::ConfigTypeLayout::Uint8
632 | fdecl::ConfigTypeLayout::Uint16
633 | fdecl::ConfigTypeLayout::Uint32
634 | fdecl::ConfigTypeLayout::Uint64
635 | fdecl::ConfigTypeLayout::Int8
636 | fdecl::ConfigTypeLayout::Int16
637 | fdecl::ConfigTypeLayout::Int32
638 | fdecl::ConfigTypeLayout::Int64 => {
639 if let Some(parameters) = &type_.parameters {
641 if !parameters.is_empty() {
642 self.errors
643 .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
644 }
645 } else {
646 self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"));
647 }
648
649 if !type_.constraints.is_empty() {
650 self.errors.push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
651 }
652 }
653 fdecl::ConfigTypeLayout::String => {
654 if let Some(parameters) = &type_.parameters {
656 if !parameters.is_empty() {
657 self.errors
658 .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
659 }
660 } else {
661 self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"));
662 }
663
664 if type_.constraints.is_empty() {
665 self.errors.push(Error::missing_field(DeclType::ConfigType, "constraints"));
666 } else if type_.constraints.len() > 1 {
667 self.errors.push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
668 } else if let fdecl::LayoutConstraint::MaxSize(_) = &type_.constraints[0] {
669 } else {
670 self.errors.push(Error::invalid_field(DeclType::ConfigType, "constraints"));
671 }
672 }
673 fdecl::ConfigTypeLayout::Vector => {
674 if accept_vectors {
675 if let Some(parameters) = &type_.parameters {
677 if parameters.is_empty() {
678 self.errors
679 .push(Error::missing_field(DeclType::ConfigType, "parameters"));
680 } else if parameters.len() > 1 {
681 self.errors
682 .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
683 } else if let fdecl::LayoutParameter::NestedType(nested_type) =
684 ¶meters[0]
685 {
686 self.validate_config_type(nested_type, false);
687 } else {
688 self.errors
689 .push(Error::invalid_field(DeclType::ConfigType, "parameters"));
690 }
691 } else {
692 self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"))
693 }
694
695 if type_.constraints.is_empty() {
696 self.errors.push(Error::missing_field(DeclType::ConfigType, "constraints"));
697 } else if type_.constraints.len() > 1 {
698 self.errors
699 .push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
700 } else if let fdecl::LayoutConstraint::MaxSize(_) = &type_.constraints[0] {
701 } else {
702 self.errors.push(Error::invalid_field(DeclType::ConfigType, "constraints"));
703 }
704 } else {
705 self.errors.push(Error::nested_vector());
706 }
707 }
708 _ => self.errors.push(Error::invalid_field(DeclType::ConfigType, "layout")),
709 }
710 }
711
712 fn validate_capability_decls(
713 &mut self,
714 capabilities: &'a [fdecl::Capability],
715 as_builtin: bool,
716 ) {
717 for capability in capabilities {
718 self.validate_capability_decl(capability, as_builtin);
719 }
720 }
721
722 fn validate_capability_decl(&mut self, capability: &'a fdecl::Capability, as_builtin: bool) {
727 match capability {
728 fdecl::Capability::Service(service) => self.validate_service_decl(&service, as_builtin),
729 fdecl::Capability::Protocol(protocol) => {
730 self.validate_protocol_decl(&protocol, as_builtin)
731 }
732 fdecl::Capability::Directory(directory) => {
733 self.validate_directory_decl(&directory, as_builtin)
734 }
735 fdecl::Capability::Storage(storage) => {
736 if as_builtin {
737 self.errors.push(Error::CapabilityCannotBeBuiltin(DeclType::Storage))
738 } else {
739 self.validate_storage_decl(&storage)
740 }
741 }
742 fdecl::Capability::Runner(runner) => self.validate_runner_decl(&runner, as_builtin),
743 fdecl::Capability::Resolver(resolver) => {
744 self.validate_resolver_decl(&resolver, as_builtin)
745 }
746 fdecl::Capability::EventStream(event) => {
747 if as_builtin {
748 self.validate_event_stream_decl(&event)
749 } else {
750 self.errors.push(Error::CapabilityMustBeBuiltin(DeclType::EventStream))
751 }
752 }
753 #[cfg(fuchsia_api_level_at_least = "25")]
754 fdecl::Capability::Dictionary(dictionary) => {
755 self.validate_dictionary_decl(&dictionary);
756 }
757 #[cfg(fuchsia_api_level_at_least = "HEAD")]
758 fdecl::Capability::Config(config) => {
759 self.validate_configuration_decl(&config);
760 }
761 fdecl::CapabilityUnknown!() => self.errors.push(Error::UnknownCapability),
762 }
763 }
764
765 fn validate_use_decls(
767 &mut self,
768 uses: &'a [fdecl::Use],
769 ) -> (Option<&'a String>, Option<&'a fdecl::Ref>) {
770 for use_ in uses.iter() {
772 self.validate_use_decl(&use_);
773 }
774 self.validate_use_paths(&uses);
775
776 #[cfg(fuchsia_api_level_at_least = "HEAD")]
777 {
778 let mut use_runner_name = None;
779 let mut use_runner_source = None;
780 for use_ in uses.iter() {
781 if let fdecl::Use::Runner(use_runner) = use_ {
782 if use_runner_name.is_some() {
783 self.errors.push(Error::MultipleRunnersUsed);
784 }
785
786 use_runner_name = use_runner.source_name.as_ref();
787 use_runner_source = use_runner.source.as_ref();
788 }
789 }
790 return (use_runner_name, use_runner_source);
791 }
792 #[cfg(fuchsia_api_level_less_than = "HEAD")]
793 return (None, None);
794 }
795
796 fn validate_use_decl(&mut self, use_: &'a fdecl::Use) {
797 match use_ {
798 fdecl::Use::Service(u) => {
799 let decl = DeclType::UseService;
800 self.validate_use_fields(
801 decl,
802 Self::service_checker,
803 u.source.as_ref(),
804 u.source_name.as_ref(),
805 get_source_dictionary!(u),
806 u.target_path.as_ref(),
807 u.dependency_type.as_ref(),
808 u.availability.as_ref(),
809 );
810 if u.dependency_type.is_none() {
811 self.errors.push(Error::missing_field(decl, "dependency_type"));
812 }
813 }
814 fdecl::Use::Protocol(u) => {
815 let decl = DeclType::UseProtocol;
816 self.validate_use_fields(
817 decl,
818 Self::protocol_checker,
819 u.source.as_ref(),
820 u.source_name.as_ref(),
821 get_source_dictionary!(u),
822 u.target_path.as_ref(),
823 u.dependency_type.as_ref(),
824 u.availability.as_ref(),
825 );
826 if u.dependency_type.is_none() {
827 self.errors.push(Error::missing_field(decl, "dependency_type"));
828 }
829 }
830 fdecl::Use::Directory(u) => {
831 let decl = DeclType::UseDirectory;
832 self.validate_use_fields(
833 decl,
834 Self::directory_checker,
835 u.source.as_ref(),
836 u.source_name.as_ref(),
837 get_source_dictionary!(u),
838 u.target_path.as_ref(),
839 u.dependency_type.as_ref(),
840 u.availability.as_ref(),
841 );
842 if u.dependency_type.is_none() {
843 self.errors.push(Error::missing_field(decl, "dependency_type"));
844 }
845 if u.rights.is_none() {
846 self.errors.push(Error::missing_field(DeclType::UseDirectory, "rights"));
847 }
848 if let Some(subdir) = u.subdir.as_ref() {
849 check_relative_path(
850 Some(subdir),
851 DeclType::UseDirectory,
852 "subdir",
853 &mut self.errors,
854 );
855 }
856 }
857 fdecl::Use::Storage(u) => {
858 const SOURCE: Option<fdecl::Ref> = Some(fdecl::Ref::Parent(fdecl::ParentRef {}));
859 const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
860 Some(fdecl::DependencyType::Strong);
861 self.validate_use_fields(
862 DeclType::UseStorage,
863 Self::storage_checker,
864 SOURCE.as_ref(),
865 u.source_name.as_ref(),
866 None,
867 u.target_path.as_ref(),
868 DEPENDENCY_TYPE.as_ref(),
869 u.availability.as_ref(),
870 );
871 }
872 fdecl::Use::EventStream(u) => {
873 const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
874 Some(fdecl::DependencyType::Strong);
875 let decl = DeclType::UseEventStream;
876 self.validate_use_fields(
877 decl,
878 Self::event_stream_checker,
879 u.source.as_ref(),
880 u.source_name.as_ref(),
881 None,
882 u.target_path.as_ref(),
883 DEPENDENCY_TYPE.as_ref(),
884 u.availability.as_ref(),
885 );
886 match u.source {
888 Some(fdecl::Ref::Child(_)) | Some(fdecl::Ref::Parent(_)) => {
889 }
891 Some(fdecl::Ref::Framework(_))
892 | Some(fdecl::Ref::Self_(_))
893 | Some(fdecl::Ref::Debug(_)) => {
894 self.errors.push(Error::invalid_field(decl, "source"));
896 }
897 Some(fdecl::Ref::Collection(_)) | Some(fdecl::RefUnknown!()) | None => {
898 }
900 }
901 if let Some(scope) = &u.scope {
902 for reference in scope {
903 if !matches!(reference, fdecl::Ref::Child(_) | fdecl::Ref::Collection(_)) {
904 self.errors.push(Error::invalid_field(decl, "scope"));
905 }
906 }
907 }
908 }
909 #[cfg(fuchsia_api_level_at_least = "HEAD")]
910 fdecl::Use::Runner(u) => {
911 const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
912 Some(fdecl::DependencyType::Strong);
913 const AVAILABILITY: Option<fdecl::Availability> =
914 Some(fdecl::Availability::Required);
915 let decl = DeclType::UseRunner;
916 self.validate_use_fields(
917 decl,
918 Self::runner_checker,
919 u.source.as_ref(),
920 u.source_name.as_ref(),
921 get_source_dictionary!(u),
922 None,
923 DEPENDENCY_TYPE.as_ref(),
924 AVAILABILITY.as_ref(),
925 );
926 }
927 #[cfg(fuchsia_api_level_at_least = "HEAD")]
928 fdecl::Use::Config(u) => {
929 const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
930 Some(fdecl::DependencyType::Strong);
931 let decl = DeclType::UseConfiguration;
932 self.validate_use_fields(
933 decl,
934 Self::config_checker,
935 u.source.as_ref(),
936 u.source_name.as_ref(),
937 None,
938 None,
939 DEPENDENCY_TYPE.as_ref(),
940 u.availability.as_ref(),
941 );
942 }
943 fdecl::UseUnknown!() => {
944 self.errors.push(Error::invalid_field(DeclType::Component, "use"));
945 }
946 }
947 }
948
949 fn validate_program(
952 &mut self,
953 program: &fdecl::Program,
954 use_runner_name: Option<&String>,
955 _use_runner_source: Option<&fdecl::Ref>,
956 ) {
957 match &program.runner {
958 Some(_) =>
959 {
960 #[cfg(fuchsia_api_level_at_least = "HEAD")]
961 if use_runner_name.is_some() {
962 if use_runner_name != program.runner.as_ref()
963 || _use_runner_source
964 != Some(&fdecl::Ref::Environment(fdecl::EnvironmentRef))
965 {
966 self.errors.push(Error::ConflictingRunners);
967 }
968 }
969 }
970 None => {
971 if use_runner_name.is_none() {
972 self.errors.push(Error::MissingRunner);
973 }
974 }
975 }
976
977 if program.info.is_none() {
978 self.errors.push(Error::missing_field(DeclType::Program, "info"));
979 }
980 }
981
982 fn validate_use_paths(&mut self, uses: &[fdecl::Use]) {
985 #[derive(Debug, PartialEq, Clone, Copy)]
986 struct PathCapability<'a> {
987 decl: DeclType,
988 dir: &'a Path,
989 use_: &'a fdecl::Use,
990 }
991 let mut used_paths = HashMap::new();
992 for use_ in uses.iter() {
993 match use_ {
994 fdecl::Use::Service(fdecl::UseService { target_path: Some(path), .. })
995 | fdecl::Use::Protocol(fdecl::UseProtocol { target_path: Some(path), .. })
996 | fdecl::Use::Directory(fdecl::UseDirectory { target_path: Some(path), .. })
997 | fdecl::Use::Storage(fdecl::UseStorage { target_path: Some(path), .. }) => {
998 let capability = match use_ {
999 fdecl::Use::Service(_) => {
1000 let dir = match Path::new(path).parent() {
1001 Some(p) => p,
1002 None => continue, };
1004 PathCapability { decl: DeclType::UseService, dir, use_ }
1005 }
1006 fdecl::Use::Protocol(_) => {
1007 let dir = match Path::new(path).parent() {
1008 Some(p) => p,
1009 None => continue, };
1011 PathCapability { decl: DeclType::UseProtocol, dir, use_ }
1012 }
1013 fdecl::Use::Directory(_) => PathCapability {
1014 decl: DeclType::UseDirectory,
1015 dir: Path::new(path),
1016 use_,
1017 },
1018 fdecl::Use::Storage(_) => PathCapability {
1019 decl: DeclType::UseStorage,
1020 dir: Path::new(path),
1021 use_,
1022 },
1023 _ => unreachable!(),
1024 };
1025 if used_paths.insert(path, capability).is_some() {
1026 self.errors.push(Error::duplicate_field(
1028 capability.decl,
1029 "target_path",
1030 path,
1031 ));
1032 }
1033 }
1034 _ => {}
1035 }
1036 }
1037 for ((&path_a, capability_a), (&path_b, capability_b)) in
1038 used_paths.iter().tuple_combinations()
1039 {
1040 if match (capability_a.use_, capability_b.use_) {
1041 (fdecl::Use::Directory(_), fdecl::Use::Directory(_))
1043 | (fdecl::Use::Storage(_), fdecl::Use::Directory(_))
1044 | (fdecl::Use::Directory(_), fdecl::Use::Storage(_))
1045 | (fdecl::Use::Storage(_), fdecl::Use::Storage(_)) => {
1046 capability_b.dir == capability_a.dir
1047 || capability_b.dir.starts_with(capability_a.dir)
1048 || capability_a.dir.starts_with(capability_b.dir)
1049 }
1050
1051 (_, fdecl::Use::Directory(_)) | (fdecl::Use::Directory(_), _) => {
1053 capability_b.dir == capability_a.dir
1054 || capability_b.dir.starts_with(capability_a.dir)
1055 || capability_a.dir.starts_with(capability_b.dir)
1056 }
1057
1058 (_, _) => {
1061 capability_b.dir != capability_a.dir
1062 && (capability_b.dir.starts_with(capability_a.dir)
1063 || capability_a.dir.starts_with(capability_b.dir))
1064 }
1065 } {
1066 self.errors.push(Error::invalid_path_overlap(
1067 capability_a.decl,
1068 path_a,
1069 capability_b.decl,
1070 path_b,
1071 ));
1072 }
1073 }
1074 for (used_path, capability) in used_paths.iter() {
1075 if used_path.as_str() == "/pkg" || used_path.starts_with("/pkg/") {
1076 self.errors.push(Error::pkg_path_overlap(capability.decl, *used_path));
1077 }
1078 }
1079 }
1080
1081 fn validate_use_fields(
1082 &mut self,
1083 decl: DeclType,
1084 capability_checker: impl Fn(&Self) -> &dyn Container,
1088 source: Option<&'a fdecl::Ref>,
1089 source_name: Option<&'a String>,
1090 source_dictionary: Option<&'a String>,
1091 target_path: Option<&'a String>,
1092 dependency_type: Option<&fdecl::DependencyType>,
1093 availability: Option<&'a fdecl::Availability>,
1094 ) {
1095 self.validate_use_source(decl, source, source_dictionary);
1096
1097 check_name(source_name, decl, "source_name", &mut self.errors);
1098 if source_dictionary.is_some() {
1099 check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
1100 }
1101 if decl != DeclType::UseRunner && decl != DeclType::UseConfiguration {
1102 check_path(target_path, decl, "target_path", &mut self.errors);
1103 }
1104 check_use_availability(decl, availability, &mut self.errors);
1105
1106 let is_use_from_child = match source {
1108 Some(fdecl::Ref::Child(_)) => true,
1109 _ => false,
1110 };
1111 match (is_use_from_child, dependency_type) {
1112 (false, Some(fdecl::DependencyType::Weak)) => {
1113 self.errors.push(Error::invalid_field(decl, "dependency_type"));
1114 }
1115 _ => {}
1116 }
1117
1118 self.validate_route_from_self(
1119 decl,
1120 source,
1121 source_name,
1122 source_dictionary,
1123 capability_checker,
1124 );
1125 }
1126
1127 fn validate_use_source(
1128 &mut self,
1129 decl: DeclType,
1130 source: Option<&'a fdecl::Ref>,
1131 source_dictionary: Option<&'a String>,
1132 ) {
1133 match (source, source_dictionary) {
1134 (Some(fdecl::Ref::Parent(_)), _) => {}
1136 (Some(fdecl::Ref::Self_(_)), _) => {}
1137 (Some(fdecl::Ref::Child(child)), _) => {
1138 self.validate_child_ref(decl, "source", &child, OfferType::Static);
1139 return;
1140 }
1141 (Some(fdecl::Ref::Framework(_)), None) => {}
1143 (Some(fdecl::Ref::Debug(_)), None) => {}
1144 (Some(fdecl::Ref::Capability(c)), None) => {
1145 self.validate_source_capability(&c, decl, "source");
1146 return;
1147 }
1148 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1149 (Some(fdecl::Ref::Environment(_)), None) => {}
1150 (Some(fdecl::Ref::Collection(collection)), None) if decl == DeclType::UseService => {
1151 self.validate_collection_ref(decl, "source", &collection);
1152 return;
1153 }
1154 (None, _) => self.errors.push(Error::missing_field(decl, "source")),
1156 (_, _) => self.errors.push(Error::invalid_field(decl, "source")),
1158 }
1159 }
1160
1161 fn validate_child_decl(&mut self, child: &'a fdecl::Child) {
1162 if let Err(mut e) = validate_child(child, check_name) {
1163 self.errors.append(&mut e.errs);
1164 }
1165 if let Some(name) = child.name.as_ref() {
1166 let name: &str = name;
1167 if self.all_children.insert(name, child).is_some() {
1168 self.errors.push(Error::duplicate_field(DeclType::Child, "name", name));
1169 }
1170 if let Some(env) = child.environment.as_ref() {
1171 let source = DependencyNode::Environment(env.as_str());
1172 let target = DependencyNode::Child(name, None);
1173 self.add_strong_dep(Some(source), Some(target));
1174 }
1175 }
1176 if let Some(environment) = child.environment.as_ref() {
1177 if !self.all_environment_names.contains(environment.as_str()) {
1178 self.errors.push(Error::invalid_environment(
1179 DeclType::Child,
1180 "environment",
1181 environment,
1182 ));
1183 }
1184 }
1185 }
1186
1187 fn validate_collection_decl(&mut self, collection: &'a fdecl::Collection) {
1188 let name = collection.name.as_ref();
1189 if check_name(name, DeclType::Collection, "name", &mut self.errors) {
1190 let name: &str = name.unwrap();
1191 if !self.all_collections.insert(name) {
1192 self.errors.push(Error::duplicate_field(DeclType::Collection, "name", name));
1193 }
1194 }
1195 if collection.durability.is_none() {
1196 self.errors.push(Error::missing_field(DeclType::Collection, "durability"));
1197 }
1198 if let Some(environment) = collection.environment.as_ref() {
1199 if !self.all_environment_names.contains(environment.as_str()) {
1200 self.errors.push(Error::invalid_environment(
1201 DeclType::Collection,
1202 "environment",
1203 environment,
1204 ));
1205 }
1206 if let Some(name) = collection.name.as_ref() {
1207 let source = DependencyNode::Environment(environment.as_str());
1208 let target = DependencyNode::Collection(name.as_str());
1209 self.add_strong_dep(Some(source), Some(target));
1210 }
1211 }
1212 }
1214
1215 fn validate_environment_decl(&mut self, environment: &'a fdecl::Environment) {
1216 let name = environment.name.as_ref();
1217 check_name(name, DeclType::Environment, "name", &mut self.errors);
1218 if environment.extends.is_none() {
1219 self.errors.push(Error::missing_field(DeclType::Environment, "extends"));
1220 }
1221 if let Some(runners) = environment.runners.as_ref() {
1222 let mut registered_runners = HashSet::new();
1223 for runner in runners {
1224 self.validate_runner_registration(runner, name.clone(), &mut registered_runners);
1225 }
1226 }
1227 if let Some(resolvers) = environment.resolvers.as_ref() {
1228 let mut registered_schemes = HashSet::new();
1229 for resolver in resolvers {
1230 self.validate_resolver_registration(
1231 resolver,
1232 name.clone(),
1233 &mut registered_schemes,
1234 );
1235 }
1236 }
1237
1238 match environment.extends.as_ref() {
1239 Some(fdecl::EnvironmentExtends::None) => {
1240 if environment.stop_timeout_ms.is_none() {
1241 self.errors
1242 .push(Error::missing_field(DeclType::Environment, "stop_timeout_ms"));
1243 }
1244 }
1245 None | Some(fdecl::EnvironmentExtends::Realm) => {}
1246 }
1247
1248 if let Some(debugs) = environment.debug_capabilities.as_ref() {
1249 for debug in debugs {
1250 self.validate_environment_debug_registration(debug, name.clone());
1251 }
1252 }
1253 }
1254
1255 fn validate_runner_registration(
1256 &mut self,
1257 runner_registration: &'a fdecl::RunnerRegistration,
1258 environment_name: Option<&'a String>,
1259 runner_names: &mut HashSet<&'a str>,
1260 ) {
1261 check_name(
1262 runner_registration.source_name.as_ref(),
1263 DeclType::RunnerRegistration,
1264 "source_name",
1265 &mut self.errors,
1266 );
1267 self.validate_registration_source(
1268 environment_name,
1269 runner_registration.source.as_ref(),
1270 DeclType::RunnerRegistration,
1271 );
1272 if let (Some(fdecl::Ref::Self_(_)), Some(ref name)) =
1274 (&runner_registration.source, &runner_registration.source_name)
1275 {
1276 if !self.all_runners.contains(name as &str) {
1277 self.errors.push(Error::invalid_runner(
1278 DeclType::RunnerRegistration,
1279 "source_name",
1280 name,
1281 ));
1282 }
1283 }
1284
1285 check_name(
1286 runner_registration.target_name.as_ref(),
1287 DeclType::RunnerRegistration,
1288 "target_name",
1289 &mut self.errors,
1290 );
1291 if let Some(name) = runner_registration.target_name.as_ref() {
1292 if !runner_names.insert(name.as_str()) {
1293 self.errors.push(Error::duplicate_field(
1294 DeclType::RunnerRegistration,
1295 "target_name",
1296 name,
1297 ));
1298 }
1299 }
1300 }
1301
1302 fn validate_resolver_registration(
1303 &mut self,
1304 resolver_registration: &'a fdecl::ResolverRegistration,
1305 environment_name: Option<&'a String>,
1306 schemes: &mut HashSet<&'a str>,
1307 ) {
1308 check_name(
1309 resolver_registration.resolver.as_ref(),
1310 DeclType::ResolverRegistration,
1311 "resolver",
1312 &mut self.errors,
1313 );
1314 self.validate_registration_source(
1315 environment_name,
1316 resolver_registration.source.as_ref(),
1317 DeclType::ResolverRegistration,
1318 );
1319 check_url_scheme(
1320 resolver_registration.scheme.as_ref(),
1321 DeclType::ResolverRegistration,
1322 "scheme",
1323 &mut self.errors,
1324 );
1325 if let Some(scheme) = resolver_registration.scheme.as_ref() {
1326 if !schemes.insert(scheme.as_str()) {
1327 self.errors.push(Error::duplicate_field(
1328 DeclType::ResolverRegistration,
1329 "scheme",
1330 scheme,
1331 ));
1332 }
1333 }
1334 }
1335
1336 fn validate_registration_source(
1337 &mut self,
1338 environment_name: Option<&'a String>,
1339 source: Option<&'a fdecl::Ref>,
1340 ty: DeclType,
1341 ) {
1342 match source {
1343 Some(fdecl::Ref::Parent(_)) => {}
1344 Some(fdecl::Ref::Self_(_)) => {}
1345 Some(fdecl::Ref::Child(child_ref)) => {
1346 self.validate_child_ref(ty, "source", &child_ref, OfferType::Static);
1348 }
1349 Some(_) => {
1350 self.errors.push(Error::invalid_field(ty, "source"));
1351 }
1352 None => {
1353 self.errors.push(Error::missing_field(ty, "source"));
1354 }
1355 }
1356
1357 let source = self.source_dependency_from_ref(None, None, source);
1358 if let Some(source) = source {
1359 if let Some(env_name) = &environment_name {
1360 let target = DependencyNode::Environment(env_name);
1361 self.strong_dependencies.add_edge(source, target);
1362 }
1363 }
1364 }
1365
1366 fn validate_service_decl(&mut self, service: &'a fdecl::Service, as_builtin: bool) {
1367 if check_name(service.name.as_ref(), DeclType::Service, "name", &mut self.errors) {
1368 let name = service.name.as_ref().unwrap();
1369 if !self.all_capability_ids.insert(name) {
1370 self.errors.push(Error::duplicate_field(DeclType::Service, "name", name.as_str()));
1371 }
1372 self.all_services.insert(name);
1373 }
1374 match as_builtin {
1375 true => {
1376 if let Some(path) = service.source_path.as_ref() {
1377 self.errors.push(Error::extraneous_source_path(DeclType::Service, path))
1378 }
1379 }
1380 false => {
1381 check_path(
1382 service.source_path.as_ref(),
1383 DeclType::Service,
1384 "source_path",
1385 &mut self.errors,
1386 );
1387 }
1388 }
1389 }
1390
1391 fn validate_protocol_decl(&mut self, protocol: &'a fdecl::Protocol, as_builtin: bool) {
1392 if check_name(protocol.name.as_ref(), DeclType::Protocol, "name", &mut self.errors) {
1393 let name = protocol.name.as_ref().unwrap();
1394 if !self.all_capability_ids.insert(name) {
1395 self.errors.push(Error::duplicate_field(DeclType::Protocol, "name", name.as_str()));
1396 }
1397 self.all_protocols.insert(name);
1398 }
1399 match as_builtin {
1400 true => {
1401 if let Some(path) = protocol.source_path.as_ref() {
1402 self.errors.push(Error::extraneous_source_path(DeclType::Protocol, path))
1403 }
1404 }
1405 false => {
1406 check_path(
1407 protocol.source_path.as_ref(),
1408 DeclType::Protocol,
1409 "source_path",
1410 &mut self.errors,
1411 );
1412 }
1413 }
1414
1415 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1416 match protocol.delivery {
1417 Some(delivery) => match cm_types::DeliveryType::try_from(delivery) {
1418 Ok(_) => {}
1419 Err(_) => self.errors.push(Error::invalid_field(DeclType::Protocol, "delivery")),
1420 },
1421 None => {}
1422 }
1423 }
1424
1425 fn validate_directory_decl(&mut self, directory: &'a fdecl::Directory, as_builtin: bool) {
1426 if check_name(directory.name.as_ref(), DeclType::Directory, "name", &mut self.errors) {
1427 let name = directory.name.as_ref().unwrap();
1428 if !self.all_capability_ids.insert(name) {
1429 self.errors.push(Error::duplicate_field(
1430 DeclType::Directory,
1431 "name",
1432 name.as_str(),
1433 ));
1434 }
1435 self.all_directories.insert(name);
1436 }
1437 match as_builtin {
1438 true => {
1439 if let Some(path) = directory.source_path.as_ref() {
1440 self.errors.push(Error::extraneous_source_path(DeclType::Directory, path))
1441 }
1442 }
1443 false => {
1444 check_path(
1445 directory.source_path.as_ref(),
1446 DeclType::Directory,
1447 "source_path",
1448 &mut self.errors,
1449 );
1450 }
1451 }
1452 if directory.rights.is_none() {
1453 self.errors.push(Error::missing_field(DeclType::Directory, "rights"));
1454 }
1455 }
1456
1457 fn validate_storage_decl(&mut self, storage: &'a fdecl::Storage) {
1458 match storage.source.as_ref() {
1459 Some(fdecl::Ref::Parent(_)) => {}
1460 Some(fdecl::Ref::Self_(_)) => {}
1461 Some(fdecl::Ref::Child(child)) => {
1462 let _ =
1463 self.validate_child_ref(DeclType::Storage, "source", &child, OfferType::Static);
1464 }
1465 Some(_) => {
1466 self.errors.push(Error::invalid_field(DeclType::Storage, "source"));
1467 }
1468 None => {
1469 self.errors.push(Error::missing_field(DeclType::Storage, "source"));
1470 }
1471 };
1472 if check_name(storage.name.as_ref(), DeclType::Storage, "name", &mut self.errors) {
1473 let name = storage.name.as_ref().unwrap();
1474 if !self.all_capability_ids.insert(name) {
1475 self.errors.push(Error::duplicate_field(DeclType::Storage, "name", name.as_str()));
1476 }
1477 self.all_storages.insert(name, storage.source.as_ref());
1478 }
1479 if storage.storage_id.is_none() {
1480 self.errors.push(Error::missing_field(DeclType::Storage, "storage_id"));
1481 }
1482 check_name(
1483 storage.backing_dir.as_ref(),
1484 DeclType::Storage,
1485 "backing_dir",
1486 &mut self.errors,
1487 );
1488
1489 if let (Some(name), Some(backing_dir), Some(source)) =
1491 (storage.name.as_ref(), storage.backing_dir.as_ref(), storage.source.as_ref())
1492 {
1493 let source = self.source_dependency_from_ref(Some(backing_dir), None, Some(source));
1494 let target = Some(DependencyNode::Capability(name));
1495 self.add_strong_dep(source, target);
1496 }
1497 }
1498
1499 fn validate_runner_decl(&mut self, runner: &'a fdecl::Runner, as_builtin: bool) {
1500 if check_name(runner.name.as_ref(), DeclType::Runner, "name", &mut self.errors) {
1501 let name = runner.name.as_ref().unwrap();
1502 if !self.all_capability_ids.insert(name) {
1503 self.errors.push(Error::duplicate_field(DeclType::Runner, "name", name.as_str()));
1504 }
1505 self.all_runners.insert(name);
1506 }
1507 match as_builtin {
1508 true => {
1509 if let Some(path) = runner.source_path.as_ref() {
1510 self.errors.push(Error::extraneous_source_path(DeclType::Runner, path))
1511 }
1512 }
1513 false => {
1514 check_path(
1515 runner.source_path.as_ref(),
1516 DeclType::Runner,
1517 "source_path",
1518 &mut self.errors,
1519 );
1520 }
1521 }
1522 }
1523
1524 fn validate_resolver_decl(&mut self, resolver: &'a fdecl::Resolver, as_builtin: bool) {
1525 if check_name(resolver.name.as_ref(), DeclType::Resolver, "name", &mut self.errors) {
1526 let name = resolver.name.as_ref().unwrap();
1527 if !self.all_capability_ids.insert(name) {
1528 self.errors.push(Error::duplicate_field(DeclType::Resolver, "name", name.as_str()));
1529 }
1530 self.all_resolvers.insert(name);
1531 }
1532 match as_builtin {
1533 true => {
1534 if let Some(path) = resolver.source_path.as_ref() {
1535 self.errors.push(Error::extraneous_source_path(DeclType::Resolver, path))
1536 }
1537 }
1538 false => {
1539 check_path(
1540 resolver.source_path.as_ref(),
1541 DeclType::Resolver,
1542 "source_path",
1543 &mut self.errors,
1544 );
1545 }
1546 }
1547 }
1548
1549 #[cfg(fuchsia_api_level_at_least = "25")]
1553 fn load_dictionary_names(&mut self, dictionaries: impl Iterator<Item = &'a fdecl::Dictionary>) {
1554 for dictionary in dictionaries {
1555 let decl = DeclType::Dictionary;
1556 if check_name(dictionary.name.as_ref(), decl, "name", &mut self.errors) {
1557 let name = dictionary.name.as_ref().unwrap();
1558 if !self.all_capability_ids.insert(name) {
1559 self.errors.push(Error::duplicate_field(decl, "name", name.as_str()));
1560 }
1561 self.all_dictionaries.insert(name, &dictionary);
1562 }
1563 }
1564 }
1565
1566 #[cfg(fuchsia_api_level_at_least = "25")]
1567 fn validate_dictionary_decl(&mut self, dictionary: &'a fdecl::Dictionary) {
1568 let decl = DeclType::Dictionary;
1569 if let Some(path) = dictionary.source_path.as_ref() {
1570 if dictionary.source.is_some() {
1571 self.errors.push(Error::extraneous_field(decl, "source"));
1572 }
1573 check_path(Some(path), DeclType::Dictionary, "source_path", &mut self.errors);
1574 if let Some(name) = dictionary.name.as_ref() {
1577 self.add_strong_dep(
1578 Some(DependencyNode::Self_),
1579 Some(DependencyNode::Capability(name)),
1580 );
1581 }
1582 }
1583 }
1584
1585 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1586 fn validate_configuration_decl(&mut self, config: &'a fdecl::Configuration) {
1587 let decl = DeclType::Configuration;
1588 if check_name(config.name.as_ref(), decl, "name", &mut self.errors) {
1589 let name = config.name.as_ref().unwrap();
1590 if !self.all_capability_ids.insert(name) {
1591 self.errors.push(Error::duplicate_field(decl, "name", name.as_str()));
1592 }
1593 self.all_configs.insert(name);
1594 }
1595 }
1596
1597 fn validate_environment_debug_registration(
1598 &mut self,
1599 debug: &'a fdecl::DebugRegistration,
1600 environment_name: Option<&'a String>,
1601 ) {
1602 match debug {
1603 fdecl::DebugRegistration::Protocol(o) => {
1604 let decl = DeclType::DebugProtocolRegistration;
1605 self.validate_environment_debug_fields(
1606 decl,
1607 o.source.as_ref(),
1608 o.source_name.as_ref(),
1609 o.target_name.as_ref(),
1610 );
1611
1612 if let (Some(fdecl::Ref::Self_(_)), Some(ref name)) = (&o.source, &o.source_name) {
1613 if !self.all_protocols.contains(&name as &str) {
1614 self.errors.push(Error::invalid_field(decl, "source"));
1615 }
1616 }
1617
1618 if let Some(env_name) = &environment_name {
1619 let source = self.source_dependency_from_ref(
1620 o.source_name.as_ref(),
1621 None,
1622 o.source.as_ref(),
1623 );
1624 let target = Some(DependencyNode::Environment(env_name));
1625 self.add_strong_dep(source, target);
1626 }
1627 }
1628 _ => {
1629 self.errors.push(Error::invalid_field(DeclType::Environment, "debug"));
1630 }
1631 }
1632 }
1633
1634 fn validate_environment_debug_fields(
1635 &mut self,
1636 decl: DeclType,
1637 source: Option<&fdecl::Ref>,
1638 source_name: Option<&String>,
1639 target_name: Option<&'a String>,
1640 ) {
1641 match source {
1643 Some(fdecl::Ref::Parent(_)) => {}
1644 Some(fdecl::Ref::Self_(_)) => {}
1645 Some(fdecl::Ref::Framework(_)) => {}
1646 Some(fdecl::Ref::Child(child)) => {
1647 let _ = self.validate_child_ref(decl, "source", &child, OfferType::Static);
1648 }
1649 Some(_) => self.errors.push(Error::invalid_field(decl, "source")),
1650 None => self.errors.push(Error::missing_field(decl, "source")),
1651 }
1652 check_name(source_name, decl, "source_name", &mut self.errors);
1653 check_name(target_name, decl, "target_name", &mut self.errors);
1654 }
1655
1656 fn validate_event_stream_decl(&mut self, event: &'a fdecl::EventStream) {
1657 if check_name(event.name.as_ref(), DeclType::EventStream, "name", &mut self.errors) {
1658 let name = event.name.as_ref().unwrap();
1659 if !self.all_capability_ids.insert(name) {
1660 self.errors.push(Error::duplicate_field(
1661 DeclType::EventStream,
1662 "name",
1663 name.as_str(),
1664 ));
1665 }
1666 }
1667 }
1668
1669 fn validate_source_collection(
1670 &mut self,
1671 collection: &fdecl::CollectionRef,
1672 decl_type: DeclType,
1673 ) -> bool {
1674 let num_errors = self.errors.len();
1675 if check_name(Some(&collection.name), decl_type, "source.collection.name", &mut self.errors)
1676 && !self.all_collections.contains(&collection.name as &str)
1677 {
1678 self.errors.push(Error::invalid_collection(
1679 decl_type,
1680 "source",
1681 &collection.name as &str,
1682 ));
1683 }
1684 num_errors == self.errors.len()
1685 }
1686
1687 fn validate_filtered_service_fields(
1688 &mut self,
1689 decl_type: DeclType,
1690 source_instance_filter: Option<&Vec<String>>,
1691 renamed_instances: Option<&Vec<fdecl::NameMapping>>,
1692 ) {
1693 if let Some(source_instance_filter) = source_instance_filter {
1694 if source_instance_filter.is_empty() {
1695 self.errors.push(Error::invalid_field(decl_type, "source_instance_filter"));
1698 }
1699 for name in source_instance_filter {
1700 check_name(Some(name), decl_type, "source_instance_filter", &mut self.errors);
1701 }
1702 }
1703 if let Some(renamed_instances) = renamed_instances {
1704 let mut seen_target_names = HashSet::<String>::new();
1706 for mapping in renamed_instances {
1707 check_name(
1708 Some(&mapping.source_name),
1709 decl_type,
1710 "renamed_instances.source_name",
1711 &mut self.errors,
1712 );
1713 check_name(
1714 Some(&mapping.target_name),
1715 decl_type,
1716 "renamed_instances.target_name",
1717 &mut self.errors,
1718 );
1719 if !seen_target_names.insert(mapping.target_name.clone()) {
1720 self.errors.push(Error::invalid_field(decl_type, "renamed_instances"));
1721 break;
1722 }
1723 }
1724 }
1725 }
1726
1727 fn validate_source_capability(
1728 &mut self,
1729 capability: &fdecl::CapabilityRef,
1730 decl_type: DeclType,
1731 field: &str,
1732 ) -> bool {
1733 let num_errors = self.errors.len();
1734 if check_name(Some(&capability.name), decl_type, "source.capability.name", &mut self.errors)
1735 && !self.all_capability_ids.contains(capability.name.as_str())
1736 {
1737 self.errors.push(Error::invalid_capability(decl_type, field, &capability.name));
1738 }
1739 num_errors == self.errors.len()
1740 }
1741
1742 fn make_group_key(
1746 target_name: Option<&'a String>,
1747 target: Option<&'a fdecl::Ref>,
1748 ) -> Option<(&'a str, RefKey<'a>)> {
1749 if target_name.is_none() {
1750 return None;
1751 }
1752 let target_name = target_name.unwrap().as_str();
1753 if target.is_none() {
1754 return None;
1755 }
1756 let target = match target.unwrap() {
1757 fdecl::Ref::Parent(_) => RefKey::Parent,
1758 fdecl::Ref::Self_(_) => RefKey::Self_,
1759 fdecl::Ref::Child(r) => RefKey::Child(r.name.as_str()),
1760 fdecl::Ref::Collection(r) => RefKey::Collection(r.name.as_str()),
1761 fdecl::Ref::Framework(_) => RefKey::Framework,
1762 fdecl::Ref::Capability(_) => RefKey::Capability,
1763 fdecl::Ref::Debug(_) => RefKey::Debug,
1764 fdecl::RefUnknown!() => {
1765 return None;
1766 }
1767 };
1768 Some((target_name, target))
1769 }
1770
1771 fn validate_aggregation_has_same_availability(
1772 &mut self,
1773 route_group: &Vec<impl HasAvailability>,
1774 ) {
1775 let availability_of_sources: BTreeSet<_> =
1777 route_group.iter().map(|r| r.availability()).collect();
1778
1779 if availability_of_sources.len() > 1 {
1781 self.errors.push(Error::different_availability_in_aggregation(
1782 availability_of_sources.into_iter().collect(),
1783 ));
1784 }
1785 }
1786
1787 fn validate_expose_group(&mut self, exposes: &'a Vec<fdecl::Expose>) {
1790 let mut expose_groups: HashMap<_, Vec<fdecl::ExposeService>> = HashMap::new();
1791 let service_exposes = exposes.into_iter().filter_map(|o| {
1792 if let fdecl::Expose::Service(s) = o {
1793 Some(s)
1794 } else {
1795 None
1796 }
1797 });
1798 for expose in service_exposes {
1799 let key = Self::make_group_key(expose.target_name.as_ref(), expose.target.as_ref());
1800 if let Some(key) = key {
1801 expose_groups.entry(key).or_insert_with(|| vec![]).push(expose.clone());
1802 }
1803 }
1804 for expose_group in expose_groups.into_values() {
1805 if expose_group.len() == 1 {
1806 continue;
1809 }
1810
1811 self.validate_aggregation_has_same_availability(&expose_group);
1812 }
1813 }
1814
1815 fn validate_expose_decl(
1816 &mut self,
1817 expose: &'a fdecl::Expose,
1818 expose_to_parent_ids: &mut HashMap<&'a str, AllowableIds>,
1819 expose_to_framework_ids: &mut HashMap<&'a str, AllowableIds>,
1820 ) {
1821 match expose {
1822 fdecl::Expose::Service(e) => {
1823 let decl = DeclType::ExposeService;
1824 self.validate_expose_fields(
1825 decl,
1826 AllowableIds::Many,
1827 CollectionSource::Allow,
1828 Self::service_checker,
1829 e.source.as_ref(),
1830 e.source_name.as_ref(),
1831 get_source_dictionary!(e),
1832 e.target.as_ref(),
1833 e.target_name.as_ref(),
1834 e.availability.as_ref(),
1835 expose_to_parent_ids,
1836 expose_to_framework_ids,
1837 );
1838 }
1839 fdecl::Expose::Protocol(e) => {
1840 let decl = DeclType::ExposeProtocol;
1841 self.validate_expose_fields(
1842 decl,
1843 AllowableIds::One,
1844 CollectionSource::Deny,
1845 Self::protocol_checker,
1846 e.source.as_ref(),
1847 e.source_name.as_ref(),
1848 get_source_dictionary!(e),
1849 e.target.as_ref(),
1850 e.target_name.as_ref(),
1851 e.availability.as_ref(),
1852 expose_to_parent_ids,
1853 expose_to_framework_ids,
1854 );
1855 }
1856 fdecl::Expose::Directory(e) => {
1857 let decl = DeclType::ExposeDirectory;
1858 self.validate_expose_fields(
1859 decl,
1860 AllowableIds::One,
1861 CollectionSource::Deny,
1862 Self::directory_checker,
1863 e.source.as_ref(),
1864 e.source_name.as_ref(),
1865 get_source_dictionary!(e),
1866 e.target.as_ref(),
1867 e.target_name.as_ref(),
1868 e.availability.as_ref(),
1869 expose_to_parent_ids,
1870 expose_to_framework_ids,
1871 );
1872
1873 match e.target.as_ref() {
1876 Some(fdecl::Ref::Framework(_)) => {
1877 if e.subdir.is_some() {
1878 self.errors.push(Error::invalid_field(decl, "subdir"));
1879 }
1880 }
1881 _ => {}
1882 }
1883
1884 if let Some(subdir) = e.subdir.as_ref() {
1885 check_relative_path(Some(subdir), decl, "subdir", &mut self.errors);
1886 }
1887 }
1888 fdecl::Expose::Runner(e) => {
1889 let decl = DeclType::ExposeRunner;
1890 self.validate_expose_fields(
1891 decl,
1892 AllowableIds::One,
1893 CollectionSource::Deny,
1894 Self::runner_checker,
1895 e.source.as_ref(),
1896 e.source_name.as_ref(),
1897 get_source_dictionary!(e),
1898 e.target.as_ref(),
1899 e.target_name.as_ref(),
1900 Some(&fdecl::Availability::Required),
1901 expose_to_parent_ids,
1902 expose_to_framework_ids,
1903 );
1904 }
1905 fdecl::Expose::Resolver(e) => {
1906 let decl = DeclType::ExposeResolver;
1907 self.validate_expose_fields(
1908 decl,
1909 AllowableIds::One,
1910 CollectionSource::Deny,
1911 Self::resolver_checker,
1912 e.source.as_ref(),
1913 e.source_name.as_ref(),
1914 get_source_dictionary!(e),
1915 e.target.as_ref(),
1916 e.target_name.as_ref(),
1917 Some(&fdecl::Availability::Required),
1918 expose_to_parent_ids,
1919 expose_to_framework_ids,
1920 );
1921 }
1922 #[cfg(fuchsia_api_level_at_least = "25")]
1923 fdecl::Expose::Dictionary(e) => {
1924 let decl = DeclType::ExposeDictionary;
1925 self.validate_expose_fields(
1926 decl,
1927 AllowableIds::One,
1928 CollectionSource::Deny,
1929 Self::dictionary_checker,
1930 e.source.as_ref(),
1931 e.source_name.as_ref(),
1932 get_source_dictionary!(e),
1933 e.target.as_ref(),
1934 e.target_name.as_ref(),
1935 Some(&fdecl::Availability::Required),
1936 expose_to_parent_ids,
1937 expose_to_framework_ids,
1938 );
1939 }
1940 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1941 fdecl::Expose::Config(e) => {
1942 let decl = DeclType::ExposeConfig;
1943 self.validate_expose_fields(
1944 decl,
1945 AllowableIds::One,
1946 CollectionSource::Deny,
1947 Self::config_checker,
1948 e.source.as_ref(),
1949 e.source_name.as_ref(),
1950 None,
1951 e.target.as_ref(),
1952 e.target_name.as_ref(),
1953 e.availability.as_ref(),
1954 expose_to_parent_ids,
1955 expose_to_framework_ids,
1956 );
1957 }
1958 _ => {
1959 self.errors.push(Error::invalid_field(DeclType::Component, "expose"));
1960 }
1961 }
1962 }
1963
1964 fn validate_expose_fields(
1965 &mut self,
1966 decl: DeclType,
1967 allowable_ids: AllowableIds,
1968 collection_source: CollectionSource,
1969 capability_checker: impl Fn(&Self) -> &dyn Container,
1973 source: Option<&fdecl::Ref>,
1974 source_name: Option<&String>,
1975 source_dictionary: Option<&String>,
1976 target: Option<&fdecl::Ref>,
1977 target_name: Option<&'a String>,
1978 availability: Option<&fdecl::Availability>,
1979 expose_to_parent_ids: &mut HashMap<&'a str, AllowableIds>,
1980 expose_to_framework_ids: &mut HashMap<&'a str, AllowableIds>,
1981 ) {
1982 self.validate_expose_source(decl, collection_source, source, source_dictionary);
1983 check_route_availability(decl, availability, source, source_name, &mut self.errors);
1984 match target {
1985 Some(r) => match r {
1986 fdecl::Ref::Parent(_) => {}
1987 fdecl::Ref::Framework(_) => {}
1988 _ => {
1989 self.errors.push(Error::invalid_field(decl, "target"));
1990 }
1991 },
1992 None => {
1993 self.errors.push(Error::missing_field(decl, "target"));
1994 }
1995 }
1996 check_name(source_name, decl, "source_name", &mut self.errors);
1997 if source_dictionary.is_some() {
1998 check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
1999 }
2000 if check_name(target_name, decl, "target_name", &mut self.errors) {
2001 let maybe_ids_set = match target {
2002 Some(fdecl::Ref::Parent(_)) => Some(expose_to_parent_ids),
2003 Some(fdecl::Ref::Framework(_)) => Some(expose_to_framework_ids),
2004 _ => None,
2005 };
2006 if let Some(ids_set) = maybe_ids_set {
2007 let target_name = target_name.unwrap();
2008 if let Some(prev_state) = ids_set.insert(target_name, allowable_ids) {
2009 if prev_state == AllowableIds::One || prev_state != allowable_ids {
2010 self.errors.push(Error::duplicate_field(decl, "target_name", target_name));
2011 }
2012 }
2013 }
2014 }
2015
2016 self.validate_route_from_self(
2017 decl,
2018 source,
2019 source_name,
2020 source_dictionary,
2021 capability_checker,
2022 );
2023 }
2024
2025 fn validate_expose_source(
2026 &mut self,
2027 decl: DeclType,
2028 collection_source: CollectionSource,
2029 source: Option<&fdecl::Ref>,
2030 source_dictionary: Option<&String>,
2031 ) {
2032 match (source, source_dictionary) {
2033 (Some(fdecl::Ref::Self_(_)), _) => {}
2035 (Some(fdecl::Ref::Child(child)), _) => {
2036 let _ = self.validate_child_ref(decl, "source", &child, OfferType::Static);
2037 }
2038 (Some(fdecl::Ref::VoidType(_)), None) => {}
2040 (Some(fdecl::Ref::Framework(_)), None) => {}
2041 (Some(fdecl::Ref::Capability(c)), None) => {
2042 self.validate_source_capability(c, decl, "source");
2043 }
2044 (Some(fdecl::Ref::Collection(c)), None)
2045 if collection_source == CollectionSource::Allow =>
2046 {
2047 self.validate_source_collection(c, decl);
2048 }
2049 (None, _) => {
2051 self.errors.push(Error::missing_field(decl, "source"));
2052 }
2053 (_, _) => {
2055 self.errors.push(Error::invalid_field(decl, "source"));
2056 }
2057 }
2058 }
2059
2060 fn add_strong_dep(
2067 &mut self,
2068 source: Option<DependencyNode<'a>>,
2069 target: Option<DependencyNode<'a>>,
2070 ) {
2071 if source.is_none() || target.is_none() {
2072 return;
2073 }
2074 let source = source.unwrap();
2075 let target = target.unwrap();
2076 match (source, target) {
2077 (DependencyNode::Self_, DependencyNode::Self_) => {
2078 }
2080 (source, target) => {
2081 self.strong_dependencies.add_edge(source, target);
2082 }
2083 }
2084 }
2085
2086 fn validate_offer_group(&mut self, offers: &'a Vec<fdecl::Offer>, offer_type: OfferType) {
2089 let mut offer_groups: HashMap<_, Vec<fdecl::OfferService>> = HashMap::new();
2090 let service_offers = offers.into_iter().filter_map(|o| {
2091 if let fdecl::Offer::Service(s) = o {
2092 Some(s)
2093 } else {
2094 None
2095 }
2096 });
2097 for offer in service_offers {
2098 let key = Self::make_group_key(offer.target_name.as_ref(), offer.target.as_ref());
2099 if let Some(key) = key {
2100 offer_groups.entry(key).or_insert_with(|| vec![]).push(offer.clone());
2101 }
2102 }
2103 for offer_group in offer_groups.into_values() {
2104 if offer_group.len() == 1 {
2105 continue;
2108 }
2109
2110 self.validate_aggregation_has_same_availability(&offer_group);
2111
2112 let mut source_instance_filter_entries: HashSet<String> = HashSet::new();
2113 let mut service_source_names: HashSet<String> = HashSet::new();
2114 for o in offer_group {
2115 match (o.source_instance_filter, offer_type) {
2117 (Some(source_instance_filter), _) => {
2118 for instance_name in source_instance_filter {
2119 if !source_instance_filter_entries.insert(instance_name.clone()) {
2120 self.errors.push(Error::invalid_aggregate_offer(format!(
2123 "Conflicting source_instance_filter in aggregate service \
2124 offer, instance_name '{}' seen in filter lists multiple times",
2125 instance_name,
2126 )));
2127 }
2128 }
2129 }
2130 (None, OfferType::Static) => {}
2131 (None, OfferType::Dynamic) => {
2132 self.errors.push(Error::invalid_aggregate_offer(
2134 "source_instance_filter must be set for dynamic aggregate service \
2135 offers",
2136 ));
2137 }
2138 }
2139 service_source_names.insert(
2140 o.source_name
2141 .expect("Offer Service declarations must always contain source_name"),
2142 );
2143 }
2144
2145 if service_source_names.len() > 1 {
2146 self.errors.push(Error::invalid_aggregate_offer(format!(
2147 "All aggregate service offers must have the same source_name, saw {}. Use \
2148 renamed_instances to rename instance names to avoid conflict.",
2149 service_source_names.into_iter().sorted().collect::<Vec<String>>().join(", ")
2150 )));
2151 }
2152 }
2153 }
2154
2155 fn validate_offer_decl(&mut self, offer: &'a fdecl::Offer, offer_type: OfferType) {
2156 match offer {
2157 fdecl::Offer::Service(o) => {
2158 let decl = DeclType::OfferService;
2159 self.validate_offer_fields(
2160 decl,
2161 AllowableIds::Many,
2162 CollectionSource::Allow,
2163 Self::service_checker,
2164 o.source.as_ref(),
2165 o.source_name.as_ref(),
2166 get_source_dictionary!(o),
2167 o.target.as_ref(),
2168 o.target_name.as_ref(),
2169 #[cfg(fuchsia_api_level_at_least = "HEAD")]
2170 Some(o.dependency_type.as_ref().unwrap_or(&fdecl::DependencyType::Strong)),
2171 #[cfg(fuchsia_api_level_less_than = "HEAD")]
2172 Some(&fdecl::DependencyType::Strong),
2173 o.availability.as_ref(),
2174 offer_type,
2175 );
2176 self.validate_filtered_service_fields(
2177 decl,
2178 o.source_instance_filter.as_ref(),
2179 o.renamed_instances.as_ref(),
2180 );
2181 }
2182 fdecl::Offer::Protocol(o) => {
2183 let decl = DeclType::OfferProtocol;
2184 self.validate_offer_fields(
2185 decl,
2186 AllowableIds::One,
2187 CollectionSource::Deny,
2188 Self::protocol_checker,
2189 o.source.as_ref(),
2190 o.source_name.as_ref(),
2191 get_source_dictionary!(o),
2192 o.target.as_ref(),
2193 o.target_name.as_ref(),
2194 o.dependency_type.as_ref(),
2195 o.availability.as_ref(),
2196 offer_type,
2197 );
2198 }
2199 fdecl::Offer::Directory(o) => {
2200 let decl = DeclType::OfferDirectory;
2201 self.validate_offer_fields(
2202 decl,
2203 AllowableIds::One,
2204 CollectionSource::Deny,
2205 Self::directory_checker,
2206 o.source.as_ref(),
2207 o.source_name.as_ref(),
2208 get_source_dictionary!(o),
2209 o.target.as_ref(),
2210 o.target_name.as_ref(),
2211 o.dependency_type.as_ref(),
2212 o.availability.as_ref(),
2213 offer_type,
2214 );
2215 if let Some(subdir) = o.subdir.as_ref() {
2216 check_relative_path(
2217 Some(subdir),
2218 DeclType::OfferDirectory,
2219 "subdir",
2220 &mut self.errors,
2221 );
2222 }
2223 }
2224 fdecl::Offer::Storage(o) => {
2225 let decl = DeclType::OfferStorage;
2226 self.validate_storage_offer_fields(
2227 decl,
2228 Self::storage_checker,
2229 o.source.as_ref(),
2230 o.source_name.as_ref(),
2231 o.target.as_ref(),
2232 o.target_name.as_ref(),
2233 o.availability.as_ref(),
2234 offer_type,
2235 );
2236 self.add_strong_dep(
2237 self.source_dependency_from_ref(
2238 o.source_name.as_ref(),
2239 None,
2240 o.source.as_ref(),
2241 ),
2242 self.target_dependency_from_ref(o.target.as_ref()),
2243 );
2244 }
2245 fdecl::Offer::Runner(o) => {
2246 let decl = DeclType::OfferRunner;
2247 self.validate_offer_fields(
2248 decl,
2249 AllowableIds::One,
2250 CollectionSource::Deny,
2251 Self::runner_checker,
2252 o.source.as_ref(),
2253 o.source_name.as_ref(),
2254 get_source_dictionary!(o),
2255 o.target.as_ref(),
2256 o.target_name.as_ref(),
2257 Some(&fdecl::DependencyType::Strong),
2258 Some(&fdecl::Availability::Required),
2259 offer_type,
2260 );
2261 }
2262 fdecl::Offer::Resolver(o) => {
2263 let decl = DeclType::OfferResolver;
2264 self.validate_offer_fields(
2265 decl,
2266 AllowableIds::One,
2267 CollectionSource::Deny,
2268 Self::resolver_checker,
2269 o.source.as_ref(),
2270 o.source_name.as_ref(),
2271 get_source_dictionary!(o),
2272 o.target.as_ref(),
2273 o.target_name.as_ref(),
2274 Some(&fdecl::DependencyType::Strong),
2275 Some(&fdecl::Availability::Required),
2276 offer_type,
2277 );
2278 }
2279 fdecl::Offer::EventStream(e) => {
2280 self.validate_event_stream_offer_fields(e, offer_type);
2281 }
2282 #[cfg(fuchsia_api_level_at_least = "25")]
2283 fdecl::Offer::Dictionary(o) => {
2284 let decl = DeclType::OfferDictionary;
2285 self.validate_offer_fields(
2286 decl,
2287 AllowableIds::One,
2288 CollectionSource::Deny,
2289 Self::dictionary_checker,
2290 o.source.as_ref(),
2291 o.source_name.as_ref(),
2292 get_source_dictionary!(o),
2293 o.target.as_ref(),
2294 o.target_name.as_ref(),
2295 o.dependency_type.as_ref(),
2296 o.availability.as_ref(),
2297 offer_type,
2298 );
2299 }
2300 #[cfg(fuchsia_api_level_at_least = "HEAD")]
2301 fdecl::Offer::Config(o) => {
2302 let decl = DeclType::OfferConfig;
2303 self.validate_offer_fields(
2304 decl,
2305 AllowableIds::One,
2306 CollectionSource::Deny,
2307 Self::config_checker,
2308 o.source.as_ref(),
2309 o.source_name.as_ref(),
2310 None,
2311 o.target.as_ref(),
2312 o.target_name.as_ref(),
2313 Some(&fdecl::DependencyType::Strong),
2314 o.availability.as_ref(),
2315 offer_type,
2316 );
2317 }
2318 fdecl::OfferUnknown!() => {
2319 self.errors.push(Error::invalid_field(DeclType::Component, "offer"));
2320 }
2321 }
2322 }
2323
2324 fn validate_offer_fields(
2325 &mut self,
2326 decl: DeclType,
2327 allowable_names: AllowableIds,
2328 collection_source: CollectionSource,
2329 capability_checker: impl Fn(&Self) -> &dyn Container,
2333 source: Option<&'a fdecl::Ref>,
2334 source_name: Option<&'a String>,
2335 source_dictionary: Option<&'a String>,
2336 target: Option<&'a fdecl::Ref>,
2337 target_name: Option<&'a String>,
2338 dependency_type: Option<&'a fdecl::DependencyType>,
2339 availability: Option<&'a fdecl::Availability>,
2340 offer_type: OfferType,
2341 ) {
2342 self.validate_offer_source(decl, collection_source, source, source_dictionary, offer_type);
2343 check_route_availability(decl, availability, source, source_name, &mut self.errors);
2344 check_offer_name(source_name, decl, "source_name", offer_type, &mut self.errors);
2345 if source_dictionary.is_some() {
2346 check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
2347 }
2348 self.validate_offer_target(decl, allowable_names, target, target_name, offer_type);
2349 check_offer_name(target_name, decl, "target_name", offer_type, &mut self.errors);
2350
2351 if dependency_type.is_none() {
2352 self.errors.push(Error::missing_field(decl, "dependency_type"));
2353 } else if dependency_type == Some(&fdecl::DependencyType::Strong) {
2354 self.add_strong_dep(
2355 self.source_dependency_from_ref(source_name, source_dictionary, source),
2356 self.target_dependency_from_ref(target),
2357 );
2358
2359 if let Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: collection })) =
2362 target.as_ref()
2363 {
2364 for name in self.dynamic_children_in_collection(&collection) {
2365 self.add_strong_dep(
2366 self.source_dependency_from_ref(source_name, source_dictionary, source),
2367 Some(DependencyNode::Child(name, Some(&collection))),
2368 );
2369 }
2370 }
2371 }
2372
2373 if let Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: collection })) = source {
2376 for name in self.dynamic_children_in_collection(&collection) {
2377 self.add_strong_dep(
2378 Some(DependencyNode::Child(&name, Some(&collection))),
2379 Some(DependencyNode::Collection(collection)),
2380 );
2381 }
2382 }
2383
2384 self.validate_route_from_self(
2385 decl,
2386 source,
2387 source_name,
2388 source_dictionary,
2389 capability_checker,
2390 );
2391 }
2392
2393 fn validate_offer_source(
2394 &mut self,
2395 decl: DeclType,
2396 collection_source: CollectionSource,
2397 source: Option<&'a fdecl::Ref>,
2398 source_dictionary: Option<&'a String>,
2399 offer_type: OfferType,
2400 ) {
2401 match (source, source_dictionary) {
2402 (Some(fdecl::Ref::Parent(_)), _) => {}
2404 (Some(fdecl::Ref::Self_(_)), _) => {}
2405 (Some(fdecl::Ref::Child(child)), _) => {
2406 self.validate_child_ref(decl, "source", &child, offer_type);
2407 }
2408 (Some(fdecl::Ref::VoidType(_)), None) => {}
2410 (Some(fdecl::Ref::Framework(_)), None) => {}
2411 (Some(fdecl::Ref::Capability(c)), None) => {
2412 self.validate_source_capability(c, decl, "source");
2413 }
2414 (Some(fdecl::Ref::Collection(c)), None)
2415 if collection_source == CollectionSource::Allow =>
2416 {
2417 self.validate_source_collection(c, decl);
2418 }
2419 (None, _) => self.errors.push(Error::missing_field(decl, "source")),
2421 (_, _) => self.errors.push(Error::invalid_field(decl, "source")),
2423 }
2424 }
2425
2426 fn validate_storage_offer_fields(
2427 &mut self,
2428 decl: DeclType,
2429 capability_checker: impl Fn(&Self) -> &dyn Container,
2433 source: Option<&'a fdecl::Ref>,
2434 source_name: Option<&'a String>,
2435 target: Option<&'a fdecl::Ref>,
2436 target_name: Option<&'a String>,
2437 availability: Option<&fdecl::Availability>,
2438 offer_type: OfferType,
2439 ) {
2440 match source {
2441 Some(fdecl::Ref::Parent(_) | fdecl::Ref::VoidType(_) | fdecl::Ref::Self_(_)) => {}
2442 Some(_) => {
2443 self.errors.push(Error::invalid_field(decl, "source"));
2444 }
2445 None => {
2446 self.errors.push(Error::missing_field(decl, "source"));
2447 }
2448 }
2449 check_route_availability(decl, availability, source, source_name, &mut self.errors);
2450 check_offer_name(source_name, decl, "source_name", offer_type, &mut self.errors);
2451 self.validate_offer_target(decl, AllowableIds::One, target, target_name, offer_type);
2452 check_offer_name(target_name, decl, "target_name", offer_type, &mut self.errors);
2453
2454 if let (Some(fdecl::Ref::Self_(_)), Some(name)) = (source, source_name) {
2455 if !(capability_checker)(self).contains(name) {
2456 self.errors.push(Error::invalid_capability(decl, "source", name));
2457 }
2458 }
2459 }
2460
2461 fn validate_event_stream_offer_fields(
2462 &mut self,
2463 event_stream: &'a fdecl::OfferEventStream,
2464 offer_type: OfferType,
2465 ) {
2466 let decl = DeclType::OfferEventStream;
2467 check_name(event_stream.source_name.as_ref(), decl, "source_name", &mut self.errors);
2468 if event_stream.target == Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})) {
2469 self.errors.push(Error::invalid_field(DeclType::OfferEventStream, "target"));
2471 }
2472 if let Some(scope) = &event_stream.scope {
2473 if scope.is_empty() {
2474 self.errors.push(Error::invalid_field(decl, "scope"));
2475 }
2476 for value in scope {
2477 match value {
2478 fdecl::Ref::Child(child) => {
2479 self.validate_child_ref(
2480 DeclType::OfferEventStream,
2481 "scope",
2482 &child,
2483 offer_type,
2484 );
2485 }
2486 fdecl::Ref::Collection(collection) => {
2487 self.validate_collection_ref(
2488 DeclType::OfferEventStream,
2489 "scope",
2490 &collection,
2491 );
2492 }
2493 _ => {
2494 self.errors.push(Error::invalid_field(DeclType::OfferEventStream, "scope"));
2495 }
2496 }
2497 }
2498 }
2499 match event_stream.source {
2501 Some(
2502 fdecl::Ref::Parent(_)
2503 | fdecl::Ref::Framework(_)
2504 | fdecl::Ref::Child(_)
2505 | fdecl::Ref::VoidType(_),
2506 ) => {}
2507 Some(_) => {
2508 self.errors.push(Error::invalid_field(decl, "source"));
2509 }
2510 None => {
2511 self.errors.push(Error::missing_field(decl, "source"));
2512 }
2513 };
2514
2515 check_route_availability(
2516 decl,
2517 event_stream.availability.as_ref(),
2518 event_stream.source.as_ref(),
2519 event_stream.source_name.as_ref(),
2520 &mut self.errors,
2521 );
2522
2523 self.validate_offer_target(
2524 decl,
2525 AllowableIds::One,
2526 event_stream.target.as_ref(),
2527 event_stream.target_name.as_ref(),
2528 offer_type,
2529 );
2530 check_name(event_stream.target_name.as_ref(), decl, "target_name", &mut self.errors);
2531 }
2532
2533 fn validate_child_ref(
2535 &mut self,
2536 decl: DeclType,
2537 field_name: &str,
2538 child: &fdecl::ChildRef,
2539 offer_type: OfferType,
2540 ) -> bool {
2541 if offer_type == OfferType::Dynamic && child.collection.is_some() {
2542 return self.validate_dynamic_child_ref(decl, field_name, child);
2543 }
2544 let mut valid = true;
2548 if !check_name(
2549 Some(&child.name),
2550 decl,
2551 &format!("{}.child.name", field_name),
2552 &mut self.errors,
2553 ) {
2554 valid = false;
2555 }
2556 if child.collection.is_some() {
2557 self.errors
2558 .push(Error::extraneous_field(decl, format!("{}.child.collection", field_name)));
2559 valid = false;
2560 }
2561 if !valid {
2562 return false;
2563 }
2564
2565 let name: &str = &child.name;
2567 if !self.all_children.contains_key(name) {
2568 self.errors.push(Error::invalid_child(decl, field_name, name));
2569 return false;
2570 }
2571
2572 true
2573 }
2574
2575 fn validate_dynamic_child_ref(
2580 &mut self,
2581 decl: DeclType,
2582 field_name: &str,
2583 child: &fdecl::ChildRef,
2584 ) -> bool {
2585 let mut valid = true;
2589 if !check_dynamic_name(
2590 Some(&child.name),
2591 decl,
2592 &format!("{}.child.name", field_name),
2593 &mut self.errors,
2594 ) {
2595 valid = false;
2596 }
2597 if !check_name(
2598 child.collection.as_ref(),
2599 decl,
2600 &format!("{}.child.collection", field_name),
2601 &mut self.errors,
2602 ) {
2603 valid = false;
2604 }
2605 valid
2606 }
2607
2608 fn validate_collection_ref(
2610 &mut self,
2611 decl: DeclType,
2612 field_name: &str,
2613 collection: &fdecl::CollectionRef,
2614 ) -> bool {
2615 if !check_name(
2617 Some(&collection.name),
2618 decl,
2619 &format!("{}.collection.name", field_name),
2620 &mut self.errors,
2621 ) {
2622 return false;
2623 }
2624
2625 if !self.all_collections.contains(&collection.name as &str) {
2627 self.errors.push(Error::invalid_collection(decl, field_name, &collection.name as &str));
2628 return false;
2629 }
2630
2631 true
2632 }
2633
2634 fn validate_offer_target(
2635 &mut self,
2636 decl: DeclType,
2637 allowable_names: AllowableIds,
2638 target: Option<&'a fdecl::Ref>,
2639 target_name: Option<&'a String>,
2640 offer_type: OfferType,
2641 ) {
2642 match target {
2643 Some(fdecl::Ref::Child(c)) => {
2644 self.validate_target_child(decl, allowable_names, c, target_name, offer_type);
2645 }
2646 Some(fdecl::Ref::Collection(c)) => {
2647 self.validate_target_collection(decl, allowable_names, c, target_name);
2648 }
2649 Some(fdecl::Ref::Capability(c)) => {
2650 #[cfg(fuchsia_api_level_at_least = "25")]
2652 if let Some(d) = self.all_dictionaries.get(&c.name.as_str()) {
2653 if d.source_path.is_some() {
2654 self.errors.push(Error::invalid_field(decl, "target"));
2657 }
2658 } else {
2659 self.errors.push(Error::invalid_field(decl, "target"));
2660 }
2661 #[cfg(not(fuchsia_api_level_at_least = "25"))]
2662 {
2663 let _ = c;
2664 self.errors.push(Error::invalid_field(decl, "target"));
2665 }
2666 }
2667 Some(_) => {
2668 self.errors.push(Error::invalid_field(decl, "target"));
2669 }
2670 None => {
2671 self.errors.push(Error::missing_field(decl, "target"));
2672 }
2673 }
2674 }
2675
2676 fn validate_target_child(
2677 &mut self,
2678 decl: DeclType,
2679 allowable_names: AllowableIds,
2680 child: &'a fdecl::ChildRef,
2681 target_name: Option<&'a String>,
2682 offer_type: OfferType,
2683 ) {
2684 if !self.validate_child_ref(decl, "target", child, offer_type) {
2685 return;
2686 }
2687 if let Some(target_name) = target_name {
2688 let names_for_target = self
2689 .target_ids
2690 .entry(TargetId::Component(
2691 &child.name,
2692 child.collection.as_ref().map(|s| s.as_str()),
2693 ))
2694 .or_insert(HashMap::new());
2695 if let Some(prev_state) = names_for_target.insert(target_name, allowable_names) {
2696 if prev_state == AllowableIds::One || prev_state != allowable_names {
2697 self.errors.push(Error::duplicate_field(
2698 decl,
2699 "target_name",
2700 target_name as &str,
2701 ));
2702 }
2703 }
2704 if let Some(collection) = child.collection.as_ref() {
2705 if let Some(names_for_target) =
2706 self.target_ids.get(&TargetId::Collection(&collection))
2707 {
2708 if names_for_target.contains_key(&target_name.as_str()) {
2709 self.errors.push(Error::duplicate_field(
2711 decl,
2712 "target_name",
2713 target_name as &str,
2714 ));
2715 }
2716 }
2717 }
2718 }
2719 }
2720
2721 fn validate_target_collection(
2722 &mut self,
2723 decl: DeclType,
2724 allowable_names: AllowableIds,
2725 collection: &'a fdecl::CollectionRef,
2726 target_name: Option<&'a String>,
2727 ) {
2728 if !self.validate_collection_ref(decl, "target", &collection) {
2729 return;
2730 }
2731 if let Some(target_name) = target_name {
2732 let names_for_target = self
2733 .target_ids
2734 .entry(TargetId::Collection(&collection.name))
2735 .or_insert(HashMap::new());
2736 if let Some(prev_state) = names_for_target.insert(target_name, allowable_names) {
2737 if prev_state == AllowableIds::One || prev_state != allowable_names {
2738 self.errors.push(Error::duplicate_field(
2739 decl,
2740 "target_name",
2741 target_name as &str,
2742 ));
2743 }
2744 }
2745 }
2746 }
2747
2748 fn validate_route_from_self(
2749 &mut self,
2750 decl: DeclType,
2751 source: Option<&fdecl::Ref>,
2752 source_name: Option<&String>,
2753 source_dictionary: Option<&String>,
2754 capability_checker: impl Fn(&Self) -> &dyn Container,
2755 ) {
2756 let (Some(fdecl::Ref::Self_(_)), Some(name)) = (source, source_name) else {
2757 return;
2758 };
2759 match source_dictionary {
2760 Some(source_dictionary) => {
2761 #[cfg(fuchsia_api_level_at_least = "25")]
2762 {
2763 use cm_types::IterablePath;
2764 if let Ok(path) = cm_types::RelativePath::new(source_dictionary) {
2765 if let Some(first_segment) = path.iter_segments().next().map(|s| s.as_str())
2766 {
2767 if !self.all_dictionaries.contains_key(first_segment) {
2768 self.errors.push(Error::invalid_capability(
2769 decl,
2770 "source",
2771 first_segment,
2772 ));
2773 }
2774 }
2775 }
2776 }
2777 #[cfg(not(fuchsia_api_level_at_least = "25"))]
2778 let _ = source_dictionary;
2779 }
2780 None => {
2781 if !(capability_checker)(self).contains(name) {
2782 self.errors.push(Error::invalid_capability(decl, "source", name));
2783 }
2784 }
2785 }
2786 }
2787
2788 fn source_dependency_from_ref(
2789 &self,
2790 source_name: Option<&'a String>,
2791 source_dictionary: Option<&'a String>,
2792 ref_: Option<&'a fdecl::Ref>,
2793 ) -> Option<DependencyNode<'a>> {
2794 if ref_.is_none() {
2795 return None;
2796 }
2797 match ref_.unwrap() {
2798 fdecl::Ref::Child(fdecl::ChildRef { name, collection }) => {
2799 Some(DependencyNode::Child(name.as_str(), collection.as_ref().map(|s| s.as_str())))
2800 }
2801 fdecl::Ref::Collection(fdecl::CollectionRef { name, .. }) => {
2802 Some(DependencyNode::Collection(name.as_str()))
2803 }
2804 fdecl::Ref::Capability(fdecl::CapabilityRef { name, .. }) => {
2805 Some(DependencyNode::Capability(name.as_str()))
2806 }
2807 fdecl::Ref::Self_(_) => {
2808 #[cfg(fuchsia_api_level_at_least = "25")]
2809 if let Some(source_dictionary) = source_dictionary {
2810 let root_dict = source_dictionary.split('/').next().unwrap();
2811 if self.all_dictionaries.contains_key(root_dict) {
2812 Some(DependencyNode::Capability(root_dict))
2813 } else {
2814 Some(DependencyNode::Self_)
2815 }
2816 } else if let Some(source_name) = source_name {
2817 if self.all_storages.contains_key(source_name.as_str())
2818 || self.all_dictionaries.contains_key(source_name.as_str())
2819 {
2820 Some(DependencyNode::Capability(source_name))
2821 } else {
2822 Some(DependencyNode::Self_)
2823 }
2824 } else {
2825 Some(DependencyNode::Self_)
2826 }
2827 #[cfg(not(fuchsia_api_level_at_least = "25"))]
2828 {
2829 let _ = source_dictionary;
2830 if let Some(source_name) = source_name {
2831 if self.all_storages.contains_key(source_name.as_str()) {
2832 Some(DependencyNode::Capability(source_name))
2833 } else {
2834 Some(DependencyNode::Self_)
2835 }
2836 } else {
2837 Some(DependencyNode::Self_)
2838 }
2839 }
2840 }
2841 fdecl::Ref::Parent(_) => {
2842 None
2845 }
2846 fdecl::Ref::Framework(_) => {
2847 None
2850 }
2851 fdecl::Ref::Debug(_) => {
2852 None
2856 }
2857 fdecl::Ref::VoidType(_) => None,
2858 fdecl::RefUnknown!() => {
2859 None
2861 }
2862 }
2863 }
2864
2865 fn target_dependency_from_ref(
2866 &self,
2867 ref_: Option<&'a fdecl::Ref>,
2868 ) -> Option<DependencyNode<'a>> {
2869 self.source_dependency_from_ref(None, None, ref_)
2870 }
2871
2872 fn dynamic_children_in_collection(&self, collection: &'a str) -> Vec<&'a str> {
2873 self.dynamic_children
2874 .iter()
2875 .filter_map(|(n, c)| if *c == collection { Some(*n) } else { None })
2876 .collect()
2877 }
2878
2879 fn service_checker(&self) -> &dyn Container {
2882 &self.all_services
2883 }
2884 fn protocol_checker(&self) -> &dyn Container {
2885 &self.all_protocols
2886 }
2887 fn directory_checker(&self) -> &dyn Container {
2888 &self.all_directories
2889 }
2890 fn runner_checker(&self) -> &dyn Container {
2891 &self.all_runners
2892 }
2893 fn resolver_checker(&self) -> &dyn Container {
2894 &self.all_resolvers
2895 }
2896
2897 #[cfg(fuchsia_api_level_at_least = "25")]
2898 fn dictionary_checker(&self) -> &dyn Container {
2899 &self.all_dictionaries
2900 }
2901
2902 #[cfg(fuchsia_api_level_at_least = "HEAD")]
2903 fn config_checker(&self) -> &dyn Container {
2904 &self.all_configs
2905 }
2906 fn storage_checker(&self) -> &dyn Container {
2907 &self.all_storages
2908 }
2909 fn event_stream_checker(&self) -> &dyn Container {
2910 struct AlwaysTrueContainer {}
2914 impl Container for AlwaysTrueContainer {
2915 fn contains(&self, _key: &str) -> bool {
2916 true
2917 }
2918 }
2919 static CONTAINER: AlwaysTrueContainer = AlwaysTrueContainer {};
2920 &CONTAINER
2921 }
2922}
2923
2924#[cfg(test)]
2925mod tests {
2926 use super::*;
2927 use cm_types::MAX_LONG_NAME_LENGTH;
2928 use test_case::test_case;
2929 use {
2930 fidl_fuchsia_component_decl as fdecl, fidl_fuchsia_data as fdata, fidl_fuchsia_io as fio,
2931 };
2932
2933 macro_rules! test_validate {
2934 (
2935 $(
2936 $test_name:ident => {
2937 input = $input:expr,
2938 result = $result:expr,
2939 },
2940 )+
2941 ) => {
2942 $(
2943 #[test]
2944 fn $test_name() {
2945 validate_test($input, $result);
2946 }
2947 )+
2948 }
2949 }
2950
2951 macro_rules! test_validate_any_result {
2952 (
2953 $(
2954 $test_name:ident => {
2955 input = $input:expr,
2956 results = $results:expr,
2957 },
2958 )+
2959 ) => {
2960 $(
2961 #[test]
2962 fn $test_name() {
2963 validate_test_any_result($input, $results);
2964 }
2965 )+
2966 }
2967 }
2968
2969 macro_rules! test_validate_values_data {
2970 (
2971 $(
2972 $test_name:ident => {
2973 input = $input:expr,
2974 result = $result:expr,
2975 },
2976 )+
2977 ) => {
2978 $(
2979 #[test]
2980 fn $test_name() {
2981 validate_values_data_test($input, $result);
2982 }
2983 )+
2984 }
2985 }
2986
2987 macro_rules! test_validate_capabilities {
2988 (
2989 $(
2990 $test_name:ident => {
2991 input = $input:expr,
2992 as_builtin = $as_builtin:expr,
2993 result = $result:expr,
2994 },
2995 )+
2996 ) => {
2997 $(
2998 #[test]
2999 fn $test_name() {
3000 validate_capabilities_test($input, $as_builtin, $result);
3001 }
3002 )+
3003 }
3004 }
3005
3006 macro_rules! test_dependency {
3007 (
3008 $(
3009 ($test_name:ident) => {
3010 ty = $ty:expr,
3011 offer_decl = $offer_decl:expr,
3012 },
3013 )+
3014 ) => {
3015 $(
3016 #[test]
3017 fn $test_name() {
3018 let mut decl = new_component_decl();
3019 let dependencies = vec![
3020 ("a", "b"),
3021 ("b", "a"),
3022 ];
3023 let offers = dependencies.into_iter().map(|(from,to)| {
3024 let mut offer_decl = $offer_decl;
3025 offer_decl.source = Some(fdecl::Ref::Child(
3026 fdecl::ChildRef { name: from.to_string(), collection: None },
3027 ));
3028 offer_decl.target = Some(fdecl::Ref::Child(
3029 fdecl::ChildRef { name: to.to_string(), collection: None },
3030 ));
3031 $ty(offer_decl)
3032 }).collect();
3033 let children = ["a", "b"].iter().map(|name| {
3034 fdecl::Child {
3035 name: Some(name.to_string()),
3036 url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
3037 startup: Some(fdecl::StartupMode::Lazy),
3038 on_terminate: None,
3039 environment: None,
3040 ..Default::default()
3041 }
3042 }).collect();
3043 decl.offers = Some(offers);
3044 decl.children = Some(children);
3045 let result = Err(ErrorList::new(vec![
3046 Error::dependency_cycle(
3047 directed_graph::Error::CyclesDetected([vec!["child a", "child b", "child a"]].iter().cloned().collect()).format_cycle()),
3048 ]));
3049 validate_test(decl, result);
3050 }
3051 )+
3052 }
3053 }
3054
3055 macro_rules! test_weak_dependency {
3056 (
3057 $(
3058 ($test_name:ident) => {
3059 ty = $ty:expr,
3060 offer_decl = $offer_decl:expr,
3061 },
3062 )+
3063 ) => {
3064 $(
3065 #[test_case(fdecl::DependencyType::Weak)]
3066 fn $test_name(weak_dep: fdecl::DependencyType) {
3067 let mut decl = new_component_decl();
3068 let offers = vec![
3069 {
3070 let mut offer_decl = $offer_decl;
3071 offer_decl.source = Some(fdecl::Ref::Child(
3072 fdecl::ChildRef { name: "a".to_string(), collection: None },
3073 ));
3074 offer_decl.target = Some(fdecl::Ref::Child(
3075 fdecl::ChildRef { name: "b".to_string(), collection: None },
3076 ));
3077 offer_decl.dependency_type = Some(fdecl::DependencyType::Strong);
3078 $ty(offer_decl)
3079 },
3080 {
3081 let mut offer_decl = $offer_decl;
3082 offer_decl.source = Some(fdecl::Ref::Child(
3083 fdecl::ChildRef { name: "b".to_string(), collection: None },
3084 ));
3085 offer_decl.target = Some(fdecl::Ref::Child(
3086 fdecl::ChildRef { name: "a".to_string(), collection: None },
3087 ));
3088 offer_decl.dependency_type = Some(weak_dep);
3089 $ty(offer_decl)
3090 },
3091 ];
3092 let children = ["a", "b"].iter().map(|name| {
3093 fdecl::Child {
3094 name: Some(name.to_string()),
3095 url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
3096 startup: Some(fdecl::StartupMode::Lazy),
3097 on_terminate: None,
3098 environment: None,
3099 ..Default::default()
3100 }
3101 }).collect();
3102 decl.offers = Some(offers);
3103 decl.children = Some(children);
3104 let result = Ok(());
3105 validate_test(decl, result);
3106 }
3107 )+
3108 }
3109 }
3110
3111 #[track_caller]
3112 fn validate_test(input: fdecl::Component, expected_res: Result<(), ErrorList>) {
3113 let res = validate(&input);
3114 assert_eq!(res, expected_res);
3115 }
3116
3117 #[track_caller]
3118 fn validate_test_any_result(input: fdecl::Component, expected_res: Vec<Result<(), ErrorList>>) {
3119 let res = format!("{:?}", validate(&input));
3120 let expected_res_debug = format!("{:?}", expected_res);
3121
3122 let matched_exp =
3123 expected_res.into_iter().find(|expected| res == format!("{:?}", expected));
3124
3125 assert!(
3126 matched_exp.is_some(),
3127 "assertion failed: Expected one of:\n{:?}\nActual:\n{:?}",
3128 expected_res_debug,
3129 res
3130 );
3131 }
3132
3133 #[track_caller]
3134 fn validate_values_data_test(
3135 input: fdecl::ConfigValuesData,
3136 expected_res: Result<(), ErrorList>,
3137 ) {
3138 let res = validate_values_data(&input);
3139 assert_eq!(res, expected_res);
3140 }
3141
3142 #[track_caller]
3143 fn validate_capabilities_test(
3144 input: Vec<fdecl::Capability>,
3145 as_builtin: bool,
3146 expected_res: Result<(), ErrorList>,
3147 ) {
3148 let res = validate_capabilities(&input, as_builtin);
3149 assert_eq!(res, expected_res);
3150 }
3151
3152 fn new_component_decl() -> fdecl::Component {
3153 fdecl::Component {
3154 program: None,
3155 uses: None,
3156 exposes: None,
3157 offers: None,
3158 facets: None,
3159 capabilities: None,
3160 children: None,
3161 collections: None,
3162 environments: None,
3163 ..Default::default()
3164 }
3165 }
3166
3167 test_validate_any_result! {
3168 test_validate_use_disallows_nested_dirs => {
3169 input = {
3170 let mut decl = new_component_decl();
3171 decl.uses = Some(vec![
3172 fdecl::Use::Directory(fdecl::UseDirectory {
3173 dependency_type: Some(fdecl::DependencyType::Strong),
3174 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3175 source_name: Some("abc".to_string()),
3176 target_path: Some("/foo/bar".to_string()),
3177 rights: Some(fio::Operations::CONNECT),
3178 subdir: None,
3179 ..Default::default()
3180 }),
3181 fdecl::Use::Directory(fdecl::UseDirectory {
3182 dependency_type: Some(fdecl::DependencyType::Strong),
3183 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3184 source_name: Some("abc".to_string()),
3185 target_path: Some("/foo/bar/baz".to_string()),
3186 rights: Some(fio::Operations::CONNECT),
3187 subdir: None,
3188 ..Default::default()
3189 }),
3190 ]);
3191 decl
3192 },
3193 results = vec![
3194 Err(ErrorList::new(vec![
3195 Error::invalid_path_overlap(
3196 DeclType::UseDirectory, "/foo/bar/baz", DeclType::UseDirectory, "/foo/bar"),
3197 ])),
3198 Err(ErrorList::new(vec![
3199 Error::invalid_path_overlap(
3200 DeclType::UseDirectory, "/foo/bar", DeclType::UseDirectory, "/foo/bar/baz"),
3201 ])),
3202 ],
3203 },
3204 test_validate_use_disallows_nested_dirs_storage => {
3205 input = {
3206 let mut decl = new_component_decl();
3207 decl.uses = Some(vec![
3208 fdecl::Use::Storage(fdecl::UseStorage {
3209 source_name: Some("abc".to_string()),
3210 target_path: Some("/foo/bar".to_string()),
3211 ..Default::default()
3212 }),
3213 fdecl::Use::Storage(fdecl::UseStorage {
3214 source_name: Some("abc".to_string()),
3215 target_path: Some("/foo/bar/baz".to_string()),
3216 ..Default::default()
3217 }),
3218 ]);
3219 decl
3220 },
3221 results = vec![
3222 Err(ErrorList::new(vec![
3223 Error::invalid_path_overlap(
3224 DeclType::UseStorage, "/foo/bar/baz", DeclType::UseStorage, "/foo/bar"),
3225 ])),
3226 Err(ErrorList::new(vec![
3227 Error::invalid_path_overlap(
3228 DeclType::UseStorage, "/foo/bar", DeclType::UseStorage, "/foo/bar/baz"),
3229 ])),
3230 ],
3231 },
3232 test_validate_use_disallows_nested_dirs_directory_and_storage => {
3233 input = {
3234 let mut decl = new_component_decl();
3235 decl.uses = Some(vec![
3236 fdecl::Use::Directory(fdecl::UseDirectory {
3237 dependency_type: Some(fdecl::DependencyType::Strong),
3238 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3239 source_name: Some("abc".to_string()),
3240 target_path: Some("/foo/bar".to_string()),
3241 rights: Some(fio::Operations::CONNECT),
3242 subdir: None,
3243 ..Default::default()
3244 }),
3245 fdecl::Use::Storage(fdecl::UseStorage {
3246 source_name: Some("abc".to_string()),
3247 target_path: Some("/foo/bar/baz".to_string()),
3248 ..Default::default()
3249 }),
3250 ]);
3251 decl
3252 },
3253 results = vec![
3254 Err(ErrorList::new(vec![
3255 Error::invalid_path_overlap(
3256 DeclType::UseStorage, "/foo/bar/baz", DeclType::UseDirectory, "/foo/bar"),
3257 ])),
3258 Err(ErrorList::new(vec![
3259 Error::invalid_path_overlap(
3260 DeclType::UseDirectory, "/foo/bar", DeclType::UseStorage, "/foo/bar/baz"),
3261 ])),
3262 ],
3263 },
3264 test_validate_use_disallows_common_prefixes_protocol => {
3265 input = {
3266 let mut decl = new_component_decl();
3267 decl.uses = Some(vec![
3268 fdecl::Use::Directory(fdecl::UseDirectory {
3269 dependency_type: Some(fdecl::DependencyType::Strong),
3270 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3271 source_name: Some("abc".to_string()),
3272 target_path: Some("/foo/bar".to_string()),
3273 rights: Some(fio::Operations::CONNECT),
3274 subdir: None,
3275 ..Default::default()
3276 }),
3277 fdecl::Use::Protocol(fdecl::UseProtocol {
3278 dependency_type: Some(fdecl::DependencyType::Strong),
3279 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3280 source_name: Some("crow".to_string()),
3281 target_path: Some("/foo/bar/fuchsia.2".to_string()),
3282 ..Default::default()
3283 }),
3284 ]);
3285 decl
3286 },
3287 results = vec![
3288 Err(ErrorList::new(vec![
3289 Error::invalid_path_overlap(
3290 DeclType::UseProtocol, "/foo/bar/fuchsia.2", DeclType::UseDirectory, "/foo/bar"),
3291 ])),
3292 Err(ErrorList::new(vec![
3293 Error::invalid_path_overlap(
3294 DeclType::UseDirectory, "/foo/bar", DeclType::UseProtocol, "/foo/bar/fuchsia.2"),
3295 ])),
3296 ],
3297 },
3298 test_validate_use_disallows_common_prefixes_service => {
3299 input = {
3300 let mut decl = new_component_decl();
3301 decl.uses = Some(vec![
3302 fdecl::Use::Directory(fdecl::UseDirectory {
3303 dependency_type: Some(fdecl::DependencyType::Strong),
3304 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3305 source_name: Some("abc".to_string()),
3306 target_path: Some("/foo/bar".to_string()),
3307 rights: Some(fio::Operations::CONNECT),
3308 subdir: None,
3309 ..Default::default()
3310 }),
3311 fdecl::Use::Service(fdecl::UseService {
3312 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3313 source_name: Some("space".to_string()),
3314 target_path: Some("/foo/bar/baz/fuchsia.logger.Log".to_string()),
3315 dependency_type: Some(fdecl::DependencyType::Strong),
3316 ..Default::default()
3317 }),
3318 ]);
3319 decl
3320 },
3321 results = vec![
3322 Err(ErrorList::new(vec![
3323 Error::invalid_path_overlap(
3324 DeclType::UseService, "/foo/bar/baz/fuchsia.logger.Log", DeclType::UseDirectory, "/foo/bar"),
3325 ])),
3326 Err(ErrorList::new(vec![
3327 Error::invalid_path_overlap(
3328 DeclType::UseDirectory, "/foo/bar", DeclType::UseService, "/foo/bar/baz/fuchsia.logger.Log"),
3329 ])),
3330 ],
3331 },
3332 test_validate_use_disallows_pkg => {
3333 input = {
3334 let mut decl = new_component_decl();
3335 decl.uses = Some(vec![
3336 fdecl::Use::Directory(fdecl::UseDirectory {
3337 dependency_type: Some(fdecl::DependencyType::Strong),
3338 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3339 source_name: Some("abc".to_string()),
3340 target_path: Some("/pkg".to_string()),
3341 rights: Some(fio::Operations::CONNECT),
3342 subdir: None,
3343 ..Default::default()
3344 }),
3345 ]);
3346 decl
3347 },
3348 results = vec![
3349 Err(ErrorList::new(vec![
3350 Error::pkg_path_overlap(DeclType::UseDirectory, "/pkg"),
3351 ])),
3352 ],
3353 },
3354 test_validate_use_disallows_pkg_overlap => {
3355 input = {
3356 let mut decl = new_component_decl();
3357 decl.uses = Some(vec![
3358 fdecl::Use::Directory(fdecl::UseDirectory {
3359 dependency_type: Some(fdecl::DependencyType::Strong),
3360 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3361 source_name: Some("abc".to_string()),
3362 target_path: Some("/pkg/foo".to_string()),
3363 rights: Some(fio::Operations::CONNECT),
3364 subdir: None,
3365 ..Default::default()
3366 }),
3367 ]);
3368 decl
3369 },
3370 results = vec![
3371 Err(ErrorList::new(vec![
3372 Error::pkg_path_overlap(DeclType::UseDirectory, "/pkg/foo"),
3373 ])),
3374 ],
3375 },
3376 test_validate_use_optional_config_correct => {
3377 input = {
3378 let mut decl = new_component_decl();
3379 decl.uses = Some(vec![
3380 fdecl::Use::Config(fdecl::UseConfiguration {
3381 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3382 source_name: Some("abc".to_string()),
3383 target_name: Some("foo".to_string()),
3384 availability: Some(fdecl::Availability::Optional),
3385 type_: Some(fdecl::ConfigType {
3386 layout: fdecl::ConfigTypeLayout::Bool,
3387 parameters: Some(Vec::new()),
3388 constraints: Vec::new(),
3389 }),
3390 ..Default::default()
3391 }),
3392 ]);
3393 decl.config = Some(fdecl::ConfigSchema {
3394 fields: Some(vec![fdecl::ConfigField {
3395 key: Some("foo".into()),
3396 type_: Some(fdecl::ConfigType {
3397 layout: fdecl::ConfigTypeLayout::Bool,
3398 parameters: Some(Vec::new()),
3399 constraints: Vec::new(),
3400 }),
3401 mutability: None,
3402 ..Default::default()}]),
3403 checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3404 value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3405 ..Default::default()
3406 });
3407 decl
3408 },
3409 results = vec![Ok(())],
3410 },
3411 test_validate_use_optional_config_no_config_schema => {
3412 input = {
3413 let mut decl = new_component_decl();
3414 decl.uses = Some(vec![
3415 fdecl::Use::Config(fdecl::UseConfiguration {
3416 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3417 source_name: Some("abc".to_string()),
3418 target_name: Some("foo".to_string()),
3419 availability: Some(fdecl::Availability::Optional),
3420 type_: Some(fdecl::ConfigType {
3421 layout: fdecl::ConfigTypeLayout::Bool,
3422 parameters: None,
3423 constraints: Vec::new(),
3424 }),
3425 ..Default::default()
3426 }),
3427 ]);
3428 decl
3429 },
3430 results = vec![
3431 Err(ErrorList::new(vec![
3432 Error::missing_field(DeclType::ConfigField, "config"),
3433 ])),
3434 ],
3435 },
3436 test_validate_use_optional_config_no_config_field => {
3437 input = {
3438 let mut decl = new_component_decl();
3439 decl.uses = Some(vec![
3440 fdecl::Use::Config(fdecl::UseConfiguration {
3441 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3442 source_name: Some("abc".to_string()),
3443 target_name: Some("foo".to_string()),
3444 availability: Some(fdecl::Availability::Optional),
3445 type_: Some(fdecl::ConfigType {
3446 layout: fdecl::ConfigTypeLayout::Bool,
3447 parameters: None,
3448 constraints: Vec::new(),
3449 }),
3450 ..Default::default()
3451 }),
3452 ]);
3453 decl.config = Some(fdecl::ConfigSchema {
3454 fields: Some(vec![]),
3455 checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3456 value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3457 ..Default::default()
3458 });
3459 decl
3460 },
3461 results = vec![
3462 Err(ErrorList::new(vec![
3463 Error::missing_field(DeclType::ConfigField, "foo"),
3464 ])),
3465 ],
3466 },
3467 test_validate_use_optional_config_bad_type => {
3468 input = {
3469 let mut decl = new_component_decl();
3470 decl.uses = Some(vec![
3471 fdecl::Use::Config(fdecl::UseConfiguration {
3472 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3473 source_name: Some("abc".to_string()),
3474 target_name: Some("foo".to_string()),
3475 availability: Some(fdecl::Availability::Optional),
3476 type_: Some(fdecl::ConfigType {
3477 layout: fdecl::ConfigTypeLayout::Bool,
3478 parameters: None,
3479 constraints: Vec::new(),
3480 }),
3481 ..Default::default()
3482 }),
3483 ]);
3484 decl.config = Some(fdecl::ConfigSchema {
3485 fields: Some(vec![fdecl::ConfigField {
3486 key: Some("foo".into()),
3487 type_: Some(fdecl::ConfigType {
3488 layout: fdecl::ConfigTypeLayout::Int16,
3489 parameters: Some(Vec::new()),
3490 constraints: Vec::new(),
3491 }),
3492 mutability: None,
3493 ..Default::default()}]),
3494 checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3495 value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3496 ..Default::default()
3497 });
3498 decl
3499 },
3500 results = vec![
3501 Err(ErrorList::new(vec![
3502 Error::invalid_field(DeclType::ConfigField, "foo"),
3503 ])),
3504 ],
3505 },
3506 }
3507
3508 test_validate_values_data! {
3509 test_values_data_ok => {
3510 input = fdecl::ConfigValuesData {
3511 values: Some(vec![
3512 fdecl::ConfigValueSpec {
3513 value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(true))),
3514 ..Default::default()
3515 }
3516 ]),
3517 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3518 ..Default::default()
3519 },
3520 result = Ok(()),
3521 },
3522 test_values_data_no_checksum => {
3523 input = fdecl::ConfigValuesData {
3524 values: Some(vec![]),
3525 checksum: None,
3526 ..Default::default()
3527 },
3528 result = Err(ErrorList::new(vec![
3529 Error::missing_field(DeclType::ConfigValuesData, "checksum")
3530 ])),
3531 },
3532 test_values_data_unknown_checksum => {
3533 input = fdecl::ConfigValuesData {
3534 values: Some(vec![]),
3535 checksum: Some(fdecl::ConfigChecksum::unknown_variant_for_testing()),
3536 ..Default::default()
3537 },
3538 result = Err(ErrorList::new(vec![
3539 Error::invalid_field(DeclType::ConfigValuesData, "checksum")
3540 ])),
3541 },
3542 test_values_data_no_values => {
3543 input = fdecl::ConfigValuesData {
3544 values: None,
3545 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3546 ..Default::default()
3547 },
3548 result = Err(ErrorList::new(vec![
3549 Error::missing_field(DeclType::ConfigValuesData, "values")
3550 ])),
3551 },
3552 test_values_data_no_inner_value => {
3553 input = fdecl::ConfigValuesData {
3554 values: Some(vec![
3555 fdecl::ConfigValueSpec::default()
3556 ]),
3557 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3558 ..Default::default()
3559 },
3560 result = Err(ErrorList::new(vec![
3561 Error::missing_field(DeclType::ConfigValueSpec, "value")
3562 ])),
3563 },
3564 test_values_data_unknown_inner_value => {
3565 input = fdecl::ConfigValuesData {
3566 values: Some(vec![
3567 fdecl::ConfigValueSpec {
3568 value: Some(fdecl::ConfigValue::unknown_variant_for_testing()),
3569 ..Default::default()
3570 }
3571 ]),
3572 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3573 ..Default::default()
3574 },
3575 result = Err(ErrorList::new(vec![
3576 Error::invalid_field(DeclType::ConfigValueSpec, "value")
3577 ])),
3578 },
3579 test_values_data_unknown_single_value => {
3580 input = fdecl::ConfigValuesData {
3581 values: Some(vec![
3582 fdecl::ConfigValueSpec {
3583 value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::unknown_variant_for_testing())),
3584 ..Default::default()
3585 }
3586 ]),
3587 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3588 ..Default::default()
3589 },
3590 result = Err(ErrorList::new(vec![
3591 Error::invalid_field(DeclType::ConfigValueSpec, "value")
3592 ])),
3593 },
3594 test_values_data_unknown_list_value => {
3595 input = fdecl::ConfigValuesData {
3596 values: Some(vec![
3597 fdecl::ConfigValueSpec {
3598 value: Some(fdecl::ConfigValue::Vector(fdecl::ConfigVectorValue::unknown_variant_for_testing())),
3599 ..Default::default()
3600 }
3601 ]),
3602 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3603 ..Default::default()
3604 },
3605 result = Err(ErrorList::new(vec![
3606 Error::invalid_field(DeclType::ConfigValueSpec, "value")
3607 ])),
3608 },
3609 }
3610
3611 test_validate! {
3612 test_validate_uses_empty => {
3614 input = {
3615 let mut decl = new_component_decl();
3616 decl.program = Some(fdecl::Program {
3617 runner: Some("elf".to_string()),
3618 info: Some(fdata::Dictionary {
3619 entries: None,
3620 ..Default::default()
3621 }),
3622 ..Default::default()
3623 });
3624 decl.uses = Some(vec![
3625 fdecl::Use::Service(fdecl::UseService {
3626 source: None,
3627 source_name: None,
3628 target_path: None,
3629 dependency_type: None,
3630 ..Default::default()
3631 }),
3632 fdecl::Use::Protocol(fdecl::UseProtocol {
3633 dependency_type: None,
3634 source: None,
3635 source_name: None,
3636 target_path: None,
3637 ..Default::default()
3638 }),
3639 fdecl::Use::Directory(fdecl::UseDirectory {
3640 dependency_type: None,
3641 source: None,
3642 source_name: None,
3643 target_path: None,
3644 rights: None,
3645 subdir: None,
3646 ..Default::default()
3647 }),
3648 fdecl::Use::Storage(fdecl::UseStorage {
3649 source_name: None,
3650 target_path: None,
3651 ..Default::default()
3652 }),
3653 fdecl::Use::EventStream(fdecl::UseEventStream {
3654 source_name: None,
3655 source: None,
3656 target_path: None,
3657 ..Default::default()
3658 }),
3659 fdecl::Use::Runner(fdecl::UseRunner {
3660 source_name: None,
3661 source: None,
3662 ..Default::default()
3663 }),
3664 ]);
3665 decl
3666 },
3667 result = Err(ErrorList::new(vec![
3668 Error::missing_field(DeclType::UseService, "source"),
3669 Error::missing_field(DeclType::UseService, "source_name"),
3670 Error::missing_field(DeclType::UseService, "target_path"),
3671 Error::missing_field(DeclType::UseService, "dependency_type"),
3672 Error::missing_field(DeclType::UseProtocol, "source"),
3673 Error::missing_field(DeclType::UseProtocol, "source_name"),
3674 Error::missing_field(DeclType::UseProtocol, "target_path"),
3675 Error::missing_field(DeclType::UseProtocol, "dependency_type"),
3676 Error::missing_field(DeclType::UseDirectory, "source"),
3677 Error::missing_field(DeclType::UseDirectory, "source_name"),
3678 Error::missing_field(DeclType::UseDirectory, "target_path"),
3679 Error::missing_field(DeclType::UseDirectory, "dependency_type"),
3680 Error::missing_field(DeclType::UseDirectory, "rights"),
3681 Error::missing_field(DeclType::UseStorage, "source_name"),
3682 Error::missing_field(DeclType::UseStorage, "target_path"),
3683 Error::missing_field(DeclType::UseEventStream, "source"),
3684 Error::missing_field(DeclType::UseEventStream, "source_name"),
3685 Error::missing_field(DeclType::UseEventStream, "target_path"),
3686 Error::missing_field(DeclType::UseRunner, "source"),
3687 Error::missing_field(DeclType::UseRunner, "source_name"),
3688 ])),
3689 },
3690 test_validate_missing_program_info => {
3691 input = {
3692 let mut decl = new_component_decl();
3693 decl.program = Some(fdecl::Program {
3694 runner: Some("runner".to_string()),
3695 info: None,
3696 ..Default::default()
3697 });
3698 decl
3699 },
3700 result = Err(ErrorList::new(vec![
3701 Error::missing_field(DeclType::Program, "info")
3702 ])),
3703 },
3704 test_validate_uses_invalid_identifiers => {
3705 input = {
3706 let mut decl = new_component_decl();
3707 decl.uses = Some(vec![
3708 fdecl::Use::Service(fdecl::UseService {
3709 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
3710 name: "^bad".to_string(),
3711 })),
3712 source_name: Some("foo/".to_string()),
3713 target_path: Some("a/foo".to_string()),
3714 dependency_type: Some(fdecl::DependencyType::Strong),
3715 ..Default::default()
3716 }),
3717 fdecl::Use::Protocol(fdecl::UseProtocol {
3718 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3719 name: "^bad".to_string(),
3720 collection: None,
3721 })),
3722 source_name: Some("foo/".to_string()),
3723 target_path: Some("b/foo".to_string()),
3724 dependency_type: Some(fdecl::DependencyType::Strong),
3725 ..Default::default()
3726 }),
3727 fdecl::Use::Directory(fdecl::UseDirectory {
3728 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3729 name: "^bad".to_string(),
3730 collection: None,
3731 })),
3732 source_name: Some("foo/".to_string()),
3733 target_path: Some("c".to_string()),
3734 rights: Some(fio::Operations::CONNECT),
3735 subdir: Some("/foo".to_string()),
3736 dependency_type: Some(fdecl::DependencyType::Strong),
3737 ..Default::default()
3738 }),
3739 fdecl::Use::Storage(fdecl::UseStorage {
3740 source_name: Some("foo/".to_string()),
3741 target_path: Some("d".to_string()),
3742 ..Default::default()
3743 }),
3744 fdecl::Use::EventStream(fdecl::UseEventStream {
3745 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3746 name: "^bad".to_string(),
3747 collection: None,
3748 })),
3749 source_name: Some("foo/".to_string()),
3750 target_path: Some("e".to_string()),
3751 ..Default::default()
3752 }),
3753 fdecl::Use::Runner(fdecl::UseRunner {
3754 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3755 name: "^bad".to_string(),
3756 collection: None,
3757 })),
3758 source_name: Some("foo/".to_string()),
3759 ..Default::default()
3760 }),
3761 ]);
3762 decl
3763 },
3764 result = Err(ErrorList::new(vec![
3765 Error::invalid_field(DeclType::UseService, "source.capability.name"),
3766 Error::invalid_field(DeclType::UseService, "source_name"),
3767 Error::invalid_field(DeclType::UseService, "target_path"),
3768 Error::invalid_field(DeclType::UseProtocol, "source.child.name"),
3769 Error::invalid_field(DeclType::UseProtocol, "source_name"),
3770 Error::invalid_field(DeclType::UseProtocol, "target_path"),
3771 Error::invalid_field(DeclType::UseDirectory, "source.child.name"),
3772 Error::invalid_field(DeclType::UseDirectory, "source_name"),
3773 Error::invalid_field(DeclType::UseDirectory, "target_path"),
3774 Error::invalid_field(DeclType::UseDirectory, "subdir"),
3775 Error::invalid_field(DeclType::UseStorage, "source_name"),
3776 Error::invalid_field(DeclType::UseStorage, "target_path"),
3777 Error::invalid_field(DeclType::UseEventStream, "source.child.name"),
3778 Error::invalid_field(DeclType::UseEventStream, "source_name"),
3779 Error::invalid_field(DeclType::UseEventStream, "target_path"),
3780 Error::invalid_field(DeclType::UseRunner, "source.child.name"),
3781 Error::invalid_field(DeclType::UseRunner, "source_name"),
3782 ])),
3783 },
3784 test_validate_uses_missing_source => {
3785 input = {
3786 fdecl::Component {
3787 uses: Some(vec![
3788 fdecl::Use::Protocol(fdecl::UseProtocol {
3789 dependency_type: Some(fdecl::DependencyType::Strong),
3790 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
3791 name: "this-storage-doesnt-exist".to_string(),
3792 })),
3793 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3794 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3795 ..Default::default()
3796 })
3797 ]),
3798 ..new_component_decl()
3799 }
3800 },
3801 result = Err(ErrorList::new(vec![
3802 Error::invalid_capability(DeclType::UseProtocol, "source", "this-storage-doesnt-exist"),
3803 ])),
3804 },
3805 test_validate_uses_invalid_child => {
3806 input = {
3807 fdecl::Component {
3808 uses: Some(vec![
3809 fdecl::Use::Protocol(fdecl::UseProtocol {
3810 dependency_type: Some(fdecl::DependencyType::Strong),
3811 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3812 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3813 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3814 ..Default::default()
3815 }),
3816 fdecl::Use::Service(fdecl::UseService {
3817 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3818 source_name: Some("service_name".to_string()),
3819 target_path: Some("/svc/service_name".to_string()),
3820 dependency_type: Some(fdecl::DependencyType::Strong),
3821 ..Default::default()
3822 }),
3823 fdecl::Use::Directory(fdecl::UseDirectory {
3824 dependency_type: Some(fdecl::DependencyType::Strong),
3825 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3826 source_name: Some("DirectoryName".to_string()),
3827 target_path: Some("/data/DirectoryName".to_string()),
3828 rights: Some(fio::Operations::CONNECT),
3829 subdir: None,
3830 ..Default::default()
3831 }),
3832 fdecl::Use::Runner(fdecl::UseRunner {
3833 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3834 source_name: Some("RunnerName".to_string()),
3835 ..Default::default()
3836 }),
3837 ]),
3838 ..new_component_decl()
3839 }
3840 },
3841 result = Err(ErrorList::new(vec![
3842 Error::invalid_child(DeclType::UseProtocol, "source", "no-such-child"),
3843 Error::invalid_child(DeclType::UseService, "source", "no-such-child"),
3844 Error::invalid_child(DeclType::UseDirectory, "source", "no-such-child"),
3845 Error::invalid_child(DeclType::UseRunner, "source", "no-such-child"),
3846 ])),
3847 },
3848 test_validate_uses_invalid_capability_from_self => {
3849 input = {
3850 let mut decl = new_component_decl();
3851 decl.uses = Some(vec![
3852 fdecl::Use::Service(fdecl::UseService {
3853 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3854 source_name: Some("fuchsia.some.library.SomeService".into()),
3855 target_path: Some("/svc/foo".into()),
3856 dependency_type: Some(fdecl::DependencyType::Strong),
3857 ..Default::default()
3858 }),
3859 fdecl::Use::Protocol(fdecl::UseProtocol {
3860 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3861 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
3862 target_path: Some("/svc/bar".into()),
3863 dependency_type: Some(fdecl::DependencyType::Strong),
3864 ..Default::default()
3865 }),
3866 fdecl::Use::Directory(fdecl::UseDirectory {
3867 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3868 source_name: Some("dir".into()),
3869 target_path: Some("/assets".into()),
3870 dependency_type: Some(fdecl::DependencyType::Strong),
3871 rights: Some(fio::Operations::CONNECT),
3872 ..Default::default()
3873 }),
3874 fdecl::Use::Runner(fdecl::UseRunner {
3875 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3876 source_name: Some("source_elf".into()),
3877 ..Default::default()
3878 }),
3879 fdecl::Use::Config(fdecl::UseConfiguration {
3880 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3881 source_name: Some("source_config".into()),
3882 target_name: Some("config".into()),
3883 type_: Some(fdecl::ConfigType {
3884 layout: fdecl::ConfigTypeLayout::Bool,
3885 parameters: Some(Vec::new()),
3886 constraints: Vec::new(),
3887 }),
3888 ..Default::default()
3889 }),
3890 fdecl::Use::Protocol(fdecl::UseProtocol {
3891 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3892 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
3893 source_dictionary: Some("dict/inner".into()),
3894 target_path: Some("/svc/baz".into()),
3895 dependency_type: Some(fdecl::DependencyType::Strong),
3896 ..Default::default()
3897 }),
3898 ]);
3899 decl
3900 },
3901 result = Err(ErrorList::new(vec![
3902 Error::invalid_capability(
3903 DeclType::UseService,
3904 "source",
3905 "fuchsia.some.library.SomeService"),
3906 Error::invalid_capability(
3907 DeclType::UseProtocol,
3908 "source",
3909 "fuchsia.some.library.SomeProtocol"),
3910 Error::invalid_capability(DeclType::UseDirectory, "source", "dir"),
3911 Error::invalid_capability(DeclType::UseRunner, "source", "source_elf"),
3912 Error::invalid_capability(DeclType::UseConfiguration, "source", "source_config"),
3913 Error::invalid_capability(DeclType::UseProtocol, "source", "dict"),
3914 ])),
3915 },
3916 test_validate_use_from_child_offer_to_child_strong_cycle => {
3917 input = {
3918 fdecl::Component {
3919 capabilities: Some(vec![
3920 fdecl::Capability::Service(fdecl::Service {
3921 name: Some("a".to_string()),
3922 source_path: Some("/a".to_string()),
3923 ..Default::default()
3924 })]),
3925 uses: Some(vec![
3926 fdecl::Use::Protocol(fdecl::UseProtocol {
3927 dependency_type: Some(fdecl::DependencyType::Strong),
3928 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3929 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3930 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3931 ..Default::default()
3932 }),
3933 fdecl::Use::Service(fdecl::UseService {
3934 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3935 source_name: Some("service_name".to_string()),
3936 target_path: Some("/svc/service_name".to_string()),
3937 dependency_type: Some(fdecl::DependencyType::Strong),
3938 ..Default::default()
3939 }),
3940 fdecl::Use::Directory(fdecl::UseDirectory {
3941 dependency_type: Some(fdecl::DependencyType::Strong),
3942 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3943 source_name: Some("DirectoryName".to_string()),
3944 target_path: Some("/data/DirectoryName".to_string()),
3945 rights: Some(fio::Operations::CONNECT),
3946 subdir: None,
3947 ..Default::default()
3948 }),
3949 ]),
3950 offers: Some(vec![
3951 fdecl::Offer::Service(fdecl::OfferService {
3952 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3953 source_name: Some("a".to_string()),
3954 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
3955 target_name: Some("a".to_string()),
3956 ..Default::default()
3957 })
3958 ]),
3959 children: Some(vec![
3960 fdecl::Child {
3961 name: Some("child".to_string()),
3962 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3963 startup: Some(fdecl::StartupMode::Lazy),
3964 on_terminate: None,
3965 ..Default::default()
3966 }
3967 ]),
3968 ..new_component_decl()
3969 }
3970 },
3971 result = Err(ErrorList::new(vec![
3972 Error::dependency_cycle("{{self -> child child -> self}}".to_string()),
3973 ])),
3974 },
3975 test_validate_use_from_child_storage_no_cycle => {
3976 input = {
3977 fdecl::Component {
3978 capabilities: Some(vec![
3979 fdecl::Capability::Storage(fdecl::Storage {
3980 name: Some("cdata".to_string()),
3981 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None } )),
3982 backing_dir: Some("minfs".to_string()),
3983 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
3984 ..Default::default()
3985 }),
3986 fdecl::Capability::Storage(fdecl::Storage {
3987 name: Some("pdata".to_string()),
3988 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
3989 backing_dir: Some("minfs".to_string()),
3990 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
3991 ..Default::default()
3992 }),
3993 ]),
3994 uses: Some(vec![
3995 fdecl::Use::Protocol(fdecl::UseProtocol {
3996 dependency_type: Some(fdecl::DependencyType::Strong),
3997 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child1".to_string(), collection: None})),
3998 source_name: Some("a".to_string()),
3999 target_path: Some("/svc/a".to_string()),
4000 ..Default::default()
4001 }),
4002 ]),
4003 offers: Some(vec![
4004 fdecl::Offer::Storage(fdecl::OfferStorage {
4005 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4006 source_name: Some("cdata".to_string()),
4007 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4008 target_name: Some("cdata".to_string()),
4009 ..Default::default()
4010 }),
4011 fdecl::Offer::Storage(fdecl::OfferStorage {
4012 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4013 source_name: Some("pdata".to_string()),
4014 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4015 target_name: Some("pdata".to_string()),
4016 ..Default::default()
4017 }),
4018 ]),
4019 children: Some(vec![
4020 fdecl::Child {
4021 name: Some("child1".to_string()),
4022 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4023 startup: Some(fdecl::StartupMode::Lazy),
4024 on_terminate: None,
4025 ..Default::default()
4026 },
4027 fdecl::Child {
4028 name: Some("child2".to_string()),
4029 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4030 startup: Some(fdecl::StartupMode::Lazy),
4031 on_terminate: None,
4032 ..Default::default()
4033 }
4034 ]),
4035 ..new_component_decl()
4036 }
4037 },
4038 result = Ok(()),
4039 },
4040 test_validate_use_from_child_storage_cycle => {
4041 input = {
4042 fdecl::Component {
4043 capabilities: Some(vec![
4044 fdecl::Capability::Storage(fdecl::Storage {
4045 name: Some("data".to_string()),
4046 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4047 backing_dir: Some("minfs".to_string()),
4048 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4049 ..Default::default()
4050 }),
4051 ]),
4052 uses: Some(vec![
4053 fdecl::Use::Protocol(fdecl::UseProtocol {
4054 dependency_type: Some(fdecl::DependencyType::Strong),
4055 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4056 source_name: Some("a".to_string()),
4057 target_path: Some("/svc/a".to_string()),
4058 ..Default::default()
4059 }),
4060 ]),
4061 offers: Some(vec![
4062 fdecl::Offer::Storage(fdecl::OfferStorage {
4063 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4064 source_name: Some("data".to_string()),
4065 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4066 target_name: Some("data".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 -> capability data -> child child -> self}}".to_string()),
4084 ])),
4085 },
4086 test_validate_storage_strong_cycle_between_children => {
4087 input = {
4088 fdecl::Component {
4089 capabilities: Some(vec![
4090 fdecl::Capability::Storage(fdecl::Storage {
4091 name: Some("data".to_string()),
4092 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None } )),
4093 backing_dir: Some("minfs".to_string()),
4094 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4095 ..Default::default()
4096 })
4097 ]),
4098 offers: Some(vec![
4099 fdecl::Offer::Storage(fdecl::OfferStorage {
4100 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4101 source_name: Some("data".to_string()),
4102 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4103 target_name: Some("data".to_string()),
4104 ..Default::default()
4105 }),
4106 fdecl::Offer::Service(fdecl::OfferService {
4107 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4108 source_name: Some("a".to_string()),
4109 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4110 target_name: Some("a".to_string()),
4111 ..Default::default()
4112 }),
4113 ]),
4114 children: Some(vec![
4115 fdecl::Child {
4116 name: Some("child1".to_string()),
4117 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4118 startup: Some(fdecl::StartupMode::Lazy),
4119 on_terminate: None,
4120 ..Default::default()
4121 },
4122 fdecl::Child {
4123 name: Some("child2".to_string()),
4124 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4125 startup: Some(fdecl::StartupMode::Lazy),
4126 on_terminate: None,
4127 ..Default::default()
4128 }
4129 ]),
4130 ..new_component_decl()
4131 }
4132 },
4133 result = Err(ErrorList::new(vec![
4134 Error::dependency_cycle("{{child child1 -> capability data -> child child2 -> child child1}}".to_string()),
4135 ])),
4136 },
4137 test_validate_strong_cycle_between_children_through_environment_debug => {
4138 input = {
4139 fdecl::Component {
4140 environments: Some(vec![
4141 fdecl::Environment {
4142 name: Some("env".to_string()),
4143 extends: Some(fdecl::EnvironmentExtends::Realm),
4144 debug_capabilities: Some(vec![
4145 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
4146 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4147 source_name: Some("fuchsia.foo.Bar".to_string()),
4148 target_name: Some("fuchsia.foo.Bar".to_string()),
4149 ..Default::default()
4150 }),
4151 ]),
4152 ..Default::default()
4153 },
4154 ]),
4155 offers: Some(vec![
4156 fdecl::Offer::Service(fdecl::OfferService {
4157 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4158 source_name: Some("a".to_string()),
4159 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4160 target_name: Some("a".to_string()),
4161 ..Default::default()
4162 }),
4163 ]),
4164 children: Some(vec![
4165 fdecl::Child {
4166 name: Some("child1".to_string()),
4167 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4168 startup: Some(fdecl::StartupMode::Lazy),
4169 on_terminate: None,
4170 ..Default::default()
4171 },
4172 fdecl::Child {
4173 name: Some("child2".to_string()),
4174 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4175 startup: Some(fdecl::StartupMode::Lazy),
4176 environment: Some("env".to_string()),
4177 on_terminate: None,
4178 ..Default::default()
4179 }
4180 ]),
4181 ..new_component_decl()
4182 }
4183 },
4184 result = Err(ErrorList::new(vec![
4185 Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}".to_string()),
4186 ])),
4187 },
4188 test_validate_strong_cycle_between_children_through_environment_runner => {
4189 input = {
4190 fdecl::Component {
4191 environments: Some(vec![
4192 fdecl::Environment {
4193 name: Some("env".to_string()),
4194 extends: Some(fdecl::EnvironmentExtends::Realm),
4195 runners: Some(vec![
4196 fdecl::RunnerRegistration {
4197 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4198 source_name: Some("coff".to_string()),
4199 target_name: Some("coff".to_string()),
4200 ..Default::default()
4201 }
4202 ]),
4203 ..Default::default()
4204 },
4205 ]),
4206 offers: Some(vec![
4207 fdecl::Offer::Service(fdecl::OfferService {
4208 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4209 source_name: Some("a".to_string()),
4210 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4211 target_name: Some("a".to_string()),
4212 ..Default::default()
4213 }),
4214 ]),
4215 children: Some(vec![
4216 fdecl::Child {
4217 name: Some("child1".to_string()),
4218 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4219 startup: Some(fdecl::StartupMode::Lazy),
4220 on_terminate: None,
4221 ..Default::default()
4222 },
4223 fdecl::Child {
4224 name: Some("child2".to_string()),
4225 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4226 startup: Some(fdecl::StartupMode::Lazy),
4227 environment: Some("env".to_string()),
4228 on_terminate: None,
4229 ..Default::default()
4230 }
4231 ]),
4232 ..new_component_decl()
4233 }
4234 },
4235 result = Err(ErrorList::new(vec![
4236 Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}".to_string()),
4237 ])),
4238 },
4239 test_validate_strong_cycle_between_children_through_environment_resolver => {
4240 input = {
4241 fdecl::Component {
4242 environments: Some(vec![
4243 fdecl::Environment {
4244 name: Some("env".to_string()),
4245 extends: Some(fdecl::EnvironmentExtends::Realm),
4246 resolvers: Some(vec![
4247 fdecl::ResolverRegistration {
4248 resolver: Some("gopher".to_string()),
4249 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4250 scheme: Some("gopher".to_string()),
4251 ..Default::default()
4252 }
4253 ]),
4254 ..Default::default()
4255 },
4256 ]),
4257 offers: Some(vec![
4258 fdecl::Offer::Service(fdecl::OfferService {
4259 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4260 source_name: Some("a".to_string()),
4261 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4262 target_name: Some("a".to_string()),
4263 ..Default::default()
4264 }),
4265 ]),
4266 children: Some(vec![
4267 fdecl::Child {
4268 name: Some("child1".to_string()),
4269 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4270 startup: Some(fdecl::StartupMode::Lazy),
4271 on_terminate: None,
4272 ..Default::default()
4273 },
4274 fdecl::Child {
4275 name: Some("child2".to_string()),
4276 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4277 startup: Some(fdecl::StartupMode::Lazy),
4278 environment: Some("env".to_string()),
4279 on_terminate: None,
4280 ..Default::default()
4281 }
4282 ]),
4283 ..new_component_decl()
4284 }
4285 },
4286 result = Err(ErrorList::new(vec![
4287 Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}".to_string()),
4288 ])),
4289 },
4290 test_validate_strong_cycle_between_self_and_two_children => {
4291 input = {
4292 fdecl::Component {
4293 capabilities: Some(vec![
4294 fdecl::Capability::Protocol(fdecl::Protocol {
4295 name: Some("fuchsia.foo.Bar".to_string()),
4296 source_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4297 ..Default::default()
4298 })
4299 ]),
4300 offers: Some(vec![
4301 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4302 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4303 source_name: Some("fuchsia.foo.Bar".to_string()),
4304 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4305 target_name: Some("fuchsia.foo.Bar".to_string()),
4306 dependency_type: Some(fdecl::DependencyType::Strong),
4307 ..Default::default()
4308 }),
4309 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4310 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4311 source_name: Some("fuchsia.bar.Baz".to_string()),
4312 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4313 target_name: Some("fuchsia.bar.Baz".to_string()),
4314 dependency_type: Some(fdecl::DependencyType::Strong),
4315 ..Default::default()
4316 }),
4317 ]),
4318 uses: Some(vec![
4319 fdecl::Use::Protocol(fdecl::UseProtocol {
4320 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child2".to_string(), collection: None})),
4321 source_name: Some("fuchsia.baz.Foo".to_string()),
4322 target_path: Some("/svc/fuchsia.baz.Foo".to_string()),
4323 dependency_type: Some(fdecl::DependencyType::Strong),
4324 ..Default::default()
4325 }),
4326 ]),
4327 children: Some(vec![
4328 fdecl::Child {
4329 name: Some("child1".to_string()),
4330 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4331 startup: Some(fdecl::StartupMode::Lazy),
4332 on_terminate: None,
4333 ..Default::default()
4334 },
4335 fdecl::Child {
4336 name: Some("child2".to_string()),
4337 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4338 startup: Some(fdecl::StartupMode::Lazy),
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("{{self -> child child1 -> child child2 -> self}}".to_string()),
4348 ])),
4349 },
4350 test_validate_strong_cycle_with_self_storage => {
4351 input = {
4352 fdecl::Component {
4353 capabilities: Some(vec![
4354 fdecl::Capability::Storage(fdecl::Storage {
4355 name: Some("data".to_string()),
4356 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4357 backing_dir: Some("minfs".to_string()),
4358 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4359 ..Default::default()
4360 }),
4361 fdecl::Capability::Directory(fdecl::Directory {
4362 name: Some("minfs".to_string()),
4363 source_path: Some("/minfs".to_string()),
4364 rights: Some(fio::RW_STAR_DIR),
4365 ..Default::default()
4366 }),
4367 ]),
4368 offers: Some(vec![
4369 fdecl::Offer::Storage(fdecl::OfferStorage {
4370 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4371 source_name: Some("data".to_string()),
4372 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4373 target_name: Some("data".to_string()),
4374 ..Default::default()
4375 }),
4376 ]),
4377 uses: Some(vec![
4378 fdecl::Use::Protocol(fdecl::UseProtocol {
4379 dependency_type: Some(fdecl::DependencyType::Strong),
4380 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4381 source_name: Some("fuchsia.foo.Bar".to_string()),
4382 target_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4383 ..Default::default()
4384 }),
4385 ]),
4386 children: Some(vec![
4387 fdecl::Child {
4388 name: Some("child".to_string()),
4389 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4390 startup: Some(fdecl::StartupMode::Lazy),
4391 ..Default::default()
4392 },
4393 ]),
4394 ..new_component_decl()
4395 }
4396 },
4397 result = Err(ErrorList::new(vec![
4398 Error::dependency_cycle("{{self -> capability data -> child child -> self}}".to_string()),
4399 ])),
4400 },
4401 test_validate_strong_cycle_with_self_storage_admin_protocol => {
4402 input = {
4403 fdecl::Component {
4404 capabilities: Some(vec![
4405 fdecl::Capability::Storage(fdecl::Storage {
4406 name: Some("data".to_string()),
4407 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4408 backing_dir: Some("minfs".to_string()),
4409 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4410 ..Default::default()
4411 }),
4412 fdecl::Capability::Directory(fdecl::Directory {
4413 name: Some("minfs".to_string()),
4414 source_path: Some("/minfs".to_string()),
4415 rights: Some(fio::RW_STAR_DIR),
4416 ..Default::default()
4417 }),
4418 ]),
4419 offers: Some(vec![
4420 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4421 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef { name: "data".to_string() })),
4422 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4423 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4424 target_name: Some("fuchsia.sys2.StorageAdmin".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 dependency_type: Some(fdecl::DependencyType::Strong),
4432 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4433 source_name: Some("fuchsia.foo.Bar".to_string()),
4434 target_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4435 ..Default::default()
4436 }),
4437 ]),
4438 children: Some(vec![
4439 fdecl::Child {
4440 name: Some("child".to_string()),
4441 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4442 startup: Some(fdecl::StartupMode::Lazy),
4443 ..Default::default()
4444 },
4445 ]),
4446 ..new_component_decl()
4447 }
4448 },
4449 result = Err(ErrorList::new(vec![
4450 Error::dependency_cycle("{{self -> capability data -> child child -> self}}".to_string()),
4451 ])),
4452 },
4453 test_validate_strong_cycle_with_dictionary => {
4454 input = fdecl::Component {
4455 offers: Some(vec![
4456 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
4457 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4458 source_name: Some("dict".into()),
4459 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4460 name: "a".into(),
4461 collection: None,
4462 })),
4463 target_name: Some("dict".into()),
4464 dependency_type: Some(fdecl::DependencyType::Strong),
4465 ..Default::default()
4466 }),
4467 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4468 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4469 name: "b".into(),
4470 collection: None,
4471 })),
4472 source_name: Some("1".into()),
4473 target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4474 name: "dict".into(),
4475 })),
4476 target_name: Some("1".into()),
4477 dependency_type: Some(fdecl::DependencyType::Strong),
4478 ..Default::default()
4479 }),
4480 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4481 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4482 name: "a".into(),
4483 collection: None,
4484 })),
4485 source_name: Some("2".into()),
4486 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4487 name: "b".into(),
4488 collection: None,
4489 })),
4490 target_name: Some("2".into()),
4491 dependency_type: Some(fdecl::DependencyType::Strong),
4492 ..Default::default()
4493 }),
4494 ]),
4495 children: Some(vec![
4496 fdecl::Child {
4497 name: Some("a".into()),
4498 url: Some("fuchsia-pkg://child".into()),
4499 startup: Some(fdecl::StartupMode::Lazy),
4500 ..Default::default()
4501 },
4502 fdecl::Child {
4503 name: Some("b".into()),
4504 url: Some("fuchsia-pkg://child".into()),
4505 startup: Some(fdecl::StartupMode::Lazy),
4506 ..Default::default()
4507 },
4508 ]),
4509 capabilities: Some(vec![
4510 fdecl::Capability::Dictionary(fdecl::Dictionary {
4511 name: Some("dict".into()),
4512 ..Default::default()
4513 }),
4514 ]),
4515 ..Default::default()
4516 },
4517 result = Err(ErrorList::new(vec![
4518 Error::dependency_cycle("{{child a -> child b -> capability dict -> child a}}".to_string()),
4519 ])),
4520 },
4521 test_validate_strong_cycle_with_dictionary_indirect => {
4522 input = fdecl::Component {
4523 offers: Some(vec![
4524 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4525 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4526 source_name: Some("3".into()),
4527 source_dictionary: Some("dict".into()),
4528 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4529 name: "a".into(),
4530 collection: None,
4531 })),
4532 target_name: Some("3".into()),
4533 dependency_type: Some(fdecl::DependencyType::Strong),
4534 ..Default::default()
4535 }),
4536 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4537 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4538 name: "b".into(),
4539 collection: None,
4540 })),
4541 source_name: Some("1".into()),
4542 target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4543 name: "dict".into(),
4544 })),
4545 target_name: Some("1".into()),
4546 dependency_type: Some(fdecl::DependencyType::Strong),
4547 ..Default::default()
4548 }),
4549 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4550 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4551 name: "a".into(),
4552 collection: None,
4553 })),
4554 source_name: Some("2".into()),
4555 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4556 name: "b".into(),
4557 collection: None,
4558 })),
4559 target_name: Some("2".into()),
4560 dependency_type: Some(fdecl::DependencyType::Strong),
4561 ..Default::default()
4562 }),
4563 ]),
4564 children: Some(vec![
4565 fdecl::Child {
4566 name: Some("a".into()),
4567 url: Some("fuchsia-pkg://child".into()),
4568 startup: Some(fdecl::StartupMode::Lazy),
4569 ..Default::default()
4570 },
4571 fdecl::Child {
4572 name: Some("b".into()),
4573 url: Some("fuchsia-pkg://child".into()),
4574 startup: Some(fdecl::StartupMode::Lazy),
4575 ..Default::default()
4576 },
4577 ]),
4578 capabilities: Some(vec![
4579 fdecl::Capability::Dictionary(fdecl::Dictionary {
4580 name: Some("dict".into()),
4581 ..Default::default()
4582 }),
4583 ]),
4584 ..Default::default()
4585 },
4586 result = Err(ErrorList::new(vec![
4587 Error::dependency_cycle("{{child a -> child b -> capability dict -> child a}}".to_string()),
4588 ])),
4589 },
4590 test_validate_use_from_child_offer_to_child_weak_cycle => {
4591 input = {
4592 fdecl::Component {
4593 capabilities: Some(vec![
4594 fdecl::Capability::Service(fdecl::Service {
4595 name: Some("a".to_string()),
4596 source_path: Some("/a".to_string()),
4597 ..Default::default()
4598 })]),
4599 uses: Some(vec![
4600 fdecl::Use::Protocol(fdecl::UseProtocol {
4601 dependency_type: Some(fdecl::DependencyType::Weak),
4602 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4603 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4604 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
4605 ..Default::default()
4606 }),
4607 fdecl::Use::Service(fdecl::UseService {
4608 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4609 source_name: Some("service_name".to_string()),
4610 target_path: Some("/svc/service_name".to_string()),
4611 dependency_type: Some(fdecl::DependencyType::Weak),
4612 ..Default::default()
4613 }),
4614 fdecl::Use::Directory(fdecl::UseDirectory {
4615 dependency_type: Some(fdecl::DependencyType::Weak),
4616 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4617 source_name: Some("DirectoryName".to_string()),
4618 target_path: Some("/data/DirectoryName".to_string()),
4619 rights: Some(fio::Operations::CONNECT),
4620 subdir: None,
4621 ..Default::default()
4622 }),
4623 ]),
4624 offers: Some(vec![
4625 fdecl::Offer::Service(fdecl::OfferService {
4626 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4627 source_name: Some("a".to_string()),
4628 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4629 target_name: Some("a".to_string()),
4630 ..Default::default()
4631 })
4632 ]),
4633 children: Some(vec![
4634 fdecl::Child {
4635 name: Some("child".to_string()),
4636 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4637 startup: Some(fdecl::StartupMode::Lazy),
4638 on_terminate: None,
4639 ..Default::default()
4640 }
4641 ]),
4642 ..new_component_decl()
4643 }
4644 },
4645 result = Ok(()),
4646 },
4647 test_validate_expose_from_self_to_framework_and_parent => {
4648 input = {
4649 fdecl::Component {
4650 capabilities: Some(vec![
4651 fdecl::Capability::Protocol(fdecl::Protocol {
4652 name: Some("a".to_string()),
4653 source_path: Some("/a".to_string()),
4654 ..Default::default()
4655 }),
4656 ]),
4657 exposes: Some(vec![
4658 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
4659 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4660 source_name: Some("a".to_string()),
4661 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4662 target_name: Some("a".to_string()),
4663 ..Default::default()
4664 }),
4665 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
4666 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4667 source_name: Some("a".to_string()),
4668 target: Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})),
4669 target_name: Some("a".to_string()),
4670 ..Default::default()
4671 }),
4672 ]),
4673 ..new_component_decl()
4674 }
4675 },
4676 result = Ok(()),
4677 },
4678 test_validate_use_from_not_child_weak => {
4679 input = {
4680 fdecl::Component {
4681 uses: Some(vec![
4682 fdecl::Use::Protocol(fdecl::UseProtocol {
4683 dependency_type: Some(fdecl::DependencyType::Weak),
4684 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4685 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4686 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
4687 ..Default::default()
4688 }),
4689 ]),
4690 ..new_component_decl()
4691 }
4692 },
4693 result = Err(ErrorList::new(vec![
4694 Error::invalid_field(DeclType::UseProtocol, "dependency_type"),
4695 ])),
4696 },
4697 test_validate_event_stream_offer_valid_decls => {
4698 input = {
4699 let mut decl = new_component_decl();
4700 decl.offers = Some(vec![
4701 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4702 source_name: Some("stopped".to_string()),
4703 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4704 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4705 target_name: Some("stopped".to_string()),
4706 ..Default::default()
4707 }),
4708 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4709 source_name: Some("started".to_string()),
4710 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4711 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4712 target_name: Some("started".to_string()),
4713 ..Default::default()
4714 }),
4715 ]);
4716 decl.children = Some(vec![fdecl::Child{
4717 name: Some("test".to_string()),
4718 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4719 startup: Some(fdecl::StartupMode::Lazy),
4720 on_terminate: None,
4721 environment: None,
4722 ..Default::default()
4723 },
4724 fdecl::Child{
4725 name: Some("test2".to_string()),
4726 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4727 startup: Some(fdecl::StartupMode::Lazy),
4728 on_terminate: None,
4729 environment: None,
4730 ..Default::default()
4731 }
4732 ]);
4733 decl
4734 },
4735 result = Ok(()),
4736 },
4737 test_validate_event_stream_offer_to_framework_invalid => {
4738 input = {
4739 let mut decl = new_component_decl();
4740 decl.offers = Some(vec![
4741 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4742 source_name: Some("stopped".to_string()),
4743 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4744 target: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
4745 target_name: Some("stopped".to_string()),
4746 ..Default::default()
4747 }),
4748 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4749 source_name: Some("started".to_string()),
4750 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4751 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4752 target_name: Some("started".to_string()),
4753 ..Default::default()
4754 }),
4755 ]);
4756 decl.children = Some(vec![fdecl::Child{
4757 name: Some("test".to_string()),
4758 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4759 startup: Some(fdecl::StartupMode::Lazy),
4760 on_terminate: None,
4761 environment: None,
4762 ..Default::default()
4763 },
4764 fdecl::Child{
4765 name: Some("test2".to_string()),
4766 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4767 startup: Some(fdecl::StartupMode::Lazy),
4768 on_terminate: None,
4769 environment: None,
4770 ..Default::default()
4771 }
4772 ]);
4773 decl
4774 },
4775 result = Err(ErrorList::new(vec![
4776 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "target".to_string() }),
4777 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "target".to_string() }),
4778 ])),
4779 },
4780 test_validate_event_stream_offer_to_scope_zero_length_invalid => {
4781 input = {
4782 let mut decl = new_component_decl();
4783 decl.offers = Some(vec![
4784 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4785 source_name: Some("started".to_string()),
4786 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4787 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4788 scope: Some(vec![]),
4789 target_name: Some("started".to_string()),
4790 ..Default::default()
4791 }),
4792 ]);
4793 decl.children = Some(vec![fdecl::Child{
4794 name: Some("test".to_string()),
4795 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4796 startup: Some(fdecl::StartupMode::Lazy),
4797 on_terminate: None,
4798 environment: None,
4799 ..Default::default()
4800 },
4801 fdecl::Child{
4802 name: Some("test2".to_string()),
4803 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4804 startup: Some(fdecl::StartupMode::Lazy),
4805 on_terminate: None,
4806 environment: None,
4807 ..Default::default()
4808 }
4809 ]);
4810 decl
4811 },
4812 result = Err(ErrorList::new(vec![
4813 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "scope".to_string() }),
4814 ])),
4815 },
4816 test_validate_event_stream_offer_to_scope_framework_invalid => {
4817 input = {
4818 let mut decl = new_component_decl();
4819 decl.offers = Some(vec![
4820 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4821 source_name: Some("started".to_string()),
4822 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4823 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4824 scope: Some(vec![fdecl::Ref::Framework(fdecl::FrameworkRef{})]),
4825 target_name: Some("started".to_string()),
4826 ..Default::default()
4827 }),
4828 ]);
4829 decl.children = Some(vec![fdecl::Child{
4830 name: Some("test".to_string()),
4831 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4832 startup: Some(fdecl::StartupMode::Lazy),
4833 on_terminate: None,
4834 environment: None,
4835 ..Default::default()
4836 },
4837 fdecl::Child{
4838 name: Some("test2".to_string()),
4839 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4840 startup: Some(fdecl::StartupMode::Lazy),
4841 on_terminate: None,
4842 environment: None,
4843 ..Default::default()
4844 }
4845 ]);
4846 decl
4847 },
4848 result = Err(ErrorList::new(vec![
4849 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "scope".to_string() }),
4850 ])),
4851 },
4852 test_validate_event_stream_offer_to_scope_valid => {
4853 input = {
4854 let mut decl = new_component_decl();
4855 decl.offers = Some(vec![
4856 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4857 source_name: Some("started".to_string()),
4858 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4859 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4860 scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
4861 target_name: Some("started".to_string()),
4862 ..Default::default()
4863 }),
4864 ]);
4865 decl.children = Some(vec![fdecl::Child{
4866 name: Some("test".to_string()),
4867 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4868 startup: Some(fdecl::StartupMode::Lazy),
4869 on_terminate: None,
4870 environment: None,
4871 ..Default::default()
4872 },
4873 fdecl::Child{
4874 name: Some("test2".to_string()),
4875 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4876 startup: Some(fdecl::StartupMode::Lazy),
4877 on_terminate: None,
4878 environment: None,
4879 ..Default::default()
4880 }
4881 ]);
4882 decl
4883 },
4884 result = Ok(()),
4885 },
4886 test_validate_event_stream_offer_to_scope_with_capability_requested => {
4887 input = {
4888 let mut decl = new_component_decl();
4889 decl.offers = Some(vec![
4890 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4891 source_name: Some("capability_requested".to_string()),
4892 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4893 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4894 scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
4895 target_name: Some("started".to_string()),
4896 ..Default::default()
4897 }),
4898 ]);
4899 decl.children = Some(vec![fdecl::Child{
4900 name: Some("test".to_string()),
4901 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4902 startup: Some(fdecl::StartupMode::Lazy),
4903 on_terminate: None,
4904 environment: None,
4905 ..Default::default()
4906 },
4907 fdecl::Child{
4908 name: Some("test2".to_string()),
4909 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4910 startup: Some(fdecl::StartupMode::Lazy),
4911 on_terminate: None,
4912 environment: None,
4913 ..Default::default()
4914 }
4915 ]);
4916 decl
4917 },
4918 result = Ok(()),
4919 },
4920 test_validate_event_stream_offer_with_no_source_name_invalid => {
4921 input = {
4922 let mut decl = new_component_decl();
4923 decl.offers = Some(vec![
4924 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4925 source_name: None,
4926 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4927 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4928 scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
4929 target_name: Some("started".to_string()),
4930 ..Default::default()
4931 }),
4932 ]);
4933 decl.children = Some(vec![fdecl::Child{
4934 name: Some("test".to_string()),
4935 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4936 startup: Some(fdecl::StartupMode::Lazy),
4937 on_terminate: None,
4938 environment: None,
4939 ..Default::default()
4940 },
4941 fdecl::Child{
4942 name: Some("test2".to_string()),
4943 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4944 startup: Some(fdecl::StartupMode::Lazy),
4945 on_terminate: None,
4946 environment: None,
4947 ..Default::default()
4948 }
4949 ]);
4950 decl
4951 },
4952 result = Err(ErrorList::new(vec![
4953 Error::MissingField(DeclField { decl: DeclType::OfferEventStream, field: "source_name".to_string() }),
4954 ])),
4955 },
4956 test_validate_event_stream_offer_invalid_source => {
4957 input = {
4958 let mut decl = new_component_decl();
4959 decl.offers = Some(vec![
4960 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4961 source_name: Some("stopped".to_string()),
4962 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
4963 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4964 target_name: Some("stopped".to_string()),
4965 ..Default::default()
4966 }),
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 target_name: Some("started".to_string()),
4972 ..Default::default()
4973 }),
4974 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4975 source_name: Some("capability_requested".to_string()),
4976 source: Some(fdecl::Ref::Debug(fdecl::DebugRef{})),
4977 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4978 target_name: Some("capability_requested".to_string()),
4979 ..Default::default()
4980 }),
4981 ]);
4982 decl.children = Some(vec![fdecl::Child{
4983 name: Some("test".to_string()),
4984 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4985 startup: Some(fdecl::StartupMode::Lazy),
4986 on_terminate: None,
4987 environment: None,
4988 ..Default::default()
4989 },
4990 fdecl::Child{
4991 name: Some("test2".to_string()),
4992 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4993 startup: Some(fdecl::StartupMode::Lazy),
4994 on_terminate: None,
4995 environment: None,
4996 ..Default::default()
4997 }
4998 ]);
4999 decl
5000 },
5001 result = Err(ErrorList::new(vec![
5002 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "source".to_string() }),
5003 ])),
5004 },
5005
5006 test_validate_event_stream_offer_missing_source => {
5007 input = {
5008 let mut decl = new_component_decl();
5009 decl.offers = Some(vec![
5010 fdecl::Offer::EventStream(fdecl::OfferEventStream {
5011 source_name: Some("stopped".to_string()),
5012 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
5013 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5014 target_name: Some("stopped".to_string()),
5015 ..Default::default()
5016 }),
5017 fdecl::Offer::EventStream(fdecl::OfferEventStream {
5018 source_name: Some("started".to_string()),
5019 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5020 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5021 target_name: Some("started".to_string()),
5022 ..Default::default()
5023 }),
5024 fdecl::Offer::EventStream(fdecl::OfferEventStream {
5025 source_name: Some("capability_requested".to_string()),
5026 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5027 target_name: Some("capability_requested".to_string()),
5028 ..Default::default()
5029 }),
5030 ]);
5031 decl.children = Some(vec![fdecl::Child{
5032 name: Some("test".to_string()),
5033 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5034 startup: Some(fdecl::StartupMode::Lazy),
5035 on_terminate: None,
5036 environment: None,
5037 ..Default::default()
5038 },
5039 fdecl::Child{
5040 name: Some("test2".to_string()),
5041 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
5042 startup: Some(fdecl::StartupMode::Lazy),
5043 on_terminate: None,
5044 environment: None,
5045 ..Default::default()
5046 }
5047 ]);
5048 decl
5049 },
5050 result = Err(ErrorList::new(vec![
5051 Error::MissingField(DeclField { decl: DeclType::OfferEventStream, field: "source".to_string() }),
5052 ])),
5053 },
5054 test_validate_event_stream_must_have_target_path => {
5055 input = {
5056 let mut decl = new_component_decl();
5057 decl.uses = Some(vec![
5058 fdecl::Use::EventStream(fdecl::UseEventStream {
5059 source_name: Some("bar".to_string()),
5060 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5061 ..Default::default()
5062 }),
5063 ]);
5064 decl
5065 },
5066 result = Err(ErrorList::new(vec![
5067 Error::MissingField(DeclField { decl: DeclType::UseEventStream, field: "target_path".to_string() })
5068 ])),
5069 },
5070 test_validate_event_stream_must_have_source_names => {
5071 input = {
5072 let mut decl = new_component_decl();
5073 decl.uses = Some(vec![
5074 fdecl::Use::EventStream(fdecl::UseEventStream {
5075 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5076 target_path: Some("/svc/something".to_string()),
5077 ..Default::default()
5078 }),
5079 ]);
5080 decl
5081 },
5082 result = Err(ErrorList::new(vec![
5083 Error::MissingField(DeclField { decl: DeclType::UseEventStream, field: "source_name".to_string() })
5084 ])),
5085 },
5086 test_validate_event_stream_scope_must_be_child_or_collection => {
5087 input = {
5088 let mut decl = new_component_decl();
5089 decl.uses = Some(vec![
5090 fdecl::Use::EventStream(fdecl::UseEventStream {
5091 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5092 target_path: Some("/svc/something".to_string()),
5093 source_name: Some("some_source".to_string()),
5094 scope: Some(vec![fdecl::Ref::Framework(fdecl::FrameworkRef{})]),
5095 ..Default::default()
5096 }),
5097 ]);
5098 decl
5099 },
5100 result = Err(ErrorList::new(vec![
5101 Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "scope".to_string() })
5102 ])),
5103 },
5104 test_validate_event_stream_source_must_be_parent_or_child => {
5105 input = {
5106 let mut decl = new_component_decl();
5107 decl.uses = Some(vec![
5108 fdecl::Use::EventStream(fdecl::UseEventStream {
5109 source: Some(fdecl::Ref::Debug(fdecl::DebugRef{})),
5110 target_path: Some("/svc/something".to_string()),
5111 source_name: Some("some_source".to_string()),
5112 scope: Some(vec![]),
5113 ..Default::default()
5114 }),
5115 fdecl::Use::EventStream(fdecl::UseEventStream {
5116 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
5117 target_path: Some("/svc/something_else".to_string()),
5118 source_name: Some("some_source".to_string()),
5119 scope: Some(vec![]),
5120 ..Default::default()
5121 }),
5122 fdecl::Use::EventStream(fdecl::UseEventStream {
5123 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5124 target_path: Some("/svc/yet_something_else".to_string()),
5125 source_name: Some("some_source".to_string()),
5126 scope: Some(vec![]),
5127 ..Default::default()
5128 }),
5129 ]);
5130 decl
5131 },
5132 result = Err(ErrorList::new(vec![
5133 Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() }),
5134 Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() }),
5135 Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() })
5136 ])),
5137 },
5138 test_validate_no_runner => {
5139 input = {
5140 let mut decl = new_component_decl();
5141 decl.program = Some(fdecl::Program {
5142 runner: None,
5143 info: Some(fdata::Dictionary {
5144 entries: None,
5145 ..Default::default()
5146 }),
5147 ..Default::default()
5148 });
5149 decl
5150 },
5151 result = Err(ErrorList::new(vec![
5152 Error::MissingRunner,
5153 ])),
5154 },
5155 test_validate_uses_runner => {
5156 input = {
5157 let mut decl = new_component_decl();
5158 decl.program = Some(fdecl::Program {
5159 runner: None,
5160 info: Some(fdata::Dictionary {
5161 entries: None,
5162 ..Default::default()
5163 }),
5164 ..Default::default()
5165 });
5166 decl.uses = Some(vec![
5167 fdecl::Use::Runner(fdecl::UseRunner {
5168 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5169 source_name: Some("runner".to_string()),
5170 ..Default::default()
5171 }),
5172 ]);
5173 decl
5174 },
5175 result = Ok(()),
5176 },
5177 test_validate_program_and_uses_runner_match => {
5178 input = {
5179 let mut decl = new_component_decl();
5180 decl.program = Some(fdecl::Program {
5181 runner: Some("runner".to_string()),
5182 info: Some(fdata::Dictionary {
5183 entries: None,
5184 ..Default::default()
5185 }),
5186 ..Default::default()
5187 });
5188 decl.uses = Some(vec![
5189 fdecl::Use::Runner(fdecl::UseRunner {
5190 source: Some(fdecl::Ref::Environment(fdecl::EnvironmentRef {})),
5191 source_name: Some("runner".to_string()),
5192 ..Default::default()
5193 }),
5194 ]);
5195 decl
5196 },
5197 result = Ok(()),
5198 },
5199 test_validate_runner_names_conflict => {
5200 input = {
5201 let mut decl = new_component_decl();
5202 decl.program = Some(fdecl::Program {
5203 runner: Some("runner".to_string()),
5204 info: Some(fdata::Dictionary {
5205 entries: None,
5206 ..Default::default()
5207 }),
5208 ..Default::default()
5209 });
5210 decl.uses = Some(vec![
5211 fdecl::Use::Runner(fdecl::UseRunner {
5212 source: Some(fdecl::Ref::Environment(fdecl::EnvironmentRef {})),
5213 source_name: Some("other.runner".to_string()),
5214 ..Default::default()
5215 }),
5216 ]);
5217 decl
5218 },
5219 result = Err(ErrorList::new(vec![
5220 Error::ConflictingRunners,
5221 ])),
5222 },
5223 test_validate_uses_runner_not_environement => {
5224 input = {
5225 let mut decl = new_component_decl();
5226 decl.program = Some(fdecl::Program {
5227 runner: Some("runner".to_string()),
5228 info: Some(fdata::Dictionary {
5229 entries: None,
5230 ..Default::default()
5231 }),
5232 ..Default::default()
5233 });
5234 decl.uses = Some(vec![
5235 fdecl::Use::Runner(fdecl::UseRunner {
5236 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5237 source_name: Some("runner".to_string()),
5238 ..Default::default()
5239 }),
5240 ]);
5241 decl
5242 },
5243 result = Err(ErrorList::new(vec![
5244 Error::ConflictingRunners,
5245 ])),
5246 },
5247 test_validate_uses_long_identifiers => {
5248 input = {
5249 let mut decl = new_component_decl();
5250 decl.program = Some(fdecl::Program {
5251 runner: Some("elf".to_string()),
5252 info: Some(fdata::Dictionary {
5253 entries: None,
5254 ..Default::default()
5255 }),
5256 ..Default::default()
5257 });
5258 decl.uses = Some(vec![
5259 fdecl::Use::Service(fdecl::UseService {
5260 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5261 source_name: Some(format!("{}", "a".repeat(256))),
5262 target_path: Some("/a".repeat(2048)),
5263 dependency_type: Some(fdecl::DependencyType::Strong),
5264 ..Default::default()
5265 }),
5266 fdecl::Use::Protocol(fdecl::UseProtocol {
5267 dependency_type: Some(fdecl::DependencyType::Strong),
5268 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5269 source_name: Some(format!("{}", "a".repeat(256))),
5270 target_path: Some("/b".repeat(2048)),
5271 ..Default::default()
5272 }),
5273 fdecl::Use::Directory(fdecl::UseDirectory {
5274 dependency_type: Some(fdecl::DependencyType::Strong),
5275 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5276 source_name: Some(format!("{}", "a".repeat(256))),
5277 target_path: Some("/c".repeat(2048)),
5278 rights: Some(fio::Operations::CONNECT),
5279 subdir: None,
5280 ..Default::default()
5281 }),
5282 fdecl::Use::Storage(fdecl::UseStorage {
5283 source_name: Some("cache".to_string()),
5284 target_path: Some("/d".repeat(2048)),
5285 ..Default::default()
5286 }),
5287 ]);
5288 decl
5289 },
5290 result = Err(ErrorList::new(vec![
5291 Error::field_too_long(DeclType::UseService, "source_name"),
5292 Error::field_too_long(DeclType::UseService, "target_path"),
5293 Error::field_too_long(DeclType::UseProtocol, "source_name"),
5294 Error::field_too_long(DeclType::UseProtocol, "target_path"),
5295 Error::field_too_long(DeclType::UseDirectory, "source_name"),
5296 Error::field_too_long(DeclType::UseDirectory, "target_path"),
5297 Error::field_too_long(DeclType::UseStorage, "target_path"),
5298 ])),
5299 },
5300 test_validate_conflicting_paths => {
5301 input = {
5302 let mut decl = new_component_decl();
5303 decl.uses = Some(vec![
5304 fdecl::Use::Service(fdecl::UseService {
5305 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5306 source_name: Some("foo".to_string()),
5307 target_path: Some("/bar".to_string()),
5308 dependency_type: Some(fdecl::DependencyType::Strong),
5309 ..Default::default()
5310 }),
5311 fdecl::Use::Protocol(fdecl::UseProtocol {
5312 dependency_type: Some(fdecl::DependencyType::Strong),
5313 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5314 source_name: Some("space".to_string()),
5315 target_path: Some("/bar".to_string()),
5316 ..Default::default()
5317 }),
5318 fdecl::Use::Directory(fdecl::UseDirectory {
5319 dependency_type: Some(fdecl::DependencyType::Strong),
5320 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5321 source_name: Some("crow".to_string()),
5322 target_path: Some("/bar".to_string()),
5323 rights: Some(fio::Operations::CONNECT),
5324 subdir: None,
5325 ..Default::default()
5326 }),
5327 ]);
5328 decl
5329 },
5330 result = Err(ErrorList::new(vec![
5331 Error::duplicate_field(DeclType::UseProtocol, "target_path", "/bar"),
5332 Error::duplicate_field(DeclType::UseDirectory, "target_path", "/bar"),
5333 ])),
5334 },
5335 test_validate_exposes_empty => {
5337 input = {
5338 let mut decl = new_component_decl();
5339 decl.exposes = Some(vec![
5340 fdecl::Expose::Service(fdecl::ExposeService {
5341 source: None,
5342 source_name: None,
5343 target_name: None,
5344 target: None,
5345 ..Default::default()
5346 }),
5347 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5348 source: None,
5349 source_name: None,
5350 target_name: None,
5351 target: None,
5352 ..Default::default()
5353 }),
5354 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5355 source: None,
5356 source_name: None,
5357 target_name: None,
5358 target: None,
5359 rights: None,
5360 subdir: None,
5361 ..Default::default()
5362 }),
5363 fdecl::Expose::Runner(fdecl::ExposeRunner {
5364 source: None,
5365 source_name: None,
5366 target: None,
5367 target_name: None,
5368 ..Default::default()
5369 }),
5370 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5371 source: None,
5372 source_name: None,
5373 target: None,
5374 target_name: None,
5375 ..Default::default()
5376 }),
5377 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5378 ..Default::default()
5379 }),
5380 ]);
5381 decl
5382 },
5383 result = Err(ErrorList::new(vec![
5384 Error::missing_field(DeclType::ExposeService, "source"),
5385 Error::missing_field(DeclType::ExposeService, "target"),
5386 Error::missing_field(DeclType::ExposeService, "source_name"),
5387 Error::missing_field(DeclType::ExposeService, "target_name"),
5388 Error::missing_field(DeclType::ExposeProtocol, "source"),
5389 Error::missing_field(DeclType::ExposeProtocol, "target"),
5390 Error::missing_field(DeclType::ExposeProtocol, "source_name"),
5391 Error::missing_field(DeclType::ExposeProtocol, "target_name"),
5392 Error::missing_field(DeclType::ExposeDirectory, "source"),
5393 Error::missing_field(DeclType::ExposeDirectory, "target"),
5394 Error::missing_field(DeclType::ExposeDirectory, "source_name"),
5395 Error::missing_field(DeclType::ExposeDirectory, "target_name"),
5396 Error::missing_field(DeclType::ExposeRunner, "source"),
5397 Error::missing_field(DeclType::ExposeRunner, "target"),
5398 Error::missing_field(DeclType::ExposeRunner, "source_name"),
5399 Error::missing_field(DeclType::ExposeRunner, "target_name"),
5400 Error::missing_field(DeclType::ExposeResolver, "source"),
5401 Error::missing_field(DeclType::ExposeResolver, "target"),
5402 Error::missing_field(DeclType::ExposeResolver, "source_name"),
5403 Error::missing_field(DeclType::ExposeResolver, "target_name"),
5404 Error::missing_field(DeclType::ExposeDictionary, "source"),
5405 Error::missing_field(DeclType::ExposeDictionary, "target"),
5406 Error::missing_field(DeclType::ExposeDictionary, "source_name"),
5407 Error::missing_field(DeclType::ExposeDictionary, "target_name"),
5408 ])),
5409 },
5410 test_validate_exposes_extraneous => {
5411 input = {
5412 let mut decl = new_component_decl();
5413 decl.exposes = Some(vec![
5414 fdecl::Expose::Service(fdecl::ExposeService {
5415 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5416 name: "logger".to_string(),
5417 collection: Some("modular".to_string()),
5418 })),
5419 source_name: Some("logger".to_string()),
5420 target_name: Some("logger".to_string()),
5421 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5422 ..Default::default()
5423 }),
5424 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5425 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5426 name: "logger".to_string(),
5427 collection: Some("modular".to_string()),
5428 })),
5429 source_name: Some("legacy_logger".to_string()),
5430 target_name: Some("legacy_logger".to_string()),
5431 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5432 ..Default::default()
5433 }),
5434 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5435 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5436 name: "netstack".to_string(),
5437 collection: Some("modular".to_string()),
5438 })),
5439 source_name: Some("data".to_string()),
5440 target_name: Some("data".to_string()),
5441 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5442 rights: Some(fio::Operations::CONNECT),
5443 subdir: None,
5444 ..Default::default()
5445 }),
5446 fdecl::Expose::Runner(fdecl::ExposeRunner {
5447 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5448 name: "netstack".to_string(),
5449 collection: Some("modular".to_string()),
5450 })),
5451 source_name: Some("elf".to_string()),
5452 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5453 target_name: Some("elf".to_string()),
5454 ..Default::default()
5455 }),
5456 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5457 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5458 name: "netstack".to_string(),
5459 collection: Some("modular".to_string()),
5460 })),
5461 source_name: Some("pkg".to_string()),
5462 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5463 target_name: Some("pkg".to_string()),
5464 ..Default::default()
5465 }),
5466 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5467 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5468 name: "netstack".to_string(),
5469 collection: Some("modular".to_string()),
5470 })),
5471 source_name: Some("dict".to_string()),
5472 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5473 target_name: Some("dict".to_string()),
5474 ..Default::default()
5475 }),
5476 ]);
5477 decl
5478 },
5479 result = Err(ErrorList::new(vec![
5480 Error::extraneous_field(DeclType::ExposeService, "source.child.collection"),
5481 Error::extraneous_field(DeclType::ExposeProtocol, "source.child.collection"),
5482 Error::extraneous_field(DeclType::ExposeDirectory, "source.child.collection"),
5483 Error::extraneous_field(DeclType::ExposeRunner, "source.child.collection"),
5484 Error::extraneous_field(DeclType::ExposeResolver, "source.child.collection"),
5485 Error::extraneous_field(DeclType::ExposeDictionary, "source.child.collection"),
5486 ])),
5487 },
5488 test_validate_exposes_invalid_identifiers => {
5489 input = {
5490 let mut decl = new_component_decl();
5491 decl.exposes = Some(vec![
5492 fdecl::Expose::Service(fdecl::ExposeService {
5493 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5494 name: "^bad".to_string(),
5495 collection: None,
5496 })),
5497 source_name: Some("foo/".to_string()),
5498 target_name: Some("/".to_string()),
5499 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5500 ..Default::default()
5501 }),
5502 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5503 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5504 name: "^bad".to_string(),
5505 collection: None,
5506 })),
5507 source_name: Some("foo/".to_string()),
5508 target_name: Some("/".to_string()),
5509 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5510 ..Default::default()
5511 }),
5512 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5513 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5514 name: "^bad".to_string(),
5515 collection: None,
5516 })),
5517 source_name: Some("foo/".to_string()),
5518 target_name: Some("/".to_string()),
5519 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5520 rights: Some(fio::Operations::CONNECT),
5521 subdir: Some("/foo".to_string()),
5522 ..Default::default()
5523 }),
5524 fdecl::Expose::Runner(fdecl::ExposeRunner {
5525 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5526 name: "^bad".to_string(),
5527 collection: None,
5528 })),
5529 source_name: Some("/path".to_string()),
5530 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5531 target_name: Some("elf!".to_string()),
5532 ..Default::default()
5533 }),
5534 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5535 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5536 name: "^bad".to_string(),
5537 collection: None,
5538 })),
5539 source_name: Some("/path".to_string()),
5540 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5541 target_name: Some("pkg!".to_string()),
5542 ..Default::default()
5543 }),
5544 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5545 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5546 name: "^bad".to_string(),
5547 collection: None,
5548 })),
5549 source_name: Some("/path".to_string()),
5550 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5551 target_name: Some("pkg!".to_string()),
5552 ..Default::default()
5553 }),
5554 ]);
5555 decl
5556 },
5557 result = Err(ErrorList::new(vec![
5558 Error::invalid_field(DeclType::ExposeService, "source.child.name"),
5559 Error::invalid_field(DeclType::ExposeService, "source_name"),
5560 Error::invalid_field(DeclType::ExposeService, "target_name"),
5561 Error::invalid_field(DeclType::ExposeProtocol, "source.child.name"),
5562 Error::invalid_field(DeclType::ExposeProtocol, "source_name"),
5563 Error::invalid_field(DeclType::ExposeProtocol, "target_name"),
5564 Error::invalid_field(DeclType::ExposeDirectory, "source.child.name"),
5565 Error::invalid_field(DeclType::ExposeDirectory, "source_name"),
5566 Error::invalid_field(DeclType::ExposeDirectory, "target_name"),
5567 Error::invalid_field(DeclType::ExposeDirectory, "subdir"),
5568 Error::invalid_field(DeclType::ExposeRunner, "source.child.name"),
5569 Error::invalid_field(DeclType::ExposeRunner, "source_name"),
5570 Error::invalid_field(DeclType::ExposeRunner, "target_name"),
5571 Error::invalid_field(DeclType::ExposeResolver, "source.child.name"),
5572 Error::invalid_field(DeclType::ExposeResolver, "source_name"),
5573 Error::invalid_field(DeclType::ExposeResolver, "target_name"),
5574 Error::invalid_field(DeclType::ExposeDictionary, "source.child.name"),
5575 Error::invalid_field(DeclType::ExposeDictionary, "source_name"),
5576 Error::invalid_field(DeclType::ExposeDictionary, "target_name"),
5577 ])),
5578 },
5579 test_validate_exposes_invalid_source_target => {
5580 input = {
5581 let mut decl = new_component_decl();
5582 decl.children = Some(vec![fdecl::Child{
5583 name: Some("logger".to_string()),
5584 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5585 startup: Some(fdecl::StartupMode::Lazy),
5586 on_terminate: None,
5587 environment: None,
5588 ..Default::default()
5589 }]);
5590 decl.exposes = Some(vec![
5591 fdecl::Expose::Service(fdecl::ExposeService {
5592 source: None,
5593 source_name: Some("a".to_string()),
5594 target_name: Some("b".to_string()),
5595 target: None,
5596 ..Default::default()
5597 }),
5598 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5599 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5600 source_name: Some("c".to_string()),
5601 target_name: Some("d".to_string()),
5602 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5603 ..Default::default()
5604 }),
5605 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5606 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "z".to_string()})),
5607 source_name: Some("e".to_string()),
5608 target_name: Some("f".to_string()),
5609 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "z".to_string()})),
5610 rights: Some(fio::Operations::CONNECT),
5611 subdir: None,
5612 ..Default::default()
5613 }),
5614 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5615 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5616 source_name: Some("g".to_string()),
5617 target_name: Some("h".to_string()),
5618 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5619 rights: Some(fio::Operations::CONNECT),
5620 subdir: None,
5621 ..Default::default()
5622 }),
5623 fdecl::Expose::Runner(fdecl::ExposeRunner {
5624 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5625 source_name: Some("i".to_string()),
5626 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5627 target_name: Some("j".to_string()),
5628 ..Default::default()
5629 }),
5630 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5631 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5632 source_name: Some("k".to_string()),
5633 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5634 target_name: Some("l".to_string()),
5635 ..Default::default()
5636 }),
5637 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5638 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5639 name: "logger".to_string(),
5640 collection: None,
5641 })),
5642 source_name: Some("m".to_string()),
5643 target_name: Some("n".to_string()),
5644 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5645 ..Default::default()
5646 }),
5647 ]);
5648 decl
5649 },
5650 result = Err(ErrorList::new(vec![
5651 Error::missing_field(DeclType::ExposeService, "source"),
5652 Error::missing_field(DeclType::ExposeService, "target"),
5653 Error::invalid_field(DeclType::ExposeProtocol, "source"),
5654 Error::invalid_field(DeclType::ExposeProtocol, "target"),
5655 Error::invalid_field(DeclType::ExposeDirectory, "source"),
5656 Error::invalid_field(DeclType::ExposeDirectory, "target"),
5657 Error::invalid_field(DeclType::ExposeDirectory, "source"),
5658 Error::invalid_field(DeclType::ExposeDirectory, "target"),
5659 Error::invalid_field(DeclType::ExposeRunner, "source"),
5660 Error::invalid_field(DeclType::ExposeRunner, "target"),
5661 Error::invalid_field(DeclType::ExposeResolver, "source"),
5662 Error::invalid_field(DeclType::ExposeResolver, "target"),
5663 Error::invalid_field(DeclType::ExposeDictionary, "target"),
5664 ])),
5665 },
5666 test_validate_exposes_invalid_source_collection => {
5667 input = {
5668 let mut decl = new_component_decl();
5669 decl.collections = Some(vec![fdecl::Collection{
5670 name: Some("col".to_string()),
5671 durability: Some(fdecl::Durability::Transient),
5672 allowed_offers: None,
5673 allow_long_names: None,
5674 ..Default::default()
5675 }]);
5676 decl.exposes = Some(vec![
5677 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5678 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
5679 source_name: Some("a".to_string()),
5680 target_name: Some("a".to_string()),
5681 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5682 ..Default::default()
5683 }),
5684 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5685 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5686 source_name: Some("b".to_string()),
5687 target_name: Some("b".to_string()),
5688 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5689 rights: Some(fio::Operations::CONNECT),
5690 subdir: None,
5691 ..Default::default()
5692 }),
5693 fdecl::Expose::Runner(fdecl::ExposeRunner {
5694 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5695 source_name: Some("c".to_string()),
5696 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5697 target_name: Some("c".to_string()),
5698 ..Default::default()
5699 }),
5700 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5701 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5702 source_name: Some("d".to_string()),
5703 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5704 target_name: Some("d".to_string()),
5705 ..Default::default()
5706 }),
5707 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5708 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5709 source_name: Some("e".to_string()),
5710 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5711 target_name: Some("e".to_string()),
5712 ..Default::default()
5713 }),
5714 ]);
5715 decl
5716 },
5717 result = Err(ErrorList::new(vec![
5718 Error::invalid_field(DeclType::ExposeProtocol, "source"),
5719 Error::invalid_field(DeclType::ExposeDirectory, "source"),
5720 Error::invalid_field(DeclType::ExposeRunner, "source"),
5721 Error::invalid_field(DeclType::ExposeResolver, "source"),
5722 Error::invalid_field(DeclType::ExposeDictionary, "source"),
5723 ])),
5724 },
5725 test_validate_exposes_sources_collection => {
5726 input = {
5727 let mut decl = new_component_decl();
5728 decl.collections = Some(vec![
5729 fdecl::Collection {
5730 name: Some("col".to_string()),
5731 durability: Some(fdecl::Durability::Transient),
5732 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
5733 allow_long_names: None,
5734 ..Default::default()
5735 }
5736 ]);
5737 decl.exposes = Some(vec![
5738 fdecl::Expose::Service(fdecl::ExposeService {
5739 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
5740 source_name: Some("a".to_string()),
5741 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5742 target_name: Some("a".to_string()),
5743 ..Default::default()
5744 })
5745 ]);
5746 decl
5747 },
5748 result = Ok(()),
5749 },
5750 test_validate_exposes_long_identifiers => {
5751 input = {
5752 let mut decl = new_component_decl();
5753 decl.exposes = Some(vec![
5754 fdecl::Expose::Service(fdecl::ExposeService {
5755 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5756 name: "b".repeat(256),
5757 collection: None,
5758 })),
5759 source_name: Some(format!("{}", "a".repeat(1025))),
5760 target_name: Some(format!("{}", "b".repeat(1025))),
5761 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5762 ..Default::default()
5763 }),
5764 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5765 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5766 name: "b".repeat(256),
5767 collection: None,
5768 })),
5769 source_name: Some(format!("{}", "a".repeat(256))),
5770 target_name: Some(format!("{}", "b".repeat(256))),
5771 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5772 ..Default::default()
5773 }),
5774 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5775 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5776 name: "b".repeat(256),
5777 collection: None,
5778 })),
5779 source_name: Some(format!("{}", "a".repeat(256))),
5780 target_name: Some(format!("{}", "b".repeat(256))),
5781 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5782 rights: Some(fio::Operations::CONNECT),
5783 subdir: None,
5784 ..Default::default()
5785 }),
5786 fdecl::Expose::Runner(fdecl::ExposeRunner {
5787 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5788 name: "b".repeat(256),
5789 collection: None,
5790 })),
5791 source_name: Some("a".repeat(256)),
5792 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5793 target_name: Some("b".repeat(256)),
5794 ..Default::default()
5795 }),
5796 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5797 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5798 name: "b".repeat(256),
5799 collection: None,
5800 })),
5801 source_name: Some("a".repeat(256)),
5802 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5803 target_name: Some("b".repeat(256)),
5804 ..Default::default()
5805 }),
5806 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5807 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5808 name: "b".repeat(256),
5809 collection: None,
5810 })),
5811 source_name: Some("a".repeat(256)),
5812 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5813 target_name: Some("b".repeat(256)),
5814 ..Default::default()
5815 }),
5816 ]);
5817 decl
5818 },
5819 result = Err(ErrorList::new(vec![
5820 Error::field_too_long(DeclType::ExposeService, "source.child.name"),
5821 Error::field_too_long(DeclType::ExposeService, "source_name"),
5822 Error::field_too_long(DeclType::ExposeService, "target_name"),
5823 Error::field_too_long(DeclType::ExposeProtocol, "source.child.name"),
5824 Error::field_too_long(DeclType::ExposeProtocol, "source_name"),
5825 Error::field_too_long(DeclType::ExposeProtocol, "target_name"),
5826 Error::field_too_long(DeclType::ExposeDirectory, "source.child.name"),
5827 Error::field_too_long(DeclType::ExposeDirectory, "source_name"),
5828 Error::field_too_long(DeclType::ExposeDirectory, "target_name"),
5829 Error::field_too_long(DeclType::ExposeRunner, "source.child.name"),
5830 Error::field_too_long(DeclType::ExposeRunner, "source_name"),
5831 Error::field_too_long(DeclType::ExposeRunner, "target_name"),
5832 Error::field_too_long(DeclType::ExposeResolver, "source.child.name"),
5833 Error::field_too_long(DeclType::ExposeResolver, "source_name"),
5834 Error::field_too_long(DeclType::ExposeResolver, "target_name"),
5835 Error::field_too_long(DeclType::ExposeDictionary, "source.child.name"),
5836 Error::field_too_long(DeclType::ExposeDictionary, "source_name"),
5837 Error::field_too_long(DeclType::ExposeDictionary, "target_name"),
5838 ])),
5839 },
5840 test_validate_exposes_invalid_child => {
5841 input = {
5842 let mut decl = new_component_decl();
5843 decl.exposes = Some(vec![
5844 fdecl::Expose::Service(fdecl::ExposeService {
5845 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5846 name: "netstack".to_string(),
5847 collection: None,
5848 })),
5849 source_name: Some("fuchsia.logger.Log".to_string()),
5850 target_name: Some("fuchsia.logger.Log".to_string()),
5851 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5852 ..Default::default()
5853 }),
5854 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5855 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5856 name: "netstack".to_string(),
5857 collection: None,
5858 })),
5859 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
5860 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
5861 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5862 ..Default::default()
5863 }),
5864 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5865 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5866 name: "netstack".to_string(),
5867 collection: None,
5868 })),
5869 source_name: Some("data".to_string()),
5870 target_name: Some("data".to_string()),
5871 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5872 rights: Some(fio::Operations::CONNECT),
5873 subdir: None,
5874 ..Default::default()
5875 }),
5876 fdecl::Expose::Runner(fdecl::ExposeRunner {
5877 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5878 name: "netstack".to_string(),
5879 collection: None,
5880 })),
5881 source_name: Some("elf".to_string()),
5882 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5883 target_name: Some("elf".to_string()),
5884 ..Default::default()
5885 }),
5886 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5887 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5888 name: "netstack".to_string(),
5889 collection: None,
5890 })),
5891 source_name: Some("pkg".to_string()),
5892 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5893 target_name: Some("pkg".to_string()),
5894 ..Default::default()
5895 }),
5896 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5897 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5898 name: "netstack".to_string(),
5899 collection: None,
5900 })),
5901 source_name: Some("dict".to_string()),
5902 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5903 target_name: Some("dict".to_string()),
5904 ..Default::default()
5905 }),
5906 ]);
5907 decl
5908 },
5909 result = Err(ErrorList::new(vec![
5910 Error::invalid_child(DeclType::ExposeService, "source", "netstack"),
5911 Error::invalid_child(DeclType::ExposeProtocol, "source", "netstack"),
5912 Error::invalid_child(DeclType::ExposeDirectory, "source", "netstack"),
5913 Error::invalid_child(DeclType::ExposeRunner, "source", "netstack"),
5914 Error::invalid_child(DeclType::ExposeResolver, "source", "netstack"),
5915 Error::invalid_child(DeclType::ExposeDictionary, "source", "netstack"),
5916 ])),
5917 },
5918 test_validate_exposes_invalid_source_capability => {
5919 input = {
5920 fdecl::Component {
5921 exposes: Some(vec![
5922 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5923 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
5924 name: "this-storage-doesnt-exist".to_string(),
5925 })),
5926 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
5927 target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
5928 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5929 ..Default::default()
5930 }),
5931 ]),
5932 ..new_component_decl()
5933 }
5934 },
5935 result = Err(ErrorList::new(vec![
5936 Error::invalid_capability(DeclType::ExposeProtocol, "source", "this-storage-doesnt-exist"),
5937 ])),
5938 },
5939 test_validate_exposes_duplicate_target => {
5940 input = {
5941 let mut decl = new_component_decl();
5942 decl.exposes = Some(vec![
5943 fdecl::Expose::Service(fdecl::ExposeService {
5944 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
5945 name: "coll".into(),
5946 })),
5947 source_name: Some("netstack".to_string()),
5948 target_name: Some("fuchsia.net.Stack".to_string()),
5949 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5950 ..Default::default()
5951 }),
5952 fdecl::Expose::Service(fdecl::ExposeService {
5953 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
5954 name: "coll2".into(),
5955 })),
5956 source_name: Some("netstack2".to_string()),
5957 target_name: Some("fuchsia.net.Stack".to_string()),
5958 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5959 ..Default::default()
5960 }),
5961 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5962 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5963 source_name: Some("fonts".to_string()),
5964 target_name: Some("fuchsia.fonts.Provider".to_string()),
5965 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5966 ..Default::default()
5967 }),
5968 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5969 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5970 source_name: Some("fonts2".to_string()),
5971 target_name: Some("fuchsia.fonts.Provider".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::Self_(fdecl::SelfRef{})),
5977 source_name: Some("assets".to_string()),
5978 target_name: Some("stuff".to_string()),
5979 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5980 rights: None,
5981 subdir: None,
5982 ..Default::default()
5983 }),
5984 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5985 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5986 source_name: Some("assets2".to_string()),
5987 target_name: Some("stuff".to_string()),
5988 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5989 rights: None,
5990 subdir: None,
5991 ..Default::default()
5992 }),
5993 fdecl::Expose::Runner(fdecl::ExposeRunner {
5994 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5995 source_name: Some("source_elf".to_string()),
5996 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5997 target_name: Some("elf".to_string()),
5998 ..Default::default()
5999 }),
6000 fdecl::Expose::Runner(fdecl::ExposeRunner {
6001 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6002 source_name: Some("source_elf".to_string()),
6003 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6004 target_name: Some("elf".to_string()),
6005 ..Default::default()
6006 }),
6007 fdecl::Expose::Resolver(fdecl::ExposeResolver {
6008 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6009 source_name: Some("source_pkg".to_string()),
6010 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6011 target_name: Some("pkg".to_string()),
6012 ..Default::default()
6013 }),
6014 fdecl::Expose::Resolver(fdecl::ExposeResolver {
6015 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6016 source_name: Some("source_pkg".to_string()),
6017 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6018 target_name: Some("pkg".to_string()),
6019 ..Default::default()
6020 }),
6021 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
6022 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6023 source_name: Some("source_dict".to_string()),
6024 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6025 target_name: Some("dict".to_string()),
6026 ..Default::default()
6027 }),
6028 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
6029 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6030 source_name: Some("source_dict".to_string()),
6031 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6032 target_name: Some("dict".to_string()),
6033 ..Default::default()
6034 }),
6035 ]);
6036 decl.collections = Some(vec![
6037 fdecl::Collection {
6038 name: Some("coll".into()),
6039 durability: Some(fdecl::Durability::Transient),
6040 ..Default::default()
6041 },
6042 fdecl::Collection {
6043 name: Some("coll2".into()),
6044 durability: Some(fdecl::Durability::Transient),
6045 ..Default::default()
6046 },
6047 ]);
6048 decl.capabilities = Some(vec![
6049 fdecl::Capability::Service(fdecl::Service {
6050 name: Some("netstack".to_string()),
6051 source_path: Some("/path".to_string()),
6052 ..Default::default()
6053 }),
6054 fdecl::Capability::Service(fdecl::Service {
6055 name: Some("netstack2".to_string()),
6056 source_path: Some("/path".to_string()),
6057 ..Default::default()
6058 }),
6059 fdecl::Capability::Protocol(fdecl::Protocol {
6060 name: Some("fonts".to_string()),
6061 source_path: Some("/path".to_string()),
6062 ..Default::default()
6063 }),
6064 fdecl::Capability::Protocol(fdecl::Protocol {
6065 name: Some("fonts2".to_string()),
6066 source_path: Some("/path".to_string()),
6067 ..Default::default()
6068 }),
6069 fdecl::Capability::Directory(fdecl::Directory {
6070 name: Some("assets".to_string()),
6071 source_path: Some("/path".to_string()),
6072 rights: Some(fio::Operations::CONNECT),
6073 ..Default::default()
6074 }),
6075 fdecl::Capability::Directory(fdecl::Directory {
6076 name: Some("assets2".to_string()),
6077 source_path: Some("/path".to_string()),
6078 rights: Some(fio::Operations::CONNECT),
6079 ..Default::default()
6080 }),
6081 fdecl::Capability::Runner(fdecl::Runner {
6082 name: Some("source_elf".to_string()),
6083 source_path: Some("/path".to_string()),
6084 ..Default::default()
6085 }),
6086 fdecl::Capability::Resolver(fdecl::Resolver {
6087 name: Some("source_pkg".to_string()),
6088 source_path: Some("/path".to_string()),
6089 ..Default::default()
6090 }),
6091 fdecl::Capability::Dictionary(fdecl::Dictionary {
6092 name: Some("source_dict".to_string()),
6093 ..Default::default()
6094 }),
6095 ]);
6096 decl
6097 },
6098 result = Err(ErrorList::new(vec![
6099 Error::duplicate_field(DeclType::ExposeProtocol, "target_name",
6101 "fuchsia.fonts.Provider"),
6102 Error::duplicate_field(DeclType::ExposeDirectory, "target_name",
6103 "stuff"),
6104 Error::duplicate_field(DeclType::ExposeRunner, "target_name",
6105 "elf"),
6106 Error::duplicate_field(DeclType::ExposeResolver, "target_name", "pkg"),
6107 Error::duplicate_field(DeclType::ExposeDictionary, "target_name", "dict"),
6108 ])),
6109 },
6110 test_validate_exposes_invalid_capability_from_self => {
6111 input = {
6112 let mut decl = new_component_decl();
6113 decl.exposes = Some(vec![
6114 fdecl::Expose::Service(fdecl::ExposeService {
6115 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6116 source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
6117 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6118 target_name: Some("foo".to_string()),
6119 ..Default::default()
6120 }),
6121 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6122 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6123 source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
6124 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6125 target_name: Some("bar".to_string()),
6126 ..Default::default()
6127 }),
6128 fdecl::Expose::Directory(fdecl::ExposeDirectory {
6129 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6130 source_name: Some("dir".to_string()),
6131 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6132 target_name: Some("assets".to_string()),
6133 rights: None,
6134 subdir: None,
6135 ..Default::default()
6136 }),
6137 fdecl::Expose::Runner(fdecl::ExposeRunner {
6138 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6139 source_name: Some("source_elf".to_string()),
6140 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6141 target_name: Some("elf".to_string()),
6142 ..Default::default()
6143 }),
6144 fdecl::Expose::Resolver(fdecl::ExposeResolver {
6145 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6146 source_name: Some("source_pkg".to_string()),
6147 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6148 target_name: Some("pkg".to_string()),
6149 ..Default::default()
6150 }),
6151 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
6152 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6153 source_name: Some("source_dict".to_string()),
6154 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6155 target_name: Some("dict".to_string()),
6156 ..Default::default()
6157 }),
6158 fdecl::Expose::Config(fdecl::ExposeConfiguration {
6159 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6160 source_name: Some("source_config".to_string()),
6161 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6162 target_name: Some("config".to_string()),
6163 ..Default::default()
6164 }),
6165 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6166 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6167 source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
6168 source_dictionary: Some("dict/inner".to_string()),
6169 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6170 target_name: Some("baz".to_string()),
6171 ..Default::default()
6172 }),
6173 ]);
6174 decl
6175 },
6176 result = Err(ErrorList::new(vec![
6177 Error::invalid_capability(
6178 DeclType::ExposeService,
6179 "source",
6180 "fuchsia.some.library.SomeProtocol"),
6181 Error::invalid_capability(
6182 DeclType::ExposeProtocol,
6183 "source",
6184 "fuchsia.some.library.SomeProtocol"),
6185 Error::invalid_capability(DeclType::ExposeDirectory, "source", "dir"),
6186 Error::invalid_capability(DeclType::ExposeRunner, "source", "source_elf"),
6187 Error::invalid_capability(DeclType::ExposeResolver, "source", "source_pkg"),
6188 Error::invalid_capability(DeclType::ExposeDictionary, "source", "source_dict"),
6189 Error::invalid_capability(DeclType::ExposeConfig, "source", "source_config"),
6190 Error::invalid_capability(DeclType::ExposeProtocol, "source", "dict"),
6191 ])),
6192 },
6193
6194 test_validate_exposes_availability_service => {
6195 input = {
6196 let mut decl = generate_expose_different_source_and_availability_decl(
6197 |source, availability, target_name|
6198 fdecl::Expose::Service(fdecl::ExposeService {
6199 source: Some(source),
6200 source_name: Some("fuchsia.examples.Echo".to_string()),
6201 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6202 target_name: Some(target_name.to_string()),
6203 availability: Some(availability),
6204 ..Default::default()
6205 })
6206 );
6207 decl.capabilities = Some(vec![
6208 fdecl::Capability::Service(fdecl::Service {
6209 name: Some("fuchsia.examples.Echo".to_string()),
6210 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
6211 ..Default::default()
6212 }),
6213 ]);
6214 decl
6215 },
6216 result = {
6217 Err(ErrorList::new(vec![
6218 Error::availability_must_be_optional(
6219 DeclType::ExposeService,
6220 "availability",
6221 Some(&"fuchsia.examples.Echo".to_string()),
6222 ),
6223 Error::availability_must_be_optional(
6224 DeclType::ExposeService,
6225 "availability",
6226 Some(&"fuchsia.examples.Echo".to_string()),
6227 ),
6228 ]))
6229 },
6230 },
6231 test_validate_exposes_availability_protocol => {
6232 input = {
6233 let mut decl = generate_expose_different_source_and_availability_decl(
6234 |source, availability, target_name|
6235 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6236 source: Some(source),
6237 source_name: Some("fuchsia.examples.Echo".to_string()),
6238 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6239 target_name: Some(target_name.to_string()),
6240 availability: Some(availability),
6241 ..Default::default()
6242 })
6243 );
6244 decl.capabilities = Some(vec![
6245 fdecl::Capability::Protocol(fdecl::Protocol {
6246 name: Some("fuchsia.examples.Echo".to_string()),
6247 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
6248 ..Default::default()
6249 }),
6250 ]);
6251 decl
6252 },
6253 result = {
6254 Err(ErrorList::new(vec![
6255 Error::availability_must_be_optional(
6256 DeclType::ExposeProtocol,
6257 "availability",
6258 Some(&"fuchsia.examples.Echo".to_string()),
6259 ),
6260 Error::availability_must_be_optional(
6261 DeclType::ExposeProtocol,
6262 "availability",
6263 Some(&"fuchsia.examples.Echo".to_string()),
6264 ),
6265 ]))
6266 },
6267 },
6268 test_validate_exposes_availability_directory => {
6269 input = {
6270 let mut decl = generate_expose_different_source_and_availability_decl(
6271 |source, availability, target_name|
6272 fdecl::Expose::Directory(fdecl::ExposeDirectory {
6273 source: Some(source),
6274 source_name: Some("fuchsia.examples.Echo".to_string()),
6275 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6276 target_name: Some(target_name.to_string()),
6277 availability: Some(availability),
6278 ..Default::default()
6279 })
6280 );
6281 decl.capabilities = Some(vec![
6282 fdecl::Capability::Directory(fdecl::Directory {
6283 name: Some("fuchsia.examples.Echo".to_string()),
6284 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
6285 rights: Some(fio::Operations::READ_BYTES),
6286 ..Default::default()
6287 }),
6288 ]);
6289 decl
6290 },
6291 result = {
6292 Err(ErrorList::new(vec![
6293 Error::availability_must_be_optional(
6294 DeclType::ExposeDirectory,
6295 "availability",
6296 Some(&"fuchsia.examples.Echo".to_string()),
6297 ),
6298 Error::availability_must_be_optional(
6299 DeclType::ExposeDirectory,
6300 "availability",
6301 Some(&"fuchsia.examples.Echo".to_string()),
6302 ),
6303 ]))
6304 },
6305 },
6306
6307 test_validate_offers_empty => {
6309 input = {
6310 let mut decl = new_component_decl();
6311 decl.offers = Some(vec![
6312 fdecl::Offer::Service(fdecl::OfferService {
6313 source: None,
6314 source_name: None,
6315 target: None,
6316 target_name: None,
6317 ..Default::default()
6318 }),
6319 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6320 source: None,
6321 source_name: None,
6322 target: None,
6323 target_name: None,
6324 dependency_type: None,
6325 ..Default::default()
6326 }),
6327 fdecl::Offer::Directory(fdecl::OfferDirectory {
6328 source: None,
6329 source_name: None,
6330 target: None,
6331 target_name: None,
6332 rights: None,
6333 subdir: None,
6334 dependency_type: None,
6335 ..Default::default()
6336 }),
6337 fdecl::Offer::Storage(fdecl::OfferStorage {
6338 source_name: None,
6339 source: None,
6340 target: None,
6341 target_name: None,
6342 ..Default::default()
6343 }),
6344 fdecl::Offer::Runner(fdecl::OfferRunner {
6345 source: None,
6346 source_name: None,
6347 target: None,
6348 target_name: None,
6349 ..Default::default()
6350 }),
6351 fdecl::Offer::Resolver(fdecl::OfferResolver {
6352 ..Default::default()
6353 }),
6354 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6355 ..Default::default()
6356 }),
6357 ]);
6358 decl
6359 },
6360 result = Err(ErrorList::new(vec![
6363 Error::missing_field(DeclType::OfferService, "source"),
6364 Error::missing_field(DeclType::OfferService, "source_name"),
6365 Error::missing_field(DeclType::OfferService, "target"),
6366 Error::missing_field(DeclType::OfferService, "target_name"),
6367 Error::missing_field(DeclType::OfferProtocol, "source"),
6369 Error::missing_field(DeclType::OfferProtocol, "source_name"),
6370 Error::missing_field(DeclType::OfferProtocol, "target"),
6371 Error::missing_field(DeclType::OfferProtocol, "target_name"),
6372 Error::missing_field(DeclType::OfferProtocol, "dependency_type"),
6373 Error::missing_field(DeclType::OfferDirectory, "source"),
6375 Error::missing_field(DeclType::OfferDirectory, "source_name"),
6376 Error::missing_field(DeclType::OfferDirectory, "target"),
6377 Error::missing_field(DeclType::OfferDirectory, "target_name"),
6378 Error::missing_field(DeclType::OfferDirectory, "dependency_type"),
6379 Error::missing_field(DeclType::OfferStorage, "source"),
6381 Error::missing_field(DeclType::OfferStorage, "source_name"),
6382 Error::missing_field(DeclType::OfferStorage, "target"),
6383 Error::missing_field(DeclType::OfferStorage, "target_name"),
6384 Error::missing_field(DeclType::OfferRunner, "source"),
6386 Error::missing_field(DeclType::OfferRunner, "source_name"),
6387 Error::missing_field(DeclType::OfferRunner, "target"),
6388 Error::missing_field(DeclType::OfferRunner, "target_name"),
6389 Error::missing_field(DeclType::OfferResolver, "source"),
6391 Error::missing_field(DeclType::OfferResolver, "source_name"),
6392 Error::missing_field(DeclType::OfferResolver, "target"),
6393 Error::missing_field(DeclType::OfferResolver, "target_name"),
6394 Error::missing_field(DeclType::OfferDictionary, "source"),
6395 Error::missing_field(DeclType::OfferDictionary, "source_name"),
6396 Error::missing_field(DeclType::OfferDictionary, "target"),
6397 Error::missing_field(DeclType::OfferDictionary, "target_name"),
6398 Error::missing_field(DeclType::OfferDictionary, "dependency_type"),
6399 ])),
6400 },
6401 test_validate_offers_long_identifiers => {
6402 input = {
6403 let mut decl = new_component_decl();
6404 decl.offers = Some(vec![
6405 fdecl::Offer::Service(fdecl::OfferService {
6406 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6407 name: "a".repeat(256),
6408 collection: None,
6409 })),
6410 source_name: Some(format!("{}", "a".repeat(256))),
6411 target: Some(fdecl::Ref::Child(
6412 fdecl::ChildRef {
6413 name: "b".repeat(256),
6414 collection: None,
6415 }
6416 )),
6417 target_name: Some(format!("{}", "b".repeat(256))),
6418 ..Default::default()
6419 }),
6420 fdecl::Offer::Service(fdecl::OfferService {
6421 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6422 source_name: Some("a".to_string()),
6423 target: Some(fdecl::Ref::Collection(
6424 fdecl::CollectionRef {
6425 name: "b".repeat(256),
6426 }
6427 )),
6428 target_name: Some(format!("{}", "b".repeat(256))),
6429 ..Default::default()
6430 }),
6431 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6432 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6433 name: "a".repeat(256),
6434 collection: None,
6435 })),
6436 source_name: Some(format!("{}", "a".repeat(256))),
6437 target: Some(fdecl::Ref::Child(
6438 fdecl::ChildRef {
6439 name: "b".repeat(256),
6440 collection: None,
6441 }
6442 )),
6443 target_name: Some(format!("{}", "b".repeat(256))),
6444 dependency_type: Some(fdecl::DependencyType::Strong),
6445 ..Default::default()
6446 }),
6447 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6448 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6449 source_name: Some("a".to_string()),
6450 target: Some(fdecl::Ref::Collection(
6451 fdecl::CollectionRef {
6452 name: "b".repeat(256),
6453 }
6454 )),
6455 target_name: Some(format!("{}", "b".repeat(256))),
6456 dependency_type: Some(fdecl::DependencyType::Weak),
6457 ..Default::default()
6458 }),
6459 fdecl::Offer::Directory(fdecl::OfferDirectory {
6460 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6461 name: "a".repeat(256),
6462 collection: None,
6463 })),
6464 source_name: Some(format!("{}", "a".repeat(256))),
6465 target: Some(fdecl::Ref::Child(
6466 fdecl::ChildRef {
6467 name: "b".repeat(256),
6468 collection: None,
6469 }
6470 )),
6471 target_name: Some(format!("{}", "b".repeat(256))),
6472 rights: Some(fio::Operations::CONNECT),
6473 subdir: None,
6474 dependency_type: Some(fdecl::DependencyType::Strong),
6475 ..Default::default()
6476 }),
6477 fdecl::Offer::Directory(fdecl::OfferDirectory {
6478 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6479 source_name: Some("a".to_string()),
6480 target: Some(fdecl::Ref::Collection(
6481 fdecl::CollectionRef {
6482 name: "b".repeat(256),
6483 }
6484 )),
6485 target_name: Some(format!("{}", "b".repeat(256))),
6486 rights: Some(fio::Operations::CONNECT),
6487 subdir: None,
6488 dependency_type: Some(fdecl::DependencyType::Weak),
6489 ..Default::default()
6490 }),
6491 fdecl::Offer::Storage(fdecl::OfferStorage {
6492 source_name: Some("data".to_string()),
6493 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6494 target: Some(fdecl::Ref::Child(
6495 fdecl::ChildRef {
6496 name: "b".repeat(256),
6497 collection: None,
6498 }
6499 )),
6500 target_name: Some("data".to_string()),
6501 ..Default::default()
6502 }),
6503 fdecl::Offer::Storage(fdecl::OfferStorage {
6504 source_name: Some("data".to_string()),
6505 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6506 target: Some(fdecl::Ref::Collection(
6507 fdecl::CollectionRef { name: "b".repeat(256) }
6508 )),
6509 target_name: Some("data".to_string()),
6510 ..Default::default()
6511 }),
6512 fdecl::Offer::Runner(fdecl::OfferRunner {
6513 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6514 name: "a".repeat(256),
6515 collection: None,
6516 })),
6517 source_name: Some("b".repeat(256)),
6518 target: Some(fdecl::Ref::Collection(
6519 fdecl::CollectionRef {
6520 name: "c".repeat(256),
6521 }
6522 )),
6523 target_name: Some("d".repeat(256)),
6524 ..Default::default()
6525 }),
6526 fdecl::Offer::Resolver(fdecl::OfferResolver {
6527 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6528 name: "a".repeat(256),
6529 collection: None,
6530 })),
6531 source_name: Some("b".repeat(256)),
6532 target: Some(fdecl::Ref::Collection(
6533 fdecl::CollectionRef {
6534 name: "c".repeat(256),
6535 }
6536 )),
6537 target_name: Some("d".repeat(256)),
6538 ..Default::default()
6539 }),
6540 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6541 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6542 name: "a".repeat(256),
6543 collection: None,
6544 })),
6545 source_name: Some("b".repeat(256)),
6546 target: Some(fdecl::Ref::Collection(
6547 fdecl::CollectionRef {
6548 name: "c".repeat(256),
6549 }
6550 )),
6551 target_name: Some("d".repeat(256)),
6552 dependency_type: Some(fdecl::DependencyType::Strong),
6553 ..Default::default()
6554 }),
6555 ]);
6556 decl
6557 },
6558 result = Err(ErrorList::new(vec![
6559 Error::field_too_long(DeclType::OfferService, "source.child.name"),
6560 Error::field_too_long(DeclType::OfferService, "source_name"),
6561 Error::field_too_long(DeclType::OfferService, "target.child.name"),
6562 Error::field_too_long(DeclType::OfferService, "target_name"),
6563 Error::field_too_long(DeclType::OfferService, "target.collection.name"),
6564 Error::field_too_long(DeclType::OfferService, "target_name"),
6565 Error::field_too_long(DeclType::OfferProtocol, "source.child.name"),
6566 Error::field_too_long(DeclType::OfferProtocol, "source_name"),
6567 Error::field_too_long(DeclType::OfferProtocol, "target.child.name"),
6568 Error::field_too_long(DeclType::OfferProtocol, "target_name"),
6569 Error::field_too_long(DeclType::OfferProtocol, "target.collection.name"),
6570 Error::field_too_long(DeclType::OfferProtocol, "target_name"),
6571 Error::field_too_long(DeclType::OfferDirectory, "source.child.name"),
6572 Error::field_too_long(DeclType::OfferDirectory, "source_name"),
6573 Error::field_too_long(DeclType::OfferDirectory, "target.child.name"),
6574 Error::field_too_long(DeclType::OfferDirectory, "target_name"),
6575 Error::field_too_long(DeclType::OfferDirectory, "target.collection.name"),
6576 Error::field_too_long(DeclType::OfferDirectory, "target_name"),
6577 Error::field_too_long(DeclType::OfferStorage, "target.child.name"),
6578 Error::field_too_long(DeclType::OfferStorage, "target.collection.name"),
6579 Error::field_too_long(DeclType::OfferRunner, "source.child.name"),
6580 Error::field_too_long(DeclType::OfferRunner, "source_name"),
6581 Error::field_too_long(DeclType::OfferRunner, "target.collection.name"),
6582 Error::field_too_long(DeclType::OfferRunner, "target_name"),
6583 Error::field_too_long(DeclType::OfferResolver, "source.child.name"),
6584 Error::field_too_long(DeclType::OfferResolver, "source_name"),
6585 Error::field_too_long(DeclType::OfferResolver, "target.collection.name"),
6586 Error::field_too_long(DeclType::OfferResolver, "target_name"),
6587 Error::field_too_long(DeclType::OfferDictionary, "source.child.name"),
6588 Error::field_too_long(DeclType::OfferDictionary, "source_name"),
6589 Error::field_too_long(DeclType::OfferDictionary, "target.collection.name"),
6590 Error::field_too_long(DeclType::OfferDictionary, "target_name"),
6591 ])),
6592 },
6593 test_validate_offers_extraneous => {
6594 input = {
6595 let mut decl = new_component_decl();
6596 decl.offers = Some(vec![
6597 fdecl::Offer::Service(fdecl::OfferService {
6598 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6599 name: "logger".to_string(),
6600 collection: Some("modular".to_string()),
6601 })),
6602 source_name: Some("fuchsia.logger.Log".to_string()),
6603 target: Some(fdecl::Ref::Child(
6604 fdecl::ChildRef {
6605 name: "netstack".to_string(),
6606 collection: Some("modular".to_string()),
6607 }
6608 )),
6609 target_name: Some("fuchsia.logger.Log".to_string()),
6610 ..Default::default()
6611 }),
6612 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6613 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6614 name: "logger".to_string(),
6615 collection: Some("modular".to_string()),
6616 })),
6617 source_name: Some("fuchsia.logger.Log".to_string()),
6618 target: Some(fdecl::Ref::Child(
6619 fdecl::ChildRef {
6620 name: "netstack".to_string(),
6621 collection: Some("modular".to_string()),
6622 }
6623 )),
6624 target_name: Some("fuchsia.logger.Log".to_string()),
6625 dependency_type: Some(fdecl::DependencyType::Strong),
6626 ..Default::default()
6627 }),
6628 fdecl::Offer::Directory(fdecl::OfferDirectory {
6629 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6630 name: "logger".to_string(),
6631 collection: Some("modular".to_string()),
6632 })),
6633 source_name: Some("assets".to_string()),
6634 target: Some(fdecl::Ref::Child(
6635 fdecl::ChildRef {
6636 name: "netstack".to_string(),
6637 collection: Some("modular".to_string()),
6638 }
6639 )),
6640 target_name: Some("assets".to_string()),
6641 rights: Some(fio::Operations::CONNECT),
6642 subdir: None,
6643 dependency_type: Some(fdecl::DependencyType::Weak),
6644 ..Default::default()
6645 }),
6646 fdecl::Offer::Storage(fdecl::OfferStorage {
6647 source_name: Some("data".to_string()),
6648 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{ })),
6649 target: Some(fdecl::Ref::Child(
6650 fdecl::ChildRef {
6651 name: "netstack".to_string(),
6652 collection: Some("modular".to_string()),
6653 }
6654 )),
6655 target_name: Some("data".to_string()),
6656 ..Default::default()
6657 }),
6658 fdecl::Offer::Runner(fdecl::OfferRunner {
6659 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6660 name: "logger".to_string(),
6661 collection: Some("modular".to_string()),
6662 })),
6663 source_name: Some("elf".to_string()),
6664 target: Some(fdecl::Ref::Child(
6665 fdecl::ChildRef {
6666 name: "netstack".to_string(),
6667 collection: Some("modular".to_string()),
6668 }
6669 )),
6670 target_name: Some("elf".to_string()),
6671 ..Default::default()
6672 }),
6673 fdecl::Offer::Resolver(fdecl::OfferResolver {
6674 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6675 name: "logger".to_string(),
6676 collection: Some("modular".to_string()),
6677 })),
6678 source_name: Some("pkg".to_string()),
6679 target: Some(fdecl::Ref::Child(
6680 fdecl::ChildRef {
6681 name: "netstack".to_string(),
6682 collection: Some("modular".to_string()),
6683 }
6684 )),
6685 target_name: Some("pkg".to_string()),
6686 ..Default::default()
6687 }),
6688 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6689 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6690 name: "logger".to_string(),
6691 collection: Some("modular".to_string()),
6692 })),
6693 source_name: Some("dict".to_string()),
6694 target: Some(fdecl::Ref::Child(
6695 fdecl::ChildRef {
6696 name: "netstack".to_string(),
6697 collection: Some("modular".to_string()),
6698 }
6699 )),
6700 target_name: Some("dict".to_string()),
6701 dependency_type: Some(fdecl::DependencyType::Strong),
6702 ..Default::default()
6703 }),
6704 ]);
6705 decl.capabilities = Some(vec![
6706 fdecl::Capability::Protocol(fdecl::Protocol {
6707 name: Some("fuchsia.logger.Log".to_string()),
6708 source_path: Some("/svc/logger".to_string()),
6709 ..Default::default()
6710 }),
6711 fdecl::Capability::Directory(fdecl::Directory {
6712 name: Some("assets".to_string()),
6713 source_path: Some("/data/assets".to_string()),
6714 rights: Some(fio::Operations::CONNECT),
6715 ..Default::default()
6716 }),
6717 ]);
6718 decl
6719 },
6720 result = Err(ErrorList::new(vec![
6721 Error::extraneous_field(DeclType::OfferService, "source.child.collection"),
6722 Error::extraneous_field(DeclType::OfferService, "target.child.collection"),
6723 Error::extraneous_field(DeclType::OfferProtocol, "source.child.collection"),
6724 Error::extraneous_field(DeclType::OfferProtocol, "target.child.collection"),
6725 Error::extraneous_field(DeclType::OfferDirectory, "source.child.collection"),
6726 Error::extraneous_field(DeclType::OfferDirectory, "target.child.collection"),
6727 Error::extraneous_field(DeclType::OfferStorage, "target.child.collection"),
6728 Error::extraneous_field(DeclType::OfferRunner, "source.child.collection"),
6729 Error::extraneous_field(DeclType::OfferRunner, "target.child.collection"),
6730 Error::extraneous_field(DeclType::OfferResolver, "source.child.collection"),
6731 Error::extraneous_field(DeclType::OfferResolver, "target.child.collection"),
6732 Error::extraneous_field(DeclType::OfferDictionary, "source.child.collection"),
6733 Error::extraneous_field(DeclType::OfferDictionary, "target.child.collection"),
6734 ])),
6735 },
6736 test_validate_offers_invalid_filtered_service_fields => {
6737 input = {
6738 let mut decl = new_component_decl();
6739 decl.offers = Some(vec![
6740 fdecl::Offer::Service(fdecl::OfferService {
6741 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6742 source_name: Some("fuchsia.logger.Log".to_string()),
6743 target: Some(fdecl::Ref::Child(
6744 fdecl::ChildRef {
6745 name: "logger".to_string(),
6746 collection: None,
6747 }
6748 )),
6749 target_name: Some("fuchsia.logger.Log".to_string()),
6750 source_instance_filter: Some(vec![]),
6751 ..Default::default()
6752 }),
6753 fdecl::Offer::Service(fdecl::OfferService {
6754 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6755 source_name: Some("fuchsia.logger.Log".to_string()),
6756 target: Some(fdecl::Ref::Child(
6757 fdecl::ChildRef {
6758 name: "logger".to_string(),
6759 collection: None,
6760 }
6761 )),
6762 target_name: Some("fuchsia.logger.Log2".to_string()),
6763 source_instance_filter: Some(vec!["^badname".to_string()]),
6764 ..Default::default()
6765 }),
6766 fdecl::Offer::Service(fdecl::OfferService {
6767 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6768 source_name: Some("fuchsia.logger.Log".to_string()),
6769 target: Some(fdecl::Ref::Child(
6770 fdecl::ChildRef {
6771 name: "logger".to_string(),
6772 collection: None,
6773 }
6774 )),
6775 target_name: Some("fuchsia.logger.Log1".to_string()),
6776 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()}]),
6777 ..Default::default()
6778 }),
6779 fdecl::Offer::Service(fdecl::OfferService {
6780 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6781 source_name: Some("fuchsia.logger.Log".to_string()),
6782 target: Some(fdecl::Ref::Child(
6783 fdecl::ChildRef {
6784 name: "logger".to_string(),
6785 collection: None,
6786 }
6787 )),
6788 target_name: Some("fuchsia.logger.Log3".to_string()),
6789 renamed_instances: Some(vec![
6790 fdecl::NameMapping {
6791 source_name: "^badname".to_string(),
6792 target_name: "^badname".to_string(),
6793 }
6794 ]),
6795 ..Default::default()
6796 })
6797 ]);
6798 decl.children = Some(vec![
6799 fdecl::Child {
6800 name: Some("logger".to_string()),
6801 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
6802 startup: Some(fdecl::StartupMode::Lazy),
6803 on_terminate: None,
6804 environment: None,
6805 ..Default::default()
6806 },
6807 ]);
6808 decl
6809 },
6810 result = Err(ErrorList::new(vec![
6811 Error::invalid_field(DeclType::OfferService, "source_instance_filter"),
6812 Error::invalid_field(DeclType::OfferService, "source_instance_filter"),
6813 Error::invalid_field(DeclType::OfferService, "renamed_instances"),
6814 Error::invalid_field(DeclType::OfferService, "renamed_instances.source_name"),
6815 Error::invalid_field(DeclType::OfferService, "renamed_instances.target_name"),
6816 ])),
6817 },
6818 test_validate_offers_invalid_identifiers => {
6819 input = {
6820 let mut decl = new_component_decl();
6821 decl.offers = Some(vec![
6822 fdecl::Offer::Service(fdecl::OfferService {
6823 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6824 name: "^bad".to_string(),
6825 collection: None,
6826 })),
6827 source_name: Some("foo/".to_string()),
6828 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6829 name: "%bad".to_string(),
6830 collection: None,
6831 })),
6832 target_name: Some("/".to_string()),
6833 ..Default::default()
6834 }),
6835 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6836 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6837 name: "^bad".to_string(),
6838 collection: None,
6839 })),
6840 source_name: Some("foo/".to_string()),
6841 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6842 name: "%bad".to_string(),
6843 collection: None,
6844 })),
6845 target_name: Some("/".to_string()),
6846 dependency_type: Some(fdecl::DependencyType::Strong),
6847 ..Default::default()
6848 }),
6849 fdecl::Offer::Directory(fdecl::OfferDirectory {
6850 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6851 name: "^bad".to_string(),
6852 collection: None,
6853 })),
6854 source_name: Some("foo/".to_string()),
6855 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6856 name: "%bad".to_string(),
6857 collection: None,
6858 })),
6859 target_name: Some("/".to_string()),
6860 rights: Some(fio::Operations::CONNECT),
6861 subdir: Some("/foo".to_string()),
6862 dependency_type: Some(fdecl::DependencyType::Strong),
6863 ..Default::default()
6864 }),
6865 fdecl::Offer::Runner(fdecl::OfferRunner {
6866 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6867 name: "^bad".to_string(),
6868 collection: None,
6869 })),
6870 source_name: Some("/path".to_string()),
6871 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6872 name: "%bad".to_string(),
6873 collection: None,
6874 })),
6875 target_name: Some("elf!".to_string()),
6876 ..Default::default()
6877 }),
6878 fdecl::Offer::Resolver(fdecl::OfferResolver {
6879 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6880 name: "^bad".to_string(),
6881 collection: None,
6882 })),
6883 source_name: Some("/path".to_string()),
6884 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6885 name: "%bad".to_string(),
6886 collection: None,
6887 })),
6888 target_name: Some("pkg!".to_string()),
6889 ..Default::default()
6890 }),
6891 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6892 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6893 name: "^bad".to_string(),
6894 collection: None,
6895 })),
6896 source_name: Some("/path".to_string()),
6897 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6898 name: "%bad".to_string(),
6899 collection: None,
6900 })),
6901 target_name: Some("pkg!".to_string()),
6902 dependency_type: Some(fdecl::DependencyType::Strong),
6903 ..Default::default()
6904 }),
6905 ]);
6906 decl
6907 },
6908 result = Err(ErrorList::new(vec![
6909 Error::invalid_field(DeclType::OfferService, "source.child.name"),
6910 Error::invalid_field(DeclType::OfferService, "source_name"),
6911 Error::invalid_field(DeclType::OfferService, "target.child.name"),
6912 Error::invalid_field(DeclType::OfferService, "target_name"),
6913 Error::invalid_field(DeclType::OfferProtocol, "source.child.name"),
6914 Error::invalid_field(DeclType::OfferProtocol, "source_name"),
6915 Error::invalid_field(DeclType::OfferProtocol, "target.child.name"),
6916 Error::invalid_field(DeclType::OfferProtocol, "target_name"),
6917 Error::invalid_field(DeclType::OfferDirectory, "source.child.name"),
6918 Error::invalid_field(DeclType::OfferDirectory, "source_name"),
6919 Error::invalid_field(DeclType::OfferDirectory, "target.child.name"),
6920 Error::invalid_field(DeclType::OfferDirectory, "target_name"),
6921 Error::invalid_field(DeclType::OfferDirectory, "subdir"),
6922 Error::invalid_field(DeclType::OfferRunner, "source.child.name"),
6923 Error::invalid_field(DeclType::OfferRunner, "source_name"),
6924 Error::invalid_field(DeclType::OfferRunner, "target.child.name"),
6925 Error::invalid_field(DeclType::OfferRunner, "target_name"),
6926 Error::invalid_field(DeclType::OfferResolver, "source.child.name"),
6927 Error::invalid_field(DeclType::OfferResolver, "source_name"),
6928 Error::invalid_field(DeclType::OfferResolver, "target.child.name"),
6929 Error::invalid_field(DeclType::OfferResolver, "target_name"),
6930 Error::invalid_field(DeclType::OfferDictionary, "source.child.name"),
6931 Error::invalid_field(DeclType::OfferDictionary, "source_name"),
6932 Error::invalid_field(DeclType::OfferDictionary, "target.child.name"),
6933 Error::invalid_field(DeclType::OfferDictionary, "target_name"),
6934 ])),
6935 },
6936 test_validate_offers_target_equals_source => {
6937 input = {
6938 let mut decl = new_component_decl();
6939 decl.offers = Some(vec![
6940 fdecl::Offer::Service(fdecl::OfferService {
6941 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6942 name: "logger".to_string(),
6943 collection: None,
6944 })),
6945 source_name: Some("logger".to_string()),
6946 target: Some(fdecl::Ref::Child(
6947 fdecl::ChildRef {
6948 name: "logger".to_string(),
6949 collection: None,
6950 }
6951 )),
6952 target_name: Some("logger".to_string()),
6953 ..Default::default()
6954 }),
6955 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6956 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6957 name: "logger".to_string(),
6958 collection: None,
6959 })),
6960 source_name: Some("legacy_logger".to_string()),
6961 target: Some(fdecl::Ref::Child(
6962 fdecl::ChildRef {
6963 name: "logger".to_string(),
6964 collection: None,
6965 }
6966 )),
6967 target_name: Some("weak_legacy_logger".to_string()),
6968 dependency_type: Some(fdecl::DependencyType::Weak),
6969 ..Default::default()
6970 }),
6971 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6972 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6973 name: "logger".to_string(),
6974 collection: None,
6975 })),
6976 source_name: Some("legacy_logger".to_string()),
6977 target: Some(fdecl::Ref::Child(
6978 fdecl::ChildRef {
6979 name: "logger".to_string(),
6980 collection: None,
6981 }
6982 )),
6983 target_name: Some("strong_legacy_logger".to_string()),
6984 dependency_type: Some(fdecl::DependencyType::Strong),
6985 ..Default::default()
6986 }),
6987 fdecl::Offer::Directory(fdecl::OfferDirectory {
6988 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6989 name: "logger".to_string(),
6990 collection: None,
6991 })),
6992 source_name: Some("assets".to_string()),
6993 target: Some(fdecl::Ref::Child(
6994 fdecl::ChildRef {
6995 name: "logger".to_string(),
6996 collection: None,
6997 }
6998 )),
6999 target_name: Some("assets".to_string()),
7000 rights: Some(fio::Operations::CONNECT),
7001 subdir: None,
7002 dependency_type: Some(fdecl::DependencyType::Strong),
7003 ..Default::default()
7004 }),
7005 fdecl::Offer::Runner(fdecl::OfferRunner {
7006 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7007 name: "logger".to_string(),
7008 collection: None,
7009 })),
7010 source_name: Some("web".to_string()),
7011 target: Some(fdecl::Ref::Child(
7012 fdecl::ChildRef {
7013 name: "logger".to_string(),
7014 collection: None,
7015 }
7016 )),
7017 target_name: Some("web".to_string()),
7018 ..Default::default()
7019 }),
7020 fdecl::Offer::Resolver(fdecl::OfferResolver {
7021 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7022 name: "logger".to_string(),
7023 collection: None,
7024 })),
7025 source_name: Some("pkg".to_string()),
7026 target: Some(fdecl::Ref::Child(
7027 fdecl::ChildRef {
7028 name: "logger".to_string(),
7029 collection: None,
7030 }
7031 )),
7032 target_name: Some("pkg".to_string()),
7033 ..Default::default()
7034 }),
7035 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7036 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7037 name: "logger".to_string(),
7038 collection: None,
7039 })),
7040 source_name: Some("dict".to_string()),
7041 target: Some(fdecl::Ref::Child(
7042 fdecl::ChildRef {
7043 name: "logger".to_string(),
7044 collection: None,
7045 }
7046 )),
7047 target_name: Some("dict".to_string()),
7048 dependency_type: Some(fdecl::DependencyType::Strong),
7049 ..Default::default()
7050 }),
7051 ]);
7052 decl.children = Some(vec![fdecl::Child{
7053 name: Some("logger".to_string()),
7054 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
7055 startup: Some(fdecl::StartupMode::Lazy),
7056 on_terminate: None,
7057 environment: None,
7058 ..Default::default()
7059 }]);
7060 decl
7061 },
7062 result = Err(ErrorList::new(vec![
7063 Error::dependency_cycle("{{child logger -> child logger}}".to_string()),
7064 ])),
7065 },
7066 test_validate_offers_storage_target_equals_source => {
7067 input = fdecl::Component {
7068 offers: Some(vec![
7069 fdecl::Offer::Storage(fdecl::OfferStorage {
7070 source_name: Some("data".to_string()),
7071 source: Some(fdecl::Ref::Self_(fdecl::SelfRef { })),
7072 target: Some(fdecl::Ref::Child(
7073 fdecl::ChildRef {
7074 name: "logger".to_string(),
7075 collection: None,
7076 }
7077 )),
7078 target_name: Some("data".to_string()),
7079 ..Default::default()
7080 })
7081 ]),
7082 capabilities: Some(vec![
7083 fdecl::Capability::Storage(fdecl::Storage {
7084 name: Some("data".to_string()),
7085 backing_dir: Some("minfs".to_string()),
7086 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7087 name: "logger".to_string(),
7088 collection: None,
7089 })),
7090 subdir: None,
7091 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
7092 ..Default::default()
7093 }),
7094 ]),
7095 children: Some(vec![
7096 fdecl::Child {
7097 name: Some("logger".to_string()),
7098 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
7099 startup: Some(fdecl::StartupMode::Lazy),
7100 on_terminate: None,
7101 environment: None,
7102 ..Default::default()
7103 },
7104 ]),
7105 ..new_component_decl()
7106 },
7107 result = Err(ErrorList::new(vec![
7108 Error::dependency_cycle("{{child logger -> capability data -> child logger}}".to_string()),
7109 ])),
7110 },
7111 test_validate_offers_invalid_child => {
7112 input = {
7113 let mut decl = new_component_decl();
7114 decl.offers = Some(vec![
7115 fdecl::Offer::Service(fdecl::OfferService {
7116 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7117 name: "logger".to_string(),
7118 collection: None,
7119 })),
7120 source_name: Some("fuchsia.logger.Log".to_string()),
7121 target: Some(fdecl::Ref::Child(
7122 fdecl::ChildRef {
7123 name: "netstack".to_string(),
7124 collection: None,
7125 }
7126 )),
7127 target_name: Some("fuchsia.logger.Log".to_string()),
7128 ..Default::default()
7129 }),
7130 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7131 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7132 name: "logger".to_string(),
7133 collection: None,
7134 })),
7135 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
7136 target: Some(fdecl::Ref::Child(
7137 fdecl::ChildRef {
7138 name: "netstack".to_string(),
7139 collection: None,
7140 }
7141 )),
7142 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7143 dependency_type: Some(fdecl::DependencyType::Strong),
7144 ..Default::default()
7145 }),
7146 fdecl::Offer::Directory(fdecl::OfferDirectory {
7147 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7148 name: "logger".to_string(),
7149 collection: None,
7150 })),
7151 source_name: Some("assets".to_string()),
7152 target: Some(fdecl::Ref::Collection(
7153 fdecl::CollectionRef { name: "modular".to_string() }
7154 )),
7155 target_name: Some("assets".to_string()),
7156 rights: Some(fio::Operations::CONNECT),
7157 subdir: None,
7158 dependency_type: Some(fdecl::DependencyType::Weak),
7159 ..Default::default()
7160 }),
7161 ]);
7162 decl.capabilities = Some(vec![
7163 fdecl::Capability::Storage(fdecl::Storage {
7164 name: Some("memfs".to_string()),
7165 backing_dir: Some("memfs".to_string()),
7166 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7167 name: "logger".to_string(),
7168 collection: None,
7169 })),
7170 subdir: None,
7171 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
7172 ..Default::default()
7173 }),
7174 ]);
7175 decl.children = Some(vec![
7176 fdecl::Child {
7177 name: Some("netstack".to_string()),
7178 url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
7179 startup: Some(fdecl::StartupMode::Lazy),
7180 on_terminate: None,
7181 environment: None,
7182 ..Default::default()
7183 },
7184 ]);
7185 decl.collections = Some(vec![
7186 fdecl::Collection {
7187 name: Some("modular".to_string()),
7188 durability: Some(fdecl::Durability::Transient),
7189 environment: None,
7190 allowed_offers: Some(fdecl::AllowedOffers::StaticAndDynamic),
7191 allow_long_names: None,
7192 ..Default::default()
7193 },
7194 ]);
7195 decl
7196 },
7197 result = Err(ErrorList::new(vec![
7198 Error::invalid_child(DeclType::Storage, "source", "logger"),
7199 Error::invalid_child(DeclType::OfferService, "source", "logger"),
7200 Error::invalid_child(DeclType::OfferProtocol, "source", "logger"),
7201 Error::invalid_child(DeclType::OfferDirectory, "source", "logger"),
7202 ])),
7203 },
7204 test_validate_offers_invalid_source_capability => {
7205 input = {
7206 fdecl::Component {
7207 offers: Some(vec![
7208 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7209 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
7210 name: "this-storage-doesnt-exist".to_string(),
7211 })),
7212 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
7213 target: Some(fdecl::Ref::Child(
7214 fdecl::ChildRef {
7215 name: "netstack".to_string(),
7216 collection: None,
7217 }
7218 )),
7219 target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
7220 dependency_type: Some(fdecl::DependencyType::Strong),
7221 ..Default::default()
7222 }),
7223 ]),
7224 ..new_component_decl()
7225 }
7226 },
7227 result = Err(ErrorList::new(vec![
7228 Error::invalid_capability(DeclType::OfferProtocol, "source", "this-storage-doesnt-exist"),
7229 Error::invalid_child(DeclType::OfferProtocol, "target", "netstack"),
7230 ])),
7231 },
7232 test_validate_offers_target => {
7233 input = {
7234 let mut decl = new_component_decl();
7235 decl.offers = Some(vec![
7236 fdecl::Offer::Service(fdecl::OfferService {
7237 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
7238 name: "modular".into()
7239 })),
7240 source_name: Some("logger".to_string()),
7241 target: Some(fdecl::Ref::Child(
7242 fdecl::ChildRef {
7243 name: "netstack".to_string(),
7244 collection: None,
7245 }
7246 )),
7247 target_name: Some("fuchsia.logger.Log".to_string()),
7248 ..Default::default()
7249 }),
7250 fdecl::Offer::Service(fdecl::OfferService {
7251 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
7252 name: "modular".into()
7253 })),
7254 source_name: Some("logger".to_string()),
7255 target: Some(fdecl::Ref::Child(
7256 fdecl::ChildRef {
7257 name: "netstack".to_string(),
7258 collection: None,
7259 }
7260 )),
7261 target_name: Some("fuchsia.logger.Log".to_string()),
7262 ..Default::default()
7263 }),
7264 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7265 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7266 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
7267 target: Some(fdecl::Ref::Child(
7268 fdecl::ChildRef {
7269 name: "netstack".to_string(),
7270 collection: None,
7271 }
7272 )),
7273 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7274 dependency_type: Some(fdecl::DependencyType::Strong),
7275 ..Default::default()
7276 }),
7277 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7278 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7279 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
7280 target: Some(fdecl::Ref::Child(
7281 fdecl::ChildRef {
7282 name: "netstack".to_string(),
7283 collection: None,
7284 }
7285 )),
7286 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7287 dependency_type: Some(fdecl::DependencyType::Strong),
7288 ..Default::default()
7289 }),
7290 fdecl::Offer::Directory(fdecl::OfferDirectory {
7291 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7292 source_name: Some("assets".to_string()),
7293 target: Some(fdecl::Ref::Collection(
7294 fdecl::CollectionRef { name: "modular".to_string() }
7295 )),
7296 target_name: Some("assets".to_string()),
7297 rights: Some(fio::Operations::CONNECT),
7298 subdir: None,
7299 dependency_type: Some(fdecl::DependencyType::Strong),
7300 ..Default::default()
7301 }),
7302 fdecl::Offer::Directory(fdecl::OfferDirectory {
7303 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7304 source_name: Some("assets".to_string()),
7305 target: Some(fdecl::Ref::Collection(
7306 fdecl::CollectionRef { name: "modular".to_string() }
7307 )),
7308 target_name: Some("assets".to_string()),
7309 rights: Some(fio::Operations::CONNECT),
7310 subdir: None,
7311 dependency_type: Some(fdecl::DependencyType::Weak),
7312 ..Default::default()
7313 }),
7314 fdecl::Offer::Storage(fdecl::OfferStorage {
7315 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7316 source_name: Some("data".to_string()),
7317 target: Some(fdecl::Ref::Collection(
7318 fdecl::CollectionRef { name: "modular".to_string() }
7319 )),
7320 target_name: Some("data".to_string()),
7321 ..Default::default()
7322 }),
7323 fdecl::Offer::Storage(fdecl::OfferStorage {
7324 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7325 source_name: Some("data".to_string()),
7326 target: Some(fdecl::Ref::Collection(
7327 fdecl::CollectionRef { name: "modular".to_string() }
7328 )),
7329 target_name: Some("data".to_string()),
7330 ..Default::default()
7331 }),
7332 fdecl::Offer::Runner(fdecl::OfferRunner {
7333 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7334 source_name: Some("elf".to_string()),
7335 target: Some(fdecl::Ref::Collection(
7336 fdecl::CollectionRef { name: "modular".to_string() }
7337 )),
7338 target_name: Some("duplicated".to_string()),
7339 ..Default::default()
7340 }),
7341 fdecl::Offer::Runner(fdecl::OfferRunner {
7342 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7343 source_name: Some("elf".to_string()),
7344 target: Some(fdecl::Ref::Collection(
7345 fdecl::CollectionRef { name: "modular".to_string() }
7346 )),
7347 target_name: Some("duplicated".to_string()),
7348 ..Default::default()
7349 }),
7350 fdecl::Offer::Resolver(fdecl::OfferResolver {
7351 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7352 source_name: Some("pkg".to_string()),
7353 target: Some(fdecl::Ref::Collection(
7354 fdecl::CollectionRef { name: "modular".to_string() }
7355 )),
7356 target_name: Some("duplicated".to_string()),
7357 ..Default::default()
7358 }),
7359 fdecl::Offer::EventStream(fdecl::OfferEventStream {
7360 source_name: Some("started".to_string()),
7361 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7362 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "netstack".to_string(), collection: None})),
7363 target_name: Some("started".to_string()),
7364 ..Default::default()
7365 }),
7366 fdecl::Offer::EventStream(fdecl::OfferEventStream {
7367 source_name: Some("started".to_string()),
7368 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7369 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "netstack".to_string(), collection: None})),
7370 target_name: Some("started".to_string()),
7371 ..Default::default()
7372 }),
7373 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7374 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7375 source_name: Some("a".to_string()),
7376 target: Some(fdecl::Ref::Collection(
7377 fdecl::CollectionRef { name: "modular".to_string() }
7378 )),
7379 target_name: Some("dict".to_string()),
7380 dependency_type: Some(fdecl::DependencyType::Strong),
7381 ..Default::default()
7382 }),
7383 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7384 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7385 source_name: Some("b".to_string()),
7386 target: Some(fdecl::Ref::Collection(
7387 fdecl::CollectionRef { name: "modular".to_string() }
7388 )),
7389 target_name: Some("dict".to_string()),
7390 dependency_type: Some(fdecl::DependencyType::Strong),
7391 ..Default::default()
7392 }),
7393 ]);
7394 decl.children = Some(vec![
7395 fdecl::Child{
7396 name: Some("netstack".to_string()),
7397 url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
7398 startup: Some(fdecl::StartupMode::Eager),
7399 on_terminate: None,
7400 environment: None,
7401 ..Default::default()
7402 },
7403 ]);
7404 decl.collections = Some(vec![
7405 fdecl::Collection{
7406 name: Some("modular".to_string()),
7407 durability: Some(fdecl::Durability::Transient),
7408 environment: None,
7409 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7410 allow_long_names: None,
7411 ..Default::default()
7412 },
7413 ]);
7414 decl
7415 },
7416 result = Err(ErrorList::new(vec![
7417 Error::duplicate_field(DeclType::OfferProtocol, "target_name", "fuchsia.logger.LegacyLog"),
7419 Error::duplicate_field(DeclType::OfferDirectory, "target_name", "assets"),
7420 Error::duplicate_field(DeclType::OfferStorage, "target_name", "data"),
7421 Error::duplicate_field(DeclType::OfferRunner, "target_name", "duplicated"),
7422 Error::duplicate_field(DeclType::OfferResolver, "target_name", "duplicated"),
7423 Error::duplicate_field(DeclType::OfferEventStream, "target_name", "started"),
7424 Error::duplicate_field(DeclType::OfferDictionary, "target_name", "dict"),
7425 ])),
7426 },
7427 test_validate_offers_target_invalid => {
7428 input = {
7429 let mut decl = new_component_decl();
7430 decl.offers = Some(vec![
7431 fdecl::Offer::Service(fdecl::OfferService {
7432 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7433 source_name: Some("logger".to_string()),
7434 target: Some(fdecl::Ref::Child(
7435 fdecl::ChildRef {
7436 name: "netstack".to_string(),
7437 collection: None,
7438 }
7439 )),
7440 target_name: Some("fuchsia.logger.Log".to_string()),
7441 ..Default::default()
7442 }),
7443 fdecl::Offer::Service(fdecl::OfferService {
7444 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7445 source_name: Some("logger".to_string()),
7446 target: Some(fdecl::Ref::Collection(
7447 fdecl::CollectionRef { name: "modular".to_string(), }
7448 )),
7449 target_name: Some("fuchsia.logger.Log".to_string()),
7450 ..Default::default()
7451 }),
7452 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7453 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7454 source_name: Some("legacy_logger".to_string()),
7455 target: Some(fdecl::Ref::Child(
7456 fdecl::ChildRef {
7457 name: "netstack".to_string(),
7458 collection: None,
7459 }
7460 )),
7461 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7462 dependency_type: Some(fdecl::DependencyType::Weak),
7463 ..Default::default()
7464 }),
7465 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7466 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7467 source_name: Some("legacy_logger".to_string()),
7468 target: Some(fdecl::Ref::Collection(
7469 fdecl::CollectionRef { name: "modular".to_string(), }
7470 )),
7471 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7472 dependency_type: Some(fdecl::DependencyType::Strong),
7473 ..Default::default()
7474 }),
7475 fdecl::Offer::Directory(fdecl::OfferDirectory {
7476 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7477 source_name: Some("assets".to_string()),
7478 target: Some(fdecl::Ref::Child(
7479 fdecl::ChildRef {
7480 name: "netstack".to_string(),
7481 collection: None,
7482 }
7483 )),
7484 target_name: Some("data".to_string()),
7485 rights: Some(fio::Operations::CONNECT),
7486 subdir: None,
7487 dependency_type: Some(fdecl::DependencyType::Strong),
7488 ..Default::default()
7489 }),
7490 fdecl::Offer::Directory(fdecl::OfferDirectory {
7491 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7492 source_name: Some("assets".to_string()),
7493 target: Some(fdecl::Ref::Collection(
7494 fdecl::CollectionRef { name: "modular".to_string(), }
7495 )),
7496 target_name: Some("data".to_string()),
7497 rights: Some(fio::Operations::CONNECT),
7498 subdir: None,
7499 dependency_type: Some(fdecl::DependencyType::Weak),
7500 ..Default::default()
7501 }),
7502 fdecl::Offer::Storage(fdecl::OfferStorage {
7503 source_name: Some("data".to_string()),
7504 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7505 target: Some(fdecl::Ref::Child(
7506 fdecl::ChildRef {
7507 name: "netstack".to_string(),
7508 collection: None,
7509 }
7510 )),
7511 target_name: Some("data".to_string()),
7512 ..Default::default()
7513 }),
7514 fdecl::Offer::Storage(fdecl::OfferStorage {
7515 source_name: Some("data".to_string()),
7516 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7517 target: Some(fdecl::Ref::Collection(
7518 fdecl::CollectionRef { name: "modular".to_string(), }
7519 )),
7520 target_name: Some("data".to_string()),
7521 ..Default::default()
7522 }),
7523 fdecl::Offer::Runner(fdecl::OfferRunner {
7524 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7525 source_name: Some("elf".to_string()),
7526 target: Some(fdecl::Ref::Child(
7527 fdecl::ChildRef {
7528 name: "netstack".to_string(),
7529 collection: None,
7530 }
7531 )),
7532 target_name: Some("elf".to_string()),
7533 ..Default::default()
7534 }),
7535 fdecl::Offer::Runner(fdecl::OfferRunner {
7536 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7537 source_name: Some("elf".to_string()),
7538 target: Some(fdecl::Ref::Collection(
7539 fdecl::CollectionRef { name: "modular".to_string(), }
7540 )),
7541 target_name: Some("elf".to_string()),
7542 ..Default::default()
7543 }),
7544 fdecl::Offer::Resolver(fdecl::OfferResolver {
7545 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7546 source_name: Some("pkg".to_string()),
7547 target: Some(fdecl::Ref::Child(
7548 fdecl::ChildRef {
7549 name: "netstack".to_string(),
7550 collection: None,
7551 }
7552 )),
7553 target_name: Some("pkg".to_string()),
7554 ..Default::default()
7555 }),
7556 fdecl::Offer::Resolver(fdecl::OfferResolver {
7557 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7558 source_name: Some("pkg".to_string()),
7559 target: Some(fdecl::Ref::Collection(
7560 fdecl::CollectionRef { name: "modular".to_string(), }
7561 )),
7562 target_name: Some("pkg".to_string()),
7563 ..Default::default()
7564 }),
7565 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7566 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7567 source_name: Some("pkg".to_string()),
7568 target: Some(fdecl::Ref::Child(
7569 fdecl::ChildRef {
7570 name: "netstack".to_string(),
7571 collection: None,
7572 }
7573 )),
7574 target_name: Some("pkg".to_string()),
7575 dependency_type: Some(fdecl::DependencyType::Strong),
7576 ..Default::default()
7577 }),
7578 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7579 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7580 source_name: Some("pkg".to_string()),
7581 target: Some(fdecl::Ref::Collection(
7582 fdecl::CollectionRef { name: "modular".to_string(), }
7583 )),
7584 target_name: Some("pkg".to_string()),
7585 dependency_type: Some(fdecl::DependencyType::Strong),
7586 ..Default::default()
7587 }),
7588 ]);
7589 decl
7590 },
7591 result = Err(ErrorList::new(vec![
7592 Error::invalid_child(DeclType::OfferService, "target", "netstack"),
7593 Error::invalid_collection(DeclType::OfferService, "target", "modular"),
7594 Error::invalid_child(DeclType::OfferProtocol, "target", "netstack"),
7595 Error::invalid_collection(DeclType::OfferProtocol, "target", "modular"),
7596 Error::invalid_child(DeclType::OfferDirectory, "target", "netstack"),
7597 Error::invalid_collection(DeclType::OfferDirectory, "target", "modular"),
7598 Error::invalid_child(DeclType::OfferStorage, "target", "netstack"),
7599 Error::invalid_collection(DeclType::OfferStorage, "target", "modular"),
7600 Error::invalid_child(DeclType::OfferRunner, "target", "netstack"),
7601 Error::invalid_collection(DeclType::OfferRunner, "target", "modular"),
7602 Error::invalid_child(DeclType::OfferResolver, "target", "netstack"),
7603 Error::invalid_collection(DeclType::OfferResolver, "target", "modular"),
7604 Error::invalid_child(DeclType::OfferDictionary, "target", "netstack"),
7605 Error::invalid_collection(DeclType::OfferDictionary, "target", "modular"),
7606 ])),
7607 },
7608 test_validate_offers_target_dictionary => {
7609 input = fdecl::Component {
7610 offers: Some(vec![
7611 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7613 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7614 source_name: Some("p".to_string()),
7615 target: Some(fdecl::Ref::Capability(
7616 fdecl::CapabilityRef {
7617 name: "dict".into(),
7618 },
7619 )),
7620 target_name: Some("p".into()),
7621 dependency_type: Some(fdecl::DependencyType::Strong),
7622 ..Default::default()
7623 }),
7624 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7626 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7627 source_name: Some("p".to_string()),
7628 target: Some(fdecl::Ref::Capability(
7629 fdecl::CapabilityRef {
7630 name: "dynamic".into(),
7631 },
7632 )),
7633 target_name: Some("p".into()),
7634 dependency_type: Some(fdecl::DependencyType::Strong),
7635 ..Default::default()
7636 }),
7637 ]),
7638 capabilities: Some(vec![
7639 fdecl::Capability::Dictionary(fdecl::Dictionary {
7640 name: Some("dict".into()),
7641 ..Default::default()
7642 }),
7643 fdecl::Capability::Dictionary(fdecl::Dictionary {
7644 name: Some("dynamic".into()),
7645 source_path: Some("/out/dir".into()),
7646 ..Default::default()
7647 }),
7648 ]),
7649 ..Default::default()
7650 },
7651 result = Err(ErrorList::new(vec![
7652 Error::invalid_field(DeclType::OfferProtocol, "target"),
7653 ])),
7654 },
7655 test_validate_offers_invalid_source_collection => {
7656 input = {
7657 let mut decl = new_component_decl();
7658 decl.collections = Some(vec![
7659 fdecl::Collection {
7660 name: Some("col".to_string()),
7661 durability: Some(fdecl::Durability::Transient),
7662 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7663 allow_long_names: None,
7664 ..Default::default()
7665 }
7666 ]);
7667 decl.children = Some(vec![
7668 fdecl::Child {
7669 name: Some("child".to_string()),
7670 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7671 startup: Some(fdecl::StartupMode::Lazy),
7672 on_terminate: None,
7673 ..Default::default()
7674 }
7675 ]);
7676 decl.offers = Some(vec![
7677 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7678 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7679 source_name: Some("a".to_string()),
7680 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7681 target_name: Some("a".to_string()),
7682 dependency_type: Some(fdecl::DependencyType::Strong),
7683 ..Default::default()
7684 }),
7685 fdecl::Offer::Directory(fdecl::OfferDirectory {
7686 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7687 source_name: Some("b".to_string()),
7688 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7689 target_name: Some("b".to_string()),
7690 rights: Some(fio::Operations::CONNECT),
7691 subdir: None,
7692 dependency_type: Some(fdecl::DependencyType::Strong),
7693 ..Default::default()
7694 }),
7695 fdecl::Offer::Storage(fdecl::OfferStorage {
7696 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7697 source_name: Some("c".to_string()),
7698 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7699 target_name: Some("c".to_string()),
7700 ..Default::default()
7701 }),
7702 fdecl::Offer::Runner(fdecl::OfferRunner {
7703 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7704 source_name: Some("d".to_string()),
7705 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7706 target_name: Some("d".to_string()),
7707 ..Default::default()
7708 }),
7709 fdecl::Offer::Resolver(fdecl::OfferResolver {
7710 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7711 source_name: Some("e".to_string()),
7712 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7713 target_name: Some("e".to_string()),
7714 ..Default::default()
7715 }),
7716 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7717 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7718 source_name: Some("f".to_string()),
7719 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7720 target_name: Some("f".to_string()),
7721 dependency_type: Some(fdecl::DependencyType::Strong),
7722 ..Default::default()
7723 }),
7724 ]);
7725 decl
7726 },
7727 result = Err(ErrorList::new(vec![
7728 Error::invalid_field(DeclType::OfferProtocol, "source"),
7729 Error::invalid_field(DeclType::OfferDirectory, "source"),
7730 Error::invalid_field(DeclType::OfferStorage, "source"),
7731 Error::invalid_field(DeclType::OfferRunner, "source"),
7732 Error::invalid_field(DeclType::OfferResolver, "source"),
7733 Error::invalid_field(DeclType::OfferDictionary, "source"),
7734 ])),
7735 },
7736 test_validate_offers_source_collection => {
7737 input = {
7738 let mut decl = new_component_decl();
7739 decl.collections = Some(vec![
7740 fdecl::Collection {
7741 name: Some("col".to_string()),
7742 durability: Some(fdecl::Durability::Transient),
7743 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7744 allow_long_names: None,
7745 ..Default::default()
7746 }
7747 ]);
7748 decl.children = Some(vec![
7749 fdecl::Child {
7750 name: Some("child".to_string()),
7751 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7752 startup: Some(fdecl::StartupMode::Lazy),
7753 on_terminate: None,
7754 ..Default::default()
7755 }
7756 ]);
7757 decl.offers = Some(vec![
7758 fdecl::Offer::Service(fdecl::OfferService {
7759 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7760 source_name: Some("a".to_string()),
7761 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7762 target_name: Some("a".to_string()),
7763 ..Default::default()
7764 })
7765 ]);
7766 decl
7767 },
7768 result = Ok(()),
7769 },
7770 test_validate_offers_invalid_capability_from_self => {
7771 input = {
7772 let mut decl = new_component_decl();
7773 decl.children = Some(vec![
7774 fdecl::Child {
7775 name: Some("child".to_string()),
7776 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7777 startup: Some(fdecl::StartupMode::Lazy),
7778 ..Default::default()
7779 }
7780 ]);
7781 decl.offers = Some(vec![
7782 fdecl::Offer::Service(fdecl::OfferService {
7783 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7784 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7785 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7786 name: "child".into(),
7787 collection: None
7788 })),
7789 target_name: Some("foo".into()),
7790 ..Default::default()
7791 }),
7792 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7793 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7794 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7795 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7796 name: "child".into(),
7797 collection: None
7798 })),
7799 target_name: Some("bar".into()),
7800 dependency_type: Some(fdecl::DependencyType::Strong),
7801 ..Default::default()
7802 }),
7803 fdecl::Offer::Directory(fdecl::OfferDirectory {
7804 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7805 source_name: Some("dir".into()),
7806 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7807 name: "child".into(),
7808 collection: None
7809 })),
7810 target_name: Some("assets".into()),
7811 dependency_type: Some(fdecl::DependencyType::Strong),
7812 ..Default::default()
7813 }),
7814 fdecl::Offer::Runner(fdecl::OfferRunner {
7815 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7816 source_name: Some("source_elf".into()),
7817 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7818 name: "child".into(),
7819 collection: None
7820 })),
7821 target_name: Some("elf".into()),
7822 ..Default::default()
7823 }),
7824 fdecl::Offer::Resolver(fdecl::OfferResolver {
7825 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7826 source_name: Some("source_pkg".into()),
7827 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7828 name: "child".into(),
7829 collection: None
7830 })),
7831 target_name: Some("pkg".into()),
7832 ..Default::default()
7833 }),
7834 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7835 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7836 source_name: Some("source_dict".into()),
7837 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7838 name: "child".into(),
7839 collection: None
7840 })),
7841 target_name: Some("dict".into()),
7842 dependency_type: Some(fdecl::DependencyType::Strong),
7843 ..Default::default()
7844 }),
7845 fdecl::Offer::Storage(fdecl::OfferStorage {
7846 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7847 source_name: Some("source_storage".into()),
7848 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7849 name: "child".into(),
7850 collection: None
7851 })),
7852 target_name: Some("storage".into()),
7853 ..Default::default()
7854 }),
7855 fdecl::Offer::Config(fdecl::OfferConfiguration {
7856 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7857 source_name: Some("source_config".into()),
7858 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7859 name: "child".into(),
7860 collection: None
7861 })),
7862 target_name: Some("config".into()),
7863 ..Default::default()
7864 }),
7865 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7866 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7867 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7868 source_dictionary: Some("dict/inner".into()),
7869 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7870 name: "child".into(),
7871 collection: None
7872 })),
7873 target_name: Some("baz".into()),
7874 dependency_type: Some(fdecl::DependencyType::Strong),
7875 ..Default::default()
7876 }),
7877 ]);
7878 decl
7879 },
7880 result = Err(ErrorList::new(vec![
7881 Error::invalid_capability(
7882 DeclType::OfferService,
7883 "source",
7884 "fuchsia.some.library.SomeProtocol"),
7885 Error::invalid_capability(
7886 DeclType::OfferProtocol,
7887 "source",
7888 "fuchsia.some.library.SomeProtocol"),
7889 Error::invalid_capability(DeclType::OfferDirectory, "source", "dir"),
7890 Error::invalid_capability(DeclType::OfferRunner, "source", "source_elf"),
7891 Error::invalid_capability(DeclType::OfferResolver, "source", "source_pkg"),
7892 Error::invalid_capability(DeclType::OfferDictionary, "source", "source_dict"),
7893 Error::invalid_capability(DeclType::OfferStorage, "source", "source_storage"),
7894 Error::invalid_capability(DeclType::OfferConfig, "source", "source_config"),
7895 Error::invalid_capability(DeclType::OfferProtocol, "source", "dict"),
7896 ])),
7897 },
7898 test_validate_offers_long_dependency_cycle => {
7899 input = {
7900 let mut decl = new_component_decl();
7901 let dependencies = vec![
7902 ("d", "b"),
7903 ("a", "b"),
7904 ("b", "c"),
7905 ("b", "d"),
7906 ("c", "a"),
7907 ];
7908 let offers = dependencies.into_iter().map(|(from,to)|
7909 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7910 source: Some(fdecl::Ref::Child(
7911 fdecl::ChildRef { name: from.to_string(), collection: None },
7912 )),
7913 source_name: Some(format!("thing_{}", from)),
7914 target: Some(fdecl::Ref::Child(
7915 fdecl::ChildRef { name: to.to_string(), collection: None },
7916 )),
7917 target_name: Some(format!("thing_{}", from)),
7918 dependency_type: Some(fdecl::DependencyType::Strong),
7919 ..Default::default()
7920 })).collect();
7921 let children = ["a", "b", "c", "d"].iter().map(|name| {
7922 fdecl::Child {
7923 name: Some(name.to_string()),
7924 url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
7925 startup: Some(fdecl::StartupMode::Lazy),
7926 on_terminate: None,
7927 environment: None,
7928 ..Default::default()
7929 }
7930 }).collect();
7931 decl.offers = Some(offers);
7932 decl.children = Some(children);
7933 decl
7934 },
7935 result = Err(ErrorList::new(vec![
7936 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()),
7937 ])),
7938 },
7939 test_validate_offers_not_required_invalid_source_service => {
7940 input = {
7941 let mut decl = generate_offer_different_source_and_availability_decl(
7942 |source, availability, target_name|
7943 fdecl::Offer::Service(fdecl::OfferService {
7944 source: Some(source),
7945 source_name: Some("fuchsia.examples.Echo".to_string()),
7946 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7947 name: "sink".to_string(),
7948 collection: None,
7949 })),
7950 target_name: Some(target_name.into()),
7951 availability: Some(availability),
7952 ..Default::default()
7953 })
7954 );
7955 decl.capabilities = Some(vec![
7956 fdecl::Capability::Service(fdecl::Service {
7957 name: Some("fuchsia.examples.Echo".to_string()),
7958 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
7959 ..Default::default()
7960 }),
7961 ]);
7962 decl
7963 },
7964 result = {
7965 Err(ErrorList::new(vec![
7966 Error::availability_must_be_optional(
7967 DeclType::OfferService,
7968 "availability",
7969 Some(&"fuchsia.examples.Echo".to_string()),
7970 ),
7971 Error::availability_must_be_optional(
7972 DeclType::OfferService,
7973 "availability",
7974 Some(&"fuchsia.examples.Echo".to_string()),
7975 ),
7976 ]))
7977 },
7978 },
7979 test_validate_offers_not_required_invalid_source_protocol => {
7980 input = {
7981 let mut decl = generate_offer_different_source_and_availability_decl(
7982 |source, availability, target_name|
7983 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7984 source: Some(source),
7985 source_name: Some("fuchsia.examples.Echo".to_string()),
7986 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7987 name: "sink".to_string(),
7988 collection: None,
7989 })),
7990 target_name: Some(target_name.into()),
7991 dependency_type: Some(fdecl::DependencyType::Strong),
7992 availability: Some(availability),
7993 ..Default::default()
7994 })
7995 );
7996 decl.capabilities = Some(vec![
7997 fdecl::Capability::Protocol(fdecl::Protocol {
7998 name: Some("fuchsia.examples.Echo".to_string()),
7999 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
8000 ..Default::default()
8001 }),
8002 ]);
8003 decl
8004 },
8005 result = {
8006 Err(ErrorList::new(vec![
8007 Error::availability_must_be_optional(
8008 DeclType::OfferProtocol,
8009 "availability",
8010 Some(&"fuchsia.examples.Echo".to_string()),
8011 ),
8012 Error::availability_must_be_optional(
8013 DeclType::OfferProtocol,
8014 "availability",
8015 Some(&"fuchsia.examples.Echo".to_string()),
8016 ),
8017 ]))
8018 },
8019 },
8020 test_validate_offers_not_required_invalid_source_directory => {
8021 input = {
8022 let mut decl = generate_offer_different_source_and_availability_decl(
8023 |source, availability, target_name|
8024 fdecl::Offer::Directory(fdecl::OfferDirectory {
8025 source: Some(source),
8026 source_name: Some("assets".to_string()),
8027 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8028 name: "sink".to_string(),
8029 collection: None,
8030 })),
8031 target_name: Some(target_name.into()),
8032 rights: Some(fio::Operations::CONNECT),
8033 subdir: None,
8034 dependency_type: Some(fdecl::DependencyType::Weak),
8035 availability: Some(availability),
8036 ..Default::default()
8037 })
8038 );
8039 decl.capabilities = Some(vec![
8040 fdecl::Capability::Directory(fdecl::Directory {
8041 name: Some("assets".to_string()),
8042 source_path: Some("/assets".to_string()),
8043 rights: Some(fio::Operations::CONNECT),
8044 ..Default::default()
8045 }),
8046 ]);
8047 decl
8048 },
8049 result = {
8050 Err(ErrorList::new(vec![
8051 Error::availability_must_be_optional(
8052 DeclType::OfferDirectory,
8053 "availability",
8054 Some(&"assets".to_string()),
8055 ),
8056 Error::availability_must_be_optional(
8057 DeclType::OfferDirectory,
8058 "availability",
8059 Some(&"assets".to_string()),
8060 ),
8061 ]))
8062 },
8063 },
8064 test_validate_offers_not_required_invalid_source_storage => {
8065 input = {
8066 let mut decl = new_component_decl();
8067 decl.children = Some(vec![
8068 fdecl::Child {
8069 name: Some("sink".to_string()),
8070 url: Some("fuchsia-pkg://fuchsia.com/sink#meta/sink.cm".to_string()),
8071 startup: Some(fdecl::StartupMode::Lazy),
8072 on_terminate: None,
8073 environment: None,
8074 ..Default::default()
8075 },
8076 ]);
8077 decl.capabilities = Some(vec![
8078 fdecl::Capability::Storage(fdecl::Storage {
8079 name: Some("data".to_string()),
8080 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8081 backing_dir: Some("minfs".to_string()),
8082 subdir: None,
8083 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
8084 ..Default::default()
8085 }),
8086 ]);
8087 let new_offer = |source: fdecl::Ref, availability: fdecl::Availability,
8088 target_name: &str|
8089 {
8090 fdecl::Offer::Storage(fdecl::OfferStorage {
8091 source: Some(source),
8092 source_name: Some("data".to_string()),
8093 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8094 name: "sink".to_string(),
8095 collection: None,
8096 })),
8097 target_name: Some(target_name.into()),
8098 availability: Some(availability),
8099 ..Default::default()
8100 })
8101 };
8102 decl.offers = Some(vec![
8103 new_offer(
8106 fdecl::Ref::Parent(fdecl::ParentRef {}),
8107 fdecl::Availability::Required,
8108 "data0",
8109 ),
8110 new_offer(
8111 fdecl::Ref::Parent(fdecl::ParentRef {}),
8112 fdecl::Availability::Optional,
8113 "data1",
8114 ),
8115 new_offer(
8116 fdecl::Ref::Parent(fdecl::ParentRef {}),
8117 fdecl::Availability::SameAsTarget,
8118 "data2",
8119 ),
8120 new_offer(
8121 fdecl::Ref::VoidType(fdecl::VoidRef {}),
8122 fdecl::Availability::Optional,
8123 "data3",
8124 ),
8125 new_offer(
8128 fdecl::Ref::Self_(fdecl::SelfRef {}),
8129 fdecl::Availability::Optional,
8130 "data4",
8131 ),
8132 new_offer(
8133 fdecl::Ref::Self_(fdecl::SelfRef {}),
8134 fdecl::Availability::SameAsTarget,
8135 "data5",
8136 ),
8137 new_offer(
8139 fdecl::Ref::VoidType(fdecl::VoidRef {}),
8140 fdecl::Availability::Required,
8141 "data6",
8142 ),
8143 new_offer(
8144 fdecl::Ref::VoidType(fdecl::VoidRef {}),
8145 fdecl::Availability::SameAsTarget,
8146 "data7",
8147 ),
8148 ]);
8149 decl
8150 },
8151 result = {
8152 Err(ErrorList::new(vec![
8153 Error::availability_must_be_optional(
8154 DeclType::OfferStorage,
8155 "availability",
8156 Some(&"data".to_string()),
8157 ),
8158 Error::availability_must_be_optional(
8159 DeclType::OfferStorage,
8160 "availability",
8161 Some(&"data".to_string()),
8162 ),
8163 ]))
8164 },
8165 },
8166
8167 test_validate_offers_valid_service_aggregation => {
8168 input = {
8169 let mut decl = new_component_decl();
8170 decl.offers = Some(vec![
8171 fdecl::Offer::Service(fdecl::OfferService {
8172 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
8173 name: "coll_a".to_string()
8174 })),
8175 source_name: Some("fuchsia.logger.Log".to_string()),
8176 target: Some(fdecl::Ref::Child(
8177 fdecl::ChildRef {
8178 name: "child_c".to_string(),
8179 collection: None,
8180 }
8181 )),
8182 target_name: Some("fuchsia.logger.Log".to_string()),
8183 source_instance_filter: None,
8184 ..Default::default()
8185 }),
8186 fdecl::Offer::Service(fdecl::OfferService {
8187 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
8188 name: "coll_b".to_string()
8189 })),
8190 source_name: Some("fuchsia.logger.Log".to_string()),
8191 target: Some(fdecl::Ref::Child(
8192 fdecl::ChildRef {
8193 name: "child_c".to_string(),
8194 collection: None,
8195 }
8196 )),
8197 target_name: Some("fuchsia.logger.Log".to_string()),
8198 source_instance_filter: Some(vec!["a_different_default".to_string()]),
8199 ..Default::default()
8200 })
8201 ]);
8202 decl.children = Some(vec![
8203 fdecl::Child {
8204 name: Some("child_c".to_string()),
8205 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
8206 startup: Some(fdecl::StartupMode::Lazy),
8207 ..Default::default()
8208 },
8209 ]);
8210 decl.collections = Some(vec![
8211 fdecl::Collection {
8212 name: Some("coll_a".into()),
8213 durability: Some(fdecl::Durability::Transient),
8214 ..Default::default()
8215 },
8216 fdecl::Collection {
8217 name: Some("coll_b".into()),
8218 durability: Some(fdecl::Durability::Transient),
8219 ..Default::default()
8220 },
8221 ]);
8222 decl
8223 },
8224 result = Ok(()),
8225 },
8226
8227 test_validate_source_dictionary => {
8229 input = fdecl::Component {
8230 program: Some(fdecl::Program {
8231 runner: Some("elf".into()),
8232 info: Some(fdata::Dictionary {
8233 entries: None,
8234 ..Default::default()
8235 }),
8236 ..Default::default()
8237 }),
8238 uses: Some(vec![
8239 fdecl::Use::Protocol(fdecl::UseProtocol {
8240 dependency_type: Some(fdecl::DependencyType::Strong),
8241 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8242 source_dictionary: Some("bad//".into()),
8243 source_name: Some("foo".into()),
8244 target_path: Some("/svc/foo".into()),
8245 ..Default::default()
8246 }),
8247 ]),
8248 exposes: Some(vec![
8249 fdecl::Expose::Directory(fdecl::ExposeDirectory {
8250 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8251 name: "missing".into(),
8252 collection: None,
8253 })),
8254 source_dictionary: Some("in/dict".into()),
8255 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8256 source_name: Some("foo".into()),
8257 target_name: Some("bar".into()),
8258 ..Default::default()
8259 }),
8260 ]),
8261 offers: Some(vec![
8262 fdecl::Offer::Service(fdecl::OfferService {
8263 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8264 source_dictionary: Some("bad//".into()),
8265 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8266 name: "child".into(),
8267 collection: None,
8268 })),
8269 source_name: Some("foo".into()),
8270 target_name: Some("bar".into()),
8271 ..Default::default()
8272 }),
8273 ]),
8274 children: Some(vec![
8275 fdecl::Child {
8276 name: Some("child".into()),
8277 url: Some("fuchsia-pkg://child".into()),
8278 startup: Some(fdecl::StartupMode::Lazy),
8279 ..Default::default()
8280 },
8281 ]),
8282 ..Default::default()
8283 },
8284 result = Err(ErrorList::new(vec![
8285 Error::invalid_field(DeclType::UseProtocol, "source_dictionary"),
8286 Error::invalid_child(DeclType::ExposeDirectory, "source", "missing"),
8287 Error::invalid_field(DeclType::OfferService, "source_dictionary"),
8288 ])),
8289 },
8290 test_validate_dictionary_too_long => {
8291 input = fdecl::Component {
8292 program: Some(fdecl::Program {
8293 runner: Some("elf".into()),
8294 info: Some(fdata::Dictionary {
8295 entries: None,
8296 ..Default::default()
8297 }),
8298 ..Default::default()
8299 }),
8300 uses: Some(vec![
8301 fdecl::Use::Protocol(fdecl::UseProtocol {
8302 dependency_type: Some(fdecl::DependencyType::Strong),
8303 source: Some(fdecl::Ref::Parent( fdecl::ParentRef {} )),
8304 source_dictionary: Some("a".repeat(4096)),
8305 source_name: Some("foo".into()),
8306 target_path: Some("/svc/foo".into()),
8307 ..Default::default()
8308 }),
8309 ]),
8310 ..Default::default()
8311 },
8312 result = Err(ErrorList::new(vec![
8313 Error::field_too_long(DeclType::UseProtocol, "source_dictionary"),
8314 ])),
8315 },
8316
8317 test_validate_environment_empty => {
8319 input = {
8320 let mut decl = new_component_decl();
8321 decl.environments = Some(vec![fdecl::Environment {
8322 name: None,
8323 extends: None,
8324 runners: None,
8325 resolvers: None,
8326 stop_timeout_ms: None,
8327 debug_capabilities: None,
8328 ..Default::default()
8329 }]);
8330 decl
8331 },
8332 result = Err(ErrorList::new(vec![
8333 Error::missing_field(DeclType::Environment, "name"),
8334 Error::missing_field(DeclType::Environment, "extends"),
8335 ])),
8336 },
8337
8338 test_validate_environment_no_stop_timeout => {
8339 input = {
8340 let mut decl = new_component_decl();
8341 decl.environments = Some(vec![fdecl::Environment {
8342 name: Some("env".to_string()),
8343 extends: Some(fdecl::EnvironmentExtends::None),
8344 runners: None,
8345 resolvers: None,
8346 stop_timeout_ms: None,
8347 ..Default::default()
8348 }]);
8349 decl
8350 },
8351 result = Err(ErrorList::new(vec![Error::missing_field(DeclType::Environment, "stop_timeout_ms")])),
8352 },
8353
8354 test_validate_environment_extends_stop_timeout => {
8355 input = { let mut decl = new_component_decl();
8356 decl.environments = Some(vec![fdecl::Environment {
8357 name: Some("env".to_string()),
8358 extends: Some(fdecl::EnvironmentExtends::Realm),
8359 runners: None,
8360 resolvers: None,
8361 stop_timeout_ms: None,
8362 ..Default::default()
8363 }]);
8364 decl
8365 },
8366 result = Ok(()),
8367 },
8368 test_validate_environment_long_identifiers => {
8369 input = {
8370 let mut decl = new_component_decl();
8371 decl.environments = Some(vec![fdecl::Environment {
8372 name: Some("a".repeat(256)),
8373 extends: Some(fdecl::EnvironmentExtends::None),
8374 runners: Some(vec![
8375 fdecl::RunnerRegistration {
8376 source_name: Some("a".repeat(256)),
8377 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8378 target_name: Some("a".repeat(256)),
8379 ..Default::default()
8380 },
8381 ]),
8382 resolvers: Some(vec![
8383 fdecl::ResolverRegistration {
8384 resolver: Some("a".repeat(256)),
8385 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8386 scheme: Some("a".repeat(256)),
8387 ..Default::default()
8388 },
8389 ]),
8390 stop_timeout_ms: Some(1234),
8391 ..Default::default()
8392 }]);
8393 decl
8394 },
8395 result = Err(ErrorList::new(vec![
8396 Error::field_too_long(DeclType::Environment, "name"),
8397 Error::field_too_long(DeclType::RunnerRegistration, "source_name"),
8398 Error::field_too_long(DeclType::RunnerRegistration, "target_name"),
8399 Error::field_too_long(DeclType::ResolverRegistration, "resolver"),
8400 Error::field_too_long(DeclType::ResolverRegistration, "scheme"),
8401 ])),
8402 },
8403 test_validate_environment_empty_runner_resolver_fields => {
8404 input = {
8405 let mut decl = new_component_decl();
8406 decl.environments = Some(vec![fdecl::Environment {
8407 name: Some("a".to_string()),
8408 extends: Some(fdecl::EnvironmentExtends::None),
8409 runners: Some(vec![
8410 fdecl::RunnerRegistration {
8411 source_name: None,
8412 source: None,
8413 target_name: None,
8414 ..Default::default()
8415 },
8416 ]),
8417 resolvers: Some(vec![
8418 fdecl::ResolverRegistration {
8419 resolver: None,
8420 source: None,
8421 scheme: None,
8422 ..Default::default()
8423 },
8424 ]),
8425 stop_timeout_ms: Some(1234),
8426 ..Default::default()
8427 }]);
8428 decl
8429 },
8430 result = Err(ErrorList::new(vec![
8431 Error::missing_field(DeclType::RunnerRegistration, "source_name"),
8432 Error::missing_field(DeclType::RunnerRegistration, "source"),
8433 Error::missing_field(DeclType::RunnerRegistration, "target_name"),
8434 Error::missing_field(DeclType::ResolverRegistration, "resolver"),
8435 Error::missing_field(DeclType::ResolverRegistration, "source"),
8436 Error::missing_field(DeclType::ResolverRegistration, "scheme"),
8437 ])),
8438 },
8439 test_validate_environment_invalid_fields => {
8440 input = {
8441 let mut decl = new_component_decl();
8442 decl.environments = Some(vec![fdecl::Environment {
8443 name: Some("a".to_string()),
8444 extends: Some(fdecl::EnvironmentExtends::None),
8445 runners: Some(vec![
8446 fdecl::RunnerRegistration {
8447 source_name: Some("^a".to_string()),
8448 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
8449 target_name: Some("%a".to_string()),
8450 ..Default::default()
8451 },
8452 ]),
8453 resolvers: Some(vec![
8454 fdecl::ResolverRegistration {
8455 resolver: Some("^a".to_string()),
8456 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
8457 scheme: Some("9scheme".to_string()),
8458 ..Default::default()
8459 },
8460 ]),
8461 stop_timeout_ms: Some(1234),
8462 ..Default::default()
8463 }]);
8464 decl
8465 },
8466 result = Err(ErrorList::new(vec![
8467 Error::invalid_field(DeclType::RunnerRegistration, "source_name"),
8468 Error::invalid_field(DeclType::RunnerRegistration, "source"),
8469 Error::invalid_field(DeclType::RunnerRegistration, "target_name"),
8470 Error::invalid_field(DeclType::ResolverRegistration, "resolver"),
8471 Error::invalid_field(DeclType::ResolverRegistration, "source"),
8472 Error::invalid_field(DeclType::ResolverRegistration, "scheme"),
8473 ])),
8474 },
8475 test_validate_environment_missing_runner => {
8476 input = {
8477 let mut decl = new_component_decl();
8478 decl.environments = Some(vec![fdecl::Environment {
8479 name: Some("a".to_string()),
8480 extends: Some(fdecl::EnvironmentExtends::None),
8481 runners: Some(vec![
8482 fdecl::RunnerRegistration {
8483 source_name: Some("dart".to_string()),
8484 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
8485 target_name: Some("dart".to_string()),
8486 ..Default::default()
8487 },
8488 ]),
8489 resolvers: None,
8490 stop_timeout_ms: Some(1234),
8491 ..Default::default()
8492 }]);
8493 decl
8494 },
8495 result = Err(ErrorList::new(vec![
8496 Error::invalid_runner(DeclType::RunnerRegistration, "source_name", "dart"),
8497 ])),
8498 },
8499 test_validate_environment_duplicate_registrations => {
8500 input = {
8501 let mut decl = new_component_decl();
8502 decl.environments = Some(vec![fdecl::Environment {
8503 name: Some("a".to_string()),
8504 extends: Some(fdecl::EnvironmentExtends::None),
8505 runners: Some(vec![
8506 fdecl::RunnerRegistration {
8507 source_name: Some("dart".to_string()),
8508 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8509 target_name: Some("dart".to_string()),
8510 ..Default::default()
8511 },
8512 fdecl::RunnerRegistration {
8513 source_name: Some("other-dart".to_string()),
8514 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8515 target_name: Some("dart".to_string()),
8516 ..Default::default()
8517 },
8518 ]),
8519 resolvers: Some(vec![
8520 fdecl::ResolverRegistration {
8521 resolver: Some("pkg_resolver".to_string()),
8522 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8523 scheme: Some("fuchsia-pkg".to_string()),
8524 ..Default::default()
8525 },
8526 fdecl::ResolverRegistration {
8527 resolver: Some("base_resolver".to_string()),
8528 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8529 scheme: Some("fuchsia-pkg".to_string()),
8530 ..Default::default()
8531 },
8532 ]),
8533 stop_timeout_ms: Some(1234),
8534 ..Default::default()
8535 }]);
8536 decl
8537 },
8538 result = Err(ErrorList::new(vec![
8539 Error::duplicate_field(DeclType::RunnerRegistration, "target_name", "dart"),
8540 Error::duplicate_field(DeclType::ResolverRegistration, "scheme", "fuchsia-pkg"),
8541 ])),
8542 },
8543 test_validate_environment_from_missing_child => {
8544 input = {
8545 let mut decl = new_component_decl();
8546 decl.environments = Some(vec![fdecl::Environment {
8547 name: Some("a".to_string()),
8548 extends: Some(fdecl::EnvironmentExtends::None),
8549 runners: Some(vec![
8550 fdecl::RunnerRegistration {
8551 source_name: Some("elf".to_string()),
8552 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8553 name: "missing".to_string(),
8554 collection: None,
8555 })),
8556 target_name: Some("elf".to_string()),
8557 ..Default::default()
8558 },
8559 ]),
8560 resolvers: Some(vec![
8561 fdecl::ResolverRegistration {
8562 resolver: Some("pkg_resolver".to_string()),
8563 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8564 name: "missing".to_string(),
8565 collection: None,
8566 })),
8567 scheme: Some("fuchsia-pkg".to_string()),
8568 ..Default::default()
8569 },
8570 ]),
8571 stop_timeout_ms: Some(1234),
8572 ..Default::default()
8573 }]);
8574 decl
8575 },
8576 result = Err(ErrorList::new(vec![
8577 Error::invalid_child(DeclType::RunnerRegistration, "source", "missing"),
8578 Error::invalid_child(DeclType::ResolverRegistration, "source", "missing"),
8579 ])),
8580 },
8581 test_validate_environment_runner_child_cycle => {
8582 input = {
8583 let mut decl = new_component_decl();
8584 decl.environments = Some(vec![fdecl::Environment {
8585 name: Some("env".to_string()),
8586 extends: Some(fdecl::EnvironmentExtends::None),
8587 runners: Some(vec![
8588 fdecl::RunnerRegistration {
8589 source_name: Some("elf".to_string()),
8590 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8591 name: "child".to_string(),
8592 collection: None,
8593 })),
8594 target_name: Some("elf".to_string()),
8595 ..Default::default()
8596 },
8597 ]),
8598 resolvers: None,
8599 stop_timeout_ms: Some(1234),
8600 ..Default::default()
8601 }]);
8602 decl.children = Some(vec![fdecl::Child {
8603 name: Some("child".to_string()),
8604 startup: Some(fdecl::StartupMode::Lazy),
8605 on_terminate: None,
8606 url: Some("fuchsia-pkg://child".to_string()),
8607 environment: Some("env".to_string()),
8608 ..Default::default()
8609 }]);
8610 decl
8611 },
8612 result = Err(ErrorList::new(vec![
8613 Error::dependency_cycle(
8614 directed_graph::Error::CyclesDetected([vec!["child child", "environment env", "child child"]].iter().cloned().collect()).format_cycle()
8615 ),
8616 ])),
8617 },
8618 test_validate_environment_resolver_child_cycle => {
8619 input = {
8620 let mut decl = new_component_decl();
8621 decl.environments = Some(vec![fdecl::Environment {
8622 name: Some("env".to_string()),
8623 extends: Some(fdecl::EnvironmentExtends::None),
8624 runners: None,
8625 resolvers: Some(vec![
8626 fdecl::ResolverRegistration {
8627 resolver: Some("pkg_resolver".to_string()),
8628 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8629 name: "child".to_string(),
8630 collection: None,
8631 })),
8632 scheme: Some("fuchsia-pkg".to_string()),
8633 ..Default::default()
8634 },
8635 ]),
8636 stop_timeout_ms: Some(1234),
8637 ..Default::default()
8638 }]);
8639 decl.children = Some(vec![fdecl::Child {
8640 name: Some("child".to_string()),
8641 startup: Some(fdecl::StartupMode::Lazy),
8642 on_terminate: None,
8643 url: Some("fuchsia-pkg://child".to_string()),
8644 environment: Some("env".to_string()),
8645 ..Default::default()
8646 }]);
8647 decl
8648 },
8649 result = Err(ErrorList::new(vec![
8650 Error::dependency_cycle(
8651 directed_graph::Error::CyclesDetected([vec!["child child", "environment env", "child child"]].iter().cloned().collect()).format_cycle()
8652 ),
8653 ])),
8654 },
8655 test_validate_environment_resolver_multiple_children_cycle => {
8656 input = {
8657 let mut decl = new_component_decl();
8658 decl.environments = Some(vec![fdecl::Environment {
8659 name: Some("env".to_string()),
8660 extends: Some(fdecl::EnvironmentExtends::None),
8661 runners: None,
8662 resolvers: Some(vec![
8663 fdecl::ResolverRegistration {
8664 resolver: Some("pkg_resolver".to_string()),
8665 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8666 name: "a".to_string(),
8667 collection: None,
8668 })),
8669 scheme: Some("fuchsia-pkg".to_string()),
8670 ..Default::default()
8671 },
8672 ]),
8673 stop_timeout_ms: Some(1234),
8674 ..Default::default()
8675 }]);
8676 decl.children = Some(vec![
8677 fdecl::Child {
8678 name: Some("a".to_string()),
8679 startup: Some(fdecl::StartupMode::Lazy),
8680 on_terminate: None,
8681 url: Some("fuchsia-pkg://child-a".to_string()),
8682 environment: None,
8683 ..Default::default()
8684 },
8685 fdecl::Child {
8686 name: Some("b".to_string()),
8687 startup: Some(fdecl::StartupMode::Lazy),
8688 on_terminate: None,
8689 url: Some("fuchsia-pkg://child-b".to_string()),
8690 environment: Some("env".to_string()),
8691 ..Default::default()
8692 },
8693 ]);
8694 decl.offers = Some(vec![fdecl::Offer::Service(fdecl::OfferService {
8695 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8696 name: "b".to_string(),
8697 collection: None,
8698 })),
8699 source_name: Some("thing".to_string()),
8700 target: Some(fdecl::Ref::Child(
8701 fdecl::ChildRef {
8702 name: "a".to_string(),
8703 collection: None,
8704 }
8705 )),
8706 target_name: Some("thing".to_string()),
8707 ..Default::default()
8708 })]);
8709 decl
8710 },
8711 result = Err(ErrorList::new(vec![
8712 Error::dependency_cycle(
8713 directed_graph::Error::CyclesDetected([vec!["child a", "environment env", "child b", "child a"]].iter().cloned().collect()).format_cycle()
8714 ),
8715 ])),
8716 },
8717 test_validate_environment_debug_empty => {
8718 input = {
8719 let mut decl = new_component_decl();
8720 decl.environments = Some(vec![
8721 fdecl::Environment {
8722 name: Some("a".to_string()),
8723 extends: Some(fdecl::EnvironmentExtends::None),
8724 stop_timeout_ms: Some(2),
8725 debug_capabilities:Some(vec![
8726 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8727 source: None,
8728 source_name: None,
8729 target_name: None,
8730 ..Default::default()
8731 }),
8732 ]),
8733 ..Default::default()
8734 }]);
8735 decl
8736 },
8737 result = Err(ErrorList::new(vec![
8738 Error::missing_field(DeclType::DebugProtocolRegistration, "source"),
8739 Error::missing_field(DeclType::DebugProtocolRegistration, "source_name"),
8740 Error::missing_field(DeclType::DebugProtocolRegistration, "target_name"),
8741 ])),
8742 },
8743 test_validate_environment_debug_log_identifier => {
8744 input = {
8745 let mut decl = new_component_decl();
8746 decl.environments = Some(vec![
8747 fdecl::Environment {
8748 name: Some("a".to_string()),
8749 extends: Some(fdecl::EnvironmentExtends::None),
8750 stop_timeout_ms: Some(2),
8751 debug_capabilities:Some(vec![
8752 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8753 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8754 name: "a".repeat(256),
8755 collection: None,
8756 })),
8757 source_name: Some(format!("{}", "a".repeat(256))),
8758 target_name: Some(format!("{}", "b".repeat(256))),
8759 ..Default::default()
8760 }),
8761 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8762 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8763 source_name: Some("a".to_string()),
8764 target_name: Some(format!("{}", "b".repeat(256))),
8765 ..Default::default()
8766 }),
8767 ]),
8768 ..Default::default()
8769 }]);
8770 decl
8771 },
8772 result = Err(ErrorList::new(vec![
8773 Error::field_too_long(DeclType::DebugProtocolRegistration, "source.child.name"),
8774 Error::field_too_long(DeclType::DebugProtocolRegistration, "source_name"),
8775 Error::field_too_long(DeclType::DebugProtocolRegistration, "target_name"),
8776 Error::field_too_long(DeclType::DebugProtocolRegistration, "target_name"),
8777 ])),
8778 },
8779 test_validate_environment_debug_log_extraneous => {
8780 input = {
8781 let mut decl = new_component_decl();
8782 decl.environments = Some(vec![
8783 fdecl::Environment {
8784 name: Some("a".to_string()),
8785 extends: Some(fdecl::EnvironmentExtends::None),
8786 stop_timeout_ms: Some(2),
8787 debug_capabilities:Some(vec![
8788 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8789 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8790 name: "logger".to_string(),
8791 collection: Some("modular".to_string()),
8792 })),
8793 source_name: Some("fuchsia.logger.Log".to_string()),
8794 target_name: Some("fuchsia.logger.Log".to_string()),
8795 ..Default::default()
8796 }),
8797 ]),
8798 ..Default::default()
8799 }]);
8800 decl
8801 },
8802 result = Err(ErrorList::new(vec![
8803 Error::extraneous_field(DeclType::DebugProtocolRegistration, "source.child.collection"),
8804 ])),
8805 },
8806 test_validate_environment_debug_log_invalid_identifiers => {
8807 input = {
8808 let mut decl = new_component_decl();
8809 decl.environments = Some(vec![
8810 fdecl::Environment {
8811 name: Some("a".to_string()),
8812 extends: Some(fdecl::EnvironmentExtends::None),
8813 stop_timeout_ms: Some(2),
8814 debug_capabilities:Some(vec![
8815 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8816 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8817 name: "^bad".to_string(),
8818 collection: None,
8819 })),
8820 source_name: Some("foo/".to_string()),
8821 target_name: Some("/".to_string()),
8822 ..Default::default()
8823 }),
8824 ]),
8825 ..Default::default()
8826 }]);
8827 decl
8828 },
8829 result = Err(ErrorList::new(vec![
8830 Error::invalid_field(DeclType::DebugProtocolRegistration, "source.child.name"),
8831 Error::invalid_field(DeclType::DebugProtocolRegistration, "source_name"),
8832 Error::invalid_field(DeclType::DebugProtocolRegistration, "target_name"),
8833 ])),
8834 },
8835 test_validate_environment_debug_log_invalid_child => {
8836 input = {
8837 let mut decl = new_component_decl();
8838 decl.environments = Some(vec![
8839 fdecl::Environment {
8840 name: Some("a".to_string()),
8841 extends: Some(fdecl::EnvironmentExtends::None),
8842 stop_timeout_ms: Some(2),
8843 debug_capabilities:Some(vec![
8844 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8845 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8846 name: "logger".to_string(),
8847 collection: None,
8848 })),
8849 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
8850 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
8851 ..Default::default()
8852 }),
8853 ]),
8854 ..Default::default()
8855 }]);
8856 decl.children = Some(vec![
8857 fdecl::Child {
8858 name: Some("netstack".to_string()),
8859 url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
8860 startup: Some(fdecl::StartupMode::Lazy),
8861 on_terminate: None,
8862 environment: None,
8863 ..Default::default()
8864 },
8865 ]);
8866 decl
8867 },
8868 result = Err(ErrorList::new(vec![
8869 Error::invalid_child(DeclType::DebugProtocolRegistration, "source", "logger"),
8870
8871 ])),
8872 },
8873 test_validate_environment_debug_source_capability => {
8874 input = {
8875 let mut decl = new_component_decl();
8876 decl.environments = Some(vec![
8877 fdecl::Environment {
8878 name: Some("a".to_string()),
8879 extends: Some(fdecl::EnvironmentExtends::None),
8880 stop_timeout_ms: Some(2),
8881 debug_capabilities:Some(vec![
8882 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8883 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
8884 name: "storage".to_string(),
8885 })),
8886 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
8887 target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
8888 ..Default::default()
8889 }),
8890 ]),
8891 ..Default::default()
8892 }]);
8893 decl
8894 },
8895 result = Err(ErrorList::new(vec![
8896 Error::invalid_field(DeclType::DebugProtocolRegistration, "source"),
8897 ])),
8898 },
8899
8900 test_validate_children_empty => {
8902 input = {
8903 let mut decl = new_component_decl();
8904 decl.children = Some(vec![fdecl::Child{
8905 name: None,
8906 url: None,
8907 startup: None,
8908 on_terminate: None,
8909 environment: None,
8910 ..Default::default()
8911 }]);
8912 decl
8913 },
8914 result = Err(ErrorList::new(vec![
8915 Error::missing_field(DeclType::Child, "name"),
8916 Error::missing_field(DeclType::Child, "url"),
8917 Error::missing_field(DeclType::Child, "startup"),
8918 ])),
8920 },
8921 test_validate_children_invalid_identifiers => {
8922 input = {
8923 let mut decl = new_component_decl();
8924 decl.children = Some(vec![fdecl::Child{
8925 name: Some("^bad".to_string()),
8926 url: Some("scheme://invalid-port:99999999/path#frag".to_string()),
8927 startup: Some(fdecl::StartupMode::Lazy),
8928 on_terminate: None,
8929 environment: None,
8930 ..Default::default()
8931 }]);
8932 decl
8933 },
8934 result = Err(ErrorList::new(vec![
8935 Error::invalid_field(DeclType::Child, "name"),
8936 Error::invalid_url(DeclType::Child, "url", "\"scheme://invalid-port:99999999/path#frag\": Malformed URL: InvalidPort."),
8937 ])),
8938 },
8939 test_validate_children_long_identifiers => {
8940 input = {
8941 let mut decl = new_component_decl();
8942 decl.children = Some(vec![fdecl::Child{
8943 name: Some("a".repeat(1025)),
8944 url: Some(format!("fuchsia-pkg://{}", "a".repeat(4083))),
8945 startup: Some(fdecl::StartupMode::Lazy),
8946 on_terminate: None,
8947 environment: Some("a".repeat(1025)),
8948 ..Default::default()
8949 }]);
8950 decl
8951 },
8952 result = Err(ErrorList::new(vec![
8953 Error::field_too_long(DeclType::Child, "name"),
8954 Error::field_too_long(DeclType::Child, "url"),
8955 Error::field_too_long(DeclType::Child, "environment"),
8956 Error::invalid_environment(DeclType::Child, "environment", "a".repeat(1025)),
8957 ])),
8958 },
8959 test_validate_child_references_unknown_env => {
8960 input = {
8961 let mut decl = new_component_decl();
8962 decl.children = Some(vec![fdecl::Child{
8963 name: Some("foo".to_string()),
8964 url: Some("fuchsia-pkg://foo".to_string()),
8965 startup: Some(fdecl::StartupMode::Lazy),
8966 on_terminate: None,
8967 environment: Some("test_env".to_string()),
8968 ..Default::default()
8969 }]);
8970 decl
8971 },
8972 result = Err(ErrorList::new(vec![
8973 Error::invalid_environment(DeclType::Child, "environment", "test_env"),
8974 ])),
8975 },
8976
8977 test_validate_collections_empty => {
8979 input = {
8980 let mut decl = new_component_decl();
8981 decl.collections = Some(vec![fdecl::Collection{
8982 name: None,
8983 durability: None,
8984 environment: None,
8985 allowed_offers: None,
8986 allow_long_names: None,
8987 ..Default::default()
8988 }]);
8989 decl
8990 },
8991 result = Err(ErrorList::new(vec![
8992 Error::missing_field(DeclType::Collection, "name"),
8993 Error::missing_field(DeclType::Collection, "durability"),
8994 ])),
8995 },
8996 test_validate_collections_invalid_identifiers => {
8997 input = {
8998 let mut decl = new_component_decl();
8999 decl.collections = Some(vec![fdecl::Collection{
9000 name: Some("^bad".to_string()),
9001 durability: Some(fdecl::Durability::Transient),
9002 environment: None,
9003 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
9004 allow_long_names: None,
9005 ..Default::default()
9006 }]);
9007 decl
9008 },
9009 result = Err(ErrorList::new(vec![
9010 Error::invalid_field(DeclType::Collection, "name"),
9011 ])),
9012 },
9013 test_validate_collections_long_identifiers => {
9014 input = {
9015 let mut decl = new_component_decl();
9016 decl.collections = Some(vec![fdecl::Collection{
9017 name: Some("a".repeat(1025)),
9018 durability: Some(fdecl::Durability::Transient),
9019 environment: None,
9020 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
9021 allow_long_names: None,
9022 ..Default::default()
9023 }]);
9024 decl
9025 },
9026 result = Err(ErrorList::new(vec![
9027 Error::field_too_long(DeclType::Collection, "name"),
9028 ])),
9029 },
9030 test_validate_collection_references_unknown_env => {
9031 input = {
9032 let mut decl = new_component_decl();
9033 decl.collections = Some(vec![fdecl::Collection {
9034 name: Some("foo".to_string()),
9035 durability: Some(fdecl::Durability::Transient),
9036 environment: Some("test_env".to_string()),
9037 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
9038 allow_long_names: None,
9039 ..Default::default()
9040 }]);
9041 decl
9042 },
9043 result = Err(ErrorList::new(vec![
9044 Error::invalid_environment(DeclType::Collection, "environment", "test_env"),
9045 ])),
9046 },
9047
9048 test_validate_capabilities_empty => {
9050 input = {
9051 let mut decl = new_component_decl();
9052 decl.capabilities = Some(vec![
9053 fdecl::Capability::Service(fdecl::Service {
9054 name: None,
9055 source_path: None,
9056 ..Default::default()
9057 }),
9058 fdecl::Capability::Protocol(fdecl::Protocol {
9059 name: None,
9060 source_path: None,
9061 ..Default::default()
9062 }),
9063 fdecl::Capability::Directory(fdecl::Directory {
9064 name: None,
9065 source_path: None,
9066 rights: None,
9067 ..Default::default()
9068 }),
9069 fdecl::Capability::Storage(fdecl::Storage {
9070 name: None,
9071 source: None,
9072 backing_dir: None,
9073 subdir: None,
9074 storage_id: None,
9075 ..Default::default()
9076 }),
9077 fdecl::Capability::Runner(fdecl::Runner {
9078 name: None,
9079 source_path: None,
9080 ..Default::default()
9081 }),
9082 fdecl::Capability::Resolver(fdecl::Resolver {
9083 name: None,
9084 source_path: None,
9085 ..Default::default()
9086 }),
9087 fdecl::Capability::Dictionary(fdecl::Dictionary {
9088 ..Default::default()
9089 }),
9090 ]);
9091 decl
9092 },
9093 result = Err(ErrorList::new(vec![
9094 Error::missing_field(DeclType::Dictionary, "name"),
9095 Error::missing_field(DeclType::Service, "name"),
9096 Error::missing_field(DeclType::Service, "source_path"),
9097 Error::missing_field(DeclType::Protocol, "name"),
9098 Error::missing_field(DeclType::Protocol, "source_path"),
9099 Error::missing_field(DeclType::Directory, "name"),
9100 Error::missing_field(DeclType::Directory, "source_path"),
9101 Error::missing_field(DeclType::Directory, "rights"),
9102 Error::missing_field(DeclType::Storage, "source"),
9103 Error::missing_field(DeclType::Storage, "name"),
9104 Error::missing_field(DeclType::Storage, "storage_id"),
9105 Error::missing_field(DeclType::Storage, "backing_dir"),
9106 Error::missing_field(DeclType::Runner, "name"),
9107 Error::missing_field(DeclType::Runner, "source_path"),
9108 Error::missing_field(DeclType::Resolver, "name"),
9109 Error::missing_field(DeclType::Resolver, "source_path"),
9110 ])),
9111 },
9112 test_validate_capabilities_invalid_identifiers => {
9113 input = {
9114 let mut decl = new_component_decl();
9115 decl.capabilities = Some(vec![
9116 fdecl::Capability::Service(fdecl::Service {
9117 name: Some("^bad".to_string()),
9118 source_path: Some("&bad".to_string()),
9119 ..Default::default()
9120 }),
9121 fdecl::Capability::Protocol(fdecl::Protocol {
9122 name: Some("^bad".to_string()),
9123 source_path: Some("&bad".to_string()),
9124 ..Default::default()
9125 }),
9126 fdecl::Capability::Directory(fdecl::Directory {
9127 name: Some("^bad".to_string()),
9128 source_path: Some("&bad".to_string()),
9129 rights: Some(fio::Operations::CONNECT),
9130 ..Default::default()
9131 }),
9132 fdecl::Capability::Storage(fdecl::Storage {
9133 name: Some("^bad".to_string()),
9134 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9135 name: "/bad".to_string()
9136 })),
9137 backing_dir: Some("&bad".to_string()),
9138 subdir: None,
9139 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9140 ..Default::default()
9141 }),
9142 fdecl::Capability::Runner(fdecl::Runner {
9143 name: Some("^bad".to_string()),
9144 source_path: Some("&bad".to_string()),
9145 ..Default::default()
9146 }),
9147 fdecl::Capability::Resolver(fdecl::Resolver {
9148 name: Some("^bad".to_string()),
9149 source_path: Some("&bad".to_string()),
9150 ..Default::default()
9151 }),
9152 fdecl::Capability::Dictionary(fdecl::Dictionary {
9153 name: Some("^bad".to_string()),
9154 ..Default::default()
9155 }),
9156 ]);
9157 decl
9158 },
9159 result = Err(ErrorList::new(vec![
9160 Error::invalid_field(DeclType::Dictionary, "name"),
9161 Error::invalid_field(DeclType::Service, "name"),
9162 Error::invalid_field(DeclType::Service, "source_path"),
9163 Error::invalid_field(DeclType::Protocol, "name"),
9164 Error::invalid_field(DeclType::Protocol, "source_path"),
9165 Error::invalid_field(DeclType::Directory, "name"),
9166 Error::invalid_field(DeclType::Directory, "source_path"),
9167 Error::invalid_field(DeclType::Storage, "source"),
9168 Error::invalid_field(DeclType::Storage, "name"),
9169 Error::invalid_field(DeclType::Storage, "backing_dir"),
9170 Error::invalid_field(DeclType::Runner, "name"),
9171 Error::invalid_field(DeclType::Runner, "source_path"),
9172 Error::invalid_field(DeclType::Resolver, "name"),
9173 Error::invalid_field(DeclType::Resolver, "source_path"),
9174 ])),
9175 },
9176 test_validate_capabilities_invalid_child => {
9177 input = {
9178 let mut decl = new_component_decl();
9179 decl.capabilities = Some(vec![
9180 fdecl::Capability::Storage(fdecl::Storage {
9181 name: Some("foo".to_string()),
9182 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9183 name: "invalid".to_string(),
9184 })),
9185 backing_dir: Some("foo".to_string()),
9186 subdir: None,
9187 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9188 ..Default::default()
9189 }),
9190 ]);
9191 decl
9192 },
9193 result = Err(ErrorList::new(vec![
9194 Error::invalid_field(DeclType::Storage, "source"),
9195 ])),
9196 },
9197 test_validate_capabilities_long_identifiers => {
9198 input = {
9199 let mut decl = new_component_decl();
9200 decl.capabilities = Some(vec![
9201 fdecl::Capability::Service(fdecl::Service {
9202 name: Some("a".repeat(256)),
9203 source_path: Some("/c".repeat(2048)),
9204 ..Default::default()
9205 }),
9206 fdecl::Capability::Protocol(fdecl::Protocol {
9207 name: Some("a".repeat(256)),
9208 source_path: Some("/c".repeat(2048)),
9209 ..Default::default()
9210 }),
9211 fdecl::Capability::Directory(fdecl::Directory {
9212 name: Some("a".repeat(256)),
9213 source_path: Some("/c".repeat(2048)),
9214 rights: Some(fio::Operations::CONNECT),
9215 ..Default::default()
9216 }),
9217 fdecl::Capability::Storage(fdecl::Storage {
9218 name: Some("a".repeat(256)),
9219 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
9220 name: "b".repeat(256),
9221 collection: None,
9222 })),
9223 backing_dir: Some(format!("{}", "c".repeat(256))),
9224 subdir: None,
9225 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9226 ..Default::default()
9227 }),
9228 fdecl::Capability::Runner(fdecl::Runner {
9229 name: Some("a".repeat(256)),
9230 source_path: Some("/c".repeat(2048)),
9231 ..Default::default()
9232 }),
9233 fdecl::Capability::Resolver(fdecl::Resolver {
9234 name: Some("a".repeat(256)),
9235 source_path: Some("/c".repeat(2048)),
9236 ..Default::default()
9237 }),
9238 fdecl::Capability::Dictionary(fdecl::Dictionary {
9239 name: Some("a".repeat(256)),
9240 ..Default::default()
9241 }),
9242 ]);
9243 decl
9244 },
9245 result = Err(ErrorList::new(vec![
9246 Error::field_too_long(DeclType::Dictionary, "name"),
9247 Error::field_too_long(DeclType::Service, "name"),
9248 Error::field_too_long(DeclType::Service, "source_path"),
9249 Error::field_too_long(DeclType::Protocol, "name"),
9250 Error::field_too_long(DeclType::Protocol, "source_path"),
9251 Error::field_too_long(DeclType::Directory, "name"),
9252 Error::field_too_long(DeclType::Directory, "source_path"),
9253 Error::field_too_long(DeclType::Storage, "source.child.name"),
9254 Error::field_too_long(DeclType::Storage, "name"),
9255 Error::field_too_long(DeclType::Storage, "backing_dir"),
9256 Error::field_too_long(DeclType::Runner, "name"),
9257 Error::field_too_long(DeclType::Runner, "source_path"),
9258 Error::field_too_long(DeclType::Resolver, "name"),
9259 Error::field_too_long(DeclType::Resolver, "source_path"),
9260 ])),
9261 },
9262 test_validate_capabilities_duplicate_name => {
9263 input = {
9264 let mut decl = new_component_decl();
9265 decl.capabilities = Some(vec![
9266 fdecl::Capability::Service(fdecl::Service {
9267 name: Some("service".to_string()),
9268 source_path: Some("/service".to_string()),
9269 ..Default::default()
9270 }),
9271 fdecl::Capability::Service(fdecl::Service {
9272 name: Some("service".to_string()),
9273 source_path: Some("/service".to_string()),
9274 ..Default::default()
9275 }),
9276 fdecl::Capability::Protocol(fdecl::Protocol {
9277 name: Some("protocol".to_string()),
9278 source_path: Some("/protocol".to_string()),
9279 ..Default::default()
9280 }),
9281 fdecl::Capability::Protocol(fdecl::Protocol {
9282 name: Some("protocol".to_string()),
9283 source_path: Some("/protocol".to_string()),
9284 ..Default::default()
9285 }),
9286 fdecl::Capability::Directory(fdecl::Directory {
9287 name: Some("directory".to_string()),
9288 source_path: Some("/directory".to_string()),
9289 rights: Some(fio::Operations::CONNECT),
9290 ..Default::default()
9291 }),
9292 fdecl::Capability::Directory(fdecl::Directory {
9293 name: Some("directory".to_string()),
9294 source_path: Some("/directory".to_string()),
9295 rights: Some(fio::Operations::CONNECT),
9296 ..Default::default()
9297 }),
9298 fdecl::Capability::Storage(fdecl::Storage {
9299 name: Some("storage".to_string()),
9300 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
9301 backing_dir: Some("directory".to_string()),
9302 subdir: None,
9303 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9304 ..Default::default()
9305 }),
9306 fdecl::Capability::Storage(fdecl::Storage {
9307 name: Some("storage".to_string()),
9308 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
9309 backing_dir: Some("directory".to_string()),
9310 subdir: None,
9311 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9312 ..Default::default()
9313 }),
9314 fdecl::Capability::Runner(fdecl::Runner {
9315 name: Some("runner".to_string()),
9316 source_path: Some("/runner".to_string()),
9317 ..Default::default()
9318 }),
9319 fdecl::Capability::Runner(fdecl::Runner {
9320 name: Some("runner".to_string()),
9321 source_path: Some("/runner".to_string()),
9322 ..Default::default()
9323 }),
9324 fdecl::Capability::Resolver(fdecl::Resolver {
9325 name: Some("resolver".to_string()),
9326 source_path: Some("/resolver".to_string()),
9327 ..Default::default()
9328 }),
9329 fdecl::Capability::Resolver(fdecl::Resolver {
9330 name: Some("resolver".to_string()),
9331 source_path: Some("/resolver".to_string()),
9332 ..Default::default()
9333 }),
9334 fdecl::Capability::Dictionary(fdecl::Dictionary {
9335 name: Some("dictionary".to_string()),
9336 ..Default::default()
9337 }),
9338 fdecl::Capability::Dictionary(fdecl::Dictionary {
9339 name: Some("dictionary".to_string()),
9340 ..Default::default()
9341 }),
9342 ]);
9343 decl
9344 },
9345 result = Err(ErrorList::new(vec![
9346 Error::duplicate_field(DeclType::Dictionary, "name", "dictionary"),
9347 Error::duplicate_field(DeclType::Service, "name", "service"),
9348 Error::duplicate_field(DeclType::Protocol, "name", "protocol"),
9349 Error::duplicate_field(DeclType::Directory, "name", "directory"),
9350 Error::duplicate_field(DeclType::Storage, "name", "storage"),
9351 Error::duplicate_field(DeclType::Runner, "name", "runner"),
9352 Error::duplicate_field(DeclType::Resolver, "name", "resolver"),
9353 ])),
9354 },
9355 test_validate_invalid_service_aggregation_conflicting_filter => {
9356 input = {
9357 let mut decl = new_component_decl();
9358 decl.offers = Some(vec![
9359 fdecl::Offer::Service(fdecl::OfferService {
9360 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9361 name: "coll_a".to_string()
9362 })),
9363 source_name: Some("fuchsia.logger.Log".to_string()),
9364 target: Some(fdecl::Ref::Child(
9365 fdecl::ChildRef {
9366 name: "child_c".to_string(),
9367 collection: None,
9368 }
9369 )),
9370 target_name: Some("fuchsia.logger.Log1".to_string()),
9371 source_instance_filter: Some(vec!["default".to_string()]),
9372 ..Default::default()
9373 }),
9374 fdecl::Offer::Service(fdecl::OfferService {
9375 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9376 name: "coll_b".to_string()
9377 })),
9378 source_name: Some("fuchsia.logger.Log".to_string()),
9379 target: Some(fdecl::Ref::Child(
9380 fdecl::ChildRef {
9381 name: "child_c".to_string(),
9382 collection: None,
9383 }
9384 )),
9385 target_name: Some("fuchsia.logger.Log1".to_string()),
9386 source_instance_filter: Some(vec!["default".to_string()]),
9387 ..Default::default()
9388 }),
9389 ]);
9390 decl.collections = Some(vec![
9391 fdecl::Collection {
9392 name: Some("coll_a".to_string()),
9393 durability: Some(fdecl::Durability::Transient),
9394 ..Default::default()
9395 },
9396 fdecl::Collection {
9397 name: Some("coll_b".to_string()),
9398 durability: Some(fdecl::Durability::Transient),
9399 ..Default::default()
9400 },
9401 ]);
9402 decl.children = Some(vec![
9403 fdecl::Child {
9404 name: Some("child_c".to_string()),
9405 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9406 startup: Some(fdecl::StartupMode::Lazy),
9407 ..Default::default()
9408 },
9409 ]);
9410 decl
9411 },
9412 result = Err(ErrorList::new(vec![
9413 Error::invalid_aggregate_offer("Conflicting source_instance_filter in aggregate \
9414 service offer, instance_name 'default' seen in filter lists multiple times"),
9415 ])),
9416 },
9417
9418 test_validate_invalid_service_aggregation_conflicting_source_name => {
9419 input = {
9420 let mut decl = new_component_decl();
9421 decl.offers = Some(vec![
9422 fdecl::Offer::Service(fdecl::OfferService {
9423 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9424 name: "coll_a".into()
9425 })),
9426 source_name: Some("fuchsia.logger.Log".to_string()),
9427 target: Some(fdecl::Ref::Child(
9428 fdecl::ChildRef {
9429 name: "child_c".to_string(),
9430 collection: None,
9431 }
9432 )),
9433 target_name: Some("fuchsia.logger.Log2".to_string()),
9434 source_instance_filter: Some(vec!["default2".to_string()]),
9435 ..Default::default()
9436 }),
9437 fdecl::Offer::Service(fdecl::OfferService {
9438 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9439 name: "coll_b".into()
9440 })),
9441 source_name: Some("fuchsia.logger.LogAlt".to_string()),
9442 target: Some(fdecl::Ref::Child(
9443 fdecl::ChildRef {
9444 name: "child_c".to_string(),
9445 collection: None,
9446 }
9447 )),
9448 target_name: Some("fuchsia.logger.Log2".to_string()),
9449 source_instance_filter: Some(vec!["default".to_string()]),
9450 ..Default::default()
9451 })
9452 ]);
9453 decl.collections = Some(vec![
9454 fdecl::Collection {
9455 name: Some("coll_a".to_string()),
9456 durability: Some(fdecl::Durability::Transient),
9457 ..Default::default()
9458 },
9459 fdecl::Collection {
9460 name: Some("coll_b".to_string()),
9461 durability: Some(fdecl::Durability::Transient),
9462 ..Default::default()
9463 },
9464 ]);
9465 decl.children = Some(vec![
9466 fdecl::Child {
9467 name: Some("child_c".to_string()),
9468 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9469 startup: Some(fdecl::StartupMode::Lazy),
9470 on_terminate: None,
9471 environment: None,
9472 ..Default::default()
9473 },
9474 ]);
9475 decl
9476 },
9477 result = Err(ErrorList::new(vec![
9478 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."),
9479 ])),
9480 },
9481
9482 test_validate_resolvers_missing_from_offer => {
9483 input = {
9484 let mut decl = new_component_decl();
9485 decl.offers = Some(vec![fdecl::Offer::Resolver(fdecl::OfferResolver {
9486 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
9487 source_name: Some("a".to_string()),
9488 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
9489 target_name: Some("a".to_string()),
9490 ..Default::default()
9491 })]);
9492 decl.children = Some(vec![fdecl::Child {
9493 name: Some("child".to_string()),
9494 url: Some("test:///child".to_string()),
9495 startup: Some(fdecl::StartupMode::Eager),
9496 on_terminate: None,
9497 environment: None,
9498 ..Default::default()
9499 }]);
9500 decl
9501 },
9502 result = Err(ErrorList::new(vec![
9503 Error::invalid_capability(DeclType::OfferResolver, "source", "a"),
9504 ])),
9505 },
9506 test_validate_resolvers_missing_from_expose => {
9507 input = {
9508 let mut decl = new_component_decl();
9509 decl.exposes = Some(vec![fdecl::Expose::Resolver(fdecl::ExposeResolver {
9510 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
9511 source_name: Some("a".to_string()),
9512 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
9513 target_name: Some("a".to_string()),
9514 ..Default::default()
9515 })]);
9516 decl
9517 },
9518 result = Err(ErrorList::new(vec![
9519 Error::invalid_capability(DeclType::ExposeResolver, "source", "a"),
9520 ])),
9521 },
9522
9523 test_validate_config_missing_config => {
9524 input = {
9525 let mut decl = new_component_decl();
9526 decl.config = Some(fdecl::ConfigSchema{
9527 fields: None,
9528 checksum: None,
9529 value_source: None,
9530 ..Default::default()
9531 });
9532 decl
9533 },
9534 result = Err(ErrorList::new(vec![
9535 Error::missing_field(DeclType::ConfigSchema, "fields"),
9536 Error::missing_field(DeclType::ConfigSchema, "checksum"),
9537 Error::missing_field(DeclType::ConfigSchema, "value_source"),
9538 ])),
9539 },
9540
9541 test_validate_config_missing_config_field => {
9542 input = {
9543 let mut decl = new_component_decl();
9544 decl.config = Some(fdecl::ConfigSchema{
9545 fields: Some(vec![
9546 fdecl::ConfigField {
9547 key: None,
9548 type_: None,
9549 ..Default::default()
9550 }
9551 ]),
9552 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9553 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9554 ..Default::default()
9555 });
9556 decl
9557 },
9558 result = Err(ErrorList::new(vec![
9559 Error::missing_field(DeclType::ConfigField, "key"),
9560 Error::missing_field(DeclType::ConfigField, "value_type"),
9561 ])),
9562 },
9563
9564 test_validate_config_bool => {
9565 input = {
9566 let mut decl = new_component_decl();
9567 decl.config = Some(fdecl::ConfigSchema{
9568 fields: Some(vec![
9569 fdecl::ConfigField {
9570 key: Some("test".to_string()),
9571 type_: Some(fdecl::ConfigType {
9572 layout: fdecl::ConfigTypeLayout::Bool,
9573 parameters: Some(vec![]),
9574 constraints: vec![]
9575 }),
9576 ..Default::default()
9577 }
9578 ]),
9579 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9580 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9581 ..Default::default()
9582 });
9583 decl
9584 },
9585 result = Ok(()),
9586 },
9587
9588 test_validate_config_bool_extra_constraint => {
9589 input = {
9590 let mut decl = new_component_decl();
9591 decl.config = Some(fdecl::ConfigSchema{
9592 fields: Some(vec![
9593 fdecl::ConfigField {
9594 key: Some("test".to_string()),
9595 type_: Some(fdecl::ConfigType {
9596 layout: fdecl::ConfigTypeLayout::Bool,
9597 parameters: Some(vec![]),
9598 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9599 }),
9600 ..Default::default()
9601 }
9602 ]),
9603 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9604 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9605 ..Default::default()
9606 });
9607 decl
9608 },
9609 result = Err(ErrorList::new(vec![
9610 Error::extraneous_field(DeclType::ConfigType, "constraints")
9611 ])),
9612 },
9613
9614 test_validate_config_bool_missing_parameters => {
9615 input = {
9616 let mut decl = new_component_decl();
9617 decl.config = Some(fdecl::ConfigSchema{
9618 fields: Some(vec![
9619 fdecl::ConfigField {
9620 key: Some("test".to_string()),
9621 type_: Some(fdecl::ConfigType {
9622 layout: fdecl::ConfigTypeLayout::Bool,
9623 parameters: None,
9624 constraints: vec![]
9625 }),
9626 ..Default::default()
9627 }
9628 ]),
9629 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9630 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9631 ..Default::default()
9632 });
9633 decl
9634 },
9635 result = Err(ErrorList::new(vec![
9636 Error::missing_field(DeclType::ConfigType, "parameters")
9637 ])),
9638 },
9639
9640 test_validate_config_string => {
9641 input = {
9642 let mut decl = new_component_decl();
9643 decl.config = Some(fdecl::ConfigSchema{
9644 fields: Some(vec![
9645 fdecl::ConfigField {
9646 key: Some("test".to_string()),
9647 type_: Some(fdecl::ConfigType {
9648 layout: fdecl::ConfigTypeLayout::String,
9649 parameters: Some(vec![]),
9650 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9651 }),
9652 ..Default::default()
9653 }
9654 ]),
9655 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9656 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9657 ..Default::default()
9658 });
9659 decl
9660 },
9661 result = Ok(()),
9662 },
9663
9664 test_validate_config_string_missing_parameter => {
9665 input = {
9666 let mut decl = new_component_decl();
9667 decl.config = Some(fdecl::ConfigSchema{
9668 fields: Some(vec![
9669 fdecl::ConfigField {
9670 key: Some("test".to_string()),
9671 type_: Some(fdecl::ConfigType {
9672 layout: fdecl::ConfigTypeLayout::String,
9673 parameters: None,
9674 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9675 }),
9676 ..Default::default()
9677 }
9678 ]),
9679 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9680 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9681 ..Default::default()
9682 });
9683 decl
9684 },
9685 result = Err(ErrorList::new(vec![
9686 Error::missing_field(DeclType::ConfigType, "parameters")
9687 ])),
9688 },
9689
9690 test_validate_config_string_missing_constraint => {
9691 input = {
9692 let mut decl = new_component_decl();
9693 decl.config = Some(fdecl::ConfigSchema{
9694 fields: Some(vec![
9695 fdecl::ConfigField {
9696 key: Some("test".to_string()),
9697 type_: Some(fdecl::ConfigType {
9698 layout: fdecl::ConfigTypeLayout::String,
9699 parameters: Some(vec![]),
9700 constraints: vec![]
9701 }),
9702 ..Default::default()
9703 }
9704 ]),
9705 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9706 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9707 ..Default::default()
9708 });
9709 decl
9710 },
9711 result = Err(ErrorList::new(vec![
9712 Error::missing_field(DeclType::ConfigType, "constraints")
9713 ])),
9714 },
9715
9716 test_validate_config_string_extra_constraint => {
9717 input = {
9718 let mut decl = new_component_decl();
9719 decl.config = Some(fdecl::ConfigSchema{
9720 fields: Some(vec![
9721 fdecl::ConfigField {
9722 key: Some("test".to_string()),
9723 type_: Some(fdecl::ConfigType {
9724 layout: fdecl::ConfigTypeLayout::String,
9725 parameters: Some(vec![]),
9726 constraints: vec![fdecl::LayoutConstraint::MaxSize(10), fdecl::LayoutConstraint::MaxSize(10)]
9727 }),
9728 ..Default::default()
9729 }
9730 ]),
9731 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9732 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9733 ..Default::default()
9734 });
9735 decl
9736 },
9737 result = Err(ErrorList::new(vec![
9738 Error::extraneous_field(DeclType::ConfigType, "constraints")
9739 ])),
9740 },
9741
9742 test_validate_config_vector_bool => {
9743 input = {
9744 let mut decl = new_component_decl();
9745 decl.config = Some(fdecl::ConfigSchema{
9746 fields: Some(vec![
9747 fdecl::ConfigField {
9748 key: Some("test".to_string()),
9749 type_: Some(fdecl::ConfigType {
9750 layout: fdecl::ConfigTypeLayout::Vector,
9751 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9752 layout: fdecl::ConfigTypeLayout::Bool,
9753 parameters: Some(vec![]),
9754 constraints: vec![],
9755 })]),
9756 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9757 }),
9758 ..Default::default()
9759 }
9760 ]),
9761 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9762 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9763 ..Default::default()
9764 });
9765 decl
9766 },
9767 result = Ok(()),
9768 },
9769
9770 test_validate_config_vector_extra_parameter => {
9771 input = {
9772 let mut decl = new_component_decl();
9773 decl.config = Some(fdecl::ConfigSchema{
9774 fields: Some(vec![
9775 fdecl::ConfigField {
9776 key: Some("test".to_string()),
9777 type_: Some(fdecl::ConfigType {
9778 layout: fdecl::ConfigTypeLayout::Vector,
9779 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9780 layout: fdecl::ConfigTypeLayout::Bool,
9781 parameters: Some(vec![]),
9782 constraints: vec![],
9783 }), fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9784 layout: fdecl::ConfigTypeLayout::Uint8,
9785 parameters: Some(vec![]),
9786 constraints: vec![],
9787 })]),
9788 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9789 }),
9790 ..Default::default()
9791 }
9792 ]),
9793 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9794 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9795 ..Default::default()
9796 });
9797 decl
9798 },
9799 result = Err(ErrorList::new(vec![
9800 Error::extraneous_field(DeclType::ConfigType, "parameters")
9801 ])),
9802 },
9803
9804 test_validate_config_vector_missing_parameter => {
9805 input = {
9806 let mut decl = new_component_decl();
9807 decl.config = Some(fdecl::ConfigSchema{
9808 fields: Some(vec![
9809 fdecl::ConfigField {
9810 key: Some("test".to_string()),
9811 type_: Some(fdecl::ConfigType {
9812 layout: fdecl::ConfigTypeLayout::Vector,
9813 parameters: Some(vec![]),
9814 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9815 }),
9816 ..Default::default()
9817 }
9818 ]),
9819 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9820 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9821 ..Default::default()
9822 });
9823 decl
9824 },
9825 result = Err(ErrorList::new(vec![
9826 Error::missing_field(DeclType::ConfigType, "parameters")
9827 ])),
9828 },
9829
9830 test_validate_config_vector_string => {
9831 input = {
9832 let mut decl = new_component_decl();
9833 decl.config = Some(fdecl::ConfigSchema{
9834 fields: Some(vec![
9835 fdecl::ConfigField {
9836 key: Some("test".to_string()),
9837 type_: Some(fdecl::ConfigType {
9838 layout: fdecl::ConfigTypeLayout::Vector,
9839 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9840 layout: fdecl::ConfigTypeLayout::String,
9841 parameters: Some(vec![]),
9842 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9843 })]),
9844 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9845 }),
9846 ..Default::default()
9847 }
9848 ]),
9849 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9850 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9851 ..Default::default()
9852 });
9853 decl
9854 },
9855 result = Ok(()),
9856 },
9857
9858 test_validate_config_vector_vector => {
9859 input = {
9860 let mut decl = new_component_decl();
9861 decl.config = Some(fdecl::ConfigSchema{
9862 fields: Some(vec![
9863 fdecl::ConfigField {
9864 key: Some("test".to_string()),
9865 type_: Some(fdecl::ConfigType {
9866 layout: fdecl::ConfigTypeLayout::Vector,
9867 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9868 layout: fdecl::ConfigTypeLayout::Vector,
9869 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9870 layout: fdecl::ConfigTypeLayout::String,
9871 parameters: Some(vec![]),
9872 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9873 })]),
9874 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9875 })]),
9876 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9877 }),
9878 ..Default::default()
9879 }
9880 ]),
9881 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9882 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9883 ..Default::default()
9884 });
9885 decl
9886 },
9887 result = Err(ErrorList::new(vec![
9888 Error::nested_vector()
9889 ])),
9890 },
9891
9892 test_validate_exposes_invalid_aggregation_different_availability => {
9893 input = {
9894 let mut decl = new_component_decl();
9895 decl.exposes = Some(vec![
9896 fdecl::Expose::Service(fdecl::ExposeService {
9897 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9898 name: "coll_a".into()
9899 })),
9900 source_name: Some("fuchsia.logger.Log".to_string()),
9901 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
9902 target_name: Some("fuchsia.logger.Log".to_string()),
9903 availability: Some(fdecl::Availability::Required),
9904 ..Default::default()
9905 }),
9906 fdecl::Expose::Service(fdecl::ExposeService {
9907 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9908 name: "coll_b".into()
9909 })),
9910 source_name: Some("fuchsia.logger.Log".to_string()),
9911 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
9912 target_name: Some("fuchsia.logger.Log".to_string()),
9913 availability: Some(fdecl::Availability::Optional),
9914 ..Default::default()
9915 })
9916 ]);
9917 decl.collections = Some(vec![
9918 fdecl::Collection {
9919 name: Some("coll_a".to_string()),
9920 durability: Some(fdecl::Durability::Transient),
9921 ..Default::default()
9922 },
9923 fdecl::Collection {
9924 name: Some("coll_b".to_string()),
9925 durability: Some(fdecl::Durability::Transient),
9926 ..Default::default()
9927 },
9928 ]);
9929 decl
9930 },
9931 result = Err(ErrorList::new(vec![
9932 Error::DifferentAvailabilityInAggregation(AvailabilityList(vec![
9933 fdecl::Availability::Required,
9934 fdecl::Availability::Optional,
9935 ]))
9936 ])),
9937 },
9938
9939 test_validate_offers_invalid_aggregation_different_availability => {
9940 input = {
9941 let mut decl = new_component_decl();
9942 decl.offers = Some(vec![
9943 fdecl::Offer::Service(fdecl::OfferService {
9944 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9945 name: "coll_a".into()
9946 })),
9947 source_name: Some("fuchsia.logger.Log".to_string()),
9948 target: Some(fdecl::Ref::Child(
9949 fdecl::ChildRef {
9950 name: "child_c".to_string(),
9951 collection: None,
9952 }
9953 )),
9954 target_name: Some("fuchsia.logger.Log".to_string()),
9955 source_instance_filter: Some(vec!["default".to_string()]),
9956 availability: Some(fdecl::Availability::Required),
9957 ..Default::default()
9958 }),
9959 fdecl::Offer::Service(fdecl::OfferService {
9960 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9961 name: "coll_b".into()
9962 })),
9963 source_name: Some("fuchsia.logger.Log".to_string()),
9964 target: Some(fdecl::Ref::Child(
9965 fdecl::ChildRef {
9966 name: "child_c".to_string(),
9967 collection: None,
9968 }
9969 )),
9970 target_name: Some("fuchsia.logger.Log".to_string()),
9971 source_instance_filter: Some(vec!["a_different_default".to_string()]),
9972 availability: Some(fdecl::Availability::Optional),
9973 ..Default::default()
9974 })
9975 ]);
9976 decl.collections = Some(vec![
9977 fdecl::Collection {
9978 name: Some("coll_a".to_string()),
9979 durability: Some(fdecl::Durability::Transient),
9980 ..Default::default()
9981 },
9982 fdecl::Collection {
9983 name: Some("coll_b".to_string()),
9984 durability: Some(fdecl::Durability::Transient),
9985 ..Default::default()
9986 },
9987 ]);
9988 decl.children = Some(vec![
9989 fdecl::Child {
9990 name: Some("child_c".to_string()),
9991 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9992 startup: Some(fdecl::StartupMode::Lazy),
9993 on_terminate: None,
9994 environment: None,
9995 ..Default::default()
9996 },
9997 ]);
9998 decl
9999 },
10000 result = Err(ErrorList::new(vec![
10001 Error::DifferentAvailabilityInAggregation(AvailabilityList(vec![
10002 fdecl::Availability::Required,
10003 fdecl::Availability::Optional,
10004 ]))
10005 ])),
10006 },
10007 }
10008
10009 test_validate_capabilities! {
10010 test_validate_capabilities_individually_ok => {
10011 input = vec![
10012 fdecl::Capability::Protocol(fdecl::Protocol {
10013 name: Some("foo_svc".into()),
10014 source_path: Some("/svc/foo".into()),
10015 ..Default::default()
10016 }),
10017 fdecl::Capability::Directory(fdecl::Directory {
10018 name: Some("foo_dir".into()),
10019 source_path: Some("/foo".into()),
10020 rights: Some(fio::Operations::CONNECT),
10021 ..Default::default()
10022 }),
10023 ],
10024 as_builtin = false,
10025 result = Ok(()),
10026 },
10027 test_validate_capabilities_individually_err => {
10028 input = vec![
10029 fdecl::Capability::Protocol(fdecl::Protocol {
10030 name: None,
10031 source_path: None,
10032 ..Default::default()
10033 }),
10034 fdecl::Capability::Directory(fdecl::Directory {
10035 name: None,
10036 source_path: None,
10037 rights: None,
10038 ..Default::default()
10039 }),
10040 ],
10041 as_builtin = false,
10042 result = Err(ErrorList::new(vec![
10043 Error::missing_field(DeclType::Protocol, "name"),
10044 Error::missing_field(DeclType::Protocol, "source_path"),
10045 Error::missing_field(DeclType::Directory, "name"),
10046 Error::missing_field(DeclType::Directory, "source_path"),
10047 Error::missing_field(DeclType::Directory, "rights"),
10048 ])),
10049 },
10050 test_validate_builtin_capabilities_individually_ok => {
10051 input = vec![
10052 fdecl::Capability::Protocol(fdecl::Protocol {
10053 name: Some("foo_protocol".into()),
10054 source_path: None,
10055 ..Default::default()
10056 }),
10057 fdecl::Capability::Directory(fdecl::Directory {
10058 name: Some("foo_dir".into()),
10059 source_path: None,
10060 rights: Some(fio::Operations::CONNECT),
10061 ..Default::default()
10062 }),
10063 fdecl::Capability::Service(fdecl::Service {
10064 name: Some("foo_svc".into()),
10065 source_path: None,
10066 ..Default::default()
10067 }),
10068 fdecl::Capability::Runner(fdecl::Runner {
10069 name: Some("foo_runner".into()),
10070 source_path: None,
10071 ..Default::default()
10072 }),
10073 fdecl::Capability::Resolver(fdecl::Resolver {
10074 name: Some("foo_resolver".into()),
10075 source_path: None,
10076 ..Default::default()
10077 }),
10078 ],
10079 as_builtin = true,
10080 result = Ok(()),
10081 },
10082 test_validate_builtin_capabilities_individually_err => {
10083 input = vec![
10084 fdecl::Capability::Protocol(fdecl::Protocol {
10085 name: None,
10086 source_path: Some("/svc/foo".into()),
10087 ..Default::default()
10088 }),
10089 fdecl::Capability::Directory(fdecl::Directory {
10090 name: None,
10091 source_path: Some("/foo".into()),
10092 rights: None,
10093 ..Default::default()
10094 }),
10095 fdecl::Capability::Service(fdecl::Service {
10096 name: None,
10097 source_path: Some("/svc/foo".into()),
10098 ..Default::default()
10099 }),
10100 fdecl::Capability::Runner(fdecl::Runner {
10101 name: None,
10102 source_path: Some("/foo".into()),
10103 ..Default::default()
10104 }),
10105 fdecl::Capability::Resolver(fdecl::Resolver {
10106 name: None,
10107 source_path: Some("/foo".into()),
10108 ..Default::default()
10109 }),
10110 fdecl::Capability::Storage(fdecl::Storage {
10111 name: None,
10112 ..Default::default()
10113 }),
10114 ],
10115 as_builtin = true,
10116 result = Err(ErrorList::new(vec![
10117 Error::missing_field(DeclType::Protocol, "name"),
10118 Error::extraneous_source_path(DeclType::Protocol, "/svc/foo"),
10119 Error::missing_field(DeclType::Directory, "name"),
10120 Error::extraneous_source_path(DeclType::Directory, "/foo"),
10121 Error::missing_field(DeclType::Directory, "rights"),
10122 Error::missing_field(DeclType::Service, "name"),
10123 Error::extraneous_source_path(DeclType::Service, "/svc/foo"),
10124 Error::missing_field(DeclType::Runner, "name"),
10125 Error::extraneous_source_path(DeclType::Runner, "/foo"),
10126 Error::missing_field(DeclType::Resolver, "name"),
10127 Error::extraneous_source_path(DeclType::Resolver, "/foo"),
10128 Error::CapabilityCannotBeBuiltin(DeclType::Storage),
10129 ])),
10130 },
10131 test_validate_delivery_type_ok => {
10132 input = vec![
10133 fdecl::Capability::Protocol(fdecl::Protocol {
10134 name: Some("foo_svc1".into()),
10135 source_path: Some("/svc/foo1".into()),
10136 ..Default::default()
10137 }),
10138 fdecl::Capability::Protocol(fdecl::Protocol {
10139 name: Some("foo_svc2".into()),
10140 source_path: Some("/svc/foo2".into()),
10141 delivery: Some(fdecl::DeliveryType::Immediate),
10142 ..Default::default()
10143 }),
10144 fdecl::Capability::Protocol(fdecl::Protocol {
10145 name: Some("foo_svc3".into()),
10146 source_path: Some("/svc/foo3".into()),
10147 delivery: Some(fdecl::DeliveryType::OnReadable),
10148 ..Default::default()
10149 }),
10150 ],
10151 as_builtin = false,
10152 result = Ok(()),
10153 },
10154 test_validate_delivery_type_err => {
10155 input = vec![
10156 fdecl::Capability::Protocol(fdecl::Protocol {
10157 name: Some("foo_svc".into()),
10158 source_path: Some("/svc/foo".into()),
10159 delivery: Some(fdecl::DeliveryType::unknown()),
10160 ..Default::default()
10161 }),
10162 ],
10163 as_builtin = false,
10164 result = Err(ErrorList::new(vec![
10165 Error::invalid_field(DeclType::Protocol, "delivery"),
10166 ])),
10167 },
10168 }
10169
10170 fn generate_expose_different_source_and_availability_decl(
10173 new_expose: impl Fn(fdecl::Ref, fdecl::Availability, &str) -> fdecl::Expose,
10174 ) -> fdecl::Component {
10175 let mut decl = new_component_decl();
10176 let child = "child";
10177 decl.children = Some(vec![fdecl::Child {
10178 name: Some(child.to_string()),
10179 url: Some("fuchsia-pkg://fuchsia.com/source#meta/source.cm".to_string()),
10180 startup: Some(fdecl::StartupMode::Lazy),
10181 ..Default::default()
10182 }]);
10183 decl.exposes = Some(vec![
10184 new_expose(
10186 fdecl::Ref::Self_(fdecl::SelfRef {}),
10187 fdecl::Availability::Optional,
10188 "fuchsia.examples.Echo1",
10189 ),
10190 new_expose(
10192 fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
10193 fdecl::Availability::Optional,
10194 "fuchsia.examples.Echo2",
10195 ),
10196 new_expose(
10198 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10199 fdecl::Availability::Optional,
10200 "fuchsia.examples.Echo3",
10201 ),
10202 new_expose(
10204 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10205 fdecl::Availability::Optional,
10206 "fuchsia.examples.Echo4",
10207 ),
10208 new_expose(
10210 fdecl::Ref::Self_(fdecl::SelfRef {}),
10211 fdecl::Availability::Transitional,
10212 "fuchsia.examples.Echo5",
10213 ),
10214 new_expose(
10216 fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
10217 fdecl::Availability::Transitional,
10218 "fuchsia.examples.Echo6",
10219 ),
10220 new_expose(
10222 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10223 fdecl::Availability::Transitional,
10224 "fuchsia.examples.Echo7",
10225 ),
10226 new_expose(
10228 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10229 fdecl::Availability::Transitional,
10230 "fuchsia.examples.Echo8",
10231 ),
10232 new_expose(
10234 fdecl::Ref::Self_(fdecl::SelfRef {}),
10235 fdecl::Availability::SameAsTarget,
10236 "fuchsia.examples.Echo9",
10237 ),
10238 new_expose(
10240 fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
10241 fdecl::Availability::SameAsTarget,
10242 "fuchsia.examples.Echo10",
10243 ),
10244 new_expose(
10246 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10247 fdecl::Availability::SameAsTarget,
10248 "fuchsia.examples.Echo11",
10249 ),
10250 new_expose(
10252 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10253 fdecl::Availability::Required,
10254 "fuchsia.examples.Echo12",
10255 ),
10256 new_expose(
10258 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10259 fdecl::Availability::SameAsTarget,
10260 "fuchsia.examples.Echo13",
10261 ),
10262 ]);
10263 decl
10264 }
10265
10266 fn generate_offer_different_source_and_availability_decl(
10269 new_offer: impl Fn(fdecl::Ref, fdecl::Availability, &str) -> fdecl::Offer,
10270 ) -> fdecl::Component {
10271 let mut decl = new_component_decl();
10272 decl.children = Some(vec![
10273 fdecl::Child {
10274 name: Some("source".to_string()),
10275 url: Some("fuchsia-pkg://fuchsia.com/source#meta/source.cm".to_string()),
10276 startup: Some(fdecl::StartupMode::Lazy),
10277 on_terminate: None,
10278 environment: None,
10279 ..Default::default()
10280 },
10281 fdecl::Child {
10282 name: Some("sink".to_string()),
10283 url: Some("fuchsia-pkg://fuchsia.com/sink#meta/sink.cm".to_string()),
10284 startup: Some(fdecl::StartupMode::Lazy),
10285 on_terminate: None,
10286 environment: None,
10287 ..Default::default()
10288 },
10289 ]);
10290 decl.offers = Some(vec![
10291 new_offer(
10294 fdecl::Ref::Parent(fdecl::ParentRef {}),
10295 fdecl::Availability::Required,
10296 "fuchsia.examples.Echo0",
10297 ),
10298 new_offer(
10299 fdecl::Ref::Parent(fdecl::ParentRef {}),
10300 fdecl::Availability::Optional,
10301 "fuchsia.examples.Echo1",
10302 ),
10303 new_offer(
10304 fdecl::Ref::Parent(fdecl::ParentRef {}),
10305 fdecl::Availability::SameAsTarget,
10306 "fuchsia.examples.Echo2",
10307 ),
10308 new_offer(
10309 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10310 fdecl::Availability::Optional,
10311 "fuchsia.examples.Echo3",
10312 ),
10313 new_offer(
10316 fdecl::Ref::Self_(fdecl::SelfRef {}),
10317 fdecl::Availability::Optional,
10318 "fuchsia.examples.Echo4",
10319 ),
10320 new_offer(
10321 fdecl::Ref::Self_(fdecl::SelfRef {}),
10322 fdecl::Availability::SameAsTarget,
10323 "fuchsia.examples.Echo5",
10324 ),
10325 new_offer(
10326 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10327 fdecl::Availability::Optional,
10328 "fuchsia.examples.Echo6",
10329 ),
10330 new_offer(
10331 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10332 fdecl::Availability::SameAsTarget,
10333 "fuchsia.examples.Echo7",
10334 ),
10335 new_offer(
10336 fdecl::Ref::Child(fdecl::ChildRef { name: "source".to_string(), collection: None }),
10337 fdecl::Availability::Optional,
10338 "fuchsia.examples.Echo8",
10339 ),
10340 new_offer(
10341 fdecl::Ref::Child(fdecl::ChildRef { name: "source".to_string(), collection: None }),
10342 fdecl::Availability::SameAsTarget,
10343 "fuchsia.examples.Echo9",
10344 ),
10345 new_offer(
10347 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10348 fdecl::Availability::Required,
10349 "fuchsia.examples.Echo10",
10350 ),
10351 new_offer(
10352 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10353 fdecl::Availability::SameAsTarget,
10354 "fuchsia.examples.Echo11",
10355 ),
10356 ]);
10357 decl
10358 }
10359
10360 #[test]
10361 fn test_validate_dynamic_offers_empty() {
10362 assert_eq!(validate_dynamic_offers(vec![], &vec![], &fdecl::Component::default()), Ok(()));
10363 }
10364
10365 #[test]
10366 fn test_validate_dynamic_offers_okay() {
10367 assert_eq!(
10368 validate_dynamic_offers(
10369 vec![],
10370 &vec![
10371 fdecl::Offer::Protocol(fdecl::OfferProtocol {
10372 dependency_type: Some(fdecl::DependencyType::Strong),
10373 source: Some(fdecl::Ref::Self_(fdecl::SelfRef)),
10374 source_name: Some("thing".to_string()),
10375 target_name: Some("thing".repeat(26)),
10376 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10377 name: "foo".to_string(),
10378 collection: Some("foo".to_string()),
10379 })),
10380 ..Default::default()
10381 }),
10382 fdecl::Offer::Service(fdecl::OfferService {
10383 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10384 source_name: Some("thang".repeat(26)),
10385 target_name: Some("thang".repeat(26)),
10386 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10387 name: "foo".to_string(),
10388 collection: Some("foo".to_string()),
10389 })),
10390 ..Default::default()
10391 }),
10392 fdecl::Offer::Directory(fdecl::OfferDirectory {
10393 dependency_type: Some(fdecl::DependencyType::Strong),
10394 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10395 source_name: Some("thung1".repeat(26)),
10396 target_name: Some("thung1".repeat(26)),
10397 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10398 name: "foo".to_string(),
10399 collection: Some("foo".to_string()),
10400 })),
10401 ..Default::default()
10402 }),
10403 fdecl::Offer::Storage(fdecl::OfferStorage {
10404 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10405 source_name: Some("thung2".repeat(26)),
10406 target_name: Some("thung2".repeat(26)),
10407 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10408 name: "foo".to_string(),
10409 collection: Some("foo".to_string()),
10410 })),
10411 ..Default::default()
10412 }),
10413 fdecl::Offer::Runner(fdecl::OfferRunner {
10414 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10415 source_name: Some("thung3".repeat(26)),
10416 target_name: Some("thung3".repeat(26)),
10417 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10418 name: "foo".to_string(),
10419 collection: Some("foo".to_string()),
10420 })),
10421 ..Default::default()
10422 }),
10423 fdecl::Offer::Resolver(fdecl::OfferResolver {
10424 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10425 source_name: Some("thung4".repeat(26)),
10426 target_name: Some("thung4".repeat(26)),
10427 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10428 name: "foo".to_string(),
10429 collection: Some("foo".to_string()),
10430 })),
10431 ..Default::default()
10432 }),
10433 ],
10434 &fdecl::Component {
10435 capabilities: Some(vec![fdecl::Capability::Protocol(fdecl::Protocol {
10436 name: Some("thing".to_string()),
10437 source_path: Some("/svc/foo".into()),
10438 ..Default::default()
10439 }),]),
10440 ..Default::default()
10441 }
10442 ),
10443 Ok(())
10444 );
10445 }
10446
10447 #[test]
10448 fn test_validate_dynamic_offers_valid_service_aggregation() {
10449 assert_eq!(
10450 validate_dynamic_offers(
10451 vec![],
10452 &vec![
10453 fdecl::Offer::Service(fdecl::OfferService {
10454 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10455 name: "child_a".to_string(),
10456 collection: None
10457 })),
10458 source_name: Some("fuchsia.logger.Log".to_string()),
10459 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10460 name: "child_c".to_string(),
10461 collection: None,
10462 })),
10463 target_name: Some("fuchsia.logger.Log".to_string()),
10464 source_instance_filter: Some(vec!["default".to_string()]),
10465 ..Default::default()
10466 }),
10467 fdecl::Offer::Service(fdecl::OfferService {
10468 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10469 name: "child_b".to_string(),
10470 collection: None
10471 })),
10472 source_name: Some("fuchsia.logger.Log".to_string()),
10473 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10474 name: "child_c".to_string(),
10475 collection: None,
10476 })),
10477 target_name: Some("fuchsia.logger.Log".to_string()),
10478 source_instance_filter: Some(vec!["a_different_default".to_string()]),
10479 ..Default::default()
10480 })
10481 ],
10482 &fdecl::Component {
10483 children: Some(vec![
10484 fdecl::Child {
10485 name: Some("child_a".to_string()),
10486 url: Some(
10487 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10488 .to_string()
10489 ),
10490 startup: Some(fdecl::StartupMode::Lazy),
10491 on_terminate: None,
10492 environment: None,
10493 ..Default::default()
10494 },
10495 fdecl::Child {
10496 name: Some("child_b".to_string()),
10497 url: Some(
10498 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10499 .to_string()
10500 ),
10501 startup: Some(fdecl::StartupMode::Lazy),
10502 on_terminate: None,
10503 environment: None,
10504 ..Default::default()
10505 },
10506 fdecl::Child {
10507 name: Some("child_c".to_string()),
10508 url: Some(
10509 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10510 .to_string()
10511 ),
10512 startup: Some(fdecl::StartupMode::Lazy),
10513 on_terminate: None,
10514 environment: None,
10515 ..Default::default()
10516 },
10517 ]),
10518 ..Default::default()
10519 }
10520 ),
10521 Ok(())
10522 );
10523 }
10524
10525 #[test]
10526 fn test_validate_dynamic_service_aggregation_missing_filter() {
10527 assert_eq!(
10528 validate_dynamic_offers(
10529 vec![],
10530 &vec![
10531 fdecl::Offer::Service(fdecl::OfferService {
10532 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10533 name: "child_a".to_string(),
10534 collection: None
10535 })),
10536 source_name: Some("fuchsia.logger.Log".to_string()),
10537 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10538 name: "child_c".to_string(),
10539 collection: None,
10540 })),
10541 target_name: Some("fuchsia.logger.Log".to_string()),
10542 source_instance_filter: Some(vec!["default".to_string()]),
10543 ..Default::default()
10544 }),
10545 fdecl::Offer::Service(fdecl::OfferService {
10546 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10547 name: "child_b".to_string(),
10548 collection: None
10549 })),
10550 source_name: Some("fuchsia.logger.Log".to_string()),
10551 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10552 name: "child_c".to_string(),
10553 collection: None,
10554 })),
10555 target_name: Some("fuchsia.logger.Log".to_string()),
10556 source_instance_filter: None,
10557 ..Default::default()
10558 }),
10559 ],
10560 &fdecl::Component {
10561 children: Some(vec![
10562 fdecl::Child {
10563 name: Some("child_a".to_string()),
10564 url: Some(
10565 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10566 .to_string()
10567 ),
10568 startup: Some(fdecl::StartupMode::Lazy),
10569 ..Default::default()
10570 },
10571 fdecl::Child {
10572 name: Some("child_b".to_string()),
10573 url: Some(
10574 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10575 .to_string()
10576 ),
10577 startup: Some(fdecl::StartupMode::Lazy),
10578 ..Default::default()
10579 },
10580 fdecl::Child {
10581 name: Some("child_c".to_string()),
10582 url: Some(
10583 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10584 .to_string()
10585 ),
10586 startup: Some(fdecl::StartupMode::Lazy),
10587 ..Default::default()
10588 },
10589 ]),
10590 ..Default::default()
10591 },
10592 ),
10593 Err(ErrorList::new(vec![Error::invalid_aggregate_offer(
10594 "source_instance_filter must be set for dynamic aggregate service offers"
10595 ),]))
10596 );
10597 }
10598
10599 #[test]
10600 fn test_validate_dynamic_offers_omit_target() {
10601 assert_eq!(
10602 validate_dynamic_offers(
10603 vec![],
10604 &vec![
10605 fdecl::Offer::Protocol(fdecl::OfferProtocol {
10606 dependency_type: Some(fdecl::DependencyType::Strong),
10607 source: Some(fdecl::Ref::Self_(fdecl::SelfRef)),
10608 source_name: Some("thing".to_string()),
10609 target_name: Some("thing".to_string()),
10610 ..Default::default()
10611 }),
10612 fdecl::Offer::Service(fdecl::OfferService {
10613 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10614 source_name: Some("thang".to_string()),
10615 target_name: Some("thang".to_string()),
10616 ..Default::default()
10617 }),
10618 fdecl::Offer::Directory(fdecl::OfferDirectory {
10619 dependency_type: Some(fdecl::DependencyType::Strong),
10620 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10621 source_name: Some("thung1".to_string()),
10622 target_name: Some("thung1".to_string()),
10623 ..Default::default()
10624 }),
10625 fdecl::Offer::Storage(fdecl::OfferStorage {
10626 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10627 source_name: Some("thung2".to_string()),
10628 target_name: Some("thung2".to_string()),
10629 ..Default::default()
10630 }),
10631 fdecl::Offer::Runner(fdecl::OfferRunner {
10632 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10633 source_name: Some("thung3".to_string()),
10634 target_name: Some("thung3".to_string()),
10635 ..Default::default()
10636 }),
10637 fdecl::Offer::Resolver(fdecl::OfferResolver {
10638 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10639 source_name: Some("thung4".to_string()),
10640 target_name: Some("thung4".to_string()),
10641 ..Default::default()
10642 }),
10643 ],
10644 &fdecl::Component {
10645 capabilities: Some(vec![fdecl::Capability::Protocol(fdecl::Protocol {
10646 name: Some("thing".to_string()),
10647 source_path: Some("/svc/foo".into()),
10648 ..Default::default()
10649 }),]),
10650 ..Default::default()
10651 }
10652 ),
10653 Err(ErrorList::new(vec![
10654 Error::missing_field(DeclType::OfferProtocol, "target"),
10655 Error::missing_field(DeclType::OfferService, "target"),
10656 Error::missing_field(DeclType::OfferDirectory, "target"),
10657 Error::missing_field(DeclType::OfferStorage, "target"),
10658 Error::missing_field(DeclType::OfferRunner, "target"),
10659 Error::missing_field(DeclType::OfferResolver, "target"),
10660 ]))
10661 );
10662 }
10663
10664 #[test]
10665 fn test_validate_dynamic_offers_collection_collision() {
10666 assert_eq!(
10667 validate_dynamic_offers(
10668 vec![],
10669 &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10670 dependency_type: Some(fdecl::DependencyType::Strong),
10671 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10672 source_name: Some("thing".to_string()),
10673 target_name: Some("thing".to_string()),
10674 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10675 name: "child".to_string(),
10676 collection: Some("coll".to_string()),
10677 })),
10678 ..Default::default()
10679 }),],
10680 &fdecl::Component {
10681 offers: Some(vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10682 dependency_type: Some(fdecl::DependencyType::Strong),
10683 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10684 source_name: Some("thing".to_string()),
10685 target_name: Some("thing".to_string()),
10686 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10687 name: "coll".into()
10688 })),
10689 ..Default::default()
10690 }),]),
10691 collections: Some(vec![fdecl::Collection {
10692 name: Some("coll".to_string()),
10693 durability: Some(fdecl::Durability::Transient),
10694 ..Default::default()
10695 },]),
10696 ..Default::default()
10697 }
10698 ),
10699 Err(ErrorList::new(vec![Error::duplicate_field(
10700 DeclType::OfferProtocol,
10701 "target_name",
10702 "thing"
10703 ),]))
10704 );
10705 }
10706
10707 #[test]
10708 fn test_validate_dynamic_offers_cycle_collection_to_static_child() {
10709 assert_eq!(
10710 validate_dynamic_offers(
10711 vec![("dyn", "coll")],
10712 &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10713 source_name: Some("bar".to_string()),
10714 target_name: Some("bar".to_string()),
10715 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10716 name: "static_child".into(),
10717 collection: None,
10718 })),
10719 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10720 name: "dyn".to_string(),
10721 collection: Some("coll".to_string()),
10722 })),
10723 dependency_type: Some(fdecl::DependencyType::Strong),
10724 ..Default::default()
10725 }),],
10726 &fdecl::Component {
10727 offers: Some(vec![fdecl::Offer::Service(fdecl::OfferService {
10728 source_name: Some("foo".to_string()),
10729 target_name: Some("foo".to_string()),
10730 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10731 name: "coll".into(),
10732 })),
10733 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10734 name: "static_child".into(),
10735 collection: None,
10736 })),
10737 ..Default::default()
10738 })]),
10739 children: Some(vec![fdecl::Child {
10740 name: Some("static_child".into()),
10741 url: Some("url#child.cm".into()),
10742 startup: Some(fdecl::StartupMode::Lazy),
10743 ..Default::default()
10744 }]),
10745 collections: Some(vec![fdecl::Collection {
10746 name: Some("coll".into()),
10747 durability: Some(fdecl::Durability::Transient),
10748 ..Default::default()
10749 }]),
10750 ..Default::default()
10751 }
10752 ),
10753 Err(ErrorList::new(vec![Error::dependency_cycle(
10754 directed_graph::Error::CyclesDetected(
10755 [vec![
10756 "child coll:dyn",
10757 "collection coll",
10758 "child static_child",
10759 "child coll:dyn",
10760 ]]
10761 .iter()
10762 .cloned()
10763 .collect()
10764 )
10765 .format_cycle()
10766 )]))
10767 );
10768 }
10769
10770 #[test]
10771 fn test_validate_dynamic_offers_cycle_collection_to_dynamic_child() {
10772 assert_eq!(
10773 validate_dynamic_offers(
10774 vec![("dyn", "coll1"), ("dyn", "coll2")],
10775 &vec![
10776 fdecl::Offer::Service(fdecl::OfferService {
10777 source_name: Some("foo".to_string()),
10778 target_name: Some("foo".to_string()),
10779 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10780 name: "coll2".into(),
10781 })),
10782 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10783 name: "dyn".into(),
10784 collection: Some("coll1".into()),
10785 })),
10786 ..Default::default()
10787 }),
10788 fdecl::Offer::Protocol(fdecl::OfferProtocol {
10789 source_name: Some("bar".to_string()),
10790 target_name: Some("bar".to_string()),
10791 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10792 name: "dyn".into(),
10793 collection: Some("coll1".into()),
10794 })),
10795 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10796 name: "dyn".to_string(),
10797 collection: Some("coll2".to_string()),
10798 })),
10799 dependency_type: Some(fdecl::DependencyType::Strong),
10800 ..Default::default()
10801 }),
10802 ],
10803 &fdecl::Component {
10804 collections: Some(vec![
10805 fdecl::Collection {
10806 name: Some("coll1".into()),
10807 durability: Some(fdecl::Durability::Transient),
10808 ..Default::default()
10809 },
10810 fdecl::Collection {
10811 name: Some("coll2".into()),
10812 durability: Some(fdecl::Durability::Transient),
10813 ..Default::default()
10814 },
10815 ]),
10816 ..Default::default()
10817 }
10818 ),
10819 Err(ErrorList::new(vec![Error::dependency_cycle(
10820 directed_graph::Error::CyclesDetected(
10821 [vec![
10822 "child coll1:dyn",
10823 "child coll2:dyn",
10824 "collection coll2",
10825 "child coll1:dyn",
10826 ]]
10827 .iter()
10828 .cloned()
10829 .collect()
10830 )
10831 .format_cycle()
10832 )]))
10833 );
10834 }
10835
10836 #[test]
10837 fn test_validate_dynamic_offers_cycle_collection_to_collection() {
10838 assert_eq!(
10839 validate_dynamic_offers(
10840 vec![("dyn", "coll1"), ("dyn", "coll2")],
10841 &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10842 source_name: Some("bar".to_string()),
10843 target_name: Some("bar".to_string()),
10844 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10845 name: "dyn".into(),
10846 collection: Some("coll2".parse().unwrap()),
10847 })),
10848 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10849 name: "dyn".into(),
10850 collection: Some("coll1".parse().unwrap()),
10851 })),
10852 dependency_type: Some(fdecl::DependencyType::Strong),
10853 ..Default::default()
10854 }),],
10855 &fdecl::Component {
10856 offers: Some(vec![fdecl::Offer::Service(fdecl::OfferService {
10857 source_name: Some("foo".to_string()),
10858 target_name: Some("foo".to_string()),
10859 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10860 name: "coll1".into(),
10861 })),
10862 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10863 name: "coll2".into(),
10864 })),
10865 ..Default::default()
10866 })]),
10867 collections: Some(vec![
10868 fdecl::Collection {
10869 name: Some("coll1".into()),
10870 durability: Some(fdecl::Durability::Transient),
10871 ..Default::default()
10872 },
10873 fdecl::Collection {
10874 name: Some("coll2".into()),
10875 durability: Some(fdecl::Durability::Transient),
10876 ..Default::default()
10877 },
10878 ]),
10879 ..Default::default()
10880 }
10881 ),
10882 Err(ErrorList::new(vec![Error::dependency_cycle(
10883 directed_graph::Error::CyclesDetected(
10884 [vec![
10885 "child coll1:dyn",
10886 "collection coll1",
10887 "child coll2:dyn",
10888 "child coll1:dyn",
10889 ]]
10890 .iter()
10891 .cloned()
10892 .collect()
10893 )
10894 .format_cycle()
10895 )]))
10896 );
10897 }
10898
10899 #[test]
10900 fn test_validate_dynamic_child() {
10901 assert_eq!(
10902 Ok(()),
10903 validate_dynamic_child(&fdecl::Child {
10904 name: Some("a".repeat(MAX_LONG_NAME_LENGTH).to_string()),
10905 url: Some("test:///child".to_string()),
10906 startup: Some(fdecl::StartupMode::Lazy),
10907 on_terminate: None,
10908 environment: None,
10909 ..Default::default()
10910 })
10911 );
10912 }
10913
10914 #[test]
10915 fn test_validate_dynamic_child_environment_is_invalid() {
10916 assert_eq!(
10917 validate_dynamic_child(&fdecl::Child {
10918 name: Some("a".repeat(MAX_LONG_NAME_LENGTH).to_string()),
10919 url: Some("test:///child".to_string()),
10920 startup: Some(fdecl::StartupMode::Lazy),
10921 on_terminate: None,
10922 environment: Some("env".to_string()),
10923 ..Default::default()
10924 }),
10925 Err(ErrorList::new(vec![Error::DynamicChildWithEnvironment]))
10926 );
10927 }
10928
10929 #[test]
10930 fn test_validate_dynamic_offers_missing_stuff() {
10931 assert_eq!(
10932 validate_dynamic_offers(
10933 vec![],
10934 &vec![
10935 fdecl::Offer::Protocol(fdecl::OfferProtocol::default()),
10936 fdecl::Offer::Service(fdecl::OfferService::default()),
10937 fdecl::Offer::Directory(fdecl::OfferDirectory::default()),
10938 fdecl::Offer::Storage(fdecl::OfferStorage::default()),
10939 fdecl::Offer::Runner(fdecl::OfferRunner::default()),
10940 fdecl::Offer::Resolver(fdecl::OfferResolver::default()),
10941 ],
10942 &fdecl::Component::default()
10943 ),
10944 Err(ErrorList::new(vec![
10945 Error::missing_field(DeclType::OfferProtocol, "source"),
10946 Error::missing_field(DeclType::OfferProtocol, "source_name"),
10947 Error::missing_field(DeclType::OfferProtocol, "target"),
10948 Error::missing_field(DeclType::OfferProtocol, "target_name"),
10949 Error::missing_field(DeclType::OfferProtocol, "dependency_type"),
10950 Error::missing_field(DeclType::OfferService, "source"),
10951 Error::missing_field(DeclType::OfferService, "source_name"),
10952 Error::missing_field(DeclType::OfferService, "target"),
10953 Error::missing_field(DeclType::OfferService, "target_name"),
10954 Error::missing_field(DeclType::OfferDirectory, "source"),
10955 Error::missing_field(DeclType::OfferDirectory, "source_name"),
10956 Error::missing_field(DeclType::OfferDirectory, "target"),
10957 Error::missing_field(DeclType::OfferDirectory, "target_name"),
10958 Error::missing_field(DeclType::OfferDirectory, "dependency_type"),
10959 Error::missing_field(DeclType::OfferStorage, "source"),
10960 Error::missing_field(DeclType::OfferStorage, "source_name"),
10961 Error::missing_field(DeclType::OfferStorage, "target"),
10962 Error::missing_field(DeclType::OfferStorage, "target_name"),
10963 Error::missing_field(DeclType::OfferRunner, "source"),
10964 Error::missing_field(DeclType::OfferRunner, "source_name"),
10965 Error::missing_field(DeclType::OfferRunner, "target"),
10966 Error::missing_field(DeclType::OfferRunner, "target_name"),
10967 Error::missing_field(DeclType::OfferResolver, "source"),
10968 Error::missing_field(DeclType::OfferResolver, "source_name"),
10969 Error::missing_field(DeclType::OfferResolver, "target"),
10970 Error::missing_field(DeclType::OfferResolver, "target_name"),
10971 ]))
10972 );
10973 }
10974
10975 test_dependency! {
10976 (test_validate_offers_protocol_dependency_cycle) => {
10977 ty = fdecl::Offer::Protocol,
10978 offer_decl = fdecl::OfferProtocol {
10979 source: None, target: None, source_name: Some(format!("thing")),
10982 target_name: Some(format!("thing")),
10983 dependency_type: Some(fdecl::DependencyType::Strong),
10984 ..Default::default()
10985 },
10986 },
10987 (test_validate_offers_directory_dependency_cycle) => {
10988 ty = fdecl::Offer::Directory,
10989 offer_decl = fdecl::OfferDirectory {
10990 source: None, target: None, source_name: Some(format!("thing")),
10993 target_name: Some(format!("thing")),
10994 rights: Some(fio::Operations::CONNECT),
10995 subdir: None,
10996 dependency_type: Some(fdecl::DependencyType::Strong),
10997 ..Default::default()
10998 },
10999 },
11000 (test_validate_offers_service_dependency_cycle) => {
11001 ty = fdecl::Offer::Service,
11002 offer_decl = fdecl::OfferService {
11003 source: None, target: None, source_name: Some(format!("thing")),
11006 target_name: Some(format!("thing")),
11007 ..Default::default()
11008 },
11009 },
11010 (test_validate_offers_runner_dependency_cycle) => {
11011 ty = fdecl::Offer::Runner,
11012 offer_decl = fdecl::OfferRunner {
11013 source: None, target: None, source_name: Some(format!("thing")),
11016 target_name: Some(format!("thing")),
11017 ..Default::default()
11018 },
11019 },
11020 (test_validate_offers_resolver_dependency_cycle) => {
11021 ty = fdecl::Offer::Resolver,
11022 offer_decl = fdecl::OfferResolver {
11023 source: None, target: None, source_name: Some(format!("thing")),
11026 target_name: Some(format!("thing")),
11027 ..Default::default()
11028 },
11029 },
11030 }
11031 test_weak_dependency! {
11032 (test_validate_offers_protocol_weak_dependency_cycle) => {
11033 ty = fdecl::Offer::Protocol,
11034 offer_decl = fdecl::OfferProtocol {
11035 source: None, target: None, source_name: Some(format!("thing")),
11038 target_name: Some(format!("thing")),
11039 dependency_type: None, ..Default::default()
11041 },
11042 },
11043 (test_validate_offers_directory_weak_dependency_cycle) => {
11044 ty = fdecl::Offer::Directory,
11045 offer_decl = fdecl::OfferDirectory {
11046 source: None, target: None, source_name: Some(format!("thing")),
11049 target_name: Some(format!("thing")),
11050 rights: Some(fio::Operations::CONNECT),
11051 subdir: None,
11052 dependency_type: None, ..Default::default()
11054 },
11055 },
11056 (test_validate_offers_service_weak_dependency_cycle) => {
11057 ty = fdecl::Offer::Service,
11058 offer_decl = fdecl::OfferService {
11059 source: None, target: None, source_name: Some(format!("thing")),
11062 target_name: Some(format!("thing")),
11063 dependency_type: None, ..Default::default()
11065 },
11066 },
11067 }
11068}