1pub(crate) mod util;
6
7pub mod error;
8
9pub use crate::util::check_url;
10
11use crate::error::*;
12use crate::util::*;
13use directed_graph::DirectedGraph;
14use fidl_fuchsia_component_decl as fdecl;
15use itertools::Itertools;
16use std::collections::{BTreeSet, HashMap, HashSet};
17use std::path::Path;
18
19trait HasAvailability {
20 fn availability(&self) -> fdecl::Availability;
21}
22
23impl HasAvailability for fdecl::ExposeService {
24 fn availability(&self) -> fdecl::Availability {
25 return self.availability.unwrap_or(fdecl::Availability::Required);
26 }
27}
28
29impl HasAvailability for fdecl::OfferService {
30 fn availability(&self) -> fdecl::Availability {
31 return self.availability.unwrap_or(fdecl::Availability::Required);
32 }
33}
34
35#[cfg(fuchsia_api_level_at_least = "25")]
36macro_rules! get_source_dictionary {
37 ($decl:ident) => {
38 $decl.source_dictionary.as_ref()
39 };
40}
41#[cfg(fuchsia_api_level_less_than = "25")]
42macro_rules! get_source_dictionary {
43 ($decl:ident) => {
44 None
45 };
46}
47
48pub fn validate_value_spec(spec: &fdecl::ConfigValueSpec) -> Result<(), ErrorList> {
52 let mut errors = vec![];
53 if let Some(value) = &spec.value {
54 match value {
55 fdecl::ConfigValue::Single(s) => match s {
56 fdecl::ConfigSingleValue::Bool(_)
57 | fdecl::ConfigSingleValue::Uint8(_)
58 | fdecl::ConfigSingleValue::Uint16(_)
59 | fdecl::ConfigSingleValue::Uint32(_)
60 | fdecl::ConfigSingleValue::Uint64(_)
61 | fdecl::ConfigSingleValue::Int8(_)
62 | fdecl::ConfigSingleValue::Int16(_)
63 | fdecl::ConfigSingleValue::Int32(_)
64 | fdecl::ConfigSingleValue::Int64(_)
65 | fdecl::ConfigSingleValue::String(_) => {}
66 fdecl::ConfigSingleValueUnknown!() => {
67 errors.push(Error::invalid_field(DeclType::ConfigValueSpec, "value"));
68 }
69 },
70 fdecl::ConfigValue::Vector(l) => match l {
71 fdecl::ConfigVectorValue::BoolVector(_)
72 | fdecl::ConfigVectorValue::Uint8Vector(_)
73 | fdecl::ConfigVectorValue::Uint16Vector(_)
74 | fdecl::ConfigVectorValue::Uint32Vector(_)
75 | fdecl::ConfigVectorValue::Uint64Vector(_)
76 | fdecl::ConfigVectorValue::Int8Vector(_)
77 | fdecl::ConfigVectorValue::Int16Vector(_)
78 | fdecl::ConfigVectorValue::Int32Vector(_)
79 | fdecl::ConfigVectorValue::Int64Vector(_)
80 | fdecl::ConfigVectorValue::StringVector(_) => {}
81 fdecl::ConfigVectorValueUnknown!() => {
82 errors.push(Error::invalid_field(DeclType::ConfigValueSpec, "value"));
83 }
84 },
85 fdecl::ConfigValueUnknown!() => {
86 errors.push(Error::invalid_field(DeclType::ConfigValueSpec, "value"));
87 }
88 }
89 } else {
90 errors.push(Error::missing_field(DeclType::ConfigValueSpec, "value"));
91 }
92
93 if errors.is_empty() {
94 Ok(())
95 } else {
96 Err(ErrorList::new(errors))
97 }
98}
99
100pub fn validate_values_data(data: &fdecl::ConfigValuesData) -> Result<(), ErrorList> {
108 let mut errors = vec![];
109 if let Some(values) = &data.values {
110 for spec in values {
111 if let Err(mut e) = validate_value_spec(spec) {
112 errors.append(&mut e.errs);
113 }
114 }
115 } else {
116 errors.push(Error::missing_field(DeclType::ConfigValuesData, "values"));
117 }
118
119 if let Some(checksum) = &data.checksum {
120 match checksum {
121 fdecl::ConfigChecksum::Sha256(_) => {}
122 fdecl::ConfigChecksumUnknown!() => {
123 errors.push(Error::invalid_field(DeclType::ConfigValuesData, "checksum"));
124 }
125 }
126 } else {
127 errors.push(Error::missing_field(DeclType::ConfigValuesData, "checksum"));
128 }
129
130 if errors.is_empty() {
131 Ok(())
132 } else {
133 Err(ErrorList::new(errors))
134 }
135}
136
137#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
139enum RefKey<'a> {
140 Parent,
141 Self_,
142 Child(&'a str),
143 Collection(&'a str),
144 Framework,
145 Capability,
146 Debug,
147}
148
149pub fn validate(decl: &fdecl::Component) -> Result<(), ErrorList> {
163 let ctx = ValidationContext::default();
164 ctx.validate(decl, &vec![]).map_err(|errs| ErrorList::new(errs))
165}
166
167fn validate_capabilities(
169 capabilities: &[fdecl::Capability],
170 as_builtin: bool,
171) -> Result<(), ErrorList> {
172 let mut ctx = ValidationContext::default();
173
174 #[cfg(fuchsia_api_level_at_least = "25")]
175 ctx.load_dictionary_names(capabilities.iter().filter_map(|capability| match capability {
176 fdecl::Capability::Dictionary(dictionary_decl) => Some(dictionary_decl),
177 _ => None,
178 }));
179
180 ctx.validate_capability_decls(capabilities, as_builtin);
181 if ctx.errors.is_empty() {
182 Ok(())
183 } else {
184 Err(ErrorList::new(ctx.errors))
185 }
186}
187
188pub fn validate_builtin_capabilities(
190 capabilities: &Vec<fdecl::Capability>,
191) -> Result<(), ErrorList> {
192 validate_capabilities(capabilities, true)
193}
194
195pub fn validate_namespace_capabilities(
197 capabilities: &Vec<fdecl::Capability>,
198) -> Result<(), ErrorList> {
199 validate_capabilities(capabilities, false)
200}
201
202type CheckChildNameFn = fn(Option<&String>, DeclType, &str, &mut Vec<Error>) -> bool;
205
206pub fn validate_dynamic_child(child: &fdecl::Child) -> Result<(), ErrorList> {
207 let mut errors = vec![];
208
209 if let Err(mut error_list) = validate_child(child, check_dynamic_name) {
210 errors.append(&mut error_list.errs);
211 }
212
213 if child.environment.is_some() {
214 errors.push(Error::DynamicChildWithEnvironment);
215 }
216
217 if errors.is_empty() {
218 Ok(())
219 } else {
220 Err(ErrorList { errs: errors })
221 }
222}
223
224fn validate_child(
227 child: &fdecl::Child,
228 check_child_name: CheckChildNameFn,
229) -> Result<(), ErrorList> {
230 let mut errors = vec![];
231 check_child_name(child.name.as_ref(), DeclType::Child, "name", &mut errors);
232 check_url(child.url.as_ref(), DeclType::Child, "url", &mut errors);
233 if child.startup.is_none() {
234 errors.push(Error::missing_field(DeclType::Child, "startup"));
235 }
236 if child.environment.is_some() {
238 check_name(child.environment.as_ref(), DeclType::Child, "environment", &mut errors);
239 }
240 if errors.is_empty() {
241 Ok(())
242 } else {
243 Err(ErrorList { errs: errors })
244 }
245}
246
247pub fn validate_dynamic_offers<'a>(
256 dynamic_children: Vec<(&'a str, &'a str)>,
257 offers: &'a Vec<fdecl::Offer>,
258 decl: &'a fdecl::Component,
259) -> Result<(), ErrorList> {
260 let mut ctx = ValidationContext::default();
261 ctx.dynamic_children = dynamic_children;
262 ctx.validate(decl, offers).map_err(|errs| ErrorList::new(errs))
263}
264
265fn check_offer_name(
266 prop: Option<&String>,
267 decl: DeclType,
268 keyword: &str,
269 offer_type: OfferType,
270 errors: &mut Vec<Error>,
271) -> bool {
272 if offer_type == OfferType::Dynamic {
273 check_dynamic_name(prop, decl, keyword, errors)
274 } else {
275 check_name(prop, decl, keyword, errors)
276 }
277}
278
279#[derive(Default)]
280struct ValidationContext<'a> {
281 all_children: HashMap<&'a str, &'a fdecl::Child>,
282 all_collections: HashSet<&'a str>,
283 all_capability_ids: HashSet<&'a str>,
284 all_storages: HashMap<&'a str, Option<&'a fdecl::Ref>>,
285 all_services: HashSet<&'a str>,
286 all_protocols: HashSet<&'a str>,
287 all_directories: HashSet<&'a str>,
288 all_runners: HashSet<&'a str>,
289 all_resolvers: HashSet<&'a str>,
290 #[cfg(fuchsia_api_level_at_least = "25")]
291 all_dictionaries: HashMap<&'a str, &'a fdecl::Dictionary>,
292
293 #[cfg(fuchsia_api_level_at_least = "HEAD")]
294 all_configs: HashSet<&'a str>,
295
296 all_environment_names: HashSet<&'a str>,
297 dynamic_children: Vec<(&'a str, &'a str)>,
298 strong_dependencies: DirectedGraph<cm_graph::DependencyNode<'a>>,
299 target_ids: IdMap<'a>,
300 errors: Vec<Error>,
301}
302
303trait Container {
307 fn contains(&self, key: &str) -> bool;
308}
309
310impl<'a> Container for HashSet<&'a str> {
311 fn contains(&self, key: &str) -> bool {
312 self.contains(key)
313 }
314}
315
316impl<'a, T> Container for HashMap<&'a str, T> {
317 fn contains(&self, key: &str) -> bool {
318 self.contains_key(key)
319 }
320}
321
322impl<'a> ValidationContext<'a> {
323 fn validate(
324 mut self,
325 decl: &'a fdecl::Component,
326 dynamic_offers: &'a Vec<fdecl::Offer>,
327 ) -> Result<(), Vec<Error>> {
328 if let Some(envs) = &decl.environments {
330 self.collect_environment_names(&envs);
331 }
332
333 if let Some(children) = decl.children.as_ref() {
335 for child in children {
336 self.validate_child_decl(&child);
337 }
338 }
339
340 if let Some(collections) = decl.collections.as_ref() {
342 for collection in collections {
343 self.validate_collection_decl(&collection);
344 }
345 }
346
347 if let Some(capabilities) = decl.capabilities.as_ref() {
349 #[cfg(fuchsia_api_level_at_least = "25")]
350 self.load_dictionary_names(capabilities.iter().filter_map(
351 |capability| match capability {
352 fdecl::Capability::Dictionary(dictionary_decl) => Some(dictionary_decl),
353 _ => None,
354 },
355 ));
356 self.validate_capability_decls(capabilities, false);
357 }
358
359 let mut use_runner_name = None;
361 let mut use_runner_source = None;
362 if let Some(uses) = decl.uses.as_ref() {
363 (use_runner_name, use_runner_source) = self.validate_use_decls(uses);
364 }
365
366 if let Some(program) = decl.program.as_ref() {
368 self.validate_program(program, use_runner_name, use_runner_source);
369 }
370
371 if let Some(exposes) = decl.exposes.as_ref() {
373 let mut expose_to_parent_ids = HashMap::new();
374 let mut expose_to_framework_ids = HashMap::new();
375 for expose in exposes.iter() {
376 self.validate_expose_decl(
377 &expose,
378 &mut expose_to_parent_ids,
379 &mut expose_to_framework_ids,
380 );
381 }
382 self.validate_expose_group(&exposes);
383 }
384
385 if let Some(offers) = decl.offers.as_ref() {
387 for offer in offers.iter() {
388 self.validate_offer_decl(&offer, OfferType::Static);
389 }
390 self.validate_offer_group(&offers, OfferType::Static);
391 }
392
393 for dynamic_offer in dynamic_offers.iter() {
394 self.validate_offer_decl(&dynamic_offer, OfferType::Dynamic);
395 }
396 self.validate_offer_group(&dynamic_offers, OfferType::Dynamic);
397
398 if let Some(environment) = decl.environments.as_ref() {
400 for environment in environment {
401 self.validate_environment_decl(&environment);
402 }
403 }
404
405 #[cfg(fuchsia_api_level_at_least = "20")]
407 self.validate_config(decl.config.as_ref(), decl.uses.as_ref());
408
409 cm_graph::generate_dependency_graph(
411 &mut self.strong_dependencies,
412 &decl,
413 &self.dynamic_children,
414 dynamic_offers,
415 );
416 if let Err(e) = self.strong_dependencies.topological_sort() {
417 self.errors.push(Error::dependency_cycle(e.format_cycle()));
418 }
419
420 if self.errors.is_empty() {
421 Ok(())
422 } else {
423 Err(self.errors)
424 }
425 }
426
427 fn collect_environment_names(&mut self, envs: &'a [fdecl::Environment]) {
429 for env in envs {
430 if let Some(name) = env.name.as_ref() {
431 if !self.all_environment_names.insert(name) {
432 self.errors.push(Error::duplicate_field(DeclType::Environment, "name", name));
433 }
434 }
435 }
436 }
437
438 #[cfg(fuchsia_api_level_at_least = "20")]
441 fn validate_config(
442 &mut self,
443 config: Option<&fdecl::ConfigSchema>,
444 uses: Option<&Vec<fdecl::Use>>,
445 ) {
446 use std::collections::BTreeMap;
447
448 let optional_use_keys: BTreeMap<String, fdecl::ConfigType> =
450 uses.map_or(BTreeMap::new(), |u| {
451 u.iter()
452 .map(|u| {
453 let fdecl::Use::Config(config) = u else {
454 return None;
455 };
456 if config.availability == Some(fdecl::Availability::Required)
457 || config.availability == None
458 {
459 return None;
460 }
461 if let Some(_) = config.default.as_ref() {
462 return None;
463 }
464 let Some(key) = config.target_name.clone() else {
465 return None;
466 };
467 let Some(value) = config.type_.clone() else {
468 return None;
469 };
470 Some((key, value))
471 })
472 .flatten()
473 .collect()
474 });
475
476 for u in uses.iter().flat_map(|x| x.iter()) {
478 let fdecl::Use::Config(config) = u else { continue };
479 let Some(default) = config.default.as_ref() else { continue };
480 validate_value_spec(&fdecl::ConfigValueSpec {
481 value: Some(default.clone()),
482 ..Default::default()
483 })
484 .map_err(|mut e| self.errors.append(&mut e.errs))
485 .ok();
486 }
487
488 let Some(config) = config else {
489 if !optional_use_keys.is_empty() {
490 self.errors.push(Error::missing_field(DeclType::ConfigField, "config"))
491 }
492 return;
493 };
494
495 if let Some(fields) = &config.fields {
496 for field in fields {
497 if field.key.is_none() {
498 self.errors.push(Error::missing_field(DeclType::ConfigField, "key"));
499 }
500 if let Some(type_) = &field.type_ {
501 self.validate_config_type(type_, true);
502 } else {
503 self.errors.push(Error::missing_field(DeclType::ConfigField, "value_type"));
504 }
505 }
506 } else {
507 self.errors.push(Error::missing_field(DeclType::ConfigSchema, "fields"));
508 }
509
510 if let Some(checksum) = &config.checksum {
511 match checksum {
512 fdecl::ConfigChecksum::Sha256(_) => {}
513 fdecl::ConfigChecksumUnknown!() => {
514 self.errors.push(Error::invalid_field(DeclType::ConfigSchema, "checksum"));
515 }
516 }
517 } else {
518 self.errors.push(Error::missing_field(DeclType::ConfigSchema, "checksum"));
519 }
520
521 'outer: for (key, value) in optional_use_keys.iter() {
522 for field in config.fields.iter().flatten() {
523 if field.key.as_ref() == Some(key) {
524 if field.type_.as_ref() != Some(value) {
525 self.errors.push(Error::invalid_field(DeclType::ConfigField, key.clone()));
526 }
527 continue 'outer;
528 }
529 }
530 self.errors.push(Error::missing_field(DeclType::ConfigField, key.clone()));
531 }
532
533 match config.value_source {
534 None => self.errors.push(Error::missing_field(DeclType::ConfigSchema, "value_source")),
535 #[cfg(fuchsia_api_level_at_least = "HEAD")]
536 Some(fdecl::ConfigValueSource::Capabilities(_)) => {
537 if !optional_use_keys.is_empty() {
538 self.errors
539 .push(Error::invalid_field(DeclType::ConfigValueSource, "ValueSource"))
540 }
541 }
542 Some(fdecl::ConfigValueSource::__SourceBreaking { .. }) => {
543 self.errors.push(Error::invalid_field(DeclType::ConfigValueSource, "ValueSource"))
544 }
545 _ => (),
546 };
547 }
548
549 #[cfg(fuchsia_api_level_at_least = "20")]
550 fn validate_config_type(&mut self, type_: &fdecl::ConfigType, accept_vectors: bool) {
551 match &type_.layout {
552 fdecl::ConfigTypeLayout::Bool
553 | fdecl::ConfigTypeLayout::Uint8
554 | fdecl::ConfigTypeLayout::Uint16
555 | fdecl::ConfigTypeLayout::Uint32
556 | fdecl::ConfigTypeLayout::Uint64
557 | fdecl::ConfigTypeLayout::Int8
558 | fdecl::ConfigTypeLayout::Int16
559 | fdecl::ConfigTypeLayout::Int32
560 | fdecl::ConfigTypeLayout::Int64 => {
561 if let Some(parameters) = &type_.parameters {
563 if !parameters.is_empty() {
564 self.errors
565 .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
566 }
567 } else {
568 self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"));
569 }
570
571 if !type_.constraints.is_empty() {
572 self.errors.push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
573 }
574 }
575 fdecl::ConfigTypeLayout::String => {
576 if let Some(parameters) = &type_.parameters {
578 if !parameters.is_empty() {
579 self.errors
580 .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
581 }
582 } else {
583 self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"));
584 }
585
586 if type_.constraints.is_empty() {
587 self.errors.push(Error::missing_field(DeclType::ConfigType, "constraints"));
588 } else if type_.constraints.len() > 1 {
589 self.errors.push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
590 } else if let fdecl::LayoutConstraint::MaxSize(_) = &type_.constraints[0] {
591 } else {
592 self.errors.push(Error::invalid_field(DeclType::ConfigType, "constraints"));
593 }
594 }
595 fdecl::ConfigTypeLayout::Vector => {
596 if accept_vectors {
597 if let Some(parameters) = &type_.parameters {
599 if parameters.is_empty() {
600 self.errors
601 .push(Error::missing_field(DeclType::ConfigType, "parameters"));
602 } else if parameters.len() > 1 {
603 self.errors
604 .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
605 } else if let fdecl::LayoutParameter::NestedType(nested_type) =
606 ¶meters[0]
607 {
608 self.validate_config_type(nested_type, false);
609 } else {
610 self.errors
611 .push(Error::invalid_field(DeclType::ConfigType, "parameters"));
612 }
613 } else {
614 self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"))
615 }
616
617 if type_.constraints.is_empty() {
618 self.errors.push(Error::missing_field(DeclType::ConfigType, "constraints"));
619 } else if type_.constraints.len() > 1 {
620 self.errors
621 .push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
622 } else if let fdecl::LayoutConstraint::MaxSize(_) = &type_.constraints[0] {
623 } else {
624 self.errors.push(Error::invalid_field(DeclType::ConfigType, "constraints"));
625 }
626 } else {
627 self.errors.push(Error::nested_vector());
628 }
629 }
630 _ => self.errors.push(Error::invalid_field(DeclType::ConfigType, "layout")),
631 }
632 }
633
634 fn validate_capability_decls(
635 &mut self,
636 capabilities: &'a [fdecl::Capability],
637 as_builtin: bool,
638 ) {
639 for capability in capabilities {
640 self.validate_capability_decl(capability, as_builtin);
641 }
642 }
643
644 fn validate_capability_decl(&mut self, capability: &'a fdecl::Capability, as_builtin: bool) {
649 match capability {
650 fdecl::Capability::Service(service) => self.validate_service_decl(&service, as_builtin),
651 fdecl::Capability::Protocol(protocol) => {
652 self.validate_protocol_decl(&protocol, as_builtin)
653 }
654 fdecl::Capability::Directory(directory) => {
655 self.validate_directory_decl(&directory, as_builtin)
656 }
657 fdecl::Capability::Storage(storage) => {
658 if as_builtin {
659 self.errors.push(Error::CapabilityCannotBeBuiltin(DeclType::Storage))
660 } else {
661 self.validate_storage_decl(&storage)
662 }
663 }
664 fdecl::Capability::Runner(runner) => self.validate_runner_decl(&runner, as_builtin),
665 fdecl::Capability::Resolver(resolver) => {
666 self.validate_resolver_decl(&resolver, as_builtin)
667 }
668 fdecl::Capability::EventStream(event) => {
669 if as_builtin {
670 self.validate_event_stream_decl(&event)
671 } else {
672 self.errors.push(Error::CapabilityMustBeBuiltin(DeclType::EventStream))
673 }
674 }
675 #[cfg(fuchsia_api_level_at_least = "25")]
676 fdecl::Capability::Dictionary(dictionary) => {
677 self.validate_dictionary_decl(&dictionary);
678 }
679 #[cfg(fuchsia_api_level_at_least = "HEAD")]
680 fdecl::Capability::Config(config) => {
681 self.validate_configuration_decl(&config);
682 }
683 fdecl::CapabilityUnknown!() => self.errors.push(Error::UnknownCapability),
684 }
685 }
686
687 fn validate_use_decls(
689 &mut self,
690 uses: &'a [fdecl::Use],
691 ) -> (Option<&'a String>, Option<&'a fdecl::Ref>) {
692 for use_ in uses.iter() {
694 self.validate_use_decl(&use_);
695 }
696 self.validate_use_paths(&uses);
697
698 #[cfg(fuchsia_api_level_at_least = "HEAD")]
699 {
700 let mut use_runner_name = None;
701 let mut use_runner_source = None;
702 for use_ in uses.iter() {
703 if let fdecl::Use::Runner(use_runner) = use_ {
704 if use_runner_name.is_some() {
705 self.errors.push(Error::MultipleRunnersUsed);
706 }
707
708 use_runner_name = use_runner.source_name.as_ref();
709 use_runner_source = use_runner.source.as_ref();
710 }
711 }
712 return (use_runner_name, use_runner_source);
713 }
714 #[cfg(fuchsia_api_level_less_than = "HEAD")]
715 return (None, None);
716 }
717
718 fn validate_use_decl(&mut self, use_: &'a fdecl::Use) {
719 match use_ {
720 fdecl::Use::Service(u) => {
721 let decl = DeclType::UseService;
722 self.validate_use_fields(
723 decl,
724 Self::service_checker,
725 u.source.as_ref(),
726 u.source_name.as_ref(),
727 get_source_dictionary!(u),
728 u.target_path.as_ref(),
729 u.dependency_type.as_ref(),
730 u.availability.as_ref(),
731 );
732 if u.dependency_type.is_none() {
733 self.errors.push(Error::missing_field(decl, "dependency_type"));
734 }
735 }
736 fdecl::Use::Protocol(u) => {
737 let decl = DeclType::UseProtocol;
738 self.validate_use_fields(
739 decl,
740 Self::protocol_checker,
741 u.source.as_ref(),
742 u.source_name.as_ref(),
743 get_source_dictionary!(u),
744 u.target_path.as_ref(),
745 u.dependency_type.as_ref(),
746 u.availability.as_ref(),
747 );
748 if u.dependency_type.is_none() {
749 self.errors.push(Error::missing_field(decl, "dependency_type"));
750 }
751 }
752 fdecl::Use::Directory(u) => {
753 let decl = DeclType::UseDirectory;
754 self.validate_use_fields(
755 decl,
756 Self::directory_checker,
757 u.source.as_ref(),
758 u.source_name.as_ref(),
759 get_source_dictionary!(u),
760 u.target_path.as_ref(),
761 u.dependency_type.as_ref(),
762 u.availability.as_ref(),
763 );
764 if u.dependency_type.is_none() {
765 self.errors.push(Error::missing_field(decl, "dependency_type"));
766 }
767 if u.rights.is_none() {
768 self.errors.push(Error::missing_field(DeclType::UseDirectory, "rights"));
769 }
770 if let Some(subdir) = u.subdir.as_ref() {
771 check_relative_path(
772 Some(subdir),
773 DeclType::UseDirectory,
774 "subdir",
775 &mut self.errors,
776 );
777 }
778 }
779 fdecl::Use::Storage(u) => {
780 const SOURCE: Option<fdecl::Ref> = Some(fdecl::Ref::Parent(fdecl::ParentRef {}));
781 const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
782 Some(fdecl::DependencyType::Strong);
783 self.validate_use_fields(
784 DeclType::UseStorage,
785 Self::storage_checker,
786 SOURCE.as_ref(),
787 u.source_name.as_ref(),
788 None,
789 u.target_path.as_ref(),
790 DEPENDENCY_TYPE.as_ref(),
791 u.availability.as_ref(),
792 );
793 }
794 fdecl::Use::EventStream(u) => {
795 const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
796 Some(fdecl::DependencyType::Strong);
797 let decl = DeclType::UseEventStream;
798 self.validate_use_fields(
799 decl,
800 Self::event_stream_checker,
801 u.source.as_ref(),
802 u.source_name.as_ref(),
803 None,
804 u.target_path.as_ref(),
805 DEPENDENCY_TYPE.as_ref(),
806 u.availability.as_ref(),
807 );
808 match u.source {
810 Some(fdecl::Ref::Child(_)) | Some(fdecl::Ref::Parent(_)) => {
811 }
813 Some(fdecl::Ref::Framework(_))
814 | Some(fdecl::Ref::Self_(_))
815 | Some(fdecl::Ref::Debug(_)) => {
816 self.errors.push(Error::invalid_field(decl, "source"));
818 }
819 Some(fdecl::Ref::Collection(_)) | Some(fdecl::RefUnknown!()) | None => {
820 }
822 }
823 if let Some(scope) = &u.scope {
824 for reference in scope {
825 if !matches!(reference, fdecl::Ref::Child(_) | fdecl::Ref::Collection(_)) {
826 self.errors.push(Error::invalid_field(decl, "scope"));
827 }
828 }
829 }
830 }
831 #[cfg(fuchsia_api_level_at_least = "HEAD")]
832 fdecl::Use::Runner(u) => {
833 const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
834 Some(fdecl::DependencyType::Strong);
835 const AVAILABILITY: Option<fdecl::Availability> =
836 Some(fdecl::Availability::Required);
837 let decl = DeclType::UseRunner;
838 self.validate_use_fields(
839 decl,
840 Self::runner_checker,
841 u.source.as_ref(),
842 u.source_name.as_ref(),
843 get_source_dictionary!(u),
844 None,
845 DEPENDENCY_TYPE.as_ref(),
846 AVAILABILITY.as_ref(),
847 );
848 }
849 #[cfg(fuchsia_api_level_at_least = "HEAD")]
850 fdecl::Use::Config(u) => {
851 const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
852 Some(fdecl::DependencyType::Strong);
853 let decl = DeclType::UseConfiguration;
854 self.validate_use_fields(
855 decl,
856 Self::config_checker,
857 u.source.as_ref(),
858 u.source_name.as_ref(),
859 None,
860 None,
861 DEPENDENCY_TYPE.as_ref(),
862 u.availability.as_ref(),
863 );
864 }
865 fdecl::UseUnknown!() => {
866 self.errors.push(Error::invalid_field(DeclType::Component, "use"));
867 }
868 }
869 }
870
871 fn validate_program(
874 &mut self,
875 program: &fdecl::Program,
876 use_runner_name: Option<&String>,
877 _use_runner_source: Option<&fdecl::Ref>,
878 ) {
879 match &program.runner {
880 Some(_) =>
881 {
882 #[cfg(fuchsia_api_level_at_least = "HEAD")]
883 if use_runner_name.is_some() {
884 if use_runner_name != program.runner.as_ref()
885 || _use_runner_source
886 != Some(&fdecl::Ref::Environment(fdecl::EnvironmentRef))
887 {
888 self.errors.push(Error::ConflictingRunners);
889 }
890 }
891 }
892 None => {
893 if use_runner_name.is_none() {
894 self.errors.push(Error::MissingRunner);
895 }
896 }
897 }
898
899 if program.info.is_none() {
900 self.errors.push(Error::missing_field(DeclType::Program, "info"));
901 }
902 }
903
904 fn validate_use_paths(&mut self, uses: &[fdecl::Use]) {
907 #[derive(Debug, PartialEq, Clone, Copy)]
908 struct PathCapability<'a> {
909 decl: DeclType,
910 dir: &'a Path,
911 use_: &'a fdecl::Use,
912 }
913 let mut used_paths = HashMap::new();
914 for use_ in uses.iter() {
915 match use_ {
916 fdecl::Use::Service(fdecl::UseService { target_path: Some(path), .. })
917 | fdecl::Use::Protocol(fdecl::UseProtocol { target_path: Some(path), .. })
918 | fdecl::Use::Directory(fdecl::UseDirectory { target_path: Some(path), .. })
919 | fdecl::Use::Storage(fdecl::UseStorage { target_path: Some(path), .. }) => {
920 let capability = match use_ {
921 fdecl::Use::Service(_) => {
922 let dir = match Path::new(path).parent() {
923 Some(p) => p,
924 None => continue, };
926 PathCapability { decl: DeclType::UseService, dir, use_ }
927 }
928 fdecl::Use::Protocol(_) => {
929 let dir = match Path::new(path).parent() {
930 Some(p) => p,
931 None => continue, };
933 PathCapability { decl: DeclType::UseProtocol, dir, use_ }
934 }
935 fdecl::Use::Directory(_) => PathCapability {
936 decl: DeclType::UseDirectory,
937 dir: Path::new(path),
938 use_,
939 },
940 fdecl::Use::Storage(_) => PathCapability {
941 decl: DeclType::UseStorage,
942 dir: Path::new(path),
943 use_,
944 },
945 _ => unreachable!(),
946 };
947 if used_paths.insert(path, capability).is_some() {
948 self.errors.push(Error::duplicate_field(
950 capability.decl,
951 "target_path",
952 path,
953 ));
954 }
955 }
956 _ => {}
957 }
958 }
959 for ((&path_a, capability_a), (&path_b, capability_b)) in
960 used_paths.iter().tuple_combinations()
961 {
962 if match (capability_a.use_, capability_b.use_) {
963 (fdecl::Use::Directory(_), fdecl::Use::Directory(_))
965 | (fdecl::Use::Storage(_), fdecl::Use::Directory(_))
966 | (fdecl::Use::Directory(_), fdecl::Use::Storage(_))
967 | (fdecl::Use::Storage(_), fdecl::Use::Storage(_)) => {
968 capability_b.dir == capability_a.dir
969 || capability_b.dir.starts_with(capability_a.dir)
970 || capability_a.dir.starts_with(capability_b.dir)
971 }
972
973 (_, fdecl::Use::Directory(_)) | (fdecl::Use::Directory(_), _) => {
975 capability_b.dir == capability_a.dir
976 || capability_b.dir.starts_with(capability_a.dir)
977 || capability_a.dir.starts_with(capability_b.dir)
978 }
979
980 (_, _) => {
983 capability_b.dir != capability_a.dir
984 && (capability_b.dir.starts_with(capability_a.dir)
985 || capability_a.dir.starts_with(capability_b.dir))
986 }
987 } {
988 self.errors.push(Error::invalid_path_overlap(
989 capability_a.decl,
990 path_a,
991 capability_b.decl,
992 path_b,
993 ));
994 }
995 }
996 for (used_path, capability) in used_paths.iter() {
997 if used_path.as_str() == "/pkg" || used_path.starts_with("/pkg/") {
998 self.errors.push(Error::pkg_path_overlap(capability.decl, *used_path));
999 }
1000 }
1001 }
1002
1003 fn validate_use_fields(
1004 &mut self,
1005 decl: DeclType,
1006 capability_checker: impl Fn(&Self) -> &dyn Container,
1010 source: Option<&'a fdecl::Ref>,
1011 source_name: Option<&'a String>,
1012 source_dictionary: Option<&'a String>,
1013 target_path: Option<&'a String>,
1014 dependency_type: Option<&fdecl::DependencyType>,
1015 availability: Option<&'a fdecl::Availability>,
1016 ) {
1017 self.validate_use_source(decl, source, source_dictionary);
1018
1019 check_name(source_name, decl, "source_name", &mut self.errors);
1020 if source_dictionary.is_some() {
1021 check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
1022 }
1023 if decl != DeclType::UseRunner && decl != DeclType::UseConfiguration {
1024 check_path(target_path, decl, "target_path", &mut self.errors);
1025 }
1026 check_use_availability(decl, availability, &mut self.errors);
1027
1028 let is_use_from_child = match source {
1030 Some(fdecl::Ref::Child(_)) => true,
1031 _ => false,
1032 };
1033 match (is_use_from_child, dependency_type) {
1034 (false, Some(fdecl::DependencyType::Weak)) => {
1035 self.errors.push(Error::invalid_field(decl, "dependency_type"));
1036 }
1037 _ => {}
1038 }
1039
1040 self.validate_route_from_self(
1041 decl,
1042 source,
1043 source_name,
1044 source_dictionary,
1045 capability_checker,
1046 );
1047 }
1048
1049 fn validate_use_source(
1050 &mut self,
1051 decl: DeclType,
1052 source: Option<&'a fdecl::Ref>,
1053 source_dictionary: Option<&'a String>,
1054 ) {
1055 match (source, source_dictionary) {
1056 (Some(fdecl::Ref::Parent(_)), _) => {}
1058 (Some(fdecl::Ref::Self_(_)), _) => {}
1059 (Some(fdecl::Ref::Child(child)), _) => {
1060 self.validate_child_ref(decl, "source", &child, OfferType::Static);
1061 return;
1062 }
1063 (Some(fdecl::Ref::Framework(_)), None) => {}
1065 (Some(fdecl::Ref::Debug(_)), None) => {}
1066 (Some(fdecl::Ref::Capability(c)), None) => {
1067 self.validate_source_capability(&c, decl, "source");
1068 return;
1069 }
1070 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1071 (Some(fdecl::Ref::Environment(_)), None) => {}
1072 (Some(fdecl::Ref::Collection(collection)), None) if decl == DeclType::UseService => {
1073 self.validate_collection_ref(decl, "source", &collection);
1074 return;
1075 }
1076 (None, _) => self.errors.push(Error::missing_field(decl, "source")),
1078 (_, _) => self.errors.push(Error::invalid_field(decl, "source")),
1080 }
1081 }
1082
1083 fn validate_child_decl(&mut self, child: &'a fdecl::Child) {
1084 if let Err(mut e) = validate_child(child, check_name) {
1085 self.errors.append(&mut e.errs);
1086 }
1087 if let Some(name) = child.name.as_ref() {
1088 let name: &str = name;
1089 if self.all_children.insert(name, child).is_some() {
1090 self.errors.push(Error::duplicate_field(DeclType::Child, "name", name));
1091 }
1092 }
1093 if let Some(environment) = child.environment.as_ref() {
1094 if !self.all_environment_names.contains(environment.as_str()) {
1095 self.errors.push(Error::invalid_environment(
1096 DeclType::Child,
1097 "environment",
1098 environment,
1099 ));
1100 }
1101 }
1102 }
1103
1104 fn validate_collection_decl(&mut self, collection: &'a fdecl::Collection) {
1105 let name = collection.name.as_ref();
1106 if check_name(name, DeclType::Collection, "name", &mut self.errors) {
1107 let name: &str = name.unwrap();
1108 if !self.all_collections.insert(name) {
1109 self.errors.push(Error::duplicate_field(DeclType::Collection, "name", name));
1110 }
1111 }
1112 if collection.durability.is_none() {
1113 self.errors.push(Error::missing_field(DeclType::Collection, "durability"));
1114 }
1115 if let Some(environment) = collection.environment.as_ref() {
1116 if !self.all_environment_names.contains(environment.as_str()) {
1117 self.errors.push(Error::invalid_environment(
1118 DeclType::Collection,
1119 "environment",
1120 environment,
1121 ));
1122 }
1123 }
1124 }
1126
1127 fn validate_environment_decl(&mut self, environment: &'a fdecl::Environment) {
1128 let name = environment.name.as_ref();
1129 check_name(name, DeclType::Environment, "name", &mut self.errors);
1130 if environment.extends.is_none() {
1131 self.errors.push(Error::missing_field(DeclType::Environment, "extends"));
1132 }
1133 if let Some(runners) = environment.runners.as_ref() {
1134 let mut registered_runners = HashSet::new();
1135 for runner in runners {
1136 self.validate_runner_registration(runner, &mut registered_runners);
1137 }
1138 }
1139 if let Some(resolvers) = environment.resolvers.as_ref() {
1140 let mut registered_schemes = HashSet::new();
1141 for resolver in resolvers {
1142 self.validate_resolver_registration(resolver, &mut registered_schemes);
1143 }
1144 }
1145
1146 match environment.extends.as_ref() {
1147 Some(fdecl::EnvironmentExtends::None) => {
1148 if environment.stop_timeout_ms.is_none() {
1149 self.errors
1150 .push(Error::missing_field(DeclType::Environment, "stop_timeout_ms"));
1151 }
1152 }
1153 None | Some(fdecl::EnvironmentExtends::Realm) => {}
1154 }
1155
1156 if let Some(debugs) = environment.debug_capabilities.as_ref() {
1157 for debug in debugs {
1158 self.validate_environment_debug_registration(debug);
1159 }
1160 }
1161 }
1162
1163 fn validate_runner_registration(
1164 &mut self,
1165 runner_registration: &'a fdecl::RunnerRegistration,
1166 runner_names: &mut HashSet<&'a str>,
1167 ) {
1168 check_name(
1169 runner_registration.source_name.as_ref(),
1170 DeclType::RunnerRegistration,
1171 "source_name",
1172 &mut self.errors,
1173 );
1174 self.validate_registration_source(
1175 runner_registration.source.as_ref(),
1176 DeclType::RunnerRegistration,
1177 );
1178 if let (Some(fdecl::Ref::Self_(_)), Some(ref name)) =
1180 (&runner_registration.source, &runner_registration.source_name)
1181 {
1182 if !self.all_runners.contains(name as &str) {
1183 self.errors.push(Error::invalid_runner(
1184 DeclType::RunnerRegistration,
1185 "source_name",
1186 name,
1187 ));
1188 }
1189 }
1190
1191 check_name(
1192 runner_registration.target_name.as_ref(),
1193 DeclType::RunnerRegistration,
1194 "target_name",
1195 &mut self.errors,
1196 );
1197 if let Some(name) = runner_registration.target_name.as_ref() {
1198 if !runner_names.insert(name.as_str()) {
1199 self.errors.push(Error::duplicate_field(
1200 DeclType::RunnerRegistration,
1201 "target_name",
1202 name,
1203 ));
1204 }
1205 }
1206 }
1207
1208 fn validate_resolver_registration(
1209 &mut self,
1210 resolver_registration: &'a fdecl::ResolverRegistration,
1211 schemes: &mut HashSet<&'a str>,
1212 ) {
1213 check_name(
1214 resolver_registration.resolver.as_ref(),
1215 DeclType::ResolverRegistration,
1216 "resolver",
1217 &mut self.errors,
1218 );
1219 self.validate_registration_source(
1220 resolver_registration.source.as_ref(),
1221 DeclType::ResolverRegistration,
1222 );
1223 check_url_scheme(
1224 resolver_registration.scheme.as_ref(),
1225 DeclType::ResolverRegistration,
1226 "scheme",
1227 &mut self.errors,
1228 );
1229 if let Some(scheme) = resolver_registration.scheme.as_ref() {
1230 if !schemes.insert(scheme.as_str()) {
1231 self.errors.push(Error::duplicate_field(
1232 DeclType::ResolverRegistration,
1233 "scheme",
1234 scheme,
1235 ));
1236 }
1237 }
1238 }
1239
1240 fn validate_registration_source(&mut self, source: Option<&'a fdecl::Ref>, ty: DeclType) {
1241 match source {
1242 Some(fdecl::Ref::Parent(_)) => {}
1243 Some(fdecl::Ref::Self_(_)) => {}
1244 Some(fdecl::Ref::Child(child_ref)) => {
1245 self.validate_child_ref(ty, "source", &child_ref, OfferType::Static);
1247 }
1248 Some(_) => {
1249 self.errors.push(Error::invalid_field(ty, "source"));
1250 }
1251 None => {
1252 self.errors.push(Error::missing_field(ty, "source"));
1253 }
1254 }
1255 }
1256
1257 fn validate_service_decl(&mut self, service: &'a fdecl::Service, as_builtin: bool) {
1258 if check_name(service.name.as_ref(), DeclType::Service, "name", &mut self.errors) {
1259 let name = service.name.as_ref().unwrap();
1260 if !self.all_capability_ids.insert(name) {
1261 self.errors.push(Error::duplicate_field(DeclType::Service, "name", name.as_str()));
1262 }
1263 self.all_services.insert(name);
1264 }
1265 match as_builtin {
1266 true => {
1267 if let Some(path) = service.source_path.as_ref() {
1268 self.errors.push(Error::extraneous_source_path(DeclType::Service, path))
1269 }
1270 }
1271 false => {
1272 check_path(
1273 service.source_path.as_ref(),
1274 DeclType::Service,
1275 "source_path",
1276 &mut self.errors,
1277 );
1278 }
1279 }
1280 }
1281
1282 fn validate_protocol_decl(&mut self, protocol: &'a fdecl::Protocol, as_builtin: bool) {
1283 if check_name(protocol.name.as_ref(), DeclType::Protocol, "name", &mut self.errors) {
1284 let name = protocol.name.as_ref().unwrap();
1285 if !self.all_capability_ids.insert(name) {
1286 self.errors.push(Error::duplicate_field(DeclType::Protocol, "name", name.as_str()));
1287 }
1288 self.all_protocols.insert(name);
1289 }
1290 match as_builtin {
1291 true => {
1292 if let Some(path) = protocol.source_path.as_ref() {
1293 self.errors.push(Error::extraneous_source_path(DeclType::Protocol, path))
1294 }
1295 }
1296 false => {
1297 check_path(
1298 protocol.source_path.as_ref(),
1299 DeclType::Protocol,
1300 "source_path",
1301 &mut self.errors,
1302 );
1303 }
1304 }
1305
1306 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1307 match protocol.delivery {
1308 Some(delivery) => match cm_types::DeliveryType::try_from(delivery) {
1309 Ok(_) => {}
1310 Err(_) => self.errors.push(Error::invalid_field(DeclType::Protocol, "delivery")),
1311 },
1312 None => {}
1313 }
1314 }
1315
1316 fn validate_directory_decl(&mut self, directory: &'a fdecl::Directory, as_builtin: bool) {
1317 if check_name(directory.name.as_ref(), DeclType::Directory, "name", &mut self.errors) {
1318 let name = directory.name.as_ref().unwrap();
1319 if !self.all_capability_ids.insert(name) {
1320 self.errors.push(Error::duplicate_field(
1321 DeclType::Directory,
1322 "name",
1323 name.as_str(),
1324 ));
1325 }
1326 self.all_directories.insert(name);
1327 }
1328 match as_builtin {
1329 true => {
1330 if let Some(path) = directory.source_path.as_ref() {
1331 self.errors.push(Error::extraneous_source_path(DeclType::Directory, path))
1332 }
1333 }
1334 false => {
1335 check_path(
1336 directory.source_path.as_ref(),
1337 DeclType::Directory,
1338 "source_path",
1339 &mut self.errors,
1340 );
1341 }
1342 }
1343 if directory.rights.is_none() {
1344 self.errors.push(Error::missing_field(DeclType::Directory, "rights"));
1345 }
1346 }
1347
1348 fn validate_storage_decl(&mut self, storage: &'a fdecl::Storage) {
1349 match storage.source.as_ref() {
1350 Some(fdecl::Ref::Parent(_)) => {}
1351 Some(fdecl::Ref::Self_(_)) => {}
1352 Some(fdecl::Ref::Child(child)) => {
1353 let _ =
1354 self.validate_child_ref(DeclType::Storage, "source", &child, OfferType::Static);
1355 }
1356 Some(_) => {
1357 self.errors.push(Error::invalid_field(DeclType::Storage, "source"));
1358 }
1359 None => {
1360 self.errors.push(Error::missing_field(DeclType::Storage, "source"));
1361 }
1362 };
1363 if check_name(storage.name.as_ref(), DeclType::Storage, "name", &mut self.errors) {
1364 let name = storage.name.as_ref().unwrap();
1365 if !self.all_capability_ids.insert(name) {
1366 self.errors.push(Error::duplicate_field(DeclType::Storage, "name", name.as_str()));
1367 }
1368 self.all_storages.insert(name, storage.source.as_ref());
1369 }
1370 if storage.storage_id.is_none() {
1371 self.errors.push(Error::missing_field(DeclType::Storage, "storage_id"));
1372 }
1373 check_name(
1374 storage.backing_dir.as_ref(),
1375 DeclType::Storage,
1376 "backing_dir",
1377 &mut self.errors,
1378 );
1379 }
1380
1381 fn validate_runner_decl(&mut self, runner: &'a fdecl::Runner, as_builtin: bool) {
1382 if check_name(runner.name.as_ref(), DeclType::Runner, "name", &mut self.errors) {
1383 let name = runner.name.as_ref().unwrap();
1384 if !self.all_capability_ids.insert(name) {
1385 self.errors.push(Error::duplicate_field(DeclType::Runner, "name", name.as_str()));
1386 }
1387 self.all_runners.insert(name);
1388 }
1389 match as_builtin {
1390 true => {
1391 if let Some(path) = runner.source_path.as_ref() {
1392 self.errors.push(Error::extraneous_source_path(DeclType::Runner, path))
1393 }
1394 }
1395 false => {
1396 check_path(
1397 runner.source_path.as_ref(),
1398 DeclType::Runner,
1399 "source_path",
1400 &mut self.errors,
1401 );
1402 }
1403 }
1404 }
1405
1406 fn validate_resolver_decl(&mut self, resolver: &'a fdecl::Resolver, as_builtin: bool) {
1407 if check_name(resolver.name.as_ref(), DeclType::Resolver, "name", &mut self.errors) {
1408 let name = resolver.name.as_ref().unwrap();
1409 if !self.all_capability_ids.insert(name) {
1410 self.errors.push(Error::duplicate_field(DeclType::Resolver, "name", name.as_str()));
1411 }
1412 self.all_resolvers.insert(name);
1413 }
1414 match as_builtin {
1415 true => {
1416 if let Some(path) = resolver.source_path.as_ref() {
1417 self.errors.push(Error::extraneous_source_path(DeclType::Resolver, path))
1418 }
1419 }
1420 false => {
1421 check_path(
1422 resolver.source_path.as_ref(),
1423 DeclType::Resolver,
1424 "source_path",
1425 &mut self.errors,
1426 );
1427 }
1428 }
1429 }
1430
1431 #[cfg(fuchsia_api_level_at_least = "25")]
1435 fn load_dictionary_names(&mut self, dictionaries: impl Iterator<Item = &'a fdecl::Dictionary>) {
1436 for dictionary in dictionaries {
1437 let decl = DeclType::Dictionary;
1438 if check_name(dictionary.name.as_ref(), decl, "name", &mut self.errors) {
1439 let name = dictionary.name.as_ref().unwrap();
1440 if !self.all_capability_ids.insert(name) {
1441 self.errors.push(Error::duplicate_field(decl, "name", name.as_str()));
1442 }
1443 self.all_dictionaries.insert(name, &dictionary);
1444 }
1445 }
1446 }
1447
1448 #[cfg(fuchsia_api_level_at_least = "25")]
1449 fn validate_dictionary_decl(&mut self, dictionary: &'a fdecl::Dictionary) {
1450 let decl = DeclType::Dictionary;
1451 if let Some(path) = dictionary.source_path.as_ref() {
1452 if dictionary.source.is_some() {
1453 self.errors.push(Error::extraneous_field(decl, "source"));
1454 }
1455 check_path(Some(path), DeclType::Dictionary, "source_path", &mut self.errors);
1456 }
1457 }
1458
1459 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1460 fn validate_configuration_decl(&mut self, config: &'a fdecl::Configuration) {
1461 let decl = DeclType::Configuration;
1462 if check_name(config.name.as_ref(), decl, "name", &mut self.errors) {
1463 let name = config.name.as_ref().unwrap();
1464 if !self.all_capability_ids.insert(name) {
1465 self.errors.push(Error::duplicate_field(decl, "name", name.as_str()));
1466 }
1467 self.all_configs.insert(name);
1468 }
1469 }
1470
1471 fn validate_environment_debug_registration(&mut self, debug: &'a fdecl::DebugRegistration) {
1472 match debug {
1473 fdecl::DebugRegistration::Protocol(o) => {
1474 let decl = DeclType::DebugProtocolRegistration;
1475 self.validate_environment_debug_fields(
1476 decl,
1477 o.source.as_ref(),
1478 o.source_name.as_ref(),
1479 o.target_name.as_ref(),
1480 );
1481
1482 if let (Some(fdecl::Ref::Self_(_)), Some(ref name)) = (&o.source, &o.source_name) {
1483 if !self.all_protocols.contains(&name as &str) {
1484 self.errors.push(Error::invalid_field(decl, "source"));
1485 }
1486 }
1487 }
1488 _ => {
1489 self.errors.push(Error::invalid_field(DeclType::Environment, "debug"));
1490 }
1491 }
1492 }
1493
1494 fn validate_environment_debug_fields(
1495 &mut self,
1496 decl: DeclType,
1497 source: Option<&fdecl::Ref>,
1498 source_name: Option<&String>,
1499 target_name: Option<&'a String>,
1500 ) {
1501 match source {
1503 Some(fdecl::Ref::Parent(_)) => {}
1504 Some(fdecl::Ref::Self_(_)) => {}
1505 Some(fdecl::Ref::Framework(_)) => {}
1506 Some(fdecl::Ref::Child(child)) => {
1507 let _ = self.validate_child_ref(decl, "source", &child, OfferType::Static);
1508 }
1509 Some(_) => self.errors.push(Error::invalid_field(decl, "source")),
1510 None => self.errors.push(Error::missing_field(decl, "source")),
1511 }
1512 check_name(source_name, decl, "source_name", &mut self.errors);
1513 check_name(target_name, decl, "target_name", &mut self.errors);
1514 }
1515
1516 fn validate_event_stream_decl(&mut self, event: &'a fdecl::EventStream) {
1517 if check_name(event.name.as_ref(), DeclType::EventStream, "name", &mut self.errors) {
1518 let name = event.name.as_ref().unwrap();
1519 if !self.all_capability_ids.insert(name) {
1520 self.errors.push(Error::duplicate_field(
1521 DeclType::EventStream,
1522 "name",
1523 name.as_str(),
1524 ));
1525 }
1526 }
1527 }
1528
1529 fn validate_source_collection(
1530 &mut self,
1531 collection: &fdecl::CollectionRef,
1532 decl_type: DeclType,
1533 ) -> bool {
1534 let num_errors = self.errors.len();
1535 if check_name(Some(&collection.name), decl_type, "source.collection.name", &mut self.errors)
1536 && !self.all_collections.contains(&collection.name as &str)
1537 {
1538 self.errors.push(Error::invalid_collection(
1539 decl_type,
1540 "source",
1541 &collection.name as &str,
1542 ));
1543 }
1544 num_errors == self.errors.len()
1545 }
1546
1547 fn validate_filtered_service_fields(
1548 &mut self,
1549 decl_type: DeclType,
1550 source_instance_filter: Option<&Vec<String>>,
1551 renamed_instances: Option<&Vec<fdecl::NameMapping>>,
1552 ) {
1553 if let Some(source_instance_filter) = source_instance_filter {
1554 if source_instance_filter.is_empty() {
1555 self.errors.push(Error::invalid_field(decl_type, "source_instance_filter"));
1558 }
1559 for name in source_instance_filter {
1560 check_name(Some(name), decl_type, "source_instance_filter", &mut self.errors);
1561 }
1562 }
1563 if let Some(renamed_instances) = renamed_instances {
1564 let mut seen_target_names = HashSet::<String>::new();
1566 for mapping in renamed_instances {
1567 check_name(
1568 Some(&mapping.source_name),
1569 decl_type,
1570 "renamed_instances.source_name",
1571 &mut self.errors,
1572 );
1573 check_name(
1574 Some(&mapping.target_name),
1575 decl_type,
1576 "renamed_instances.target_name",
1577 &mut self.errors,
1578 );
1579 if !seen_target_names.insert(mapping.target_name.clone()) {
1580 self.errors.push(Error::invalid_field(decl_type, "renamed_instances"));
1581 break;
1582 }
1583 }
1584 }
1585 }
1586
1587 fn validate_source_capability(
1588 &mut self,
1589 capability: &fdecl::CapabilityRef,
1590 decl_type: DeclType,
1591 field: &str,
1592 ) -> bool {
1593 let num_errors = self.errors.len();
1594 if check_name(Some(&capability.name), decl_type, "source.capability.name", &mut self.errors)
1595 && !self.all_capability_ids.contains(capability.name.as_str())
1596 {
1597 self.errors.push(Error::invalid_capability(decl_type, field, &capability.name));
1598 }
1599 num_errors == self.errors.len()
1600 }
1601
1602 fn make_group_key(
1606 target_name: Option<&'a String>,
1607 target: Option<&'a fdecl::Ref>,
1608 ) -> Option<(&'a str, RefKey<'a>)> {
1609 if target_name.is_none() {
1610 return None;
1611 }
1612 let target_name = target_name.unwrap().as_str();
1613 if target.is_none() {
1614 return None;
1615 }
1616 let target = match target.unwrap() {
1617 fdecl::Ref::Parent(_) => RefKey::Parent,
1618 fdecl::Ref::Self_(_) => RefKey::Self_,
1619 fdecl::Ref::Child(r) => RefKey::Child(r.name.as_str()),
1620 fdecl::Ref::Collection(r) => RefKey::Collection(r.name.as_str()),
1621 fdecl::Ref::Framework(_) => RefKey::Framework,
1622 fdecl::Ref::Capability(_) => RefKey::Capability,
1623 fdecl::Ref::Debug(_) => RefKey::Debug,
1624 fdecl::RefUnknown!() => {
1625 return None;
1626 }
1627 };
1628 Some((target_name, target))
1629 }
1630
1631 fn validate_aggregation_has_same_availability(
1632 &mut self,
1633 route_group: &Vec<impl HasAvailability>,
1634 ) {
1635 let availability_of_sources: BTreeSet<_> =
1637 route_group.iter().map(|r| r.availability()).collect();
1638
1639 if availability_of_sources.len() > 1 {
1641 self.errors.push(Error::different_availability_in_aggregation(
1642 availability_of_sources.into_iter().collect(),
1643 ));
1644 }
1645 }
1646
1647 fn validate_expose_group(&mut self, exposes: &'a Vec<fdecl::Expose>) {
1650 let mut expose_groups: HashMap<_, Vec<fdecl::ExposeService>> = HashMap::new();
1651 let service_exposes = exposes.into_iter().filter_map(|o| {
1652 if let fdecl::Expose::Service(s) = o {
1653 Some(s)
1654 } else {
1655 None
1656 }
1657 });
1658 for expose in service_exposes {
1659 let key = Self::make_group_key(expose.target_name.as_ref(), expose.target.as_ref());
1660 if let Some(key) = key {
1661 expose_groups.entry(key).or_insert_with(|| vec![]).push(expose.clone());
1662 }
1663 }
1664 for expose_group in expose_groups.into_values() {
1665 if expose_group.len() == 1 {
1666 continue;
1669 }
1670
1671 self.validate_aggregation_has_same_availability(&expose_group);
1672 }
1673 }
1674
1675 fn validate_expose_decl(
1676 &mut self,
1677 expose: &'a fdecl::Expose,
1678 expose_to_parent_ids: &mut HashMap<&'a str, AllowableIds>,
1679 expose_to_framework_ids: &mut HashMap<&'a str, AllowableIds>,
1680 ) {
1681 match expose {
1682 fdecl::Expose::Service(e) => {
1683 let decl = DeclType::ExposeService;
1684 self.validate_expose_fields(
1685 decl,
1686 AllowableIds::Many,
1687 CollectionSource::Allow,
1688 Self::service_checker,
1689 e.source.as_ref(),
1690 e.source_name.as_ref(),
1691 get_source_dictionary!(e),
1692 e.target.as_ref(),
1693 e.target_name.as_ref(),
1694 e.availability.as_ref(),
1695 expose_to_parent_ids,
1696 expose_to_framework_ids,
1697 );
1698 }
1699 fdecl::Expose::Protocol(e) => {
1700 let decl = DeclType::ExposeProtocol;
1701 self.validate_expose_fields(
1702 decl,
1703 AllowableIds::One,
1704 CollectionSource::Deny,
1705 Self::protocol_checker,
1706 e.source.as_ref(),
1707 e.source_name.as_ref(),
1708 get_source_dictionary!(e),
1709 e.target.as_ref(),
1710 e.target_name.as_ref(),
1711 e.availability.as_ref(),
1712 expose_to_parent_ids,
1713 expose_to_framework_ids,
1714 );
1715 }
1716 fdecl::Expose::Directory(e) => {
1717 let decl = DeclType::ExposeDirectory;
1718 self.validate_expose_fields(
1719 decl,
1720 AllowableIds::One,
1721 CollectionSource::Deny,
1722 Self::directory_checker,
1723 e.source.as_ref(),
1724 e.source_name.as_ref(),
1725 get_source_dictionary!(e),
1726 e.target.as_ref(),
1727 e.target_name.as_ref(),
1728 e.availability.as_ref(),
1729 expose_to_parent_ids,
1730 expose_to_framework_ids,
1731 );
1732
1733 match e.target.as_ref() {
1736 Some(fdecl::Ref::Framework(_)) => {
1737 if e.subdir.is_some() {
1738 self.errors.push(Error::invalid_field(decl, "subdir"));
1739 }
1740 }
1741 _ => {}
1742 }
1743
1744 if let Some(subdir) = e.subdir.as_ref() {
1745 check_relative_path(Some(subdir), decl, "subdir", &mut self.errors);
1746 }
1747 }
1748 fdecl::Expose::Runner(e) => {
1749 let decl = DeclType::ExposeRunner;
1750 self.validate_expose_fields(
1751 decl,
1752 AllowableIds::One,
1753 CollectionSource::Deny,
1754 Self::runner_checker,
1755 e.source.as_ref(),
1756 e.source_name.as_ref(),
1757 get_source_dictionary!(e),
1758 e.target.as_ref(),
1759 e.target_name.as_ref(),
1760 Some(&fdecl::Availability::Required),
1761 expose_to_parent_ids,
1762 expose_to_framework_ids,
1763 );
1764 }
1765 fdecl::Expose::Resolver(e) => {
1766 let decl = DeclType::ExposeResolver;
1767 self.validate_expose_fields(
1768 decl,
1769 AllowableIds::One,
1770 CollectionSource::Deny,
1771 Self::resolver_checker,
1772 e.source.as_ref(),
1773 e.source_name.as_ref(),
1774 get_source_dictionary!(e),
1775 e.target.as_ref(),
1776 e.target_name.as_ref(),
1777 Some(&fdecl::Availability::Required),
1778 expose_to_parent_ids,
1779 expose_to_framework_ids,
1780 );
1781 }
1782 #[cfg(fuchsia_api_level_at_least = "25")]
1783 fdecl::Expose::Dictionary(e) => {
1784 let decl = DeclType::ExposeDictionary;
1785 self.validate_expose_fields(
1786 decl,
1787 AllowableIds::One,
1788 CollectionSource::Deny,
1789 Self::dictionary_checker,
1790 e.source.as_ref(),
1791 e.source_name.as_ref(),
1792 get_source_dictionary!(e),
1793 e.target.as_ref(),
1794 e.target_name.as_ref(),
1795 Some(&fdecl::Availability::Required),
1796 expose_to_parent_ids,
1797 expose_to_framework_ids,
1798 );
1799 }
1800 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1801 fdecl::Expose::Config(e) => {
1802 let decl = DeclType::ExposeConfig;
1803 self.validate_expose_fields(
1804 decl,
1805 AllowableIds::One,
1806 CollectionSource::Deny,
1807 Self::config_checker,
1808 e.source.as_ref(),
1809 e.source_name.as_ref(),
1810 None,
1811 e.target.as_ref(),
1812 e.target_name.as_ref(),
1813 e.availability.as_ref(),
1814 expose_to_parent_ids,
1815 expose_to_framework_ids,
1816 );
1817 }
1818 _ => {
1819 self.errors.push(Error::invalid_field(DeclType::Component, "expose"));
1820 }
1821 }
1822 }
1823
1824 fn validate_expose_fields(
1825 &mut self,
1826 decl: DeclType,
1827 allowable_ids: AllowableIds,
1828 collection_source: CollectionSource,
1829 capability_checker: impl Fn(&Self) -> &dyn Container,
1833 source: Option<&fdecl::Ref>,
1834 source_name: Option<&String>,
1835 source_dictionary: Option<&String>,
1836 target: Option<&fdecl::Ref>,
1837 target_name: Option<&'a String>,
1838 availability: Option<&fdecl::Availability>,
1839 expose_to_parent_ids: &mut HashMap<&'a str, AllowableIds>,
1840 expose_to_framework_ids: &mut HashMap<&'a str, AllowableIds>,
1841 ) {
1842 self.validate_expose_source(decl, collection_source, source, source_dictionary);
1843 check_route_availability(decl, availability, source, source_name, &mut self.errors);
1844 match target {
1845 Some(r) => match r {
1846 fdecl::Ref::Parent(_) => {}
1847 fdecl::Ref::Framework(_) => {}
1848 _ => {
1849 self.errors.push(Error::invalid_field(decl, "target"));
1850 }
1851 },
1852 None => {
1853 self.errors.push(Error::missing_field(decl, "target"));
1854 }
1855 }
1856 check_name(source_name, decl, "source_name", &mut self.errors);
1857 if source_dictionary.is_some() {
1858 check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
1859 }
1860 if check_name(target_name, decl, "target_name", &mut self.errors) {
1861 let maybe_ids_set = match target {
1862 Some(fdecl::Ref::Parent(_)) => Some(expose_to_parent_ids),
1863 Some(fdecl::Ref::Framework(_)) => Some(expose_to_framework_ids),
1864 _ => None,
1865 };
1866 if let Some(ids_set) = maybe_ids_set {
1867 let target_name = target_name.unwrap();
1868 if let Some(prev_state) = ids_set.insert(target_name, allowable_ids) {
1869 if prev_state == AllowableIds::One || prev_state != allowable_ids {
1870 self.errors.push(Error::duplicate_field(decl, "target_name", target_name));
1871 }
1872 }
1873 }
1874 }
1875
1876 self.validate_route_from_self(
1877 decl,
1878 source,
1879 source_name,
1880 source_dictionary,
1881 capability_checker,
1882 );
1883 }
1884
1885 fn validate_expose_source(
1886 &mut self,
1887 decl: DeclType,
1888 collection_source: CollectionSource,
1889 source: Option<&fdecl::Ref>,
1890 source_dictionary: Option<&String>,
1891 ) {
1892 match (source, source_dictionary) {
1893 (Some(fdecl::Ref::Self_(_)), _) => {}
1895 (Some(fdecl::Ref::Child(child)), _) => {
1896 let _ = self.validate_child_ref(decl, "source", &child, OfferType::Static);
1897 }
1898 (Some(fdecl::Ref::VoidType(_)), None) => {}
1900 (Some(fdecl::Ref::Framework(_)), None) => {}
1901 (Some(fdecl::Ref::Capability(c)), None) => {
1902 self.validate_source_capability(c, decl, "source");
1903 }
1904 (Some(fdecl::Ref::Collection(c)), None)
1905 if collection_source == CollectionSource::Allow =>
1906 {
1907 self.validate_source_collection(c, decl);
1908 }
1909 (None, _) => {
1911 self.errors.push(Error::missing_field(decl, "source"));
1912 }
1913 (_, _) => {
1915 self.errors.push(Error::invalid_field(decl, "source"));
1916 }
1917 }
1918 }
1919
1920 fn validate_offer_group(&mut self, offers: &'a Vec<fdecl::Offer>, offer_type: OfferType) {
1923 let mut offer_groups: HashMap<_, Vec<fdecl::OfferService>> = HashMap::new();
1924 let service_offers = offers.into_iter().filter_map(|o| {
1925 if let fdecl::Offer::Service(s) = o {
1926 Some(s)
1927 } else {
1928 None
1929 }
1930 });
1931 for offer in service_offers {
1932 let key = Self::make_group_key(offer.target_name.as_ref(), offer.target.as_ref());
1933 if let Some(key) = key {
1934 offer_groups.entry(key).or_insert_with(|| vec![]).push(offer.clone());
1935 }
1936 }
1937 for offer_group in offer_groups.into_values() {
1938 if offer_group.len() == 1 {
1939 continue;
1942 }
1943
1944 self.validate_aggregation_has_same_availability(&offer_group);
1945
1946 let mut source_instance_filter_entries: HashSet<String> = HashSet::new();
1947 let mut service_source_names: HashSet<String> = HashSet::new();
1948 for o in offer_group {
1949 match (o.source_instance_filter, offer_type) {
1951 (Some(source_instance_filter), _) => {
1952 for instance_name in source_instance_filter {
1953 if !source_instance_filter_entries.insert(instance_name.clone()) {
1954 self.errors.push(Error::invalid_aggregate_offer(format!(
1957 "Conflicting source_instance_filter in aggregate service \
1958 offer, instance_name '{}' seen in filter lists multiple times",
1959 instance_name,
1960 )));
1961 }
1962 }
1963 }
1964 (None, OfferType::Static) => {}
1965 (None, OfferType::Dynamic) => {
1966 self.errors.push(Error::invalid_aggregate_offer(
1968 "source_instance_filter must be set for dynamic aggregate service \
1969 offers",
1970 ));
1971 }
1972 }
1973 service_source_names.insert(
1974 o.source_name
1975 .expect("Offer Service declarations must always contain source_name"),
1976 );
1977 }
1978
1979 if service_source_names.len() > 1 {
1980 self.errors.push(Error::invalid_aggregate_offer(format!(
1981 "All aggregate service offers must have the same source_name, saw {}. Use \
1982 renamed_instances to rename instance names to avoid conflict.",
1983 service_source_names.into_iter().sorted().collect::<Vec<String>>().join(", ")
1984 )));
1985 }
1986 }
1987 }
1988
1989 fn validate_offer_decl(&mut self, offer: &'a fdecl::Offer, offer_type: OfferType) {
1990 match offer {
1991 fdecl::Offer::Service(o) => {
1992 let decl = DeclType::OfferService;
1993 self.validate_offer_fields(
1994 decl,
1995 AllowableIds::Many,
1996 CollectionSource::Allow,
1997 Self::service_checker,
1998 o.source.as_ref(),
1999 o.source_name.as_ref(),
2000 get_source_dictionary!(o),
2001 o.target.as_ref(),
2002 o.target_name.as_ref(),
2003 #[cfg(fuchsia_api_level_at_least = "HEAD")]
2004 Some(o.dependency_type.as_ref().unwrap_or(&fdecl::DependencyType::Strong)),
2005 #[cfg(fuchsia_api_level_less_than = "HEAD")]
2006 Some(&fdecl::DependencyType::Strong),
2007 o.availability.as_ref(),
2008 offer_type,
2009 );
2010 self.validate_filtered_service_fields(
2011 decl,
2012 o.source_instance_filter.as_ref(),
2013 o.renamed_instances.as_ref(),
2014 );
2015 }
2016 fdecl::Offer::Protocol(o) => {
2017 let decl = DeclType::OfferProtocol;
2018 self.validate_offer_fields(
2019 decl,
2020 AllowableIds::One,
2021 CollectionSource::Deny,
2022 Self::protocol_checker,
2023 o.source.as_ref(),
2024 o.source_name.as_ref(),
2025 get_source_dictionary!(o),
2026 o.target.as_ref(),
2027 o.target_name.as_ref(),
2028 o.dependency_type.as_ref(),
2029 o.availability.as_ref(),
2030 offer_type,
2031 );
2032 }
2033 fdecl::Offer::Directory(o) => {
2034 let decl = DeclType::OfferDirectory;
2035 self.validate_offer_fields(
2036 decl,
2037 AllowableIds::One,
2038 CollectionSource::Deny,
2039 Self::directory_checker,
2040 o.source.as_ref(),
2041 o.source_name.as_ref(),
2042 get_source_dictionary!(o),
2043 o.target.as_ref(),
2044 o.target_name.as_ref(),
2045 o.dependency_type.as_ref(),
2046 o.availability.as_ref(),
2047 offer_type,
2048 );
2049 if let Some(subdir) = o.subdir.as_ref() {
2050 check_relative_path(
2051 Some(subdir),
2052 DeclType::OfferDirectory,
2053 "subdir",
2054 &mut self.errors,
2055 );
2056 }
2057 }
2058 fdecl::Offer::Storage(o) => {
2059 let decl = DeclType::OfferStorage;
2060 self.validate_storage_offer_fields(
2061 decl,
2062 Self::storage_checker,
2063 o.source.as_ref(),
2064 o.source_name.as_ref(),
2065 o.target.as_ref(),
2066 o.target_name.as_ref(),
2067 o.availability.as_ref(),
2068 offer_type,
2069 );
2070 }
2071 fdecl::Offer::Runner(o) => {
2072 let decl = DeclType::OfferRunner;
2073 self.validate_offer_fields(
2074 decl,
2075 AllowableIds::One,
2076 CollectionSource::Deny,
2077 Self::runner_checker,
2078 o.source.as_ref(),
2079 o.source_name.as_ref(),
2080 get_source_dictionary!(o),
2081 o.target.as_ref(),
2082 o.target_name.as_ref(),
2083 Some(&fdecl::DependencyType::Strong),
2084 Some(&fdecl::Availability::Required),
2085 offer_type,
2086 );
2087 }
2088 fdecl::Offer::Resolver(o) => {
2089 let decl = DeclType::OfferResolver;
2090 self.validate_offer_fields(
2091 decl,
2092 AllowableIds::One,
2093 CollectionSource::Deny,
2094 Self::resolver_checker,
2095 o.source.as_ref(),
2096 o.source_name.as_ref(),
2097 get_source_dictionary!(o),
2098 o.target.as_ref(),
2099 o.target_name.as_ref(),
2100 Some(&fdecl::DependencyType::Strong),
2101 Some(&fdecl::Availability::Required),
2102 offer_type,
2103 );
2104 }
2105 fdecl::Offer::EventStream(e) => {
2106 self.validate_event_stream_offer_fields(e, offer_type);
2107 }
2108 #[cfg(fuchsia_api_level_at_least = "25")]
2109 fdecl::Offer::Dictionary(o) => {
2110 let decl = DeclType::OfferDictionary;
2111 self.validate_offer_fields(
2112 decl,
2113 AllowableIds::One,
2114 CollectionSource::Deny,
2115 Self::dictionary_checker,
2116 o.source.as_ref(),
2117 o.source_name.as_ref(),
2118 get_source_dictionary!(o),
2119 o.target.as_ref(),
2120 o.target_name.as_ref(),
2121 o.dependency_type.as_ref(),
2122 o.availability.as_ref(),
2123 offer_type,
2124 );
2125 }
2126 #[cfg(fuchsia_api_level_at_least = "HEAD")]
2127 fdecl::Offer::Config(o) => {
2128 let decl = DeclType::OfferConfig;
2129 self.validate_offer_fields(
2130 decl,
2131 AllowableIds::One,
2132 CollectionSource::Deny,
2133 Self::config_checker,
2134 o.source.as_ref(),
2135 o.source_name.as_ref(),
2136 None,
2137 o.target.as_ref(),
2138 o.target_name.as_ref(),
2139 Some(&fdecl::DependencyType::Strong),
2140 o.availability.as_ref(),
2141 offer_type,
2142 );
2143 }
2144 fdecl::OfferUnknown!() => {
2145 self.errors.push(Error::invalid_field(DeclType::Component, "offer"));
2146 }
2147 }
2148 }
2149
2150 fn validate_offer_fields(
2151 &mut self,
2152 decl: DeclType,
2153 allowable_names: AllowableIds,
2154 collection_source: CollectionSource,
2155 capability_checker: impl Fn(&Self) -> &dyn Container,
2156 source: Option<&'a fdecl::Ref>,
2157 source_name: Option<&'a String>,
2158 source_dictionary: Option<&'a String>,
2159 target: Option<&'a fdecl::Ref>,
2160 target_name: Option<&'a String>,
2161 dependency_type: Option<&'a fdecl::DependencyType>,
2162 availability: Option<&'a fdecl::Availability>,
2163 offer_type: OfferType,
2164 ) {
2165 self.validate_offer_source(decl, collection_source, source, source_dictionary, offer_type);
2166 check_route_availability(decl, availability, source, source_name, &mut self.errors);
2167 check_offer_name(source_name, decl, "source_name", offer_type, &mut self.errors);
2168 if source_dictionary.is_some() {
2169 check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
2170 }
2171 self.validate_offer_target(decl, allowable_names, target, target_name, offer_type);
2172 check_offer_name(target_name, decl, "target_name", offer_type, &mut self.errors);
2173
2174 if dependency_type.is_none() {
2175 self.errors.push(Error::missing_field(decl, "dependency_type"));
2176 }
2177
2178 self.validate_route_from_self(
2179 decl,
2180 source,
2181 source_name,
2182 source_dictionary,
2183 capability_checker,
2184 );
2185 }
2186
2187 fn validate_offer_source(
2188 &mut self,
2189 decl: DeclType,
2190 collection_source: CollectionSource,
2191 source: Option<&'a fdecl::Ref>,
2192 source_dictionary: Option<&'a String>,
2193 offer_type: OfferType,
2194 ) {
2195 match (source, source_dictionary) {
2196 (Some(fdecl::Ref::Parent(_)), _) => {}
2198 (Some(fdecl::Ref::Self_(_)), _) => {}
2199 (Some(fdecl::Ref::Child(child)), _) => {
2200 self.validate_child_ref(decl, "source", &child, offer_type);
2201 }
2202 (Some(fdecl::Ref::VoidType(_)), None) => {}
2204 (Some(fdecl::Ref::Framework(_)), None) => {}
2205 (Some(fdecl::Ref::Capability(c)), None) => {
2206 self.validate_source_capability(c, decl, "source");
2207 }
2208 (Some(fdecl::Ref::Collection(c)), None)
2209 if collection_source == CollectionSource::Allow =>
2210 {
2211 self.validate_source_collection(c, decl);
2212 }
2213 (None, _) => self.errors.push(Error::missing_field(decl, "source")),
2215 (_, _) => self.errors.push(Error::invalid_field(decl, "source")),
2217 }
2218 }
2219
2220 fn validate_storage_offer_fields(
2221 &mut self,
2222 decl: DeclType,
2223 capability_checker: impl Fn(&Self) -> &dyn Container,
2227 source: Option<&'a fdecl::Ref>,
2228 source_name: Option<&'a String>,
2229 target: Option<&'a fdecl::Ref>,
2230 target_name: Option<&'a String>,
2231 availability: Option<&fdecl::Availability>,
2232 offer_type: OfferType,
2233 ) {
2234 match source {
2235 Some(fdecl::Ref::Parent(_) | fdecl::Ref::VoidType(_) | fdecl::Ref::Self_(_)) => {}
2236 Some(_) => {
2237 self.errors.push(Error::invalid_field(decl, "source"));
2238 }
2239 None => {
2240 self.errors.push(Error::missing_field(decl, "source"));
2241 }
2242 }
2243 check_route_availability(decl, availability, source, source_name, &mut self.errors);
2244 check_offer_name(source_name, decl, "source_name", offer_type, &mut self.errors);
2245 self.validate_offer_target(decl, AllowableIds::One, target, target_name, offer_type);
2246 check_offer_name(target_name, decl, "target_name", offer_type, &mut self.errors);
2247
2248 if let (Some(fdecl::Ref::Self_(_)), Some(name)) = (source, source_name) {
2249 if !(capability_checker)(self).contains(name) {
2250 self.errors.push(Error::invalid_capability(decl, "source", name));
2251 }
2252 }
2253 }
2254
2255 fn validate_event_stream_offer_fields(
2256 &mut self,
2257 event_stream: &'a fdecl::OfferEventStream,
2258 offer_type: OfferType,
2259 ) {
2260 let decl = DeclType::OfferEventStream;
2261 check_name(event_stream.source_name.as_ref(), decl, "source_name", &mut self.errors);
2262 if event_stream.target == Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})) {
2263 self.errors.push(Error::invalid_field(DeclType::OfferEventStream, "target"));
2265 }
2266 if let Some(scope) = &event_stream.scope {
2267 if scope.is_empty() {
2268 self.errors.push(Error::invalid_field(decl, "scope"));
2269 }
2270 for value in scope {
2271 match value {
2272 fdecl::Ref::Child(child) => {
2273 self.validate_child_ref(
2274 DeclType::OfferEventStream,
2275 "scope",
2276 &child,
2277 offer_type,
2278 );
2279 }
2280 fdecl::Ref::Collection(collection) => {
2281 self.validate_collection_ref(
2282 DeclType::OfferEventStream,
2283 "scope",
2284 &collection,
2285 );
2286 }
2287 _ => {
2288 self.errors.push(Error::invalid_field(DeclType::OfferEventStream, "scope"));
2289 }
2290 }
2291 }
2292 }
2293 match event_stream.source {
2295 Some(
2296 fdecl::Ref::Parent(_)
2297 | fdecl::Ref::Framework(_)
2298 | fdecl::Ref::Child(_)
2299 | fdecl::Ref::VoidType(_),
2300 ) => {}
2301 Some(_) => {
2302 self.errors.push(Error::invalid_field(decl, "source"));
2303 }
2304 None => {
2305 self.errors.push(Error::missing_field(decl, "source"));
2306 }
2307 };
2308
2309 check_route_availability(
2310 decl,
2311 event_stream.availability.as_ref(),
2312 event_stream.source.as_ref(),
2313 event_stream.source_name.as_ref(),
2314 &mut self.errors,
2315 );
2316
2317 self.validate_offer_target(
2318 decl,
2319 AllowableIds::One,
2320 event_stream.target.as_ref(),
2321 event_stream.target_name.as_ref(),
2322 offer_type,
2323 );
2324 check_name(event_stream.target_name.as_ref(), decl, "target_name", &mut self.errors);
2325 }
2326
2327 fn validate_child_ref(
2329 &mut self,
2330 decl: DeclType,
2331 field_name: &str,
2332 child: &fdecl::ChildRef,
2333 offer_type: OfferType,
2334 ) -> bool {
2335 if offer_type == OfferType::Dynamic && child.collection.is_some() {
2336 return self.validate_dynamic_child_ref(decl, field_name, child);
2337 }
2338 let mut valid = true;
2342 if !check_name(
2343 Some(&child.name),
2344 decl,
2345 &format!("{}.child.name", field_name),
2346 &mut self.errors,
2347 ) {
2348 valid = false;
2349 }
2350 if child.collection.is_some() {
2351 self.errors
2352 .push(Error::extraneous_field(decl, format!("{}.child.collection", field_name)));
2353 valid = false;
2354 }
2355 if !valid {
2356 return false;
2357 }
2358
2359 let name: &str = &child.name;
2361 if !self.all_children.contains_key(name) {
2362 self.errors.push(Error::invalid_child(decl, field_name, name));
2363 return false;
2364 }
2365
2366 true
2367 }
2368
2369 fn validate_dynamic_child_ref(
2374 &mut self,
2375 decl: DeclType,
2376 field_name: &str,
2377 child: &fdecl::ChildRef,
2378 ) -> bool {
2379 let mut valid = true;
2383 if !check_dynamic_name(
2384 Some(&child.name),
2385 decl,
2386 &format!("{}.child.name", field_name),
2387 &mut self.errors,
2388 ) {
2389 valid = false;
2390 }
2391 if !check_name(
2392 child.collection.as_ref(),
2393 decl,
2394 &format!("{}.child.collection", field_name),
2395 &mut self.errors,
2396 ) {
2397 valid = false;
2398 }
2399 valid
2400 }
2401
2402 fn validate_collection_ref(
2404 &mut self,
2405 decl: DeclType,
2406 field_name: &str,
2407 collection: &fdecl::CollectionRef,
2408 ) -> bool {
2409 if !check_name(
2411 Some(&collection.name),
2412 decl,
2413 &format!("{}.collection.name", field_name),
2414 &mut self.errors,
2415 ) {
2416 return false;
2417 }
2418
2419 if !self.all_collections.contains(&collection.name as &str) {
2421 self.errors.push(Error::invalid_collection(decl, field_name, &collection.name as &str));
2422 return false;
2423 }
2424
2425 true
2426 }
2427
2428 fn validate_offer_target(
2429 &mut self,
2430 decl: DeclType,
2431 allowable_names: AllowableIds,
2432 target: Option<&'a fdecl::Ref>,
2433 target_name: Option<&'a String>,
2434 offer_type: OfferType,
2435 ) {
2436 match target {
2437 Some(fdecl::Ref::Child(c)) => {
2438 self.validate_target_child(decl, allowable_names, c, target_name, offer_type);
2439 }
2440 Some(fdecl::Ref::Collection(c)) => {
2441 self.validate_target_collection(decl, allowable_names, c, target_name);
2442 }
2443 Some(fdecl::Ref::Capability(c)) => {
2444 #[cfg(fuchsia_api_level_at_least = "25")]
2446 if let Some(d) = self.all_dictionaries.get(&c.name.as_str()) {
2447 if d.source_path.is_some() {
2448 self.errors.push(Error::invalid_field(decl, "target"));
2451 }
2452 } else {
2453 self.errors.push(Error::invalid_field(decl, "target"));
2454 }
2455 #[cfg(not(fuchsia_api_level_at_least = "25"))]
2456 {
2457 let _ = c;
2458 self.errors.push(Error::invalid_field(decl, "target"));
2459 }
2460 }
2461 Some(_) => {
2462 self.errors.push(Error::invalid_field(decl, "target"));
2463 }
2464 None => {
2465 self.errors.push(Error::missing_field(decl, "target"));
2466 }
2467 }
2468 }
2469
2470 fn validate_target_child(
2471 &mut self,
2472 decl: DeclType,
2473 allowable_names: AllowableIds,
2474 child: &'a fdecl::ChildRef,
2475 target_name: Option<&'a String>,
2476 offer_type: OfferType,
2477 ) {
2478 if !self.validate_child_ref(decl, "target", child, offer_type) {
2479 return;
2480 }
2481 if let Some(target_name) = target_name {
2482 let names_for_target = self
2483 .target_ids
2484 .entry(TargetId::Component(
2485 &child.name,
2486 child.collection.as_ref().map(|s| s.as_str()),
2487 ))
2488 .or_insert(HashMap::new());
2489 if let Some(prev_state) = names_for_target.insert(target_name, allowable_names) {
2490 if prev_state == AllowableIds::One || prev_state != allowable_names {
2491 self.errors.push(Error::duplicate_field(
2492 decl,
2493 "target_name",
2494 target_name as &str,
2495 ));
2496 }
2497 }
2498 if let Some(collection) = child.collection.as_ref() {
2499 if let Some(names_for_target) =
2500 self.target_ids.get(&TargetId::Collection(&collection))
2501 {
2502 if names_for_target.contains_key(&target_name.as_str()) {
2503 self.errors.push(Error::duplicate_field(
2505 decl,
2506 "target_name",
2507 target_name as &str,
2508 ));
2509 }
2510 }
2511 }
2512 }
2513 }
2514
2515 fn validate_target_collection(
2516 &mut self,
2517 decl: DeclType,
2518 allowable_names: AllowableIds,
2519 collection: &'a fdecl::CollectionRef,
2520 target_name: Option<&'a String>,
2521 ) {
2522 if !self.validate_collection_ref(decl, "target", &collection) {
2523 return;
2524 }
2525 if let Some(target_name) = target_name {
2526 let names_for_target = self
2527 .target_ids
2528 .entry(TargetId::Collection(&collection.name))
2529 .or_insert(HashMap::new());
2530 if let Some(prev_state) = names_for_target.insert(target_name, allowable_names) {
2531 if prev_state == AllowableIds::One || prev_state != allowable_names {
2532 self.errors.push(Error::duplicate_field(
2533 decl,
2534 "target_name",
2535 target_name as &str,
2536 ));
2537 }
2538 }
2539 }
2540 }
2541
2542 fn validate_route_from_self(
2543 &mut self,
2544 decl: DeclType,
2545 source: Option<&fdecl::Ref>,
2546 source_name: Option<&String>,
2547 source_dictionary: Option<&String>,
2548 capability_checker: impl Fn(&Self) -> &dyn Container,
2549 ) {
2550 let (Some(fdecl::Ref::Self_(_)), Some(name)) = (source, source_name) else {
2551 return;
2552 };
2553 match source_dictionary {
2554 Some(source_dictionary) => {
2555 #[cfg(fuchsia_api_level_at_least = "25")]
2556 {
2557 use cm_types::IterablePath;
2558 if let Ok(path) = cm_types::RelativePath::new(source_dictionary) {
2559 if let Some(first_segment) = path.iter_segments().next().map(|s| s.as_str())
2560 {
2561 if !self.all_dictionaries.contains_key(first_segment) {
2562 self.errors.push(Error::invalid_capability(
2563 decl,
2564 "source",
2565 first_segment,
2566 ));
2567 }
2568 }
2569 }
2570 }
2571 #[cfg(not(fuchsia_api_level_at_least = "25"))]
2572 let _ = source_dictionary;
2573 }
2574 None => {
2575 if !(capability_checker)(self).contains(name) {
2576 self.errors.push(Error::invalid_capability(decl, "source", name));
2577 }
2578 }
2579 }
2580 }
2581
2582 fn service_checker(&self) -> &dyn Container {
2585 &self.all_services
2586 }
2587 fn protocol_checker(&self) -> &dyn Container {
2588 &self.all_protocols
2589 }
2590 fn directory_checker(&self) -> &dyn Container {
2591 &self.all_directories
2592 }
2593 fn runner_checker(&self) -> &dyn Container {
2594 &self.all_runners
2595 }
2596 fn resolver_checker(&self) -> &dyn Container {
2597 &self.all_resolvers
2598 }
2599
2600 #[cfg(fuchsia_api_level_at_least = "25")]
2601 fn dictionary_checker(&self) -> &dyn Container {
2602 &self.all_dictionaries
2603 }
2604
2605 #[cfg(fuchsia_api_level_at_least = "HEAD")]
2606 fn config_checker(&self) -> &dyn Container {
2607 &self.all_configs
2608 }
2609 fn storage_checker(&self) -> &dyn Container {
2610 &self.all_storages
2611 }
2612 fn event_stream_checker(&self) -> &dyn Container {
2613 struct AlwaysTrueContainer {}
2617 impl Container for AlwaysTrueContainer {
2618 fn contains(&self, _key: &str) -> bool {
2619 true
2620 }
2621 }
2622 static CONTAINER: AlwaysTrueContainer = AlwaysTrueContainer {};
2623 &CONTAINER
2624 }
2625}
2626
2627#[cfg(test)]
2628mod tests {
2629 use super::*;
2630 use cm_types::MAX_LONG_NAME_LENGTH;
2631 use test_case::test_case;
2632 use {
2633 fidl_fuchsia_component_decl as fdecl, fidl_fuchsia_data as fdata, fidl_fuchsia_io as fio,
2634 };
2635
2636 macro_rules! test_validate {
2637 (
2638 $(
2639 $test_name:ident => {
2640 input = $input:expr,
2641 result = $result:expr,
2642 },
2643 )+
2644 ) => {
2645 $(
2646 #[test]
2647 fn $test_name() {
2648 validate_test($input, $result);
2649 }
2650 )+
2651 }
2652 }
2653
2654 macro_rules! test_validate_any_result {
2655 (
2656 $(
2657 $test_name:ident => {
2658 input = $input:expr,
2659 results = $results:expr,
2660 },
2661 )+
2662 ) => {
2663 $(
2664 #[test]
2665 fn $test_name() {
2666 validate_test_any_result($input, $results);
2667 }
2668 )+
2669 }
2670 }
2671
2672 macro_rules! test_validate_values_data {
2673 (
2674 $(
2675 $test_name:ident => {
2676 input = $input:expr,
2677 result = $result:expr,
2678 },
2679 )+
2680 ) => {
2681 $(
2682 #[test]
2683 fn $test_name() {
2684 validate_values_data_test($input, $result);
2685 }
2686 )+
2687 }
2688 }
2689
2690 macro_rules! test_validate_capabilities {
2691 (
2692 $(
2693 $test_name:ident => {
2694 input = $input:expr,
2695 as_builtin = $as_builtin:expr,
2696 result = $result:expr,
2697 },
2698 )+
2699 ) => {
2700 $(
2701 #[test]
2702 fn $test_name() {
2703 validate_capabilities_test($input, $as_builtin, $result);
2704 }
2705 )+
2706 }
2707 }
2708
2709 macro_rules! test_dependency {
2710 (
2711 $(
2712 ($test_name:ident) => {
2713 ty = $ty:expr,
2714 offer_decl = $offer_decl:expr,
2715 },
2716 )+
2717 ) => {
2718 $(
2719 #[test]
2720 fn $test_name() {
2721 let mut decl = new_component_decl();
2722 let dependencies = vec![
2723 ("a", "b"),
2724 ("b", "a"),
2725 ];
2726 let offers = dependencies.into_iter().map(|(from,to)| {
2727 let mut offer_decl = $offer_decl;
2728 offer_decl.source = Some(fdecl::Ref::Child(
2729 fdecl::ChildRef { name: from.to_string(), collection: None },
2730 ));
2731 offer_decl.target = Some(fdecl::Ref::Child(
2732 fdecl::ChildRef { name: to.to_string(), collection: None },
2733 ));
2734 $ty(offer_decl)
2735 }).collect();
2736 let children = ["a", "b"].iter().map(|name| {
2737 fdecl::Child {
2738 name: Some(name.to_string()),
2739 url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
2740 startup: Some(fdecl::StartupMode::Lazy),
2741 on_terminate: None,
2742 environment: None,
2743 ..Default::default()
2744 }
2745 }).collect();
2746 decl.offers = Some(offers);
2747 decl.children = Some(children);
2748 let result = Err(ErrorList::new(vec![
2749 Error::dependency_cycle(
2750 directed_graph::Error::CyclesDetected([vec!["child a", "child b", "child a"]].iter().cloned().collect()).format_cycle()),
2751 ]));
2752 validate_test(decl, result);
2753 }
2754 )+
2755 }
2756 }
2757
2758 macro_rules! test_weak_dependency {
2759 (
2760 $(
2761 ($test_name:ident) => {
2762 ty = $ty:expr,
2763 offer_decl = $offer_decl:expr,
2764 },
2765 )+
2766 ) => {
2767 $(
2768 #[test_case(fdecl::DependencyType::Weak)]
2769 fn $test_name(weak_dep: fdecl::DependencyType) {
2770 let mut decl = new_component_decl();
2771 let offers = vec![
2772 {
2773 let mut offer_decl = $offer_decl;
2774 offer_decl.source = Some(fdecl::Ref::Child(
2775 fdecl::ChildRef { name: "a".to_string(), collection: None },
2776 ));
2777 offer_decl.target = Some(fdecl::Ref::Child(
2778 fdecl::ChildRef { name: "b".to_string(), collection: None },
2779 ));
2780 offer_decl.dependency_type = Some(fdecl::DependencyType::Strong);
2781 $ty(offer_decl)
2782 },
2783 {
2784 let mut offer_decl = $offer_decl;
2785 offer_decl.source = Some(fdecl::Ref::Child(
2786 fdecl::ChildRef { name: "b".to_string(), collection: None },
2787 ));
2788 offer_decl.target = Some(fdecl::Ref::Child(
2789 fdecl::ChildRef { name: "a".to_string(), collection: None },
2790 ));
2791 offer_decl.dependency_type = Some(weak_dep);
2792 $ty(offer_decl)
2793 },
2794 ];
2795 let children = ["a", "b"].iter().map(|name| {
2796 fdecl::Child {
2797 name: Some(name.to_string()),
2798 url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
2799 startup: Some(fdecl::StartupMode::Lazy),
2800 on_terminate: None,
2801 environment: None,
2802 ..Default::default()
2803 }
2804 }).collect();
2805 decl.offers = Some(offers);
2806 decl.children = Some(children);
2807 let result = Ok(());
2808 validate_test(decl, result);
2809 }
2810 )+
2811 }
2812 }
2813
2814 #[track_caller]
2815 fn validate_test(input: fdecl::Component, expected_res: Result<(), ErrorList>) {
2816 let res = validate(&input);
2817 assert_eq!(res, expected_res);
2818 }
2819
2820 #[track_caller]
2821 fn validate_test_any_result(input: fdecl::Component, expected_res: Vec<Result<(), ErrorList>>) {
2822 let res = format!("{:?}", validate(&input));
2823 let expected_res_debug = format!("{:?}", expected_res);
2824
2825 let matched_exp =
2826 expected_res.into_iter().find(|expected| res == format!("{:?}", expected));
2827
2828 assert!(
2829 matched_exp.is_some(),
2830 "assertion failed: Expected one of:\n{:?}\nActual:\n{:?}",
2831 expected_res_debug,
2832 res
2833 );
2834 }
2835
2836 #[track_caller]
2837 fn validate_values_data_test(
2838 input: fdecl::ConfigValuesData,
2839 expected_res: Result<(), ErrorList>,
2840 ) {
2841 let res = validate_values_data(&input);
2842 assert_eq!(res, expected_res);
2843 }
2844
2845 #[track_caller]
2846 fn validate_capabilities_test(
2847 input: Vec<fdecl::Capability>,
2848 as_builtin: bool,
2849 expected_res: Result<(), ErrorList>,
2850 ) {
2851 let res = validate_capabilities(&input, as_builtin);
2852 assert_eq!(res, expected_res);
2853 }
2854
2855 fn new_component_decl() -> fdecl::Component {
2856 fdecl::Component {
2857 program: None,
2858 uses: None,
2859 exposes: None,
2860 offers: None,
2861 facets: None,
2862 capabilities: None,
2863 children: None,
2864 collections: None,
2865 environments: None,
2866 ..Default::default()
2867 }
2868 }
2869
2870 test_validate_any_result! {
2871 test_validate_use_disallows_nested_dirs => {
2872 input = {
2873 let mut decl = new_component_decl();
2874 decl.uses = Some(vec![
2875 fdecl::Use::Directory(fdecl::UseDirectory {
2876 dependency_type: Some(fdecl::DependencyType::Strong),
2877 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2878 source_name: Some("abc".to_string()),
2879 target_path: Some("/foo/bar".to_string()),
2880 rights: Some(fio::Operations::CONNECT),
2881 subdir: None,
2882 ..Default::default()
2883 }),
2884 fdecl::Use::Directory(fdecl::UseDirectory {
2885 dependency_type: Some(fdecl::DependencyType::Strong),
2886 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2887 source_name: Some("abc".to_string()),
2888 target_path: Some("/foo/bar/baz".to_string()),
2889 rights: Some(fio::Operations::CONNECT),
2890 subdir: None,
2891 ..Default::default()
2892 }),
2893 ]);
2894 decl
2895 },
2896 results = vec![
2897 Err(ErrorList::new(vec![
2898 Error::invalid_path_overlap(
2899 DeclType::UseDirectory, "/foo/bar/baz", DeclType::UseDirectory, "/foo/bar"),
2900 ])),
2901 Err(ErrorList::new(vec![
2902 Error::invalid_path_overlap(
2903 DeclType::UseDirectory, "/foo/bar", DeclType::UseDirectory, "/foo/bar/baz"),
2904 ])),
2905 ],
2906 },
2907 test_validate_use_disallows_nested_dirs_storage => {
2908 input = {
2909 let mut decl = new_component_decl();
2910 decl.uses = Some(vec![
2911 fdecl::Use::Storage(fdecl::UseStorage {
2912 source_name: Some("abc".to_string()),
2913 target_path: Some("/foo/bar".to_string()),
2914 ..Default::default()
2915 }),
2916 fdecl::Use::Storage(fdecl::UseStorage {
2917 source_name: Some("abc".to_string()),
2918 target_path: Some("/foo/bar/baz".to_string()),
2919 ..Default::default()
2920 }),
2921 ]);
2922 decl
2923 },
2924 results = vec![
2925 Err(ErrorList::new(vec![
2926 Error::invalid_path_overlap(
2927 DeclType::UseStorage, "/foo/bar/baz", DeclType::UseStorage, "/foo/bar"),
2928 ])),
2929 Err(ErrorList::new(vec![
2930 Error::invalid_path_overlap(
2931 DeclType::UseStorage, "/foo/bar", DeclType::UseStorage, "/foo/bar/baz"),
2932 ])),
2933 ],
2934 },
2935 test_validate_use_disallows_nested_dirs_directory_and_storage => {
2936 input = {
2937 let mut decl = new_component_decl();
2938 decl.uses = Some(vec![
2939 fdecl::Use::Directory(fdecl::UseDirectory {
2940 dependency_type: Some(fdecl::DependencyType::Strong),
2941 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2942 source_name: Some("abc".to_string()),
2943 target_path: Some("/foo/bar".to_string()),
2944 rights: Some(fio::Operations::CONNECT),
2945 subdir: None,
2946 ..Default::default()
2947 }),
2948 fdecl::Use::Storage(fdecl::UseStorage {
2949 source_name: Some("abc".to_string()),
2950 target_path: Some("/foo/bar/baz".to_string()),
2951 ..Default::default()
2952 }),
2953 ]);
2954 decl
2955 },
2956 results = vec![
2957 Err(ErrorList::new(vec![
2958 Error::invalid_path_overlap(
2959 DeclType::UseStorage, "/foo/bar/baz", DeclType::UseDirectory, "/foo/bar"),
2960 ])),
2961 Err(ErrorList::new(vec![
2962 Error::invalid_path_overlap(
2963 DeclType::UseDirectory, "/foo/bar", DeclType::UseStorage, "/foo/bar/baz"),
2964 ])),
2965 ],
2966 },
2967 test_validate_use_disallows_common_prefixes_protocol => {
2968 input = {
2969 let mut decl = new_component_decl();
2970 decl.uses = Some(vec![
2971 fdecl::Use::Directory(fdecl::UseDirectory {
2972 dependency_type: Some(fdecl::DependencyType::Strong),
2973 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2974 source_name: Some("abc".to_string()),
2975 target_path: Some("/foo/bar".to_string()),
2976 rights: Some(fio::Operations::CONNECT),
2977 subdir: None,
2978 ..Default::default()
2979 }),
2980 fdecl::Use::Protocol(fdecl::UseProtocol {
2981 dependency_type: Some(fdecl::DependencyType::Strong),
2982 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2983 source_name: Some("crow".to_string()),
2984 target_path: Some("/foo/bar/fuchsia.2".to_string()),
2985 ..Default::default()
2986 }),
2987 ]);
2988 decl
2989 },
2990 results = vec![
2991 Err(ErrorList::new(vec![
2992 Error::invalid_path_overlap(
2993 DeclType::UseProtocol, "/foo/bar/fuchsia.2", DeclType::UseDirectory, "/foo/bar"),
2994 ])),
2995 Err(ErrorList::new(vec![
2996 Error::invalid_path_overlap(
2997 DeclType::UseDirectory, "/foo/bar", DeclType::UseProtocol, "/foo/bar/fuchsia.2"),
2998 ])),
2999 ],
3000 },
3001 test_validate_use_disallows_common_prefixes_service => {
3002 input = {
3003 let mut decl = new_component_decl();
3004 decl.uses = Some(vec![
3005 fdecl::Use::Directory(fdecl::UseDirectory {
3006 dependency_type: Some(fdecl::DependencyType::Strong),
3007 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3008 source_name: Some("abc".to_string()),
3009 target_path: Some("/foo/bar".to_string()),
3010 rights: Some(fio::Operations::CONNECT),
3011 subdir: None,
3012 ..Default::default()
3013 }),
3014 fdecl::Use::Service(fdecl::UseService {
3015 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3016 source_name: Some("space".to_string()),
3017 target_path: Some("/foo/bar/baz/fuchsia.logger.Log".to_string()),
3018 dependency_type: Some(fdecl::DependencyType::Strong),
3019 ..Default::default()
3020 }),
3021 ]);
3022 decl
3023 },
3024 results = vec![
3025 Err(ErrorList::new(vec![
3026 Error::invalid_path_overlap(
3027 DeclType::UseService, "/foo/bar/baz/fuchsia.logger.Log", DeclType::UseDirectory, "/foo/bar"),
3028 ])),
3029 Err(ErrorList::new(vec![
3030 Error::invalid_path_overlap(
3031 DeclType::UseDirectory, "/foo/bar", DeclType::UseService, "/foo/bar/baz/fuchsia.logger.Log"),
3032 ])),
3033 ],
3034 },
3035 test_validate_use_disallows_pkg => {
3036 input = {
3037 let mut decl = new_component_decl();
3038 decl.uses = Some(vec![
3039 fdecl::Use::Directory(fdecl::UseDirectory {
3040 dependency_type: Some(fdecl::DependencyType::Strong),
3041 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3042 source_name: Some("abc".to_string()),
3043 target_path: Some("/pkg".to_string()),
3044 rights: Some(fio::Operations::CONNECT),
3045 subdir: None,
3046 ..Default::default()
3047 }),
3048 ]);
3049 decl
3050 },
3051 results = vec![
3052 Err(ErrorList::new(vec![
3053 Error::pkg_path_overlap(DeclType::UseDirectory, "/pkg"),
3054 ])),
3055 ],
3056 },
3057 test_validate_use_disallows_pkg_overlap => {
3058 input = {
3059 let mut decl = new_component_decl();
3060 decl.uses = Some(vec![
3061 fdecl::Use::Directory(fdecl::UseDirectory {
3062 dependency_type: Some(fdecl::DependencyType::Strong),
3063 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3064 source_name: Some("abc".to_string()),
3065 target_path: Some("/pkg/foo".to_string()),
3066 rights: Some(fio::Operations::CONNECT),
3067 subdir: None,
3068 ..Default::default()
3069 }),
3070 ]);
3071 decl
3072 },
3073 results = vec![
3074 Err(ErrorList::new(vec![
3075 Error::pkg_path_overlap(DeclType::UseDirectory, "/pkg/foo"),
3076 ])),
3077 ],
3078 },
3079 test_validate_use_optional_config_correct => {
3080 input = {
3081 let mut decl = new_component_decl();
3082 decl.uses = Some(vec![
3083 fdecl::Use::Config(fdecl::UseConfiguration {
3084 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3085 source_name: Some("abc".to_string()),
3086 target_name: Some("foo".to_string()),
3087 availability: Some(fdecl::Availability::Optional),
3088 type_: Some(fdecl::ConfigType {
3089 layout: fdecl::ConfigTypeLayout::Bool,
3090 parameters: Some(Vec::new()),
3091 constraints: Vec::new(),
3092 }),
3093 ..Default::default()
3094 }),
3095 ]);
3096 decl.config = Some(fdecl::ConfigSchema {
3097 fields: Some(vec![fdecl::ConfigField {
3098 key: Some("foo".into()),
3099 type_: Some(fdecl::ConfigType {
3100 layout: fdecl::ConfigTypeLayout::Bool,
3101 parameters: Some(Vec::new()),
3102 constraints: Vec::new(),
3103 }),
3104 mutability: None,
3105 ..Default::default()}]),
3106 checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3107 value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3108 ..Default::default()
3109 });
3110 decl
3111 },
3112 results = vec![Ok(())],
3113 },
3114 test_validate_use_optional_config_no_config_schema => {
3115 input = {
3116 let mut decl = new_component_decl();
3117 decl.uses = Some(vec![
3118 fdecl::Use::Config(fdecl::UseConfiguration {
3119 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3120 source_name: Some("abc".to_string()),
3121 target_name: Some("foo".to_string()),
3122 availability: Some(fdecl::Availability::Optional),
3123 type_: Some(fdecl::ConfigType {
3124 layout: fdecl::ConfigTypeLayout::Bool,
3125 parameters: None,
3126 constraints: Vec::new(),
3127 }),
3128 ..Default::default()
3129 }),
3130 ]);
3131 decl
3132 },
3133 results = vec![
3134 Err(ErrorList::new(vec![
3135 Error::missing_field(DeclType::ConfigField, "config"),
3136 ])),
3137 ],
3138 },
3139 test_validate_use_optional_config_no_config_field => {
3140 input = {
3141 let mut decl = new_component_decl();
3142 decl.uses = Some(vec![
3143 fdecl::Use::Config(fdecl::UseConfiguration {
3144 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3145 source_name: Some("abc".to_string()),
3146 target_name: Some("foo".to_string()),
3147 availability: Some(fdecl::Availability::Optional),
3148 type_: Some(fdecl::ConfigType {
3149 layout: fdecl::ConfigTypeLayout::Bool,
3150 parameters: None,
3151 constraints: Vec::new(),
3152 }),
3153 ..Default::default()
3154 }),
3155 ]);
3156 decl.config = Some(fdecl::ConfigSchema {
3157 fields: Some(vec![]),
3158 checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3159 value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3160 ..Default::default()
3161 });
3162 decl
3163 },
3164 results = vec![
3165 Err(ErrorList::new(vec![
3166 Error::missing_field(DeclType::ConfigField, "foo"),
3167 ])),
3168 ],
3169 },
3170 test_validate_use_optional_config_bad_type => {
3171 input = {
3172 let mut decl = new_component_decl();
3173 decl.uses = Some(vec![
3174 fdecl::Use::Config(fdecl::UseConfiguration {
3175 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3176 source_name: Some("abc".to_string()),
3177 target_name: Some("foo".to_string()),
3178 availability: Some(fdecl::Availability::Optional),
3179 type_: Some(fdecl::ConfigType {
3180 layout: fdecl::ConfigTypeLayout::Bool,
3181 parameters: None,
3182 constraints: Vec::new(),
3183 }),
3184 ..Default::default()
3185 }),
3186 ]);
3187 decl.config = Some(fdecl::ConfigSchema {
3188 fields: Some(vec![fdecl::ConfigField {
3189 key: Some("foo".into()),
3190 type_: Some(fdecl::ConfigType {
3191 layout: fdecl::ConfigTypeLayout::Int16,
3192 parameters: Some(Vec::new()),
3193 constraints: Vec::new(),
3194 }),
3195 mutability: None,
3196 ..Default::default()}]),
3197 checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3198 value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3199 ..Default::default()
3200 });
3201 decl
3202 },
3203 results = vec![
3204 Err(ErrorList::new(vec![
3205 Error::invalid_field(DeclType::ConfigField, "foo"),
3206 ])),
3207 ],
3208 },
3209 }
3210
3211 test_validate_values_data! {
3212 test_values_data_ok => {
3213 input = fdecl::ConfigValuesData {
3214 values: Some(vec![
3215 fdecl::ConfigValueSpec {
3216 value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(true))),
3217 ..Default::default()
3218 }
3219 ]),
3220 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3221 ..Default::default()
3222 },
3223 result = Ok(()),
3224 },
3225 test_values_data_no_checksum => {
3226 input = fdecl::ConfigValuesData {
3227 values: Some(vec![]),
3228 checksum: None,
3229 ..Default::default()
3230 },
3231 result = Err(ErrorList::new(vec![
3232 Error::missing_field(DeclType::ConfigValuesData, "checksum")
3233 ])),
3234 },
3235 test_values_data_unknown_checksum => {
3236 input = fdecl::ConfigValuesData {
3237 values: Some(vec![]),
3238 checksum: Some(fdecl::ConfigChecksum::unknown_variant_for_testing()),
3239 ..Default::default()
3240 },
3241 result = Err(ErrorList::new(vec![
3242 Error::invalid_field(DeclType::ConfigValuesData, "checksum")
3243 ])),
3244 },
3245 test_values_data_no_values => {
3246 input = fdecl::ConfigValuesData {
3247 values: None,
3248 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3249 ..Default::default()
3250 },
3251 result = Err(ErrorList::new(vec![
3252 Error::missing_field(DeclType::ConfigValuesData, "values")
3253 ])),
3254 },
3255 test_values_data_no_inner_value => {
3256 input = fdecl::ConfigValuesData {
3257 values: Some(vec![
3258 fdecl::ConfigValueSpec::default()
3259 ]),
3260 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3261 ..Default::default()
3262 },
3263 result = Err(ErrorList::new(vec![
3264 Error::missing_field(DeclType::ConfigValueSpec, "value")
3265 ])),
3266 },
3267 test_values_data_unknown_inner_value => {
3268 input = fdecl::ConfigValuesData {
3269 values: Some(vec![
3270 fdecl::ConfigValueSpec {
3271 value: Some(fdecl::ConfigValue::unknown_variant_for_testing()),
3272 ..Default::default()
3273 }
3274 ]),
3275 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3276 ..Default::default()
3277 },
3278 result = Err(ErrorList::new(vec![
3279 Error::invalid_field(DeclType::ConfigValueSpec, "value")
3280 ])),
3281 },
3282 test_values_data_unknown_single_value => {
3283 input = fdecl::ConfigValuesData {
3284 values: Some(vec![
3285 fdecl::ConfigValueSpec {
3286 value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::unknown_variant_for_testing())),
3287 ..Default::default()
3288 }
3289 ]),
3290 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3291 ..Default::default()
3292 },
3293 result = Err(ErrorList::new(vec![
3294 Error::invalid_field(DeclType::ConfigValueSpec, "value")
3295 ])),
3296 },
3297 test_values_data_unknown_list_value => {
3298 input = fdecl::ConfigValuesData {
3299 values: Some(vec![
3300 fdecl::ConfigValueSpec {
3301 value: Some(fdecl::ConfigValue::Vector(fdecl::ConfigVectorValue::unknown_variant_for_testing())),
3302 ..Default::default()
3303 }
3304 ]),
3305 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3306 ..Default::default()
3307 },
3308 result = Err(ErrorList::new(vec![
3309 Error::invalid_field(DeclType::ConfigValueSpec, "value")
3310 ])),
3311 },
3312 }
3313
3314 test_validate! {
3315 test_validate_uses_empty => {
3317 input = {
3318 let mut decl = new_component_decl();
3319 decl.program = Some(fdecl::Program {
3320 runner: Some("elf".to_string()),
3321 info: Some(fdata::Dictionary {
3322 entries: None,
3323 ..Default::default()
3324 }),
3325 ..Default::default()
3326 });
3327 decl.uses = Some(vec![
3328 fdecl::Use::Service(fdecl::UseService {
3329 source: None,
3330 source_name: None,
3331 target_path: None,
3332 dependency_type: None,
3333 ..Default::default()
3334 }),
3335 fdecl::Use::Protocol(fdecl::UseProtocol {
3336 dependency_type: None,
3337 source: None,
3338 source_name: None,
3339 target_path: None,
3340 ..Default::default()
3341 }),
3342 fdecl::Use::Directory(fdecl::UseDirectory {
3343 dependency_type: None,
3344 source: None,
3345 source_name: None,
3346 target_path: None,
3347 rights: None,
3348 subdir: None,
3349 ..Default::default()
3350 }),
3351 fdecl::Use::Storage(fdecl::UseStorage {
3352 source_name: None,
3353 target_path: None,
3354 ..Default::default()
3355 }),
3356 fdecl::Use::EventStream(fdecl::UseEventStream {
3357 source_name: None,
3358 source: None,
3359 target_path: None,
3360 ..Default::default()
3361 }),
3362 fdecl::Use::Runner(fdecl::UseRunner {
3363 source_name: None,
3364 source: None,
3365 ..Default::default()
3366 }),
3367 ]);
3368 decl
3369 },
3370 result = Err(ErrorList::new(vec![
3371 Error::missing_field(DeclType::UseService, "source"),
3372 Error::missing_field(DeclType::UseService, "source_name"),
3373 Error::missing_field(DeclType::UseService, "target_path"),
3374 Error::missing_field(DeclType::UseService, "dependency_type"),
3375 Error::missing_field(DeclType::UseProtocol, "source"),
3376 Error::missing_field(DeclType::UseProtocol, "source_name"),
3377 Error::missing_field(DeclType::UseProtocol, "target_path"),
3378 Error::missing_field(DeclType::UseProtocol, "dependency_type"),
3379 Error::missing_field(DeclType::UseDirectory, "source"),
3380 Error::missing_field(DeclType::UseDirectory, "source_name"),
3381 Error::missing_field(DeclType::UseDirectory, "target_path"),
3382 Error::missing_field(DeclType::UseDirectory, "dependency_type"),
3383 Error::missing_field(DeclType::UseDirectory, "rights"),
3384 Error::missing_field(DeclType::UseStorage, "source_name"),
3385 Error::missing_field(DeclType::UseStorage, "target_path"),
3386 Error::missing_field(DeclType::UseEventStream, "source"),
3387 Error::missing_field(DeclType::UseEventStream, "source_name"),
3388 Error::missing_field(DeclType::UseEventStream, "target_path"),
3389 Error::missing_field(DeclType::UseRunner, "source"),
3390 Error::missing_field(DeclType::UseRunner, "source_name"),
3391 ])),
3392 },
3393 test_validate_missing_program_info => {
3394 input = {
3395 let mut decl = new_component_decl();
3396 decl.program = Some(fdecl::Program {
3397 runner: Some("runner".to_string()),
3398 info: None,
3399 ..Default::default()
3400 });
3401 decl
3402 },
3403 result = Err(ErrorList::new(vec![
3404 Error::missing_field(DeclType::Program, "info")
3405 ])),
3406 },
3407 test_validate_uses_invalid_identifiers => {
3408 input = {
3409 let mut decl = new_component_decl();
3410 decl.uses = Some(vec![
3411 fdecl::Use::Service(fdecl::UseService {
3412 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
3413 name: "^bad".to_string(),
3414 })),
3415 source_name: Some("foo/".to_string()),
3416 target_path: Some("a/foo".to_string()),
3417 dependency_type: Some(fdecl::DependencyType::Strong),
3418 ..Default::default()
3419 }),
3420 fdecl::Use::Protocol(fdecl::UseProtocol {
3421 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3422 name: "^bad".to_string(),
3423 collection: None,
3424 })),
3425 source_name: Some("foo/".to_string()),
3426 target_path: Some("b/foo".to_string()),
3427 dependency_type: Some(fdecl::DependencyType::Strong),
3428 ..Default::default()
3429 }),
3430 fdecl::Use::Directory(fdecl::UseDirectory {
3431 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3432 name: "^bad".to_string(),
3433 collection: None,
3434 })),
3435 source_name: Some("foo/".to_string()),
3436 target_path: Some("c".to_string()),
3437 rights: Some(fio::Operations::CONNECT),
3438 subdir: Some("/foo".to_string()),
3439 dependency_type: Some(fdecl::DependencyType::Strong),
3440 ..Default::default()
3441 }),
3442 fdecl::Use::Storage(fdecl::UseStorage {
3443 source_name: Some("foo/".to_string()),
3444 target_path: Some("d".to_string()),
3445 ..Default::default()
3446 }),
3447 fdecl::Use::EventStream(fdecl::UseEventStream {
3448 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3449 name: "^bad".to_string(),
3450 collection: None,
3451 })),
3452 source_name: Some("foo/".to_string()),
3453 target_path: Some("e".to_string()),
3454 ..Default::default()
3455 }),
3456 fdecl::Use::Runner(fdecl::UseRunner {
3457 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3458 name: "^bad".to_string(),
3459 collection: None,
3460 })),
3461 source_name: Some("foo/".to_string()),
3462 ..Default::default()
3463 }),
3464 ]);
3465 decl
3466 },
3467 result = Err(ErrorList::new(vec![
3468 Error::invalid_field(DeclType::UseService, "source.capability.name"),
3469 Error::invalid_field(DeclType::UseService, "source_name"),
3470 Error::invalid_field(DeclType::UseService, "target_path"),
3471 Error::invalid_field(DeclType::UseProtocol, "source.child.name"),
3472 Error::invalid_field(DeclType::UseProtocol, "source_name"),
3473 Error::invalid_field(DeclType::UseProtocol, "target_path"),
3474 Error::invalid_field(DeclType::UseDirectory, "source.child.name"),
3475 Error::invalid_field(DeclType::UseDirectory, "source_name"),
3476 Error::invalid_field(DeclType::UseDirectory, "target_path"),
3477 Error::invalid_field(DeclType::UseDirectory, "subdir"),
3478 Error::invalid_field(DeclType::UseStorage, "source_name"),
3479 Error::invalid_field(DeclType::UseStorage, "target_path"),
3480 Error::invalid_field(DeclType::UseEventStream, "source.child.name"),
3481 Error::invalid_field(DeclType::UseEventStream, "source_name"),
3482 Error::invalid_field(DeclType::UseEventStream, "target_path"),
3483 Error::invalid_field(DeclType::UseRunner, "source.child.name"),
3484 Error::invalid_field(DeclType::UseRunner, "source_name"),
3485 ])),
3486 },
3487 test_validate_uses_missing_source => {
3488 input = {
3489 fdecl::Component {
3490 uses: Some(vec![
3491 fdecl::Use::Protocol(fdecl::UseProtocol {
3492 dependency_type: Some(fdecl::DependencyType::Strong),
3493 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
3494 name: "this-storage-doesnt-exist".to_string(),
3495 })),
3496 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3497 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3498 ..Default::default()
3499 })
3500 ]),
3501 ..new_component_decl()
3502 }
3503 },
3504 result = Err(ErrorList::new(vec![
3505 Error::invalid_capability(DeclType::UseProtocol, "source", "this-storage-doesnt-exist"),
3506 ])),
3507 },
3508 test_validate_uses_invalid_child => {
3509 input = {
3510 fdecl::Component {
3511 uses: Some(vec![
3512 fdecl::Use::Protocol(fdecl::UseProtocol {
3513 dependency_type: Some(fdecl::DependencyType::Strong),
3514 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3515 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3516 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3517 ..Default::default()
3518 }),
3519 fdecl::Use::Service(fdecl::UseService {
3520 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3521 source_name: Some("service_name".to_string()),
3522 target_path: Some("/svc/service_name".to_string()),
3523 dependency_type: Some(fdecl::DependencyType::Strong),
3524 ..Default::default()
3525 }),
3526 fdecl::Use::Directory(fdecl::UseDirectory {
3527 dependency_type: Some(fdecl::DependencyType::Strong),
3528 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3529 source_name: Some("DirectoryName".to_string()),
3530 target_path: Some("/data/DirectoryName".to_string()),
3531 rights: Some(fio::Operations::CONNECT),
3532 subdir: None,
3533 ..Default::default()
3534 }),
3535 fdecl::Use::Runner(fdecl::UseRunner {
3536 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3537 source_name: Some("RunnerName".to_string()),
3538 ..Default::default()
3539 }),
3540 ]),
3541 ..new_component_decl()
3542 }
3543 },
3544 result = Err(ErrorList::new(vec![
3545 Error::invalid_child(DeclType::UseProtocol, "source", "no-such-child"),
3546 Error::invalid_child(DeclType::UseService, "source", "no-such-child"),
3547 Error::invalid_child(DeclType::UseDirectory, "source", "no-such-child"),
3548 Error::invalid_child(DeclType::UseRunner, "source", "no-such-child"),
3549 ])),
3550 },
3551 test_validate_uses_invalid_capability_from_self => {
3552 input = {
3553 let mut decl = new_component_decl();
3554 decl.uses = Some(vec![
3555 fdecl::Use::Service(fdecl::UseService {
3556 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3557 source_name: Some("fuchsia.some.library.SomeService".into()),
3558 target_path: Some("/svc/foo".into()),
3559 dependency_type: Some(fdecl::DependencyType::Strong),
3560 ..Default::default()
3561 }),
3562 fdecl::Use::Protocol(fdecl::UseProtocol {
3563 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3564 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
3565 target_path: Some("/svc/bar".into()),
3566 dependency_type: Some(fdecl::DependencyType::Strong),
3567 ..Default::default()
3568 }),
3569 fdecl::Use::Directory(fdecl::UseDirectory {
3570 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3571 source_name: Some("dir".into()),
3572 target_path: Some("/assets".into()),
3573 dependency_type: Some(fdecl::DependencyType::Strong),
3574 rights: Some(fio::Operations::CONNECT),
3575 ..Default::default()
3576 }),
3577 fdecl::Use::Runner(fdecl::UseRunner {
3578 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3579 source_name: Some("source_elf".into()),
3580 ..Default::default()
3581 }),
3582 fdecl::Use::Config(fdecl::UseConfiguration {
3583 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3584 source_name: Some("source_config".into()),
3585 target_name: Some("config".into()),
3586 type_: Some(fdecl::ConfigType {
3587 layout: fdecl::ConfigTypeLayout::Bool,
3588 parameters: Some(Vec::new()),
3589 constraints: Vec::new(),
3590 }),
3591 ..Default::default()
3592 }),
3593 fdecl::Use::Protocol(fdecl::UseProtocol {
3594 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3595 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
3596 source_dictionary: Some("dict/inner".into()),
3597 target_path: Some("/svc/baz".into()),
3598 dependency_type: Some(fdecl::DependencyType::Strong),
3599 ..Default::default()
3600 }),
3601 ]);
3602 decl
3603 },
3604 result = Err(ErrorList::new(vec![
3605 Error::invalid_capability(
3606 DeclType::UseService,
3607 "source",
3608 "fuchsia.some.library.SomeService"),
3609 Error::invalid_capability(
3610 DeclType::UseProtocol,
3611 "source",
3612 "fuchsia.some.library.SomeProtocol"),
3613 Error::invalid_capability(DeclType::UseDirectory, "source", "dir"),
3614 Error::invalid_capability(DeclType::UseRunner, "source", "source_elf"),
3615 Error::invalid_capability(DeclType::UseConfiguration, "source", "source_config"),
3616 Error::invalid_capability(DeclType::UseProtocol, "source", "dict"),
3617 ])),
3618 },
3619 test_validate_use_from_child_offer_to_child_strong_cycle => {
3620 input = {
3621 fdecl::Component {
3622 capabilities: Some(vec![
3623 fdecl::Capability::Service(fdecl::Service {
3624 name: Some("a".to_string()),
3625 source_path: Some("/a".to_string()),
3626 ..Default::default()
3627 })]),
3628 uses: Some(vec![
3629 fdecl::Use::Protocol(fdecl::UseProtocol {
3630 dependency_type: Some(fdecl::DependencyType::Strong),
3631 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3632 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3633 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3634 ..Default::default()
3635 }),
3636 fdecl::Use::Service(fdecl::UseService {
3637 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3638 source_name: Some("service_name".to_string()),
3639 target_path: Some("/svc/service_name".to_string()),
3640 dependency_type: Some(fdecl::DependencyType::Strong),
3641 ..Default::default()
3642 }),
3643 fdecl::Use::Directory(fdecl::UseDirectory {
3644 dependency_type: Some(fdecl::DependencyType::Strong),
3645 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3646 source_name: Some("DirectoryName".to_string()),
3647 target_path: Some("/data/DirectoryName".to_string()),
3648 rights: Some(fio::Operations::CONNECT),
3649 subdir: None,
3650 ..Default::default()
3651 }),
3652 ]),
3653 offers: Some(vec![
3654 fdecl::Offer::Service(fdecl::OfferService {
3655 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3656 source_name: Some("a".to_string()),
3657 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
3658 target_name: Some("a".to_string()),
3659 ..Default::default()
3660 })
3661 ]),
3662 children: Some(vec![
3663 fdecl::Child {
3664 name: Some("child".to_string()),
3665 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3666 startup: Some(fdecl::StartupMode::Lazy),
3667 on_terminate: None,
3668 ..Default::default()
3669 }
3670 ]),
3671 ..new_component_decl()
3672 }
3673 },
3674 result = Err(ErrorList::new(vec![
3675 Error::dependency_cycle("{{self -> child child -> self}}".to_string()),
3676 ])),
3677 },
3678 test_validate_use_from_child_storage_no_cycle => {
3679 input = {
3680 fdecl::Component {
3681 capabilities: Some(vec![
3682 fdecl::Capability::Storage(fdecl::Storage {
3683 name: Some("cdata".to_string()),
3684 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None } )),
3685 backing_dir: Some("minfs".to_string()),
3686 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
3687 ..Default::default()
3688 }),
3689 fdecl::Capability::Storage(fdecl::Storage {
3690 name: Some("pdata".to_string()),
3691 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
3692 backing_dir: Some("minfs".to_string()),
3693 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
3694 ..Default::default()
3695 }),
3696 ]),
3697 uses: Some(vec![
3698 fdecl::Use::Protocol(fdecl::UseProtocol {
3699 dependency_type: Some(fdecl::DependencyType::Strong),
3700 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child1".to_string(), collection: None})),
3701 source_name: Some("a".to_string()),
3702 target_path: Some("/svc/a".to_string()),
3703 ..Default::default()
3704 }),
3705 ]),
3706 offers: Some(vec![
3707 fdecl::Offer::Storage(fdecl::OfferStorage {
3708 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3709 source_name: Some("cdata".to_string()),
3710 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3711 target_name: Some("cdata".to_string()),
3712 ..Default::default()
3713 }),
3714 fdecl::Offer::Storage(fdecl::OfferStorage {
3715 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3716 source_name: Some("pdata".to_string()),
3717 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3718 target_name: Some("pdata".to_string()),
3719 ..Default::default()
3720 }),
3721 ]),
3722 children: Some(vec![
3723 fdecl::Child {
3724 name: Some("child1".to_string()),
3725 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3726 startup: Some(fdecl::StartupMode::Lazy),
3727 on_terminate: None,
3728 ..Default::default()
3729 },
3730 fdecl::Child {
3731 name: Some("child2".to_string()),
3732 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
3733 startup: Some(fdecl::StartupMode::Lazy),
3734 on_terminate: None,
3735 ..Default::default()
3736 }
3737 ]),
3738 ..new_component_decl()
3739 }
3740 },
3741 result = Ok(()),
3742 },
3743 test_validate_use_from_child_storage_cycle => {
3744 input = {
3745 fdecl::Component {
3746 capabilities: Some(vec![
3747 fdecl::Capability::Storage(fdecl::Storage {
3748 name: Some("data".to_string()),
3749 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
3750 backing_dir: Some("minfs".to_string()),
3751 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
3752 ..Default::default()
3753 }),
3754 ]),
3755 uses: Some(vec![
3756 fdecl::Use::Protocol(fdecl::UseProtocol {
3757 dependency_type: Some(fdecl::DependencyType::Strong),
3758 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3759 source_name: Some("a".to_string()),
3760 target_path: Some("/svc/a".to_string()),
3761 ..Default::default()
3762 }),
3763 ]),
3764 offers: Some(vec![
3765 fdecl::Offer::Storage(fdecl::OfferStorage {
3766 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3767 source_name: Some("data".to_string()),
3768 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
3769 target_name: Some("data".to_string()),
3770 ..Default::default()
3771 }),
3772 ]),
3773 children: Some(vec![
3774 fdecl::Child {
3775 name: Some("child".to_string()),
3776 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3777 startup: Some(fdecl::StartupMode::Lazy),
3778 on_terminate: None,
3779 ..Default::default()
3780 },
3781 ]),
3782 ..new_component_decl()
3783 }
3784 },
3785 result = Err(ErrorList::new(vec![
3786 Error::dependency_cycle("{{self -> capability data -> child child -> self}}".to_string()),
3787 ])),
3788 },
3789 test_validate_storage_strong_cycle_between_children => {
3790 input = {
3791 fdecl::Component {
3792 capabilities: Some(vec![
3793 fdecl::Capability::Storage(fdecl::Storage {
3794 name: Some("data".to_string()),
3795 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None } )),
3796 backing_dir: Some("minfs".to_string()),
3797 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
3798 ..Default::default()
3799 })
3800 ]),
3801 offers: Some(vec![
3802 fdecl::Offer::Storage(fdecl::OfferStorage {
3803 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3804 source_name: Some("data".to_string()),
3805 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
3806 target_name: Some("data".to_string()),
3807 ..Default::default()
3808 }),
3809 fdecl::Offer::Service(fdecl::OfferService {
3810 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
3811 source_name: Some("a".to_string()),
3812 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3813 target_name: Some("a".to_string()),
3814 ..Default::default()
3815 }),
3816 ]),
3817 children: Some(vec![
3818 fdecl::Child {
3819 name: Some("child1".to_string()),
3820 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3821 startup: Some(fdecl::StartupMode::Lazy),
3822 on_terminate: None,
3823 ..Default::default()
3824 },
3825 fdecl::Child {
3826 name: Some("child2".to_string()),
3827 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
3828 startup: Some(fdecl::StartupMode::Lazy),
3829 on_terminate: None,
3830 ..Default::default()
3831 }
3832 ]),
3833 ..new_component_decl()
3834 }
3835 },
3836 result = Err(ErrorList::new(vec![
3837 Error::dependency_cycle("{{child child1 -> capability data -> child child2 -> child child1}}".to_string()),
3838 ])),
3839 },
3840 test_validate_strong_cycle_between_children_through_environment_debug => {
3841 input = {
3842 fdecl::Component {
3843 environments: Some(vec![
3844 fdecl::Environment {
3845 name: Some("env".to_string()),
3846 extends: Some(fdecl::EnvironmentExtends::Realm),
3847 debug_capabilities: Some(vec![
3848 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
3849 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3850 source_name: Some("fuchsia.foo.Bar".to_string()),
3851 target_name: Some("fuchsia.foo.Bar".to_string()),
3852 ..Default::default()
3853 }),
3854 ]),
3855 ..Default::default()
3856 },
3857 ]),
3858 offers: Some(vec![
3859 fdecl::Offer::Service(fdecl::OfferService {
3860 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
3861 source_name: Some("a".to_string()),
3862 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3863 target_name: Some("a".to_string()),
3864 ..Default::default()
3865 }),
3866 ]),
3867 children: Some(vec![
3868 fdecl::Child {
3869 name: Some("child1".to_string()),
3870 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3871 startup: Some(fdecl::StartupMode::Lazy),
3872 on_terminate: None,
3873 ..Default::default()
3874 },
3875 fdecl::Child {
3876 name: Some("child2".to_string()),
3877 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
3878 startup: Some(fdecl::StartupMode::Lazy),
3879 environment: Some("env".to_string()),
3880 on_terminate: None,
3881 ..Default::default()
3882 }
3883 ]),
3884 ..new_component_decl()
3885 }
3886 },
3887 result = Err(ErrorList::new(vec![
3888 Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}".to_string()),
3889 ])),
3890 },
3891 test_validate_strong_cycle_between_children_through_environment_runner => {
3892 input = {
3893 fdecl::Component {
3894 environments: Some(vec![
3895 fdecl::Environment {
3896 name: Some("env".to_string()),
3897 extends: Some(fdecl::EnvironmentExtends::Realm),
3898 runners: Some(vec![
3899 fdecl::RunnerRegistration {
3900 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3901 source_name: Some("coff".to_string()),
3902 target_name: Some("coff".to_string()),
3903 ..Default::default()
3904 }
3905 ]),
3906 ..Default::default()
3907 },
3908 ]),
3909 offers: Some(vec![
3910 fdecl::Offer::Service(fdecl::OfferService {
3911 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
3912 source_name: Some("a".to_string()),
3913 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3914 target_name: Some("a".to_string()),
3915 ..Default::default()
3916 }),
3917 ]),
3918 children: Some(vec![
3919 fdecl::Child {
3920 name: Some("child1".to_string()),
3921 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3922 startup: Some(fdecl::StartupMode::Lazy),
3923 on_terminate: None,
3924 ..Default::default()
3925 },
3926 fdecl::Child {
3927 name: Some("child2".to_string()),
3928 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
3929 startup: Some(fdecl::StartupMode::Lazy),
3930 environment: Some("env".to_string()),
3931 on_terminate: None,
3932 ..Default::default()
3933 }
3934 ]),
3935 ..new_component_decl()
3936 }
3937 },
3938 result = Err(ErrorList::new(vec![
3939 Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}".to_string()),
3940 ])),
3941 },
3942 test_validate_strong_cycle_between_children_through_environment_resolver => {
3943 input = {
3944 fdecl::Component {
3945 environments: Some(vec![
3946 fdecl::Environment {
3947 name: Some("env".to_string()),
3948 extends: Some(fdecl::EnvironmentExtends::Realm),
3949 resolvers: Some(vec![
3950 fdecl::ResolverRegistration {
3951 resolver: Some("gopher".to_string()),
3952 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3953 scheme: Some("gopher".to_string()),
3954 ..Default::default()
3955 }
3956 ]),
3957 ..Default::default()
3958 },
3959 ]),
3960 offers: Some(vec![
3961 fdecl::Offer::Service(fdecl::OfferService {
3962 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
3963 source_name: Some("a".to_string()),
3964 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3965 target_name: Some("a".to_string()),
3966 ..Default::default()
3967 }),
3968 ]),
3969 children: Some(vec![
3970 fdecl::Child {
3971 name: Some("child1".to_string()),
3972 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3973 startup: Some(fdecl::StartupMode::Lazy),
3974 on_terminate: None,
3975 ..Default::default()
3976 },
3977 fdecl::Child {
3978 name: Some("child2".to_string()),
3979 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
3980 startup: Some(fdecl::StartupMode::Lazy),
3981 environment: Some("env".to_string()),
3982 on_terminate: None,
3983 ..Default::default()
3984 }
3985 ]),
3986 ..new_component_decl()
3987 }
3988 },
3989 result = Err(ErrorList::new(vec![
3990 Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}".to_string()),
3991 ])),
3992 },
3993 test_validate_strong_cycle_between_self_and_two_children => {
3994 input = {
3995 fdecl::Component {
3996 capabilities: Some(vec![
3997 fdecl::Capability::Protocol(fdecl::Protocol {
3998 name: Some("fuchsia.foo.Bar".to_string()),
3999 source_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4000 ..Default::default()
4001 })
4002 ]),
4003 offers: Some(vec![
4004 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4005 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4006 source_name: Some("fuchsia.foo.Bar".to_string()),
4007 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4008 target_name: Some("fuchsia.foo.Bar".to_string()),
4009 dependency_type: Some(fdecl::DependencyType::Strong),
4010 ..Default::default()
4011 }),
4012 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4013 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4014 source_name: Some("fuchsia.bar.Baz".to_string()),
4015 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4016 target_name: Some("fuchsia.bar.Baz".to_string()),
4017 dependency_type: Some(fdecl::DependencyType::Strong),
4018 ..Default::default()
4019 }),
4020 ]),
4021 uses: Some(vec![
4022 fdecl::Use::Protocol(fdecl::UseProtocol {
4023 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child2".to_string(), collection: None})),
4024 source_name: Some("fuchsia.baz.Foo".to_string()),
4025 target_path: Some("/svc/fuchsia.baz.Foo".to_string()),
4026 dependency_type: Some(fdecl::DependencyType::Strong),
4027 ..Default::default()
4028 }),
4029 ]),
4030 children: Some(vec![
4031 fdecl::Child {
4032 name: Some("child1".to_string()),
4033 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4034 startup: Some(fdecl::StartupMode::Lazy),
4035 on_terminate: None,
4036 ..Default::default()
4037 },
4038 fdecl::Child {
4039 name: Some("child2".to_string()),
4040 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4041 startup: Some(fdecl::StartupMode::Lazy),
4042 on_terminate: None,
4043 ..Default::default()
4044 }
4045 ]),
4046 ..new_component_decl()
4047 }
4048 },
4049 result = Err(ErrorList::new(vec![
4050 Error::dependency_cycle("{{self -> child child1 -> child child2 -> self}}".to_string()),
4051 ])),
4052 },
4053 test_validate_strong_cycle_with_self_storage => {
4054 input = {
4055 fdecl::Component {
4056 capabilities: Some(vec![
4057 fdecl::Capability::Storage(fdecl::Storage {
4058 name: Some("data".to_string()),
4059 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4060 backing_dir: Some("minfs".to_string()),
4061 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4062 ..Default::default()
4063 }),
4064 fdecl::Capability::Directory(fdecl::Directory {
4065 name: Some("minfs".to_string()),
4066 source_path: Some("/minfs".to_string()),
4067 rights: Some(fio::RW_STAR_DIR),
4068 ..Default::default()
4069 }),
4070 ]),
4071 offers: Some(vec![
4072 fdecl::Offer::Storage(fdecl::OfferStorage {
4073 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4074 source_name: Some("data".to_string()),
4075 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4076 target_name: Some("data".to_string()),
4077 ..Default::default()
4078 }),
4079 ]),
4080 uses: Some(vec![
4081 fdecl::Use::Protocol(fdecl::UseProtocol {
4082 dependency_type: Some(fdecl::DependencyType::Strong),
4083 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4084 source_name: Some("fuchsia.foo.Bar".to_string()),
4085 target_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4086 ..Default::default()
4087 }),
4088 ]),
4089 children: Some(vec![
4090 fdecl::Child {
4091 name: Some("child".to_string()),
4092 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4093 startup: Some(fdecl::StartupMode::Lazy),
4094 ..Default::default()
4095 },
4096 ]),
4097 ..new_component_decl()
4098 }
4099 },
4100 result = Err(ErrorList::new(vec![
4101 Error::dependency_cycle("{{self -> capability data -> child child -> self}}".to_string()),
4102 ])),
4103 },
4104 test_validate_strong_cycle_with_self_storage_admin_protocol => {
4105 input = {
4106 fdecl::Component {
4107 capabilities: Some(vec![
4108 fdecl::Capability::Storage(fdecl::Storage {
4109 name: Some("data".to_string()),
4110 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4111 backing_dir: Some("minfs".to_string()),
4112 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4113 ..Default::default()
4114 }),
4115 fdecl::Capability::Directory(fdecl::Directory {
4116 name: Some("minfs".to_string()),
4117 source_path: Some("/minfs".to_string()),
4118 rights: Some(fio::RW_STAR_DIR),
4119 ..Default::default()
4120 }),
4121 ]),
4122 offers: Some(vec![
4123 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4124 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef { name: "data".to_string() })),
4125 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4126 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4127 target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4128 dependency_type: Some(fdecl::DependencyType::Strong),
4129 ..Default::default()
4130 }),
4131 ]),
4132 uses: Some(vec![
4133 fdecl::Use::Protocol(fdecl::UseProtocol {
4134 dependency_type: Some(fdecl::DependencyType::Strong),
4135 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4136 source_name: Some("fuchsia.foo.Bar".to_string()),
4137 target_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4138 ..Default::default()
4139 }),
4140 ]),
4141 children: Some(vec![
4142 fdecl::Child {
4143 name: Some("child".to_string()),
4144 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4145 startup: Some(fdecl::StartupMode::Lazy),
4146 ..Default::default()
4147 },
4148 ]),
4149 ..new_component_decl()
4150 }
4151 },
4152 result = Err(ErrorList::new(vec![
4153 Error::dependency_cycle("{{self -> capability data -> child child -> self}}".to_string()),
4154 ])),
4155 },
4156 test_validate_strong_cycle_with_dictionary => {
4157 input = fdecl::Component {
4158 offers: Some(vec![
4159 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
4160 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4161 source_name: Some("dict".into()),
4162 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4163 name: "a".into(),
4164 collection: None,
4165 })),
4166 target_name: Some("dict".into()),
4167 dependency_type: Some(fdecl::DependencyType::Strong),
4168 ..Default::default()
4169 }),
4170 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4171 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4172 name: "b".into(),
4173 collection: None,
4174 })),
4175 source_name: Some("1".into()),
4176 target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4177 name: "dict".into(),
4178 })),
4179 target_name: Some("1".into()),
4180 dependency_type: Some(fdecl::DependencyType::Strong),
4181 ..Default::default()
4182 }),
4183 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4184 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4185 name: "a".into(),
4186 collection: None,
4187 })),
4188 source_name: Some("2".into()),
4189 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4190 name: "b".into(),
4191 collection: None,
4192 })),
4193 target_name: Some("2".into()),
4194 dependency_type: Some(fdecl::DependencyType::Strong),
4195 ..Default::default()
4196 }),
4197 ]),
4198 children: Some(vec![
4199 fdecl::Child {
4200 name: Some("a".into()),
4201 url: Some("fuchsia-pkg://child".into()),
4202 startup: Some(fdecl::StartupMode::Lazy),
4203 ..Default::default()
4204 },
4205 fdecl::Child {
4206 name: Some("b".into()),
4207 url: Some("fuchsia-pkg://child".into()),
4208 startup: Some(fdecl::StartupMode::Lazy),
4209 ..Default::default()
4210 },
4211 ]),
4212 capabilities: Some(vec![
4213 fdecl::Capability::Dictionary(fdecl::Dictionary {
4214 name: Some("dict".into()),
4215 ..Default::default()
4216 }),
4217 ]),
4218 ..Default::default()
4219 },
4220 result = Err(ErrorList::new(vec![
4221 Error::dependency_cycle("{{child a -> child b -> capability dict -> child a}}".to_string()),
4222 ])),
4223 },
4224 test_validate_strong_cycle_with_dictionary_indirect => {
4225 input = fdecl::Component {
4226 offers: Some(vec![
4227 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4228 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4229 source_name: Some("3".into()),
4230 source_dictionary: Some("dict".into()),
4231 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4232 name: "a".into(),
4233 collection: None,
4234 })),
4235 target_name: Some("3".into()),
4236 dependency_type: Some(fdecl::DependencyType::Strong),
4237 ..Default::default()
4238 }),
4239 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4240 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4241 name: "b".into(),
4242 collection: None,
4243 })),
4244 source_name: Some("1".into()),
4245 target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4246 name: "dict".into(),
4247 })),
4248 target_name: Some("1".into()),
4249 dependency_type: Some(fdecl::DependencyType::Strong),
4250 ..Default::default()
4251 }),
4252 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4253 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4254 name: "a".into(),
4255 collection: None,
4256 })),
4257 source_name: Some("2".into()),
4258 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4259 name: "b".into(),
4260 collection: None,
4261 })),
4262 target_name: Some("2".into()),
4263 dependency_type: Some(fdecl::DependencyType::Strong),
4264 ..Default::default()
4265 }),
4266 ]),
4267 children: Some(vec![
4268 fdecl::Child {
4269 name: Some("a".into()),
4270 url: Some("fuchsia-pkg://child".into()),
4271 startup: Some(fdecl::StartupMode::Lazy),
4272 ..Default::default()
4273 },
4274 fdecl::Child {
4275 name: Some("b".into()),
4276 url: Some("fuchsia-pkg://child".into()),
4277 startup: Some(fdecl::StartupMode::Lazy),
4278 ..Default::default()
4279 },
4280 ]),
4281 capabilities: Some(vec![
4282 fdecl::Capability::Dictionary(fdecl::Dictionary {
4283 name: Some("dict".into()),
4284 ..Default::default()
4285 }),
4286 ]),
4287 ..Default::default()
4288 },
4289 result = Err(ErrorList::new(vec![
4290 Error::dependency_cycle("{{child a -> child b -> capability dict -> child a}}".to_string()),
4291 ])),
4292 },
4293 test_validate_use_from_child_offer_to_child_weak_cycle => {
4294 input = {
4295 fdecl::Component {
4296 capabilities: Some(vec![
4297 fdecl::Capability::Service(fdecl::Service {
4298 name: Some("a".to_string()),
4299 source_path: Some("/a".to_string()),
4300 ..Default::default()
4301 })]),
4302 uses: Some(vec![
4303 fdecl::Use::Protocol(fdecl::UseProtocol {
4304 dependency_type: Some(fdecl::DependencyType::Weak),
4305 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4306 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4307 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
4308 ..Default::default()
4309 }),
4310 fdecl::Use::Service(fdecl::UseService {
4311 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4312 source_name: Some("service_name".to_string()),
4313 target_path: Some("/svc/service_name".to_string()),
4314 dependency_type: Some(fdecl::DependencyType::Weak),
4315 ..Default::default()
4316 }),
4317 fdecl::Use::Directory(fdecl::UseDirectory {
4318 dependency_type: Some(fdecl::DependencyType::Weak),
4319 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4320 source_name: Some("DirectoryName".to_string()),
4321 target_path: Some("/data/DirectoryName".to_string()),
4322 rights: Some(fio::Operations::CONNECT),
4323 subdir: None,
4324 ..Default::default()
4325 }),
4326 ]),
4327 offers: Some(vec![
4328 fdecl::Offer::Service(fdecl::OfferService {
4329 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4330 source_name: Some("a".to_string()),
4331 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4332 target_name: Some("a".to_string()),
4333 ..Default::default()
4334 })
4335 ]),
4336 children: Some(vec![
4337 fdecl::Child {
4338 name: Some("child".to_string()),
4339 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4340 startup: Some(fdecl::StartupMode::Lazy),
4341 on_terminate: None,
4342 ..Default::default()
4343 }
4344 ]),
4345 ..new_component_decl()
4346 }
4347 },
4348 result = Ok(()),
4349 },
4350 test_validate_expose_from_self_to_framework_and_parent => {
4351 input = {
4352 fdecl::Component {
4353 capabilities: Some(vec![
4354 fdecl::Capability::Protocol(fdecl::Protocol {
4355 name: Some("a".to_string()),
4356 source_path: Some("/a".to_string()),
4357 ..Default::default()
4358 }),
4359 ]),
4360 exposes: Some(vec![
4361 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
4362 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4363 source_name: Some("a".to_string()),
4364 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4365 target_name: Some("a".to_string()),
4366 ..Default::default()
4367 }),
4368 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
4369 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4370 source_name: Some("a".to_string()),
4371 target: Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})),
4372 target_name: Some("a".to_string()),
4373 ..Default::default()
4374 }),
4375 ]),
4376 ..new_component_decl()
4377 }
4378 },
4379 result = Ok(()),
4380 },
4381 test_validate_use_from_not_child_weak => {
4382 input = {
4383 fdecl::Component {
4384 uses: Some(vec![
4385 fdecl::Use::Protocol(fdecl::UseProtocol {
4386 dependency_type: Some(fdecl::DependencyType::Weak),
4387 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4388 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4389 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
4390 ..Default::default()
4391 }),
4392 ]),
4393 ..new_component_decl()
4394 }
4395 },
4396 result = Err(ErrorList::new(vec![
4397 Error::invalid_field(DeclType::UseProtocol, "dependency_type"),
4398 ])),
4399 },
4400 test_validate_event_stream_offer_valid_decls => {
4401 input = {
4402 let mut decl = new_component_decl();
4403 decl.offers = Some(vec![
4404 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4405 source_name: Some("stopped".to_string()),
4406 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4407 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4408 target_name: Some("stopped".to_string()),
4409 ..Default::default()
4410 }),
4411 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4412 source_name: Some("started".to_string()),
4413 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4414 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4415 target_name: Some("started".to_string()),
4416 ..Default::default()
4417 }),
4418 ]);
4419 decl.children = Some(vec![fdecl::Child{
4420 name: Some("test".to_string()),
4421 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4422 startup: Some(fdecl::StartupMode::Lazy),
4423 on_terminate: None,
4424 environment: None,
4425 ..Default::default()
4426 },
4427 fdecl::Child{
4428 name: Some("test2".to_string()),
4429 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4430 startup: Some(fdecl::StartupMode::Lazy),
4431 on_terminate: None,
4432 environment: None,
4433 ..Default::default()
4434 }
4435 ]);
4436 decl
4437 },
4438 result = Ok(()),
4439 },
4440 test_validate_event_stream_offer_to_framework_invalid => {
4441 input = {
4442 let mut decl = new_component_decl();
4443 decl.offers = Some(vec![
4444 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4445 source_name: Some("stopped".to_string()),
4446 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4447 target: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
4448 target_name: Some("stopped".to_string()),
4449 ..Default::default()
4450 }),
4451 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4452 source_name: Some("started".to_string()),
4453 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4454 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4455 target_name: Some("started".to_string()),
4456 ..Default::default()
4457 }),
4458 ]);
4459 decl.children = Some(vec![fdecl::Child{
4460 name: Some("test".to_string()),
4461 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4462 startup: Some(fdecl::StartupMode::Lazy),
4463 on_terminate: None,
4464 environment: None,
4465 ..Default::default()
4466 },
4467 fdecl::Child{
4468 name: Some("test2".to_string()),
4469 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4470 startup: Some(fdecl::StartupMode::Lazy),
4471 on_terminate: None,
4472 environment: None,
4473 ..Default::default()
4474 }
4475 ]);
4476 decl
4477 },
4478 result = Err(ErrorList::new(vec![
4479 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "target".to_string() }),
4480 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "target".to_string() }),
4481 ])),
4482 },
4483 test_validate_event_stream_offer_to_scope_zero_length_invalid => {
4484 input = {
4485 let mut decl = new_component_decl();
4486 decl.offers = Some(vec![
4487 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4488 source_name: Some("started".to_string()),
4489 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4490 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4491 scope: Some(vec![]),
4492 target_name: Some("started".to_string()),
4493 ..Default::default()
4494 }),
4495 ]);
4496 decl.children = Some(vec![fdecl::Child{
4497 name: Some("test".to_string()),
4498 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4499 startup: Some(fdecl::StartupMode::Lazy),
4500 on_terminate: None,
4501 environment: None,
4502 ..Default::default()
4503 },
4504 fdecl::Child{
4505 name: Some("test2".to_string()),
4506 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4507 startup: Some(fdecl::StartupMode::Lazy),
4508 on_terminate: None,
4509 environment: None,
4510 ..Default::default()
4511 }
4512 ]);
4513 decl
4514 },
4515 result = Err(ErrorList::new(vec![
4516 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "scope".to_string() }),
4517 ])),
4518 },
4519 test_validate_event_stream_offer_to_scope_framework_invalid => {
4520 input = {
4521 let mut decl = new_component_decl();
4522 decl.offers = Some(vec![
4523 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4524 source_name: Some("started".to_string()),
4525 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4526 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4527 scope: Some(vec![fdecl::Ref::Framework(fdecl::FrameworkRef{})]),
4528 target_name: Some("started".to_string()),
4529 ..Default::default()
4530 }),
4531 ]);
4532 decl.children = Some(vec![fdecl::Child{
4533 name: Some("test".to_string()),
4534 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4535 startup: Some(fdecl::StartupMode::Lazy),
4536 on_terminate: None,
4537 environment: None,
4538 ..Default::default()
4539 },
4540 fdecl::Child{
4541 name: Some("test2".to_string()),
4542 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4543 startup: Some(fdecl::StartupMode::Lazy),
4544 on_terminate: None,
4545 environment: None,
4546 ..Default::default()
4547 }
4548 ]);
4549 decl
4550 },
4551 result = Err(ErrorList::new(vec![
4552 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "scope".to_string() }),
4553 ])),
4554 },
4555 test_validate_event_stream_offer_to_scope_valid => {
4556 input = {
4557 let mut decl = new_component_decl();
4558 decl.offers = Some(vec![
4559 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4560 source_name: Some("started".to_string()),
4561 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4562 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4563 scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
4564 target_name: Some("started".to_string()),
4565 ..Default::default()
4566 }),
4567 ]);
4568 decl.children = Some(vec![fdecl::Child{
4569 name: Some("test".to_string()),
4570 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4571 startup: Some(fdecl::StartupMode::Lazy),
4572 on_terminate: None,
4573 environment: None,
4574 ..Default::default()
4575 },
4576 fdecl::Child{
4577 name: Some("test2".to_string()),
4578 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4579 startup: Some(fdecl::StartupMode::Lazy),
4580 on_terminate: None,
4581 environment: None,
4582 ..Default::default()
4583 }
4584 ]);
4585 decl
4586 },
4587 result = Ok(()),
4588 },
4589 test_validate_event_stream_offer_to_scope_with_capability_requested => {
4590 input = {
4591 let mut decl = new_component_decl();
4592 decl.offers = Some(vec![
4593 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4594 source_name: Some("capability_requested".to_string()),
4595 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4596 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4597 scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
4598 target_name: Some("started".to_string()),
4599 ..Default::default()
4600 }),
4601 ]);
4602 decl.children = Some(vec![fdecl::Child{
4603 name: Some("test".to_string()),
4604 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4605 startup: Some(fdecl::StartupMode::Lazy),
4606 on_terminate: None,
4607 environment: None,
4608 ..Default::default()
4609 },
4610 fdecl::Child{
4611 name: Some("test2".to_string()),
4612 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4613 startup: Some(fdecl::StartupMode::Lazy),
4614 on_terminate: None,
4615 environment: None,
4616 ..Default::default()
4617 }
4618 ]);
4619 decl
4620 },
4621 result = Ok(()),
4622 },
4623 test_validate_event_stream_offer_with_no_source_name_invalid => {
4624 input = {
4625 let mut decl = new_component_decl();
4626 decl.offers = Some(vec![
4627 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4628 source_name: None,
4629 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4630 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4631 scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
4632 target_name: Some("started".to_string()),
4633 ..Default::default()
4634 }),
4635 ]);
4636 decl.children = Some(vec![fdecl::Child{
4637 name: Some("test".to_string()),
4638 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4639 startup: Some(fdecl::StartupMode::Lazy),
4640 on_terminate: None,
4641 environment: None,
4642 ..Default::default()
4643 },
4644 fdecl::Child{
4645 name: Some("test2".to_string()),
4646 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4647 startup: Some(fdecl::StartupMode::Lazy),
4648 on_terminate: None,
4649 environment: None,
4650 ..Default::default()
4651 }
4652 ]);
4653 decl
4654 },
4655 result = Err(ErrorList::new(vec![
4656 Error::MissingField(DeclField { decl: DeclType::OfferEventStream, field: "source_name".to_string() }),
4657 ])),
4658 },
4659 test_validate_event_stream_offer_invalid_source => {
4660 input = {
4661 let mut decl = new_component_decl();
4662 decl.offers = Some(vec![
4663 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4664 source_name: Some("stopped".to_string()),
4665 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
4666 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4667 target_name: Some("stopped".to_string()),
4668 ..Default::default()
4669 }),
4670 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4671 source_name: Some("started".to_string()),
4672 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4673 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4674 target_name: Some("started".to_string()),
4675 ..Default::default()
4676 }),
4677 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4678 source_name: Some("capability_requested".to_string()),
4679 source: Some(fdecl::Ref::Debug(fdecl::DebugRef{})),
4680 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4681 target_name: Some("capability_requested".to_string()),
4682 ..Default::default()
4683 }),
4684 ]);
4685 decl.children = Some(vec![fdecl::Child{
4686 name: Some("test".to_string()),
4687 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4688 startup: Some(fdecl::StartupMode::Lazy),
4689 on_terminate: None,
4690 environment: None,
4691 ..Default::default()
4692 },
4693 fdecl::Child{
4694 name: Some("test2".to_string()),
4695 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4696 startup: Some(fdecl::StartupMode::Lazy),
4697 on_terminate: None,
4698 environment: None,
4699 ..Default::default()
4700 }
4701 ]);
4702 decl
4703 },
4704 result = Err(ErrorList::new(vec![
4705 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "source".to_string() }),
4706 ])),
4707 },
4708
4709 test_validate_event_stream_offer_missing_source => {
4710 input = {
4711 let mut decl = new_component_decl();
4712 decl.offers = Some(vec![
4713 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4714 source_name: Some("stopped".to_string()),
4715 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
4716 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4717 target_name: Some("stopped".to_string()),
4718 ..Default::default()
4719 }),
4720 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4721 source_name: Some("started".to_string()),
4722 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4723 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4724 target_name: Some("started".to_string()),
4725 ..Default::default()
4726 }),
4727 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4728 source_name: Some("capability_requested".to_string()),
4729 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4730 target_name: Some("capability_requested".to_string()),
4731 ..Default::default()
4732 }),
4733 ]);
4734 decl.children = Some(vec![fdecl::Child{
4735 name: Some("test".to_string()),
4736 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4737 startup: Some(fdecl::StartupMode::Lazy),
4738 on_terminate: None,
4739 environment: None,
4740 ..Default::default()
4741 },
4742 fdecl::Child{
4743 name: Some("test2".to_string()),
4744 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4745 startup: Some(fdecl::StartupMode::Lazy),
4746 on_terminate: None,
4747 environment: None,
4748 ..Default::default()
4749 }
4750 ]);
4751 decl
4752 },
4753 result = Err(ErrorList::new(vec![
4754 Error::MissingField(DeclField { decl: DeclType::OfferEventStream, field: "source".to_string() }),
4755 ])),
4756 },
4757 test_validate_event_stream_must_have_target_path => {
4758 input = {
4759 let mut decl = new_component_decl();
4760 decl.uses = Some(vec![
4761 fdecl::Use::EventStream(fdecl::UseEventStream {
4762 source_name: Some("bar".to_string()),
4763 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4764 ..Default::default()
4765 }),
4766 ]);
4767 decl
4768 },
4769 result = Err(ErrorList::new(vec![
4770 Error::MissingField(DeclField { decl: DeclType::UseEventStream, field: "target_path".to_string() })
4771 ])),
4772 },
4773 test_validate_event_stream_must_have_source_names => {
4774 input = {
4775 let mut decl = new_component_decl();
4776 decl.uses = Some(vec![
4777 fdecl::Use::EventStream(fdecl::UseEventStream {
4778 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4779 target_path: Some("/svc/something".to_string()),
4780 ..Default::default()
4781 }),
4782 ]);
4783 decl
4784 },
4785 result = Err(ErrorList::new(vec![
4786 Error::MissingField(DeclField { decl: DeclType::UseEventStream, field: "source_name".to_string() })
4787 ])),
4788 },
4789 test_validate_event_stream_scope_must_be_child_or_collection => {
4790 input = {
4791 let mut decl = new_component_decl();
4792 decl.uses = Some(vec![
4793 fdecl::Use::EventStream(fdecl::UseEventStream {
4794 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4795 target_path: Some("/svc/something".to_string()),
4796 source_name: Some("some_source".to_string()),
4797 scope: Some(vec![fdecl::Ref::Framework(fdecl::FrameworkRef{})]),
4798 ..Default::default()
4799 }),
4800 ]);
4801 decl
4802 },
4803 result = Err(ErrorList::new(vec![
4804 Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "scope".to_string() })
4805 ])),
4806 },
4807 test_validate_event_stream_source_must_be_parent_or_child => {
4808 input = {
4809 let mut decl = new_component_decl();
4810 decl.uses = Some(vec![
4811 fdecl::Use::EventStream(fdecl::UseEventStream {
4812 source: Some(fdecl::Ref::Debug(fdecl::DebugRef{})),
4813 target_path: Some("/svc/something".to_string()),
4814 source_name: Some("some_source".to_string()),
4815 scope: Some(vec![]),
4816 ..Default::default()
4817 }),
4818 fdecl::Use::EventStream(fdecl::UseEventStream {
4819 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
4820 target_path: Some("/svc/something_else".to_string()),
4821 source_name: Some("some_source".to_string()),
4822 scope: Some(vec![]),
4823 ..Default::default()
4824 }),
4825 fdecl::Use::EventStream(fdecl::UseEventStream {
4826 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4827 target_path: Some("/svc/yet_something_else".to_string()),
4828 source_name: Some("some_source".to_string()),
4829 scope: Some(vec![]),
4830 ..Default::default()
4831 }),
4832 ]);
4833 decl
4834 },
4835 result = Err(ErrorList::new(vec![
4836 Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() }),
4837 Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() }),
4838 Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() })
4839 ])),
4840 },
4841 test_validate_no_runner => {
4842 input = {
4843 let mut decl = new_component_decl();
4844 decl.program = Some(fdecl::Program {
4845 runner: None,
4846 info: Some(fdata::Dictionary {
4847 entries: None,
4848 ..Default::default()
4849 }),
4850 ..Default::default()
4851 });
4852 decl
4853 },
4854 result = Err(ErrorList::new(vec![
4855 Error::MissingRunner,
4856 ])),
4857 },
4858 test_validate_uses_runner => {
4859 input = {
4860 let mut decl = new_component_decl();
4861 decl.program = Some(fdecl::Program {
4862 runner: None,
4863 info: Some(fdata::Dictionary {
4864 entries: None,
4865 ..Default::default()
4866 }),
4867 ..Default::default()
4868 });
4869 decl.uses = Some(vec![
4870 fdecl::Use::Runner(fdecl::UseRunner {
4871 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4872 source_name: Some("runner".to_string()),
4873 ..Default::default()
4874 }),
4875 ]);
4876 decl
4877 },
4878 result = Ok(()),
4879 },
4880 test_validate_program_and_uses_runner_match => {
4881 input = {
4882 let mut decl = new_component_decl();
4883 decl.program = Some(fdecl::Program {
4884 runner: Some("runner".to_string()),
4885 info: Some(fdata::Dictionary {
4886 entries: None,
4887 ..Default::default()
4888 }),
4889 ..Default::default()
4890 });
4891 decl.uses = Some(vec![
4892 fdecl::Use::Runner(fdecl::UseRunner {
4893 source: Some(fdecl::Ref::Environment(fdecl::EnvironmentRef {})),
4894 source_name: Some("runner".to_string()),
4895 ..Default::default()
4896 }),
4897 ]);
4898 decl
4899 },
4900 result = Ok(()),
4901 },
4902 test_validate_runner_names_conflict => {
4903 input = {
4904 let mut decl = new_component_decl();
4905 decl.program = Some(fdecl::Program {
4906 runner: Some("runner".to_string()),
4907 info: Some(fdata::Dictionary {
4908 entries: None,
4909 ..Default::default()
4910 }),
4911 ..Default::default()
4912 });
4913 decl.uses = Some(vec![
4914 fdecl::Use::Runner(fdecl::UseRunner {
4915 source: Some(fdecl::Ref::Environment(fdecl::EnvironmentRef {})),
4916 source_name: Some("other.runner".to_string()),
4917 ..Default::default()
4918 }),
4919 ]);
4920 decl
4921 },
4922 result = Err(ErrorList::new(vec![
4923 Error::ConflictingRunners,
4924 ])),
4925 },
4926 test_validate_uses_runner_not_environement => {
4927 input = {
4928 let mut decl = new_component_decl();
4929 decl.program = Some(fdecl::Program {
4930 runner: Some("runner".to_string()),
4931 info: Some(fdata::Dictionary {
4932 entries: None,
4933 ..Default::default()
4934 }),
4935 ..Default::default()
4936 });
4937 decl.uses = Some(vec![
4938 fdecl::Use::Runner(fdecl::UseRunner {
4939 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4940 source_name: Some("runner".to_string()),
4941 ..Default::default()
4942 }),
4943 ]);
4944 decl
4945 },
4946 result = Err(ErrorList::new(vec![
4947 Error::ConflictingRunners,
4948 ])),
4949 },
4950 test_validate_uses_long_identifiers => {
4951 input = {
4952 let mut decl = new_component_decl();
4953 decl.program = Some(fdecl::Program {
4954 runner: Some("elf".to_string()),
4955 info: Some(fdata::Dictionary {
4956 entries: None,
4957 ..Default::default()
4958 }),
4959 ..Default::default()
4960 });
4961 decl.uses = Some(vec![
4962 fdecl::Use::Service(fdecl::UseService {
4963 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4964 source_name: Some(format!("{}", "a".repeat(256))),
4965 target_path: Some("/a".repeat(2048)),
4966 dependency_type: Some(fdecl::DependencyType::Strong),
4967 ..Default::default()
4968 }),
4969 fdecl::Use::Protocol(fdecl::UseProtocol {
4970 dependency_type: Some(fdecl::DependencyType::Strong),
4971 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4972 source_name: Some(format!("{}", "a".repeat(256))),
4973 target_path: Some("/b".repeat(2048)),
4974 ..Default::default()
4975 }),
4976 fdecl::Use::Directory(fdecl::UseDirectory {
4977 dependency_type: Some(fdecl::DependencyType::Strong),
4978 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4979 source_name: Some(format!("{}", "a".repeat(256))),
4980 target_path: Some("/c".repeat(2048)),
4981 rights: Some(fio::Operations::CONNECT),
4982 subdir: None,
4983 ..Default::default()
4984 }),
4985 fdecl::Use::Storage(fdecl::UseStorage {
4986 source_name: Some("cache".to_string()),
4987 target_path: Some("/d".repeat(2048)),
4988 ..Default::default()
4989 }),
4990 ]);
4991 decl
4992 },
4993 result = Err(ErrorList::new(vec![
4994 Error::field_too_long(DeclType::UseService, "source_name"),
4995 Error::field_too_long(DeclType::UseService, "target_path"),
4996 Error::field_too_long(DeclType::UseProtocol, "source_name"),
4997 Error::field_too_long(DeclType::UseProtocol, "target_path"),
4998 Error::field_too_long(DeclType::UseDirectory, "source_name"),
4999 Error::field_too_long(DeclType::UseDirectory, "target_path"),
5000 Error::field_too_long(DeclType::UseStorage, "target_path"),
5001 ])),
5002 },
5003 test_validate_conflicting_paths => {
5004 input = {
5005 let mut decl = new_component_decl();
5006 decl.uses = Some(vec![
5007 fdecl::Use::Service(fdecl::UseService {
5008 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5009 source_name: Some("foo".to_string()),
5010 target_path: Some("/bar".to_string()),
5011 dependency_type: Some(fdecl::DependencyType::Strong),
5012 ..Default::default()
5013 }),
5014 fdecl::Use::Protocol(fdecl::UseProtocol {
5015 dependency_type: Some(fdecl::DependencyType::Strong),
5016 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5017 source_name: Some("space".to_string()),
5018 target_path: Some("/bar".to_string()),
5019 ..Default::default()
5020 }),
5021 fdecl::Use::Directory(fdecl::UseDirectory {
5022 dependency_type: Some(fdecl::DependencyType::Strong),
5023 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5024 source_name: Some("crow".to_string()),
5025 target_path: Some("/bar".to_string()),
5026 rights: Some(fio::Operations::CONNECT),
5027 subdir: None,
5028 ..Default::default()
5029 }),
5030 ]);
5031 decl
5032 },
5033 result = Err(ErrorList::new(vec![
5034 Error::duplicate_field(DeclType::UseProtocol, "target_path", "/bar"),
5035 Error::duplicate_field(DeclType::UseDirectory, "target_path", "/bar"),
5036 ])),
5037 },
5038 test_validate_exposes_empty => {
5040 input = {
5041 let mut decl = new_component_decl();
5042 decl.exposes = Some(vec![
5043 fdecl::Expose::Service(fdecl::ExposeService {
5044 source: None,
5045 source_name: None,
5046 target_name: None,
5047 target: None,
5048 ..Default::default()
5049 }),
5050 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5051 source: None,
5052 source_name: None,
5053 target_name: None,
5054 target: None,
5055 ..Default::default()
5056 }),
5057 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5058 source: None,
5059 source_name: None,
5060 target_name: None,
5061 target: None,
5062 rights: None,
5063 subdir: None,
5064 ..Default::default()
5065 }),
5066 fdecl::Expose::Runner(fdecl::ExposeRunner {
5067 source: None,
5068 source_name: None,
5069 target: None,
5070 target_name: None,
5071 ..Default::default()
5072 }),
5073 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5074 source: None,
5075 source_name: None,
5076 target: None,
5077 target_name: None,
5078 ..Default::default()
5079 }),
5080 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5081 ..Default::default()
5082 }),
5083 ]);
5084 decl
5085 },
5086 result = Err(ErrorList::new(vec![
5087 Error::missing_field(DeclType::ExposeService, "source"),
5088 Error::missing_field(DeclType::ExposeService, "target"),
5089 Error::missing_field(DeclType::ExposeService, "source_name"),
5090 Error::missing_field(DeclType::ExposeService, "target_name"),
5091 Error::missing_field(DeclType::ExposeProtocol, "source"),
5092 Error::missing_field(DeclType::ExposeProtocol, "target"),
5093 Error::missing_field(DeclType::ExposeProtocol, "source_name"),
5094 Error::missing_field(DeclType::ExposeProtocol, "target_name"),
5095 Error::missing_field(DeclType::ExposeDirectory, "source"),
5096 Error::missing_field(DeclType::ExposeDirectory, "target"),
5097 Error::missing_field(DeclType::ExposeDirectory, "source_name"),
5098 Error::missing_field(DeclType::ExposeDirectory, "target_name"),
5099 Error::missing_field(DeclType::ExposeRunner, "source"),
5100 Error::missing_field(DeclType::ExposeRunner, "target"),
5101 Error::missing_field(DeclType::ExposeRunner, "source_name"),
5102 Error::missing_field(DeclType::ExposeRunner, "target_name"),
5103 Error::missing_field(DeclType::ExposeResolver, "source"),
5104 Error::missing_field(DeclType::ExposeResolver, "target"),
5105 Error::missing_field(DeclType::ExposeResolver, "source_name"),
5106 Error::missing_field(DeclType::ExposeResolver, "target_name"),
5107 Error::missing_field(DeclType::ExposeDictionary, "source"),
5108 Error::missing_field(DeclType::ExposeDictionary, "target"),
5109 Error::missing_field(DeclType::ExposeDictionary, "source_name"),
5110 Error::missing_field(DeclType::ExposeDictionary, "target_name"),
5111 ])),
5112 },
5113 test_validate_exposes_extraneous => {
5114 input = {
5115 let mut decl = new_component_decl();
5116 decl.exposes = Some(vec![
5117 fdecl::Expose::Service(fdecl::ExposeService {
5118 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5119 name: "logger".to_string(),
5120 collection: Some("modular".to_string()),
5121 })),
5122 source_name: Some("logger".to_string()),
5123 target_name: Some("logger".to_string()),
5124 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5125 ..Default::default()
5126 }),
5127 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5128 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5129 name: "logger".to_string(),
5130 collection: Some("modular".to_string()),
5131 })),
5132 source_name: Some("legacy_logger".to_string()),
5133 target_name: Some("legacy_logger".to_string()),
5134 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5135 ..Default::default()
5136 }),
5137 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5138 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5139 name: "netstack".to_string(),
5140 collection: Some("modular".to_string()),
5141 })),
5142 source_name: Some("data".to_string()),
5143 target_name: Some("data".to_string()),
5144 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5145 rights: Some(fio::Operations::CONNECT),
5146 subdir: None,
5147 ..Default::default()
5148 }),
5149 fdecl::Expose::Runner(fdecl::ExposeRunner {
5150 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5151 name: "netstack".to_string(),
5152 collection: Some("modular".to_string()),
5153 })),
5154 source_name: Some("elf".to_string()),
5155 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5156 target_name: Some("elf".to_string()),
5157 ..Default::default()
5158 }),
5159 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5160 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5161 name: "netstack".to_string(),
5162 collection: Some("modular".to_string()),
5163 })),
5164 source_name: Some("pkg".to_string()),
5165 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5166 target_name: Some("pkg".to_string()),
5167 ..Default::default()
5168 }),
5169 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5170 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5171 name: "netstack".to_string(),
5172 collection: Some("modular".to_string()),
5173 })),
5174 source_name: Some("dict".to_string()),
5175 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5176 target_name: Some("dict".to_string()),
5177 ..Default::default()
5178 }),
5179 ]);
5180 decl
5181 },
5182 result = Err(ErrorList::new(vec![
5183 Error::extraneous_field(DeclType::ExposeService, "source.child.collection"),
5184 Error::extraneous_field(DeclType::ExposeProtocol, "source.child.collection"),
5185 Error::extraneous_field(DeclType::ExposeDirectory, "source.child.collection"),
5186 Error::extraneous_field(DeclType::ExposeRunner, "source.child.collection"),
5187 Error::extraneous_field(DeclType::ExposeResolver, "source.child.collection"),
5188 Error::extraneous_field(DeclType::ExposeDictionary, "source.child.collection"),
5189 ])),
5190 },
5191 test_validate_exposes_invalid_identifiers => {
5192 input = {
5193 let mut decl = new_component_decl();
5194 decl.exposes = Some(vec![
5195 fdecl::Expose::Service(fdecl::ExposeService {
5196 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5197 name: "^bad".to_string(),
5198 collection: None,
5199 })),
5200 source_name: Some("foo/".to_string()),
5201 target_name: Some("/".to_string()),
5202 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5203 ..Default::default()
5204 }),
5205 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5206 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5207 name: "^bad".to_string(),
5208 collection: None,
5209 })),
5210 source_name: Some("foo/".to_string()),
5211 target_name: Some("/".to_string()),
5212 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5213 ..Default::default()
5214 }),
5215 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5216 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5217 name: "^bad".to_string(),
5218 collection: None,
5219 })),
5220 source_name: Some("foo/".to_string()),
5221 target_name: Some("/".to_string()),
5222 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5223 rights: Some(fio::Operations::CONNECT),
5224 subdir: Some("/foo".to_string()),
5225 ..Default::default()
5226 }),
5227 fdecl::Expose::Runner(fdecl::ExposeRunner {
5228 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5229 name: "^bad".to_string(),
5230 collection: None,
5231 })),
5232 source_name: Some("/path".to_string()),
5233 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5234 target_name: Some("elf!".to_string()),
5235 ..Default::default()
5236 }),
5237 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5238 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5239 name: "^bad".to_string(),
5240 collection: None,
5241 })),
5242 source_name: Some("/path".to_string()),
5243 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5244 target_name: Some("pkg!".to_string()),
5245 ..Default::default()
5246 }),
5247 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5248 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5249 name: "^bad".to_string(),
5250 collection: None,
5251 })),
5252 source_name: Some("/path".to_string()),
5253 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5254 target_name: Some("pkg!".to_string()),
5255 ..Default::default()
5256 }),
5257 ]);
5258 decl
5259 },
5260 result = Err(ErrorList::new(vec![
5261 Error::invalid_field(DeclType::ExposeService, "source.child.name"),
5262 Error::invalid_field(DeclType::ExposeService, "source_name"),
5263 Error::invalid_field(DeclType::ExposeService, "target_name"),
5264 Error::invalid_field(DeclType::ExposeProtocol, "source.child.name"),
5265 Error::invalid_field(DeclType::ExposeProtocol, "source_name"),
5266 Error::invalid_field(DeclType::ExposeProtocol, "target_name"),
5267 Error::invalid_field(DeclType::ExposeDirectory, "source.child.name"),
5268 Error::invalid_field(DeclType::ExposeDirectory, "source_name"),
5269 Error::invalid_field(DeclType::ExposeDirectory, "target_name"),
5270 Error::invalid_field(DeclType::ExposeDirectory, "subdir"),
5271 Error::invalid_field(DeclType::ExposeRunner, "source.child.name"),
5272 Error::invalid_field(DeclType::ExposeRunner, "source_name"),
5273 Error::invalid_field(DeclType::ExposeRunner, "target_name"),
5274 Error::invalid_field(DeclType::ExposeResolver, "source.child.name"),
5275 Error::invalid_field(DeclType::ExposeResolver, "source_name"),
5276 Error::invalid_field(DeclType::ExposeResolver, "target_name"),
5277 Error::invalid_field(DeclType::ExposeDictionary, "source.child.name"),
5278 Error::invalid_field(DeclType::ExposeDictionary, "source_name"),
5279 Error::invalid_field(DeclType::ExposeDictionary, "target_name"),
5280 ])),
5281 },
5282 test_validate_exposes_invalid_source_target => {
5283 input = {
5284 let mut decl = new_component_decl();
5285 decl.children = Some(vec![fdecl::Child{
5286 name: Some("logger".to_string()),
5287 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5288 startup: Some(fdecl::StartupMode::Lazy),
5289 on_terminate: None,
5290 environment: None,
5291 ..Default::default()
5292 }]);
5293 decl.exposes = Some(vec![
5294 fdecl::Expose::Service(fdecl::ExposeService {
5295 source: None,
5296 source_name: Some("a".to_string()),
5297 target_name: Some("b".to_string()),
5298 target: None,
5299 ..Default::default()
5300 }),
5301 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5302 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5303 source_name: Some("c".to_string()),
5304 target_name: Some("d".to_string()),
5305 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5306 ..Default::default()
5307 }),
5308 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5309 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "z".to_string()})),
5310 source_name: Some("e".to_string()),
5311 target_name: Some("f".to_string()),
5312 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "z".to_string()})),
5313 rights: Some(fio::Operations::CONNECT),
5314 subdir: None,
5315 ..Default::default()
5316 }),
5317 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5318 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5319 source_name: Some("g".to_string()),
5320 target_name: Some("h".to_string()),
5321 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5322 rights: Some(fio::Operations::CONNECT),
5323 subdir: None,
5324 ..Default::default()
5325 }),
5326 fdecl::Expose::Runner(fdecl::ExposeRunner {
5327 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5328 source_name: Some("i".to_string()),
5329 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5330 target_name: Some("j".to_string()),
5331 ..Default::default()
5332 }),
5333 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5334 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5335 source_name: Some("k".to_string()),
5336 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5337 target_name: Some("l".to_string()),
5338 ..Default::default()
5339 }),
5340 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5341 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5342 name: "logger".to_string(),
5343 collection: None,
5344 })),
5345 source_name: Some("m".to_string()),
5346 target_name: Some("n".to_string()),
5347 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5348 ..Default::default()
5349 }),
5350 ]);
5351 decl
5352 },
5353 result = Err(ErrorList::new(vec![
5354 Error::missing_field(DeclType::ExposeService, "source"),
5355 Error::missing_field(DeclType::ExposeService, "target"),
5356 Error::invalid_field(DeclType::ExposeProtocol, "source"),
5357 Error::invalid_field(DeclType::ExposeProtocol, "target"),
5358 Error::invalid_field(DeclType::ExposeDirectory, "source"),
5359 Error::invalid_field(DeclType::ExposeDirectory, "target"),
5360 Error::invalid_field(DeclType::ExposeDirectory, "source"),
5361 Error::invalid_field(DeclType::ExposeDirectory, "target"),
5362 Error::invalid_field(DeclType::ExposeRunner, "source"),
5363 Error::invalid_field(DeclType::ExposeRunner, "target"),
5364 Error::invalid_field(DeclType::ExposeResolver, "source"),
5365 Error::invalid_field(DeclType::ExposeResolver, "target"),
5366 Error::invalid_field(DeclType::ExposeDictionary, "target"),
5367 ])),
5368 },
5369 test_validate_exposes_invalid_source_collection => {
5370 input = {
5371 let mut decl = new_component_decl();
5372 decl.collections = Some(vec![fdecl::Collection{
5373 name: Some("col".to_string()),
5374 durability: Some(fdecl::Durability::Transient),
5375 allowed_offers: None,
5376 allow_long_names: None,
5377 ..Default::default()
5378 }]);
5379 decl.exposes = Some(vec![
5380 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5381 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
5382 source_name: Some("a".to_string()),
5383 target_name: Some("a".to_string()),
5384 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5385 ..Default::default()
5386 }),
5387 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5388 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5389 source_name: Some("b".to_string()),
5390 target_name: Some("b".to_string()),
5391 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5392 rights: Some(fio::Operations::CONNECT),
5393 subdir: None,
5394 ..Default::default()
5395 }),
5396 fdecl::Expose::Runner(fdecl::ExposeRunner {
5397 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5398 source_name: Some("c".to_string()),
5399 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5400 target_name: Some("c".to_string()),
5401 ..Default::default()
5402 }),
5403 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5404 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5405 source_name: Some("d".to_string()),
5406 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5407 target_name: Some("d".to_string()),
5408 ..Default::default()
5409 }),
5410 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5411 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5412 source_name: Some("e".to_string()),
5413 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5414 target_name: Some("e".to_string()),
5415 ..Default::default()
5416 }),
5417 ]);
5418 decl
5419 },
5420 result = Err(ErrorList::new(vec![
5421 Error::invalid_field(DeclType::ExposeProtocol, "source"),
5422 Error::invalid_field(DeclType::ExposeDirectory, "source"),
5423 Error::invalid_field(DeclType::ExposeRunner, "source"),
5424 Error::invalid_field(DeclType::ExposeResolver, "source"),
5425 Error::invalid_field(DeclType::ExposeDictionary, "source"),
5426 ])),
5427 },
5428 test_validate_exposes_sources_collection => {
5429 input = {
5430 let mut decl = new_component_decl();
5431 decl.collections = Some(vec![
5432 fdecl::Collection {
5433 name: Some("col".to_string()),
5434 durability: Some(fdecl::Durability::Transient),
5435 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
5436 allow_long_names: None,
5437 ..Default::default()
5438 }
5439 ]);
5440 decl.exposes = Some(vec![
5441 fdecl::Expose::Service(fdecl::ExposeService {
5442 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
5443 source_name: Some("a".to_string()),
5444 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5445 target_name: Some("a".to_string()),
5446 ..Default::default()
5447 })
5448 ]);
5449 decl
5450 },
5451 result = Ok(()),
5452 },
5453 test_validate_exposes_long_identifiers => {
5454 input = {
5455 let mut decl = new_component_decl();
5456 decl.exposes = Some(vec![
5457 fdecl::Expose::Service(fdecl::ExposeService {
5458 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5459 name: "b".repeat(256),
5460 collection: None,
5461 })),
5462 source_name: Some(format!("{}", "a".repeat(1025))),
5463 target_name: Some(format!("{}", "b".repeat(1025))),
5464 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5465 ..Default::default()
5466 }),
5467 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5468 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5469 name: "b".repeat(256),
5470 collection: None,
5471 })),
5472 source_name: Some(format!("{}", "a".repeat(256))),
5473 target_name: Some(format!("{}", "b".repeat(256))),
5474 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5475 ..Default::default()
5476 }),
5477 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5478 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5479 name: "b".repeat(256),
5480 collection: None,
5481 })),
5482 source_name: Some(format!("{}", "a".repeat(256))),
5483 target_name: Some(format!("{}", "b".repeat(256))),
5484 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5485 rights: Some(fio::Operations::CONNECT),
5486 subdir: None,
5487 ..Default::default()
5488 }),
5489 fdecl::Expose::Runner(fdecl::ExposeRunner {
5490 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5491 name: "b".repeat(256),
5492 collection: None,
5493 })),
5494 source_name: Some("a".repeat(256)),
5495 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5496 target_name: Some("b".repeat(256)),
5497 ..Default::default()
5498 }),
5499 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5500 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5501 name: "b".repeat(256),
5502 collection: None,
5503 })),
5504 source_name: Some("a".repeat(256)),
5505 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5506 target_name: Some("b".repeat(256)),
5507 ..Default::default()
5508 }),
5509 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5510 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5511 name: "b".repeat(256),
5512 collection: None,
5513 })),
5514 source_name: Some("a".repeat(256)),
5515 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5516 target_name: Some("b".repeat(256)),
5517 ..Default::default()
5518 }),
5519 ]);
5520 decl
5521 },
5522 result = Err(ErrorList::new(vec![
5523 Error::field_too_long(DeclType::ExposeService, "source.child.name"),
5524 Error::field_too_long(DeclType::ExposeService, "source_name"),
5525 Error::field_too_long(DeclType::ExposeService, "target_name"),
5526 Error::field_too_long(DeclType::ExposeProtocol, "source.child.name"),
5527 Error::field_too_long(DeclType::ExposeProtocol, "source_name"),
5528 Error::field_too_long(DeclType::ExposeProtocol, "target_name"),
5529 Error::field_too_long(DeclType::ExposeDirectory, "source.child.name"),
5530 Error::field_too_long(DeclType::ExposeDirectory, "source_name"),
5531 Error::field_too_long(DeclType::ExposeDirectory, "target_name"),
5532 Error::field_too_long(DeclType::ExposeRunner, "source.child.name"),
5533 Error::field_too_long(DeclType::ExposeRunner, "source_name"),
5534 Error::field_too_long(DeclType::ExposeRunner, "target_name"),
5535 Error::field_too_long(DeclType::ExposeResolver, "source.child.name"),
5536 Error::field_too_long(DeclType::ExposeResolver, "source_name"),
5537 Error::field_too_long(DeclType::ExposeResolver, "target_name"),
5538 Error::field_too_long(DeclType::ExposeDictionary, "source.child.name"),
5539 Error::field_too_long(DeclType::ExposeDictionary, "source_name"),
5540 Error::field_too_long(DeclType::ExposeDictionary, "target_name"),
5541 ])),
5542 },
5543 test_validate_exposes_invalid_child => {
5544 input = {
5545 let mut decl = new_component_decl();
5546 decl.exposes = Some(vec![
5547 fdecl::Expose::Service(fdecl::ExposeService {
5548 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5549 name: "netstack".to_string(),
5550 collection: None,
5551 })),
5552 source_name: Some("fuchsia.logger.Log".to_string()),
5553 target_name: Some("fuchsia.logger.Log".to_string()),
5554 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5555 ..Default::default()
5556 }),
5557 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5558 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5559 name: "netstack".to_string(),
5560 collection: None,
5561 })),
5562 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
5563 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
5564 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5565 ..Default::default()
5566 }),
5567 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5568 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5569 name: "netstack".to_string(),
5570 collection: None,
5571 })),
5572 source_name: Some("data".to_string()),
5573 target_name: Some("data".to_string()),
5574 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5575 rights: Some(fio::Operations::CONNECT),
5576 subdir: None,
5577 ..Default::default()
5578 }),
5579 fdecl::Expose::Runner(fdecl::ExposeRunner {
5580 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5581 name: "netstack".to_string(),
5582 collection: None,
5583 })),
5584 source_name: Some("elf".to_string()),
5585 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5586 target_name: Some("elf".to_string()),
5587 ..Default::default()
5588 }),
5589 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5590 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5591 name: "netstack".to_string(),
5592 collection: None,
5593 })),
5594 source_name: Some("pkg".to_string()),
5595 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5596 target_name: Some("pkg".to_string()),
5597 ..Default::default()
5598 }),
5599 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5600 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5601 name: "netstack".to_string(),
5602 collection: None,
5603 })),
5604 source_name: Some("dict".to_string()),
5605 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5606 target_name: Some("dict".to_string()),
5607 ..Default::default()
5608 }),
5609 ]);
5610 decl
5611 },
5612 result = Err(ErrorList::new(vec![
5613 Error::invalid_child(DeclType::ExposeService, "source", "netstack"),
5614 Error::invalid_child(DeclType::ExposeProtocol, "source", "netstack"),
5615 Error::invalid_child(DeclType::ExposeDirectory, "source", "netstack"),
5616 Error::invalid_child(DeclType::ExposeRunner, "source", "netstack"),
5617 Error::invalid_child(DeclType::ExposeResolver, "source", "netstack"),
5618 Error::invalid_child(DeclType::ExposeDictionary, "source", "netstack"),
5619 ])),
5620 },
5621 test_validate_exposes_invalid_source_capability => {
5622 input = {
5623 fdecl::Component {
5624 exposes: Some(vec![
5625 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5626 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
5627 name: "this-storage-doesnt-exist".to_string(),
5628 })),
5629 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
5630 target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
5631 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5632 ..Default::default()
5633 }),
5634 ]),
5635 ..new_component_decl()
5636 }
5637 },
5638 result = Err(ErrorList::new(vec![
5639 Error::invalid_capability(DeclType::ExposeProtocol, "source", "this-storage-doesnt-exist"),
5640 ])),
5641 },
5642 test_validate_exposes_duplicate_target => {
5643 input = {
5644 let mut decl = new_component_decl();
5645 decl.exposes = Some(vec![
5646 fdecl::Expose::Service(fdecl::ExposeService {
5647 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
5648 name: "coll".into(),
5649 })),
5650 source_name: Some("netstack".to_string()),
5651 target_name: Some("fuchsia.net.Stack".to_string()),
5652 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5653 ..Default::default()
5654 }),
5655 fdecl::Expose::Service(fdecl::ExposeService {
5656 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
5657 name: "coll2".into(),
5658 })),
5659 source_name: Some("netstack2".to_string()),
5660 target_name: Some("fuchsia.net.Stack".to_string()),
5661 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5662 ..Default::default()
5663 }),
5664 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5665 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5666 source_name: Some("fonts".to_string()),
5667 target_name: Some("fuchsia.fonts.Provider".to_string()),
5668 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5669 ..Default::default()
5670 }),
5671 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5672 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5673 source_name: Some("fonts2".to_string()),
5674 target_name: Some("fuchsia.fonts.Provider".to_string()),
5675 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5676 ..Default::default()
5677 }),
5678 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5679 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5680 source_name: Some("assets".to_string()),
5681 target_name: Some("stuff".to_string()),
5682 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5683 rights: None,
5684 subdir: None,
5685 ..Default::default()
5686 }),
5687 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5688 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5689 source_name: Some("assets2".to_string()),
5690 target_name: Some("stuff".to_string()),
5691 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5692 rights: None,
5693 subdir: None,
5694 ..Default::default()
5695 }),
5696 fdecl::Expose::Runner(fdecl::ExposeRunner {
5697 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5698 source_name: Some("source_elf".to_string()),
5699 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5700 target_name: Some("elf".to_string()),
5701 ..Default::default()
5702 }),
5703 fdecl::Expose::Runner(fdecl::ExposeRunner {
5704 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5705 source_name: Some("source_elf".to_string()),
5706 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5707 target_name: Some("elf".to_string()),
5708 ..Default::default()
5709 }),
5710 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5711 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5712 source_name: Some("source_pkg".to_string()),
5713 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5714 target_name: Some("pkg".to_string()),
5715 ..Default::default()
5716 }),
5717 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5718 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5719 source_name: Some("source_pkg".to_string()),
5720 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5721 target_name: Some("pkg".to_string()),
5722 ..Default::default()
5723 }),
5724 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5725 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5726 source_name: Some("source_dict".to_string()),
5727 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5728 target_name: Some("dict".to_string()),
5729 ..Default::default()
5730 }),
5731 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5732 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5733 source_name: Some("source_dict".to_string()),
5734 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5735 target_name: Some("dict".to_string()),
5736 ..Default::default()
5737 }),
5738 ]);
5739 decl.collections = Some(vec![
5740 fdecl::Collection {
5741 name: Some("coll".into()),
5742 durability: Some(fdecl::Durability::Transient),
5743 ..Default::default()
5744 },
5745 fdecl::Collection {
5746 name: Some("coll2".into()),
5747 durability: Some(fdecl::Durability::Transient),
5748 ..Default::default()
5749 },
5750 ]);
5751 decl.capabilities = Some(vec![
5752 fdecl::Capability::Service(fdecl::Service {
5753 name: Some("netstack".to_string()),
5754 source_path: Some("/path".to_string()),
5755 ..Default::default()
5756 }),
5757 fdecl::Capability::Service(fdecl::Service {
5758 name: Some("netstack2".to_string()),
5759 source_path: Some("/path".to_string()),
5760 ..Default::default()
5761 }),
5762 fdecl::Capability::Protocol(fdecl::Protocol {
5763 name: Some("fonts".to_string()),
5764 source_path: Some("/path".to_string()),
5765 ..Default::default()
5766 }),
5767 fdecl::Capability::Protocol(fdecl::Protocol {
5768 name: Some("fonts2".to_string()),
5769 source_path: Some("/path".to_string()),
5770 ..Default::default()
5771 }),
5772 fdecl::Capability::Directory(fdecl::Directory {
5773 name: Some("assets".to_string()),
5774 source_path: Some("/path".to_string()),
5775 rights: Some(fio::Operations::CONNECT),
5776 ..Default::default()
5777 }),
5778 fdecl::Capability::Directory(fdecl::Directory {
5779 name: Some("assets2".to_string()),
5780 source_path: Some("/path".to_string()),
5781 rights: Some(fio::Operations::CONNECT),
5782 ..Default::default()
5783 }),
5784 fdecl::Capability::Runner(fdecl::Runner {
5785 name: Some("source_elf".to_string()),
5786 source_path: Some("/path".to_string()),
5787 ..Default::default()
5788 }),
5789 fdecl::Capability::Resolver(fdecl::Resolver {
5790 name: Some("source_pkg".to_string()),
5791 source_path: Some("/path".to_string()),
5792 ..Default::default()
5793 }),
5794 fdecl::Capability::Dictionary(fdecl::Dictionary {
5795 name: Some("source_dict".to_string()),
5796 ..Default::default()
5797 }),
5798 ]);
5799 decl
5800 },
5801 result = Err(ErrorList::new(vec![
5802 Error::duplicate_field(DeclType::ExposeProtocol, "target_name",
5804 "fuchsia.fonts.Provider"),
5805 Error::duplicate_field(DeclType::ExposeDirectory, "target_name",
5806 "stuff"),
5807 Error::duplicate_field(DeclType::ExposeRunner, "target_name",
5808 "elf"),
5809 Error::duplicate_field(DeclType::ExposeResolver, "target_name", "pkg"),
5810 Error::duplicate_field(DeclType::ExposeDictionary, "target_name", "dict"),
5811 ])),
5812 },
5813 test_validate_exposes_invalid_capability_from_self => {
5814 input = {
5815 let mut decl = new_component_decl();
5816 decl.exposes = Some(vec![
5817 fdecl::Expose::Service(fdecl::ExposeService {
5818 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5819 source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
5820 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5821 target_name: Some("foo".to_string()),
5822 ..Default::default()
5823 }),
5824 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5825 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5826 source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
5827 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5828 target_name: Some("bar".to_string()),
5829 ..Default::default()
5830 }),
5831 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5832 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5833 source_name: Some("dir".to_string()),
5834 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5835 target_name: Some("assets".to_string()),
5836 rights: None,
5837 subdir: None,
5838 ..Default::default()
5839 }),
5840 fdecl::Expose::Runner(fdecl::ExposeRunner {
5841 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5842 source_name: Some("source_elf".to_string()),
5843 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5844 target_name: Some("elf".to_string()),
5845 ..Default::default()
5846 }),
5847 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5848 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5849 source_name: Some("source_pkg".to_string()),
5850 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5851 target_name: Some("pkg".to_string()),
5852 ..Default::default()
5853 }),
5854 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5855 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5856 source_name: Some("source_dict".to_string()),
5857 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5858 target_name: Some("dict".to_string()),
5859 ..Default::default()
5860 }),
5861 fdecl::Expose::Config(fdecl::ExposeConfiguration {
5862 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5863 source_name: Some("source_config".to_string()),
5864 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5865 target_name: Some("config".to_string()),
5866 ..Default::default()
5867 }),
5868 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5869 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5870 source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
5871 source_dictionary: Some("dict/inner".to_string()),
5872 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5873 target_name: Some("baz".to_string()),
5874 ..Default::default()
5875 }),
5876 ]);
5877 decl
5878 },
5879 result = Err(ErrorList::new(vec![
5880 Error::invalid_capability(
5881 DeclType::ExposeService,
5882 "source",
5883 "fuchsia.some.library.SomeProtocol"),
5884 Error::invalid_capability(
5885 DeclType::ExposeProtocol,
5886 "source",
5887 "fuchsia.some.library.SomeProtocol"),
5888 Error::invalid_capability(DeclType::ExposeDirectory, "source", "dir"),
5889 Error::invalid_capability(DeclType::ExposeRunner, "source", "source_elf"),
5890 Error::invalid_capability(DeclType::ExposeResolver, "source", "source_pkg"),
5891 Error::invalid_capability(DeclType::ExposeDictionary, "source", "source_dict"),
5892 Error::invalid_capability(DeclType::ExposeConfig, "source", "source_config"),
5893 Error::invalid_capability(DeclType::ExposeProtocol, "source", "dict"),
5894 ])),
5895 },
5896
5897 test_validate_exposes_availability_service => {
5898 input = {
5899 let mut decl = generate_expose_different_source_and_availability_decl(
5900 |source, availability, target_name|
5901 fdecl::Expose::Service(fdecl::ExposeService {
5902 source: Some(source),
5903 source_name: Some("fuchsia.examples.Echo".to_string()),
5904 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5905 target_name: Some(target_name.to_string()),
5906 availability: Some(availability),
5907 ..Default::default()
5908 })
5909 );
5910 decl.capabilities = Some(vec![
5911 fdecl::Capability::Service(fdecl::Service {
5912 name: Some("fuchsia.examples.Echo".to_string()),
5913 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
5914 ..Default::default()
5915 }),
5916 ]);
5917 decl
5918 },
5919 result = {
5920 Err(ErrorList::new(vec![
5921 Error::availability_must_be_optional(
5922 DeclType::ExposeService,
5923 "availability",
5924 Some(&"fuchsia.examples.Echo".to_string()),
5925 ),
5926 Error::availability_must_be_optional(
5927 DeclType::ExposeService,
5928 "availability",
5929 Some(&"fuchsia.examples.Echo".to_string()),
5930 ),
5931 ]))
5932 },
5933 },
5934 test_validate_exposes_availability_protocol => {
5935 input = {
5936 let mut decl = generate_expose_different_source_and_availability_decl(
5937 |source, availability, target_name|
5938 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5939 source: Some(source),
5940 source_name: Some("fuchsia.examples.Echo".to_string()),
5941 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5942 target_name: Some(target_name.to_string()),
5943 availability: Some(availability),
5944 ..Default::default()
5945 })
5946 );
5947 decl.capabilities = Some(vec![
5948 fdecl::Capability::Protocol(fdecl::Protocol {
5949 name: Some("fuchsia.examples.Echo".to_string()),
5950 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
5951 ..Default::default()
5952 }),
5953 ]);
5954 decl
5955 },
5956 result = {
5957 Err(ErrorList::new(vec![
5958 Error::availability_must_be_optional(
5959 DeclType::ExposeProtocol,
5960 "availability",
5961 Some(&"fuchsia.examples.Echo".to_string()),
5962 ),
5963 Error::availability_must_be_optional(
5964 DeclType::ExposeProtocol,
5965 "availability",
5966 Some(&"fuchsia.examples.Echo".to_string()),
5967 ),
5968 ]))
5969 },
5970 },
5971 test_validate_exposes_availability_directory => {
5972 input = {
5973 let mut decl = generate_expose_different_source_and_availability_decl(
5974 |source, availability, target_name|
5975 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5976 source: Some(source),
5977 source_name: Some("fuchsia.examples.Echo".to_string()),
5978 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5979 target_name: Some(target_name.to_string()),
5980 availability: Some(availability),
5981 ..Default::default()
5982 })
5983 );
5984 decl.capabilities = Some(vec![
5985 fdecl::Capability::Directory(fdecl::Directory {
5986 name: Some("fuchsia.examples.Echo".to_string()),
5987 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
5988 rights: Some(fio::Operations::READ_BYTES),
5989 ..Default::default()
5990 }),
5991 ]);
5992 decl
5993 },
5994 result = {
5995 Err(ErrorList::new(vec![
5996 Error::availability_must_be_optional(
5997 DeclType::ExposeDirectory,
5998 "availability",
5999 Some(&"fuchsia.examples.Echo".to_string()),
6000 ),
6001 Error::availability_must_be_optional(
6002 DeclType::ExposeDirectory,
6003 "availability",
6004 Some(&"fuchsia.examples.Echo".to_string()),
6005 ),
6006 ]))
6007 },
6008 },
6009
6010 test_validate_offers_empty => {
6012 input = {
6013 let mut decl = new_component_decl();
6014 decl.offers = Some(vec![
6015 fdecl::Offer::Service(fdecl::OfferService {
6016 source: None,
6017 source_name: None,
6018 target: None,
6019 target_name: None,
6020 ..Default::default()
6021 }),
6022 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6023 source: None,
6024 source_name: None,
6025 target: None,
6026 target_name: None,
6027 dependency_type: None,
6028 ..Default::default()
6029 }),
6030 fdecl::Offer::Directory(fdecl::OfferDirectory {
6031 source: None,
6032 source_name: None,
6033 target: None,
6034 target_name: None,
6035 rights: None,
6036 subdir: None,
6037 dependency_type: None,
6038 ..Default::default()
6039 }),
6040 fdecl::Offer::Storage(fdecl::OfferStorage {
6041 source_name: None,
6042 source: None,
6043 target: None,
6044 target_name: None,
6045 ..Default::default()
6046 }),
6047 fdecl::Offer::Runner(fdecl::OfferRunner {
6048 source: None,
6049 source_name: None,
6050 target: None,
6051 target_name: None,
6052 ..Default::default()
6053 }),
6054 fdecl::Offer::Resolver(fdecl::OfferResolver {
6055 ..Default::default()
6056 }),
6057 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6058 ..Default::default()
6059 }),
6060 ]);
6061 decl
6062 },
6063 result = Err(ErrorList::new(vec![
6066 Error::missing_field(DeclType::OfferService, "source"),
6067 Error::missing_field(DeclType::OfferService, "source_name"),
6068 Error::missing_field(DeclType::OfferService, "target"),
6069 Error::missing_field(DeclType::OfferService, "target_name"),
6070 Error::missing_field(DeclType::OfferProtocol, "source"),
6072 Error::missing_field(DeclType::OfferProtocol, "source_name"),
6073 Error::missing_field(DeclType::OfferProtocol, "target"),
6074 Error::missing_field(DeclType::OfferProtocol, "target_name"),
6075 Error::missing_field(DeclType::OfferProtocol, "dependency_type"),
6076 Error::missing_field(DeclType::OfferDirectory, "source"),
6078 Error::missing_field(DeclType::OfferDirectory, "source_name"),
6079 Error::missing_field(DeclType::OfferDirectory, "target"),
6080 Error::missing_field(DeclType::OfferDirectory, "target_name"),
6081 Error::missing_field(DeclType::OfferDirectory, "dependency_type"),
6082 Error::missing_field(DeclType::OfferStorage, "source"),
6084 Error::missing_field(DeclType::OfferStorage, "source_name"),
6085 Error::missing_field(DeclType::OfferStorage, "target"),
6086 Error::missing_field(DeclType::OfferStorage, "target_name"),
6087 Error::missing_field(DeclType::OfferRunner, "source"),
6089 Error::missing_field(DeclType::OfferRunner, "source_name"),
6090 Error::missing_field(DeclType::OfferRunner, "target"),
6091 Error::missing_field(DeclType::OfferRunner, "target_name"),
6092 Error::missing_field(DeclType::OfferResolver, "source"),
6094 Error::missing_field(DeclType::OfferResolver, "source_name"),
6095 Error::missing_field(DeclType::OfferResolver, "target"),
6096 Error::missing_field(DeclType::OfferResolver, "target_name"),
6097 Error::missing_field(DeclType::OfferDictionary, "source"),
6098 Error::missing_field(DeclType::OfferDictionary, "source_name"),
6099 Error::missing_field(DeclType::OfferDictionary, "target"),
6100 Error::missing_field(DeclType::OfferDictionary, "target_name"),
6101 Error::missing_field(DeclType::OfferDictionary, "dependency_type"),
6102 ])),
6103 },
6104 test_validate_offers_long_identifiers => {
6105 input = {
6106 let mut decl = new_component_decl();
6107 decl.offers = Some(vec![
6108 fdecl::Offer::Service(fdecl::OfferService {
6109 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6110 name: "a".repeat(256),
6111 collection: None,
6112 })),
6113 source_name: Some(format!("{}", "a".repeat(256))),
6114 target: Some(fdecl::Ref::Child(
6115 fdecl::ChildRef {
6116 name: "b".repeat(256),
6117 collection: None,
6118 }
6119 )),
6120 target_name: Some(format!("{}", "b".repeat(256))),
6121 ..Default::default()
6122 }),
6123 fdecl::Offer::Service(fdecl::OfferService {
6124 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6125 source_name: Some("a".to_string()),
6126 target: Some(fdecl::Ref::Collection(
6127 fdecl::CollectionRef {
6128 name: "b".repeat(256),
6129 }
6130 )),
6131 target_name: Some(format!("{}", "b".repeat(256))),
6132 ..Default::default()
6133 }),
6134 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6135 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6136 name: "a".repeat(256),
6137 collection: None,
6138 })),
6139 source_name: Some(format!("{}", "a".repeat(256))),
6140 target: Some(fdecl::Ref::Child(
6141 fdecl::ChildRef {
6142 name: "b".repeat(256),
6143 collection: None,
6144 }
6145 )),
6146 target_name: Some(format!("{}", "b".repeat(256))),
6147 dependency_type: Some(fdecl::DependencyType::Strong),
6148 ..Default::default()
6149 }),
6150 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6151 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6152 source_name: Some("a".to_string()),
6153 target: Some(fdecl::Ref::Collection(
6154 fdecl::CollectionRef {
6155 name: "b".repeat(256),
6156 }
6157 )),
6158 target_name: Some(format!("{}", "b".repeat(256))),
6159 dependency_type: Some(fdecl::DependencyType::Weak),
6160 ..Default::default()
6161 }),
6162 fdecl::Offer::Directory(fdecl::OfferDirectory {
6163 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6164 name: "a".repeat(256),
6165 collection: None,
6166 })),
6167 source_name: Some(format!("{}", "a".repeat(256))),
6168 target: Some(fdecl::Ref::Child(
6169 fdecl::ChildRef {
6170 name: "b".repeat(256),
6171 collection: None,
6172 }
6173 )),
6174 target_name: Some(format!("{}", "b".repeat(256))),
6175 rights: Some(fio::Operations::CONNECT),
6176 subdir: None,
6177 dependency_type: Some(fdecl::DependencyType::Strong),
6178 ..Default::default()
6179 }),
6180 fdecl::Offer::Directory(fdecl::OfferDirectory {
6181 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6182 source_name: Some("a".to_string()),
6183 target: Some(fdecl::Ref::Collection(
6184 fdecl::CollectionRef {
6185 name: "b".repeat(256),
6186 }
6187 )),
6188 target_name: Some(format!("{}", "b".repeat(256))),
6189 rights: Some(fio::Operations::CONNECT),
6190 subdir: None,
6191 dependency_type: Some(fdecl::DependencyType::Weak),
6192 ..Default::default()
6193 }),
6194 fdecl::Offer::Storage(fdecl::OfferStorage {
6195 source_name: Some("data".to_string()),
6196 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6197 target: Some(fdecl::Ref::Child(
6198 fdecl::ChildRef {
6199 name: "b".repeat(256),
6200 collection: None,
6201 }
6202 )),
6203 target_name: Some("data".to_string()),
6204 ..Default::default()
6205 }),
6206 fdecl::Offer::Storage(fdecl::OfferStorage {
6207 source_name: Some("data".to_string()),
6208 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6209 target: Some(fdecl::Ref::Collection(
6210 fdecl::CollectionRef { name: "b".repeat(256) }
6211 )),
6212 target_name: Some("data".to_string()),
6213 ..Default::default()
6214 }),
6215 fdecl::Offer::Runner(fdecl::OfferRunner {
6216 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6217 name: "a".repeat(256),
6218 collection: None,
6219 })),
6220 source_name: Some("b".repeat(256)),
6221 target: Some(fdecl::Ref::Collection(
6222 fdecl::CollectionRef {
6223 name: "c".repeat(256),
6224 }
6225 )),
6226 target_name: Some("d".repeat(256)),
6227 ..Default::default()
6228 }),
6229 fdecl::Offer::Resolver(fdecl::OfferResolver {
6230 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6231 name: "a".repeat(256),
6232 collection: None,
6233 })),
6234 source_name: Some("b".repeat(256)),
6235 target: Some(fdecl::Ref::Collection(
6236 fdecl::CollectionRef {
6237 name: "c".repeat(256),
6238 }
6239 )),
6240 target_name: Some("d".repeat(256)),
6241 ..Default::default()
6242 }),
6243 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6244 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6245 name: "a".repeat(256),
6246 collection: None,
6247 })),
6248 source_name: Some("b".repeat(256)),
6249 target: Some(fdecl::Ref::Collection(
6250 fdecl::CollectionRef {
6251 name: "c".repeat(256),
6252 }
6253 )),
6254 target_name: Some("d".repeat(256)),
6255 dependency_type: Some(fdecl::DependencyType::Strong),
6256 ..Default::default()
6257 }),
6258 ]);
6259 decl
6260 },
6261 result = Err(ErrorList::new(vec![
6262 Error::field_too_long(DeclType::OfferService, "source.child.name"),
6263 Error::field_too_long(DeclType::OfferService, "source_name"),
6264 Error::field_too_long(DeclType::OfferService, "target.child.name"),
6265 Error::field_too_long(DeclType::OfferService, "target_name"),
6266 Error::field_too_long(DeclType::OfferService, "target.collection.name"),
6267 Error::field_too_long(DeclType::OfferService, "target_name"),
6268 Error::field_too_long(DeclType::OfferProtocol, "source.child.name"),
6269 Error::field_too_long(DeclType::OfferProtocol, "source_name"),
6270 Error::field_too_long(DeclType::OfferProtocol, "target.child.name"),
6271 Error::field_too_long(DeclType::OfferProtocol, "target_name"),
6272 Error::field_too_long(DeclType::OfferProtocol, "target.collection.name"),
6273 Error::field_too_long(DeclType::OfferProtocol, "target_name"),
6274 Error::field_too_long(DeclType::OfferDirectory, "source.child.name"),
6275 Error::field_too_long(DeclType::OfferDirectory, "source_name"),
6276 Error::field_too_long(DeclType::OfferDirectory, "target.child.name"),
6277 Error::field_too_long(DeclType::OfferDirectory, "target_name"),
6278 Error::field_too_long(DeclType::OfferDirectory, "target.collection.name"),
6279 Error::field_too_long(DeclType::OfferDirectory, "target_name"),
6280 Error::field_too_long(DeclType::OfferStorage, "target.child.name"),
6281 Error::field_too_long(DeclType::OfferStorage, "target.collection.name"),
6282 Error::field_too_long(DeclType::OfferRunner, "source.child.name"),
6283 Error::field_too_long(DeclType::OfferRunner, "source_name"),
6284 Error::field_too_long(DeclType::OfferRunner, "target.collection.name"),
6285 Error::field_too_long(DeclType::OfferRunner, "target_name"),
6286 Error::field_too_long(DeclType::OfferResolver, "source.child.name"),
6287 Error::field_too_long(DeclType::OfferResolver, "source_name"),
6288 Error::field_too_long(DeclType::OfferResolver, "target.collection.name"),
6289 Error::field_too_long(DeclType::OfferResolver, "target_name"),
6290 Error::field_too_long(DeclType::OfferDictionary, "source.child.name"),
6291 Error::field_too_long(DeclType::OfferDictionary, "source_name"),
6292 Error::field_too_long(DeclType::OfferDictionary, "target.collection.name"),
6293 Error::field_too_long(DeclType::OfferDictionary, "target_name"),
6294 ])),
6295 },
6296 test_validate_offers_extraneous => {
6297 input = {
6298 let mut decl = new_component_decl();
6299 decl.offers = Some(vec![
6300 fdecl::Offer::Service(fdecl::OfferService {
6301 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6302 name: "logger".to_string(),
6303 collection: Some("modular".to_string()),
6304 })),
6305 source_name: Some("fuchsia.logger.Log".to_string()),
6306 target: Some(fdecl::Ref::Child(
6307 fdecl::ChildRef {
6308 name: "netstack".to_string(),
6309 collection: Some("modular".to_string()),
6310 }
6311 )),
6312 target_name: Some("fuchsia.logger.Log".to_string()),
6313 ..Default::default()
6314 }),
6315 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6316 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6317 name: "logger".to_string(),
6318 collection: Some("modular".to_string()),
6319 })),
6320 source_name: Some("fuchsia.logger.Log".to_string()),
6321 target: Some(fdecl::Ref::Child(
6322 fdecl::ChildRef {
6323 name: "netstack".to_string(),
6324 collection: Some("modular".to_string()),
6325 }
6326 )),
6327 target_name: Some("fuchsia.logger.Log".to_string()),
6328 dependency_type: Some(fdecl::DependencyType::Strong),
6329 ..Default::default()
6330 }),
6331 fdecl::Offer::Directory(fdecl::OfferDirectory {
6332 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6333 name: "logger".to_string(),
6334 collection: Some("modular".to_string()),
6335 })),
6336 source_name: Some("assets".to_string()),
6337 target: Some(fdecl::Ref::Child(
6338 fdecl::ChildRef {
6339 name: "netstack".to_string(),
6340 collection: Some("modular".to_string()),
6341 }
6342 )),
6343 target_name: Some("assets".to_string()),
6344 rights: Some(fio::Operations::CONNECT),
6345 subdir: None,
6346 dependency_type: Some(fdecl::DependencyType::Weak),
6347 ..Default::default()
6348 }),
6349 fdecl::Offer::Storage(fdecl::OfferStorage {
6350 source_name: Some("data".to_string()),
6351 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{ })),
6352 target: Some(fdecl::Ref::Child(
6353 fdecl::ChildRef {
6354 name: "netstack".to_string(),
6355 collection: Some("modular".to_string()),
6356 }
6357 )),
6358 target_name: Some("data".to_string()),
6359 ..Default::default()
6360 }),
6361 fdecl::Offer::Runner(fdecl::OfferRunner {
6362 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6363 name: "logger".to_string(),
6364 collection: Some("modular".to_string()),
6365 })),
6366 source_name: Some("elf".to_string()),
6367 target: Some(fdecl::Ref::Child(
6368 fdecl::ChildRef {
6369 name: "netstack".to_string(),
6370 collection: Some("modular".to_string()),
6371 }
6372 )),
6373 target_name: Some("elf".to_string()),
6374 ..Default::default()
6375 }),
6376 fdecl::Offer::Resolver(fdecl::OfferResolver {
6377 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6378 name: "logger".to_string(),
6379 collection: Some("modular".to_string()),
6380 })),
6381 source_name: Some("pkg".to_string()),
6382 target: Some(fdecl::Ref::Child(
6383 fdecl::ChildRef {
6384 name: "netstack".to_string(),
6385 collection: Some("modular".to_string()),
6386 }
6387 )),
6388 target_name: Some("pkg".to_string()),
6389 ..Default::default()
6390 }),
6391 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6392 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6393 name: "logger".to_string(),
6394 collection: Some("modular".to_string()),
6395 })),
6396 source_name: Some("dict".to_string()),
6397 target: Some(fdecl::Ref::Child(
6398 fdecl::ChildRef {
6399 name: "netstack".to_string(),
6400 collection: Some("modular".to_string()),
6401 }
6402 )),
6403 target_name: Some("dict".to_string()),
6404 dependency_type: Some(fdecl::DependencyType::Strong),
6405 ..Default::default()
6406 }),
6407 ]);
6408 decl.capabilities = Some(vec![
6409 fdecl::Capability::Protocol(fdecl::Protocol {
6410 name: Some("fuchsia.logger.Log".to_string()),
6411 source_path: Some("/svc/logger".to_string()),
6412 ..Default::default()
6413 }),
6414 fdecl::Capability::Directory(fdecl::Directory {
6415 name: Some("assets".to_string()),
6416 source_path: Some("/data/assets".to_string()),
6417 rights: Some(fio::Operations::CONNECT),
6418 ..Default::default()
6419 }),
6420 ]);
6421 decl
6422 },
6423 result = Err(ErrorList::new(vec![
6424 Error::extraneous_field(DeclType::OfferService, "source.child.collection"),
6425 Error::extraneous_field(DeclType::OfferService, "target.child.collection"),
6426 Error::extraneous_field(DeclType::OfferProtocol, "source.child.collection"),
6427 Error::extraneous_field(DeclType::OfferProtocol, "target.child.collection"),
6428 Error::extraneous_field(DeclType::OfferDirectory, "source.child.collection"),
6429 Error::extraneous_field(DeclType::OfferDirectory, "target.child.collection"),
6430 Error::extraneous_field(DeclType::OfferStorage, "target.child.collection"),
6431 Error::extraneous_field(DeclType::OfferRunner, "source.child.collection"),
6432 Error::extraneous_field(DeclType::OfferRunner, "target.child.collection"),
6433 Error::extraneous_field(DeclType::OfferResolver, "source.child.collection"),
6434 Error::extraneous_field(DeclType::OfferResolver, "target.child.collection"),
6435 Error::extraneous_field(DeclType::OfferDictionary, "source.child.collection"),
6436 Error::extraneous_field(DeclType::OfferDictionary, "target.child.collection"),
6437 ])),
6438 },
6439 test_validate_offers_invalid_filtered_service_fields => {
6440 input = {
6441 let mut decl = new_component_decl();
6442 decl.offers = Some(vec![
6443 fdecl::Offer::Service(fdecl::OfferService {
6444 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6445 source_name: Some("fuchsia.logger.Log".to_string()),
6446 target: Some(fdecl::Ref::Child(
6447 fdecl::ChildRef {
6448 name: "logger".to_string(),
6449 collection: None,
6450 }
6451 )),
6452 target_name: Some("fuchsia.logger.Log".to_string()),
6453 source_instance_filter: Some(vec![]),
6454 ..Default::default()
6455 }),
6456 fdecl::Offer::Service(fdecl::OfferService {
6457 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6458 source_name: Some("fuchsia.logger.Log".to_string()),
6459 target: Some(fdecl::Ref::Child(
6460 fdecl::ChildRef {
6461 name: "logger".to_string(),
6462 collection: None,
6463 }
6464 )),
6465 target_name: Some("fuchsia.logger.Log2".to_string()),
6466 source_instance_filter: Some(vec!["^badname".to_string()]),
6467 ..Default::default()
6468 }),
6469 fdecl::Offer::Service(fdecl::OfferService {
6470 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6471 source_name: Some("fuchsia.logger.Log".to_string()),
6472 target: Some(fdecl::Ref::Child(
6473 fdecl::ChildRef {
6474 name: "logger".to_string(),
6475 collection: None,
6476 }
6477 )),
6478 target_name: Some("fuchsia.logger.Log1".to_string()),
6479 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()}]),
6480 ..Default::default()
6481 }),
6482 fdecl::Offer::Service(fdecl::OfferService {
6483 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6484 source_name: Some("fuchsia.logger.Log".to_string()),
6485 target: Some(fdecl::Ref::Child(
6486 fdecl::ChildRef {
6487 name: "logger".to_string(),
6488 collection: None,
6489 }
6490 )),
6491 target_name: Some("fuchsia.logger.Log3".to_string()),
6492 renamed_instances: Some(vec![
6493 fdecl::NameMapping {
6494 source_name: "^badname".to_string(),
6495 target_name: "^badname".to_string(),
6496 }
6497 ]),
6498 ..Default::default()
6499 })
6500 ]);
6501 decl.children = Some(vec![
6502 fdecl::Child {
6503 name: Some("logger".to_string()),
6504 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
6505 startup: Some(fdecl::StartupMode::Lazy),
6506 on_terminate: None,
6507 environment: None,
6508 ..Default::default()
6509 },
6510 ]);
6511 decl
6512 },
6513 result = Err(ErrorList::new(vec![
6514 Error::invalid_field(DeclType::OfferService, "source_instance_filter"),
6515 Error::invalid_field(DeclType::OfferService, "source_instance_filter"),
6516 Error::invalid_field(DeclType::OfferService, "renamed_instances"),
6517 Error::invalid_field(DeclType::OfferService, "renamed_instances.source_name"),
6518 Error::invalid_field(DeclType::OfferService, "renamed_instances.target_name"),
6519 ])),
6520 },
6521 test_validate_offers_invalid_identifiers => {
6522 input = {
6523 let mut decl = new_component_decl();
6524 decl.offers = Some(vec![
6525 fdecl::Offer::Service(fdecl::OfferService {
6526 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6527 name: "^bad".to_string(),
6528 collection: None,
6529 })),
6530 source_name: Some("foo/".to_string()),
6531 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6532 name: "%bad".to_string(),
6533 collection: None,
6534 })),
6535 target_name: Some("/".to_string()),
6536 ..Default::default()
6537 }),
6538 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6539 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6540 name: "^bad".to_string(),
6541 collection: None,
6542 })),
6543 source_name: Some("foo/".to_string()),
6544 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6545 name: "%bad".to_string(),
6546 collection: None,
6547 })),
6548 target_name: Some("/".to_string()),
6549 dependency_type: Some(fdecl::DependencyType::Strong),
6550 ..Default::default()
6551 }),
6552 fdecl::Offer::Directory(fdecl::OfferDirectory {
6553 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6554 name: "^bad".to_string(),
6555 collection: None,
6556 })),
6557 source_name: Some("foo/".to_string()),
6558 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6559 name: "%bad".to_string(),
6560 collection: None,
6561 })),
6562 target_name: Some("/".to_string()),
6563 rights: Some(fio::Operations::CONNECT),
6564 subdir: Some("/foo".to_string()),
6565 dependency_type: Some(fdecl::DependencyType::Strong),
6566 ..Default::default()
6567 }),
6568 fdecl::Offer::Runner(fdecl::OfferRunner {
6569 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6570 name: "^bad".to_string(),
6571 collection: None,
6572 })),
6573 source_name: Some("/path".to_string()),
6574 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6575 name: "%bad".to_string(),
6576 collection: None,
6577 })),
6578 target_name: Some("elf!".to_string()),
6579 ..Default::default()
6580 }),
6581 fdecl::Offer::Resolver(fdecl::OfferResolver {
6582 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6583 name: "^bad".to_string(),
6584 collection: None,
6585 })),
6586 source_name: Some("/path".to_string()),
6587 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6588 name: "%bad".to_string(),
6589 collection: None,
6590 })),
6591 target_name: Some("pkg!".to_string()),
6592 ..Default::default()
6593 }),
6594 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6595 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6596 name: "^bad".to_string(),
6597 collection: None,
6598 })),
6599 source_name: Some("/path".to_string()),
6600 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6601 name: "%bad".to_string(),
6602 collection: None,
6603 })),
6604 target_name: Some("pkg!".to_string()),
6605 dependency_type: Some(fdecl::DependencyType::Strong),
6606 ..Default::default()
6607 }),
6608 ]);
6609 decl
6610 },
6611 result = Err(ErrorList::new(vec![
6612 Error::invalid_field(DeclType::OfferService, "source.child.name"),
6613 Error::invalid_field(DeclType::OfferService, "source_name"),
6614 Error::invalid_field(DeclType::OfferService, "target.child.name"),
6615 Error::invalid_field(DeclType::OfferService, "target_name"),
6616 Error::invalid_field(DeclType::OfferProtocol, "source.child.name"),
6617 Error::invalid_field(DeclType::OfferProtocol, "source_name"),
6618 Error::invalid_field(DeclType::OfferProtocol, "target.child.name"),
6619 Error::invalid_field(DeclType::OfferProtocol, "target_name"),
6620 Error::invalid_field(DeclType::OfferDirectory, "source.child.name"),
6621 Error::invalid_field(DeclType::OfferDirectory, "source_name"),
6622 Error::invalid_field(DeclType::OfferDirectory, "target.child.name"),
6623 Error::invalid_field(DeclType::OfferDirectory, "target_name"),
6624 Error::invalid_field(DeclType::OfferDirectory, "subdir"),
6625 Error::invalid_field(DeclType::OfferRunner, "source.child.name"),
6626 Error::invalid_field(DeclType::OfferRunner, "source_name"),
6627 Error::invalid_field(DeclType::OfferRunner, "target.child.name"),
6628 Error::invalid_field(DeclType::OfferRunner, "target_name"),
6629 Error::invalid_field(DeclType::OfferResolver, "source.child.name"),
6630 Error::invalid_field(DeclType::OfferResolver, "source_name"),
6631 Error::invalid_field(DeclType::OfferResolver, "target.child.name"),
6632 Error::invalid_field(DeclType::OfferResolver, "target_name"),
6633 Error::invalid_field(DeclType::OfferDictionary, "source.child.name"),
6634 Error::invalid_field(DeclType::OfferDictionary, "source_name"),
6635 Error::invalid_field(DeclType::OfferDictionary, "target.child.name"),
6636 Error::invalid_field(DeclType::OfferDictionary, "target_name"),
6637 ])),
6638 },
6639 test_validate_offers_target_equals_source => {
6640 input = {
6641 let mut decl = new_component_decl();
6642 decl.offers = Some(vec![
6643 fdecl::Offer::Service(fdecl::OfferService {
6644 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6645 name: "logger".to_string(),
6646 collection: None,
6647 })),
6648 source_name: Some("logger".to_string()),
6649 target: Some(fdecl::Ref::Child(
6650 fdecl::ChildRef {
6651 name: "logger".to_string(),
6652 collection: None,
6653 }
6654 )),
6655 target_name: Some("logger".to_string()),
6656 ..Default::default()
6657 }),
6658 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6659 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6660 name: "logger".to_string(),
6661 collection: None,
6662 })),
6663 source_name: Some("legacy_logger".to_string()),
6664 target: Some(fdecl::Ref::Child(
6665 fdecl::ChildRef {
6666 name: "logger".to_string(),
6667 collection: None,
6668 }
6669 )),
6670 target_name: Some("weak_legacy_logger".to_string()),
6671 dependency_type: Some(fdecl::DependencyType::Weak),
6672 ..Default::default()
6673 }),
6674 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6675 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6676 name: "logger".to_string(),
6677 collection: None,
6678 })),
6679 source_name: Some("legacy_logger".to_string()),
6680 target: Some(fdecl::Ref::Child(
6681 fdecl::ChildRef {
6682 name: "logger".to_string(),
6683 collection: None,
6684 }
6685 )),
6686 target_name: Some("strong_legacy_logger".to_string()),
6687 dependency_type: Some(fdecl::DependencyType::Strong),
6688 ..Default::default()
6689 }),
6690 fdecl::Offer::Directory(fdecl::OfferDirectory {
6691 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6692 name: "logger".to_string(),
6693 collection: None,
6694 })),
6695 source_name: Some("assets".to_string()),
6696 target: Some(fdecl::Ref::Child(
6697 fdecl::ChildRef {
6698 name: "logger".to_string(),
6699 collection: None,
6700 }
6701 )),
6702 target_name: Some("assets".to_string()),
6703 rights: Some(fio::Operations::CONNECT),
6704 subdir: None,
6705 dependency_type: Some(fdecl::DependencyType::Strong),
6706 ..Default::default()
6707 }),
6708 fdecl::Offer::Runner(fdecl::OfferRunner {
6709 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6710 name: "logger".to_string(),
6711 collection: None,
6712 })),
6713 source_name: Some("web".to_string()),
6714 target: Some(fdecl::Ref::Child(
6715 fdecl::ChildRef {
6716 name: "logger".to_string(),
6717 collection: None,
6718 }
6719 )),
6720 target_name: Some("web".to_string()),
6721 ..Default::default()
6722 }),
6723 fdecl::Offer::Resolver(fdecl::OfferResolver {
6724 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6725 name: "logger".to_string(),
6726 collection: None,
6727 })),
6728 source_name: Some("pkg".to_string()),
6729 target: Some(fdecl::Ref::Child(
6730 fdecl::ChildRef {
6731 name: "logger".to_string(),
6732 collection: None,
6733 }
6734 )),
6735 target_name: Some("pkg".to_string()),
6736 ..Default::default()
6737 }),
6738 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6739 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6740 name: "logger".to_string(),
6741 collection: None,
6742 })),
6743 source_name: Some("dict".to_string()),
6744 target: Some(fdecl::Ref::Child(
6745 fdecl::ChildRef {
6746 name: "logger".to_string(),
6747 collection: None,
6748 }
6749 )),
6750 target_name: Some("dict".to_string()),
6751 dependency_type: Some(fdecl::DependencyType::Strong),
6752 ..Default::default()
6753 }),
6754 ]);
6755 decl.children = Some(vec![fdecl::Child{
6756 name: Some("logger".to_string()),
6757 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
6758 startup: Some(fdecl::StartupMode::Lazy),
6759 on_terminate: None,
6760 environment: None,
6761 ..Default::default()
6762 }]);
6763 decl
6764 },
6765 result = Err(ErrorList::new(vec![
6766 Error::dependency_cycle("{{child logger -> child logger}}".to_string()),
6767 ])),
6768 },
6769 test_validate_offers_storage_target_equals_source => {
6770 input = fdecl::Component {
6771 offers: Some(vec![
6772 fdecl::Offer::Storage(fdecl::OfferStorage {
6773 source_name: Some("data".to_string()),
6774 source: Some(fdecl::Ref::Self_(fdecl::SelfRef { })),
6775 target: Some(fdecl::Ref::Child(
6776 fdecl::ChildRef {
6777 name: "logger".to_string(),
6778 collection: None,
6779 }
6780 )),
6781 target_name: Some("data".to_string()),
6782 ..Default::default()
6783 })
6784 ]),
6785 capabilities: Some(vec![
6786 fdecl::Capability::Storage(fdecl::Storage {
6787 name: Some("data".to_string()),
6788 backing_dir: Some("minfs".to_string()),
6789 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6790 name: "logger".to_string(),
6791 collection: None,
6792 })),
6793 subdir: None,
6794 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
6795 ..Default::default()
6796 }),
6797 ]),
6798 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 ..new_component_decl()
6809 },
6810 result = Err(ErrorList::new(vec![
6811 Error::dependency_cycle("{{child logger -> capability data -> child logger}}".to_string()),
6812 ])),
6813 },
6814 test_validate_offers_invalid_child => {
6815 input = {
6816 let mut decl = new_component_decl();
6817 decl.offers = Some(vec![
6818 fdecl::Offer::Service(fdecl::OfferService {
6819 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6820 name: "logger".to_string(),
6821 collection: None,
6822 })),
6823 source_name: Some("fuchsia.logger.Log".to_string()),
6824 target: Some(fdecl::Ref::Child(
6825 fdecl::ChildRef {
6826 name: "netstack".to_string(),
6827 collection: None,
6828 }
6829 )),
6830 target_name: Some("fuchsia.logger.Log".to_string()),
6831 ..Default::default()
6832 }),
6833 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6834 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6835 name: "logger".to_string(),
6836 collection: None,
6837 })),
6838 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
6839 target: Some(fdecl::Ref::Child(
6840 fdecl::ChildRef {
6841 name: "netstack".to_string(),
6842 collection: None,
6843 }
6844 )),
6845 target_name: Some("fuchsia.logger.LegacyLog".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: "logger".to_string(),
6852 collection: None,
6853 })),
6854 source_name: Some("assets".to_string()),
6855 target: Some(fdecl::Ref::Collection(
6856 fdecl::CollectionRef { name: "modular".to_string() }
6857 )),
6858 target_name: Some("assets".to_string()),
6859 rights: Some(fio::Operations::CONNECT),
6860 subdir: None,
6861 dependency_type: Some(fdecl::DependencyType::Weak),
6862 ..Default::default()
6863 }),
6864 ]);
6865 decl.capabilities = Some(vec![
6866 fdecl::Capability::Storage(fdecl::Storage {
6867 name: Some("memfs".to_string()),
6868 backing_dir: Some("memfs".to_string()),
6869 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6870 name: "logger".to_string(),
6871 collection: None,
6872 })),
6873 subdir: None,
6874 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
6875 ..Default::default()
6876 }),
6877 ]);
6878 decl.children = Some(vec![
6879 fdecl::Child {
6880 name: Some("netstack".to_string()),
6881 url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
6882 startup: Some(fdecl::StartupMode::Lazy),
6883 on_terminate: None,
6884 environment: None,
6885 ..Default::default()
6886 },
6887 ]);
6888 decl.collections = Some(vec![
6889 fdecl::Collection {
6890 name: Some("modular".to_string()),
6891 durability: Some(fdecl::Durability::Transient),
6892 environment: None,
6893 allowed_offers: Some(fdecl::AllowedOffers::StaticAndDynamic),
6894 allow_long_names: None,
6895 ..Default::default()
6896 },
6897 ]);
6898 decl
6899 },
6900 result = Err(ErrorList::new(vec![
6901 Error::invalid_child(DeclType::Storage, "source", "logger"),
6902 Error::invalid_child(DeclType::OfferService, "source", "logger"),
6903 Error::invalid_child(DeclType::OfferProtocol, "source", "logger"),
6904 Error::invalid_child(DeclType::OfferDirectory, "source", "logger"),
6905 ])),
6906 },
6907 test_validate_offers_invalid_source_capability => {
6908 input = {
6909 fdecl::Component {
6910 offers: Some(vec![
6911 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6912 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
6913 name: "this-storage-doesnt-exist".to_string(),
6914 })),
6915 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
6916 target: Some(fdecl::Ref::Child(
6917 fdecl::ChildRef {
6918 name: "netstack".to_string(),
6919 collection: None,
6920 }
6921 )),
6922 target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
6923 dependency_type: Some(fdecl::DependencyType::Strong),
6924 ..Default::default()
6925 }),
6926 ]),
6927 ..new_component_decl()
6928 }
6929 },
6930 result = Err(ErrorList::new(vec![
6931 Error::invalid_capability(DeclType::OfferProtocol, "source", "this-storage-doesnt-exist"),
6932 Error::invalid_child(DeclType::OfferProtocol, "target", "netstack"),
6933 ])),
6934 },
6935 test_validate_offers_target => {
6936 input = {
6937 let mut decl = new_component_decl();
6938 decl.offers = Some(vec![
6939 fdecl::Offer::Service(fdecl::OfferService {
6940 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6941 name: "modular".into()
6942 })),
6943 source_name: Some("logger".to_string()),
6944 target: Some(fdecl::Ref::Child(
6945 fdecl::ChildRef {
6946 name: "netstack".to_string(),
6947 collection: None,
6948 }
6949 )),
6950 target_name: Some("fuchsia.logger.Log".to_string()),
6951 ..Default::default()
6952 }),
6953 fdecl::Offer::Service(fdecl::OfferService {
6954 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6955 name: "modular".into()
6956 })),
6957 source_name: Some("logger".to_string()),
6958 target: Some(fdecl::Ref::Child(
6959 fdecl::ChildRef {
6960 name: "netstack".to_string(),
6961 collection: None,
6962 }
6963 )),
6964 target_name: Some("fuchsia.logger.Log".to_string()),
6965 ..Default::default()
6966 }),
6967 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6968 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6969 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
6970 target: Some(fdecl::Ref::Child(
6971 fdecl::ChildRef {
6972 name: "netstack".to_string(),
6973 collection: None,
6974 }
6975 )),
6976 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
6977 dependency_type: Some(fdecl::DependencyType::Strong),
6978 ..Default::default()
6979 }),
6980 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6981 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6982 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
6983 target: Some(fdecl::Ref::Child(
6984 fdecl::ChildRef {
6985 name: "netstack".to_string(),
6986 collection: None,
6987 }
6988 )),
6989 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
6990 dependency_type: Some(fdecl::DependencyType::Strong),
6991 ..Default::default()
6992 }),
6993 fdecl::Offer::Directory(fdecl::OfferDirectory {
6994 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6995 source_name: Some("assets".to_string()),
6996 target: Some(fdecl::Ref::Collection(
6997 fdecl::CollectionRef { name: "modular".to_string() }
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::Directory(fdecl::OfferDirectory {
7006 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7007 source_name: Some("assets".to_string()),
7008 target: Some(fdecl::Ref::Collection(
7009 fdecl::CollectionRef { name: "modular".to_string() }
7010 )),
7011 target_name: Some("assets".to_string()),
7012 rights: Some(fio::Operations::CONNECT),
7013 subdir: None,
7014 dependency_type: Some(fdecl::DependencyType::Weak),
7015 ..Default::default()
7016 }),
7017 fdecl::Offer::Storage(fdecl::OfferStorage {
7018 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7019 source_name: Some("data".to_string()),
7020 target: Some(fdecl::Ref::Collection(
7021 fdecl::CollectionRef { name: "modular".to_string() }
7022 )),
7023 target_name: Some("data".to_string()),
7024 ..Default::default()
7025 }),
7026 fdecl::Offer::Storage(fdecl::OfferStorage {
7027 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7028 source_name: Some("data".to_string()),
7029 target: Some(fdecl::Ref::Collection(
7030 fdecl::CollectionRef { name: "modular".to_string() }
7031 )),
7032 target_name: Some("data".to_string()),
7033 ..Default::default()
7034 }),
7035 fdecl::Offer::Runner(fdecl::OfferRunner {
7036 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7037 source_name: Some("elf".to_string()),
7038 target: Some(fdecl::Ref::Collection(
7039 fdecl::CollectionRef { name: "modular".to_string() }
7040 )),
7041 target_name: Some("duplicated".to_string()),
7042 ..Default::default()
7043 }),
7044 fdecl::Offer::Runner(fdecl::OfferRunner {
7045 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7046 source_name: Some("elf".to_string()),
7047 target: Some(fdecl::Ref::Collection(
7048 fdecl::CollectionRef { name: "modular".to_string() }
7049 )),
7050 target_name: Some("duplicated".to_string()),
7051 ..Default::default()
7052 }),
7053 fdecl::Offer::Resolver(fdecl::OfferResolver {
7054 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7055 source_name: Some("pkg".to_string()),
7056 target: Some(fdecl::Ref::Collection(
7057 fdecl::CollectionRef { name: "modular".to_string() }
7058 )),
7059 target_name: Some("duplicated".to_string()),
7060 ..Default::default()
7061 }),
7062 fdecl::Offer::EventStream(fdecl::OfferEventStream {
7063 source_name: Some("started".to_string()),
7064 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7065 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "netstack".to_string(), collection: None})),
7066 target_name: Some("started".to_string()),
7067 ..Default::default()
7068 }),
7069 fdecl::Offer::EventStream(fdecl::OfferEventStream {
7070 source_name: Some("started".to_string()),
7071 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7072 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "netstack".to_string(), collection: None})),
7073 target_name: Some("started".to_string()),
7074 ..Default::default()
7075 }),
7076 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7077 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7078 source_name: Some("a".to_string()),
7079 target: Some(fdecl::Ref::Collection(
7080 fdecl::CollectionRef { name: "modular".to_string() }
7081 )),
7082 target_name: Some("dict".to_string()),
7083 dependency_type: Some(fdecl::DependencyType::Strong),
7084 ..Default::default()
7085 }),
7086 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7087 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7088 source_name: Some("b".to_string()),
7089 target: Some(fdecl::Ref::Collection(
7090 fdecl::CollectionRef { name: "modular".to_string() }
7091 )),
7092 target_name: Some("dict".to_string()),
7093 dependency_type: Some(fdecl::DependencyType::Strong),
7094 ..Default::default()
7095 }),
7096 ]);
7097 decl.children = Some(vec![
7098 fdecl::Child{
7099 name: Some("netstack".to_string()),
7100 url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
7101 startup: Some(fdecl::StartupMode::Eager),
7102 on_terminate: None,
7103 environment: None,
7104 ..Default::default()
7105 },
7106 ]);
7107 decl.collections = Some(vec![
7108 fdecl::Collection{
7109 name: Some("modular".to_string()),
7110 durability: Some(fdecl::Durability::Transient),
7111 environment: None,
7112 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7113 allow_long_names: None,
7114 ..Default::default()
7115 },
7116 ]);
7117 decl
7118 },
7119 result = Err(ErrorList::new(vec![
7120 Error::duplicate_field(DeclType::OfferProtocol, "target_name", "fuchsia.logger.LegacyLog"),
7122 Error::duplicate_field(DeclType::OfferDirectory, "target_name", "assets"),
7123 Error::duplicate_field(DeclType::OfferStorage, "target_name", "data"),
7124 Error::duplicate_field(DeclType::OfferRunner, "target_name", "duplicated"),
7125 Error::duplicate_field(DeclType::OfferResolver, "target_name", "duplicated"),
7126 Error::duplicate_field(DeclType::OfferEventStream, "target_name", "started"),
7127 Error::duplicate_field(DeclType::OfferDictionary, "target_name", "dict"),
7128 ])),
7129 },
7130 test_validate_offers_target_invalid => {
7131 input = {
7132 let mut decl = new_component_decl();
7133 decl.offers = Some(vec![
7134 fdecl::Offer::Service(fdecl::OfferService {
7135 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7136 source_name: Some("logger".to_string()),
7137 target: Some(fdecl::Ref::Child(
7138 fdecl::ChildRef {
7139 name: "netstack".to_string(),
7140 collection: None,
7141 }
7142 )),
7143 target_name: Some("fuchsia.logger.Log".to_string()),
7144 ..Default::default()
7145 }),
7146 fdecl::Offer::Service(fdecl::OfferService {
7147 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7148 source_name: Some("logger".to_string()),
7149 target: Some(fdecl::Ref::Collection(
7150 fdecl::CollectionRef { name: "modular".to_string(), }
7151 )),
7152 target_name: Some("fuchsia.logger.Log".to_string()),
7153 ..Default::default()
7154 }),
7155 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7156 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7157 source_name: Some("legacy_logger".to_string()),
7158 target: Some(fdecl::Ref::Child(
7159 fdecl::ChildRef {
7160 name: "netstack".to_string(),
7161 collection: None,
7162 }
7163 )),
7164 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7165 dependency_type: Some(fdecl::DependencyType::Weak),
7166 ..Default::default()
7167 }),
7168 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7169 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7170 source_name: Some("legacy_logger".to_string()),
7171 target: Some(fdecl::Ref::Collection(
7172 fdecl::CollectionRef { name: "modular".to_string(), }
7173 )),
7174 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7175 dependency_type: Some(fdecl::DependencyType::Strong),
7176 ..Default::default()
7177 }),
7178 fdecl::Offer::Directory(fdecl::OfferDirectory {
7179 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7180 source_name: Some("assets".to_string()),
7181 target: Some(fdecl::Ref::Child(
7182 fdecl::ChildRef {
7183 name: "netstack".to_string(),
7184 collection: None,
7185 }
7186 )),
7187 target_name: Some("data".to_string()),
7188 rights: Some(fio::Operations::CONNECT),
7189 subdir: None,
7190 dependency_type: Some(fdecl::DependencyType::Strong),
7191 ..Default::default()
7192 }),
7193 fdecl::Offer::Directory(fdecl::OfferDirectory {
7194 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7195 source_name: Some("assets".to_string()),
7196 target: Some(fdecl::Ref::Collection(
7197 fdecl::CollectionRef { name: "modular".to_string(), }
7198 )),
7199 target_name: Some("data".to_string()),
7200 rights: Some(fio::Operations::CONNECT),
7201 subdir: None,
7202 dependency_type: Some(fdecl::DependencyType::Weak),
7203 ..Default::default()
7204 }),
7205 fdecl::Offer::Storage(fdecl::OfferStorage {
7206 source_name: Some("data".to_string()),
7207 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7208 target: Some(fdecl::Ref::Child(
7209 fdecl::ChildRef {
7210 name: "netstack".to_string(),
7211 collection: None,
7212 }
7213 )),
7214 target_name: Some("data".to_string()),
7215 ..Default::default()
7216 }),
7217 fdecl::Offer::Storage(fdecl::OfferStorage {
7218 source_name: Some("data".to_string()),
7219 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7220 target: Some(fdecl::Ref::Collection(
7221 fdecl::CollectionRef { name: "modular".to_string(), }
7222 )),
7223 target_name: Some("data".to_string()),
7224 ..Default::default()
7225 }),
7226 fdecl::Offer::Runner(fdecl::OfferRunner {
7227 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7228 source_name: Some("elf".to_string()),
7229 target: Some(fdecl::Ref::Child(
7230 fdecl::ChildRef {
7231 name: "netstack".to_string(),
7232 collection: None,
7233 }
7234 )),
7235 target_name: Some("elf".to_string()),
7236 ..Default::default()
7237 }),
7238 fdecl::Offer::Runner(fdecl::OfferRunner {
7239 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7240 source_name: Some("elf".to_string()),
7241 target: Some(fdecl::Ref::Collection(
7242 fdecl::CollectionRef { name: "modular".to_string(), }
7243 )),
7244 target_name: Some("elf".to_string()),
7245 ..Default::default()
7246 }),
7247 fdecl::Offer::Resolver(fdecl::OfferResolver {
7248 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7249 source_name: Some("pkg".to_string()),
7250 target: Some(fdecl::Ref::Child(
7251 fdecl::ChildRef {
7252 name: "netstack".to_string(),
7253 collection: None,
7254 }
7255 )),
7256 target_name: Some("pkg".to_string()),
7257 ..Default::default()
7258 }),
7259 fdecl::Offer::Resolver(fdecl::OfferResolver {
7260 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7261 source_name: Some("pkg".to_string()),
7262 target: Some(fdecl::Ref::Collection(
7263 fdecl::CollectionRef { name: "modular".to_string(), }
7264 )),
7265 target_name: Some("pkg".to_string()),
7266 ..Default::default()
7267 }),
7268 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7269 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7270 source_name: Some("pkg".to_string()),
7271 target: Some(fdecl::Ref::Child(
7272 fdecl::ChildRef {
7273 name: "netstack".to_string(),
7274 collection: None,
7275 }
7276 )),
7277 target_name: Some("pkg".to_string()),
7278 dependency_type: Some(fdecl::DependencyType::Strong),
7279 ..Default::default()
7280 }),
7281 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7282 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7283 source_name: Some("pkg".to_string()),
7284 target: Some(fdecl::Ref::Collection(
7285 fdecl::CollectionRef { name: "modular".to_string(), }
7286 )),
7287 target_name: Some("pkg".to_string()),
7288 dependency_type: Some(fdecl::DependencyType::Strong),
7289 ..Default::default()
7290 }),
7291 ]);
7292 decl
7293 },
7294 result = Err(ErrorList::new(vec![
7295 Error::invalid_child(DeclType::OfferService, "target", "netstack"),
7296 Error::invalid_collection(DeclType::OfferService, "target", "modular"),
7297 Error::invalid_child(DeclType::OfferProtocol, "target", "netstack"),
7298 Error::invalid_collection(DeclType::OfferProtocol, "target", "modular"),
7299 Error::invalid_child(DeclType::OfferDirectory, "target", "netstack"),
7300 Error::invalid_collection(DeclType::OfferDirectory, "target", "modular"),
7301 Error::invalid_child(DeclType::OfferStorage, "target", "netstack"),
7302 Error::invalid_collection(DeclType::OfferStorage, "target", "modular"),
7303 Error::invalid_child(DeclType::OfferRunner, "target", "netstack"),
7304 Error::invalid_collection(DeclType::OfferRunner, "target", "modular"),
7305 Error::invalid_child(DeclType::OfferResolver, "target", "netstack"),
7306 Error::invalid_collection(DeclType::OfferResolver, "target", "modular"),
7307 Error::invalid_child(DeclType::OfferDictionary, "target", "netstack"),
7308 Error::invalid_collection(DeclType::OfferDictionary, "target", "modular"),
7309 ])),
7310 },
7311 test_validate_offers_target_dictionary => {
7312 input = fdecl::Component {
7313 offers: Some(vec![
7314 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7316 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7317 source_name: Some("p".to_string()),
7318 target: Some(fdecl::Ref::Capability(
7319 fdecl::CapabilityRef {
7320 name: "dict".into(),
7321 },
7322 )),
7323 target_name: Some("p".into()),
7324 dependency_type: Some(fdecl::DependencyType::Strong),
7325 ..Default::default()
7326 }),
7327 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7329 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7330 source_name: Some("p".to_string()),
7331 target: Some(fdecl::Ref::Capability(
7332 fdecl::CapabilityRef {
7333 name: "dynamic".into(),
7334 },
7335 )),
7336 target_name: Some("p".into()),
7337 dependency_type: Some(fdecl::DependencyType::Strong),
7338 ..Default::default()
7339 }),
7340 ]),
7341 capabilities: Some(vec![
7342 fdecl::Capability::Dictionary(fdecl::Dictionary {
7343 name: Some("dict".into()),
7344 ..Default::default()
7345 }),
7346 fdecl::Capability::Dictionary(fdecl::Dictionary {
7347 name: Some("dynamic".into()),
7348 source_path: Some("/out/dir".into()),
7349 ..Default::default()
7350 }),
7351 ]),
7352 ..Default::default()
7353 },
7354 result = Err(ErrorList::new(vec![
7355 Error::invalid_field(DeclType::OfferProtocol, "target"),
7356 ])),
7357 },
7358 test_validate_offers_invalid_source_collection => {
7359 input = {
7360 let mut decl = new_component_decl();
7361 decl.collections = Some(vec![
7362 fdecl::Collection {
7363 name: Some("col".to_string()),
7364 durability: Some(fdecl::Durability::Transient),
7365 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7366 allow_long_names: None,
7367 ..Default::default()
7368 }
7369 ]);
7370 decl.children = Some(vec![
7371 fdecl::Child {
7372 name: Some("child".to_string()),
7373 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7374 startup: Some(fdecl::StartupMode::Lazy),
7375 on_terminate: None,
7376 ..Default::default()
7377 }
7378 ]);
7379 decl.offers = Some(vec![
7380 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7381 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7382 source_name: Some("a".to_string()),
7383 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7384 target_name: Some("a".to_string()),
7385 dependency_type: Some(fdecl::DependencyType::Strong),
7386 ..Default::default()
7387 }),
7388 fdecl::Offer::Directory(fdecl::OfferDirectory {
7389 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7390 source_name: Some("b".to_string()),
7391 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7392 target_name: Some("b".to_string()),
7393 rights: Some(fio::Operations::CONNECT),
7394 subdir: None,
7395 dependency_type: Some(fdecl::DependencyType::Strong),
7396 ..Default::default()
7397 }),
7398 fdecl::Offer::Storage(fdecl::OfferStorage {
7399 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7400 source_name: Some("c".to_string()),
7401 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7402 target_name: Some("c".to_string()),
7403 ..Default::default()
7404 }),
7405 fdecl::Offer::Runner(fdecl::OfferRunner {
7406 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7407 source_name: Some("d".to_string()),
7408 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7409 target_name: Some("d".to_string()),
7410 ..Default::default()
7411 }),
7412 fdecl::Offer::Resolver(fdecl::OfferResolver {
7413 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7414 source_name: Some("e".to_string()),
7415 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7416 target_name: Some("e".to_string()),
7417 ..Default::default()
7418 }),
7419 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7420 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7421 source_name: Some("f".to_string()),
7422 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7423 target_name: Some("f".to_string()),
7424 dependency_type: Some(fdecl::DependencyType::Strong),
7425 ..Default::default()
7426 }),
7427 ]);
7428 decl
7429 },
7430 result = Err(ErrorList::new(vec![
7431 Error::invalid_field(DeclType::OfferProtocol, "source"),
7432 Error::invalid_field(DeclType::OfferDirectory, "source"),
7433 Error::invalid_field(DeclType::OfferStorage, "source"),
7434 Error::invalid_field(DeclType::OfferRunner, "source"),
7435 Error::invalid_field(DeclType::OfferResolver, "source"),
7436 Error::invalid_field(DeclType::OfferDictionary, "source"),
7437 ])),
7438 },
7439 test_validate_offers_source_collection => {
7440 input = {
7441 let mut decl = new_component_decl();
7442 decl.collections = Some(vec![
7443 fdecl::Collection {
7444 name: Some("col".to_string()),
7445 durability: Some(fdecl::Durability::Transient),
7446 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7447 allow_long_names: None,
7448 ..Default::default()
7449 }
7450 ]);
7451 decl.children = Some(vec![
7452 fdecl::Child {
7453 name: Some("child".to_string()),
7454 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7455 startup: Some(fdecl::StartupMode::Lazy),
7456 on_terminate: None,
7457 ..Default::default()
7458 }
7459 ]);
7460 decl.offers = Some(vec![
7461 fdecl::Offer::Service(fdecl::OfferService {
7462 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7463 source_name: Some("a".to_string()),
7464 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7465 target_name: Some("a".to_string()),
7466 ..Default::default()
7467 })
7468 ]);
7469 decl
7470 },
7471 result = Ok(()),
7472 },
7473 test_validate_offers_invalid_capability_from_self => {
7474 input = {
7475 let mut decl = new_component_decl();
7476 decl.children = Some(vec![
7477 fdecl::Child {
7478 name: Some("child".to_string()),
7479 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7480 startup: Some(fdecl::StartupMode::Lazy),
7481 ..Default::default()
7482 }
7483 ]);
7484 decl.offers = Some(vec![
7485 fdecl::Offer::Service(fdecl::OfferService {
7486 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7487 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7488 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7489 name: "child".into(),
7490 collection: None
7491 })),
7492 target_name: Some("foo".into()),
7493 ..Default::default()
7494 }),
7495 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7496 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7497 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7498 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7499 name: "child".into(),
7500 collection: None
7501 })),
7502 target_name: Some("bar".into()),
7503 dependency_type: Some(fdecl::DependencyType::Strong),
7504 ..Default::default()
7505 }),
7506 fdecl::Offer::Directory(fdecl::OfferDirectory {
7507 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7508 source_name: Some("dir".into()),
7509 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7510 name: "child".into(),
7511 collection: None
7512 })),
7513 target_name: Some("assets".into()),
7514 dependency_type: Some(fdecl::DependencyType::Strong),
7515 ..Default::default()
7516 }),
7517 fdecl::Offer::Runner(fdecl::OfferRunner {
7518 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7519 source_name: Some("source_elf".into()),
7520 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7521 name: "child".into(),
7522 collection: None
7523 })),
7524 target_name: Some("elf".into()),
7525 ..Default::default()
7526 }),
7527 fdecl::Offer::Resolver(fdecl::OfferResolver {
7528 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7529 source_name: Some("source_pkg".into()),
7530 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7531 name: "child".into(),
7532 collection: None
7533 })),
7534 target_name: Some("pkg".into()),
7535 ..Default::default()
7536 }),
7537 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7538 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7539 source_name: Some("source_dict".into()),
7540 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7541 name: "child".into(),
7542 collection: None
7543 })),
7544 target_name: Some("dict".into()),
7545 dependency_type: Some(fdecl::DependencyType::Strong),
7546 ..Default::default()
7547 }),
7548 fdecl::Offer::Storage(fdecl::OfferStorage {
7549 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7550 source_name: Some("source_storage".into()),
7551 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7552 name: "child".into(),
7553 collection: None
7554 })),
7555 target_name: Some("storage".into()),
7556 ..Default::default()
7557 }),
7558 fdecl::Offer::Config(fdecl::OfferConfiguration {
7559 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7560 source_name: Some("source_config".into()),
7561 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7562 name: "child".into(),
7563 collection: None
7564 })),
7565 target_name: Some("config".into()),
7566 ..Default::default()
7567 }),
7568 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7569 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7570 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7571 source_dictionary: Some("dict/inner".into()),
7572 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7573 name: "child".into(),
7574 collection: None
7575 })),
7576 target_name: Some("baz".into()),
7577 dependency_type: Some(fdecl::DependencyType::Strong),
7578 ..Default::default()
7579 }),
7580 ]);
7581 decl
7582 },
7583 result = Err(ErrorList::new(vec![
7584 Error::invalid_capability(
7585 DeclType::OfferService,
7586 "source",
7587 "fuchsia.some.library.SomeProtocol"),
7588 Error::invalid_capability(
7589 DeclType::OfferProtocol,
7590 "source",
7591 "fuchsia.some.library.SomeProtocol"),
7592 Error::invalid_capability(DeclType::OfferDirectory, "source", "dir"),
7593 Error::invalid_capability(DeclType::OfferRunner, "source", "source_elf"),
7594 Error::invalid_capability(DeclType::OfferResolver, "source", "source_pkg"),
7595 Error::invalid_capability(DeclType::OfferDictionary, "source", "source_dict"),
7596 Error::invalid_capability(DeclType::OfferStorage, "source", "source_storage"),
7597 Error::invalid_capability(DeclType::OfferConfig, "source", "source_config"),
7598 Error::invalid_capability(DeclType::OfferProtocol, "source", "dict"),
7599 ])),
7600 },
7601 test_validate_offers_long_dependency_cycle => {
7602 input = {
7603 let mut decl = new_component_decl();
7604 let dependencies = vec![
7605 ("d", "b"),
7606 ("a", "b"),
7607 ("b", "c"),
7608 ("b", "d"),
7609 ("c", "a"),
7610 ];
7611 let offers = dependencies.into_iter().map(|(from,to)|
7612 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7613 source: Some(fdecl::Ref::Child(
7614 fdecl::ChildRef { name: from.to_string(), collection: None },
7615 )),
7616 source_name: Some(format!("thing_{}", from)),
7617 target: Some(fdecl::Ref::Child(
7618 fdecl::ChildRef { name: to.to_string(), collection: None },
7619 )),
7620 target_name: Some(format!("thing_{}", from)),
7621 dependency_type: Some(fdecl::DependencyType::Strong),
7622 ..Default::default()
7623 })).collect();
7624 let children = ["a", "b", "c", "d"].iter().map(|name| {
7625 fdecl::Child {
7626 name: Some(name.to_string()),
7627 url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
7628 startup: Some(fdecl::StartupMode::Lazy),
7629 on_terminate: None,
7630 environment: None,
7631 ..Default::default()
7632 }
7633 }).collect();
7634 decl.offers = Some(offers);
7635 decl.children = Some(children);
7636 decl
7637 },
7638 result = Err(ErrorList::new(vec![
7639 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()),
7640 ])),
7641 },
7642 test_validate_offers_not_required_invalid_source_service => {
7643 input = {
7644 let mut decl = generate_offer_different_source_and_availability_decl(
7645 |source, availability, target_name|
7646 fdecl::Offer::Service(fdecl::OfferService {
7647 source: Some(source),
7648 source_name: Some("fuchsia.examples.Echo".to_string()),
7649 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7650 name: "sink".to_string(),
7651 collection: None,
7652 })),
7653 target_name: Some(target_name.into()),
7654 availability: Some(availability),
7655 ..Default::default()
7656 })
7657 );
7658 decl.capabilities = Some(vec![
7659 fdecl::Capability::Service(fdecl::Service {
7660 name: Some("fuchsia.examples.Echo".to_string()),
7661 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
7662 ..Default::default()
7663 }),
7664 ]);
7665 decl
7666 },
7667 result = {
7668 Err(ErrorList::new(vec![
7669 Error::availability_must_be_optional(
7670 DeclType::OfferService,
7671 "availability",
7672 Some(&"fuchsia.examples.Echo".to_string()),
7673 ),
7674 Error::availability_must_be_optional(
7675 DeclType::OfferService,
7676 "availability",
7677 Some(&"fuchsia.examples.Echo".to_string()),
7678 ),
7679 ]))
7680 },
7681 },
7682 test_validate_offers_not_required_invalid_source_protocol => {
7683 input = {
7684 let mut decl = generate_offer_different_source_and_availability_decl(
7685 |source, availability, target_name|
7686 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7687 source: Some(source),
7688 source_name: Some("fuchsia.examples.Echo".to_string()),
7689 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7690 name: "sink".to_string(),
7691 collection: None,
7692 })),
7693 target_name: Some(target_name.into()),
7694 dependency_type: Some(fdecl::DependencyType::Strong),
7695 availability: Some(availability),
7696 ..Default::default()
7697 })
7698 );
7699 decl.capabilities = Some(vec![
7700 fdecl::Capability::Protocol(fdecl::Protocol {
7701 name: Some("fuchsia.examples.Echo".to_string()),
7702 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
7703 ..Default::default()
7704 }),
7705 ]);
7706 decl
7707 },
7708 result = {
7709 Err(ErrorList::new(vec![
7710 Error::availability_must_be_optional(
7711 DeclType::OfferProtocol,
7712 "availability",
7713 Some(&"fuchsia.examples.Echo".to_string()),
7714 ),
7715 Error::availability_must_be_optional(
7716 DeclType::OfferProtocol,
7717 "availability",
7718 Some(&"fuchsia.examples.Echo".to_string()),
7719 ),
7720 ]))
7721 },
7722 },
7723 test_validate_offers_not_required_invalid_source_directory => {
7724 input = {
7725 let mut decl = generate_offer_different_source_and_availability_decl(
7726 |source, availability, target_name|
7727 fdecl::Offer::Directory(fdecl::OfferDirectory {
7728 source: Some(source),
7729 source_name: Some("assets".to_string()),
7730 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7731 name: "sink".to_string(),
7732 collection: None,
7733 })),
7734 target_name: Some(target_name.into()),
7735 rights: Some(fio::Operations::CONNECT),
7736 subdir: None,
7737 dependency_type: Some(fdecl::DependencyType::Weak),
7738 availability: Some(availability),
7739 ..Default::default()
7740 })
7741 );
7742 decl.capabilities = Some(vec![
7743 fdecl::Capability::Directory(fdecl::Directory {
7744 name: Some("assets".to_string()),
7745 source_path: Some("/assets".to_string()),
7746 rights: Some(fio::Operations::CONNECT),
7747 ..Default::default()
7748 }),
7749 ]);
7750 decl
7751 },
7752 result = {
7753 Err(ErrorList::new(vec![
7754 Error::availability_must_be_optional(
7755 DeclType::OfferDirectory,
7756 "availability",
7757 Some(&"assets".to_string()),
7758 ),
7759 Error::availability_must_be_optional(
7760 DeclType::OfferDirectory,
7761 "availability",
7762 Some(&"assets".to_string()),
7763 ),
7764 ]))
7765 },
7766 },
7767 test_validate_offers_not_required_invalid_source_storage => {
7768 input = {
7769 let mut decl = new_component_decl();
7770 decl.children = Some(vec![
7771 fdecl::Child {
7772 name: Some("sink".to_string()),
7773 url: Some("fuchsia-pkg://fuchsia.com/sink#meta/sink.cm".to_string()),
7774 startup: Some(fdecl::StartupMode::Lazy),
7775 on_terminate: None,
7776 environment: None,
7777 ..Default::default()
7778 },
7779 ]);
7780 decl.capabilities = Some(vec![
7781 fdecl::Capability::Storage(fdecl::Storage {
7782 name: Some("data".to_string()),
7783 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
7784 backing_dir: Some("minfs".to_string()),
7785 subdir: None,
7786 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
7787 ..Default::default()
7788 }),
7789 ]);
7790 let new_offer = |source: fdecl::Ref, availability: fdecl::Availability,
7791 target_name: &str|
7792 {
7793 fdecl::Offer::Storage(fdecl::OfferStorage {
7794 source: Some(source),
7795 source_name: Some("data".to_string()),
7796 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7797 name: "sink".to_string(),
7798 collection: None,
7799 })),
7800 target_name: Some(target_name.into()),
7801 availability: Some(availability),
7802 ..Default::default()
7803 })
7804 };
7805 decl.offers = Some(vec![
7806 new_offer(
7809 fdecl::Ref::Parent(fdecl::ParentRef {}),
7810 fdecl::Availability::Required,
7811 "data0",
7812 ),
7813 new_offer(
7814 fdecl::Ref::Parent(fdecl::ParentRef {}),
7815 fdecl::Availability::Optional,
7816 "data1",
7817 ),
7818 new_offer(
7819 fdecl::Ref::Parent(fdecl::ParentRef {}),
7820 fdecl::Availability::SameAsTarget,
7821 "data2",
7822 ),
7823 new_offer(
7824 fdecl::Ref::VoidType(fdecl::VoidRef {}),
7825 fdecl::Availability::Optional,
7826 "data3",
7827 ),
7828 new_offer(
7831 fdecl::Ref::Self_(fdecl::SelfRef {}),
7832 fdecl::Availability::Optional,
7833 "data4",
7834 ),
7835 new_offer(
7836 fdecl::Ref::Self_(fdecl::SelfRef {}),
7837 fdecl::Availability::SameAsTarget,
7838 "data5",
7839 ),
7840 new_offer(
7842 fdecl::Ref::VoidType(fdecl::VoidRef {}),
7843 fdecl::Availability::Required,
7844 "data6",
7845 ),
7846 new_offer(
7847 fdecl::Ref::VoidType(fdecl::VoidRef {}),
7848 fdecl::Availability::SameAsTarget,
7849 "data7",
7850 ),
7851 ]);
7852 decl
7853 },
7854 result = {
7855 Err(ErrorList::new(vec![
7856 Error::availability_must_be_optional(
7857 DeclType::OfferStorage,
7858 "availability",
7859 Some(&"data".to_string()),
7860 ),
7861 Error::availability_must_be_optional(
7862 DeclType::OfferStorage,
7863 "availability",
7864 Some(&"data".to_string()),
7865 ),
7866 ]))
7867 },
7868 },
7869
7870 test_validate_offers_valid_service_aggregation => {
7871 input = {
7872 let mut decl = new_component_decl();
7873 decl.offers = Some(vec![
7874 fdecl::Offer::Service(fdecl::OfferService {
7875 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
7876 name: "coll_a".to_string()
7877 })),
7878 source_name: Some("fuchsia.logger.Log".to_string()),
7879 target: Some(fdecl::Ref::Child(
7880 fdecl::ChildRef {
7881 name: "child_c".to_string(),
7882 collection: None,
7883 }
7884 )),
7885 target_name: Some("fuchsia.logger.Log".to_string()),
7886 source_instance_filter: None,
7887 ..Default::default()
7888 }),
7889 fdecl::Offer::Service(fdecl::OfferService {
7890 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
7891 name: "coll_b".to_string()
7892 })),
7893 source_name: Some("fuchsia.logger.Log".to_string()),
7894 target: Some(fdecl::Ref::Child(
7895 fdecl::ChildRef {
7896 name: "child_c".to_string(),
7897 collection: None,
7898 }
7899 )),
7900 target_name: Some("fuchsia.logger.Log".to_string()),
7901 source_instance_filter: Some(vec!["a_different_default".to_string()]),
7902 ..Default::default()
7903 })
7904 ]);
7905 decl.children = Some(vec![
7906 fdecl::Child {
7907 name: Some("child_c".to_string()),
7908 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
7909 startup: Some(fdecl::StartupMode::Lazy),
7910 ..Default::default()
7911 },
7912 ]);
7913 decl.collections = Some(vec![
7914 fdecl::Collection {
7915 name: Some("coll_a".into()),
7916 durability: Some(fdecl::Durability::Transient),
7917 ..Default::default()
7918 },
7919 fdecl::Collection {
7920 name: Some("coll_b".into()),
7921 durability: Some(fdecl::Durability::Transient),
7922 ..Default::default()
7923 },
7924 ]);
7925 decl
7926 },
7927 result = Ok(()),
7928 },
7929
7930 test_validate_source_dictionary => {
7932 input = fdecl::Component {
7933 program: Some(fdecl::Program {
7934 runner: Some("elf".into()),
7935 info: Some(fdata::Dictionary {
7936 entries: None,
7937 ..Default::default()
7938 }),
7939 ..Default::default()
7940 }),
7941 uses: Some(vec![
7942 fdecl::Use::Protocol(fdecl::UseProtocol {
7943 dependency_type: Some(fdecl::DependencyType::Strong),
7944 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
7945 source_dictionary: Some("bad//".into()),
7946 source_name: Some("foo".into()),
7947 target_path: Some("/svc/foo".into()),
7948 ..Default::default()
7949 }),
7950 ]),
7951 exposes: Some(vec![
7952 fdecl::Expose::Directory(fdecl::ExposeDirectory {
7953 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7954 name: "missing".into(),
7955 collection: None,
7956 })),
7957 source_dictionary: Some("in/dict".into()),
7958 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
7959 source_name: Some("foo".into()),
7960 target_name: Some("bar".into()),
7961 ..Default::default()
7962 }),
7963 ]),
7964 offers: Some(vec![
7965 fdecl::Offer::Service(fdecl::OfferService {
7966 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
7967 source_dictionary: Some("bad//".into()),
7968 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7969 name: "child".into(),
7970 collection: None,
7971 })),
7972 source_name: Some("foo".into()),
7973 target_name: Some("bar".into()),
7974 ..Default::default()
7975 }),
7976 ]),
7977 children: Some(vec![
7978 fdecl::Child {
7979 name: Some("child".into()),
7980 url: Some("fuchsia-pkg://child".into()),
7981 startup: Some(fdecl::StartupMode::Lazy),
7982 ..Default::default()
7983 },
7984 ]),
7985 ..Default::default()
7986 },
7987 result = Err(ErrorList::new(vec![
7988 Error::invalid_field(DeclType::UseProtocol, "source_dictionary"),
7989 Error::invalid_child(DeclType::ExposeDirectory, "source", "missing"),
7990 Error::invalid_field(DeclType::OfferService, "source_dictionary"),
7991 ])),
7992 },
7993 test_validate_dictionary_too_long => {
7994 input = fdecl::Component {
7995 program: Some(fdecl::Program {
7996 runner: Some("elf".into()),
7997 info: Some(fdata::Dictionary {
7998 entries: None,
7999 ..Default::default()
8000 }),
8001 ..Default::default()
8002 }),
8003 uses: Some(vec![
8004 fdecl::Use::Protocol(fdecl::UseProtocol {
8005 dependency_type: Some(fdecl::DependencyType::Strong),
8006 source: Some(fdecl::Ref::Parent( fdecl::ParentRef {} )),
8007 source_dictionary: Some("a".repeat(4096)),
8008 source_name: Some("foo".into()),
8009 target_path: Some("/svc/foo".into()),
8010 ..Default::default()
8011 }),
8012 ]),
8013 ..Default::default()
8014 },
8015 result = Err(ErrorList::new(vec![
8016 Error::field_too_long(DeclType::UseProtocol, "source_dictionary"),
8017 ])),
8018 },
8019
8020 test_validate_environment_empty => {
8022 input = {
8023 let mut decl = new_component_decl();
8024 decl.environments = Some(vec![fdecl::Environment {
8025 name: None,
8026 extends: None,
8027 runners: None,
8028 resolvers: None,
8029 stop_timeout_ms: None,
8030 debug_capabilities: None,
8031 ..Default::default()
8032 }]);
8033 decl
8034 },
8035 result = Err(ErrorList::new(vec![
8036 Error::missing_field(DeclType::Environment, "name"),
8037 Error::missing_field(DeclType::Environment, "extends"),
8038 ])),
8039 },
8040
8041 test_validate_environment_no_stop_timeout => {
8042 input = {
8043 let mut decl = new_component_decl();
8044 decl.environments = Some(vec![fdecl::Environment {
8045 name: Some("env".to_string()),
8046 extends: Some(fdecl::EnvironmentExtends::None),
8047 runners: None,
8048 resolvers: None,
8049 stop_timeout_ms: None,
8050 ..Default::default()
8051 }]);
8052 decl
8053 },
8054 result = Err(ErrorList::new(vec![Error::missing_field(DeclType::Environment, "stop_timeout_ms")])),
8055 },
8056
8057 test_validate_environment_extends_stop_timeout => {
8058 input = { let mut decl = new_component_decl();
8059 decl.environments = Some(vec![fdecl::Environment {
8060 name: Some("env".to_string()),
8061 extends: Some(fdecl::EnvironmentExtends::Realm),
8062 runners: None,
8063 resolvers: None,
8064 stop_timeout_ms: None,
8065 ..Default::default()
8066 }]);
8067 decl
8068 },
8069 result = Ok(()),
8070 },
8071 test_validate_environment_long_identifiers => {
8072 input = {
8073 let mut decl = new_component_decl();
8074 decl.environments = Some(vec![fdecl::Environment {
8075 name: Some("a".repeat(256)),
8076 extends: Some(fdecl::EnvironmentExtends::None),
8077 runners: Some(vec![
8078 fdecl::RunnerRegistration {
8079 source_name: Some("a".repeat(256)),
8080 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8081 target_name: Some("a".repeat(256)),
8082 ..Default::default()
8083 },
8084 ]),
8085 resolvers: Some(vec![
8086 fdecl::ResolverRegistration {
8087 resolver: Some("a".repeat(256)),
8088 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8089 scheme: Some("a".repeat(256)),
8090 ..Default::default()
8091 },
8092 ]),
8093 stop_timeout_ms: Some(1234),
8094 ..Default::default()
8095 }]);
8096 decl
8097 },
8098 result = Err(ErrorList::new(vec![
8099 Error::field_too_long(DeclType::Environment, "name"),
8100 Error::field_too_long(DeclType::RunnerRegistration, "source_name"),
8101 Error::field_too_long(DeclType::RunnerRegistration, "target_name"),
8102 Error::field_too_long(DeclType::ResolverRegistration, "resolver"),
8103 Error::field_too_long(DeclType::ResolverRegistration, "scheme"),
8104 ])),
8105 },
8106 test_validate_environment_empty_runner_resolver_fields => {
8107 input = {
8108 let mut decl = new_component_decl();
8109 decl.environments = Some(vec![fdecl::Environment {
8110 name: Some("a".to_string()),
8111 extends: Some(fdecl::EnvironmentExtends::None),
8112 runners: Some(vec![
8113 fdecl::RunnerRegistration {
8114 source_name: None,
8115 source: None,
8116 target_name: None,
8117 ..Default::default()
8118 },
8119 ]),
8120 resolvers: Some(vec![
8121 fdecl::ResolverRegistration {
8122 resolver: None,
8123 source: None,
8124 scheme: None,
8125 ..Default::default()
8126 },
8127 ]),
8128 stop_timeout_ms: Some(1234),
8129 ..Default::default()
8130 }]);
8131 decl
8132 },
8133 result = Err(ErrorList::new(vec![
8134 Error::missing_field(DeclType::RunnerRegistration, "source_name"),
8135 Error::missing_field(DeclType::RunnerRegistration, "source"),
8136 Error::missing_field(DeclType::RunnerRegistration, "target_name"),
8137 Error::missing_field(DeclType::ResolverRegistration, "resolver"),
8138 Error::missing_field(DeclType::ResolverRegistration, "source"),
8139 Error::missing_field(DeclType::ResolverRegistration, "scheme"),
8140 ])),
8141 },
8142 test_validate_environment_invalid_fields => {
8143 input = {
8144 let mut decl = new_component_decl();
8145 decl.environments = Some(vec![fdecl::Environment {
8146 name: Some("a".to_string()),
8147 extends: Some(fdecl::EnvironmentExtends::None),
8148 runners: Some(vec![
8149 fdecl::RunnerRegistration {
8150 source_name: Some("^a".to_string()),
8151 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
8152 target_name: Some("%a".to_string()),
8153 ..Default::default()
8154 },
8155 ]),
8156 resolvers: Some(vec![
8157 fdecl::ResolverRegistration {
8158 resolver: Some("^a".to_string()),
8159 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
8160 scheme: Some("9scheme".to_string()),
8161 ..Default::default()
8162 },
8163 ]),
8164 stop_timeout_ms: Some(1234),
8165 ..Default::default()
8166 }]);
8167 decl
8168 },
8169 result = Err(ErrorList::new(vec![
8170 Error::invalid_field(DeclType::RunnerRegistration, "source_name"),
8171 Error::invalid_field(DeclType::RunnerRegistration, "source"),
8172 Error::invalid_field(DeclType::RunnerRegistration, "target_name"),
8173 Error::invalid_field(DeclType::ResolverRegistration, "resolver"),
8174 Error::invalid_field(DeclType::ResolverRegistration, "source"),
8175 Error::invalid_field(DeclType::ResolverRegistration, "scheme"),
8176 ])),
8177 },
8178 test_validate_environment_missing_runner => {
8179 input = {
8180 let mut decl = new_component_decl();
8181 decl.environments = Some(vec![fdecl::Environment {
8182 name: Some("a".to_string()),
8183 extends: Some(fdecl::EnvironmentExtends::None),
8184 runners: Some(vec![
8185 fdecl::RunnerRegistration {
8186 source_name: Some("dart".to_string()),
8187 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
8188 target_name: Some("dart".to_string()),
8189 ..Default::default()
8190 },
8191 ]),
8192 resolvers: None,
8193 stop_timeout_ms: Some(1234),
8194 ..Default::default()
8195 }]);
8196 decl
8197 },
8198 result = Err(ErrorList::new(vec![
8199 Error::invalid_runner(DeclType::RunnerRegistration, "source_name", "dart"),
8200 ])),
8201 },
8202 test_validate_environment_duplicate_registrations => {
8203 input = {
8204 let mut decl = new_component_decl();
8205 decl.environments = Some(vec![fdecl::Environment {
8206 name: Some("a".to_string()),
8207 extends: Some(fdecl::EnvironmentExtends::None),
8208 runners: Some(vec![
8209 fdecl::RunnerRegistration {
8210 source_name: Some("dart".to_string()),
8211 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8212 target_name: Some("dart".to_string()),
8213 ..Default::default()
8214 },
8215 fdecl::RunnerRegistration {
8216 source_name: Some("other-dart".to_string()),
8217 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8218 target_name: Some("dart".to_string()),
8219 ..Default::default()
8220 },
8221 ]),
8222 resolvers: Some(vec![
8223 fdecl::ResolverRegistration {
8224 resolver: Some("pkg_resolver".to_string()),
8225 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8226 scheme: Some("fuchsia-pkg".to_string()),
8227 ..Default::default()
8228 },
8229 fdecl::ResolverRegistration {
8230 resolver: Some("base_resolver".to_string()),
8231 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8232 scheme: Some("fuchsia-pkg".to_string()),
8233 ..Default::default()
8234 },
8235 ]),
8236 stop_timeout_ms: Some(1234),
8237 ..Default::default()
8238 }]);
8239 decl
8240 },
8241 result = Err(ErrorList::new(vec![
8242 Error::duplicate_field(DeclType::RunnerRegistration, "target_name", "dart"),
8243 Error::duplicate_field(DeclType::ResolverRegistration, "scheme", "fuchsia-pkg"),
8244 ])),
8245 },
8246 test_validate_environment_from_missing_child => {
8247 input = {
8248 let mut decl = new_component_decl();
8249 decl.environments = Some(vec![fdecl::Environment {
8250 name: Some("a".to_string()),
8251 extends: Some(fdecl::EnvironmentExtends::None),
8252 runners: Some(vec![
8253 fdecl::RunnerRegistration {
8254 source_name: Some("elf".to_string()),
8255 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8256 name: "missing".to_string(),
8257 collection: None,
8258 })),
8259 target_name: Some("elf".to_string()),
8260 ..Default::default()
8261 },
8262 ]),
8263 resolvers: Some(vec![
8264 fdecl::ResolverRegistration {
8265 resolver: Some("pkg_resolver".to_string()),
8266 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8267 name: "missing".to_string(),
8268 collection: None,
8269 })),
8270 scheme: Some("fuchsia-pkg".to_string()),
8271 ..Default::default()
8272 },
8273 ]),
8274 stop_timeout_ms: Some(1234),
8275 ..Default::default()
8276 }]);
8277 decl
8278 },
8279 result = Err(ErrorList::new(vec![
8280 Error::invalid_child(DeclType::RunnerRegistration, "source", "missing"),
8281 Error::invalid_child(DeclType::ResolverRegistration, "source", "missing"),
8282 ])),
8283 },
8284 test_validate_environment_runner_child_cycle => {
8285 input = {
8286 let mut decl = new_component_decl();
8287 decl.environments = Some(vec![fdecl::Environment {
8288 name: Some("env".to_string()),
8289 extends: Some(fdecl::EnvironmentExtends::None),
8290 runners: Some(vec![
8291 fdecl::RunnerRegistration {
8292 source_name: Some("elf".to_string()),
8293 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8294 name: "child".to_string(),
8295 collection: None,
8296 })),
8297 target_name: Some("elf".to_string()),
8298 ..Default::default()
8299 },
8300 ]),
8301 resolvers: None,
8302 stop_timeout_ms: Some(1234),
8303 ..Default::default()
8304 }]);
8305 decl.children = Some(vec![fdecl::Child {
8306 name: Some("child".to_string()),
8307 startup: Some(fdecl::StartupMode::Lazy),
8308 on_terminate: None,
8309 url: Some("fuchsia-pkg://child".to_string()),
8310 environment: Some("env".to_string()),
8311 ..Default::default()
8312 }]);
8313 decl
8314 },
8315 result = Err(ErrorList::new(vec![
8316 Error::dependency_cycle(
8317 directed_graph::Error::CyclesDetected([vec!["child child", "environment env", "child child"]].iter().cloned().collect()).format_cycle()
8318 ),
8319 ])),
8320 },
8321 test_validate_environment_resolver_child_cycle => {
8322 input = {
8323 let mut decl = new_component_decl();
8324 decl.environments = Some(vec![fdecl::Environment {
8325 name: Some("env".to_string()),
8326 extends: Some(fdecl::EnvironmentExtends::None),
8327 runners: None,
8328 resolvers: Some(vec![
8329 fdecl::ResolverRegistration {
8330 resolver: Some("pkg_resolver".to_string()),
8331 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8332 name: "child".to_string(),
8333 collection: None,
8334 })),
8335 scheme: Some("fuchsia-pkg".to_string()),
8336 ..Default::default()
8337 },
8338 ]),
8339 stop_timeout_ms: Some(1234),
8340 ..Default::default()
8341 }]);
8342 decl.children = Some(vec![fdecl::Child {
8343 name: Some("child".to_string()),
8344 startup: Some(fdecl::StartupMode::Lazy),
8345 on_terminate: None,
8346 url: Some("fuchsia-pkg://child".to_string()),
8347 environment: Some("env".to_string()),
8348 ..Default::default()
8349 }]);
8350 decl
8351 },
8352 result = Err(ErrorList::new(vec![
8353 Error::dependency_cycle(
8354 directed_graph::Error::CyclesDetected([vec!["child child", "environment env", "child child"]].iter().cloned().collect()).format_cycle()
8355 ),
8356 ])),
8357 },
8358 test_validate_environment_resolver_multiple_children_cycle => {
8359 input = {
8360 let mut decl = new_component_decl();
8361 decl.environments = Some(vec![fdecl::Environment {
8362 name: Some("env".to_string()),
8363 extends: Some(fdecl::EnvironmentExtends::None),
8364 runners: None,
8365 resolvers: Some(vec![
8366 fdecl::ResolverRegistration {
8367 resolver: Some("pkg_resolver".to_string()),
8368 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8369 name: "a".to_string(),
8370 collection: None,
8371 })),
8372 scheme: Some("fuchsia-pkg".to_string()),
8373 ..Default::default()
8374 },
8375 ]),
8376 stop_timeout_ms: Some(1234),
8377 ..Default::default()
8378 }]);
8379 decl.children = Some(vec![
8380 fdecl::Child {
8381 name: Some("a".to_string()),
8382 startup: Some(fdecl::StartupMode::Lazy),
8383 on_terminate: None,
8384 url: Some("fuchsia-pkg://child-a".to_string()),
8385 environment: None,
8386 ..Default::default()
8387 },
8388 fdecl::Child {
8389 name: Some("b".to_string()),
8390 startup: Some(fdecl::StartupMode::Lazy),
8391 on_terminate: None,
8392 url: Some("fuchsia-pkg://child-b".to_string()),
8393 environment: Some("env".to_string()),
8394 ..Default::default()
8395 },
8396 ]);
8397 decl.offers = Some(vec![fdecl::Offer::Service(fdecl::OfferService {
8398 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8399 name: "b".to_string(),
8400 collection: None,
8401 })),
8402 source_name: Some("thing".to_string()),
8403 target: Some(fdecl::Ref::Child(
8404 fdecl::ChildRef {
8405 name: "a".to_string(),
8406 collection: None,
8407 }
8408 )),
8409 target_name: Some("thing".to_string()),
8410 ..Default::default()
8411 })]);
8412 decl
8413 },
8414 result = Err(ErrorList::new(vec![
8415 Error::dependency_cycle(
8416 directed_graph::Error::CyclesDetected([vec!["child a", "environment env", "child b", "child a"]].iter().cloned().collect()).format_cycle()
8417 ),
8418 ])),
8419 },
8420 test_validate_environment_debug_empty => {
8421 input = {
8422 let mut decl = new_component_decl();
8423 decl.environments = Some(vec![
8424 fdecl::Environment {
8425 name: Some("a".to_string()),
8426 extends: Some(fdecl::EnvironmentExtends::None),
8427 stop_timeout_ms: Some(2),
8428 debug_capabilities:Some(vec![
8429 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8430 source: None,
8431 source_name: None,
8432 target_name: None,
8433 ..Default::default()
8434 }),
8435 ]),
8436 ..Default::default()
8437 }]);
8438 decl
8439 },
8440 result = Err(ErrorList::new(vec![
8441 Error::missing_field(DeclType::DebugProtocolRegistration, "source"),
8442 Error::missing_field(DeclType::DebugProtocolRegistration, "source_name"),
8443 Error::missing_field(DeclType::DebugProtocolRegistration, "target_name"),
8444 ])),
8445 },
8446 test_validate_environment_debug_log_identifier => {
8447 input = {
8448 let mut decl = new_component_decl();
8449 decl.environments = Some(vec![
8450 fdecl::Environment {
8451 name: Some("a".to_string()),
8452 extends: Some(fdecl::EnvironmentExtends::None),
8453 stop_timeout_ms: Some(2),
8454 debug_capabilities:Some(vec![
8455 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8456 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8457 name: "a".repeat(256),
8458 collection: None,
8459 })),
8460 source_name: Some(format!("{}", "a".repeat(256))),
8461 target_name: Some(format!("{}", "b".repeat(256))),
8462 ..Default::default()
8463 }),
8464 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8465 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8466 source_name: Some("a".to_string()),
8467 target_name: Some(format!("{}", "b".repeat(256))),
8468 ..Default::default()
8469 }),
8470 ]),
8471 ..Default::default()
8472 }]);
8473 decl
8474 },
8475 result = Err(ErrorList::new(vec![
8476 Error::field_too_long(DeclType::DebugProtocolRegistration, "source.child.name"),
8477 Error::field_too_long(DeclType::DebugProtocolRegistration, "source_name"),
8478 Error::field_too_long(DeclType::DebugProtocolRegistration, "target_name"),
8479 Error::field_too_long(DeclType::DebugProtocolRegistration, "target_name"),
8480 ])),
8481 },
8482 test_validate_environment_debug_log_extraneous => {
8483 input = {
8484 let mut decl = new_component_decl();
8485 decl.environments = Some(vec![
8486 fdecl::Environment {
8487 name: Some("a".to_string()),
8488 extends: Some(fdecl::EnvironmentExtends::None),
8489 stop_timeout_ms: Some(2),
8490 debug_capabilities:Some(vec![
8491 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8492 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8493 name: "logger".to_string(),
8494 collection: Some("modular".to_string()),
8495 })),
8496 source_name: Some("fuchsia.logger.Log".to_string()),
8497 target_name: Some("fuchsia.logger.Log".to_string()),
8498 ..Default::default()
8499 }),
8500 ]),
8501 ..Default::default()
8502 }]);
8503 decl
8504 },
8505 result = Err(ErrorList::new(vec![
8506 Error::extraneous_field(DeclType::DebugProtocolRegistration, "source.child.collection"),
8507 ])),
8508 },
8509 test_validate_environment_debug_log_invalid_identifiers => {
8510 input = {
8511 let mut decl = new_component_decl();
8512 decl.environments = Some(vec![
8513 fdecl::Environment {
8514 name: Some("a".to_string()),
8515 extends: Some(fdecl::EnvironmentExtends::None),
8516 stop_timeout_ms: Some(2),
8517 debug_capabilities:Some(vec![
8518 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8519 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8520 name: "^bad".to_string(),
8521 collection: None,
8522 })),
8523 source_name: Some("foo/".to_string()),
8524 target_name: Some("/".to_string()),
8525 ..Default::default()
8526 }),
8527 ]),
8528 ..Default::default()
8529 }]);
8530 decl
8531 },
8532 result = Err(ErrorList::new(vec![
8533 Error::invalid_field(DeclType::DebugProtocolRegistration, "source.child.name"),
8534 Error::invalid_field(DeclType::DebugProtocolRegistration, "source_name"),
8535 Error::invalid_field(DeclType::DebugProtocolRegistration, "target_name"),
8536 ])),
8537 },
8538 test_validate_environment_debug_log_invalid_child => {
8539 input = {
8540 let mut decl = new_component_decl();
8541 decl.environments = Some(vec![
8542 fdecl::Environment {
8543 name: Some("a".to_string()),
8544 extends: Some(fdecl::EnvironmentExtends::None),
8545 stop_timeout_ms: Some(2),
8546 debug_capabilities:Some(vec![
8547 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8548 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8549 name: "logger".to_string(),
8550 collection: None,
8551 })),
8552 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
8553 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
8554 ..Default::default()
8555 }),
8556 ]),
8557 ..Default::default()
8558 }]);
8559 decl.children = Some(vec![
8560 fdecl::Child {
8561 name: Some("netstack".to_string()),
8562 url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
8563 startup: Some(fdecl::StartupMode::Lazy),
8564 on_terminate: None,
8565 environment: None,
8566 ..Default::default()
8567 },
8568 ]);
8569 decl
8570 },
8571 result = Err(ErrorList::new(vec![
8572 Error::invalid_child(DeclType::DebugProtocolRegistration, "source", "logger"),
8573
8574 ])),
8575 },
8576 test_validate_environment_debug_source_capability => {
8577 input = {
8578 let mut decl = new_component_decl();
8579 decl.environments = Some(vec![
8580 fdecl::Environment {
8581 name: Some("a".to_string()),
8582 extends: Some(fdecl::EnvironmentExtends::None),
8583 stop_timeout_ms: Some(2),
8584 debug_capabilities:Some(vec![
8585 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8586 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
8587 name: "storage".to_string(),
8588 })),
8589 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
8590 target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
8591 ..Default::default()
8592 }),
8593 ]),
8594 ..Default::default()
8595 }]);
8596 decl
8597 },
8598 result = Err(ErrorList::new(vec![
8599 Error::invalid_field(DeclType::DebugProtocolRegistration, "source"),
8600 ])),
8601 },
8602
8603 test_validate_children_empty => {
8605 input = {
8606 let mut decl = new_component_decl();
8607 decl.children = Some(vec![fdecl::Child{
8608 name: None,
8609 url: None,
8610 startup: None,
8611 on_terminate: None,
8612 environment: None,
8613 ..Default::default()
8614 }]);
8615 decl
8616 },
8617 result = Err(ErrorList::new(vec![
8618 Error::missing_field(DeclType::Child, "name"),
8619 Error::missing_field(DeclType::Child, "url"),
8620 Error::missing_field(DeclType::Child, "startup"),
8621 ])),
8623 },
8624 test_validate_children_invalid_identifiers => {
8625 input = {
8626 let mut decl = new_component_decl();
8627 decl.children = Some(vec![fdecl::Child{
8628 name: Some("^bad".to_string()),
8629 url: Some("scheme://invalid-port:99999999/path#frag".to_string()),
8630 startup: Some(fdecl::StartupMode::Lazy),
8631 on_terminate: None,
8632 environment: None,
8633 ..Default::default()
8634 }]);
8635 decl
8636 },
8637 result = Err(ErrorList::new(vec![
8638 Error::invalid_field(DeclType::Child, "name"),
8639 Error::invalid_url(DeclType::Child, "url", "\"scheme://invalid-port:99999999/path#frag\": Malformed URL: InvalidPort."),
8640 ])),
8641 },
8642 test_validate_children_long_identifiers => {
8643 input = {
8644 let mut decl = new_component_decl();
8645 decl.children = Some(vec![fdecl::Child{
8646 name: Some("a".repeat(1025)),
8647 url: Some(format!("fuchsia-pkg://{}", "a".repeat(4083))),
8648 startup: Some(fdecl::StartupMode::Lazy),
8649 on_terminate: None,
8650 environment: Some("a".repeat(1025)),
8651 ..Default::default()
8652 }]);
8653 decl
8654 },
8655 result = Err(ErrorList::new(vec![
8656 Error::field_too_long(DeclType::Child, "name"),
8657 Error::field_too_long(DeclType::Child, "url"),
8658 Error::field_too_long(DeclType::Child, "environment"),
8659 Error::invalid_environment(DeclType::Child, "environment", "a".repeat(1025)),
8660 ])),
8661 },
8662 test_validate_child_references_unknown_env => {
8663 input = {
8664 let mut decl = new_component_decl();
8665 decl.children = Some(vec![fdecl::Child{
8666 name: Some("foo".to_string()),
8667 url: Some("fuchsia-pkg://foo".to_string()),
8668 startup: Some(fdecl::StartupMode::Lazy),
8669 on_terminate: None,
8670 environment: Some("test_env".to_string()),
8671 ..Default::default()
8672 }]);
8673 decl
8674 },
8675 result = Err(ErrorList::new(vec![
8676 Error::invalid_environment(DeclType::Child, "environment", "test_env"),
8677 ])),
8678 },
8679
8680 test_validate_collections_empty => {
8682 input = {
8683 let mut decl = new_component_decl();
8684 decl.collections = Some(vec![fdecl::Collection{
8685 name: None,
8686 durability: None,
8687 environment: None,
8688 allowed_offers: None,
8689 allow_long_names: None,
8690 ..Default::default()
8691 }]);
8692 decl
8693 },
8694 result = Err(ErrorList::new(vec![
8695 Error::missing_field(DeclType::Collection, "name"),
8696 Error::missing_field(DeclType::Collection, "durability"),
8697 ])),
8698 },
8699 test_validate_collections_invalid_identifiers => {
8700 input = {
8701 let mut decl = new_component_decl();
8702 decl.collections = Some(vec![fdecl::Collection{
8703 name: Some("^bad".to_string()),
8704 durability: Some(fdecl::Durability::Transient),
8705 environment: None,
8706 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
8707 allow_long_names: None,
8708 ..Default::default()
8709 }]);
8710 decl
8711 },
8712 result = Err(ErrorList::new(vec![
8713 Error::invalid_field(DeclType::Collection, "name"),
8714 ])),
8715 },
8716 test_validate_collections_long_identifiers => {
8717 input = {
8718 let mut decl = new_component_decl();
8719 decl.collections = Some(vec![fdecl::Collection{
8720 name: Some("a".repeat(1025)),
8721 durability: Some(fdecl::Durability::Transient),
8722 environment: None,
8723 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
8724 allow_long_names: None,
8725 ..Default::default()
8726 }]);
8727 decl
8728 },
8729 result = Err(ErrorList::new(vec![
8730 Error::field_too_long(DeclType::Collection, "name"),
8731 ])),
8732 },
8733 test_validate_collection_references_unknown_env => {
8734 input = {
8735 let mut decl = new_component_decl();
8736 decl.collections = Some(vec![fdecl::Collection {
8737 name: Some("foo".to_string()),
8738 durability: Some(fdecl::Durability::Transient),
8739 environment: Some("test_env".to_string()),
8740 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
8741 allow_long_names: None,
8742 ..Default::default()
8743 }]);
8744 decl
8745 },
8746 result = Err(ErrorList::new(vec![
8747 Error::invalid_environment(DeclType::Collection, "environment", "test_env"),
8748 ])),
8749 },
8750
8751 test_validate_capabilities_empty => {
8753 input = {
8754 let mut decl = new_component_decl();
8755 decl.capabilities = Some(vec![
8756 fdecl::Capability::Service(fdecl::Service {
8757 name: None,
8758 source_path: None,
8759 ..Default::default()
8760 }),
8761 fdecl::Capability::Protocol(fdecl::Protocol {
8762 name: None,
8763 source_path: None,
8764 ..Default::default()
8765 }),
8766 fdecl::Capability::Directory(fdecl::Directory {
8767 name: None,
8768 source_path: None,
8769 rights: None,
8770 ..Default::default()
8771 }),
8772 fdecl::Capability::Storage(fdecl::Storage {
8773 name: None,
8774 source: None,
8775 backing_dir: None,
8776 subdir: None,
8777 storage_id: None,
8778 ..Default::default()
8779 }),
8780 fdecl::Capability::Runner(fdecl::Runner {
8781 name: None,
8782 source_path: None,
8783 ..Default::default()
8784 }),
8785 fdecl::Capability::Resolver(fdecl::Resolver {
8786 name: None,
8787 source_path: None,
8788 ..Default::default()
8789 }),
8790 fdecl::Capability::Dictionary(fdecl::Dictionary {
8791 ..Default::default()
8792 }),
8793 ]);
8794 decl
8795 },
8796 result = Err(ErrorList::new(vec![
8797 Error::missing_field(DeclType::Dictionary, "name"),
8798 Error::missing_field(DeclType::Service, "name"),
8799 Error::missing_field(DeclType::Service, "source_path"),
8800 Error::missing_field(DeclType::Protocol, "name"),
8801 Error::missing_field(DeclType::Protocol, "source_path"),
8802 Error::missing_field(DeclType::Directory, "name"),
8803 Error::missing_field(DeclType::Directory, "source_path"),
8804 Error::missing_field(DeclType::Directory, "rights"),
8805 Error::missing_field(DeclType::Storage, "source"),
8806 Error::missing_field(DeclType::Storage, "name"),
8807 Error::missing_field(DeclType::Storage, "storage_id"),
8808 Error::missing_field(DeclType::Storage, "backing_dir"),
8809 Error::missing_field(DeclType::Runner, "name"),
8810 Error::missing_field(DeclType::Runner, "source_path"),
8811 Error::missing_field(DeclType::Resolver, "name"),
8812 Error::missing_field(DeclType::Resolver, "source_path"),
8813 ])),
8814 },
8815 test_validate_capabilities_invalid_identifiers => {
8816 input = {
8817 let mut decl = new_component_decl();
8818 decl.capabilities = Some(vec![
8819 fdecl::Capability::Service(fdecl::Service {
8820 name: Some("^bad".to_string()),
8821 source_path: Some("&bad".to_string()),
8822 ..Default::default()
8823 }),
8824 fdecl::Capability::Protocol(fdecl::Protocol {
8825 name: Some("^bad".to_string()),
8826 source_path: Some("&bad".to_string()),
8827 ..Default::default()
8828 }),
8829 fdecl::Capability::Directory(fdecl::Directory {
8830 name: Some("^bad".to_string()),
8831 source_path: Some("&bad".to_string()),
8832 rights: Some(fio::Operations::CONNECT),
8833 ..Default::default()
8834 }),
8835 fdecl::Capability::Storage(fdecl::Storage {
8836 name: Some("^bad".to_string()),
8837 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
8838 name: "/bad".to_string()
8839 })),
8840 backing_dir: Some("&bad".to_string()),
8841 subdir: None,
8842 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
8843 ..Default::default()
8844 }),
8845 fdecl::Capability::Runner(fdecl::Runner {
8846 name: Some("^bad".to_string()),
8847 source_path: Some("&bad".to_string()),
8848 ..Default::default()
8849 }),
8850 fdecl::Capability::Resolver(fdecl::Resolver {
8851 name: Some("^bad".to_string()),
8852 source_path: Some("&bad".to_string()),
8853 ..Default::default()
8854 }),
8855 fdecl::Capability::Dictionary(fdecl::Dictionary {
8856 name: Some("^bad".to_string()),
8857 ..Default::default()
8858 }),
8859 ]);
8860 decl
8861 },
8862 result = Err(ErrorList::new(vec![
8863 Error::invalid_field(DeclType::Dictionary, "name"),
8864 Error::invalid_field(DeclType::Service, "name"),
8865 Error::invalid_field(DeclType::Service, "source_path"),
8866 Error::invalid_field(DeclType::Protocol, "name"),
8867 Error::invalid_field(DeclType::Protocol, "source_path"),
8868 Error::invalid_field(DeclType::Directory, "name"),
8869 Error::invalid_field(DeclType::Directory, "source_path"),
8870 Error::invalid_field(DeclType::Storage, "source"),
8871 Error::invalid_field(DeclType::Storage, "name"),
8872 Error::invalid_field(DeclType::Storage, "backing_dir"),
8873 Error::invalid_field(DeclType::Runner, "name"),
8874 Error::invalid_field(DeclType::Runner, "source_path"),
8875 Error::invalid_field(DeclType::Resolver, "name"),
8876 Error::invalid_field(DeclType::Resolver, "source_path"),
8877 ])),
8878 },
8879 test_validate_capabilities_invalid_child => {
8880 input = {
8881 let mut decl = new_component_decl();
8882 decl.capabilities = Some(vec![
8883 fdecl::Capability::Storage(fdecl::Storage {
8884 name: Some("foo".to_string()),
8885 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
8886 name: "invalid".to_string(),
8887 })),
8888 backing_dir: Some("foo".to_string()),
8889 subdir: None,
8890 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
8891 ..Default::default()
8892 }),
8893 ]);
8894 decl
8895 },
8896 result = Err(ErrorList::new(vec![
8897 Error::invalid_field(DeclType::Storage, "source"),
8898 ])),
8899 },
8900 test_validate_capabilities_long_identifiers => {
8901 input = {
8902 let mut decl = new_component_decl();
8903 decl.capabilities = Some(vec![
8904 fdecl::Capability::Service(fdecl::Service {
8905 name: Some("a".repeat(256)),
8906 source_path: Some("/c".repeat(2048)),
8907 ..Default::default()
8908 }),
8909 fdecl::Capability::Protocol(fdecl::Protocol {
8910 name: Some("a".repeat(256)),
8911 source_path: Some("/c".repeat(2048)),
8912 ..Default::default()
8913 }),
8914 fdecl::Capability::Directory(fdecl::Directory {
8915 name: Some("a".repeat(256)),
8916 source_path: Some("/c".repeat(2048)),
8917 rights: Some(fio::Operations::CONNECT),
8918 ..Default::default()
8919 }),
8920 fdecl::Capability::Storage(fdecl::Storage {
8921 name: Some("a".repeat(256)),
8922 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8923 name: "b".repeat(256),
8924 collection: None,
8925 })),
8926 backing_dir: Some(format!("{}", "c".repeat(256))),
8927 subdir: None,
8928 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
8929 ..Default::default()
8930 }),
8931 fdecl::Capability::Runner(fdecl::Runner {
8932 name: Some("a".repeat(256)),
8933 source_path: Some("/c".repeat(2048)),
8934 ..Default::default()
8935 }),
8936 fdecl::Capability::Resolver(fdecl::Resolver {
8937 name: Some("a".repeat(256)),
8938 source_path: Some("/c".repeat(2048)),
8939 ..Default::default()
8940 }),
8941 fdecl::Capability::Dictionary(fdecl::Dictionary {
8942 name: Some("a".repeat(256)),
8943 ..Default::default()
8944 }),
8945 ]);
8946 decl
8947 },
8948 result = Err(ErrorList::new(vec![
8949 Error::field_too_long(DeclType::Dictionary, "name"),
8950 Error::field_too_long(DeclType::Service, "name"),
8951 Error::field_too_long(DeclType::Service, "source_path"),
8952 Error::field_too_long(DeclType::Protocol, "name"),
8953 Error::field_too_long(DeclType::Protocol, "source_path"),
8954 Error::field_too_long(DeclType::Directory, "name"),
8955 Error::field_too_long(DeclType::Directory, "source_path"),
8956 Error::field_too_long(DeclType::Storage, "source.child.name"),
8957 Error::field_too_long(DeclType::Storage, "name"),
8958 Error::field_too_long(DeclType::Storage, "backing_dir"),
8959 Error::field_too_long(DeclType::Runner, "name"),
8960 Error::field_too_long(DeclType::Runner, "source_path"),
8961 Error::field_too_long(DeclType::Resolver, "name"),
8962 Error::field_too_long(DeclType::Resolver, "source_path"),
8963 ])),
8964 },
8965 test_validate_capabilities_duplicate_name => {
8966 input = {
8967 let mut decl = new_component_decl();
8968 decl.capabilities = Some(vec![
8969 fdecl::Capability::Service(fdecl::Service {
8970 name: Some("service".to_string()),
8971 source_path: Some("/service".to_string()),
8972 ..Default::default()
8973 }),
8974 fdecl::Capability::Service(fdecl::Service {
8975 name: Some("service".to_string()),
8976 source_path: Some("/service".to_string()),
8977 ..Default::default()
8978 }),
8979 fdecl::Capability::Protocol(fdecl::Protocol {
8980 name: Some("protocol".to_string()),
8981 source_path: Some("/protocol".to_string()),
8982 ..Default::default()
8983 }),
8984 fdecl::Capability::Protocol(fdecl::Protocol {
8985 name: Some("protocol".to_string()),
8986 source_path: Some("/protocol".to_string()),
8987 ..Default::default()
8988 }),
8989 fdecl::Capability::Directory(fdecl::Directory {
8990 name: Some("directory".to_string()),
8991 source_path: Some("/directory".to_string()),
8992 rights: Some(fio::Operations::CONNECT),
8993 ..Default::default()
8994 }),
8995 fdecl::Capability::Directory(fdecl::Directory {
8996 name: Some("directory".to_string()),
8997 source_path: Some("/directory".to_string()),
8998 rights: Some(fio::Operations::CONNECT),
8999 ..Default::default()
9000 }),
9001 fdecl::Capability::Storage(fdecl::Storage {
9002 name: Some("storage".to_string()),
9003 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
9004 backing_dir: Some("directory".to_string()),
9005 subdir: None,
9006 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9007 ..Default::default()
9008 }),
9009 fdecl::Capability::Storage(fdecl::Storage {
9010 name: Some("storage".to_string()),
9011 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
9012 backing_dir: Some("directory".to_string()),
9013 subdir: None,
9014 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9015 ..Default::default()
9016 }),
9017 fdecl::Capability::Runner(fdecl::Runner {
9018 name: Some("runner".to_string()),
9019 source_path: Some("/runner".to_string()),
9020 ..Default::default()
9021 }),
9022 fdecl::Capability::Runner(fdecl::Runner {
9023 name: Some("runner".to_string()),
9024 source_path: Some("/runner".to_string()),
9025 ..Default::default()
9026 }),
9027 fdecl::Capability::Resolver(fdecl::Resolver {
9028 name: Some("resolver".to_string()),
9029 source_path: Some("/resolver".to_string()),
9030 ..Default::default()
9031 }),
9032 fdecl::Capability::Resolver(fdecl::Resolver {
9033 name: Some("resolver".to_string()),
9034 source_path: Some("/resolver".to_string()),
9035 ..Default::default()
9036 }),
9037 fdecl::Capability::Dictionary(fdecl::Dictionary {
9038 name: Some("dictionary".to_string()),
9039 ..Default::default()
9040 }),
9041 fdecl::Capability::Dictionary(fdecl::Dictionary {
9042 name: Some("dictionary".to_string()),
9043 ..Default::default()
9044 }),
9045 ]);
9046 decl
9047 },
9048 result = Err(ErrorList::new(vec![
9049 Error::duplicate_field(DeclType::Dictionary, "name", "dictionary"),
9050 Error::duplicate_field(DeclType::Service, "name", "service"),
9051 Error::duplicate_field(DeclType::Protocol, "name", "protocol"),
9052 Error::duplicate_field(DeclType::Directory, "name", "directory"),
9053 Error::duplicate_field(DeclType::Storage, "name", "storage"),
9054 Error::duplicate_field(DeclType::Runner, "name", "runner"),
9055 Error::duplicate_field(DeclType::Resolver, "name", "resolver"),
9056 ])),
9057 },
9058 test_validate_invalid_service_aggregation_conflicting_filter => {
9059 input = {
9060 let mut decl = new_component_decl();
9061 decl.offers = Some(vec![
9062 fdecl::Offer::Service(fdecl::OfferService {
9063 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9064 name: "coll_a".to_string()
9065 })),
9066 source_name: Some("fuchsia.logger.Log".to_string()),
9067 target: Some(fdecl::Ref::Child(
9068 fdecl::ChildRef {
9069 name: "child_c".to_string(),
9070 collection: None,
9071 }
9072 )),
9073 target_name: Some("fuchsia.logger.Log1".to_string()),
9074 source_instance_filter: Some(vec!["default".to_string()]),
9075 ..Default::default()
9076 }),
9077 fdecl::Offer::Service(fdecl::OfferService {
9078 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9079 name: "coll_b".to_string()
9080 })),
9081 source_name: Some("fuchsia.logger.Log".to_string()),
9082 target: Some(fdecl::Ref::Child(
9083 fdecl::ChildRef {
9084 name: "child_c".to_string(),
9085 collection: None,
9086 }
9087 )),
9088 target_name: Some("fuchsia.logger.Log1".to_string()),
9089 source_instance_filter: Some(vec!["default".to_string()]),
9090 ..Default::default()
9091 }),
9092 ]);
9093 decl.collections = Some(vec![
9094 fdecl::Collection {
9095 name: Some("coll_a".to_string()),
9096 durability: Some(fdecl::Durability::Transient),
9097 ..Default::default()
9098 },
9099 fdecl::Collection {
9100 name: Some("coll_b".to_string()),
9101 durability: Some(fdecl::Durability::Transient),
9102 ..Default::default()
9103 },
9104 ]);
9105 decl.children = Some(vec![
9106 fdecl::Child {
9107 name: Some("child_c".to_string()),
9108 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9109 startup: Some(fdecl::StartupMode::Lazy),
9110 ..Default::default()
9111 },
9112 ]);
9113 decl
9114 },
9115 result = Err(ErrorList::new(vec![
9116 Error::invalid_aggregate_offer("Conflicting source_instance_filter in aggregate \
9117 service offer, instance_name 'default' seen in filter lists multiple times"),
9118 ])),
9119 },
9120
9121 test_validate_invalid_service_aggregation_conflicting_source_name => {
9122 input = {
9123 let mut decl = new_component_decl();
9124 decl.offers = Some(vec![
9125 fdecl::Offer::Service(fdecl::OfferService {
9126 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9127 name: "coll_a".into()
9128 })),
9129 source_name: Some("fuchsia.logger.Log".to_string()),
9130 target: Some(fdecl::Ref::Child(
9131 fdecl::ChildRef {
9132 name: "child_c".to_string(),
9133 collection: None,
9134 }
9135 )),
9136 target_name: Some("fuchsia.logger.Log2".to_string()),
9137 source_instance_filter: Some(vec!["default2".to_string()]),
9138 ..Default::default()
9139 }),
9140 fdecl::Offer::Service(fdecl::OfferService {
9141 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9142 name: "coll_b".into()
9143 })),
9144 source_name: Some("fuchsia.logger.LogAlt".to_string()),
9145 target: Some(fdecl::Ref::Child(
9146 fdecl::ChildRef {
9147 name: "child_c".to_string(),
9148 collection: None,
9149 }
9150 )),
9151 target_name: Some("fuchsia.logger.Log2".to_string()),
9152 source_instance_filter: Some(vec!["default".to_string()]),
9153 ..Default::default()
9154 })
9155 ]);
9156 decl.collections = Some(vec![
9157 fdecl::Collection {
9158 name: Some("coll_a".to_string()),
9159 durability: Some(fdecl::Durability::Transient),
9160 ..Default::default()
9161 },
9162 fdecl::Collection {
9163 name: Some("coll_b".to_string()),
9164 durability: Some(fdecl::Durability::Transient),
9165 ..Default::default()
9166 },
9167 ]);
9168 decl.children = Some(vec![
9169 fdecl::Child {
9170 name: Some("child_c".to_string()),
9171 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9172 startup: Some(fdecl::StartupMode::Lazy),
9173 on_terminate: None,
9174 environment: None,
9175 ..Default::default()
9176 },
9177 ]);
9178 decl
9179 },
9180 result = Err(ErrorList::new(vec![
9181 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."),
9182 ])),
9183 },
9184
9185 test_validate_resolvers_missing_from_offer => {
9186 input = {
9187 let mut decl = new_component_decl();
9188 decl.offers = Some(vec![fdecl::Offer::Resolver(fdecl::OfferResolver {
9189 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
9190 source_name: Some("a".to_string()),
9191 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
9192 target_name: Some("a".to_string()),
9193 ..Default::default()
9194 })]);
9195 decl.children = Some(vec![fdecl::Child {
9196 name: Some("child".to_string()),
9197 url: Some("test:///child".to_string()),
9198 startup: Some(fdecl::StartupMode::Eager),
9199 on_terminate: None,
9200 environment: None,
9201 ..Default::default()
9202 }]);
9203 decl
9204 },
9205 result = Err(ErrorList::new(vec![
9206 Error::invalid_capability(DeclType::OfferResolver, "source", "a"),
9207 ])),
9208 },
9209 test_validate_resolvers_missing_from_expose => {
9210 input = {
9211 let mut decl = new_component_decl();
9212 decl.exposes = Some(vec![fdecl::Expose::Resolver(fdecl::ExposeResolver {
9213 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
9214 source_name: Some("a".to_string()),
9215 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
9216 target_name: Some("a".to_string()),
9217 ..Default::default()
9218 })]);
9219 decl
9220 },
9221 result = Err(ErrorList::new(vec![
9222 Error::invalid_capability(DeclType::ExposeResolver, "source", "a"),
9223 ])),
9224 },
9225
9226 test_validate_config_missing_config => {
9227 input = {
9228 let mut decl = new_component_decl();
9229 decl.config = Some(fdecl::ConfigSchema{
9230 fields: None,
9231 checksum: None,
9232 value_source: None,
9233 ..Default::default()
9234 });
9235 decl
9236 },
9237 result = Err(ErrorList::new(vec![
9238 Error::missing_field(DeclType::ConfigSchema, "fields"),
9239 Error::missing_field(DeclType::ConfigSchema, "checksum"),
9240 Error::missing_field(DeclType::ConfigSchema, "value_source"),
9241 ])),
9242 },
9243
9244 test_validate_config_missing_config_field => {
9245 input = {
9246 let mut decl = new_component_decl();
9247 decl.config = Some(fdecl::ConfigSchema{
9248 fields: Some(vec![
9249 fdecl::ConfigField {
9250 key: None,
9251 type_: None,
9252 ..Default::default()
9253 }
9254 ]),
9255 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9256 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9257 ..Default::default()
9258 });
9259 decl
9260 },
9261 result = Err(ErrorList::new(vec![
9262 Error::missing_field(DeclType::ConfigField, "key"),
9263 Error::missing_field(DeclType::ConfigField, "value_type"),
9264 ])),
9265 },
9266
9267 test_validate_config_bool => {
9268 input = {
9269 let mut decl = new_component_decl();
9270 decl.config = Some(fdecl::ConfigSchema{
9271 fields: Some(vec![
9272 fdecl::ConfigField {
9273 key: Some("test".to_string()),
9274 type_: Some(fdecl::ConfigType {
9275 layout: fdecl::ConfigTypeLayout::Bool,
9276 parameters: Some(vec![]),
9277 constraints: vec![]
9278 }),
9279 ..Default::default()
9280 }
9281 ]),
9282 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9283 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9284 ..Default::default()
9285 });
9286 decl
9287 },
9288 result = Ok(()),
9289 },
9290
9291 test_validate_config_bool_extra_constraint => {
9292 input = {
9293 let mut decl = new_component_decl();
9294 decl.config = Some(fdecl::ConfigSchema{
9295 fields: Some(vec![
9296 fdecl::ConfigField {
9297 key: Some("test".to_string()),
9298 type_: Some(fdecl::ConfigType {
9299 layout: fdecl::ConfigTypeLayout::Bool,
9300 parameters: Some(vec![]),
9301 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9302 }),
9303 ..Default::default()
9304 }
9305 ]),
9306 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9307 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9308 ..Default::default()
9309 });
9310 decl
9311 },
9312 result = Err(ErrorList::new(vec![
9313 Error::extraneous_field(DeclType::ConfigType, "constraints")
9314 ])),
9315 },
9316
9317 test_validate_config_bool_missing_parameters => {
9318 input = {
9319 let mut decl = new_component_decl();
9320 decl.config = Some(fdecl::ConfigSchema{
9321 fields: Some(vec![
9322 fdecl::ConfigField {
9323 key: Some("test".to_string()),
9324 type_: Some(fdecl::ConfigType {
9325 layout: fdecl::ConfigTypeLayout::Bool,
9326 parameters: None,
9327 constraints: vec![]
9328 }),
9329 ..Default::default()
9330 }
9331 ]),
9332 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9333 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9334 ..Default::default()
9335 });
9336 decl
9337 },
9338 result = Err(ErrorList::new(vec![
9339 Error::missing_field(DeclType::ConfigType, "parameters")
9340 ])),
9341 },
9342
9343 test_validate_config_string => {
9344 input = {
9345 let mut decl = new_component_decl();
9346 decl.config = Some(fdecl::ConfigSchema{
9347 fields: Some(vec![
9348 fdecl::ConfigField {
9349 key: Some("test".to_string()),
9350 type_: Some(fdecl::ConfigType {
9351 layout: fdecl::ConfigTypeLayout::String,
9352 parameters: Some(vec![]),
9353 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9354 }),
9355 ..Default::default()
9356 }
9357 ]),
9358 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9359 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9360 ..Default::default()
9361 });
9362 decl
9363 },
9364 result = Ok(()),
9365 },
9366
9367 test_validate_config_string_missing_parameter => {
9368 input = {
9369 let mut decl = new_component_decl();
9370 decl.config = Some(fdecl::ConfigSchema{
9371 fields: Some(vec![
9372 fdecl::ConfigField {
9373 key: Some("test".to_string()),
9374 type_: Some(fdecl::ConfigType {
9375 layout: fdecl::ConfigTypeLayout::String,
9376 parameters: None,
9377 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9378 }),
9379 ..Default::default()
9380 }
9381 ]),
9382 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9383 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9384 ..Default::default()
9385 });
9386 decl
9387 },
9388 result = Err(ErrorList::new(vec![
9389 Error::missing_field(DeclType::ConfigType, "parameters")
9390 ])),
9391 },
9392
9393 test_validate_config_string_missing_constraint => {
9394 input = {
9395 let mut decl = new_component_decl();
9396 decl.config = Some(fdecl::ConfigSchema{
9397 fields: Some(vec![
9398 fdecl::ConfigField {
9399 key: Some("test".to_string()),
9400 type_: Some(fdecl::ConfigType {
9401 layout: fdecl::ConfigTypeLayout::String,
9402 parameters: Some(vec![]),
9403 constraints: vec![]
9404 }),
9405 ..Default::default()
9406 }
9407 ]),
9408 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9409 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9410 ..Default::default()
9411 });
9412 decl
9413 },
9414 result = Err(ErrorList::new(vec![
9415 Error::missing_field(DeclType::ConfigType, "constraints")
9416 ])),
9417 },
9418
9419 test_validate_config_string_extra_constraint => {
9420 input = {
9421 let mut decl = new_component_decl();
9422 decl.config = Some(fdecl::ConfigSchema{
9423 fields: Some(vec![
9424 fdecl::ConfigField {
9425 key: Some("test".to_string()),
9426 type_: Some(fdecl::ConfigType {
9427 layout: fdecl::ConfigTypeLayout::String,
9428 parameters: Some(vec![]),
9429 constraints: vec![fdecl::LayoutConstraint::MaxSize(10), fdecl::LayoutConstraint::MaxSize(10)]
9430 }),
9431 ..Default::default()
9432 }
9433 ]),
9434 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9435 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9436 ..Default::default()
9437 });
9438 decl
9439 },
9440 result = Err(ErrorList::new(vec![
9441 Error::extraneous_field(DeclType::ConfigType, "constraints")
9442 ])),
9443 },
9444
9445 test_validate_config_vector_bool => {
9446 input = {
9447 let mut decl = new_component_decl();
9448 decl.config = Some(fdecl::ConfigSchema{
9449 fields: Some(vec![
9450 fdecl::ConfigField {
9451 key: Some("test".to_string()),
9452 type_: Some(fdecl::ConfigType {
9453 layout: fdecl::ConfigTypeLayout::Vector,
9454 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9455 layout: fdecl::ConfigTypeLayout::Bool,
9456 parameters: Some(vec![]),
9457 constraints: vec![],
9458 })]),
9459 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9460 }),
9461 ..Default::default()
9462 }
9463 ]),
9464 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9465 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9466 ..Default::default()
9467 });
9468 decl
9469 },
9470 result = Ok(()),
9471 },
9472
9473 test_validate_config_vector_extra_parameter => {
9474 input = {
9475 let mut decl = new_component_decl();
9476 decl.config = Some(fdecl::ConfigSchema{
9477 fields: Some(vec![
9478 fdecl::ConfigField {
9479 key: Some("test".to_string()),
9480 type_: Some(fdecl::ConfigType {
9481 layout: fdecl::ConfigTypeLayout::Vector,
9482 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9483 layout: fdecl::ConfigTypeLayout::Bool,
9484 parameters: Some(vec![]),
9485 constraints: vec![],
9486 }), fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9487 layout: fdecl::ConfigTypeLayout::Uint8,
9488 parameters: Some(vec![]),
9489 constraints: vec![],
9490 })]),
9491 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9492 }),
9493 ..Default::default()
9494 }
9495 ]),
9496 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9497 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9498 ..Default::default()
9499 });
9500 decl
9501 },
9502 result = Err(ErrorList::new(vec![
9503 Error::extraneous_field(DeclType::ConfigType, "parameters")
9504 ])),
9505 },
9506
9507 test_validate_config_vector_missing_parameter => {
9508 input = {
9509 let mut decl = new_component_decl();
9510 decl.config = Some(fdecl::ConfigSchema{
9511 fields: Some(vec![
9512 fdecl::ConfigField {
9513 key: Some("test".to_string()),
9514 type_: Some(fdecl::ConfigType {
9515 layout: fdecl::ConfigTypeLayout::Vector,
9516 parameters: Some(vec![]),
9517 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9518 }),
9519 ..Default::default()
9520 }
9521 ]),
9522 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9523 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9524 ..Default::default()
9525 });
9526 decl
9527 },
9528 result = Err(ErrorList::new(vec![
9529 Error::missing_field(DeclType::ConfigType, "parameters")
9530 ])),
9531 },
9532
9533 test_validate_config_vector_string => {
9534 input = {
9535 let mut decl = new_component_decl();
9536 decl.config = Some(fdecl::ConfigSchema{
9537 fields: Some(vec![
9538 fdecl::ConfigField {
9539 key: Some("test".to_string()),
9540 type_: Some(fdecl::ConfigType {
9541 layout: fdecl::ConfigTypeLayout::Vector,
9542 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9543 layout: fdecl::ConfigTypeLayout::String,
9544 parameters: Some(vec![]),
9545 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9546 })]),
9547 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9548 }),
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 = Ok(()),
9559 },
9560
9561 test_validate_config_vector_vector => {
9562 input = {
9563 let mut decl = new_component_decl();
9564 decl.config = Some(fdecl::ConfigSchema{
9565 fields: Some(vec![
9566 fdecl::ConfigField {
9567 key: Some("test".to_string()),
9568 type_: Some(fdecl::ConfigType {
9569 layout: fdecl::ConfigTypeLayout::Vector,
9570 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9571 layout: fdecl::ConfigTypeLayout::Vector,
9572 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9573 layout: fdecl::ConfigTypeLayout::String,
9574 parameters: Some(vec![]),
9575 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9576 })]),
9577 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9578 })]),
9579 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9580 }),
9581 ..Default::default()
9582 }
9583 ]),
9584 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9585 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9586 ..Default::default()
9587 });
9588 decl
9589 },
9590 result = Err(ErrorList::new(vec![
9591 Error::nested_vector()
9592 ])),
9593 },
9594
9595 test_validate_exposes_invalid_aggregation_different_availability => {
9596 input = {
9597 let mut decl = new_component_decl();
9598 decl.exposes = Some(vec![
9599 fdecl::Expose::Service(fdecl::ExposeService {
9600 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9601 name: "coll_a".into()
9602 })),
9603 source_name: Some("fuchsia.logger.Log".to_string()),
9604 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
9605 target_name: Some("fuchsia.logger.Log".to_string()),
9606 availability: Some(fdecl::Availability::Required),
9607 ..Default::default()
9608 }),
9609 fdecl::Expose::Service(fdecl::ExposeService {
9610 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9611 name: "coll_b".into()
9612 })),
9613 source_name: Some("fuchsia.logger.Log".to_string()),
9614 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
9615 target_name: Some("fuchsia.logger.Log".to_string()),
9616 availability: Some(fdecl::Availability::Optional),
9617 ..Default::default()
9618 })
9619 ]);
9620 decl.collections = Some(vec![
9621 fdecl::Collection {
9622 name: Some("coll_a".to_string()),
9623 durability: Some(fdecl::Durability::Transient),
9624 ..Default::default()
9625 },
9626 fdecl::Collection {
9627 name: Some("coll_b".to_string()),
9628 durability: Some(fdecl::Durability::Transient),
9629 ..Default::default()
9630 },
9631 ]);
9632 decl
9633 },
9634 result = Err(ErrorList::new(vec![
9635 Error::DifferentAvailabilityInAggregation(AvailabilityList(vec![
9636 fdecl::Availability::Required,
9637 fdecl::Availability::Optional,
9638 ]))
9639 ])),
9640 },
9641
9642 test_validate_offers_invalid_aggregation_different_availability => {
9643 input = {
9644 let mut decl = new_component_decl();
9645 decl.offers = Some(vec![
9646 fdecl::Offer::Service(fdecl::OfferService {
9647 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9648 name: "coll_a".into()
9649 })),
9650 source_name: Some("fuchsia.logger.Log".to_string()),
9651 target: Some(fdecl::Ref::Child(
9652 fdecl::ChildRef {
9653 name: "child_c".to_string(),
9654 collection: None,
9655 }
9656 )),
9657 target_name: Some("fuchsia.logger.Log".to_string()),
9658 source_instance_filter: Some(vec!["default".to_string()]),
9659 availability: Some(fdecl::Availability::Required),
9660 ..Default::default()
9661 }),
9662 fdecl::Offer::Service(fdecl::OfferService {
9663 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9664 name: "coll_b".into()
9665 })),
9666 source_name: Some("fuchsia.logger.Log".to_string()),
9667 target: Some(fdecl::Ref::Child(
9668 fdecl::ChildRef {
9669 name: "child_c".to_string(),
9670 collection: None,
9671 }
9672 )),
9673 target_name: Some("fuchsia.logger.Log".to_string()),
9674 source_instance_filter: Some(vec!["a_different_default".to_string()]),
9675 availability: Some(fdecl::Availability::Optional),
9676 ..Default::default()
9677 })
9678 ]);
9679 decl.collections = Some(vec![
9680 fdecl::Collection {
9681 name: Some("coll_a".to_string()),
9682 durability: Some(fdecl::Durability::Transient),
9683 ..Default::default()
9684 },
9685 fdecl::Collection {
9686 name: Some("coll_b".to_string()),
9687 durability: Some(fdecl::Durability::Transient),
9688 ..Default::default()
9689 },
9690 ]);
9691 decl.children = Some(vec![
9692 fdecl::Child {
9693 name: Some("child_c".to_string()),
9694 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9695 startup: Some(fdecl::StartupMode::Lazy),
9696 on_terminate: None,
9697 environment: None,
9698 ..Default::default()
9699 },
9700 ]);
9701 decl
9702 },
9703 result = Err(ErrorList::new(vec![
9704 Error::DifferentAvailabilityInAggregation(AvailabilityList(vec![
9705 fdecl::Availability::Required,
9706 fdecl::Availability::Optional,
9707 ]))
9708 ])),
9709 },
9710 }
9711
9712 test_validate_capabilities! {
9713 test_validate_capabilities_individually_ok => {
9714 input = vec![
9715 fdecl::Capability::Protocol(fdecl::Protocol {
9716 name: Some("foo_svc".into()),
9717 source_path: Some("/svc/foo".into()),
9718 ..Default::default()
9719 }),
9720 fdecl::Capability::Directory(fdecl::Directory {
9721 name: Some("foo_dir".into()),
9722 source_path: Some("/foo".into()),
9723 rights: Some(fio::Operations::CONNECT),
9724 ..Default::default()
9725 }),
9726 ],
9727 as_builtin = false,
9728 result = Ok(()),
9729 },
9730 test_validate_capabilities_individually_err => {
9731 input = vec![
9732 fdecl::Capability::Protocol(fdecl::Protocol {
9733 name: None,
9734 source_path: None,
9735 ..Default::default()
9736 }),
9737 fdecl::Capability::Directory(fdecl::Directory {
9738 name: None,
9739 source_path: None,
9740 rights: None,
9741 ..Default::default()
9742 }),
9743 ],
9744 as_builtin = false,
9745 result = Err(ErrorList::new(vec![
9746 Error::missing_field(DeclType::Protocol, "name"),
9747 Error::missing_field(DeclType::Protocol, "source_path"),
9748 Error::missing_field(DeclType::Directory, "name"),
9749 Error::missing_field(DeclType::Directory, "source_path"),
9750 Error::missing_field(DeclType::Directory, "rights"),
9751 ])),
9752 },
9753 test_validate_builtin_capabilities_individually_ok => {
9754 input = vec![
9755 fdecl::Capability::Protocol(fdecl::Protocol {
9756 name: Some("foo_protocol".into()),
9757 source_path: None,
9758 ..Default::default()
9759 }),
9760 fdecl::Capability::Directory(fdecl::Directory {
9761 name: Some("foo_dir".into()),
9762 source_path: None,
9763 rights: Some(fio::Operations::CONNECT),
9764 ..Default::default()
9765 }),
9766 fdecl::Capability::Service(fdecl::Service {
9767 name: Some("foo_svc".into()),
9768 source_path: None,
9769 ..Default::default()
9770 }),
9771 fdecl::Capability::Runner(fdecl::Runner {
9772 name: Some("foo_runner".into()),
9773 source_path: None,
9774 ..Default::default()
9775 }),
9776 fdecl::Capability::Resolver(fdecl::Resolver {
9777 name: Some("foo_resolver".into()),
9778 source_path: None,
9779 ..Default::default()
9780 }),
9781 ],
9782 as_builtin = true,
9783 result = Ok(()),
9784 },
9785 test_validate_builtin_capabilities_individually_err => {
9786 input = vec![
9787 fdecl::Capability::Protocol(fdecl::Protocol {
9788 name: None,
9789 source_path: Some("/svc/foo".into()),
9790 ..Default::default()
9791 }),
9792 fdecl::Capability::Directory(fdecl::Directory {
9793 name: None,
9794 source_path: Some("/foo".into()),
9795 rights: None,
9796 ..Default::default()
9797 }),
9798 fdecl::Capability::Service(fdecl::Service {
9799 name: None,
9800 source_path: Some("/svc/foo".into()),
9801 ..Default::default()
9802 }),
9803 fdecl::Capability::Runner(fdecl::Runner {
9804 name: None,
9805 source_path: Some("/foo".into()),
9806 ..Default::default()
9807 }),
9808 fdecl::Capability::Resolver(fdecl::Resolver {
9809 name: None,
9810 source_path: Some("/foo".into()),
9811 ..Default::default()
9812 }),
9813 fdecl::Capability::Storage(fdecl::Storage {
9814 name: None,
9815 ..Default::default()
9816 }),
9817 ],
9818 as_builtin = true,
9819 result = Err(ErrorList::new(vec![
9820 Error::missing_field(DeclType::Protocol, "name"),
9821 Error::extraneous_source_path(DeclType::Protocol, "/svc/foo"),
9822 Error::missing_field(DeclType::Directory, "name"),
9823 Error::extraneous_source_path(DeclType::Directory, "/foo"),
9824 Error::missing_field(DeclType::Directory, "rights"),
9825 Error::missing_field(DeclType::Service, "name"),
9826 Error::extraneous_source_path(DeclType::Service, "/svc/foo"),
9827 Error::missing_field(DeclType::Runner, "name"),
9828 Error::extraneous_source_path(DeclType::Runner, "/foo"),
9829 Error::missing_field(DeclType::Resolver, "name"),
9830 Error::extraneous_source_path(DeclType::Resolver, "/foo"),
9831 Error::CapabilityCannotBeBuiltin(DeclType::Storage),
9832 ])),
9833 },
9834 test_validate_delivery_type_ok => {
9835 input = vec![
9836 fdecl::Capability::Protocol(fdecl::Protocol {
9837 name: Some("foo_svc1".into()),
9838 source_path: Some("/svc/foo1".into()),
9839 ..Default::default()
9840 }),
9841 fdecl::Capability::Protocol(fdecl::Protocol {
9842 name: Some("foo_svc2".into()),
9843 source_path: Some("/svc/foo2".into()),
9844 delivery: Some(fdecl::DeliveryType::Immediate),
9845 ..Default::default()
9846 }),
9847 fdecl::Capability::Protocol(fdecl::Protocol {
9848 name: Some("foo_svc3".into()),
9849 source_path: Some("/svc/foo3".into()),
9850 delivery: Some(fdecl::DeliveryType::OnReadable),
9851 ..Default::default()
9852 }),
9853 ],
9854 as_builtin = false,
9855 result = Ok(()),
9856 },
9857 test_validate_delivery_type_err => {
9858 input = vec![
9859 fdecl::Capability::Protocol(fdecl::Protocol {
9860 name: Some("foo_svc".into()),
9861 source_path: Some("/svc/foo".into()),
9862 delivery: Some(fdecl::DeliveryType::unknown()),
9863 ..Default::default()
9864 }),
9865 ],
9866 as_builtin = false,
9867 result = Err(ErrorList::new(vec![
9868 Error::invalid_field(DeclType::Protocol, "delivery"),
9869 ])),
9870 },
9871 }
9872
9873 fn generate_expose_different_source_and_availability_decl(
9876 new_expose: impl Fn(fdecl::Ref, fdecl::Availability, &str) -> fdecl::Expose,
9877 ) -> fdecl::Component {
9878 let mut decl = new_component_decl();
9879 let child = "child";
9880 decl.children = Some(vec![fdecl::Child {
9881 name: Some(child.to_string()),
9882 url: Some("fuchsia-pkg://fuchsia.com/source#meta/source.cm".to_string()),
9883 startup: Some(fdecl::StartupMode::Lazy),
9884 ..Default::default()
9885 }]);
9886 decl.exposes = Some(vec![
9887 new_expose(
9889 fdecl::Ref::Self_(fdecl::SelfRef {}),
9890 fdecl::Availability::Optional,
9891 "fuchsia.examples.Echo1",
9892 ),
9893 new_expose(
9895 fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
9896 fdecl::Availability::Optional,
9897 "fuchsia.examples.Echo2",
9898 ),
9899 new_expose(
9901 fdecl::Ref::VoidType(fdecl::VoidRef {}),
9902 fdecl::Availability::Optional,
9903 "fuchsia.examples.Echo3",
9904 ),
9905 new_expose(
9907 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
9908 fdecl::Availability::Optional,
9909 "fuchsia.examples.Echo4",
9910 ),
9911 new_expose(
9913 fdecl::Ref::Self_(fdecl::SelfRef {}),
9914 fdecl::Availability::Transitional,
9915 "fuchsia.examples.Echo5",
9916 ),
9917 new_expose(
9919 fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
9920 fdecl::Availability::Transitional,
9921 "fuchsia.examples.Echo6",
9922 ),
9923 new_expose(
9925 fdecl::Ref::VoidType(fdecl::VoidRef {}),
9926 fdecl::Availability::Transitional,
9927 "fuchsia.examples.Echo7",
9928 ),
9929 new_expose(
9931 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
9932 fdecl::Availability::Transitional,
9933 "fuchsia.examples.Echo8",
9934 ),
9935 new_expose(
9937 fdecl::Ref::Self_(fdecl::SelfRef {}),
9938 fdecl::Availability::SameAsTarget,
9939 "fuchsia.examples.Echo9",
9940 ),
9941 new_expose(
9943 fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
9944 fdecl::Availability::SameAsTarget,
9945 "fuchsia.examples.Echo10",
9946 ),
9947 new_expose(
9949 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
9950 fdecl::Availability::SameAsTarget,
9951 "fuchsia.examples.Echo11",
9952 ),
9953 new_expose(
9955 fdecl::Ref::VoidType(fdecl::VoidRef {}),
9956 fdecl::Availability::Required,
9957 "fuchsia.examples.Echo12",
9958 ),
9959 new_expose(
9961 fdecl::Ref::VoidType(fdecl::VoidRef {}),
9962 fdecl::Availability::SameAsTarget,
9963 "fuchsia.examples.Echo13",
9964 ),
9965 ]);
9966 decl
9967 }
9968
9969 fn generate_offer_different_source_and_availability_decl(
9972 new_offer: impl Fn(fdecl::Ref, fdecl::Availability, &str) -> fdecl::Offer,
9973 ) -> fdecl::Component {
9974 let mut decl = new_component_decl();
9975 decl.children = Some(vec![
9976 fdecl::Child {
9977 name: Some("source".to_string()),
9978 url: Some("fuchsia-pkg://fuchsia.com/source#meta/source.cm".to_string()),
9979 startup: Some(fdecl::StartupMode::Lazy),
9980 on_terminate: None,
9981 environment: None,
9982 ..Default::default()
9983 },
9984 fdecl::Child {
9985 name: Some("sink".to_string()),
9986 url: Some("fuchsia-pkg://fuchsia.com/sink#meta/sink.cm".to_string()),
9987 startup: Some(fdecl::StartupMode::Lazy),
9988 on_terminate: None,
9989 environment: None,
9990 ..Default::default()
9991 },
9992 ]);
9993 decl.offers = Some(vec![
9994 new_offer(
9997 fdecl::Ref::Parent(fdecl::ParentRef {}),
9998 fdecl::Availability::Required,
9999 "fuchsia.examples.Echo0",
10000 ),
10001 new_offer(
10002 fdecl::Ref::Parent(fdecl::ParentRef {}),
10003 fdecl::Availability::Optional,
10004 "fuchsia.examples.Echo1",
10005 ),
10006 new_offer(
10007 fdecl::Ref::Parent(fdecl::ParentRef {}),
10008 fdecl::Availability::SameAsTarget,
10009 "fuchsia.examples.Echo2",
10010 ),
10011 new_offer(
10012 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10013 fdecl::Availability::Optional,
10014 "fuchsia.examples.Echo3",
10015 ),
10016 new_offer(
10019 fdecl::Ref::Self_(fdecl::SelfRef {}),
10020 fdecl::Availability::Optional,
10021 "fuchsia.examples.Echo4",
10022 ),
10023 new_offer(
10024 fdecl::Ref::Self_(fdecl::SelfRef {}),
10025 fdecl::Availability::SameAsTarget,
10026 "fuchsia.examples.Echo5",
10027 ),
10028 new_offer(
10029 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10030 fdecl::Availability::Optional,
10031 "fuchsia.examples.Echo6",
10032 ),
10033 new_offer(
10034 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10035 fdecl::Availability::SameAsTarget,
10036 "fuchsia.examples.Echo7",
10037 ),
10038 new_offer(
10039 fdecl::Ref::Child(fdecl::ChildRef { name: "source".to_string(), collection: None }),
10040 fdecl::Availability::Optional,
10041 "fuchsia.examples.Echo8",
10042 ),
10043 new_offer(
10044 fdecl::Ref::Child(fdecl::ChildRef { name: "source".to_string(), collection: None }),
10045 fdecl::Availability::SameAsTarget,
10046 "fuchsia.examples.Echo9",
10047 ),
10048 new_offer(
10050 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10051 fdecl::Availability::Required,
10052 "fuchsia.examples.Echo10",
10053 ),
10054 new_offer(
10055 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10056 fdecl::Availability::SameAsTarget,
10057 "fuchsia.examples.Echo11",
10058 ),
10059 ]);
10060 decl
10061 }
10062
10063 #[test]
10064 fn test_validate_dynamic_offers_empty() {
10065 assert_eq!(validate_dynamic_offers(vec![], &vec![], &fdecl::Component::default()), Ok(()));
10066 }
10067
10068 #[test]
10069 fn test_validate_dynamic_offers_okay() {
10070 assert_eq!(
10071 validate_dynamic_offers(
10072 vec![],
10073 &vec![
10074 fdecl::Offer::Protocol(fdecl::OfferProtocol {
10075 dependency_type: Some(fdecl::DependencyType::Strong),
10076 source: Some(fdecl::Ref::Self_(fdecl::SelfRef)),
10077 source_name: Some("thing".to_string()),
10078 target_name: Some("thing".repeat(26)),
10079 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10080 name: "foo".to_string(),
10081 collection: Some("foo".to_string()),
10082 })),
10083 ..Default::default()
10084 }),
10085 fdecl::Offer::Service(fdecl::OfferService {
10086 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10087 source_name: Some("thang".repeat(26)),
10088 target_name: Some("thang".repeat(26)),
10089 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10090 name: "foo".to_string(),
10091 collection: Some("foo".to_string()),
10092 })),
10093 ..Default::default()
10094 }),
10095 fdecl::Offer::Directory(fdecl::OfferDirectory {
10096 dependency_type: Some(fdecl::DependencyType::Strong),
10097 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10098 source_name: Some("thung1".repeat(26)),
10099 target_name: Some("thung1".repeat(26)),
10100 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10101 name: "foo".to_string(),
10102 collection: Some("foo".to_string()),
10103 })),
10104 ..Default::default()
10105 }),
10106 fdecl::Offer::Storage(fdecl::OfferStorage {
10107 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10108 source_name: Some("thung2".repeat(26)),
10109 target_name: Some("thung2".repeat(26)),
10110 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10111 name: "foo".to_string(),
10112 collection: Some("foo".to_string()),
10113 })),
10114 ..Default::default()
10115 }),
10116 fdecl::Offer::Runner(fdecl::OfferRunner {
10117 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10118 source_name: Some("thung3".repeat(26)),
10119 target_name: Some("thung3".repeat(26)),
10120 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10121 name: "foo".to_string(),
10122 collection: Some("foo".to_string()),
10123 })),
10124 ..Default::default()
10125 }),
10126 fdecl::Offer::Resolver(fdecl::OfferResolver {
10127 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10128 source_name: Some("thung4".repeat(26)),
10129 target_name: Some("thung4".repeat(26)),
10130 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10131 name: "foo".to_string(),
10132 collection: Some("foo".to_string()),
10133 })),
10134 ..Default::default()
10135 }),
10136 ],
10137 &fdecl::Component {
10138 capabilities: Some(vec![fdecl::Capability::Protocol(fdecl::Protocol {
10139 name: Some("thing".to_string()),
10140 source_path: Some("/svc/foo".into()),
10141 ..Default::default()
10142 }),]),
10143 ..Default::default()
10144 }
10145 ),
10146 Ok(())
10147 );
10148 }
10149
10150 #[test]
10151 fn test_validate_dynamic_offers_valid_service_aggregation() {
10152 assert_eq!(
10153 validate_dynamic_offers(
10154 vec![],
10155 &vec![
10156 fdecl::Offer::Service(fdecl::OfferService {
10157 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10158 name: "child_a".to_string(),
10159 collection: None
10160 })),
10161 source_name: Some("fuchsia.logger.Log".to_string()),
10162 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10163 name: "child_c".to_string(),
10164 collection: None,
10165 })),
10166 target_name: Some("fuchsia.logger.Log".to_string()),
10167 source_instance_filter: Some(vec!["default".to_string()]),
10168 ..Default::default()
10169 }),
10170 fdecl::Offer::Service(fdecl::OfferService {
10171 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10172 name: "child_b".to_string(),
10173 collection: None
10174 })),
10175 source_name: Some("fuchsia.logger.Log".to_string()),
10176 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10177 name: "child_c".to_string(),
10178 collection: None,
10179 })),
10180 target_name: Some("fuchsia.logger.Log".to_string()),
10181 source_instance_filter: Some(vec!["a_different_default".to_string()]),
10182 ..Default::default()
10183 })
10184 ],
10185 &fdecl::Component {
10186 children: Some(vec![
10187 fdecl::Child {
10188 name: Some("child_a".to_string()),
10189 url: Some(
10190 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10191 .to_string()
10192 ),
10193 startup: Some(fdecl::StartupMode::Lazy),
10194 on_terminate: None,
10195 environment: None,
10196 ..Default::default()
10197 },
10198 fdecl::Child {
10199 name: Some("child_b".to_string()),
10200 url: Some(
10201 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10202 .to_string()
10203 ),
10204 startup: Some(fdecl::StartupMode::Lazy),
10205 on_terminate: None,
10206 environment: None,
10207 ..Default::default()
10208 },
10209 fdecl::Child {
10210 name: Some("child_c".to_string()),
10211 url: Some(
10212 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10213 .to_string()
10214 ),
10215 startup: Some(fdecl::StartupMode::Lazy),
10216 on_terminate: None,
10217 environment: None,
10218 ..Default::default()
10219 },
10220 ]),
10221 ..Default::default()
10222 }
10223 ),
10224 Ok(())
10225 );
10226 }
10227
10228 #[test]
10229 fn test_validate_dynamic_service_aggregation_missing_filter() {
10230 assert_eq!(
10231 validate_dynamic_offers(
10232 vec![],
10233 &vec![
10234 fdecl::Offer::Service(fdecl::OfferService {
10235 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10236 name: "child_a".to_string(),
10237 collection: None
10238 })),
10239 source_name: Some("fuchsia.logger.Log".to_string()),
10240 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10241 name: "child_c".to_string(),
10242 collection: None,
10243 })),
10244 target_name: Some("fuchsia.logger.Log".to_string()),
10245 source_instance_filter: Some(vec!["default".to_string()]),
10246 ..Default::default()
10247 }),
10248 fdecl::Offer::Service(fdecl::OfferService {
10249 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10250 name: "child_b".to_string(),
10251 collection: None
10252 })),
10253 source_name: Some("fuchsia.logger.Log".to_string()),
10254 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10255 name: "child_c".to_string(),
10256 collection: None,
10257 })),
10258 target_name: Some("fuchsia.logger.Log".to_string()),
10259 source_instance_filter: None,
10260 ..Default::default()
10261 }),
10262 ],
10263 &fdecl::Component {
10264 children: Some(vec![
10265 fdecl::Child {
10266 name: Some("child_a".to_string()),
10267 url: Some(
10268 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10269 .to_string()
10270 ),
10271 startup: Some(fdecl::StartupMode::Lazy),
10272 ..Default::default()
10273 },
10274 fdecl::Child {
10275 name: Some("child_b".to_string()),
10276 url: Some(
10277 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10278 .to_string()
10279 ),
10280 startup: Some(fdecl::StartupMode::Lazy),
10281 ..Default::default()
10282 },
10283 fdecl::Child {
10284 name: Some("child_c".to_string()),
10285 url: Some(
10286 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10287 .to_string()
10288 ),
10289 startup: Some(fdecl::StartupMode::Lazy),
10290 ..Default::default()
10291 },
10292 ]),
10293 ..Default::default()
10294 },
10295 ),
10296 Err(ErrorList::new(vec![Error::invalid_aggregate_offer(
10297 "source_instance_filter must be set for dynamic aggregate service offers"
10298 ),]))
10299 );
10300 }
10301
10302 #[test]
10303 fn test_validate_dynamic_offers_omit_target() {
10304 assert_eq!(
10305 validate_dynamic_offers(
10306 vec![],
10307 &vec![
10308 fdecl::Offer::Protocol(fdecl::OfferProtocol {
10309 dependency_type: Some(fdecl::DependencyType::Strong),
10310 source: Some(fdecl::Ref::Self_(fdecl::SelfRef)),
10311 source_name: Some("thing".to_string()),
10312 target_name: Some("thing".to_string()),
10313 ..Default::default()
10314 }),
10315 fdecl::Offer::Service(fdecl::OfferService {
10316 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10317 source_name: Some("thang".to_string()),
10318 target_name: Some("thang".to_string()),
10319 ..Default::default()
10320 }),
10321 fdecl::Offer::Directory(fdecl::OfferDirectory {
10322 dependency_type: Some(fdecl::DependencyType::Strong),
10323 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10324 source_name: Some("thung1".to_string()),
10325 target_name: Some("thung1".to_string()),
10326 ..Default::default()
10327 }),
10328 fdecl::Offer::Storage(fdecl::OfferStorage {
10329 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10330 source_name: Some("thung2".to_string()),
10331 target_name: Some("thung2".to_string()),
10332 ..Default::default()
10333 }),
10334 fdecl::Offer::Runner(fdecl::OfferRunner {
10335 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10336 source_name: Some("thung3".to_string()),
10337 target_name: Some("thung3".to_string()),
10338 ..Default::default()
10339 }),
10340 fdecl::Offer::Resolver(fdecl::OfferResolver {
10341 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10342 source_name: Some("thung4".to_string()),
10343 target_name: Some("thung4".to_string()),
10344 ..Default::default()
10345 }),
10346 ],
10347 &fdecl::Component {
10348 capabilities: Some(vec![fdecl::Capability::Protocol(fdecl::Protocol {
10349 name: Some("thing".to_string()),
10350 source_path: Some("/svc/foo".into()),
10351 ..Default::default()
10352 }),]),
10353 ..Default::default()
10354 }
10355 ),
10356 Err(ErrorList::new(vec![
10357 Error::missing_field(DeclType::OfferProtocol, "target"),
10358 Error::missing_field(DeclType::OfferService, "target"),
10359 Error::missing_field(DeclType::OfferDirectory, "target"),
10360 Error::missing_field(DeclType::OfferStorage, "target"),
10361 Error::missing_field(DeclType::OfferRunner, "target"),
10362 Error::missing_field(DeclType::OfferResolver, "target"),
10363 ]))
10364 );
10365 }
10366
10367 #[test]
10368 fn test_validate_dynamic_offers_collection_collision() {
10369 assert_eq!(
10370 validate_dynamic_offers(
10371 vec![],
10372 &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10373 dependency_type: Some(fdecl::DependencyType::Strong),
10374 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10375 source_name: Some("thing".to_string()),
10376 target_name: Some("thing".to_string()),
10377 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10378 name: "child".to_string(),
10379 collection: Some("coll".to_string()),
10380 })),
10381 ..Default::default()
10382 }),],
10383 &fdecl::Component {
10384 offers: Some(vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10385 dependency_type: Some(fdecl::DependencyType::Strong),
10386 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10387 source_name: Some("thing".to_string()),
10388 target_name: Some("thing".to_string()),
10389 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10390 name: "coll".into()
10391 })),
10392 ..Default::default()
10393 }),]),
10394 collections: Some(vec![fdecl::Collection {
10395 name: Some("coll".to_string()),
10396 durability: Some(fdecl::Durability::Transient),
10397 ..Default::default()
10398 },]),
10399 ..Default::default()
10400 }
10401 ),
10402 Err(ErrorList::new(vec![Error::duplicate_field(
10403 DeclType::OfferProtocol,
10404 "target_name",
10405 "thing"
10406 ),]))
10407 );
10408 }
10409
10410 #[test]
10411 fn test_validate_dynamic_offers_cycle_collection_to_static_child() {
10412 assert_eq!(
10413 validate_dynamic_offers(
10414 vec![("dyn", "coll")],
10415 &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10416 source_name: Some("bar".to_string()),
10417 target_name: Some("bar".to_string()),
10418 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10419 name: "static_child".into(),
10420 collection: None,
10421 })),
10422 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10423 name: "dyn".to_string(),
10424 collection: Some("coll".to_string()),
10425 })),
10426 dependency_type: Some(fdecl::DependencyType::Strong),
10427 ..Default::default()
10428 }),],
10429 &fdecl::Component {
10430 offers: Some(vec![fdecl::Offer::Service(fdecl::OfferService {
10431 source_name: Some("foo".to_string()),
10432 target_name: Some("foo".to_string()),
10433 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10434 name: "coll".into(),
10435 })),
10436 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10437 name: "static_child".into(),
10438 collection: None,
10439 })),
10440 ..Default::default()
10441 })]),
10442 children: Some(vec![fdecl::Child {
10443 name: Some("static_child".into()),
10444 url: Some("url#child.cm".into()),
10445 startup: Some(fdecl::StartupMode::Lazy),
10446 ..Default::default()
10447 }]),
10448 collections: Some(vec![fdecl::Collection {
10449 name: Some("coll".into()),
10450 durability: Some(fdecl::Durability::Transient),
10451 ..Default::default()
10452 }]),
10453 ..Default::default()
10454 }
10455 ),
10456 Err(ErrorList::new(vec![Error::dependency_cycle(
10457 directed_graph::Error::CyclesDetected(
10458 [vec![
10459 "child coll:dyn",
10460 "collection coll",
10461 "child static_child",
10462 "child coll:dyn",
10463 ]]
10464 .iter()
10465 .cloned()
10466 .collect()
10467 )
10468 .format_cycle()
10469 )]))
10470 );
10471 }
10472
10473 #[test]
10474 fn test_validate_dynamic_offers_cycle_collection_to_dynamic_child() {
10475 assert_eq!(
10476 validate_dynamic_offers(
10477 vec![("dyn", "coll1"), ("dyn", "coll2")],
10478 &vec![
10479 fdecl::Offer::Service(fdecl::OfferService {
10480 source_name: Some("foo".to_string()),
10481 target_name: Some("foo".to_string()),
10482 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10483 name: "coll2".into(),
10484 })),
10485 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10486 name: "dyn".into(),
10487 collection: Some("coll1".into()),
10488 })),
10489 ..Default::default()
10490 }),
10491 fdecl::Offer::Protocol(fdecl::OfferProtocol {
10492 source_name: Some("bar".to_string()),
10493 target_name: Some("bar".to_string()),
10494 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10495 name: "dyn".into(),
10496 collection: Some("coll1".into()),
10497 })),
10498 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10499 name: "dyn".to_string(),
10500 collection: Some("coll2".to_string()),
10501 })),
10502 dependency_type: Some(fdecl::DependencyType::Strong),
10503 ..Default::default()
10504 }),
10505 ],
10506 &fdecl::Component {
10507 collections: Some(vec![
10508 fdecl::Collection {
10509 name: Some("coll1".into()),
10510 durability: Some(fdecl::Durability::Transient),
10511 ..Default::default()
10512 },
10513 fdecl::Collection {
10514 name: Some("coll2".into()),
10515 durability: Some(fdecl::Durability::Transient),
10516 ..Default::default()
10517 },
10518 ]),
10519 ..Default::default()
10520 }
10521 ),
10522 Err(ErrorList::new(vec![Error::dependency_cycle(
10523 directed_graph::Error::CyclesDetected(
10524 [vec![
10525 "child coll1:dyn",
10526 "child coll2:dyn",
10527 "collection coll2",
10528 "child coll1:dyn",
10529 ]]
10530 .iter()
10531 .cloned()
10532 .collect()
10533 )
10534 .format_cycle()
10535 )]))
10536 );
10537 }
10538
10539 #[test]
10540 fn test_validate_dynamic_offers_cycle_collection_to_collection() {
10541 assert_eq!(
10542 validate_dynamic_offers(
10543 vec![("dyn", "coll1"), ("dyn", "coll2")],
10544 &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10545 source_name: Some("bar".to_string()),
10546 target_name: Some("bar".to_string()),
10547 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10548 name: "dyn".into(),
10549 collection: Some("coll2".parse().unwrap()),
10550 })),
10551 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10552 name: "dyn".into(),
10553 collection: Some("coll1".parse().unwrap()),
10554 })),
10555 dependency_type: Some(fdecl::DependencyType::Strong),
10556 ..Default::default()
10557 }),],
10558 &fdecl::Component {
10559 offers: Some(vec![fdecl::Offer::Service(fdecl::OfferService {
10560 source_name: Some("foo".to_string()),
10561 target_name: Some("foo".to_string()),
10562 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10563 name: "coll1".into(),
10564 })),
10565 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10566 name: "coll2".into(),
10567 })),
10568 ..Default::default()
10569 })]),
10570 collections: Some(vec![
10571 fdecl::Collection {
10572 name: Some("coll1".into()),
10573 durability: Some(fdecl::Durability::Transient),
10574 ..Default::default()
10575 },
10576 fdecl::Collection {
10577 name: Some("coll2".into()),
10578 durability: Some(fdecl::Durability::Transient),
10579 ..Default::default()
10580 },
10581 ]),
10582 ..Default::default()
10583 }
10584 ),
10585 Err(ErrorList::new(vec![Error::dependency_cycle(
10586 directed_graph::Error::CyclesDetected(
10587 [vec![
10588 "child coll1:dyn",
10589 "collection coll1",
10590 "child coll2:dyn",
10591 "child coll1:dyn",
10592 ]]
10593 .iter()
10594 .cloned()
10595 .collect()
10596 )
10597 .format_cycle()
10598 )]))
10599 );
10600 }
10601
10602 #[test]
10603 fn test_validate_dynamic_child() {
10604 assert_eq!(
10605 Ok(()),
10606 validate_dynamic_child(&fdecl::Child {
10607 name: Some("a".repeat(MAX_LONG_NAME_LENGTH).to_string()),
10608 url: Some("test:///child".to_string()),
10609 startup: Some(fdecl::StartupMode::Lazy),
10610 on_terminate: None,
10611 environment: None,
10612 ..Default::default()
10613 })
10614 );
10615 }
10616
10617 #[test]
10618 fn test_validate_dynamic_child_environment_is_invalid() {
10619 assert_eq!(
10620 validate_dynamic_child(&fdecl::Child {
10621 name: Some("a".repeat(MAX_LONG_NAME_LENGTH).to_string()),
10622 url: Some("test:///child".to_string()),
10623 startup: Some(fdecl::StartupMode::Lazy),
10624 on_terminate: None,
10625 environment: Some("env".to_string()),
10626 ..Default::default()
10627 }),
10628 Err(ErrorList::new(vec![Error::DynamicChildWithEnvironment]))
10629 );
10630 }
10631
10632 #[test]
10633 fn test_validate_dynamic_offers_missing_stuff() {
10634 assert_eq!(
10635 validate_dynamic_offers(
10636 vec![],
10637 &vec![
10638 fdecl::Offer::Protocol(fdecl::OfferProtocol::default()),
10639 fdecl::Offer::Service(fdecl::OfferService::default()),
10640 fdecl::Offer::Directory(fdecl::OfferDirectory::default()),
10641 fdecl::Offer::Storage(fdecl::OfferStorage::default()),
10642 fdecl::Offer::Runner(fdecl::OfferRunner::default()),
10643 fdecl::Offer::Resolver(fdecl::OfferResolver::default()),
10644 ],
10645 &fdecl::Component::default()
10646 ),
10647 Err(ErrorList::new(vec![
10648 Error::missing_field(DeclType::OfferProtocol, "source"),
10649 Error::missing_field(DeclType::OfferProtocol, "source_name"),
10650 Error::missing_field(DeclType::OfferProtocol, "target"),
10651 Error::missing_field(DeclType::OfferProtocol, "target_name"),
10652 Error::missing_field(DeclType::OfferProtocol, "dependency_type"),
10653 Error::missing_field(DeclType::OfferService, "source"),
10654 Error::missing_field(DeclType::OfferService, "source_name"),
10655 Error::missing_field(DeclType::OfferService, "target"),
10656 Error::missing_field(DeclType::OfferService, "target_name"),
10657 Error::missing_field(DeclType::OfferDirectory, "source"),
10658 Error::missing_field(DeclType::OfferDirectory, "source_name"),
10659 Error::missing_field(DeclType::OfferDirectory, "target"),
10660 Error::missing_field(DeclType::OfferDirectory, "target_name"),
10661 Error::missing_field(DeclType::OfferDirectory, "dependency_type"),
10662 Error::missing_field(DeclType::OfferStorage, "source"),
10663 Error::missing_field(DeclType::OfferStorage, "source_name"),
10664 Error::missing_field(DeclType::OfferStorage, "target"),
10665 Error::missing_field(DeclType::OfferStorage, "target_name"),
10666 Error::missing_field(DeclType::OfferRunner, "source"),
10667 Error::missing_field(DeclType::OfferRunner, "source_name"),
10668 Error::missing_field(DeclType::OfferRunner, "target"),
10669 Error::missing_field(DeclType::OfferRunner, "target_name"),
10670 Error::missing_field(DeclType::OfferResolver, "source"),
10671 Error::missing_field(DeclType::OfferResolver, "source_name"),
10672 Error::missing_field(DeclType::OfferResolver, "target"),
10673 Error::missing_field(DeclType::OfferResolver, "target_name"),
10674 ]))
10675 );
10676 }
10677
10678 test_dependency! {
10679 (test_validate_offers_protocol_dependency_cycle) => {
10680 ty = fdecl::Offer::Protocol,
10681 offer_decl = fdecl::OfferProtocol {
10682 source: None, target: None, source_name: Some(format!("thing")),
10685 target_name: Some(format!("thing")),
10686 dependency_type: Some(fdecl::DependencyType::Strong),
10687 ..Default::default()
10688 },
10689 },
10690 (test_validate_offers_directory_dependency_cycle) => {
10691 ty = fdecl::Offer::Directory,
10692 offer_decl = fdecl::OfferDirectory {
10693 source: None, target: None, source_name: Some(format!("thing")),
10696 target_name: Some(format!("thing")),
10697 rights: Some(fio::Operations::CONNECT),
10698 subdir: None,
10699 dependency_type: Some(fdecl::DependencyType::Strong),
10700 ..Default::default()
10701 },
10702 },
10703 (test_validate_offers_service_dependency_cycle) => {
10704 ty = fdecl::Offer::Service,
10705 offer_decl = fdecl::OfferService {
10706 source: None, target: None, source_name: Some(format!("thing")),
10709 target_name: Some(format!("thing")),
10710 ..Default::default()
10711 },
10712 },
10713 (test_validate_offers_runner_dependency_cycle) => {
10714 ty = fdecl::Offer::Runner,
10715 offer_decl = fdecl::OfferRunner {
10716 source: None, target: None, source_name: Some(format!("thing")),
10719 target_name: Some(format!("thing")),
10720 ..Default::default()
10721 },
10722 },
10723 (test_validate_offers_resolver_dependency_cycle) => {
10724 ty = fdecl::Offer::Resolver,
10725 offer_decl = fdecl::OfferResolver {
10726 source: None, target: None, source_name: Some(format!("thing")),
10729 target_name: Some(format!("thing")),
10730 ..Default::default()
10731 },
10732 },
10733 }
10734 test_weak_dependency! {
10735 (test_validate_offers_protocol_weak_dependency_cycle) => {
10736 ty = fdecl::Offer::Protocol,
10737 offer_decl = fdecl::OfferProtocol {
10738 source: None, target: None, source_name: Some(format!("thing")),
10741 target_name: Some(format!("thing")),
10742 dependency_type: None, ..Default::default()
10744 },
10745 },
10746 (test_validate_offers_directory_weak_dependency_cycle) => {
10747 ty = fdecl::Offer::Directory,
10748 offer_decl = fdecl::OfferDirectory {
10749 source: None, target: None, source_name: Some(format!("thing")),
10752 target_name: Some(format!("thing")),
10753 rights: Some(fio::Operations::CONNECT),
10754 subdir: None,
10755 dependency_type: None, ..Default::default()
10757 },
10758 },
10759 (test_validate_offers_service_weak_dependency_cycle) => {
10760 ty = fdecl::Offer::Service,
10761 offer_decl = fdecl::OfferService {
10762 source: None, target: None, source_name: Some(format!("thing")),
10765 target_name: Some(format!("thing")),
10766 dependency_type: None, ..Default::default()
10768 },
10769 },
10770 }
10771}