1pub(crate) mod util;
6
7pub mod error;
8
9pub use crate::util::check_url;
10
11use crate::error::*;
12use crate::util::*;
13use cm_graph::DependencyNode;
14use cm_types::IterablePath;
15use directed_graph::DirectedGraph;
16use fidl_fuchsia_component_decl as fdecl;
17use itertools::Itertools;
18use std::collections::{BTreeSet, HashMap, HashSet};
19use std::path::Path;
20
21trait HasAvailability {
22 fn availability(&self) -> fdecl::Availability;
23}
24
25impl HasAvailability for fdecl::ExposeService {
26 fn availability(&self) -> fdecl::Availability {
27 return self.availability.unwrap_or(fdecl::Availability::Required);
28 }
29}
30
31impl HasAvailability for fdecl::OfferService {
32 fn availability(&self) -> fdecl::Availability {
33 return self.availability.unwrap_or(fdecl::Availability::Required);
34 }
35}
36
37pub fn validate_value_spec(spec: &fdecl::ConfigValueSpec) -> Result<(), ErrorList> {
41 let mut errors = vec![];
42 if let Some(value) = &spec.value {
43 match value {
44 fdecl::ConfigValue::Single(s) => match s {
45 fdecl::ConfigSingleValue::Bool(_)
46 | fdecl::ConfigSingleValue::Uint8(_)
47 | fdecl::ConfigSingleValue::Uint16(_)
48 | fdecl::ConfigSingleValue::Uint32(_)
49 | fdecl::ConfigSingleValue::Uint64(_)
50 | fdecl::ConfigSingleValue::Int8(_)
51 | fdecl::ConfigSingleValue::Int16(_)
52 | fdecl::ConfigSingleValue::Int32(_)
53 | fdecl::ConfigSingleValue::Int64(_)
54 | fdecl::ConfigSingleValue::String(_) => {}
55 fdecl::ConfigSingleValueUnknown!() => {
56 errors.push(Error::invalid_field(DeclType::ConfigValueSpec, "value"));
57 }
58 },
59 fdecl::ConfigValue::Vector(l) => match l {
60 fdecl::ConfigVectorValue::BoolVector(_)
61 | fdecl::ConfigVectorValue::Uint8Vector(_)
62 | fdecl::ConfigVectorValue::Uint16Vector(_)
63 | fdecl::ConfigVectorValue::Uint32Vector(_)
64 | fdecl::ConfigVectorValue::Uint64Vector(_)
65 | fdecl::ConfigVectorValue::Int8Vector(_)
66 | fdecl::ConfigVectorValue::Int16Vector(_)
67 | fdecl::ConfigVectorValue::Int32Vector(_)
68 | fdecl::ConfigVectorValue::Int64Vector(_)
69 | fdecl::ConfigVectorValue::StringVector(_) => {}
70 fdecl::ConfigVectorValueUnknown!() => {
71 errors.push(Error::invalid_field(DeclType::ConfigValueSpec, "value"));
72 }
73 },
74 fdecl::ConfigValueUnknown!() => {
75 errors.push(Error::invalid_field(DeclType::ConfigValueSpec, "value"));
76 }
77 }
78 } else {
79 errors.push(Error::missing_field(DeclType::ConfigValueSpec, "value"));
80 }
81
82 if errors.is_empty() { Ok(()) } else { Err(ErrorList::new(errors)) }
83}
84
85pub fn validate_values_data(data: &fdecl::ConfigValuesData) -> Result<(), ErrorList> {
93 let mut errors = vec![];
94 if let Some(values) = &data.values {
95 for spec in values {
96 if let Err(mut e) = validate_value_spec(spec) {
97 errors.append(&mut e.errs);
98 }
99 }
100 } else {
101 errors.push(Error::missing_field(DeclType::ConfigValuesData, "values"));
102 }
103
104 if let Some(checksum) = &data.checksum {
105 match checksum {
106 fdecl::ConfigChecksum::Sha256(_) => {}
107 fdecl::ConfigChecksumUnknown!() => {
108 errors.push(Error::invalid_field(DeclType::ConfigValuesData, "checksum"));
109 }
110 }
111 } else {
112 errors.push(Error::missing_field(DeclType::ConfigValuesData, "checksum"));
113 }
114
115 if errors.is_empty() { Ok(()) } else { Err(ErrorList::new(errors)) }
116}
117
118#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
120enum RefKey<'a> {
121 Parent,
122 Self_,
123 Child(&'a str),
124 Collection(&'a str),
125 Framework,
126 Capability,
127 Debug,
128}
129
130pub fn validate<'a>(
147 decl: &'a fdecl::Component,
148 dependencies: &'a mut DirectedGraph<DependencyNode>,
149) -> Result<(), ErrorList> {
150 let ctx = ValidationContext::new(dependencies);
151 ctx.validate(decl, &[]).map_err(|errs| ErrorList::new(errs))
152}
153
154fn validate_capabilities(
156 capabilities: &[fdecl::Capability],
157 as_builtin: bool,
158) -> Result<(), ErrorList> {
159 let mut deps = DirectedGraph::new();
160 let mut ctx = ValidationContext::new(&mut deps);
161
162 ctx.load_dictionary_names(capabilities.iter().filter_map(|capability| match capability {
163 fdecl::Capability::Dictionary(dictionary_decl) => Some(dictionary_decl),
164 _ => None,
165 }));
166
167 ctx.validate_capability_decls(capabilities, as_builtin);
168 if ctx.errors.is_empty() { Ok(()) } else { Err(ErrorList::new(ctx.errors)) }
169}
170
171pub fn validate_builtin_capabilities(
173 capabilities: &Vec<fdecl::Capability>,
174) -> Result<(), ErrorList> {
175 validate_capabilities(capabilities, true)
176}
177
178pub fn validate_namespace_capabilities(
180 capabilities: &Vec<fdecl::Capability>,
181) -> Result<(), ErrorList> {
182 validate_capabilities(capabilities, false)
183}
184
185type CheckChildNameFn = fn(Option<&String>, DeclType, &str, &mut Vec<Error>) -> bool;
188
189pub fn validate_dynamic_child(child: &fdecl::Child) -> Result<(), ErrorList> {
190 let mut errors = vec![];
191
192 if let Err(mut error_list) = validate_child(child, check_dynamic_name) {
193 errors.append(&mut error_list.errs);
194 }
195
196 if child.environment.is_some() {
197 errors.push(Error::DynamicChildWithEnvironment);
198 }
199
200 if errors.is_empty() { Ok(()) } else { Err(ErrorList { errs: errors }) }
201}
202
203fn validate_child(
206 child: &fdecl::Child,
207 check_child_name: CheckChildNameFn,
208) -> Result<(), ErrorList> {
209 let mut errors = vec![];
210 check_child_name(child.name.as_ref(), DeclType::Child, "name", &mut errors);
211 check_url(child.url.as_ref(), DeclType::Child, "url", &mut errors);
212 if child.startup.is_none() {
213 errors.push(Error::missing_field(DeclType::Child, "startup"));
214 }
215 if child.environment.is_some() {
217 check_name(child.environment.as_ref(), DeclType::Child, "environment", &mut errors);
218 }
219 if errors.is_empty() { Ok(()) } else { Err(ErrorList { errs: errors }) }
220}
221
222pub fn validate_dynamic_offers<'a>(
235 dynamic_children: Vec<(&'a str, &'a str)>,
236 dependencies: &mut DirectedGraph<DependencyNode>,
237 new_dynamic_offers: &'a [fdecl::Offer],
238 decl: &'a fdecl::Component,
239) -> Result<(), ErrorList> {
240 let mut ctx = ValidationContext::new(dependencies);
241 ctx.dynamic_children = dynamic_children;
242 ctx.validate(decl, new_dynamic_offers).map_err(|errs| ErrorList::new(errs))
243}
244
245fn check_offer_name(
246 prop: Option<&String>,
247 decl: DeclType,
248 keyword: &str,
249 offer_type: OfferType,
250 errors: &mut Vec<Error>,
251) -> bool {
252 if offer_type == OfferType::Dynamic {
253 check_dynamic_name(prop, decl, keyword, errors)
254 } else {
255 check_name(prop, decl, keyword, errors)
256 }
257}
258
259struct ValidationContext<'a> {
260 all_children: HashMap<&'a str, &'a fdecl::Child>,
261 all_collections: HashSet<&'a str>,
262 all_capability_ids: HashSet<&'a str>,
263 all_storages: HashMap<&'a str, Option<&'a fdecl::Ref>>,
264 all_services: HashSet<&'a str>,
265 all_protocols: HashSet<&'a str>,
266 all_directories: HashSet<&'a str>,
267 all_runners: HashSet<&'a str>,
268 all_resolvers: HashSet<&'a str>,
269 all_dictionaries: HashMap<&'a str, &'a fdecl::Dictionary>,
270
271 #[cfg(fuchsia_api_level_at_least = "HEAD")]
272 all_configs: HashSet<&'a str>,
273
274 all_environment_names: HashSet<&'a str>,
275 dynamic_children: Vec<(&'a str, &'a str)>,
276 strong_dependencies: &'a mut DirectedGraph<DependencyNode>,
277 target_ids: IdMap<'a>,
278 errors: Vec<Error>,
279}
280
281trait Container {
285 fn contains(&self, key: &str) -> bool;
286}
287
288impl<'a> Container for HashSet<&'a str> {
289 fn contains(&self, key: &str) -> bool {
290 self.contains(key)
291 }
292}
293
294impl<'a, T> Container for HashMap<&'a str, T> {
295 fn contains(&self, key: &str) -> bool {
296 self.contains_key(key)
297 }
298}
299
300impl<'a> ValidationContext<'a> {
301 fn new(strong_dependencies: &'a mut DirectedGraph<DependencyNode>) -> Self {
302 Self {
303 strong_dependencies,
304 all_children: Default::default(),
305 all_collections: Default::default(),
306 all_capability_ids: Default::default(),
307 all_storages: Default::default(),
308 all_services: Default::default(),
309 all_protocols: Default::default(),
310 all_directories: Default::default(),
311 all_runners: Default::default(),
312 all_resolvers: Default::default(),
313 all_dictionaries: Default::default(),
314
315 #[cfg(fuchsia_api_level_at_least = "HEAD")]
316 all_configs: Default::default(),
317
318 all_environment_names: Default::default(),
319 dynamic_children: Default::default(),
320 target_ids: Default::default(),
321 errors: Default::default(),
322 }
323 }
324
325 fn validate(
326 mut self,
327 decl: &'a fdecl::Component,
328 new_dynamic_offers: &'a [fdecl::Offer],
329 ) -> Result<(), Vec<Error>> {
330 if let Some(envs) = &decl.environments {
332 self.collect_environment_names(&envs);
333 }
334
335 if let Some(children) = decl.children.as_ref() {
337 for child in children {
338 self.validate_child_decl(&child);
339 }
340 }
341
342 if let Some(collections) = decl.collections.as_ref() {
344 for collection in collections {
345 self.validate_collection_decl(&collection);
346 }
347 }
348
349 if let Some(capabilities) = decl.capabilities.as_ref() {
351 self.load_dictionary_names(capabilities.iter().filter_map(
352 |capability| match capability {
353 fdecl::Capability::Dictionary(dictionary_decl) => Some(dictionary_decl),
354 _ => None,
355 },
356 ));
357 self.validate_capability_decls(capabilities, false);
358 }
359
360 let mut use_runner_name = None;
362 let mut use_runner_source = None;
363 if let Some(uses) = decl.uses.as_ref() {
364 (use_runner_name, use_runner_source) = self.validate_use_decls(uses);
365 }
366
367 if let Some(program) = decl.program.as_ref() {
369 self.validate_program(program, use_runner_name, use_runner_source);
370 }
371
372 if let Some(exposes) = decl.exposes.as_ref() {
374 let mut expose_to_parent_ids = HashMap::new();
375 let mut expose_to_framework_ids = HashMap::new();
376 for expose in exposes.iter() {
377 self.validate_expose_decl(
378 &expose,
379 &mut expose_to_parent_ids,
380 &mut expose_to_framework_ids,
381 );
382 }
383 self.validate_expose_group(&exposes);
384 }
385
386 if let Some(offers) = decl.offers.as_ref() {
388 for offer in offers.iter() {
389 self.validate_offer_decl(&offer, OfferType::Static);
390 }
391 self.validate_offer_group(&offers, OfferType::Static);
392 }
393
394 for dynamic_offer in new_dynamic_offers {
395 self.validate_offer_decl(dynamic_offer, OfferType::Dynamic);
396 cm_graph::add_dependencies_from_offer(
397 &mut self.strong_dependencies,
398 dynamic_offer,
399 &self.dynamic_children,
400 );
401 }
402 self.validate_offer_group(new_dynamic_offers, OfferType::Dynamic);
403
404 if let Some(environment) = decl.environments.as_ref() {
406 for environment in environment {
407 self.validate_environment_decl(&environment);
408 }
409 }
410
411 self.validate_config(decl.config.as_ref(), decl.uses.as_ref());
413
414 cm_graph::generate_dependency_graph(
416 &mut self.strong_dependencies,
417 &decl,
418 &self.dynamic_children,
419 );
420 if let Err(e) = self.strong_dependencies.topological_sort() {
421 self.errors.push(Error::dependency_cycle(e.format_cycle()));
422 }
423
424 if self.errors.is_empty() { Ok(()) } else { Err(self.errors) }
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 fn validate_config(
441 &mut self,
442 config: Option<&fdecl::ConfigSchema>,
443 uses: Option<&Vec<fdecl::Use>>,
444 ) {
445 use std::collections::BTreeMap;
446
447 let optional_use_keys: BTreeMap<String, fdecl::ConfigType> =
449 uses.map_or(BTreeMap::new(), |u| {
450 u.iter()
451 .map(|u| {
452 let fdecl::Use::Config(config) = u else {
453 return None;
454 };
455 if config.availability == Some(fdecl::Availability::Required)
456 || config.availability == None
457 {
458 return None;
459 }
460 if let Some(_) = config.default.as_ref() {
461 return None;
462 }
463 let Some(key) = config.target_name.clone() else {
464 return None;
465 };
466 let Some(value) = config.type_.clone() else {
467 return None;
468 };
469 Some((key, value))
470 })
471 .flatten()
472 .collect()
473 });
474
475 for u in uses.iter().flat_map(|x| x.iter()) {
477 let fdecl::Use::Config(config) = u else { continue };
478 let Some(default) = config.default.as_ref() else { continue };
479 validate_value_spec(&fdecl::ConfigValueSpec {
480 value: Some(default.clone()),
481 ..Default::default()
482 })
483 .map_err(|mut e| self.errors.append(&mut e.errs))
484 .ok();
485 }
486
487 let Some(config) = config else {
488 if !optional_use_keys.is_empty() {
489 self.errors.push(Error::missing_field(DeclType::ConfigField, "config"))
490 }
491 return;
492 };
493
494 if let Some(fields) = &config.fields {
495 for field in fields {
496 if field.key.is_none() {
497 self.errors.push(Error::missing_field(DeclType::ConfigField, "key"));
498 }
499 if let Some(type_) = &field.type_ {
500 self.validate_config_type(type_, true);
501 } else {
502 self.errors.push(Error::missing_field(DeclType::ConfigField, "value_type"));
503 }
504 }
505 } else {
506 self.errors.push(Error::missing_field(DeclType::ConfigSchema, "fields"));
507 }
508
509 if let Some(checksum) = &config.checksum {
510 match checksum {
511 fdecl::ConfigChecksum::Sha256(_) => {}
512 fdecl::ConfigChecksumUnknown!() => {
513 self.errors.push(Error::invalid_field(DeclType::ConfigSchema, "checksum"));
514 }
515 }
516 } else {
517 self.errors.push(Error::missing_field(DeclType::ConfigSchema, "checksum"));
518 }
519
520 'outer: for (key, value) in optional_use_keys.iter() {
521 for field in config.fields.iter().flatten() {
522 if field.key.as_ref() == Some(key) {
523 if field.type_.as_ref() != Some(value) {
524 self.errors.push(Error::invalid_field(DeclType::ConfigField, key.clone()));
525 }
526 continue 'outer;
527 }
528 }
529 self.errors.push(Error::missing_field(DeclType::ConfigField, key.clone()));
530 }
531
532 match config.value_source {
533 None => self.errors.push(Error::missing_field(DeclType::ConfigSchema, "value_source")),
534 #[cfg(fuchsia_api_level_at_least = "HEAD")]
535 Some(fdecl::ConfigValueSource::Capabilities(_)) => {
536 if !optional_use_keys.is_empty() {
537 self.errors
538 .push(Error::invalid_field(DeclType::ConfigValueSource, "ValueSource"))
539 }
540 }
541 Some(fdecl::ConfigValueSource::__SourceBreaking { .. }) => {
542 self.errors.push(Error::invalid_field(DeclType::ConfigValueSource, "ValueSource"))
543 }
544 _ => (),
545 };
546 }
547
548 fn validate_config_type(&mut self, type_: &fdecl::ConfigType, accept_vectors: bool) {
549 match &type_.layout {
550 fdecl::ConfigTypeLayout::Bool
551 | fdecl::ConfigTypeLayout::Uint8
552 | fdecl::ConfigTypeLayout::Uint16
553 | fdecl::ConfigTypeLayout::Uint32
554 | fdecl::ConfigTypeLayout::Uint64
555 | fdecl::ConfigTypeLayout::Int8
556 | fdecl::ConfigTypeLayout::Int16
557 | fdecl::ConfigTypeLayout::Int32
558 | fdecl::ConfigTypeLayout::Int64 => {
559 if let Some(parameters) = &type_.parameters {
561 if !parameters.is_empty() {
562 self.errors
563 .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
564 }
565 } else {
566 self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"));
567 }
568
569 if !type_.constraints.is_empty() {
570 self.errors.push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
571 }
572 }
573 fdecl::ConfigTypeLayout::String => {
574 if let Some(parameters) = &type_.parameters {
576 if !parameters.is_empty() {
577 self.errors
578 .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
579 }
580 } else {
581 self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"));
582 }
583
584 if type_.constraints.is_empty() {
585 self.errors.push(Error::missing_field(DeclType::ConfigType, "constraints"));
586 } else if type_.constraints.len() > 1 {
587 self.errors.push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
588 } else if let fdecl::LayoutConstraint::MaxSize(_) = &type_.constraints[0] {
589 } else {
590 self.errors.push(Error::invalid_field(DeclType::ConfigType, "constraints"));
591 }
592 }
593 fdecl::ConfigTypeLayout::Vector => {
594 if accept_vectors {
595 if let Some(parameters) = &type_.parameters {
597 if parameters.is_empty() {
598 self.errors
599 .push(Error::missing_field(DeclType::ConfigType, "parameters"));
600 } else if parameters.len() > 1 {
601 self.errors
602 .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
603 } else if let fdecl::LayoutParameter::NestedType(nested_type) =
604 ¶meters[0]
605 {
606 self.validate_config_type(nested_type, false);
607 } else {
608 self.errors
609 .push(Error::invalid_field(DeclType::ConfigType, "parameters"));
610 }
611 } else {
612 self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"))
613 }
614
615 if type_.constraints.is_empty() {
616 self.errors.push(Error::missing_field(DeclType::ConfigType, "constraints"));
617 } else if type_.constraints.len() > 1 {
618 self.errors
619 .push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
620 } else if let fdecl::LayoutConstraint::MaxSize(_) = &type_.constraints[0] {
621 } else {
622 self.errors.push(Error::invalid_field(DeclType::ConfigType, "constraints"));
623 }
624 } else {
625 self.errors.push(Error::nested_vector());
626 }
627 }
628 _ => self.errors.push(Error::invalid_field(DeclType::ConfigType, "layout")),
629 }
630 }
631
632 fn validate_capability_decls(
633 &mut self,
634 capabilities: &'a [fdecl::Capability],
635 as_builtin: bool,
636 ) {
637 for capability in capabilities {
638 self.validate_capability_decl(capability, as_builtin);
639 }
640 }
641
642 fn validate_capability_decl(&mut self, capability: &'a fdecl::Capability, as_builtin: bool) {
647 match capability {
648 fdecl::Capability::Service(service) => self.validate_service_decl(&service, as_builtin),
649 fdecl::Capability::Protocol(protocol) => {
650 self.validate_protocol_decl(&protocol, as_builtin)
651 }
652 fdecl::Capability::Directory(directory) => {
653 self.validate_directory_decl(&directory, as_builtin)
654 }
655 fdecl::Capability::Storage(storage) => {
656 if as_builtin {
657 self.errors.push(Error::CapabilityCannotBeBuiltin(DeclType::Storage))
658 } else {
659 self.validate_storage_decl(&storage)
660 }
661 }
662 fdecl::Capability::Runner(runner) => self.validate_runner_decl(&runner, as_builtin),
663 fdecl::Capability::Resolver(resolver) => {
664 self.validate_resolver_decl(&resolver, as_builtin)
665 }
666 fdecl::Capability::EventStream(event) => {
667 if as_builtin {
668 self.validate_event_stream_decl(&event)
669 } else {
670 self.errors.push(Error::CapabilityMustBeBuiltin(DeclType::EventStream))
671 }
672 }
673 fdecl::Capability::Dictionary(dictionary) => {
674 self.validate_dictionary_decl(&dictionary);
675 }
676 #[cfg(fuchsia_api_level_at_least = "HEAD")]
677 fdecl::Capability::Config(config) => {
678 self.validate_configuration_decl(&config);
679 }
680 fdecl::CapabilityUnknown!() => self.errors.push(Error::UnknownCapability),
681 }
682 }
683
684 fn validate_use_decls(
686 &mut self,
687 uses: &'a [fdecl::Use],
688 ) -> (Option<&'a String>, Option<&'a fdecl::Ref>) {
689 for use_ in uses.iter() {
691 self.validate_use_decl(&use_);
692 }
693 self.validate_use_paths(&uses);
694
695 #[cfg(fuchsia_api_level_at_least = "HEAD")]
696 {
697 let mut use_runner_name = None;
698 let mut use_runner_source = None;
699 for use_ in uses.iter() {
700 if let fdecl::Use::Runner(use_runner) = use_ {
701 if use_runner_name.is_some() {
702 self.errors.push(Error::MultipleRunnersUsed);
703 }
704
705 use_runner_name = use_runner.source_name.as_ref();
706 use_runner_source = use_runner.source.as_ref();
707 }
708 }
709 return (use_runner_name, use_runner_source);
710 }
711 #[cfg(fuchsia_api_level_less_than = "HEAD")]
712 return (None, None);
713 }
714
715 fn validate_use_decl(&mut self, use_: &'a fdecl::Use) {
716 match use_ {
717 fdecl::Use::Service(u) => {
718 let decl = DeclType::UseService;
719 self.validate_use_fields(
720 decl,
721 Self::service_checker,
722 u.source.as_ref(),
723 u.source_name.as_ref(),
724 u.source_dictionary.as_ref(),
725 u.target_path.as_ref(),
726 u.dependency_type.as_ref(),
727 u.availability.as_ref(),
728 );
729 if u.dependency_type.is_none() {
730 self.errors.push(Error::missing_field(decl, "dependency_type"));
731 }
732 }
733 fdecl::Use::Protocol(u) => {
734 let decl = DeclType::UseProtocol;
735 self.validate_use_fields(
736 decl,
737 Self::protocol_checker,
738 u.source.as_ref(),
739 u.source_name.as_ref(),
740 u.source_dictionary.as_ref(),
741 u.target_path.as_ref(),
742 u.dependency_type.as_ref(),
743 u.availability.as_ref(),
744 );
745 #[cfg(not(fuchsia_api_level_at_least = "29"))]
746 let has_numbered_handle = false;
747 #[cfg(fuchsia_api_level_at_least = "29")]
748 let has_numbered_handle = u.numbered_handle.is_some();
749 if u.target_path.is_none() && !has_numbered_handle {
750 self.errors.push(Error::missing_field(decl, "target_path"));
751 }
752 if has_numbered_handle && u.target_path.is_some() {
753 self.errors.push(Error::extraneous_field(decl, "numbered_handle"));
754 }
755 if u.dependency_type.is_none() {
756 self.errors.push(Error::missing_field(decl, "dependency_type"));
757 }
758 }
759 fdecl::Use::Directory(u) => {
760 let decl = DeclType::UseDirectory;
761 self.validate_use_fields(
762 decl,
763 Self::directory_checker,
764 u.source.as_ref(),
765 u.source_name.as_ref(),
766 u.source_dictionary.as_ref(),
767 u.target_path.as_ref(),
768 u.dependency_type.as_ref(),
769 u.availability.as_ref(),
770 );
771 if u.dependency_type.is_none() {
772 self.errors.push(Error::missing_field(decl, "dependency_type"));
773 }
774 if u.rights.is_none() {
775 self.errors.push(Error::missing_field(DeclType::UseDirectory, "rights"));
776 }
777 if let Some(subdir) = u.subdir.as_ref() {
778 check_relative_path(
779 Some(subdir),
780 DeclType::UseDirectory,
781 "subdir",
782 &mut self.errors,
783 );
784 }
785 }
786 fdecl::Use::Storage(u) => {
787 const SOURCE: Option<fdecl::Ref> = Some(fdecl::Ref::Parent(fdecl::ParentRef {}));
788 const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
789 Some(fdecl::DependencyType::Strong);
790 self.validate_use_fields(
791 DeclType::UseStorage,
792 Self::storage_checker,
793 SOURCE.as_ref(),
794 u.source_name.as_ref(),
795 None,
796 u.target_path.as_ref(),
797 DEPENDENCY_TYPE.as_ref(),
798 u.availability.as_ref(),
799 );
800 }
801 fdecl::Use::EventStream(u) => {
802 const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
803 Some(fdecl::DependencyType::Strong);
804 let decl = DeclType::UseEventStream;
805 self.validate_use_fields(
806 decl,
807 Self::event_stream_checker,
808 u.source.as_ref(),
809 u.source_name.as_ref(),
810 None,
811 u.target_path.as_ref(),
812 DEPENDENCY_TYPE.as_ref(),
813 u.availability.as_ref(),
814 );
815 match u.source {
817 Some(fdecl::Ref::Child(_)) | Some(fdecl::Ref::Parent(_)) => {
818 }
820 Some(fdecl::Ref::Framework(_))
821 | Some(fdecl::Ref::Self_(_))
822 | Some(fdecl::Ref::Debug(_)) => {
823 self.errors.push(Error::invalid_field(decl, "source"));
825 }
826 Some(fdecl::Ref::Collection(_)) | Some(fdecl::RefUnknown!()) | None => {
827 }
829 }
830 if let Some(scope) = &u.scope {
831 for reference in scope {
832 if !matches!(reference, fdecl::Ref::Child(_) | fdecl::Ref::Collection(_)) {
833 self.errors.push(Error::invalid_field(decl, "scope"));
834 }
835 }
836 }
837 }
838 #[cfg(fuchsia_api_level_at_least = "HEAD")]
839 fdecl::Use::Runner(u) => {
840 const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
841 Some(fdecl::DependencyType::Strong);
842 const AVAILABILITY: Option<fdecl::Availability> =
843 Some(fdecl::Availability::Required);
844 let decl = DeclType::UseRunner;
845 self.validate_use_fields(
846 decl,
847 Self::runner_checker,
848 u.source.as_ref(),
849 u.source_name.as_ref(),
850 u.source_dictionary.as_ref(),
851 None,
852 DEPENDENCY_TYPE.as_ref(),
853 AVAILABILITY.as_ref(),
854 );
855 }
856 #[cfg(fuchsia_api_level_at_least = "HEAD")]
857 fdecl::Use::Config(u) => {
858 const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
859 Some(fdecl::DependencyType::Strong);
860 let decl = DeclType::UseConfiguration;
861 self.validate_use_fields(
862 decl,
863 Self::config_checker,
864 u.source.as_ref(),
865 u.source_name.as_ref(),
866 None,
867 None,
868 DEPENDENCY_TYPE.as_ref(),
869 u.availability.as_ref(),
870 );
871 }
872 #[cfg(fuchsia_api_level_at_least = "29")]
873 fdecl::Use::Dictionary(u) => {
874 let decl = DeclType::UseDictionary;
875 self.validate_use_fields(
876 decl,
877 Self::dictionary_checker,
878 u.source.as_ref(),
879 u.source_name.as_ref(),
880 u.source_dictionary.as_ref(),
881 u.target_path.as_ref(),
882 u.dependency_type.as_ref(),
883 u.availability.as_ref(),
884 );
885 if u.dependency_type.is_none() {
886 self.errors.push(Error::missing_field(decl, "dependency_type"));
887 }
888 }
889 fdecl::UseUnknown!() => {
890 self.errors.push(Error::invalid_field(DeclType::Component, "use"));
891 }
892 }
893 }
894
895 fn validate_program(
898 &mut self,
899 program: &fdecl::Program,
900 use_runner_name: Option<&String>,
901 _use_runner_source: Option<&fdecl::Ref>,
902 ) {
903 match &program.runner {
904 Some(_) =>
905 {
906 #[cfg(fuchsia_api_level_at_least = "HEAD")]
907 if use_runner_name.is_some() {
908 if use_runner_name != program.runner.as_ref()
909 || _use_runner_source
910 != Some(&fdecl::Ref::Environment(fdecl::EnvironmentRef))
911 {
912 self.errors.push(Error::ConflictingRunners);
913 }
914 }
915 }
916 None => {
917 if use_runner_name.is_none() {
918 self.errors.push(Error::MissingRunner);
919 }
920 }
921 }
922
923 if program.info.is_none() {
924 self.errors.push(Error::missing_field(DeclType::Program, "info"));
925 }
926 }
927
928 fn validate_use_paths(&mut self, uses: &[fdecl::Use]) {
931 fn use_to_path<'a>(use_: &'a fdecl::Use) -> Option<&'a Path> {
932 match use_ {
933 #[cfg(fuchsia_api_level_at_least = "29")]
934 fdecl::Use::Dictionary(fdecl::UseDictionary {
935 target_path: Some(path), ..
936 }) => Some(Path::new(path)),
937 fdecl::Use::Directory(fdecl::UseDirectory { target_path: Some(path), .. }) => {
938 Some(Path::new(path))
939 }
940 fdecl::Use::Protocol(fdecl::UseProtocol { target_path: Some(path), .. }) => {
941 Some(Path::new(path))
942 }
943 fdecl::Use::Service(fdecl::UseService { target_path: Some(path), .. }) => {
944 Some(Path::new(path))
945 }
946 fdecl::Use::Storage(fdecl::UseStorage { target_path: Some(path), .. }) => {
947 Some(Path::new(path))
948 }
949 _ => None,
950 }
951 }
952 enum NodeType {
953 Service,
954 Directory,
955 #[allow(unused)]
958 Dictionary,
959 }
960 fn use_to_type(use_: &fdecl::Use) -> Option<NodeType> {
961 match use_ {
962 fdecl::Use::Config(_) => None,
963 #[cfg(fuchsia_api_level_at_least = "29")]
964 fdecl::Use::Dictionary(_) => Some(NodeType::Dictionary),
965 fdecl::Use::Directory(_) => Some(NodeType::Directory),
966 fdecl::Use::EventStream(_) => Some(NodeType::Service),
967 fdecl::Use::Protocol(_) => Some(NodeType::Service),
968 #[cfg(fuchsia_api_level_at_least = "HEAD")]
969 fdecl::Use::Runner(_) => None,
970 fdecl::Use::Service(_) => Some(NodeType::Directory),
971 fdecl::Use::Storage(_) => Some(NodeType::Directory),
972 _ => None,
973 }
974 }
975 let mut paths_and_uses =
976 uses.iter().filter_map(|use_| use_to_path(use_).map(|x| (x, use_))).collect::<Vec<_>>();
977 let mut duplicate_fields = false;
978 while let Some((path, use_)) = paths_and_uses.pop() {
979 if paths_and_uses.iter().any(|(path_from_iter, use_from_iter)| {
980 path == *path_from_iter
981 && !(DeclType::from(*use_from_iter) == DeclType::UseDictionary
982 && DeclType::from(use_) == DeclType::UseDictionary)
983 }) {
984 self.errors.push(Error::duplicate_field(
985 use_.into(),
986 "target_path",
987 path.to_str().unwrap(),
988 ));
989 duplicate_fields = true;
990 }
991 }
992 if duplicate_fields {
993 return;
996 }
997 for (use_a, use_b) in uses.iter().tuple_combinations() {
998 let Some(path_a) = use_to_path(use_a) else {
999 continue;
1000 };
1001 let Some(path_b) = use_to_path(use_b) else {
1002 continue;
1003 };
1004 let Some(type_a) = use_to_type(use_a) else {
1005 continue;
1006 };
1007 let Some(type_b) = use_to_type(use_b) else {
1008 continue;
1009 };
1010 let mut conflicts = false;
1011 match (type_a, type_b) {
1012 (NodeType::Service, NodeType::Service)
1013 | (NodeType::Directory, NodeType::Service)
1014 | (NodeType::Service, NodeType::Directory)
1015 | (NodeType::Directory, NodeType::Directory) => {
1016 if path_a.starts_with(path_b) || path_b.starts_with(path_a) {
1017 conflicts = true;
1018 }
1019 }
1020 (NodeType::Dictionary, NodeType::Service)
1021 | (NodeType::Dictionary, NodeType::Directory) => {
1022 if path_a.starts_with(path_b) {
1023 conflicts = true;
1024 }
1025 }
1026 (NodeType::Service, NodeType::Dictionary)
1027 | (NodeType::Directory, NodeType::Dictionary) => {
1028 if path_b.starts_with(path_a) {
1029 conflicts = true;
1030 }
1031 }
1032 (NodeType::Dictionary, NodeType::Dictionary) => {
1033 }
1035 }
1036 if conflicts {
1037 self.errors.push(Error::invalid_path_overlap(
1038 use_a.into(),
1039 path_a.to_str().unwrap(),
1040 use_b.into(),
1041 path_b.to_str().unwrap(),
1042 ));
1043 }
1044 }
1045 for use_ in uses {
1046 let Some(used_path) = use_to_path(use_) else {
1047 continue;
1048 };
1049 if used_path.starts_with(Path::new("/pkg")) {
1050 self.errors.push(Error::pkg_path_overlap(use_.into(), used_path.to_str().unwrap()));
1051 }
1052 }
1053 }
1054
1055 fn validate_use_fields(
1056 &mut self,
1057 decl: DeclType,
1058 capability_checker: impl Fn(&Self) -> &dyn Container,
1062 source: Option<&'a fdecl::Ref>,
1063 source_name: Option<&'a String>,
1064 source_dictionary: Option<&'a String>,
1065 target_path: Option<&'a String>,
1066 dependency_type: Option<&fdecl::DependencyType>,
1067 availability: Option<&'a fdecl::Availability>,
1068 ) {
1069 self.validate_use_source(decl, source, source_dictionary);
1070
1071 check_name(source_name, decl, "source_name", &mut self.errors);
1072 if source_dictionary.is_some() {
1073 check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
1074 }
1075 match decl {
1076 DeclType::UseRunner | DeclType::UseConfiguration => {}
1077 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1078 DeclType::UseProtocol => {
1079 if target_path.is_some() {
1080 check_path(target_path, decl, "target_path", &mut self.errors);
1081 } else {
1082 }
1084 }
1085 _ => {
1086 check_path(target_path, decl, "target_path", &mut self.errors);
1087 }
1088 }
1089 check_use_availability(decl, availability, &mut self.errors);
1090
1091 let is_use_from_child = match source {
1093 Some(fdecl::Ref::Child(_)) => true,
1094 _ => false,
1095 };
1096 match (is_use_from_child, dependency_type) {
1097 (false, Some(fdecl::DependencyType::Weak)) => {
1098 self.errors.push(Error::invalid_field(decl, "dependency_type"));
1099 }
1100 _ => {}
1101 }
1102
1103 self.validate_route_from_self(
1104 decl,
1105 source,
1106 source_name,
1107 source_dictionary,
1108 capability_checker,
1109 );
1110 }
1111
1112 fn validate_use_source(
1113 &mut self,
1114 decl: DeclType,
1115 source: Option<&'a fdecl::Ref>,
1116 source_dictionary: Option<&'a String>,
1117 ) {
1118 match (source, source_dictionary) {
1119 (Some(fdecl::Ref::Parent(_)), _) => {}
1121 (Some(fdecl::Ref::Self_(_)), _) => {}
1122 (Some(fdecl::Ref::Child(child)), _) => {
1123 self.validate_child_ref(decl, "source", &child, OfferType::Static);
1124 return;
1125 }
1126 (Some(fdecl::Ref::Framework(_)), None) => {}
1128 (Some(fdecl::Ref::Debug(_)), None) => {}
1129 (Some(fdecl::Ref::Capability(c)), None) => {
1130 self.validate_source_capability(&c, decl, "source");
1131 return;
1132 }
1133 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1134 (Some(fdecl::Ref::Environment(_)), None) => {}
1135 (Some(fdecl::Ref::Collection(collection)), None) if decl == DeclType::UseService => {
1136 self.validate_collection_ref(decl, "source", &collection);
1137 return;
1138 }
1139 (None, _) => self.errors.push(Error::missing_field(decl, "source")),
1141 (_, _) => self.errors.push(Error::invalid_field(decl, "source")),
1143 }
1144 }
1145
1146 fn validate_child_decl(&mut self, child: &'a fdecl::Child) {
1147 if let Err(mut e) = validate_child(child, check_name) {
1148 self.errors.append(&mut e.errs);
1149 }
1150 if let Some(name) = child.name.as_ref() {
1151 let name: &str = name;
1152 if self.all_children.insert(name, child).is_some() {
1153 self.errors.push(Error::duplicate_field(DeclType::Child, "name", name));
1154 }
1155 }
1156 if let Some(environment) = child.environment.as_ref() {
1157 if !self.all_environment_names.contains(environment.as_str()) {
1158 self.errors.push(Error::invalid_environment(
1159 DeclType::Child,
1160 "environment",
1161 environment,
1162 ));
1163 }
1164 }
1165 }
1166
1167 fn validate_collection_decl(&mut self, collection: &'a fdecl::Collection) {
1168 let name = collection.name.as_ref();
1169 if check_name(name, DeclType::Collection, "name", &mut self.errors) {
1170 let name: &str = name.unwrap();
1171 if !self.all_collections.insert(name) {
1172 self.errors.push(Error::duplicate_field(DeclType::Collection, "name", name));
1173 }
1174 }
1175 if collection.durability.is_none() {
1176 self.errors.push(Error::missing_field(DeclType::Collection, "durability"));
1177 }
1178 if let Some(environment) = collection.environment.as_ref() {
1179 if !self.all_environment_names.contains(environment.as_str()) {
1180 self.errors.push(Error::invalid_environment(
1181 DeclType::Collection,
1182 "environment",
1183 environment,
1184 ));
1185 }
1186 }
1187 }
1189
1190 fn validate_environment_decl(&mut self, environment: &'a fdecl::Environment) {
1191 let name = environment.name.as_ref();
1192 check_name(name, DeclType::Environment, "name", &mut self.errors);
1193 if environment.extends.is_none() {
1194 self.errors.push(Error::missing_field(DeclType::Environment, "extends"));
1195 }
1196 if let Some(runners) = environment.runners.as_ref() {
1197 let mut registered_runners = HashSet::new();
1198 for runner in runners {
1199 self.validate_runner_registration(runner, &mut registered_runners);
1200 }
1201 }
1202 if let Some(resolvers) = environment.resolvers.as_ref() {
1203 let mut registered_schemes = HashSet::new();
1204 for resolver in resolvers {
1205 self.validate_resolver_registration(resolver, &mut registered_schemes);
1206 }
1207 }
1208
1209 match environment.extends.as_ref() {
1210 Some(fdecl::EnvironmentExtends::None) => {
1211 if environment.stop_timeout_ms.is_none() {
1212 self.errors
1213 .push(Error::missing_field(DeclType::Environment, "stop_timeout_ms"));
1214 }
1215 }
1216 None | Some(fdecl::EnvironmentExtends::Realm) => {}
1217 }
1218
1219 if let Some(debugs) = environment.debug_capabilities.as_ref() {
1220 for debug in debugs {
1221 self.validate_environment_debug_registration(debug);
1222 }
1223 }
1224 }
1225
1226 fn validate_runner_registration(
1227 &mut self,
1228 runner_registration: &'a fdecl::RunnerRegistration,
1229 runner_names: &mut HashSet<&'a str>,
1230 ) {
1231 check_name(
1232 runner_registration.source_name.as_ref(),
1233 DeclType::RunnerRegistration,
1234 "source_name",
1235 &mut self.errors,
1236 );
1237 self.validate_registration_source(
1238 runner_registration.source.as_ref(),
1239 DeclType::RunnerRegistration,
1240 );
1241 if let (Some(fdecl::Ref::Self_(_)), Some(name)) =
1243 (&runner_registration.source, runner_registration.source_name.as_ref())
1244 {
1245 if !self.all_runners.contains(name as &str) {
1246 self.errors.push(Error::invalid_runner(
1247 DeclType::RunnerRegistration,
1248 "source_name",
1249 name,
1250 ));
1251 }
1252 }
1253
1254 check_name(
1255 runner_registration.target_name.as_ref(),
1256 DeclType::RunnerRegistration,
1257 "target_name",
1258 &mut self.errors,
1259 );
1260 if let Some(name) = runner_registration.target_name.as_ref() {
1261 if !runner_names.insert(name.as_str()) {
1262 self.errors.push(Error::duplicate_field(
1263 DeclType::RunnerRegistration,
1264 "target_name",
1265 name,
1266 ));
1267 }
1268 }
1269 }
1270
1271 fn validate_resolver_registration(
1272 &mut self,
1273 resolver_registration: &'a fdecl::ResolverRegistration,
1274 schemes: &mut HashSet<&'a str>,
1275 ) {
1276 check_name(
1277 resolver_registration.resolver.as_ref(),
1278 DeclType::ResolverRegistration,
1279 "resolver",
1280 &mut self.errors,
1281 );
1282 self.validate_registration_source(
1283 resolver_registration.source.as_ref(),
1284 DeclType::ResolverRegistration,
1285 );
1286 check_url_scheme(
1287 resolver_registration.scheme.as_ref(),
1288 DeclType::ResolverRegistration,
1289 "scheme",
1290 &mut self.errors,
1291 );
1292 if let Some(scheme) = resolver_registration.scheme.as_ref() {
1293 if !schemes.insert(scheme.as_str()) {
1294 self.errors.push(Error::duplicate_field(
1295 DeclType::ResolverRegistration,
1296 "scheme",
1297 scheme,
1298 ));
1299 }
1300 }
1301 }
1302
1303 fn validate_registration_source(&mut self, source: Option<&'a fdecl::Ref>, ty: DeclType) {
1304 match source {
1305 Some(fdecl::Ref::Parent(_)) => {}
1306 Some(fdecl::Ref::Self_(_)) => {}
1307 Some(fdecl::Ref::Child(child_ref)) => {
1308 self.validate_child_ref(ty, "source", &child_ref, OfferType::Static);
1310 }
1311 Some(_) => {
1312 self.errors.push(Error::invalid_field(ty, "source"));
1313 }
1314 None => {
1315 self.errors.push(Error::missing_field(ty, "source"));
1316 }
1317 }
1318 }
1319
1320 fn validate_service_decl(&mut self, service: &'a fdecl::Service, as_builtin: bool) {
1321 if check_name(service.name.as_ref(), DeclType::Service, "name", &mut self.errors) {
1322 let name = service.name.as_ref().unwrap();
1323 if !self.all_capability_ids.insert(name) {
1324 self.errors.push(Error::duplicate_field(DeclType::Service, "name", name.as_str()));
1325 }
1326 self.all_services.insert(name);
1327 }
1328 match as_builtin {
1329 true => {
1330 if let Some(path) = service.source_path.as_ref() {
1331 self.errors.push(Error::extraneous_source_path(DeclType::Service, path))
1332 }
1333 }
1334 false => {
1335 check_path(
1336 service.source_path.as_ref(),
1337 DeclType::Service,
1338 "source_path",
1339 &mut self.errors,
1340 );
1341 }
1342 }
1343 }
1344
1345 fn validate_protocol_decl(&mut self, protocol: &'a fdecl::Protocol, as_builtin: bool) {
1346 if check_name(protocol.name.as_ref(), DeclType::Protocol, "name", &mut self.errors) {
1347 let name = protocol.name.as_ref().unwrap();
1348 if !self.all_capability_ids.insert(name) {
1349 self.errors.push(Error::duplicate_field(DeclType::Protocol, "name", name.as_str()));
1350 }
1351 self.all_protocols.insert(name);
1352 }
1353 match as_builtin {
1354 true => {
1355 if let Some(path) = protocol.source_path.as_ref() {
1356 self.errors.push(Error::extraneous_source_path(DeclType::Protocol, path))
1357 }
1358 }
1359 false => {
1360 check_path(
1361 protocol.source_path.as_ref(),
1362 DeclType::Protocol,
1363 "source_path",
1364 &mut self.errors,
1365 );
1366 }
1367 }
1368
1369 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1370 match protocol.delivery {
1371 Some(delivery) => match cm_types::DeliveryType::try_from(delivery) {
1372 Ok(_) => {}
1373 Err(_) => self.errors.push(Error::invalid_field(DeclType::Protocol, "delivery")),
1374 },
1375 None => {}
1376 }
1377 }
1378
1379 fn validate_directory_decl(&mut self, directory: &'a fdecl::Directory, as_builtin: bool) {
1380 if check_name(directory.name.as_ref(), DeclType::Directory, "name", &mut self.errors) {
1381 let name = directory.name.as_ref().unwrap();
1382 if !self.all_capability_ids.insert(name) {
1383 self.errors.push(Error::duplicate_field(
1384 DeclType::Directory,
1385 "name",
1386 name.as_str(),
1387 ));
1388 }
1389 self.all_directories.insert(name);
1390 }
1391 match as_builtin {
1392 true => {
1393 if let Some(path) = directory.source_path.as_ref() {
1394 self.errors.push(Error::extraneous_source_path(DeclType::Directory, path))
1395 }
1396 }
1397 false => {
1398 check_path(
1399 directory.source_path.as_ref(),
1400 DeclType::Directory,
1401 "source_path",
1402 &mut self.errors,
1403 );
1404 }
1405 }
1406 if directory.rights.is_none() {
1407 self.errors.push(Error::missing_field(DeclType::Directory, "rights"));
1408 }
1409 }
1410
1411 fn validate_storage_decl(&mut self, storage: &'a fdecl::Storage) {
1412 match storage.source.as_ref() {
1413 Some(fdecl::Ref::Parent(_)) => {}
1414 Some(fdecl::Ref::Self_(_)) => {}
1415 Some(fdecl::Ref::Child(child)) => {
1416 let _ =
1417 self.validate_child_ref(DeclType::Storage, "source", &child, OfferType::Static);
1418 }
1419 Some(_) => {
1420 self.errors.push(Error::invalid_field(DeclType::Storage, "source"));
1421 }
1422 None => {
1423 self.errors.push(Error::missing_field(DeclType::Storage, "source"));
1424 }
1425 };
1426 if check_name(storage.name.as_ref(), DeclType::Storage, "name", &mut self.errors) {
1427 let name = storage.name.as_ref().unwrap();
1428 if !self.all_capability_ids.insert(name) {
1429 self.errors.push(Error::duplicate_field(DeclType::Storage, "name", name.as_str()));
1430 }
1431 self.all_storages.insert(name, storage.source.as_ref());
1432 }
1433 if storage.storage_id.is_none() {
1434 self.errors.push(Error::missing_field(DeclType::Storage, "storage_id"));
1435 }
1436 check_name(
1437 storage.backing_dir.as_ref(),
1438 DeclType::Storage,
1439 "backing_dir",
1440 &mut self.errors,
1441 );
1442 }
1443
1444 fn validate_runner_decl(&mut self, runner: &'a fdecl::Runner, as_builtin: bool) {
1445 if check_name(runner.name.as_ref(), DeclType::Runner, "name", &mut self.errors) {
1446 let name = runner.name.as_ref().unwrap();
1447 if !self.all_capability_ids.insert(name) {
1448 self.errors.push(Error::duplicate_field(DeclType::Runner, "name", name.as_str()));
1449 }
1450 self.all_runners.insert(name);
1451 }
1452 match as_builtin {
1453 true => {
1454 if let Some(path) = runner.source_path.as_ref() {
1455 self.errors.push(Error::extraneous_source_path(DeclType::Runner, path))
1456 }
1457 }
1458 false => {
1459 check_path(
1460 runner.source_path.as_ref(),
1461 DeclType::Runner,
1462 "source_path",
1463 &mut self.errors,
1464 );
1465 }
1466 }
1467 }
1468
1469 fn validate_resolver_decl(&mut self, resolver: &'a fdecl::Resolver, as_builtin: bool) {
1470 if check_name(resolver.name.as_ref(), DeclType::Resolver, "name", &mut self.errors) {
1471 let name = resolver.name.as_ref().unwrap();
1472 if !self.all_capability_ids.insert(name) {
1473 self.errors.push(Error::duplicate_field(DeclType::Resolver, "name", name.as_str()));
1474 }
1475 self.all_resolvers.insert(name);
1476 }
1477 match as_builtin {
1478 true => {
1479 if let Some(path) = resolver.source_path.as_ref() {
1480 self.errors.push(Error::extraneous_source_path(DeclType::Resolver, path))
1481 }
1482 }
1483 false => {
1484 check_path(
1485 resolver.source_path.as_ref(),
1486 DeclType::Resolver,
1487 "source_path",
1488 &mut self.errors,
1489 );
1490 }
1491 }
1492 }
1493
1494 fn load_dictionary_names(&mut self, dictionaries: impl Iterator<Item = &'a fdecl::Dictionary>) {
1498 for dictionary in dictionaries {
1499 let decl = DeclType::Dictionary;
1500 if check_name(dictionary.name.as_ref(), decl, "name", &mut self.errors) {
1501 let name = dictionary.name.as_ref().unwrap();
1502 if !self.all_capability_ids.insert(name) {
1503 self.errors.push(Error::duplicate_field(decl, "name", name.as_str()));
1504 }
1505 self.all_dictionaries.insert(name, &dictionary);
1506 }
1507 }
1508 }
1509
1510 fn validate_dictionary_decl(&mut self, dictionary: &'a fdecl::Dictionary) {
1511 let decl = DeclType::Dictionary;
1512 if let Some(path) = dictionary.source_path.as_ref() {
1513 if dictionary.source.is_some() {
1514 self.errors.push(Error::extraneous_field(decl, "source"));
1515 }
1516 check_path(Some(path), DeclType::Dictionary, "source_path", &mut self.errors);
1517 }
1518 }
1519
1520 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1521 fn validate_configuration_decl(&mut self, config: &'a fdecl::Configuration) {
1522 let decl = DeclType::Configuration;
1523 if check_name(config.name.as_ref(), decl, "name", &mut self.errors) {
1524 let name = config.name.as_ref().unwrap();
1525 if !self.all_capability_ids.insert(name) {
1526 self.errors.push(Error::duplicate_field(decl, "name", name.as_str()));
1527 }
1528 self.all_configs.insert(name);
1529 }
1530 }
1531
1532 fn validate_environment_debug_registration(&mut self, debug: &'a fdecl::DebugRegistration) {
1533 match debug {
1534 fdecl::DebugRegistration::Protocol(o) => {
1535 let decl = DeclType::DebugProtocolRegistration;
1536 self.validate_environment_debug_fields(
1537 decl,
1538 o.source.as_ref(),
1539 o.source_name.as_ref(),
1540 o.target_name.as_ref(),
1541 );
1542
1543 if let (Some(fdecl::Ref::Self_(_)), Some(name)) =
1544 (&o.source, o.source_name.as_ref())
1545 {
1546 if !self.all_protocols.contains(&name as &str) {
1547 self.errors.push(Error::invalid_field(decl, "source"));
1548 }
1549 }
1550 }
1551 _ => {
1552 self.errors.push(Error::invalid_field(DeclType::Environment, "debug"));
1553 }
1554 }
1555 }
1556
1557 fn validate_environment_debug_fields(
1558 &mut self,
1559 decl: DeclType,
1560 source: Option<&fdecl::Ref>,
1561 source_name: Option<&String>,
1562 target_name: Option<&'a String>,
1563 ) {
1564 match source {
1566 Some(fdecl::Ref::Parent(_)) => {}
1567 Some(fdecl::Ref::Self_(_)) => {}
1568 Some(fdecl::Ref::Framework(_)) => {}
1569 Some(fdecl::Ref::Child(child)) => {
1570 let _ = self.validate_child_ref(decl, "source", &child, OfferType::Static);
1571 }
1572 Some(_) => self.errors.push(Error::invalid_field(decl, "source")),
1573 None => self.errors.push(Error::missing_field(decl, "source")),
1574 }
1575 check_name(source_name, decl, "source_name", &mut self.errors);
1576 check_name(target_name, decl, "target_name", &mut self.errors);
1577 }
1578
1579 fn validate_event_stream_decl(&mut self, event: &'a fdecl::EventStream) {
1580 if check_name(event.name.as_ref(), DeclType::EventStream, "name", &mut self.errors) {
1581 let name = event.name.as_ref().unwrap();
1582 if !self.all_capability_ids.insert(name) {
1583 self.errors.push(Error::duplicate_field(
1584 DeclType::EventStream,
1585 "name",
1586 name.as_str(),
1587 ));
1588 }
1589 }
1590 }
1591
1592 fn validate_source_collection(
1593 &mut self,
1594 collection: &fdecl::CollectionRef,
1595 decl_type: DeclType,
1596 ) -> bool {
1597 let num_errors = self.errors.len();
1598 if check_name(Some(&collection.name), decl_type, "source.collection.name", &mut self.errors)
1599 && !self.all_collections.contains(&collection.name as &str)
1600 {
1601 self.errors.push(Error::invalid_collection(
1602 decl_type,
1603 "source",
1604 &collection.name as &str,
1605 ));
1606 }
1607 num_errors == self.errors.len()
1608 }
1609
1610 fn validate_filtered_service_fields(
1611 &mut self,
1612 decl_type: DeclType,
1613 source_instance_filter: Option<&Vec<String>>,
1614 renamed_instances: Option<&Vec<fdecl::NameMapping>>,
1615 ) {
1616 if let Some(source_instance_filter) = source_instance_filter {
1617 if source_instance_filter.is_empty() {
1618 self.errors.push(Error::invalid_field(decl_type, "source_instance_filter"));
1621 }
1622 for name in source_instance_filter {
1623 check_name(Some(name), decl_type, "source_instance_filter", &mut self.errors);
1624 }
1625 }
1626 if let Some(renamed_instances) = renamed_instances {
1627 let mut seen_target_names = HashSet::<String>::new();
1629 for mapping in renamed_instances {
1630 check_name(
1631 Some(&mapping.source_name),
1632 decl_type,
1633 "renamed_instances.source_name",
1634 &mut self.errors,
1635 );
1636 check_name(
1637 Some(&mapping.target_name),
1638 decl_type,
1639 "renamed_instances.target_name",
1640 &mut self.errors,
1641 );
1642 if !seen_target_names.insert(mapping.target_name.clone()) {
1643 self.errors.push(Error::invalid_field(decl_type, "renamed_instances"));
1644 break;
1645 }
1646 }
1647 }
1648 }
1649
1650 fn validate_source_capability(
1651 &mut self,
1652 capability: &fdecl::CapabilityRef,
1653 decl_type: DeclType,
1654 field: &str,
1655 ) -> bool {
1656 let num_errors = self.errors.len();
1657 if check_name(Some(&capability.name), decl_type, "source.capability.name", &mut self.errors)
1658 && !self.all_capability_ids.contains(capability.name.as_str())
1659 {
1660 self.errors.push(Error::invalid_capability(decl_type, field, &capability.name));
1661 }
1662 num_errors == self.errors.len()
1663 }
1664
1665 fn make_group_key(
1669 target_name: Option<&'a String>,
1670 target: Option<&'a fdecl::Ref>,
1671 ) -> Option<(&'a str, RefKey<'a>)> {
1672 if target_name.is_none() {
1673 return None;
1674 }
1675 let target_name = target_name.unwrap().as_str();
1676 if target.is_none() {
1677 return None;
1678 }
1679 let target = match target.unwrap() {
1680 fdecl::Ref::Parent(_) => RefKey::Parent,
1681 fdecl::Ref::Self_(_) => RefKey::Self_,
1682 fdecl::Ref::Child(r) => RefKey::Child(r.name.as_str()),
1683 fdecl::Ref::Collection(r) => RefKey::Collection(r.name.as_str()),
1684 fdecl::Ref::Framework(_) => RefKey::Framework,
1685 fdecl::Ref::Capability(_) => RefKey::Capability,
1686 fdecl::Ref::Debug(_) => RefKey::Debug,
1687 fdecl::RefUnknown!() => {
1688 return None;
1689 }
1690 };
1691 Some((target_name, target))
1692 }
1693
1694 fn validate_aggregation_has_same_availability(
1695 &mut self,
1696 route_group: &Vec<impl HasAvailability>,
1697 ) {
1698 let availability_of_sources: BTreeSet<_> =
1700 route_group.iter().map(|r| r.availability()).collect();
1701
1702 if availability_of_sources.len() > 1 {
1704 self.errors.push(Error::different_availability_in_aggregation(
1705 availability_of_sources.into_iter().collect(),
1706 ));
1707 }
1708 }
1709
1710 fn validate_expose_group(&mut self, exposes: &'a [fdecl::Expose]) {
1713 let mut expose_groups: HashMap<_, Vec<fdecl::ExposeService>> = HashMap::new();
1714 let service_exposes = exposes
1715 .into_iter()
1716 .filter_map(|o| if let fdecl::Expose::Service(s) = o { Some(s) } else { None });
1717 for expose in service_exposes {
1718 let key = Self::make_group_key(expose.target_name.as_ref(), expose.target.as_ref());
1719 if let Some(key) = key {
1720 expose_groups.entry(key).or_insert_with(|| vec![]).push(expose.clone());
1721 }
1722 }
1723 for expose_group in expose_groups.into_values() {
1724 if expose_group.len() == 1 {
1725 continue;
1728 }
1729
1730 self.validate_aggregation_has_same_availability(&expose_group);
1731 }
1732 }
1733
1734 fn validate_expose_decl(
1735 &mut self,
1736 expose: &'a fdecl::Expose,
1737 expose_to_parent_ids: &mut HashMap<&'a str, AllowableIds>,
1738 expose_to_framework_ids: &mut HashMap<&'a str, AllowableIds>,
1739 ) {
1740 match expose {
1741 fdecl::Expose::Service(e) => {
1742 let decl = DeclType::ExposeService;
1743 self.validate_expose_fields(
1744 decl,
1745 AllowableIds::Many,
1746 CollectionSource::Allow,
1747 Self::service_checker,
1748 e.source.as_ref(),
1749 e.source_name.as_ref(),
1750 e.source_dictionary.as_ref(),
1751 e.target.as_ref(),
1752 e.target_name.as_ref(),
1753 e.availability.as_ref(),
1754 expose_to_parent_ids,
1755 expose_to_framework_ids,
1756 );
1757 }
1758 fdecl::Expose::Protocol(e) => {
1759 let decl = DeclType::ExposeProtocol;
1760 self.validate_expose_fields(
1761 decl,
1762 AllowableIds::One,
1763 CollectionSource::Deny,
1764 Self::protocol_checker,
1765 e.source.as_ref(),
1766 e.source_name.as_ref(),
1767 e.source_dictionary.as_ref(),
1768 e.target.as_ref(),
1769 e.target_name.as_ref(),
1770 e.availability.as_ref(),
1771 expose_to_parent_ids,
1772 expose_to_framework_ids,
1773 );
1774 }
1775 fdecl::Expose::Directory(e) => {
1776 let decl = DeclType::ExposeDirectory;
1777 self.validate_expose_fields(
1778 decl,
1779 AllowableIds::One,
1780 CollectionSource::Deny,
1781 Self::directory_checker,
1782 e.source.as_ref(),
1783 e.source_name.as_ref(),
1784 e.source_dictionary.as_ref(),
1785 e.target.as_ref(),
1786 e.target_name.as_ref(),
1787 e.availability.as_ref(),
1788 expose_to_parent_ids,
1789 expose_to_framework_ids,
1790 );
1791
1792 match e.target.as_ref() {
1795 Some(fdecl::Ref::Framework(_)) => {
1796 if e.subdir.is_some() {
1797 self.errors.push(Error::invalid_field(decl, "subdir"));
1798 }
1799 }
1800 _ => {}
1801 }
1802
1803 if let Some(subdir) = e.subdir.as_ref() {
1804 check_relative_path(Some(subdir), decl, "subdir", &mut self.errors);
1805 }
1806 }
1807 fdecl::Expose::Runner(e) => {
1808 let decl = DeclType::ExposeRunner;
1809 self.validate_expose_fields(
1810 decl,
1811 AllowableIds::One,
1812 CollectionSource::Deny,
1813 Self::runner_checker,
1814 e.source.as_ref(),
1815 e.source_name.as_ref(),
1816 e.source_dictionary.as_ref(),
1817 e.target.as_ref(),
1818 e.target_name.as_ref(),
1819 Some(&fdecl::Availability::Required),
1820 expose_to_parent_ids,
1821 expose_to_framework_ids,
1822 );
1823 }
1824 fdecl::Expose::Resolver(e) => {
1825 let decl = DeclType::ExposeResolver;
1826 self.validate_expose_fields(
1827 decl,
1828 AllowableIds::One,
1829 CollectionSource::Deny,
1830 Self::resolver_checker,
1831 e.source.as_ref(),
1832 e.source_name.as_ref(),
1833 e.source_dictionary.as_ref(),
1834 e.target.as_ref(),
1835 e.target_name.as_ref(),
1836 Some(&fdecl::Availability::Required),
1837 expose_to_parent_ids,
1838 expose_to_framework_ids,
1839 );
1840 }
1841 fdecl::Expose::Dictionary(e) => {
1842 let decl = DeclType::ExposeDictionary;
1843 self.validate_expose_fields(
1844 decl,
1845 AllowableIds::One,
1846 CollectionSource::Deny,
1847 Self::dictionary_checker,
1848 e.source.as_ref(),
1849 e.source_name.as_ref(),
1850 e.source_dictionary.as_ref(),
1851 e.target.as_ref(),
1852 e.target_name.as_ref(),
1853 e.availability.as_ref(),
1854 expose_to_parent_ids,
1855 expose_to_framework_ids,
1856 );
1857 }
1858 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1859 fdecl::Expose::Config(e) => {
1860 let decl = DeclType::ExposeConfig;
1861 self.validate_expose_fields(
1862 decl,
1863 AllowableIds::One,
1864 CollectionSource::Deny,
1865 Self::config_checker,
1866 e.source.as_ref(),
1867 e.source_name.as_ref(),
1868 None,
1869 e.target.as_ref(),
1870 e.target_name.as_ref(),
1871 e.availability.as_ref(),
1872 expose_to_parent_ids,
1873 expose_to_framework_ids,
1874 );
1875 }
1876 _ => {
1877 self.errors.push(Error::invalid_field(DeclType::Component, "expose"));
1878 }
1879 }
1880 }
1881
1882 fn validate_expose_fields(
1883 &mut self,
1884 decl: DeclType,
1885 allowable_ids: AllowableIds,
1886 collection_source: CollectionSource,
1887 capability_checker: impl Fn(&Self) -> &dyn Container,
1891 source: Option<&fdecl::Ref>,
1892 source_name: Option<&String>,
1893 source_dictionary: Option<&String>,
1894 target: Option<&fdecl::Ref>,
1895 target_name: Option<&'a String>,
1896 availability: Option<&fdecl::Availability>,
1897 expose_to_parent_ids: &mut HashMap<&'a str, AllowableIds>,
1898 expose_to_framework_ids: &mut HashMap<&'a str, AllowableIds>,
1899 ) {
1900 self.validate_expose_source(decl, collection_source, source, source_dictionary);
1901 check_route_availability(decl, availability, source, source_name, &mut self.errors);
1902 match target {
1903 Some(r) => match r {
1904 fdecl::Ref::Parent(_) => {}
1905 fdecl::Ref::Framework(_) => {}
1906 _ => {
1907 self.errors.push(Error::invalid_field(decl, "target"));
1908 }
1909 },
1910 None => {
1911 self.errors.push(Error::missing_field(decl, "target"));
1912 }
1913 }
1914 check_name(source_name, decl, "source_name", &mut self.errors);
1915 if source_dictionary.is_some() {
1916 check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
1917 }
1918 if check_name(target_name, decl, "target_name", &mut self.errors) {
1919 let maybe_ids_set = match target {
1920 Some(fdecl::Ref::Parent(_)) => Some(expose_to_parent_ids),
1921 Some(fdecl::Ref::Framework(_)) => Some(expose_to_framework_ids),
1922 _ => None,
1923 };
1924 if let Some(ids_set) = maybe_ids_set {
1925 let target_name = target_name.unwrap();
1926 if let Some(prev_state) = ids_set.insert(target_name, allowable_ids) {
1927 if prev_state == AllowableIds::One || prev_state != allowable_ids {
1928 self.errors.push(Error::duplicate_field(decl, "target_name", target_name));
1929 }
1930 }
1931 }
1932 }
1933
1934 self.validate_route_from_self(
1935 decl,
1936 source,
1937 source_name,
1938 source_dictionary,
1939 capability_checker,
1940 );
1941 }
1942
1943 fn validate_expose_source(
1944 &mut self,
1945 decl: DeclType,
1946 collection_source: CollectionSource,
1947 source: Option<&fdecl::Ref>,
1948 source_dictionary: Option<&String>,
1949 ) {
1950 match (source, source_dictionary) {
1951 (Some(fdecl::Ref::Self_(_)), _) => {}
1953 (Some(fdecl::Ref::Child(child)), _) => {
1954 let _ = self.validate_child_ref(decl, "source", &child, OfferType::Static);
1955 }
1956 (Some(fdecl::Ref::VoidType(_)), None) => {}
1958 (Some(fdecl::Ref::Framework(_)), None) => {}
1959 (Some(fdecl::Ref::Capability(c)), None) => {
1960 self.validate_source_capability(c, decl, "source");
1961 }
1962 (Some(fdecl::Ref::Collection(c)), None)
1963 if collection_source == CollectionSource::Allow =>
1964 {
1965 self.validate_source_collection(c, decl);
1966 }
1967 (None, _) => {
1969 self.errors.push(Error::missing_field(decl, "source"));
1970 }
1971 (_, _) => {
1973 self.errors.push(Error::invalid_field(decl, "source"));
1974 }
1975 }
1976 }
1977
1978 fn validate_offer_group(&mut self, offers: &'a [fdecl::Offer], offer_type: OfferType) {
1981 let mut offer_groups: HashMap<_, Vec<fdecl::OfferService>> = HashMap::new();
1982 let service_offers = offers
1983 .into_iter()
1984 .filter_map(|o| if let fdecl::Offer::Service(s) = o { Some(s) } else { None });
1985 for offer in service_offers {
1986 let key = Self::make_group_key(offer.target_name.as_ref(), offer.target.as_ref());
1987 if let Some(key) = key {
1988 offer_groups.entry(key).or_insert_with(|| vec![]).push(offer.clone());
1989 }
1990 }
1991 for offer_group in offer_groups.into_values() {
1992 if offer_group.len() == 1 {
1993 continue;
1996 }
1997
1998 self.validate_aggregation_has_same_availability(&offer_group);
1999
2000 let mut source_instance_filter_entries: HashSet<String> = HashSet::new();
2001 let mut service_source_names: HashSet<String> = HashSet::new();
2002 for o in offer_group {
2003 match (o.source_instance_filter, offer_type) {
2005 (Some(source_instance_filter), _) => {
2006 for instance_name in source_instance_filter {
2007 if !source_instance_filter_entries.insert(instance_name.clone()) {
2008 self.errors.push(Error::invalid_aggregate_offer(format!(
2011 "Conflicting source_instance_filter in aggregate service \
2012 offer, instance_name '{}' seen in filter lists multiple times",
2013 instance_name,
2014 )));
2015 }
2016 }
2017 }
2018 (None, OfferType::Static) => {}
2019 (None, OfferType::Dynamic) => {
2020 self.errors.push(Error::invalid_aggregate_offer(
2022 "source_instance_filter must be set for dynamic aggregate service \
2023 offers",
2024 ));
2025 }
2026 }
2027 service_source_names.insert(
2028 o.source_name
2029 .expect("Offer Service declarations must always contain source_name"),
2030 );
2031 }
2032
2033 if service_source_names.len() > 1 {
2034 self.errors.push(Error::invalid_aggregate_offer(format!(
2035 "All aggregate service offers must have the same source_name, saw {}. Use \
2036 renamed_instances to rename instance names to avoid conflict.",
2037 service_source_names.into_iter().sorted().collect::<Vec<String>>().join(", ")
2038 )));
2039 }
2040 }
2041 }
2042
2043 fn validate_offer_decl(&mut self, offer: &'a fdecl::Offer, offer_type: OfferType) {
2044 match offer {
2045 fdecl::Offer::Service(o) => {
2046 let decl = DeclType::OfferService;
2047 self.validate_offer_fields(
2048 decl,
2049 AllowableIds::Many,
2050 CollectionSource::Allow,
2051 Self::service_checker,
2052 o.source.as_ref(),
2053 o.source_name.as_ref(),
2054 o.source_dictionary.as_ref(),
2055 o.target.as_ref(),
2056 o.target_name.as_ref(),
2057 #[cfg(fuchsia_api_level_at_least = "HEAD")]
2058 Some(o.dependency_type.as_ref().unwrap_or(&fdecl::DependencyType::Strong)),
2059 #[cfg(fuchsia_api_level_less_than = "HEAD")]
2060 Some(&fdecl::DependencyType::Strong),
2061 o.availability.as_ref(),
2062 offer_type,
2063 );
2064 self.validate_filtered_service_fields(
2065 decl,
2066 o.source_instance_filter.as_ref(),
2067 o.renamed_instances.as_ref(),
2068 );
2069 }
2070 fdecl::Offer::Protocol(o) => {
2071 let decl = DeclType::OfferProtocol;
2072 self.validate_offer_fields(
2073 decl,
2074 AllowableIds::One,
2075 CollectionSource::Deny,
2076 Self::protocol_checker,
2077 o.source.as_ref(),
2078 o.source_name.as_ref(),
2079 o.source_dictionary.as_ref(),
2080 o.target.as_ref(),
2081 o.target_name.as_ref(),
2082 o.dependency_type.as_ref(),
2083 o.availability.as_ref(),
2084 offer_type,
2085 );
2086 }
2087 fdecl::Offer::Directory(o) => {
2088 let decl = DeclType::OfferDirectory;
2089 self.validate_offer_fields(
2090 decl,
2091 AllowableIds::One,
2092 CollectionSource::Deny,
2093 Self::directory_checker,
2094 o.source.as_ref(),
2095 o.source_name.as_ref(),
2096 o.source_dictionary.as_ref(),
2097 o.target.as_ref(),
2098 o.target_name.as_ref(),
2099 o.dependency_type.as_ref(),
2100 o.availability.as_ref(),
2101 offer_type,
2102 );
2103 if let Some(subdir) = o.subdir.as_ref() {
2104 check_relative_path(
2105 Some(subdir),
2106 DeclType::OfferDirectory,
2107 "subdir",
2108 &mut self.errors,
2109 );
2110 }
2111 }
2112 fdecl::Offer::Storage(o) => {
2113 let decl = DeclType::OfferStorage;
2114 self.validate_storage_offer_fields(
2115 decl,
2116 Self::storage_checker,
2117 o.source.as_ref(),
2118 o.source_name.as_ref(),
2119 o.target.as_ref(),
2120 o.target_name.as_ref(),
2121 o.availability.as_ref(),
2122 offer_type,
2123 );
2124 }
2125 fdecl::Offer::Runner(o) => {
2126 let decl = DeclType::OfferRunner;
2127 self.validate_offer_fields(
2128 decl,
2129 AllowableIds::One,
2130 CollectionSource::Deny,
2131 Self::runner_checker,
2132 o.source.as_ref(),
2133 o.source_name.as_ref(),
2134 o.source_dictionary.as_ref(),
2135 o.target.as_ref(),
2136 o.target_name.as_ref(),
2137 Some(&fdecl::DependencyType::Strong),
2138 Some(&fdecl::Availability::Required),
2139 offer_type,
2140 );
2141 }
2142 fdecl::Offer::Resolver(o) => {
2143 let decl = DeclType::OfferResolver;
2144 self.validate_offer_fields(
2145 decl,
2146 AllowableIds::One,
2147 CollectionSource::Deny,
2148 Self::resolver_checker,
2149 o.source.as_ref(),
2150 o.source_name.as_ref(),
2151 o.source_dictionary.as_ref(),
2152 o.target.as_ref(),
2153 o.target_name.as_ref(),
2154 Some(&fdecl::DependencyType::Strong),
2155 Some(&fdecl::Availability::Required),
2156 offer_type,
2157 );
2158 }
2159 fdecl::Offer::EventStream(e) => {
2160 self.validate_event_stream_offer_fields(e, offer_type);
2161 }
2162 fdecl::Offer::Dictionary(o) => {
2163 let decl = DeclType::OfferDictionary;
2164 self.validate_offer_fields(
2165 decl,
2166 AllowableIds::One,
2167 CollectionSource::Deny,
2168 Self::dictionary_checker,
2169 o.source.as_ref(),
2170 o.source_name.as_ref(),
2171 o.source_dictionary.as_ref(),
2172 o.target.as_ref(),
2173 o.target_name.as_ref(),
2174 o.dependency_type.as_ref(),
2175 o.availability.as_ref(),
2176 offer_type,
2177 );
2178 }
2179 #[cfg(fuchsia_api_level_at_least = "HEAD")]
2180 fdecl::Offer::Config(o) => {
2181 let decl = DeclType::OfferConfig;
2182 self.validate_offer_fields(
2183 decl,
2184 AllowableIds::One,
2185 CollectionSource::Deny,
2186 Self::config_checker,
2187 o.source.as_ref(),
2188 o.source_name.as_ref(),
2189 None,
2190 o.target.as_ref(),
2191 o.target_name.as_ref(),
2192 Some(&fdecl::DependencyType::Strong),
2193 o.availability.as_ref(),
2194 offer_type,
2195 );
2196 }
2197 fdecl::OfferUnknown!() => {
2198 self.errors.push(Error::invalid_field(DeclType::Component, "offer"));
2199 }
2200 }
2201 }
2202
2203 fn validate_offer_fields(
2204 &mut self,
2205 decl: DeclType,
2206 allowable_names: AllowableIds,
2207 collection_source: CollectionSource,
2208 capability_checker: impl Fn(&Self) -> &dyn Container,
2209 source: Option<&'a fdecl::Ref>,
2210 source_name: Option<&'a String>,
2211 source_dictionary: Option<&'a String>,
2212 target: Option<&'a fdecl::Ref>,
2213 target_name: Option<&'a String>,
2214 dependency_type: Option<&'a fdecl::DependencyType>,
2215 availability: Option<&'a fdecl::Availability>,
2216 offer_type: OfferType,
2217 ) {
2218 self.validate_offer_source(decl, collection_source, source, source_dictionary, offer_type);
2219 check_route_availability(decl, availability, source, source_name, &mut self.errors);
2220 check_offer_name(source_name, decl, "source_name", offer_type, &mut self.errors);
2221 if source_dictionary.is_some() {
2222 check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
2223 }
2224 self.validate_offer_target(decl, allowable_names, target, target_name, offer_type);
2225 check_offer_name(target_name, decl, "target_name", offer_type, &mut self.errors);
2226
2227 if dependency_type.is_none() {
2228 self.errors.push(Error::missing_field(decl, "dependency_type"));
2229 }
2230
2231 self.validate_route_from_self(
2232 decl,
2233 source,
2234 source_name,
2235 source_dictionary,
2236 capability_checker,
2237 );
2238 }
2239
2240 fn validate_offer_source(
2241 &mut self,
2242 decl: DeclType,
2243 collection_source: CollectionSource,
2244 source: Option<&'a fdecl::Ref>,
2245 source_dictionary: Option<&'a String>,
2246 offer_type: OfferType,
2247 ) {
2248 match (source, source_dictionary) {
2249 (Some(fdecl::Ref::Parent(_)), _) => {}
2251 (Some(fdecl::Ref::Self_(_)), _) => {}
2252 (Some(fdecl::Ref::Child(child)), _) => {
2253 self.validate_child_ref(decl, "source", &child, offer_type);
2254 }
2255 (Some(fdecl::Ref::VoidType(_)), None) => {}
2257 (Some(fdecl::Ref::Framework(_)), None) => {}
2258 (Some(fdecl::Ref::Capability(c)), None) => {
2259 self.validate_source_capability(c, decl, "source");
2260 }
2261 (Some(fdecl::Ref::Collection(c)), None)
2262 if collection_source == CollectionSource::Allow =>
2263 {
2264 self.validate_source_collection(c, decl);
2265 }
2266 (None, _) => self.errors.push(Error::missing_field(decl, "source")),
2268 (_, _) => self.errors.push(Error::invalid_field(decl, "source")),
2270 }
2271 }
2272
2273 fn validate_storage_offer_fields(
2274 &mut self,
2275 decl: DeclType,
2276 capability_checker: impl Fn(&Self) -> &dyn Container,
2280 source: Option<&'a fdecl::Ref>,
2281 source_name: Option<&'a String>,
2282 target: Option<&'a fdecl::Ref>,
2283 target_name: Option<&'a String>,
2284 availability: Option<&fdecl::Availability>,
2285 offer_type: OfferType,
2286 ) {
2287 match source {
2288 Some(fdecl::Ref::Parent(_) | fdecl::Ref::VoidType(_) | fdecl::Ref::Self_(_)) => {}
2289 Some(_) => {
2290 self.errors.push(Error::invalid_field(decl, "source"));
2291 }
2292 None => {
2293 self.errors.push(Error::missing_field(decl, "source"));
2294 }
2295 }
2296 check_route_availability(decl, availability, source, source_name, &mut self.errors);
2297 check_offer_name(source_name, decl, "source_name", offer_type, &mut self.errors);
2298 self.validate_offer_target(decl, AllowableIds::One, target, target_name, offer_type);
2299 check_offer_name(target_name, decl, "target_name", offer_type, &mut self.errors);
2300
2301 if let (Some(fdecl::Ref::Self_(_)), Some(name)) = (source, source_name) {
2302 if !(capability_checker)(self).contains(name) {
2303 self.errors.push(Error::invalid_capability(decl, "source", name));
2304 }
2305 }
2306 }
2307
2308 fn validate_event_stream_offer_fields(
2309 &mut self,
2310 event_stream: &'a fdecl::OfferEventStream,
2311 offer_type: OfferType,
2312 ) {
2313 let decl = DeclType::OfferEventStream;
2314 check_name(event_stream.source_name.as_ref(), decl, "source_name", &mut self.errors);
2315 if event_stream.target == Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})) {
2316 self.errors.push(Error::invalid_field(DeclType::OfferEventStream, "target"));
2318 }
2319 if let Some(scope) = &event_stream.scope {
2320 if scope.is_empty() {
2321 self.errors.push(Error::invalid_field(decl, "scope"));
2322 }
2323 for value in scope {
2324 match value {
2325 fdecl::Ref::Child(child) => {
2326 self.validate_child_ref(
2327 DeclType::OfferEventStream,
2328 "scope",
2329 &child,
2330 offer_type,
2331 );
2332 }
2333 fdecl::Ref::Collection(collection) => {
2334 self.validate_collection_ref(
2335 DeclType::OfferEventStream,
2336 "scope",
2337 &collection,
2338 );
2339 }
2340 _ => {
2341 self.errors.push(Error::invalid_field(DeclType::OfferEventStream, "scope"));
2342 }
2343 }
2344 }
2345 }
2346 match event_stream.source {
2348 Some(
2349 fdecl::Ref::Parent(_)
2350 | fdecl::Ref::Framework(_)
2351 | fdecl::Ref::Child(_)
2352 | fdecl::Ref::VoidType(_),
2353 ) => {}
2354 Some(_) => {
2355 self.errors.push(Error::invalid_field(decl, "source"));
2356 }
2357 None => {
2358 self.errors.push(Error::missing_field(decl, "source"));
2359 }
2360 };
2361
2362 check_route_availability(
2363 decl,
2364 event_stream.availability.as_ref(),
2365 event_stream.source.as_ref(),
2366 event_stream.source_name.as_ref(),
2367 &mut self.errors,
2368 );
2369
2370 self.validate_offer_target(
2371 decl,
2372 AllowableIds::One,
2373 event_stream.target.as_ref(),
2374 event_stream.target_name.as_ref(),
2375 offer_type,
2376 );
2377 check_name(event_stream.target_name.as_ref(), decl, "target_name", &mut self.errors);
2378 }
2379
2380 fn validate_child_ref(
2382 &mut self,
2383 decl: DeclType,
2384 field_name: &str,
2385 child: &fdecl::ChildRef,
2386 offer_type: OfferType,
2387 ) -> bool {
2388 if offer_type == OfferType::Dynamic && child.collection.is_some() {
2389 return self.validate_dynamic_child_ref(decl, field_name, child);
2390 }
2391 let mut valid = true;
2395 if !check_name(
2396 Some(&child.name),
2397 decl,
2398 &format!("{}.child.name", field_name),
2399 &mut self.errors,
2400 ) {
2401 valid = false;
2402 }
2403 if child.collection.is_some() {
2404 self.errors
2405 .push(Error::extraneous_field(decl, format!("{}.child.collection", field_name)));
2406 valid = false;
2407 }
2408 if !valid {
2409 return false;
2410 }
2411
2412 let name: &str = &child.name;
2414 if !self.all_children.contains_key(name) {
2415 self.errors.push(Error::invalid_child(decl, field_name, name));
2416 return false;
2417 }
2418
2419 true
2420 }
2421
2422 fn validate_dynamic_child_ref(
2427 &mut self,
2428 decl: DeclType,
2429 field_name: &str,
2430 child: &fdecl::ChildRef,
2431 ) -> bool {
2432 let mut valid = true;
2436 if !check_dynamic_name(
2437 Some(&child.name),
2438 decl,
2439 &format!("{}.child.name", field_name),
2440 &mut self.errors,
2441 ) {
2442 valid = false;
2443 }
2444 if !check_name(
2445 child.collection.as_ref(),
2446 decl,
2447 &format!("{}.child.collection", field_name),
2448 &mut self.errors,
2449 ) {
2450 valid = false;
2451 }
2452 valid
2453 }
2454
2455 fn validate_collection_ref(
2457 &mut self,
2458 decl: DeclType,
2459 field_name: &str,
2460 collection: &fdecl::CollectionRef,
2461 ) -> bool {
2462 if !check_name(
2464 Some(&collection.name),
2465 decl,
2466 &format!("{}.collection.name", field_name),
2467 &mut self.errors,
2468 ) {
2469 return false;
2470 }
2471
2472 if !self.all_collections.contains(&collection.name as &str) {
2474 self.errors.push(Error::invalid_collection(decl, field_name, &collection.name as &str));
2475 return false;
2476 }
2477
2478 true
2479 }
2480
2481 fn validate_offer_target(
2482 &mut self,
2483 decl: DeclType,
2484 allowable_names: AllowableIds,
2485 target: Option<&'a fdecl::Ref>,
2486 target_name: Option<&'a String>,
2487 offer_type: OfferType,
2488 ) {
2489 match target {
2490 Some(fdecl::Ref::Child(c)) => {
2491 self.validate_target_child(decl, allowable_names, c, target_name, offer_type);
2492 }
2493 Some(fdecl::Ref::Collection(c)) => {
2494 self.validate_target_collection(decl, allowable_names, c, target_name);
2495 }
2496 Some(fdecl::Ref::Capability(c)) => {
2497 if let Some(d) = self.all_dictionaries.get(&c.name.as_str()) {
2499 if d.source_path.is_some() {
2500 self.errors.push(Error::invalid_field(decl, "target"));
2503 }
2504 } else {
2505 self.errors.push(Error::invalid_field(decl, "target"));
2506 }
2507 }
2508 Some(_) => {
2509 self.errors.push(Error::invalid_field(decl, "target"));
2510 }
2511 None => {
2512 self.errors.push(Error::missing_field(decl, "target"));
2513 }
2514 }
2515 }
2516
2517 fn validate_target_child(
2518 &mut self,
2519 decl: DeclType,
2520 allowable_names: AllowableIds,
2521 child: &'a fdecl::ChildRef,
2522 target_name: Option<&'a String>,
2523 offer_type: OfferType,
2524 ) {
2525 if !self.validate_child_ref(decl, "target", child, offer_type) {
2526 return;
2527 }
2528 if let Some(target_name) = target_name {
2529 let names_for_target = self
2530 .target_ids
2531 .entry(TargetId::Component(
2532 &child.name,
2533 child.collection.as_ref().map(|s| s.as_str()),
2534 ))
2535 .or_insert(HashMap::new());
2536 if let Some(prev_state) = names_for_target.insert(target_name, allowable_names) {
2537 if prev_state == AllowableIds::One || prev_state != allowable_names {
2538 self.errors.push(Error::duplicate_field(
2539 decl,
2540 "target_name",
2541 target_name as &str,
2542 ));
2543 }
2544 }
2545 if let Some(collection) = child.collection.as_ref() {
2546 if let Some(names_for_target) =
2547 self.target_ids.get(&TargetId::Collection(&collection))
2548 {
2549 if names_for_target.contains_key(&target_name.as_str()) {
2550 self.errors.push(Error::duplicate_field(
2552 decl,
2553 "target_name",
2554 target_name as &str,
2555 ));
2556 }
2557 }
2558 }
2559 }
2560 }
2561
2562 fn validate_target_collection(
2563 &mut self,
2564 decl: DeclType,
2565 allowable_names: AllowableIds,
2566 collection: &'a fdecl::CollectionRef,
2567 target_name: Option<&'a String>,
2568 ) {
2569 if !self.validate_collection_ref(decl, "target", &collection) {
2570 return;
2571 }
2572 if let Some(target_name) = target_name {
2573 let names_for_target = self
2574 .target_ids
2575 .entry(TargetId::Collection(&collection.name))
2576 .or_insert(HashMap::new());
2577 if let Some(prev_state) = names_for_target.insert(target_name, allowable_names) {
2578 if prev_state == AllowableIds::One || prev_state != allowable_names {
2579 self.errors.push(Error::duplicate_field(
2580 decl,
2581 "target_name",
2582 target_name as &str,
2583 ));
2584 }
2585 }
2586 }
2587 }
2588
2589 fn validate_route_from_self(
2590 &mut self,
2591 decl: DeclType,
2592 source: Option<&fdecl::Ref>,
2593 source_name: Option<&String>,
2594 source_dictionary: Option<&String>,
2595 capability_checker: impl Fn(&Self) -> &dyn Container,
2596 ) {
2597 let (Some(fdecl::Ref::Self_(_)), Some(name)) = (source, source_name) else {
2598 return;
2599 };
2600 match source_dictionary {
2601 Some(source_dictionary) => {
2602 if let Ok(path) = cm_types::RelativePath::new(source_dictionary) {
2603 if let Some(first_segment) = path.iter_segments().next().map(|s| s.as_str()) {
2604 if !self.all_dictionaries.contains_key(first_segment) {
2605 self.errors.push(Error::invalid_capability(
2606 decl,
2607 "source",
2608 first_segment,
2609 ));
2610 }
2611 }
2612 }
2613 }
2614 None => {
2615 if !(capability_checker)(self).contains(name) {
2616 self.errors.push(Error::invalid_capability(decl, "source", name));
2617 }
2618 }
2619 }
2620 }
2621
2622 fn service_checker(&self) -> &dyn Container {
2625 &self.all_services
2626 }
2627 fn protocol_checker(&self) -> &dyn Container {
2628 &self.all_protocols
2629 }
2630 fn directory_checker(&self) -> &dyn Container {
2631 &self.all_directories
2632 }
2633 fn runner_checker(&self) -> &dyn Container {
2634 &self.all_runners
2635 }
2636 fn resolver_checker(&self) -> &dyn Container {
2637 &self.all_resolvers
2638 }
2639
2640 fn dictionary_checker(&self) -> &dyn Container {
2641 &self.all_dictionaries
2642 }
2643
2644 #[cfg(fuchsia_api_level_at_least = "HEAD")]
2645 fn config_checker(&self) -> &dyn Container {
2646 &self.all_configs
2647 }
2648 fn storage_checker(&self) -> &dyn Container {
2649 &self.all_storages
2650 }
2651 fn event_stream_checker(&self) -> &dyn Container {
2652 struct AlwaysTrueContainer {}
2656 impl Container for AlwaysTrueContainer {
2657 fn contains(&self, _key: &str) -> bool {
2658 true
2659 }
2660 }
2661 static CONTAINER: AlwaysTrueContainer = AlwaysTrueContainer {};
2662 &CONTAINER
2663 }
2664}
2665
2666#[cfg(test)]
2667mod tests {
2668 use super::*;
2669 use cm_types::MAX_LONG_NAME_LENGTH;
2670 use test_case::test_case;
2671 use {
2672 fidl_fuchsia_component_decl as fdecl, fidl_fuchsia_data as fdata, fidl_fuchsia_io as fio,
2673 };
2674
2675 macro_rules! test_validate {
2676 (
2677 $(
2678 $test_name:ident => {
2679 input = $input:expr,
2680 result = $result:expr,
2681 },
2682 )+
2683 ) => {
2684 $(
2685 #[test]
2686 fn $test_name() {
2687 validate_test($input, $result);
2688 }
2689 )+
2690 }
2691 }
2692
2693 macro_rules! test_validate_any_result {
2694 (
2695 $(
2696 $test_name:ident => {
2697 input = $input:expr,
2698 results = $results:expr,
2699 },
2700 )+
2701 ) => {
2702 $(
2703 #[test]
2704 fn $test_name() {
2705 validate_test_any_result($input, $results);
2706 }
2707 )+
2708 }
2709 }
2710
2711 macro_rules! test_validate_values_data {
2712 (
2713 $(
2714 $test_name:ident => {
2715 input = $input:expr,
2716 result = $result:expr,
2717 },
2718 )+
2719 ) => {
2720 $(
2721 #[test]
2722 fn $test_name() {
2723 validate_values_data_test($input, $result);
2724 }
2725 )+
2726 }
2727 }
2728
2729 macro_rules! test_validate_capabilities {
2730 (
2731 $(
2732 $test_name:ident => {
2733 input = $input:expr,
2734 as_builtin = $as_builtin:expr,
2735 result = $result:expr,
2736 },
2737 )+
2738 ) => {
2739 $(
2740 #[test]
2741 fn $test_name() {
2742 validate_capabilities_test($input, $as_builtin, $result);
2743 }
2744 )+
2745 }
2746 }
2747
2748 macro_rules! test_dependency {
2749 (
2750 $(
2751 ($test_name:ident) => {
2752 ty = $ty:expr,
2753 offer_decl = $offer_decl:expr,
2754 },
2755 )+
2756 ) => {
2757 $(
2758 #[test]
2759 fn $test_name() {
2760 let mut decl = new_component_decl();
2761 let dependencies = vec![
2762 ("a", "b"),
2763 ("b", "a"),
2764 ];
2765 let offers = dependencies.into_iter().map(|(from,to)| {
2766 let mut offer_decl = $offer_decl;
2767 offer_decl.source = Some(fdecl::Ref::Child(
2768 fdecl::ChildRef { name: from.to_string(), collection: None },
2769 ));
2770 offer_decl.target = Some(fdecl::Ref::Child(
2771 fdecl::ChildRef { name: to.to_string(), collection: None },
2772 ));
2773 $ty(offer_decl)
2774 }).collect();
2775 let children = ["a", "b"].iter().map(|name| {
2776 fdecl::Child {
2777 name: Some(name.to_string()),
2778 url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
2779 startup: Some(fdecl::StartupMode::Lazy),
2780 on_terminate: None,
2781 environment: None,
2782 ..Default::default()
2783 }
2784 }).collect();
2785 decl.offers = Some(offers);
2786 decl.children = Some(children);
2787 let result = Err(ErrorList::new(vec![
2788 Error::dependency_cycle("{{child a -> child b -> child a}}")
2789 ]));
2790 validate_test(decl, result);
2791 }
2792 )+
2793 }
2794 }
2795
2796 macro_rules! test_weak_dependency {
2797 (
2798 $(
2799 ($test_name:ident) => {
2800 ty = $ty:expr,
2801 offer_decl = $offer_decl:expr,
2802 },
2803 )+
2804 ) => {
2805 $(
2806 #[test_case(fdecl::DependencyType::Weak)]
2807 fn $test_name(weak_dep: fdecl::DependencyType) {
2808 let mut decl = new_component_decl();
2809 let offers = vec![
2810 {
2811 let mut offer_decl = $offer_decl;
2812 offer_decl.source = Some(fdecl::Ref::Child(
2813 fdecl::ChildRef { name: "a".to_string(), collection: None },
2814 ));
2815 offer_decl.target = Some(fdecl::Ref::Child(
2816 fdecl::ChildRef { name: "b".to_string(), collection: None },
2817 ));
2818 offer_decl.dependency_type = Some(fdecl::DependencyType::Strong);
2819 $ty(offer_decl)
2820 },
2821 {
2822 let mut offer_decl = $offer_decl;
2823 offer_decl.source = Some(fdecl::Ref::Child(
2824 fdecl::ChildRef { name: "b".to_string(), collection: None },
2825 ));
2826 offer_decl.target = Some(fdecl::Ref::Child(
2827 fdecl::ChildRef { name: "a".to_string(), collection: None },
2828 ));
2829 offer_decl.dependency_type = Some(weak_dep);
2830 $ty(offer_decl)
2831 },
2832 ];
2833 let children = ["a", "b"].iter().map(|name| {
2834 fdecl::Child {
2835 name: Some(name.to_string()),
2836 url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
2837 startup: Some(fdecl::StartupMode::Lazy),
2838 on_terminate: None,
2839 environment: None,
2840 ..Default::default()
2841 }
2842 }).collect();
2843 decl.offers = Some(offers);
2844 decl.children = Some(children);
2845 let result = Ok(());
2846 validate_test(decl, result);
2847 }
2848 )+
2849 }
2850 }
2851
2852 #[track_caller]
2853 fn validate_test(input: fdecl::Component, expected_res: Result<(), ErrorList>) {
2854 let res = validate(&input, &mut DirectedGraph::new());
2855 assert_eq!(res, expected_res);
2856 }
2857
2858 #[track_caller]
2859 fn validate_test_any_result(input: fdecl::Component, expected_res: Vec<Result<(), ErrorList>>) {
2860 let res = format!("{:?}", validate(&input, &mut DirectedGraph::new()));
2861 let expected_res_debug = format!("{:?}", expected_res);
2862
2863 let matched_exp =
2864 expected_res.into_iter().find(|expected| res == format!("{:?}", expected));
2865
2866 assert!(
2867 matched_exp.is_some(),
2868 "assertion failed: Expected one of:\n{:?}\nActual:\n{:?}",
2869 expected_res_debug,
2870 res
2871 );
2872 }
2873
2874 #[track_caller]
2875 fn validate_values_data_test(
2876 input: fdecl::ConfigValuesData,
2877 expected_res: Result<(), ErrorList>,
2878 ) {
2879 let res = validate_values_data(&input);
2880 assert_eq!(res, expected_res);
2881 }
2882
2883 #[track_caller]
2884 fn validate_capabilities_test(
2885 input: Vec<fdecl::Capability>,
2886 as_builtin: bool,
2887 expected_res: Result<(), ErrorList>,
2888 ) {
2889 let res = validate_capabilities(&input, as_builtin);
2890 assert_eq!(res, expected_res);
2891 }
2892
2893 fn new_component_decl() -> fdecl::Component {
2894 fdecl::Component {
2895 program: None,
2896 uses: None,
2897 exposes: None,
2898 offers: None,
2899 facets: None,
2900 capabilities: None,
2901 children: None,
2902 collections: None,
2903 environments: None,
2904 ..Default::default()
2905 }
2906 }
2907
2908 test_validate_any_result! {
2909 test_validate_use_disallows_nested_dirs => {
2910 input = {
2911 let mut decl = new_component_decl();
2912 decl.uses = Some(vec![
2913 fdecl::Use::Directory(fdecl::UseDirectory {
2914 dependency_type: Some(fdecl::DependencyType::Strong),
2915 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2916 source_name: Some("abc".to_string()),
2917 target_path: Some("/foo/bar".to_string()),
2918 rights: Some(fio::Operations::CONNECT),
2919 subdir: None,
2920 ..Default::default()
2921 }),
2922 fdecl::Use::Directory(fdecl::UseDirectory {
2923 dependency_type: Some(fdecl::DependencyType::Strong),
2924 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2925 source_name: Some("abc".to_string()),
2926 target_path: Some("/foo/bar/baz".to_string()),
2927 rights: Some(fio::Operations::CONNECT),
2928 subdir: None,
2929 ..Default::default()
2930 }),
2931 ]);
2932 decl
2933 },
2934 results = vec![
2935 Err(ErrorList::new(vec![
2936 Error::invalid_path_overlap(
2937 DeclType::UseDirectory, "/foo/bar/baz", DeclType::UseDirectory, "/foo/bar"),
2938 ])),
2939 Err(ErrorList::new(vec![
2940 Error::invalid_path_overlap(
2941 DeclType::UseDirectory, "/foo/bar", DeclType::UseDirectory, "/foo/bar/baz"),
2942 ])),
2943 ],
2944 },
2945 test_validate_use_disallows_nested_dirs_storage => {
2946 input = {
2947 let mut decl = new_component_decl();
2948 decl.uses = Some(vec![
2949 fdecl::Use::Storage(fdecl::UseStorage {
2950 source_name: Some("abc".to_string()),
2951 target_path: Some("/foo/bar".to_string()),
2952 ..Default::default()
2953 }),
2954 fdecl::Use::Storage(fdecl::UseStorage {
2955 source_name: Some("abc".to_string()),
2956 target_path: Some("/foo/bar/baz".to_string()),
2957 ..Default::default()
2958 }),
2959 ]);
2960 decl
2961 },
2962 results = vec![
2963 Err(ErrorList::new(vec![
2964 Error::invalid_path_overlap(
2965 DeclType::UseStorage, "/foo/bar/baz", DeclType::UseStorage, "/foo/bar"),
2966 ])),
2967 Err(ErrorList::new(vec![
2968 Error::invalid_path_overlap(
2969 DeclType::UseStorage, "/foo/bar", DeclType::UseStorage, "/foo/bar/baz"),
2970 ])),
2971 ],
2972 },
2973 test_validate_use_disallows_nested_dirs_directory_and_storage => {
2974 input = {
2975 let mut decl = new_component_decl();
2976 decl.uses = Some(vec![
2977 fdecl::Use::Directory(fdecl::UseDirectory {
2978 dependency_type: Some(fdecl::DependencyType::Strong),
2979 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2980 source_name: Some("abc".to_string()),
2981 target_path: Some("/foo/bar".to_string()),
2982 rights: Some(fio::Operations::CONNECT),
2983 subdir: None,
2984 ..Default::default()
2985 }),
2986 fdecl::Use::Storage(fdecl::UseStorage {
2987 source_name: Some("abc".to_string()),
2988 target_path: Some("/foo/bar/baz".to_string()),
2989 ..Default::default()
2990 }),
2991 ]);
2992 decl
2993 },
2994 results = vec![
2995 Err(ErrorList::new(vec![
2996 Error::invalid_path_overlap(
2997 DeclType::UseStorage, "/foo/bar/baz", DeclType::UseDirectory, "/foo/bar"),
2998 ])),
2999 Err(ErrorList::new(vec![
3000 Error::invalid_path_overlap(
3001 DeclType::UseDirectory, "/foo/bar", DeclType::UseStorage, "/foo/bar/baz"),
3002 ])),
3003 ],
3004 },
3005 test_validate_use_disallows_common_prefixes_protocol => {
3006 input = {
3007 let mut decl = new_component_decl();
3008 decl.uses = Some(vec![
3009 fdecl::Use::Directory(fdecl::UseDirectory {
3010 dependency_type: Some(fdecl::DependencyType::Strong),
3011 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3012 source_name: Some("abc".to_string()),
3013 target_path: Some("/foo/bar".to_string()),
3014 rights: Some(fio::Operations::CONNECT),
3015 subdir: None,
3016 ..Default::default()
3017 }),
3018 fdecl::Use::Protocol(fdecl::UseProtocol {
3019 dependency_type: Some(fdecl::DependencyType::Strong),
3020 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3021 source_name: Some("crow".to_string()),
3022 target_path: Some("/foo/bar/fuchsia.2".to_string()),
3023 ..Default::default()
3024 }),
3025 ]);
3026 decl
3027 },
3028 results = vec![
3029 Err(ErrorList::new(vec![
3030 Error::invalid_path_overlap(
3031 DeclType::UseProtocol, "/foo/bar/fuchsia.2", DeclType::UseDirectory, "/foo/bar"),
3032 ])),
3033 Err(ErrorList::new(vec![
3034 Error::invalid_path_overlap(
3035 DeclType::UseDirectory, "/foo/bar", DeclType::UseProtocol, "/foo/bar/fuchsia.2"),
3036 ])),
3037 ],
3038 },
3039 test_validate_use_disallows_common_prefixes_service => {
3040 input = {
3041 let mut decl = new_component_decl();
3042 decl.uses = Some(vec![
3043 fdecl::Use::Directory(fdecl::UseDirectory {
3044 dependency_type: Some(fdecl::DependencyType::Strong),
3045 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3046 source_name: Some("abc".to_string()),
3047 target_path: Some("/foo/bar".to_string()),
3048 rights: Some(fio::Operations::CONNECT),
3049 subdir: None,
3050 ..Default::default()
3051 }),
3052 fdecl::Use::Service(fdecl::UseService {
3053 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3054 source_name: Some("space".to_string()),
3055 target_path: Some("/foo/bar/baz/fuchsia.logger.Log".to_string()),
3056 dependency_type: Some(fdecl::DependencyType::Strong),
3057 ..Default::default()
3058 }),
3059 ]);
3060 decl
3061 },
3062 results = vec![
3063 Err(ErrorList::new(vec![
3064 Error::invalid_path_overlap(
3065 DeclType::UseService, "/foo/bar/baz/fuchsia.logger.Log", DeclType::UseDirectory, "/foo/bar"),
3066 ])),
3067 Err(ErrorList::new(vec![
3068 Error::invalid_path_overlap(
3069 DeclType::UseDirectory, "/foo/bar", DeclType::UseService, "/foo/bar/baz/fuchsia.logger.Log"),
3070 ])),
3071 ],
3072 },
3073 test_validate_use_disallows_pkg => {
3074 input = {
3075 let mut decl = new_component_decl();
3076 decl.uses = Some(vec![
3077 fdecl::Use::Directory(fdecl::UseDirectory {
3078 dependency_type: Some(fdecl::DependencyType::Strong),
3079 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3080 source_name: Some("abc".to_string()),
3081 target_path: Some("/pkg".to_string()),
3082 rights: Some(fio::Operations::CONNECT),
3083 subdir: None,
3084 ..Default::default()
3085 }),
3086 ]);
3087 decl
3088 },
3089 results = vec![
3090 Err(ErrorList::new(vec![
3091 Error::pkg_path_overlap(DeclType::UseDirectory, "/pkg"),
3092 ])),
3093 ],
3094 },
3095 test_validate_use_disallows_pkg_overlap => {
3096 input = {
3097 let mut decl = new_component_decl();
3098 decl.uses = Some(vec![
3099 fdecl::Use::Directory(fdecl::UseDirectory {
3100 dependency_type: Some(fdecl::DependencyType::Strong),
3101 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3102 source_name: Some("abc".to_string()),
3103 target_path: Some("/pkg/foo".to_string()),
3104 rights: Some(fio::Operations::CONNECT),
3105 subdir: None,
3106 ..Default::default()
3107 }),
3108 ]);
3109 decl
3110 },
3111 results = vec![
3112 Err(ErrorList::new(vec![
3113 Error::pkg_path_overlap(DeclType::UseDirectory, "/pkg/foo"),
3114 ])),
3115 ],
3116 },
3117 test_validate_use_optional_config_correct => {
3118 input = {
3119 let mut decl = new_component_decl();
3120 decl.uses = Some(vec![
3121 fdecl::Use::Config(fdecl::UseConfiguration {
3122 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3123 source_name: Some("abc".to_string()),
3124 target_name: Some("foo".to_string()),
3125 availability: Some(fdecl::Availability::Optional),
3126 type_: Some(fdecl::ConfigType {
3127 layout: fdecl::ConfigTypeLayout::Bool,
3128 parameters: Some(Vec::new()),
3129 constraints: Vec::new(),
3130 }),
3131 ..Default::default()
3132 }),
3133 ]);
3134 decl.config = Some(fdecl::ConfigSchema {
3135 fields: Some(vec![fdecl::ConfigField {
3136 key: Some("foo".into()),
3137 type_: Some(fdecl::ConfigType {
3138 layout: fdecl::ConfigTypeLayout::Bool,
3139 parameters: Some(Vec::new()),
3140 constraints: Vec::new(),
3141 }),
3142 mutability: None,
3143 ..Default::default()}]),
3144 checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3145 value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3146 ..Default::default()
3147 });
3148 decl
3149 },
3150 results = vec![Ok(())],
3151 },
3152 test_validate_use_optional_config_no_config_schema => {
3153 input = {
3154 let mut decl = new_component_decl();
3155 decl.uses = Some(vec![
3156 fdecl::Use::Config(fdecl::UseConfiguration {
3157 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3158 source_name: Some("abc".to_string()),
3159 target_name: Some("foo".to_string()),
3160 availability: Some(fdecl::Availability::Optional),
3161 type_: Some(fdecl::ConfigType {
3162 layout: fdecl::ConfigTypeLayout::Bool,
3163 parameters: None,
3164 constraints: Vec::new(),
3165 }),
3166 ..Default::default()
3167 }),
3168 ]);
3169 decl
3170 },
3171 results = vec![
3172 Err(ErrorList::new(vec![
3173 Error::missing_field(DeclType::ConfigField, "config"),
3174 ])),
3175 ],
3176 },
3177 test_validate_use_optional_config_no_config_field => {
3178 input = {
3179 let mut decl = new_component_decl();
3180 decl.uses = Some(vec![
3181 fdecl::Use::Config(fdecl::UseConfiguration {
3182 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3183 source_name: Some("abc".to_string()),
3184 target_name: Some("foo".to_string()),
3185 availability: Some(fdecl::Availability::Optional),
3186 type_: Some(fdecl::ConfigType {
3187 layout: fdecl::ConfigTypeLayout::Bool,
3188 parameters: None,
3189 constraints: Vec::new(),
3190 }),
3191 ..Default::default()
3192 }),
3193 ]);
3194 decl.config = Some(fdecl::ConfigSchema {
3195 fields: Some(vec![]),
3196 checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3197 value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3198 ..Default::default()
3199 });
3200 decl
3201 },
3202 results = vec![
3203 Err(ErrorList::new(vec![
3204 Error::missing_field(DeclType::ConfigField, "foo"),
3205 ])),
3206 ],
3207 },
3208 test_validate_use_optional_config_bad_type => {
3209 input = {
3210 let mut decl = new_component_decl();
3211 decl.uses = Some(vec![
3212 fdecl::Use::Config(fdecl::UseConfiguration {
3213 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3214 source_name: Some("abc".to_string()),
3215 target_name: Some("foo".to_string()),
3216 availability: Some(fdecl::Availability::Optional),
3217 type_: Some(fdecl::ConfigType {
3218 layout: fdecl::ConfigTypeLayout::Bool,
3219 parameters: None,
3220 constraints: Vec::new(),
3221 }),
3222 ..Default::default()
3223 }),
3224 ]);
3225 decl.config = Some(fdecl::ConfigSchema {
3226 fields: Some(vec![fdecl::ConfigField {
3227 key: Some("foo".into()),
3228 type_: Some(fdecl::ConfigType {
3229 layout: fdecl::ConfigTypeLayout::Int16,
3230 parameters: Some(Vec::new()),
3231 constraints: Vec::new(),
3232 }),
3233 mutability: None,
3234 ..Default::default()}]),
3235 checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3236 value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3237 ..Default::default()
3238 });
3239 decl
3240 },
3241 results = vec![
3242 Err(ErrorList::new(vec![
3243 Error::invalid_field(DeclType::ConfigField, "foo"),
3244 ])),
3245 ],
3246 },
3247 }
3248
3249 #[cfg(fuchsia_api_level_at_least = "29")]
3250 test_validate_any_result! {
3251 test_validate_use_dictionary => {
3252 input = {
3253 let mut decl = new_component_decl();
3254 decl.uses = Some(vec![
3255 fdecl::Use::Dictionary(fdecl::UseDictionary {
3256 dependency_type: Some(fdecl::DependencyType::Strong),
3257 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3258 source_name: Some("toolbox".to_string()),
3259 target_path: Some("/svc".to_string()),
3260 availability: Some(fdecl::Availability::Required),
3261 ..Default::default()
3262 }),
3263 ]);
3264 decl
3265 },
3266 results = vec![
3267 Ok(()),
3268 ],
3269 },
3270 test_validate_use_dictionary_invalid_name => {
3271 input = {
3272 let mut decl = new_component_decl();
3273 decl.uses = Some(vec![
3274 fdecl::Use::Dictionary(fdecl::UseDictionary {
3275 dependency_type: Some(fdecl::DependencyType::Strong),
3276 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3277 source_name: Some("toolbox@".to_string()),
3278 target_path: Some("/svc".to_string()),
3279 availability: Some(fdecl::Availability::Required),
3280 ..Default::default()
3281 }),
3282 ]);
3283 decl
3284 },
3285 results = vec![
3286 Err(ErrorList::new(vec![Error::invalid_field(DeclType::UseDictionary, "source_name")])),
3287 ],
3288 },
3289 test_validate_use_dictionary_invalid_path => {
3290 input = {
3291 let mut decl = new_component_decl();
3292 decl.uses = Some(vec![
3293 fdecl::Use::Dictionary(fdecl::UseDictionary {
3294 dependency_type: Some(fdecl::DependencyType::Strong),
3295 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3296 source_name: Some("toolbox".to_string()),
3297 target_path: Some("/svc@".to_string()),
3298 availability: Some(fdecl::Availability::Required),
3299 ..Default::default()
3300 }),
3301 ]);
3302 decl
3303 },
3304 results = vec![
3305 Err(ErrorList::new(vec![Error::field_invalid_segment(DeclType::UseDictionary, "target_path")])),
3306 ],
3307 },
3308 test_validate_use_dictionary_disallows_pkg_overlap => {
3309 input = {
3310 let mut decl = new_component_decl();
3311 decl.uses = Some(vec![
3312 fdecl::Use::Dictionary(fdecl::UseDictionary {
3313 dependency_type: Some(fdecl::DependencyType::Strong),
3314 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3315 source_name: Some("toolbox".to_string()),
3316 target_path: Some("/pkg/toolbox".to_string()),
3317 availability: Some(fdecl::Availability::Required),
3318 ..Default::default()
3319 }),
3320 ]);
3321 decl
3322 },
3323 results = vec![
3324 Err(ErrorList::new(vec![
3325 Error::pkg_path_overlap(DeclType::UseDictionary, "/pkg/toolbox"),
3326 ])),
3327 ],
3328 },
3329 test_validate_use_dictionary_path_overlap_directory => {
3330 input = {
3331 let mut decl = new_component_decl();
3332 decl.uses = Some(vec![
3333 fdecl::Use::Dictionary(fdecl::UseDictionary {
3334 dependency_type: Some(fdecl::DependencyType::Strong),
3335 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3336 source_name: Some("toolbox".to_string()),
3337 target_path: Some("/foo/bar".to_string()),
3338 availability: Some(fdecl::Availability::Required),
3339 ..Default::default()
3340 }),
3341 fdecl::Use::Directory(fdecl::UseDirectory {
3342 dependency_type: Some(fdecl::DependencyType::Strong),
3343 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3344 source_name: Some("data".to_string()),
3345 target_path: Some("/foo".to_string()),
3346 rights: Some(fio::Operations::CONNECT),
3347 subdir: None,
3348 ..Default::default()
3349 }),
3350 ]);
3351 decl
3352 },
3353 results = vec![
3354 Err(ErrorList::new(vec![
3355 Error::invalid_path_overlap(
3356 DeclType::UseDirectory,
3357 "/foo",
3358 DeclType::UseDictionary,
3359 "/foo/bar",
3360 ),
3361 ])),
3362 Err(ErrorList::new(vec![
3363 Error::invalid_path_overlap(
3364 DeclType::UseDictionary,
3365 "/foo/bar",
3366 DeclType::UseDirectory,
3367 "/foo",
3368 ),
3369 ])),
3370 ],
3371 },
3372 test_validate_use_dictionary_path_overlap_protocol => {
3373 input = {
3374 let mut decl = new_component_decl();
3375 decl.uses = Some(vec![
3376 fdecl::Use::Dictionary(fdecl::UseDictionary {
3377 dependency_type: Some(fdecl::DependencyType::Strong),
3378 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3379 source_name: Some("toolbox".to_string()),
3380 target_path: Some("/svc/toolbox".to_string()),
3381 availability: Some(fdecl::Availability::Required),
3382 ..Default::default()
3383 }),
3384 fdecl::Use::Protocol(fdecl::UseProtocol {
3385 dependency_type: Some(fdecl::DependencyType::Strong),
3386 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3387 source_name: Some("fuchsia.examples.Echo".to_string()),
3388 target_path: Some("/svc/fuchsia.examples.Echo".to_string()),
3389 ..Default::default()
3390 }),
3391 ]);
3392 decl
3393 },
3394 results = vec![
3395 Ok(()),
3396 ],
3397 },
3398 test_validate_use_dictionary_path_overlap_protocol_2 => {
3399 input = {
3400 let mut decl = new_component_decl();
3401 decl.uses = Some(vec![
3402 fdecl::Use::Dictionary(fdecl::UseDictionary {
3403 dependency_type: Some(fdecl::DependencyType::Strong),
3404 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3405 source_name: Some("toolbox".to_string()),
3406 target_path: Some("/svc".to_string()),
3407 availability: Some(fdecl::Availability::Required),
3408 ..Default::default()
3409 }),
3410 fdecl::Use::Protocol(fdecl::UseProtocol {
3411 dependency_type: Some(fdecl::DependencyType::Strong),
3412 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3413 source_name: Some("fuchsia.examples.Echo".to_string()),
3414 target_path: Some("/svc/fuchsia.examples.Echo".to_string()),
3415 ..Default::default()
3416 }),
3417 ]);
3418 decl
3419 },
3420 results = vec![
3421 Ok(()),
3422 ],
3423 },
3424 test_validate_use_dictionary_path_identical_dictionary => {
3425 input = {
3426 let mut decl = new_component_decl();
3427 decl.uses = Some(vec![
3428 fdecl::Use::Dictionary(fdecl::UseDictionary {
3429 dependency_type: Some(fdecl::DependencyType::Strong),
3430 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3431 source_name: Some("toolbox-1".to_string()),
3432 target_path: Some("/svc".to_string()),
3433 availability: Some(fdecl::Availability::Required),
3434 ..Default::default()
3435 }),
3436 fdecl::Use::Dictionary(fdecl::UseDictionary {
3437 dependency_type: Some(fdecl::DependencyType::Strong),
3438 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3439 source_name: Some("toolbox-2".to_string()),
3440 target_path: Some("/svc".to_string()),
3441 availability: Some(fdecl::Availability::Required),
3442 ..Default::default()
3443 }),
3444 fdecl::Use::Protocol(fdecl::UseProtocol {
3445 dependency_type: Some(fdecl::DependencyType::Strong),
3446 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3447 source_name: Some("fuchsia.examples.Echo".to_string()),
3448 target_path: Some("/svc/fuchsia.examples.Echo".to_string()),
3449 ..Default::default()
3450 }),
3451 ]);
3452 decl
3453 },
3454 results = vec![
3455 Ok(()),
3456 ],
3457 },
3458 test_validate_use_two_dictionaires_and_a_protocol_at_the_same_path => {
3459 input = {
3460 fdecl::Component {
3461 program: Some(fdecl::Program {
3462 runner: Some("realm_builder".to_string()),
3463 info: Some(fdata::Dictionary {
3464 entries: Some(vec![ ]),
3465 ..Default::default()
3466 }),
3467 ..Default::default()
3468 }),
3469 uses: Some(vec![
3470 fdecl::Use::Dictionary(fdecl::UseDictionary {
3471 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3472 source_name: Some("my_dictionary".to_string()),
3473 target_path: Some("/svc".to_string()),
3474 dependency_type: Some(fdecl::DependencyType::Strong),
3475 availability: Some(fdecl::Availability::Required),
3476 source_dictionary: None,
3477 ..Default::default()
3478 }),
3479 fdecl::Use::Dictionary(fdecl::UseDictionary {
3480 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
3481 source_name: Some("my_dictionary2".to_string()),
3482 target_path: Some("/svc".to_string()),
3483 dependency_type: Some(fdecl::DependencyType::Strong),
3484 availability: Some(fdecl::Availability::Required),
3485 source_dictionary: None,
3486 ..Default::default()
3487 }),
3488 fdecl::Use::Protocol(fdecl::UseProtocol {
3489 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
3490 source_name: Some("fuchsia.echo2".to_string()),
3491 target_path: Some("/svc/fuchsia.echo2".to_string()),
3492 dependency_type: Some(fdecl::DependencyType::Strong),
3493 availability: Some(fdecl::Availability::Required),
3494 source_dictionary: None,
3495 numbered_handle: None,
3496 ..Default::default()
3497 }),
3498 ]),
3499 ..Default::default()
3500 }
3501 },
3502 results = vec![
3503 Ok(()),
3504 ],
3505 },
3506 }
3507
3508 test_validate_values_data! {
3509 test_values_data_ok => {
3510 input = fdecl::ConfigValuesData {
3511 values: Some(vec![
3512 fdecl::ConfigValueSpec {
3513 value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(true))),
3514 ..Default::default()
3515 }
3516 ]),
3517 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3518 ..Default::default()
3519 },
3520 result = Ok(()),
3521 },
3522 test_values_data_no_checksum => {
3523 input = fdecl::ConfigValuesData {
3524 values: Some(vec![]),
3525 checksum: None,
3526 ..Default::default()
3527 },
3528 result = Err(ErrorList::new(vec![
3529 Error::missing_field(DeclType::ConfigValuesData, "checksum")
3530 ])),
3531 },
3532 test_values_data_unknown_checksum => {
3533 input = fdecl::ConfigValuesData {
3534 values: Some(vec![]),
3535 checksum: Some(fdecl::ConfigChecksum::unknown_variant_for_testing()),
3536 ..Default::default()
3537 },
3538 result = Err(ErrorList::new(vec![
3539 Error::invalid_field(DeclType::ConfigValuesData, "checksum")
3540 ])),
3541 },
3542 test_values_data_no_values => {
3543 input = fdecl::ConfigValuesData {
3544 values: None,
3545 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3546 ..Default::default()
3547 },
3548 result = Err(ErrorList::new(vec![
3549 Error::missing_field(DeclType::ConfigValuesData, "values")
3550 ])),
3551 },
3552 test_values_data_no_inner_value => {
3553 input = fdecl::ConfigValuesData {
3554 values: Some(vec![
3555 fdecl::ConfigValueSpec::default()
3556 ]),
3557 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3558 ..Default::default()
3559 },
3560 result = Err(ErrorList::new(vec![
3561 Error::missing_field(DeclType::ConfigValueSpec, "value")
3562 ])),
3563 },
3564 test_values_data_unknown_inner_value => {
3565 input = fdecl::ConfigValuesData {
3566 values: Some(vec![
3567 fdecl::ConfigValueSpec {
3568 value: Some(fdecl::ConfigValue::unknown_variant_for_testing()),
3569 ..Default::default()
3570 }
3571 ]),
3572 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3573 ..Default::default()
3574 },
3575 result = Err(ErrorList::new(vec![
3576 Error::invalid_field(DeclType::ConfigValueSpec, "value")
3577 ])),
3578 },
3579 test_values_data_unknown_single_value => {
3580 input = fdecl::ConfigValuesData {
3581 values: Some(vec![
3582 fdecl::ConfigValueSpec {
3583 value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::unknown_variant_for_testing())),
3584 ..Default::default()
3585 }
3586 ]),
3587 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3588 ..Default::default()
3589 },
3590 result = Err(ErrorList::new(vec![
3591 Error::invalid_field(DeclType::ConfigValueSpec, "value")
3592 ])),
3593 },
3594 test_values_data_unknown_list_value => {
3595 input = fdecl::ConfigValuesData {
3596 values: Some(vec![
3597 fdecl::ConfigValueSpec {
3598 value: Some(fdecl::ConfigValue::Vector(fdecl::ConfigVectorValue::unknown_variant_for_testing())),
3599 ..Default::default()
3600 }
3601 ]),
3602 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3603 ..Default::default()
3604 },
3605 result = Err(ErrorList::new(vec![
3606 Error::invalid_field(DeclType::ConfigValueSpec, "value")
3607 ])),
3608 },
3609 }
3610
3611 test_validate! {
3612 test_validate_uses_empty => {
3614 input = {
3615 let mut decl = new_component_decl();
3616 decl.program = Some(fdecl::Program {
3617 runner: Some("elf".to_string()),
3618 info: Some(fdata::Dictionary {
3619 entries: None,
3620 ..Default::default()
3621 }),
3622 ..Default::default()
3623 });
3624 decl.uses = Some(vec![
3625 fdecl::Use::Service(fdecl::UseService {
3626 source: None,
3627 source_name: None,
3628 target_path: None,
3629 dependency_type: None,
3630 ..Default::default()
3631 }),
3632 fdecl::Use::Protocol(fdecl::UseProtocol {
3633 dependency_type: None,
3634 source: None,
3635 source_name: None,
3636 target_path: None,
3637 ..Default::default()
3638 }),
3639 fdecl::Use::Directory(fdecl::UseDirectory {
3640 dependency_type: None,
3641 source: None,
3642 source_name: None,
3643 target_path: None,
3644 rights: None,
3645 subdir: None,
3646 ..Default::default()
3647 }),
3648 fdecl::Use::Storage(fdecl::UseStorage {
3649 source_name: None,
3650 target_path: None,
3651 ..Default::default()
3652 }),
3653 fdecl::Use::EventStream(fdecl::UseEventStream {
3654 source_name: None,
3655 source: None,
3656 target_path: None,
3657 ..Default::default()
3658 }),
3659 fdecl::Use::Runner(fdecl::UseRunner {
3660 source_name: None,
3661 source: None,
3662 ..Default::default()
3663 }),
3664 ]);
3665 decl
3666 },
3667 result = Err(ErrorList::new(vec![
3668 Error::missing_field(DeclType::UseService, "source"),
3669 Error::missing_field(DeclType::UseService, "source_name"),
3670 Error::missing_field(DeclType::UseService, "target_path"),
3671 Error::missing_field(DeclType::UseService, "dependency_type"),
3672 Error::missing_field(DeclType::UseProtocol, "source"),
3673 Error::missing_field(DeclType::UseProtocol, "source_name"),
3674 Error::missing_field(DeclType::UseProtocol, "target_path"),
3675 Error::missing_field(DeclType::UseProtocol, "dependency_type"),
3676 Error::missing_field(DeclType::UseDirectory, "source"),
3677 Error::missing_field(DeclType::UseDirectory, "source_name"),
3678 Error::missing_field(DeclType::UseDirectory, "target_path"),
3679 Error::missing_field(DeclType::UseDirectory, "dependency_type"),
3680 Error::missing_field(DeclType::UseDirectory, "rights"),
3681 Error::missing_field(DeclType::UseStorage, "source_name"),
3682 Error::missing_field(DeclType::UseStorage, "target_path"),
3683 Error::missing_field(DeclType::UseEventStream, "source"),
3684 Error::missing_field(DeclType::UseEventStream, "source_name"),
3685 Error::missing_field(DeclType::UseEventStream, "target_path"),
3686 Error::missing_field(DeclType::UseRunner, "source"),
3687 Error::missing_field(DeclType::UseRunner, "source_name"),
3688 ])),
3689 },
3690 test_validate_missing_program_info => {
3691 input = {
3692 let mut decl = new_component_decl();
3693 decl.program = Some(fdecl::Program {
3694 runner: Some("runner".to_string()),
3695 info: None,
3696 ..Default::default()
3697 });
3698 decl
3699 },
3700 result = Err(ErrorList::new(vec![
3701 Error::missing_field(DeclType::Program, "info")
3702 ])),
3703 },
3704 test_validate_uses_invalid_identifiers => {
3705 input = {
3706 let mut decl = new_component_decl();
3707 decl.uses = Some(vec![
3708 fdecl::Use::Service(fdecl::UseService {
3709 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
3710 name: "^bad".to_string(),
3711 })),
3712 source_name: Some("foo/".to_string()),
3713 target_path: Some("a/foo".to_string()),
3714 dependency_type: Some(fdecl::DependencyType::Strong),
3715 ..Default::default()
3716 }),
3717 fdecl::Use::Protocol(fdecl::UseProtocol {
3718 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3719 name: "^bad".to_string(),
3720 collection: None,
3721 })),
3722 source_name: Some("foo/".to_string()),
3723 target_path: Some("b/foo".to_string()),
3724 dependency_type: Some(fdecl::DependencyType::Strong),
3725 ..Default::default()
3726 }),
3727 fdecl::Use::Directory(fdecl::UseDirectory {
3728 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3729 name: "^bad".to_string(),
3730 collection: None,
3731 })),
3732 source_name: Some("foo/".to_string()),
3733 target_path: Some("c".to_string()),
3734 rights: Some(fio::Operations::CONNECT),
3735 subdir: Some("/foo".to_string()),
3736 dependency_type: Some(fdecl::DependencyType::Strong),
3737 ..Default::default()
3738 }),
3739 fdecl::Use::Storage(fdecl::UseStorage {
3740 source_name: Some("foo/".to_string()),
3741 target_path: Some("d".to_string()),
3742 ..Default::default()
3743 }),
3744 fdecl::Use::EventStream(fdecl::UseEventStream {
3745 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3746 name: "^bad".to_string(),
3747 collection: None,
3748 })),
3749 source_name: Some("foo/".to_string()),
3750 target_path: Some("e".to_string()),
3751 ..Default::default()
3752 }),
3753 fdecl::Use::Runner(fdecl::UseRunner {
3754 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3755 name: "^bad".to_string(),
3756 collection: None,
3757 })),
3758 source_name: Some("foo/".to_string()),
3759 ..Default::default()
3760 }),
3761 ]);
3762 decl
3763 },
3764 result = Err(ErrorList::new(vec![
3765 Error::invalid_field(DeclType::UseService, "source.capability.name"),
3766 Error::invalid_field(DeclType::UseService, "source_name"),
3767 Error::invalid_field(DeclType::UseService, "target_path"),
3768 Error::invalid_field(DeclType::UseProtocol, "source.child.name"),
3769 Error::invalid_field(DeclType::UseProtocol, "source_name"),
3770 Error::invalid_field(DeclType::UseProtocol, "target_path"),
3771 Error::invalid_field(DeclType::UseDirectory, "source.child.name"),
3772 Error::invalid_field(DeclType::UseDirectory, "source_name"),
3773 Error::invalid_field(DeclType::UseDirectory, "target_path"),
3774 Error::invalid_field(DeclType::UseDirectory, "subdir"),
3775 Error::invalid_field(DeclType::UseStorage, "source_name"),
3776 Error::invalid_field(DeclType::UseStorage, "target_path"),
3777 Error::invalid_field(DeclType::UseEventStream, "source.child.name"),
3778 Error::invalid_field(DeclType::UseEventStream, "source_name"),
3779 Error::invalid_field(DeclType::UseEventStream, "target_path"),
3780 Error::invalid_field(DeclType::UseRunner, "source.child.name"),
3781 Error::invalid_field(DeclType::UseRunner, "source_name"),
3782 ])),
3783 },
3784 test_validate_uses_missing_source => {
3785 input = {
3786 fdecl::Component {
3787 uses: Some(vec![
3788 fdecl::Use::Protocol(fdecl::UseProtocol {
3789 dependency_type: Some(fdecl::DependencyType::Strong),
3790 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
3791 name: "this-storage-doesnt-exist".to_string(),
3792 })),
3793 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3794 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3795 ..Default::default()
3796 })
3797 ]),
3798 ..new_component_decl()
3799 }
3800 },
3801 result = Err(ErrorList::new(vec![
3802 Error::invalid_capability(DeclType::UseProtocol, "source", "this-storage-doesnt-exist"),
3803 ])),
3804 },
3805 test_validate_uses_invalid_child => {
3806 input = {
3807 fdecl::Component {
3808 uses: Some(vec![
3809 fdecl::Use::Protocol(fdecl::UseProtocol {
3810 dependency_type: Some(fdecl::DependencyType::Strong),
3811 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3812 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3813 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3814 ..Default::default()
3815 }),
3816 fdecl::Use::Service(fdecl::UseService {
3817 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3818 source_name: Some("service_name".to_string()),
3819 target_path: Some("/svc/service_name".to_string()),
3820 dependency_type: Some(fdecl::DependencyType::Strong),
3821 ..Default::default()
3822 }),
3823 fdecl::Use::Directory(fdecl::UseDirectory {
3824 dependency_type: Some(fdecl::DependencyType::Strong),
3825 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3826 source_name: Some("DirectoryName".to_string()),
3827 target_path: Some("/data/DirectoryName".to_string()),
3828 rights: Some(fio::Operations::CONNECT),
3829 subdir: None,
3830 ..Default::default()
3831 }),
3832 fdecl::Use::Runner(fdecl::UseRunner {
3833 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3834 source_name: Some("RunnerName".to_string()),
3835 ..Default::default()
3836 }),
3837 ]),
3838 ..new_component_decl()
3839 }
3840 },
3841 result = Err(ErrorList::new(vec![
3842 Error::invalid_child(DeclType::UseProtocol, "source", "no-such-child"),
3843 Error::invalid_child(DeclType::UseService, "source", "no-such-child"),
3844 Error::invalid_child(DeclType::UseDirectory, "source", "no-such-child"),
3845 Error::invalid_child(DeclType::UseRunner, "source", "no-such-child"),
3846 ])),
3847 },
3848 test_validate_uses_invalid_capability_from_self => {
3849 input = {
3850 let mut decl = new_component_decl();
3851 decl.uses = Some(vec![
3852 fdecl::Use::Service(fdecl::UseService {
3853 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3854 source_name: Some("fuchsia.some.library.SomeService".into()),
3855 target_path: Some("/svc/foo".into()),
3856 dependency_type: Some(fdecl::DependencyType::Strong),
3857 ..Default::default()
3858 }),
3859 fdecl::Use::Protocol(fdecl::UseProtocol {
3860 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3861 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
3862 target_path: Some("/svc/bar".into()),
3863 dependency_type: Some(fdecl::DependencyType::Strong),
3864 ..Default::default()
3865 }),
3866 fdecl::Use::Directory(fdecl::UseDirectory {
3867 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3868 source_name: Some("dir".into()),
3869 target_path: Some("/assets".into()),
3870 dependency_type: Some(fdecl::DependencyType::Strong),
3871 rights: Some(fio::Operations::CONNECT),
3872 ..Default::default()
3873 }),
3874 fdecl::Use::Runner(fdecl::UseRunner {
3875 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3876 source_name: Some("source_elf".into()),
3877 ..Default::default()
3878 }),
3879 fdecl::Use::Config(fdecl::UseConfiguration {
3880 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3881 source_name: Some("source_config".into()),
3882 target_name: Some("config".into()),
3883 type_: Some(fdecl::ConfigType {
3884 layout: fdecl::ConfigTypeLayout::Bool,
3885 parameters: Some(Vec::new()),
3886 constraints: Vec::new(),
3887 }),
3888 ..Default::default()
3889 }),
3890 fdecl::Use::Protocol(fdecl::UseProtocol {
3891 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3892 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
3893 source_dictionary: Some("dict/inner".into()),
3894 target_path: Some("/svc/baz".into()),
3895 dependency_type: Some(fdecl::DependencyType::Strong),
3896 ..Default::default()
3897 }),
3898 ]);
3899 decl
3900 },
3901 result = Err(ErrorList::new(vec![
3902 Error::invalid_capability(
3903 DeclType::UseService,
3904 "source",
3905 "fuchsia.some.library.SomeService"),
3906 Error::invalid_capability(
3907 DeclType::UseProtocol,
3908 "source",
3909 "fuchsia.some.library.SomeProtocol"),
3910 Error::invalid_capability(DeclType::UseDirectory, "source", "dir"),
3911 Error::invalid_capability(DeclType::UseRunner, "source", "source_elf"),
3912 Error::invalid_capability(DeclType::UseConfiguration, "source", "source_config"),
3913 Error::invalid_capability(DeclType::UseProtocol, "source", "dict"),
3914 ])),
3915 },
3916 test_validate_use_numbered_handle => {
3917 input = {
3918 fdecl::Component {
3919 uses: Some(vec![
3920 fdecl::Use::Protocol(fdecl::UseProtocol {
3922 dependency_type: Some(fdecl::DependencyType::Strong),
3923 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
3924 source_name: Some("fuchsia.logger.LogSink".into()),
3925 numbered_handle: Some(0xab),
3926 ..Default::default()
3927 }),
3928 fdecl::Use::Protocol(fdecl::UseProtocol {
3930 dependency_type: Some(fdecl::DependencyType::Strong),
3931 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
3932 source_name: Some("fuchsia.logger.LogSink".into()),
3933 target_path: Some("/svc/fuchsia.logger.LogSink".into()),
3934 numbered_handle: Some(0xab),
3935 ..Default::default()
3936 }),
3937 ]),
3938 ..new_component_decl()
3939 }
3940 },
3941 result = Err(ErrorList::new(vec![
3942 Error::extraneous_field(DeclType::UseProtocol, "numbered_handle"),
3943 ])),
3944 },
3945 test_validate_use_from_child_offer_to_child_strong_cycle => {
3946 input = {
3947 fdecl::Component {
3948 capabilities: Some(vec![
3949 fdecl::Capability::Service(fdecl::Service {
3950 name: Some("a".to_string()),
3951 source_path: Some("/a".to_string()),
3952 ..Default::default()
3953 })]),
3954 uses: Some(vec![
3955 fdecl::Use::Protocol(fdecl::UseProtocol {
3956 dependency_type: Some(fdecl::DependencyType::Strong),
3957 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3958 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3959 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3960 ..Default::default()
3961 }),
3962 fdecl::Use::Service(fdecl::UseService {
3963 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3964 source_name: Some("service_name".to_string()),
3965 target_path: Some("/svc/service_name".to_string()),
3966 dependency_type: Some(fdecl::DependencyType::Strong),
3967 ..Default::default()
3968 }),
3969 fdecl::Use::Directory(fdecl::UseDirectory {
3970 dependency_type: Some(fdecl::DependencyType::Strong),
3971 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3972 source_name: Some("DirectoryName".to_string()),
3973 target_path: Some("/data/DirectoryName".to_string()),
3974 rights: Some(fio::Operations::CONNECT),
3975 subdir: None,
3976 ..Default::default()
3977 }),
3978 ]),
3979 offers: Some(vec![
3980 fdecl::Offer::Service(fdecl::OfferService {
3981 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3982 source_name: Some("a".to_string()),
3983 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
3984 target_name: Some("a".to_string()),
3985 ..Default::default()
3986 })
3987 ]),
3988 children: Some(vec![
3989 fdecl::Child {
3990 name: Some("child".to_string()),
3991 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3992 startup: Some(fdecl::StartupMode::Lazy),
3993 on_terminate: None,
3994 ..Default::default()
3995 }
3996 ]),
3997 ..new_component_decl()
3998 }
3999 },
4000 result = Err(ErrorList::new(vec![
4001 Error::dependency_cycle("{{self -> child child -> self}}"),
4002 ])),
4003 },
4004 test_validate_use_from_child_storage_no_cycle => {
4005 input = {
4006 fdecl::Component {
4007 capabilities: Some(vec![
4008 fdecl::Capability::Storage(fdecl::Storage {
4009 name: Some("cdata".to_string()),
4010 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None } )),
4011 backing_dir: Some("minfs".to_string()),
4012 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4013 ..Default::default()
4014 }),
4015 fdecl::Capability::Storage(fdecl::Storage {
4016 name: Some("pdata".to_string()),
4017 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4018 backing_dir: Some("minfs".to_string()),
4019 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4020 ..Default::default()
4021 }),
4022 ]),
4023 uses: Some(vec![
4024 fdecl::Use::Protocol(fdecl::UseProtocol {
4025 dependency_type: Some(fdecl::DependencyType::Strong),
4026 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child1".to_string(), collection: None})),
4027 source_name: Some("a".to_string()),
4028 target_path: Some("/svc/a".to_string()),
4029 ..Default::default()
4030 }),
4031 ]),
4032 offers: Some(vec![
4033 fdecl::Offer::Storage(fdecl::OfferStorage {
4034 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4035 source_name: Some("cdata".to_string()),
4036 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4037 target_name: Some("cdata".to_string()),
4038 ..Default::default()
4039 }),
4040 fdecl::Offer::Storage(fdecl::OfferStorage {
4041 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4042 source_name: Some("pdata".to_string()),
4043 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4044 target_name: Some("pdata".to_string()),
4045 ..Default::default()
4046 }),
4047 ]),
4048 children: Some(vec![
4049 fdecl::Child {
4050 name: Some("child1".to_string()),
4051 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4052 startup: Some(fdecl::StartupMode::Lazy),
4053 on_terminate: None,
4054 ..Default::default()
4055 },
4056 fdecl::Child {
4057 name: Some("child2".to_string()),
4058 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4059 startup: Some(fdecl::StartupMode::Lazy),
4060 on_terminate: None,
4061 ..Default::default()
4062 }
4063 ]),
4064 ..new_component_decl()
4065 }
4066 },
4067 result = Ok(()),
4068 },
4069 test_validate_use_from_child_storage_cycle => {
4070 input = {
4071 fdecl::Component {
4072 capabilities: Some(vec![
4073 fdecl::Capability::Storage(fdecl::Storage {
4074 name: Some("data".to_string()),
4075 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4076 backing_dir: Some("minfs".to_string()),
4077 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4078 ..Default::default()
4079 }),
4080 ]),
4081 uses: Some(vec![
4082 fdecl::Use::Protocol(fdecl::UseProtocol {
4083 dependency_type: Some(fdecl::DependencyType::Strong),
4084 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4085 source_name: Some("a".to_string()),
4086 target_path: Some("/svc/a".to_string()),
4087 ..Default::default()
4088 }),
4089 ]),
4090 offers: Some(vec![
4091 fdecl::Offer::Storage(fdecl::OfferStorage {
4092 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4093 source_name: Some("data".to_string()),
4094 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4095 target_name: Some("data".to_string()),
4096 ..Default::default()
4097 }),
4098 ]),
4099 children: Some(vec![
4100 fdecl::Child {
4101 name: Some("child".to_string()),
4102 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4103 startup: Some(fdecl::StartupMode::Lazy),
4104 on_terminate: None,
4105 ..Default::default()
4106 },
4107 ]),
4108 ..new_component_decl()
4109 }
4110 },
4111 result = Err(ErrorList::new(vec![
4112 Error::dependency_cycle("{{self -> capability data -> child child -> self}}"),
4113 ])),
4114 },
4115 test_validate_storage_strong_cycle_between_children => {
4116 input = {
4117 fdecl::Component {
4118 capabilities: Some(vec![
4119 fdecl::Capability::Storage(fdecl::Storage {
4120 name: Some("data".to_string()),
4121 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None } )),
4122 backing_dir: Some("minfs".to_string()),
4123 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4124 ..Default::default()
4125 })
4126 ]),
4127 offers: Some(vec![
4128 fdecl::Offer::Storage(fdecl::OfferStorage {
4129 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4130 source_name: Some("data".to_string()),
4131 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4132 target_name: Some("data".to_string()),
4133 ..Default::default()
4134 }),
4135 fdecl::Offer::Service(fdecl::OfferService {
4136 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4137 source_name: Some("a".to_string()),
4138 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4139 target_name: Some("a".to_string()),
4140 ..Default::default()
4141 }),
4142 ]),
4143 children: Some(vec![
4144 fdecl::Child {
4145 name: Some("child1".to_string()),
4146 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4147 startup: Some(fdecl::StartupMode::Lazy),
4148 on_terminate: None,
4149 ..Default::default()
4150 },
4151 fdecl::Child {
4152 name: Some("child2".to_string()),
4153 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4154 startup: Some(fdecl::StartupMode::Lazy),
4155 on_terminate: None,
4156 ..Default::default()
4157 }
4158 ]),
4159 ..new_component_decl()
4160 }
4161 },
4162 result = Err(ErrorList::new(vec![
4163 Error::dependency_cycle("{{child child1 -> capability data -> child child2 -> child child1}}"),
4164 ])),
4165 },
4166 test_validate_strong_cycle_between_children_through_environment_debug => {
4167 input = {
4168 fdecl::Component {
4169 environments: Some(vec![
4170 fdecl::Environment {
4171 name: Some("env".to_string()),
4172 extends: Some(fdecl::EnvironmentExtends::Realm),
4173 debug_capabilities: Some(vec![
4174 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
4175 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4176 source_name: Some("fuchsia.foo.Bar".to_string()),
4177 target_name: Some("fuchsia.foo.Bar".to_string()),
4178 ..Default::default()
4179 }),
4180 ]),
4181 ..Default::default()
4182 },
4183 ]),
4184 offers: Some(vec![
4185 fdecl::Offer::Service(fdecl::OfferService {
4186 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4187 source_name: Some("a".to_string()),
4188 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4189 target_name: Some("a".to_string()),
4190 ..Default::default()
4191 }),
4192 ]),
4193 children: Some(vec![
4194 fdecl::Child {
4195 name: Some("child1".to_string()),
4196 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4197 startup: Some(fdecl::StartupMode::Lazy),
4198 on_terminate: None,
4199 ..Default::default()
4200 },
4201 fdecl::Child {
4202 name: Some("child2".to_string()),
4203 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4204 startup: Some(fdecl::StartupMode::Lazy),
4205 environment: Some("env".to_string()),
4206 on_terminate: None,
4207 ..Default::default()
4208 }
4209 ]),
4210 ..new_component_decl()
4211 }
4212 },
4213 result = Err(ErrorList::new(vec![
4214 Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}"),
4215 ])),
4216 },
4217 test_validate_strong_cycle_between_children_through_environment_runner => {
4218 input = {
4219 fdecl::Component {
4220 environments: Some(vec![
4221 fdecl::Environment {
4222 name: Some("env".to_string()),
4223 extends: Some(fdecl::EnvironmentExtends::Realm),
4224 runners: Some(vec![
4225 fdecl::RunnerRegistration {
4226 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4227 source_name: Some("coff".to_string()),
4228 target_name: Some("coff".to_string()),
4229 ..Default::default()
4230 }
4231 ]),
4232 ..Default::default()
4233 },
4234 ]),
4235 offers: Some(vec![
4236 fdecl::Offer::Service(fdecl::OfferService {
4237 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4238 source_name: Some("a".to_string()),
4239 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4240 target_name: Some("a".to_string()),
4241 ..Default::default()
4242 }),
4243 ]),
4244 children: Some(vec![
4245 fdecl::Child {
4246 name: Some("child1".to_string()),
4247 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4248 startup: Some(fdecl::StartupMode::Lazy),
4249 on_terminate: None,
4250 ..Default::default()
4251 },
4252 fdecl::Child {
4253 name: Some("child2".to_string()),
4254 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4255 startup: Some(fdecl::StartupMode::Lazy),
4256 environment: Some("env".to_string()),
4257 on_terminate: None,
4258 ..Default::default()
4259 }
4260 ]),
4261 ..new_component_decl()
4262 }
4263 },
4264 result = Err(ErrorList::new(vec![
4265 Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}"),
4266 ])),
4267 },
4268 test_validate_strong_cycle_between_children_through_environment_resolver => {
4269 input = {
4270 fdecl::Component {
4271 environments: Some(vec![
4272 fdecl::Environment {
4273 name: Some("env".to_string()),
4274 extends: Some(fdecl::EnvironmentExtends::Realm),
4275 resolvers: Some(vec![
4276 fdecl::ResolverRegistration {
4277 resolver: Some("gopher".to_string()),
4278 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4279 scheme: Some("gopher".to_string()),
4280 ..Default::default()
4281 }
4282 ]),
4283 ..Default::default()
4284 },
4285 ]),
4286 offers: Some(vec![
4287 fdecl::Offer::Service(fdecl::OfferService {
4288 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4289 source_name: Some("a".to_string()),
4290 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4291 target_name: Some("a".to_string()),
4292 ..Default::default()
4293 }),
4294 ]),
4295 children: Some(vec![
4296 fdecl::Child {
4297 name: Some("child1".to_string()),
4298 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4299 startup: Some(fdecl::StartupMode::Lazy),
4300 on_terminate: None,
4301 ..Default::default()
4302 },
4303 fdecl::Child {
4304 name: Some("child2".to_string()),
4305 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4306 startup: Some(fdecl::StartupMode::Lazy),
4307 environment: Some("env".to_string()),
4308 on_terminate: None,
4309 ..Default::default()
4310 }
4311 ]),
4312 ..new_component_decl()
4313 }
4314 },
4315 result = Err(ErrorList::new(vec![
4316 Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}"),
4317 ])),
4318 },
4319 test_validate_strong_cycle_between_self_and_two_children => {
4320 input = {
4321 fdecl::Component {
4322 capabilities: Some(vec![
4323 fdecl::Capability::Protocol(fdecl::Protocol {
4324 name: Some("fuchsia.foo.Bar".to_string()),
4325 source_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4326 ..Default::default()
4327 })
4328 ]),
4329 offers: Some(vec![
4330 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4331 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4332 source_name: Some("fuchsia.foo.Bar".to_string()),
4333 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4334 target_name: Some("fuchsia.foo.Bar".to_string()),
4335 dependency_type: Some(fdecl::DependencyType::Strong),
4336 ..Default::default()
4337 }),
4338 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4339 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4340 source_name: Some("fuchsia.bar.Baz".to_string()),
4341 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4342 target_name: Some("fuchsia.bar.Baz".to_string()),
4343 dependency_type: Some(fdecl::DependencyType::Strong),
4344 ..Default::default()
4345 }),
4346 ]),
4347 uses: Some(vec![
4348 fdecl::Use::Protocol(fdecl::UseProtocol {
4349 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child2".to_string(), collection: None})),
4350 source_name: Some("fuchsia.baz.Foo".to_string()),
4351 target_path: Some("/svc/fuchsia.baz.Foo".to_string()),
4352 dependency_type: Some(fdecl::DependencyType::Strong),
4353 ..Default::default()
4354 }),
4355 ]),
4356 children: Some(vec![
4357 fdecl::Child {
4358 name: Some("child1".to_string()),
4359 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4360 startup: Some(fdecl::StartupMode::Lazy),
4361 on_terminate: None,
4362 ..Default::default()
4363 },
4364 fdecl::Child {
4365 name: Some("child2".to_string()),
4366 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4367 startup: Some(fdecl::StartupMode::Lazy),
4368 on_terminate: None,
4369 ..Default::default()
4370 }
4371 ]),
4372 ..new_component_decl()
4373 }
4374 },
4375 result = Err(ErrorList::new(vec![
4376 Error::dependency_cycle("{{self -> child child1 -> child child2 -> self}}"),
4377 ])),
4378 },
4379 test_validate_strong_cycle_with_self_storage => {
4380 input = {
4381 fdecl::Component {
4382 capabilities: Some(vec![
4383 fdecl::Capability::Storage(fdecl::Storage {
4384 name: Some("data".to_string()),
4385 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4386 backing_dir: Some("minfs".to_string()),
4387 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4388 ..Default::default()
4389 }),
4390 fdecl::Capability::Directory(fdecl::Directory {
4391 name: Some("minfs".to_string()),
4392 source_path: Some("/minfs".to_string()),
4393 rights: Some(fio::RW_STAR_DIR),
4394 ..Default::default()
4395 }),
4396 ]),
4397 offers: Some(vec![
4398 fdecl::Offer::Storage(fdecl::OfferStorage {
4399 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4400 source_name: Some("data".to_string()),
4401 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4402 target_name: Some("data".to_string()),
4403 ..Default::default()
4404 }),
4405 ]),
4406 uses: Some(vec![
4407 fdecl::Use::Protocol(fdecl::UseProtocol {
4408 dependency_type: Some(fdecl::DependencyType::Strong),
4409 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4410 source_name: Some("fuchsia.foo.Bar".to_string()),
4411 target_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4412 ..Default::default()
4413 }),
4414 ]),
4415 children: Some(vec![
4416 fdecl::Child {
4417 name: Some("child".to_string()),
4418 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4419 startup: Some(fdecl::StartupMode::Lazy),
4420 ..Default::default()
4421 },
4422 ]),
4423 ..new_component_decl()
4424 }
4425 },
4426 result = Err(ErrorList::new(vec![
4427 Error::dependency_cycle("{{self -> capability data -> child child -> self}}"),
4428 ])),
4429 },
4430 test_validate_strong_cycle_with_self_storage_admin_protocol => {
4431 input = {
4432 fdecl::Component {
4433 capabilities: Some(vec![
4434 fdecl::Capability::Storage(fdecl::Storage {
4435 name: Some("data".to_string()),
4436 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4437 backing_dir: Some("minfs".to_string()),
4438 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4439 ..Default::default()
4440 }),
4441 fdecl::Capability::Directory(fdecl::Directory {
4442 name: Some("minfs".to_string()),
4443 source_path: Some("/minfs".to_string()),
4444 rights: Some(fio::RW_STAR_DIR),
4445 ..Default::default()
4446 }),
4447 ]),
4448 offers: Some(vec![
4449 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4450 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef { name: "data".to_string() })),
4451 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4452 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4453 target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4454 dependency_type: Some(fdecl::DependencyType::Strong),
4455 ..Default::default()
4456 }),
4457 ]),
4458 uses: Some(vec![
4459 fdecl::Use::Protocol(fdecl::UseProtocol {
4460 dependency_type: Some(fdecl::DependencyType::Strong),
4461 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4462 source_name: Some("fuchsia.foo.Bar".to_string()),
4463 target_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4464 ..Default::default()
4465 }),
4466 ]),
4467 children: Some(vec![
4468 fdecl::Child {
4469 name: Some("child".to_string()),
4470 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4471 startup: Some(fdecl::StartupMode::Lazy),
4472 ..Default::default()
4473 },
4474 ]),
4475 ..new_component_decl()
4476 }
4477 },
4478 result = Err(ErrorList::new(vec![
4479 Error::dependency_cycle("{{self -> capability data -> child child -> self}}"),
4480 ])),
4481 },
4482 test_validate_strong_cycle_with_dictionary => {
4483 input = fdecl::Component {
4484 offers: Some(vec![
4485 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
4486 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4487 source_name: Some("dict".into()),
4488 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4489 name: "a".into(),
4490 collection: None,
4491 })),
4492 target_name: Some("dict".into()),
4493 dependency_type: Some(fdecl::DependencyType::Strong),
4494 ..Default::default()
4495 }),
4496 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4497 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4498 name: "b".into(),
4499 collection: None,
4500 })),
4501 source_name: Some("1".into()),
4502 target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4503 name: "dict".into(),
4504 })),
4505 target_name: Some("1".into()),
4506 dependency_type: Some(fdecl::DependencyType::Strong),
4507 ..Default::default()
4508 }),
4509 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4510 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4511 name: "a".into(),
4512 collection: None,
4513 })),
4514 source_name: Some("2".into()),
4515 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4516 name: "b".into(),
4517 collection: None,
4518 })),
4519 target_name: Some("2".into()),
4520 dependency_type: Some(fdecl::DependencyType::Strong),
4521 ..Default::default()
4522 }),
4523 ]),
4524 children: Some(vec![
4525 fdecl::Child {
4526 name: Some("a".into()),
4527 url: Some("fuchsia-pkg://child".into()),
4528 startup: Some(fdecl::StartupMode::Lazy),
4529 ..Default::default()
4530 },
4531 fdecl::Child {
4532 name: Some("b".into()),
4533 url: Some("fuchsia-pkg://child".into()),
4534 startup: Some(fdecl::StartupMode::Lazy),
4535 ..Default::default()
4536 },
4537 ]),
4538 capabilities: Some(vec![
4539 fdecl::Capability::Dictionary(fdecl::Dictionary {
4540 name: Some("dict".into()),
4541 ..Default::default()
4542 }),
4543 ]),
4544 ..Default::default()
4545 },
4546 result = Err(ErrorList::new(vec![
4547 Error::dependency_cycle("{{child a -> child b -> capability dict -> child a}}"),
4548 ])),
4549 },
4550 test_validate_strong_cycle_with_dictionary_indirect => {
4551 input = fdecl::Component {
4552 offers: Some(vec![
4553 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4554 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4555 source_name: Some("3".into()),
4556 source_dictionary: Some("dict".into()),
4557 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4558 name: "a".into(),
4559 collection: None,
4560 })),
4561 target_name: Some("3".into()),
4562 dependency_type: Some(fdecl::DependencyType::Strong),
4563 ..Default::default()
4564 }),
4565 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4566 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4567 name: "b".into(),
4568 collection: None,
4569 })),
4570 source_name: Some("1".into()),
4571 target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4572 name: "dict".into(),
4573 })),
4574 target_name: Some("1".into()),
4575 dependency_type: Some(fdecl::DependencyType::Strong),
4576 ..Default::default()
4577 }),
4578 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4579 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4580 name: "a".into(),
4581 collection: None,
4582 })),
4583 source_name: Some("2".into()),
4584 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4585 name: "b".into(),
4586 collection: None,
4587 })),
4588 target_name: Some("2".into()),
4589 dependency_type: Some(fdecl::DependencyType::Strong),
4590 ..Default::default()
4591 }),
4592 ]),
4593 children: Some(vec![
4594 fdecl::Child {
4595 name: Some("a".into()),
4596 url: Some("fuchsia-pkg://child".into()),
4597 startup: Some(fdecl::StartupMode::Lazy),
4598 ..Default::default()
4599 },
4600 fdecl::Child {
4601 name: Some("b".into()),
4602 url: Some("fuchsia-pkg://child".into()),
4603 startup: Some(fdecl::StartupMode::Lazy),
4604 ..Default::default()
4605 },
4606 ]),
4607 capabilities: Some(vec![
4608 fdecl::Capability::Dictionary(fdecl::Dictionary {
4609 name: Some("dict".into()),
4610 ..Default::default()
4611 }),
4612 ]),
4613 ..Default::default()
4614 },
4615 result = Err(ErrorList::new(vec![
4616 Error::dependency_cycle("{{child a -> child b -> capability dict -> child a}}"),
4617 ])),
4618 },
4619 test_validate_use_dependency_cycle_with_dictionary => {
4620 input = fdecl::Component {
4621 offers: Some(vec![
4622 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
4623 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4624 source_name: Some("dict".into()),
4625 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4626 name: "a".into(),
4627 collection: None,
4628 })),
4629 target_name: Some("dict".into()),
4630 dependency_type: Some(fdecl::DependencyType::Strong),
4631 ..Default::default()
4632 }),
4633 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4634 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4635 source_name: Some("1".into()),
4636 target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4637 name: "dict".into(),
4638 })),
4639 target_name: Some("1".into()),
4640 dependency_type: Some(fdecl::DependencyType::Strong),
4641 ..Default::default()
4642 }),
4643 ]),
4644 children: Some(vec![
4645 fdecl::Child {
4646 name: Some("a".into()),
4647 url: Some("fuchsia-pkg://child".into()),
4648 startup: Some(fdecl::StartupMode::Lazy),
4649 ..Default::default()
4650 },
4651 ]),
4652 uses: Some(vec![
4653 fdecl::Use::Protocol(fdecl::UseProtocol {
4654 dependency_type: Some(fdecl::DependencyType::Strong),
4655 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "a".to_string(), collection: None})),
4656 source_name: Some("2".to_string()),
4657 target_path: Some("/svc/foo".into()),
4658 ..Default::default()
4659 }),
4660 ]),
4661 capabilities: Some(vec![
4662 fdecl::Capability::Dictionary(fdecl::Dictionary {
4663 name: Some("dict".into()),
4664 ..Default::default()
4665 }),
4666 fdecl::Capability::Protocol(fdecl::Protocol {
4667 name: Some("1".to_string()),
4668 source_path: Some("/path".to_string()),
4669 ..Default::default()
4670 }),
4671 ]),
4672 ..Default::default()
4673 },
4674 result = Err(ErrorList::new(vec![
4675 Error::dependency_cycle("{{self -> capability dict -> child a -> self}}"),
4676 ])),
4677 },
4678 test_validate_use_from_child_offer_to_child_weak_cycle => {
4679 input = {
4680 fdecl::Component {
4681 capabilities: Some(vec![
4682 fdecl::Capability::Service(fdecl::Service {
4683 name: Some("a".to_string()),
4684 source_path: Some("/a".to_string()),
4685 ..Default::default()
4686 })]),
4687 uses: Some(vec![
4688 fdecl::Use::Protocol(fdecl::UseProtocol {
4689 dependency_type: Some(fdecl::DependencyType::Weak),
4690 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4691 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4692 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
4693 ..Default::default()
4694 }),
4695 fdecl::Use::Service(fdecl::UseService {
4696 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4697 source_name: Some("service_name".to_string()),
4698 target_path: Some("/svc/service_name".to_string()),
4699 dependency_type: Some(fdecl::DependencyType::Weak),
4700 ..Default::default()
4701 }),
4702 fdecl::Use::Directory(fdecl::UseDirectory {
4703 dependency_type: Some(fdecl::DependencyType::Weak),
4704 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4705 source_name: Some("DirectoryName".to_string()),
4706 target_path: Some("/data/DirectoryName".to_string()),
4707 rights: Some(fio::Operations::CONNECT),
4708 subdir: None,
4709 ..Default::default()
4710 }),
4711 ]),
4712 offers: Some(vec![
4713 fdecl::Offer::Service(fdecl::OfferService {
4714 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4715 source_name: Some("a".to_string()),
4716 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4717 target_name: Some("a".to_string()),
4718 ..Default::default()
4719 })
4720 ]),
4721 children: Some(vec![
4722 fdecl::Child {
4723 name: Some("child".to_string()),
4724 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4725 startup: Some(fdecl::StartupMode::Lazy),
4726 on_terminate: None,
4727 ..Default::default()
4728 }
4729 ]),
4730 ..new_component_decl()
4731 }
4732 },
4733 result = Ok(()),
4734 },
4735 test_validate_expose_from_self_to_framework_and_parent => {
4736 input = {
4737 fdecl::Component {
4738 capabilities: Some(vec![
4739 fdecl::Capability::Protocol(fdecl::Protocol {
4740 name: Some("a".to_string()),
4741 source_path: Some("/a".to_string()),
4742 ..Default::default()
4743 }),
4744 ]),
4745 exposes: Some(vec![
4746 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
4747 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4748 source_name: Some("a".to_string()),
4749 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4750 target_name: Some("a".to_string()),
4751 ..Default::default()
4752 }),
4753 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
4754 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4755 source_name: Some("a".to_string()),
4756 target: Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})),
4757 target_name: Some("a".to_string()),
4758 ..Default::default()
4759 }),
4760 ]),
4761 ..new_component_decl()
4762 }
4763 },
4764 result = Ok(()),
4765 },
4766 test_validate_use_from_not_child_weak => {
4767 input = {
4768 fdecl::Component {
4769 uses: Some(vec![
4770 fdecl::Use::Protocol(fdecl::UseProtocol {
4771 dependency_type: Some(fdecl::DependencyType::Weak),
4772 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4773 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4774 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
4775 ..Default::default()
4776 }),
4777 ]),
4778 ..new_component_decl()
4779 }
4780 },
4781 result = Err(ErrorList::new(vec![
4782 Error::invalid_field(DeclType::UseProtocol, "dependency_type"),
4783 ])),
4784 },
4785 test_validate_event_stream_offer_valid_decls => {
4786 input = {
4787 let mut decl = new_component_decl();
4788 decl.offers = Some(vec![
4789 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4790 source_name: Some("stopped".to_string()),
4791 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4792 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4793 target_name: Some("stopped".to_string()),
4794 ..Default::default()
4795 }),
4796 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4797 source_name: Some("started".to_string()),
4798 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4799 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4800 target_name: Some("started".to_string()),
4801 ..Default::default()
4802 }),
4803 ]);
4804 decl.children = Some(vec![fdecl::Child{
4805 name: Some("test".to_string()),
4806 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4807 startup: Some(fdecl::StartupMode::Lazy),
4808 on_terminate: None,
4809 environment: None,
4810 ..Default::default()
4811 },
4812 fdecl::Child{
4813 name: Some("test2".to_string()),
4814 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4815 startup: Some(fdecl::StartupMode::Lazy),
4816 on_terminate: None,
4817 environment: None,
4818 ..Default::default()
4819 }
4820 ]);
4821 decl
4822 },
4823 result = Ok(()),
4824 },
4825 test_validate_event_stream_offer_to_framework_invalid => {
4826 input = {
4827 let mut decl = new_component_decl();
4828 decl.offers = Some(vec![
4829 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4830 source_name: Some("stopped".to_string()),
4831 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4832 target: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
4833 target_name: Some("stopped".to_string()),
4834 ..Default::default()
4835 }),
4836 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4837 source_name: Some("started".to_string()),
4838 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4839 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4840 target_name: Some("started".to_string()),
4841 ..Default::default()
4842 }),
4843 ]);
4844 decl.children = Some(vec![fdecl::Child{
4845 name: Some("test".to_string()),
4846 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4847 startup: Some(fdecl::StartupMode::Lazy),
4848 on_terminate: None,
4849 environment: None,
4850 ..Default::default()
4851 },
4852 fdecl::Child{
4853 name: Some("test2".to_string()),
4854 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4855 startup: Some(fdecl::StartupMode::Lazy),
4856 on_terminate: None,
4857 environment: None,
4858 ..Default::default()
4859 }
4860 ]);
4861 decl
4862 },
4863 result = Err(ErrorList::new(vec![
4864 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "target".to_string() }),
4865 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "target".to_string() }),
4866 ])),
4867 },
4868 test_validate_event_stream_offer_to_scope_zero_length_invalid => {
4869 input = {
4870 let mut decl = new_component_decl();
4871 decl.offers = Some(vec![
4872 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4873 source_name: Some("started".to_string()),
4874 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4875 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4876 scope: Some(vec![]),
4877 target_name: Some("started".to_string()),
4878 ..Default::default()
4879 }),
4880 ]);
4881 decl.children = Some(vec![fdecl::Child{
4882 name: Some("test".to_string()),
4883 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4884 startup: Some(fdecl::StartupMode::Lazy),
4885 on_terminate: None,
4886 environment: None,
4887 ..Default::default()
4888 },
4889 fdecl::Child{
4890 name: Some("test2".to_string()),
4891 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4892 startup: Some(fdecl::StartupMode::Lazy),
4893 on_terminate: None,
4894 environment: None,
4895 ..Default::default()
4896 }
4897 ]);
4898 decl
4899 },
4900 result = Err(ErrorList::new(vec![
4901 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "scope".to_string() }),
4902 ])),
4903 },
4904 test_validate_event_stream_offer_to_scope_framework_invalid => {
4905 input = {
4906 let mut decl = new_component_decl();
4907 decl.offers = Some(vec![
4908 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4909 source_name: Some("started".to_string()),
4910 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4911 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4912 scope: Some(vec![fdecl::Ref::Framework(fdecl::FrameworkRef{})]),
4913 target_name: Some("started".to_string()),
4914 ..Default::default()
4915 }),
4916 ]);
4917 decl.children = Some(vec![fdecl::Child{
4918 name: Some("test".to_string()),
4919 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4920 startup: Some(fdecl::StartupMode::Lazy),
4921 on_terminate: None,
4922 environment: None,
4923 ..Default::default()
4924 },
4925 fdecl::Child{
4926 name: Some("test2".to_string()),
4927 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4928 startup: Some(fdecl::StartupMode::Lazy),
4929 on_terminate: None,
4930 environment: None,
4931 ..Default::default()
4932 }
4933 ]);
4934 decl
4935 },
4936 result = Err(ErrorList::new(vec![
4937 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "scope".to_string() }),
4938 ])),
4939 },
4940 test_validate_event_stream_offer_to_scope_valid => {
4941 input = {
4942 let mut decl = new_component_decl();
4943 decl.offers = Some(vec![
4944 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4945 source_name: Some("started".to_string()),
4946 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4947 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4948 scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
4949 target_name: Some("started".to_string()),
4950 ..Default::default()
4951 }),
4952 ]);
4953 decl.children = Some(vec![fdecl::Child{
4954 name: Some("test".to_string()),
4955 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4956 startup: Some(fdecl::StartupMode::Lazy),
4957 on_terminate: None,
4958 environment: None,
4959 ..Default::default()
4960 },
4961 fdecl::Child{
4962 name: Some("test2".to_string()),
4963 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4964 startup: Some(fdecl::StartupMode::Lazy),
4965 on_terminate: None,
4966 environment: None,
4967 ..Default::default()
4968 }
4969 ]);
4970 decl
4971 },
4972 result = Ok(()),
4973 },
4974 test_validate_event_stream_offer_to_scope_with_capability_requested => {
4975 input = {
4976 let mut decl = new_component_decl();
4977 decl.offers = Some(vec![
4978 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4979 source_name: Some("capability_requested".to_string()),
4980 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4981 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4982 scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
4983 target_name: Some("started".to_string()),
4984 ..Default::default()
4985 }),
4986 ]);
4987 decl.children = Some(vec![fdecl::Child{
4988 name: Some("test".to_string()),
4989 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4990 startup: Some(fdecl::StartupMode::Lazy),
4991 on_terminate: None,
4992 environment: None,
4993 ..Default::default()
4994 },
4995 fdecl::Child{
4996 name: Some("test2".to_string()),
4997 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4998 startup: Some(fdecl::StartupMode::Lazy),
4999 on_terminate: None,
5000 environment: None,
5001 ..Default::default()
5002 }
5003 ]);
5004 decl
5005 },
5006 result = Ok(()),
5007 },
5008 test_validate_event_stream_offer_with_no_source_name_invalid => {
5009 input = {
5010 let mut decl = new_component_decl();
5011 decl.offers = Some(vec![
5012 fdecl::Offer::EventStream(fdecl::OfferEventStream {
5013 source_name: None,
5014 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5015 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5016 scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
5017 target_name: Some("started".to_string()),
5018 ..Default::default()
5019 }),
5020 ]);
5021 decl.children = Some(vec![fdecl::Child{
5022 name: Some("test".to_string()),
5023 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5024 startup: Some(fdecl::StartupMode::Lazy),
5025 on_terminate: None,
5026 environment: None,
5027 ..Default::default()
5028 },
5029 fdecl::Child{
5030 name: Some("test2".to_string()),
5031 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
5032 startup: Some(fdecl::StartupMode::Lazy),
5033 on_terminate: None,
5034 environment: None,
5035 ..Default::default()
5036 }
5037 ]);
5038 decl
5039 },
5040 result = Err(ErrorList::new(vec![
5041 Error::MissingField(DeclField { decl: DeclType::OfferEventStream, field: "source_name".to_string() }),
5042 ])),
5043 },
5044 test_validate_event_stream_offer_invalid_source => {
5045 input = {
5046 let mut decl = new_component_decl();
5047 decl.offers = Some(vec![
5048 fdecl::Offer::EventStream(fdecl::OfferEventStream {
5049 source_name: Some("stopped".to_string()),
5050 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
5051 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5052 target_name: Some("stopped".to_string()),
5053 ..Default::default()
5054 }),
5055 fdecl::Offer::EventStream(fdecl::OfferEventStream {
5056 source_name: Some("started".to_string()),
5057 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5058 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5059 target_name: Some("started".to_string()),
5060 ..Default::default()
5061 }),
5062 fdecl::Offer::EventStream(fdecl::OfferEventStream {
5063 source_name: Some("capability_requested".to_string()),
5064 source: Some(fdecl::Ref::Debug(fdecl::DebugRef{})),
5065 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5066 target_name: Some("capability_requested".to_string()),
5067 ..Default::default()
5068 }),
5069 ]);
5070 decl.children = Some(vec![fdecl::Child{
5071 name: Some("test".to_string()),
5072 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5073 startup: Some(fdecl::StartupMode::Lazy),
5074 on_terminate: None,
5075 environment: None,
5076 ..Default::default()
5077 },
5078 fdecl::Child{
5079 name: Some("test2".to_string()),
5080 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
5081 startup: Some(fdecl::StartupMode::Lazy),
5082 on_terminate: None,
5083 environment: None,
5084 ..Default::default()
5085 }
5086 ]);
5087 decl
5088 },
5089 result = Err(ErrorList::new(vec![
5090 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "source".to_string() }),
5091 ])),
5092 },
5093
5094 test_validate_event_stream_offer_missing_source => {
5095 input = {
5096 let mut decl = new_component_decl();
5097 decl.offers = Some(vec![
5098 fdecl::Offer::EventStream(fdecl::OfferEventStream {
5099 source_name: Some("stopped".to_string()),
5100 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
5101 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5102 target_name: Some("stopped".to_string()),
5103 ..Default::default()
5104 }),
5105 fdecl::Offer::EventStream(fdecl::OfferEventStream {
5106 source_name: Some("started".to_string()),
5107 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5108 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5109 target_name: Some("started".to_string()),
5110 ..Default::default()
5111 }),
5112 fdecl::Offer::EventStream(fdecl::OfferEventStream {
5113 source_name: Some("capability_requested".to_string()),
5114 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5115 target_name: Some("capability_requested".to_string()),
5116 ..Default::default()
5117 }),
5118 ]);
5119 decl.children = Some(vec![fdecl::Child{
5120 name: Some("test".to_string()),
5121 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5122 startup: Some(fdecl::StartupMode::Lazy),
5123 on_terminate: None,
5124 environment: None,
5125 ..Default::default()
5126 },
5127 fdecl::Child{
5128 name: Some("test2".to_string()),
5129 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
5130 startup: Some(fdecl::StartupMode::Lazy),
5131 on_terminate: None,
5132 environment: None,
5133 ..Default::default()
5134 }
5135 ]);
5136 decl
5137 },
5138 result = Err(ErrorList::new(vec![
5139 Error::MissingField(DeclField { decl: DeclType::OfferEventStream, field: "source".to_string() }),
5140 ])),
5141 },
5142 test_validate_event_stream_must_have_target_path => {
5143 input = {
5144 let mut decl = new_component_decl();
5145 decl.uses = Some(vec![
5146 fdecl::Use::EventStream(fdecl::UseEventStream {
5147 source_name: Some("bar".to_string()),
5148 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5149 ..Default::default()
5150 }),
5151 ]);
5152 decl
5153 },
5154 result = Err(ErrorList::new(vec![
5155 Error::MissingField(DeclField { decl: DeclType::UseEventStream, field: "target_path".to_string() })
5156 ])),
5157 },
5158 test_validate_event_stream_must_have_source_names => {
5159 input = {
5160 let mut decl = new_component_decl();
5161 decl.uses = Some(vec![
5162 fdecl::Use::EventStream(fdecl::UseEventStream {
5163 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5164 target_path: Some("/svc/something".to_string()),
5165 ..Default::default()
5166 }),
5167 ]);
5168 decl
5169 },
5170 result = Err(ErrorList::new(vec![
5171 Error::MissingField(DeclField { decl: DeclType::UseEventStream, field: "source_name".to_string() })
5172 ])),
5173 },
5174 test_validate_event_stream_scope_must_be_child_or_collection => {
5175 input = {
5176 let mut decl = new_component_decl();
5177 decl.uses = Some(vec![
5178 fdecl::Use::EventStream(fdecl::UseEventStream {
5179 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5180 target_path: Some("/svc/something".to_string()),
5181 source_name: Some("some_source".to_string()),
5182 scope: Some(vec![fdecl::Ref::Framework(fdecl::FrameworkRef{})]),
5183 ..Default::default()
5184 }),
5185 ]);
5186 decl
5187 },
5188 result = Err(ErrorList::new(vec![
5189 Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "scope".to_string() })
5190 ])),
5191 },
5192 test_validate_event_stream_source_must_be_parent_or_child => {
5193 input = {
5194 let mut decl = new_component_decl();
5195 decl.uses = Some(vec![
5196 fdecl::Use::EventStream(fdecl::UseEventStream {
5197 source: Some(fdecl::Ref::Debug(fdecl::DebugRef{})),
5198 target_path: Some("/svc/something".to_string()),
5199 source_name: Some("some_source".to_string()),
5200 scope: Some(vec![]),
5201 ..Default::default()
5202 }),
5203 fdecl::Use::EventStream(fdecl::UseEventStream {
5204 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
5205 target_path: Some("/svc/something_else".to_string()),
5206 source_name: Some("some_source".to_string()),
5207 scope: Some(vec![]),
5208 ..Default::default()
5209 }),
5210 fdecl::Use::EventStream(fdecl::UseEventStream {
5211 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5212 target_path: Some("/svc/yet_something_else".to_string()),
5213 source_name: Some("some_source".to_string()),
5214 scope: Some(vec![]),
5215 ..Default::default()
5216 }),
5217 ]);
5218 decl
5219 },
5220 result = Err(ErrorList::new(vec![
5221 Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() }),
5222 Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() }),
5223 Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() })
5224 ])),
5225 },
5226 test_validate_no_runner => {
5227 input = {
5228 let mut decl = new_component_decl();
5229 decl.program = Some(fdecl::Program {
5230 runner: None,
5231 info: Some(fdata::Dictionary {
5232 entries: None,
5233 ..Default::default()
5234 }),
5235 ..Default::default()
5236 });
5237 decl
5238 },
5239 result = Err(ErrorList::new(vec![
5240 Error::MissingRunner,
5241 ])),
5242 },
5243 test_validate_uses_runner => {
5244 input = {
5245 let mut decl = new_component_decl();
5246 decl.program = Some(fdecl::Program {
5247 runner: None,
5248 info: Some(fdata::Dictionary {
5249 entries: None,
5250 ..Default::default()
5251 }),
5252 ..Default::default()
5253 });
5254 decl.uses = Some(vec![
5255 fdecl::Use::Runner(fdecl::UseRunner {
5256 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5257 source_name: Some("runner".to_string()),
5258 ..Default::default()
5259 }),
5260 ]);
5261 decl
5262 },
5263 result = Ok(()),
5264 },
5265 test_validate_program_and_uses_runner_match => {
5266 input = {
5267 let mut decl = new_component_decl();
5268 decl.program = Some(fdecl::Program {
5269 runner: Some("runner".to_string()),
5270 info: Some(fdata::Dictionary {
5271 entries: None,
5272 ..Default::default()
5273 }),
5274 ..Default::default()
5275 });
5276 decl.uses = Some(vec![
5277 fdecl::Use::Runner(fdecl::UseRunner {
5278 source: Some(fdecl::Ref::Environment(fdecl::EnvironmentRef {})),
5279 source_name: Some("runner".to_string()),
5280 ..Default::default()
5281 }),
5282 ]);
5283 decl
5284 },
5285 result = Ok(()),
5286 },
5287 test_validate_runner_names_conflict => {
5288 input = {
5289 let mut decl = new_component_decl();
5290 decl.program = Some(fdecl::Program {
5291 runner: Some("runner".to_string()),
5292 info: Some(fdata::Dictionary {
5293 entries: None,
5294 ..Default::default()
5295 }),
5296 ..Default::default()
5297 });
5298 decl.uses = Some(vec![
5299 fdecl::Use::Runner(fdecl::UseRunner {
5300 source: Some(fdecl::Ref::Environment(fdecl::EnvironmentRef {})),
5301 source_name: Some("other.runner".to_string()),
5302 ..Default::default()
5303 }),
5304 ]);
5305 decl
5306 },
5307 result = Err(ErrorList::new(vec![
5308 Error::ConflictingRunners,
5309 ])),
5310 },
5311 test_validate_uses_runner_not_environement => {
5312 input = {
5313 let mut decl = new_component_decl();
5314 decl.program = Some(fdecl::Program {
5315 runner: Some("runner".to_string()),
5316 info: Some(fdata::Dictionary {
5317 entries: None,
5318 ..Default::default()
5319 }),
5320 ..Default::default()
5321 });
5322 decl.uses = Some(vec![
5323 fdecl::Use::Runner(fdecl::UseRunner {
5324 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5325 source_name: Some("runner".to_string()),
5326 ..Default::default()
5327 }),
5328 ]);
5329 decl
5330 },
5331 result = Err(ErrorList::new(vec![
5332 Error::ConflictingRunners,
5333 ])),
5334 },
5335 test_validate_uses_long_identifiers => {
5336 input = {
5337 let mut decl = new_component_decl();
5338 decl.program = Some(fdecl::Program {
5339 runner: Some("elf".to_string()),
5340 info: Some(fdata::Dictionary {
5341 entries: None,
5342 ..Default::default()
5343 }),
5344 ..Default::default()
5345 });
5346 decl.uses = Some(vec![
5347 fdecl::Use::Service(fdecl::UseService {
5348 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5349 source_name: Some(format!("{}", "a".repeat(256))),
5350 target_path: Some("/a".repeat(2048)),
5351 dependency_type: Some(fdecl::DependencyType::Strong),
5352 ..Default::default()
5353 }),
5354 fdecl::Use::Protocol(fdecl::UseProtocol {
5355 dependency_type: Some(fdecl::DependencyType::Strong),
5356 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5357 source_name: Some(format!("{}", "a".repeat(256))),
5358 target_path: Some("/b".repeat(2048)),
5359 ..Default::default()
5360 }),
5361 fdecl::Use::Directory(fdecl::UseDirectory {
5362 dependency_type: Some(fdecl::DependencyType::Strong),
5363 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5364 source_name: Some(format!("{}", "a".repeat(256))),
5365 target_path: Some("/c".repeat(2048)),
5366 rights: Some(fio::Operations::CONNECT),
5367 subdir: None,
5368 ..Default::default()
5369 }),
5370 fdecl::Use::Storage(fdecl::UseStorage {
5371 source_name: Some("cache".to_string()),
5372 target_path: Some("/d".repeat(2048)),
5373 ..Default::default()
5374 }),
5375 ]);
5376 decl
5377 },
5378 result = Err(ErrorList::new(vec![
5379 Error::field_too_long(DeclType::UseService, "source_name"),
5380 Error::field_too_long(DeclType::UseService, "target_path"),
5381 Error::field_too_long(DeclType::UseProtocol, "source_name"),
5382 Error::field_too_long(DeclType::UseProtocol, "target_path"),
5383 Error::field_too_long(DeclType::UseDirectory, "source_name"),
5384 Error::field_too_long(DeclType::UseDirectory, "target_path"),
5385 Error::field_too_long(DeclType::UseStorage, "target_path"),
5386 ])),
5387 },
5388 test_validate_conflicting_paths => {
5389 input = {
5390 let mut decl = new_component_decl();
5391 decl.uses = Some(vec![
5392 fdecl::Use::Service(fdecl::UseService {
5393 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5394 source_name: Some("foo".to_string()),
5395 target_path: Some("/bar".to_string()),
5396 dependency_type: Some(fdecl::DependencyType::Strong),
5397 ..Default::default()
5398 }),
5399 fdecl::Use::Protocol(fdecl::UseProtocol {
5400 dependency_type: Some(fdecl::DependencyType::Strong),
5401 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5402 source_name: Some("space".to_string()),
5403 target_path: Some("/bar".to_string()),
5404 ..Default::default()
5405 }),
5406 fdecl::Use::Directory(fdecl::UseDirectory {
5407 dependency_type: Some(fdecl::DependencyType::Strong),
5408 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5409 source_name: Some("crow".to_string()),
5410 target_path: Some("/bar".to_string()),
5411 rights: Some(fio::Operations::CONNECT),
5412 subdir: None,
5413 ..Default::default()
5414 }),
5415 ]);
5416 decl
5417 },
5418 result = Err(ErrorList::new(vec![
5419 Error::duplicate_field(DeclType::UseDirectory, "target_path", "/bar"),
5420 Error::duplicate_field(DeclType::UseProtocol, "target_path", "/bar"),
5421 ])),
5422 },
5423 test_validate_exposes_empty => {
5425 input = {
5426 let mut decl = new_component_decl();
5427 decl.exposes = Some(vec![
5428 fdecl::Expose::Service(fdecl::ExposeService {
5429 source: None,
5430 source_name: None,
5431 target_name: None,
5432 target: None,
5433 ..Default::default()
5434 }),
5435 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5436 source: None,
5437 source_name: None,
5438 target_name: None,
5439 target: None,
5440 ..Default::default()
5441 }),
5442 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5443 source: None,
5444 source_name: None,
5445 target_name: None,
5446 target: None,
5447 rights: None,
5448 subdir: None,
5449 ..Default::default()
5450 }),
5451 fdecl::Expose::Runner(fdecl::ExposeRunner {
5452 source: None,
5453 source_name: None,
5454 target: None,
5455 target_name: None,
5456 ..Default::default()
5457 }),
5458 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5459 source: None,
5460 source_name: None,
5461 target: None,
5462 target_name: None,
5463 ..Default::default()
5464 }),
5465 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5466 ..Default::default()
5467 }),
5468 ]);
5469 decl
5470 },
5471 result = Err(ErrorList::new(vec![
5472 Error::missing_field(DeclType::ExposeService, "source"),
5473 Error::missing_field(DeclType::ExposeService, "target"),
5474 Error::missing_field(DeclType::ExposeService, "source_name"),
5475 Error::missing_field(DeclType::ExposeService, "target_name"),
5476 Error::missing_field(DeclType::ExposeProtocol, "source"),
5477 Error::missing_field(DeclType::ExposeProtocol, "target"),
5478 Error::missing_field(DeclType::ExposeProtocol, "source_name"),
5479 Error::missing_field(DeclType::ExposeProtocol, "target_name"),
5480 Error::missing_field(DeclType::ExposeDirectory, "source"),
5481 Error::missing_field(DeclType::ExposeDirectory, "target"),
5482 Error::missing_field(DeclType::ExposeDirectory, "source_name"),
5483 Error::missing_field(DeclType::ExposeDirectory, "target_name"),
5484 Error::missing_field(DeclType::ExposeRunner, "source"),
5485 Error::missing_field(DeclType::ExposeRunner, "target"),
5486 Error::missing_field(DeclType::ExposeRunner, "source_name"),
5487 Error::missing_field(DeclType::ExposeRunner, "target_name"),
5488 Error::missing_field(DeclType::ExposeResolver, "source"),
5489 Error::missing_field(DeclType::ExposeResolver, "target"),
5490 Error::missing_field(DeclType::ExposeResolver, "source_name"),
5491 Error::missing_field(DeclType::ExposeResolver, "target_name"),
5492 Error::missing_field(DeclType::ExposeDictionary, "source"),
5493 Error::missing_field(DeclType::ExposeDictionary, "target"),
5494 Error::missing_field(DeclType::ExposeDictionary, "source_name"),
5495 Error::missing_field(DeclType::ExposeDictionary, "target_name"),
5496 ])),
5497 },
5498 test_validate_exposes_extraneous => {
5499 input = {
5500 let mut decl = new_component_decl();
5501 decl.exposes = Some(vec![
5502 fdecl::Expose::Service(fdecl::ExposeService {
5503 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5504 name: "logger".to_string(),
5505 collection: Some("modular".to_string()),
5506 })),
5507 source_name: Some("logger".to_string()),
5508 target_name: Some("logger".to_string()),
5509 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5510 ..Default::default()
5511 }),
5512 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5513 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5514 name: "logger".to_string(),
5515 collection: Some("modular".to_string()),
5516 })),
5517 source_name: Some("legacy_logger".to_string()),
5518 target_name: Some("legacy_logger".to_string()),
5519 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5520 ..Default::default()
5521 }),
5522 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5523 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5524 name: "netstack".to_string(),
5525 collection: Some("modular".to_string()),
5526 })),
5527 source_name: Some("data".to_string()),
5528 target_name: Some("data".to_string()),
5529 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5530 rights: Some(fio::Operations::CONNECT),
5531 subdir: None,
5532 ..Default::default()
5533 }),
5534 fdecl::Expose::Runner(fdecl::ExposeRunner {
5535 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5536 name: "netstack".to_string(),
5537 collection: Some("modular".to_string()),
5538 })),
5539 source_name: Some("elf".to_string()),
5540 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5541 target_name: Some("elf".to_string()),
5542 ..Default::default()
5543 }),
5544 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5545 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5546 name: "netstack".to_string(),
5547 collection: Some("modular".to_string()),
5548 })),
5549 source_name: Some("pkg".to_string()),
5550 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5551 target_name: Some("pkg".to_string()),
5552 ..Default::default()
5553 }),
5554 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5555 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5556 name: "netstack".to_string(),
5557 collection: Some("modular".to_string()),
5558 })),
5559 source_name: Some("dict".to_string()),
5560 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5561 target_name: Some("dict".to_string()),
5562 ..Default::default()
5563 }),
5564 ]);
5565 decl
5566 },
5567 result = Err(ErrorList::new(vec![
5568 Error::extraneous_field(DeclType::ExposeService, "source.child.collection"),
5569 Error::extraneous_field(DeclType::ExposeProtocol, "source.child.collection"),
5570 Error::extraneous_field(DeclType::ExposeDirectory, "source.child.collection"),
5571 Error::extraneous_field(DeclType::ExposeRunner, "source.child.collection"),
5572 Error::extraneous_field(DeclType::ExposeResolver, "source.child.collection"),
5573 Error::extraneous_field(DeclType::ExposeDictionary, "source.child.collection"),
5574 ])),
5575 },
5576 test_validate_exposes_invalid_identifiers => {
5577 input = {
5578 let mut decl = new_component_decl();
5579 decl.exposes = Some(vec![
5580 fdecl::Expose::Service(fdecl::ExposeService {
5581 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5582 name: "^bad".to_string(),
5583 collection: None,
5584 })),
5585 source_name: Some("foo/".to_string()),
5586 target_name: Some("/".to_string()),
5587 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5588 ..Default::default()
5589 }),
5590 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5591 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5592 name: "^bad".to_string(),
5593 collection: None,
5594 })),
5595 source_name: Some("foo/".to_string()),
5596 target_name: Some("/".to_string()),
5597 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5598 ..Default::default()
5599 }),
5600 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5601 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5602 name: "^bad".to_string(),
5603 collection: None,
5604 })),
5605 source_name: Some("foo/".to_string()),
5606 target_name: Some("/".to_string()),
5607 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5608 rights: Some(fio::Operations::CONNECT),
5609 subdir: Some("/foo".to_string()),
5610 ..Default::default()
5611 }),
5612 fdecl::Expose::Runner(fdecl::ExposeRunner {
5613 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5614 name: "^bad".to_string(),
5615 collection: None,
5616 })),
5617 source_name: Some("/path".to_string()),
5618 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5619 target_name: Some("elf!".to_string()),
5620 ..Default::default()
5621 }),
5622 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5623 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5624 name: "^bad".to_string(),
5625 collection: None,
5626 })),
5627 source_name: Some("/path".to_string()),
5628 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5629 target_name: Some("pkg!".to_string()),
5630 ..Default::default()
5631 }),
5632 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5633 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5634 name: "^bad".to_string(),
5635 collection: None,
5636 })),
5637 source_name: Some("/path".to_string()),
5638 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5639 target_name: Some("pkg!".to_string()),
5640 ..Default::default()
5641 }),
5642 ]);
5643 decl
5644 },
5645 result = Err(ErrorList::new(vec![
5646 Error::invalid_field(DeclType::ExposeService, "source.child.name"),
5647 Error::invalid_field(DeclType::ExposeService, "source_name"),
5648 Error::invalid_field(DeclType::ExposeService, "target_name"),
5649 Error::invalid_field(DeclType::ExposeProtocol, "source.child.name"),
5650 Error::invalid_field(DeclType::ExposeProtocol, "source_name"),
5651 Error::invalid_field(DeclType::ExposeProtocol, "target_name"),
5652 Error::invalid_field(DeclType::ExposeDirectory, "source.child.name"),
5653 Error::invalid_field(DeclType::ExposeDirectory, "source_name"),
5654 Error::invalid_field(DeclType::ExposeDirectory, "target_name"),
5655 Error::invalid_field(DeclType::ExposeDirectory, "subdir"),
5656 Error::invalid_field(DeclType::ExposeRunner, "source.child.name"),
5657 Error::invalid_field(DeclType::ExposeRunner, "source_name"),
5658 Error::invalid_field(DeclType::ExposeRunner, "target_name"),
5659 Error::invalid_field(DeclType::ExposeResolver, "source.child.name"),
5660 Error::invalid_field(DeclType::ExposeResolver, "source_name"),
5661 Error::invalid_field(DeclType::ExposeResolver, "target_name"),
5662 Error::invalid_field(DeclType::ExposeDictionary, "source.child.name"),
5663 Error::invalid_field(DeclType::ExposeDictionary, "source_name"),
5664 Error::invalid_field(DeclType::ExposeDictionary, "target_name"),
5665 ])),
5666 },
5667 test_validate_exposes_invalid_source_target => {
5668 input = {
5669 let mut decl = new_component_decl();
5670 decl.children = Some(vec![fdecl::Child{
5671 name: Some("logger".to_string()),
5672 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5673 startup: Some(fdecl::StartupMode::Lazy),
5674 on_terminate: None,
5675 environment: None,
5676 ..Default::default()
5677 }]);
5678 decl.exposes = Some(vec![
5679 fdecl::Expose::Service(fdecl::ExposeService {
5680 source: None,
5681 source_name: Some("a".to_string()),
5682 target_name: Some("b".to_string()),
5683 target: None,
5684 ..Default::default()
5685 }),
5686 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5687 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5688 source_name: Some("c".to_string()),
5689 target_name: Some("d".to_string()),
5690 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5691 ..Default::default()
5692 }),
5693 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5694 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "z".to_string()})),
5695 source_name: Some("e".to_string()),
5696 target_name: Some("f".to_string()),
5697 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "z".to_string()})),
5698 rights: Some(fio::Operations::CONNECT),
5699 subdir: None,
5700 ..Default::default()
5701 }),
5702 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5703 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5704 source_name: Some("g".to_string()),
5705 target_name: Some("h".to_string()),
5706 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5707 rights: Some(fio::Operations::CONNECT),
5708 subdir: None,
5709 ..Default::default()
5710 }),
5711 fdecl::Expose::Runner(fdecl::ExposeRunner {
5712 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5713 source_name: Some("i".to_string()),
5714 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5715 target_name: Some("j".to_string()),
5716 ..Default::default()
5717 }),
5718 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5719 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5720 source_name: Some("k".to_string()),
5721 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5722 target_name: Some("l".to_string()),
5723 ..Default::default()
5724 }),
5725 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5726 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5727 name: "logger".to_string(),
5728 collection: None,
5729 })),
5730 source_name: Some("m".to_string()),
5731 target_name: Some("n".to_string()),
5732 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5733 ..Default::default()
5734 }),
5735 ]);
5736 decl
5737 },
5738 result = Err(ErrorList::new(vec![
5739 Error::missing_field(DeclType::ExposeService, "source"),
5740 Error::missing_field(DeclType::ExposeService, "target"),
5741 Error::invalid_field(DeclType::ExposeProtocol, "source"),
5742 Error::invalid_field(DeclType::ExposeProtocol, "target"),
5743 Error::invalid_field(DeclType::ExposeDirectory, "source"),
5744 Error::invalid_field(DeclType::ExposeDirectory, "target"),
5745 Error::invalid_field(DeclType::ExposeDirectory, "source"),
5746 Error::invalid_field(DeclType::ExposeDirectory, "target"),
5747 Error::invalid_field(DeclType::ExposeRunner, "source"),
5748 Error::invalid_field(DeclType::ExposeRunner, "target"),
5749 Error::invalid_field(DeclType::ExposeResolver, "source"),
5750 Error::invalid_field(DeclType::ExposeResolver, "target"),
5751 Error::invalid_field(DeclType::ExposeDictionary, "target"),
5752 ])),
5753 },
5754 test_validate_exposes_invalid_source_collection => {
5755 input = {
5756 let mut decl = new_component_decl();
5757 decl.collections = Some(vec![fdecl::Collection{
5758 name: Some("col".to_string()),
5759 durability: Some(fdecl::Durability::Transient),
5760 allowed_offers: None,
5761 allow_long_names: None,
5762 ..Default::default()
5763 }]);
5764 decl.exposes = Some(vec![
5765 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5766 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
5767 source_name: Some("a".to_string()),
5768 target_name: Some("a".to_string()),
5769 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5770 ..Default::default()
5771 }),
5772 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5773 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5774 source_name: Some("b".to_string()),
5775 target_name: Some("b".to_string()),
5776 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5777 rights: Some(fio::Operations::CONNECT),
5778 subdir: None,
5779 ..Default::default()
5780 }),
5781 fdecl::Expose::Runner(fdecl::ExposeRunner {
5782 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5783 source_name: Some("c".to_string()),
5784 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5785 target_name: Some("c".to_string()),
5786 ..Default::default()
5787 }),
5788 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5789 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5790 source_name: Some("d".to_string()),
5791 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5792 target_name: Some("d".to_string()),
5793 ..Default::default()
5794 }),
5795 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5796 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5797 source_name: Some("e".to_string()),
5798 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5799 target_name: Some("e".to_string()),
5800 ..Default::default()
5801 }),
5802 ]);
5803 decl
5804 },
5805 result = Err(ErrorList::new(vec![
5806 Error::invalid_field(DeclType::ExposeProtocol, "source"),
5807 Error::invalid_field(DeclType::ExposeDirectory, "source"),
5808 Error::invalid_field(DeclType::ExposeRunner, "source"),
5809 Error::invalid_field(DeclType::ExposeResolver, "source"),
5810 Error::invalid_field(DeclType::ExposeDictionary, "source"),
5811 ])),
5812 },
5813 test_validate_exposes_sources_collection => {
5814 input = {
5815 let mut decl = new_component_decl();
5816 decl.collections = Some(vec![
5817 fdecl::Collection {
5818 name: Some("col".to_string()),
5819 durability: Some(fdecl::Durability::Transient),
5820 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
5821 allow_long_names: None,
5822 ..Default::default()
5823 }
5824 ]);
5825 decl.exposes = Some(vec![
5826 fdecl::Expose::Service(fdecl::ExposeService {
5827 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
5828 source_name: Some("a".to_string()),
5829 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5830 target_name: Some("a".to_string()),
5831 ..Default::default()
5832 })
5833 ]);
5834 decl
5835 },
5836 result = Ok(()),
5837 },
5838 test_validate_exposes_long_identifiers => {
5839 input = {
5840 let mut decl = new_component_decl();
5841 decl.exposes = Some(vec![
5842 fdecl::Expose::Service(fdecl::ExposeService {
5843 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5844 name: "b".repeat(256),
5845 collection: None,
5846 })),
5847 source_name: Some(format!("{}", "a".repeat(1025))),
5848 target_name: Some(format!("{}", "b".repeat(1025))),
5849 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5850 ..Default::default()
5851 }),
5852 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5853 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5854 name: "b".repeat(256),
5855 collection: None,
5856 })),
5857 source_name: Some(format!("{}", "a".repeat(256))),
5858 target_name: Some(format!("{}", "b".repeat(256))),
5859 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5860 ..Default::default()
5861 }),
5862 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5863 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5864 name: "b".repeat(256),
5865 collection: None,
5866 })),
5867 source_name: Some(format!("{}", "a".repeat(256))),
5868 target_name: Some(format!("{}", "b".repeat(256))),
5869 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5870 rights: Some(fio::Operations::CONNECT),
5871 subdir: None,
5872 ..Default::default()
5873 }),
5874 fdecl::Expose::Runner(fdecl::ExposeRunner {
5875 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5876 name: "b".repeat(256),
5877 collection: None,
5878 })),
5879 source_name: Some("a".repeat(256)),
5880 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5881 target_name: Some("b".repeat(256)),
5882 ..Default::default()
5883 }),
5884 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5885 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5886 name: "b".repeat(256),
5887 collection: None,
5888 })),
5889 source_name: Some("a".repeat(256)),
5890 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5891 target_name: Some("b".repeat(256)),
5892 ..Default::default()
5893 }),
5894 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5895 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5896 name: "b".repeat(256),
5897 collection: None,
5898 })),
5899 source_name: Some("a".repeat(256)),
5900 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5901 target_name: Some("b".repeat(256)),
5902 ..Default::default()
5903 }),
5904 ]);
5905 decl
5906 },
5907 result = Err(ErrorList::new(vec![
5908 Error::field_too_long(DeclType::ExposeService, "source.child.name"),
5909 Error::field_too_long(DeclType::ExposeService, "source_name"),
5910 Error::field_too_long(DeclType::ExposeService, "target_name"),
5911 Error::field_too_long(DeclType::ExposeProtocol, "source.child.name"),
5912 Error::field_too_long(DeclType::ExposeProtocol, "source_name"),
5913 Error::field_too_long(DeclType::ExposeProtocol, "target_name"),
5914 Error::field_too_long(DeclType::ExposeDirectory, "source.child.name"),
5915 Error::field_too_long(DeclType::ExposeDirectory, "source_name"),
5916 Error::field_too_long(DeclType::ExposeDirectory, "target_name"),
5917 Error::field_too_long(DeclType::ExposeRunner, "source.child.name"),
5918 Error::field_too_long(DeclType::ExposeRunner, "source_name"),
5919 Error::field_too_long(DeclType::ExposeRunner, "target_name"),
5920 Error::field_too_long(DeclType::ExposeResolver, "source.child.name"),
5921 Error::field_too_long(DeclType::ExposeResolver, "source_name"),
5922 Error::field_too_long(DeclType::ExposeResolver, "target_name"),
5923 Error::field_too_long(DeclType::ExposeDictionary, "source.child.name"),
5924 Error::field_too_long(DeclType::ExposeDictionary, "source_name"),
5925 Error::field_too_long(DeclType::ExposeDictionary, "target_name"),
5926 ])),
5927 },
5928 test_validate_exposes_invalid_child => {
5929 input = {
5930 let mut decl = new_component_decl();
5931 decl.exposes = Some(vec![
5932 fdecl::Expose::Service(fdecl::ExposeService {
5933 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5934 name: "netstack".to_string(),
5935 collection: None,
5936 })),
5937 source_name: Some("fuchsia.logger.Log".to_string()),
5938 target_name: Some("fuchsia.logger.Log".to_string()),
5939 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5940 ..Default::default()
5941 }),
5942 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5943 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5944 name: "netstack".to_string(),
5945 collection: None,
5946 })),
5947 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
5948 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
5949 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5950 ..Default::default()
5951 }),
5952 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5953 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5954 name: "netstack".to_string(),
5955 collection: None,
5956 })),
5957 source_name: Some("data".to_string()),
5958 target_name: Some("data".to_string()),
5959 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5960 rights: Some(fio::Operations::CONNECT),
5961 subdir: None,
5962 ..Default::default()
5963 }),
5964 fdecl::Expose::Runner(fdecl::ExposeRunner {
5965 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5966 name: "netstack".to_string(),
5967 collection: None,
5968 })),
5969 source_name: Some("elf".to_string()),
5970 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5971 target_name: Some("elf".to_string()),
5972 ..Default::default()
5973 }),
5974 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5975 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5976 name: "netstack".to_string(),
5977 collection: None,
5978 })),
5979 source_name: Some("pkg".to_string()),
5980 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5981 target_name: Some("pkg".to_string()),
5982 ..Default::default()
5983 }),
5984 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5985 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5986 name: "netstack".to_string(),
5987 collection: None,
5988 })),
5989 source_name: Some("dict".to_string()),
5990 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5991 target_name: Some("dict".to_string()),
5992 ..Default::default()
5993 }),
5994 ]);
5995 decl
5996 },
5997 result = Err(ErrorList::new(vec![
5998 Error::invalid_child(DeclType::ExposeService, "source", "netstack"),
5999 Error::invalid_child(DeclType::ExposeProtocol, "source", "netstack"),
6000 Error::invalid_child(DeclType::ExposeDirectory, "source", "netstack"),
6001 Error::invalid_child(DeclType::ExposeRunner, "source", "netstack"),
6002 Error::invalid_child(DeclType::ExposeResolver, "source", "netstack"),
6003 Error::invalid_child(DeclType::ExposeDictionary, "source", "netstack"),
6004 ])),
6005 },
6006 test_validate_exposes_invalid_source_capability => {
6007 input = {
6008 fdecl::Component {
6009 exposes: Some(vec![
6010 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6011 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
6012 name: "this-storage-doesnt-exist".to_string(),
6013 })),
6014 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
6015 target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
6016 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6017 ..Default::default()
6018 }),
6019 ]),
6020 ..new_component_decl()
6021 }
6022 },
6023 result = Err(ErrorList::new(vec![
6024 Error::invalid_capability(DeclType::ExposeProtocol, "source", "this-storage-doesnt-exist"),
6025 ])),
6026 },
6027 test_validate_exposes_duplicate_target => {
6028 input = {
6029 let mut decl = new_component_decl();
6030 decl.exposes = Some(vec![
6031 fdecl::Expose::Service(fdecl::ExposeService {
6032 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6033 name: "coll".into(),
6034 })),
6035 source_name: Some("netstack".to_string()),
6036 target_name: Some("fuchsia.net.Stack".to_string()),
6037 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6038 ..Default::default()
6039 }),
6040 fdecl::Expose::Service(fdecl::ExposeService {
6041 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6042 name: "coll2".into(),
6043 })),
6044 source_name: Some("netstack2".to_string()),
6045 target_name: Some("fuchsia.net.Stack".to_string()),
6046 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6047 ..Default::default()
6048 }),
6049 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6050 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6051 source_name: Some("fonts".to_string()),
6052 target_name: Some("fuchsia.fonts.Provider".to_string()),
6053 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6054 ..Default::default()
6055 }),
6056 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6057 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6058 source_name: Some("fonts2".to_string()),
6059 target_name: Some("fuchsia.fonts.Provider".to_string()),
6060 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6061 ..Default::default()
6062 }),
6063 fdecl::Expose::Directory(fdecl::ExposeDirectory {
6064 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6065 source_name: Some("assets".to_string()),
6066 target_name: Some("stuff".to_string()),
6067 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6068 rights: None,
6069 subdir: None,
6070 ..Default::default()
6071 }),
6072 fdecl::Expose::Directory(fdecl::ExposeDirectory {
6073 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6074 source_name: Some("assets2".to_string()),
6075 target_name: Some("stuff".to_string()),
6076 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6077 rights: None,
6078 subdir: None,
6079 ..Default::default()
6080 }),
6081 fdecl::Expose::Runner(fdecl::ExposeRunner {
6082 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6083 source_name: Some("source_elf".to_string()),
6084 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6085 target_name: Some("elf".to_string()),
6086 ..Default::default()
6087 }),
6088 fdecl::Expose::Runner(fdecl::ExposeRunner {
6089 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6090 source_name: Some("source_elf".to_string()),
6091 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6092 target_name: Some("elf".to_string()),
6093 ..Default::default()
6094 }),
6095 fdecl::Expose::Resolver(fdecl::ExposeResolver {
6096 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6097 source_name: Some("source_pkg".to_string()),
6098 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6099 target_name: Some("pkg".to_string()),
6100 ..Default::default()
6101 }),
6102 fdecl::Expose::Resolver(fdecl::ExposeResolver {
6103 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6104 source_name: Some("source_pkg".to_string()),
6105 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6106 target_name: Some("pkg".to_string()),
6107 ..Default::default()
6108 }),
6109 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
6110 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6111 source_name: Some("source_dict".to_string()),
6112 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6113 target_name: Some("dict".to_string()),
6114 ..Default::default()
6115 }),
6116 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
6117 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6118 source_name: Some("source_dict".to_string()),
6119 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6120 target_name: Some("dict".to_string()),
6121 ..Default::default()
6122 }),
6123 ]);
6124 decl.collections = Some(vec![
6125 fdecl::Collection {
6126 name: Some("coll".into()),
6127 durability: Some(fdecl::Durability::Transient),
6128 ..Default::default()
6129 },
6130 fdecl::Collection {
6131 name: Some("coll2".into()),
6132 durability: Some(fdecl::Durability::Transient),
6133 ..Default::default()
6134 },
6135 ]);
6136 decl.capabilities = Some(vec![
6137 fdecl::Capability::Service(fdecl::Service {
6138 name: Some("netstack".to_string()),
6139 source_path: Some("/path".to_string()),
6140 ..Default::default()
6141 }),
6142 fdecl::Capability::Service(fdecl::Service {
6143 name: Some("netstack2".to_string()),
6144 source_path: Some("/path".to_string()),
6145 ..Default::default()
6146 }),
6147 fdecl::Capability::Protocol(fdecl::Protocol {
6148 name: Some("fonts".to_string()),
6149 source_path: Some("/path".to_string()),
6150 ..Default::default()
6151 }),
6152 fdecl::Capability::Protocol(fdecl::Protocol {
6153 name: Some("fonts2".to_string()),
6154 source_path: Some("/path".to_string()),
6155 ..Default::default()
6156 }),
6157 fdecl::Capability::Directory(fdecl::Directory {
6158 name: Some("assets".to_string()),
6159 source_path: Some("/path".to_string()),
6160 rights: Some(fio::Operations::CONNECT),
6161 ..Default::default()
6162 }),
6163 fdecl::Capability::Directory(fdecl::Directory {
6164 name: Some("assets2".to_string()),
6165 source_path: Some("/path".to_string()),
6166 rights: Some(fio::Operations::CONNECT),
6167 ..Default::default()
6168 }),
6169 fdecl::Capability::Runner(fdecl::Runner {
6170 name: Some("source_elf".to_string()),
6171 source_path: Some("/path".to_string()),
6172 ..Default::default()
6173 }),
6174 fdecl::Capability::Resolver(fdecl::Resolver {
6175 name: Some("source_pkg".to_string()),
6176 source_path: Some("/path".to_string()),
6177 ..Default::default()
6178 }),
6179 fdecl::Capability::Dictionary(fdecl::Dictionary {
6180 name: Some("source_dict".to_string()),
6181 ..Default::default()
6182 }),
6183 ]);
6184 decl
6185 },
6186 result = Err(ErrorList::new(vec![
6187 Error::duplicate_field(DeclType::ExposeProtocol, "target_name",
6189 "fuchsia.fonts.Provider"),
6190 Error::duplicate_field(DeclType::ExposeDirectory, "target_name",
6191 "stuff"),
6192 Error::duplicate_field(DeclType::ExposeRunner, "target_name",
6193 "elf"),
6194 Error::duplicate_field(DeclType::ExposeResolver, "target_name", "pkg"),
6195 Error::duplicate_field(DeclType::ExposeDictionary, "target_name", "dict"),
6196 ])),
6197 },
6198 test_validate_exposes_invalid_capability_from_self => {
6199 input = {
6200 let mut decl = new_component_decl();
6201 decl.exposes = Some(vec![
6202 fdecl::Expose::Service(fdecl::ExposeService {
6203 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6204 source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
6205 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6206 target_name: Some("foo".to_string()),
6207 ..Default::default()
6208 }),
6209 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6210 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6211 source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
6212 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6213 target_name: Some("bar".to_string()),
6214 ..Default::default()
6215 }),
6216 fdecl::Expose::Directory(fdecl::ExposeDirectory {
6217 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6218 source_name: Some("dir".to_string()),
6219 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6220 target_name: Some("assets".to_string()),
6221 rights: None,
6222 subdir: None,
6223 ..Default::default()
6224 }),
6225 fdecl::Expose::Runner(fdecl::ExposeRunner {
6226 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6227 source_name: Some("source_elf".to_string()),
6228 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6229 target_name: Some("elf".to_string()),
6230 ..Default::default()
6231 }),
6232 fdecl::Expose::Resolver(fdecl::ExposeResolver {
6233 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6234 source_name: Some("source_pkg".to_string()),
6235 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6236 target_name: Some("pkg".to_string()),
6237 ..Default::default()
6238 }),
6239 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
6240 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6241 source_name: Some("source_dict".to_string()),
6242 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6243 target_name: Some("dict".to_string()),
6244 ..Default::default()
6245 }),
6246 fdecl::Expose::Config(fdecl::ExposeConfiguration {
6247 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6248 source_name: Some("source_config".to_string()),
6249 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6250 target_name: Some("config".to_string()),
6251 ..Default::default()
6252 }),
6253 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6254 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6255 source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
6256 source_dictionary: Some("dict/inner".to_string()),
6257 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6258 target_name: Some("baz".to_string()),
6259 ..Default::default()
6260 }),
6261 ]);
6262 decl
6263 },
6264 result = Err(ErrorList::new(vec![
6265 Error::invalid_capability(
6266 DeclType::ExposeService,
6267 "source",
6268 "fuchsia.some.library.SomeProtocol"),
6269 Error::invalid_capability(
6270 DeclType::ExposeProtocol,
6271 "source",
6272 "fuchsia.some.library.SomeProtocol"),
6273 Error::invalid_capability(DeclType::ExposeDirectory, "source", "dir"),
6274 Error::invalid_capability(DeclType::ExposeRunner, "source", "source_elf"),
6275 Error::invalid_capability(DeclType::ExposeResolver, "source", "source_pkg"),
6276 Error::invalid_capability(DeclType::ExposeDictionary, "source", "source_dict"),
6277 Error::invalid_capability(DeclType::ExposeConfig, "source", "source_config"),
6278 Error::invalid_capability(DeclType::ExposeProtocol, "source", "dict"),
6279 ])),
6280 },
6281
6282 test_validate_exposes_availability_service => {
6283 input = {
6284 let mut decl = generate_expose_different_source_and_availability_decl(
6285 |source, availability, target_name|
6286 fdecl::Expose::Service(fdecl::ExposeService {
6287 source: Some(source),
6288 source_name: Some("fuchsia.examples.Echo".to_string()),
6289 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6290 target_name: Some(target_name.to_string()),
6291 availability: Some(availability),
6292 ..Default::default()
6293 })
6294 );
6295 decl.capabilities = Some(vec![
6296 fdecl::Capability::Service(fdecl::Service {
6297 name: Some("fuchsia.examples.Echo".to_string()),
6298 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
6299 ..Default::default()
6300 }),
6301 ]);
6302 decl
6303 },
6304 result = {
6305 Err(ErrorList::new(vec![
6306 Error::availability_must_be_optional(
6307 DeclType::ExposeService,
6308 "availability",
6309 Some(&"fuchsia.examples.Echo".to_string()),
6310 ),
6311 Error::availability_must_be_optional(
6312 DeclType::ExposeService,
6313 "availability",
6314 Some(&"fuchsia.examples.Echo".to_string()),
6315 ),
6316 ]))
6317 },
6318 },
6319 test_validate_exposes_availability_protocol => {
6320 input = {
6321 let mut decl = generate_expose_different_source_and_availability_decl(
6322 |source, availability, target_name|
6323 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6324 source: Some(source),
6325 source_name: Some("fuchsia.examples.Echo".to_string()),
6326 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6327 target_name: Some(target_name.to_string()),
6328 availability: Some(availability),
6329 ..Default::default()
6330 })
6331 );
6332 decl.capabilities = Some(vec![
6333 fdecl::Capability::Protocol(fdecl::Protocol {
6334 name: Some("fuchsia.examples.Echo".to_string()),
6335 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
6336 ..Default::default()
6337 }),
6338 ]);
6339 decl
6340 },
6341 result = {
6342 Err(ErrorList::new(vec![
6343 Error::availability_must_be_optional(
6344 DeclType::ExposeProtocol,
6345 "availability",
6346 Some(&"fuchsia.examples.Echo".to_string()),
6347 ),
6348 Error::availability_must_be_optional(
6349 DeclType::ExposeProtocol,
6350 "availability",
6351 Some(&"fuchsia.examples.Echo".to_string()),
6352 ),
6353 ]))
6354 },
6355 },
6356 test_validate_exposes_availability_directory => {
6357 input = {
6358 let mut decl = generate_expose_different_source_and_availability_decl(
6359 |source, availability, target_name|
6360 fdecl::Expose::Directory(fdecl::ExposeDirectory {
6361 source: Some(source),
6362 source_name: Some("fuchsia.examples.Echo".to_string()),
6363 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6364 target_name: Some(target_name.to_string()),
6365 availability: Some(availability),
6366 ..Default::default()
6367 })
6368 );
6369 decl.capabilities = Some(vec![
6370 fdecl::Capability::Directory(fdecl::Directory {
6371 name: Some("fuchsia.examples.Echo".to_string()),
6372 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
6373 rights: Some(fio::Operations::READ_BYTES),
6374 ..Default::default()
6375 }),
6376 ]);
6377 decl
6378 },
6379 result = {
6380 Err(ErrorList::new(vec![
6381 Error::availability_must_be_optional(
6382 DeclType::ExposeDirectory,
6383 "availability",
6384 Some(&"fuchsia.examples.Echo".to_string()),
6385 ),
6386 Error::availability_must_be_optional(
6387 DeclType::ExposeDirectory,
6388 "availability",
6389 Some(&"fuchsia.examples.Echo".to_string()),
6390 ),
6391 ]))
6392 },
6393 },
6394
6395 test_validate_offers_empty => {
6397 input = {
6398 let mut decl = new_component_decl();
6399 decl.offers = Some(vec![
6400 fdecl::Offer::Service(fdecl::OfferService {
6401 source: None,
6402 source_name: None,
6403 target: None,
6404 target_name: None,
6405 ..Default::default()
6406 }),
6407 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6408 source: None,
6409 source_name: None,
6410 target: None,
6411 target_name: None,
6412 dependency_type: None,
6413 ..Default::default()
6414 }),
6415 fdecl::Offer::Directory(fdecl::OfferDirectory {
6416 source: None,
6417 source_name: None,
6418 target: None,
6419 target_name: None,
6420 rights: None,
6421 subdir: None,
6422 dependency_type: None,
6423 ..Default::default()
6424 }),
6425 fdecl::Offer::Storage(fdecl::OfferStorage {
6426 source_name: None,
6427 source: None,
6428 target: None,
6429 target_name: None,
6430 ..Default::default()
6431 }),
6432 fdecl::Offer::Runner(fdecl::OfferRunner {
6433 source: None,
6434 source_name: None,
6435 target: None,
6436 target_name: None,
6437 ..Default::default()
6438 }),
6439 fdecl::Offer::Resolver(fdecl::OfferResolver {
6440 ..Default::default()
6441 }),
6442 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6443 ..Default::default()
6444 }),
6445 ]);
6446 decl
6447 },
6448 result = Err(ErrorList::new(vec![
6451 Error::missing_field(DeclType::OfferService, "source"),
6452 Error::missing_field(DeclType::OfferService, "source_name"),
6453 Error::missing_field(DeclType::OfferService, "target"),
6454 Error::missing_field(DeclType::OfferService, "target_name"),
6455 Error::missing_field(DeclType::OfferProtocol, "source"),
6457 Error::missing_field(DeclType::OfferProtocol, "source_name"),
6458 Error::missing_field(DeclType::OfferProtocol, "target"),
6459 Error::missing_field(DeclType::OfferProtocol, "target_name"),
6460 Error::missing_field(DeclType::OfferProtocol, "dependency_type"),
6461 Error::missing_field(DeclType::OfferDirectory, "source"),
6463 Error::missing_field(DeclType::OfferDirectory, "source_name"),
6464 Error::missing_field(DeclType::OfferDirectory, "target"),
6465 Error::missing_field(DeclType::OfferDirectory, "target_name"),
6466 Error::missing_field(DeclType::OfferDirectory, "dependency_type"),
6467 Error::missing_field(DeclType::OfferStorage, "source"),
6469 Error::missing_field(DeclType::OfferStorage, "source_name"),
6470 Error::missing_field(DeclType::OfferStorage, "target"),
6471 Error::missing_field(DeclType::OfferStorage, "target_name"),
6472 Error::missing_field(DeclType::OfferRunner, "source"),
6474 Error::missing_field(DeclType::OfferRunner, "source_name"),
6475 Error::missing_field(DeclType::OfferRunner, "target"),
6476 Error::missing_field(DeclType::OfferRunner, "target_name"),
6477 Error::missing_field(DeclType::OfferResolver, "source"),
6479 Error::missing_field(DeclType::OfferResolver, "source_name"),
6480 Error::missing_field(DeclType::OfferResolver, "target"),
6481 Error::missing_field(DeclType::OfferResolver, "target_name"),
6482 Error::missing_field(DeclType::OfferDictionary, "source"),
6483 Error::missing_field(DeclType::OfferDictionary, "source_name"),
6484 Error::missing_field(DeclType::OfferDictionary, "target"),
6485 Error::missing_field(DeclType::OfferDictionary, "target_name"),
6486 Error::missing_field(DeclType::OfferDictionary, "dependency_type"),
6487 ])),
6488 },
6489 test_validate_offers_long_identifiers => {
6490 input = {
6491 let mut decl = new_component_decl();
6492 decl.offers = Some(vec![
6493 fdecl::Offer::Service(fdecl::OfferService {
6494 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6495 name: "a".repeat(256),
6496 collection: None,
6497 })),
6498 source_name: Some(format!("{}", "a".repeat(256))),
6499 target: Some(fdecl::Ref::Child(
6500 fdecl::ChildRef {
6501 name: "b".repeat(256),
6502 collection: None,
6503 }
6504 )),
6505 target_name: Some(format!("{}", "b".repeat(256))),
6506 ..Default::default()
6507 }),
6508 fdecl::Offer::Service(fdecl::OfferService {
6509 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6510 source_name: Some("a".to_string()),
6511 target: Some(fdecl::Ref::Collection(
6512 fdecl::CollectionRef {
6513 name: "b".repeat(256),
6514 }
6515 )),
6516 target_name: Some(format!("{}", "b".repeat(256))),
6517 ..Default::default()
6518 }),
6519 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6520 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6521 name: "a".repeat(256),
6522 collection: None,
6523 })),
6524 source_name: Some(format!("{}", "a".repeat(256))),
6525 target: Some(fdecl::Ref::Child(
6526 fdecl::ChildRef {
6527 name: "b".repeat(256),
6528 collection: None,
6529 }
6530 )),
6531 target_name: Some(format!("{}", "b".repeat(256))),
6532 dependency_type: Some(fdecl::DependencyType::Strong),
6533 ..Default::default()
6534 }),
6535 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6536 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6537 source_name: Some("a".to_string()),
6538 target: Some(fdecl::Ref::Collection(
6539 fdecl::CollectionRef {
6540 name: "b".repeat(256),
6541 }
6542 )),
6543 target_name: Some(format!("{}", "b".repeat(256))),
6544 dependency_type: Some(fdecl::DependencyType::Weak),
6545 ..Default::default()
6546 }),
6547 fdecl::Offer::Directory(fdecl::OfferDirectory {
6548 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6549 name: "a".repeat(256),
6550 collection: None,
6551 })),
6552 source_name: Some(format!("{}", "a".repeat(256))),
6553 target: Some(fdecl::Ref::Child(
6554 fdecl::ChildRef {
6555 name: "b".repeat(256),
6556 collection: None,
6557 }
6558 )),
6559 target_name: Some(format!("{}", "b".repeat(256))),
6560 rights: Some(fio::Operations::CONNECT),
6561 subdir: None,
6562 dependency_type: Some(fdecl::DependencyType::Strong),
6563 ..Default::default()
6564 }),
6565 fdecl::Offer::Directory(fdecl::OfferDirectory {
6566 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6567 source_name: Some("a".to_string()),
6568 target: Some(fdecl::Ref::Collection(
6569 fdecl::CollectionRef {
6570 name: "b".repeat(256),
6571 }
6572 )),
6573 target_name: Some(format!("{}", "b".repeat(256))),
6574 rights: Some(fio::Operations::CONNECT),
6575 subdir: None,
6576 dependency_type: Some(fdecl::DependencyType::Weak),
6577 ..Default::default()
6578 }),
6579 fdecl::Offer::Storage(fdecl::OfferStorage {
6580 source_name: Some("data".to_string()),
6581 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6582 target: Some(fdecl::Ref::Child(
6583 fdecl::ChildRef {
6584 name: "b".repeat(256),
6585 collection: None,
6586 }
6587 )),
6588 target_name: Some("data".to_string()),
6589 ..Default::default()
6590 }),
6591 fdecl::Offer::Storage(fdecl::OfferStorage {
6592 source_name: Some("data".to_string()),
6593 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6594 target: Some(fdecl::Ref::Collection(
6595 fdecl::CollectionRef { name: "b".repeat(256) }
6596 )),
6597 target_name: Some("data".to_string()),
6598 ..Default::default()
6599 }),
6600 fdecl::Offer::Runner(fdecl::OfferRunner {
6601 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6602 name: "a".repeat(256),
6603 collection: None,
6604 })),
6605 source_name: Some("b".repeat(256)),
6606 target: Some(fdecl::Ref::Collection(
6607 fdecl::CollectionRef {
6608 name: "c".repeat(256),
6609 }
6610 )),
6611 target_name: Some("d".repeat(256)),
6612 ..Default::default()
6613 }),
6614 fdecl::Offer::Resolver(fdecl::OfferResolver {
6615 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6616 name: "a".repeat(256),
6617 collection: None,
6618 })),
6619 source_name: Some("b".repeat(256)),
6620 target: Some(fdecl::Ref::Collection(
6621 fdecl::CollectionRef {
6622 name: "c".repeat(256),
6623 }
6624 )),
6625 target_name: Some("d".repeat(256)),
6626 ..Default::default()
6627 }),
6628 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6629 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6630 name: "a".repeat(256),
6631 collection: None,
6632 })),
6633 source_name: Some("b".repeat(256)),
6634 target: Some(fdecl::Ref::Collection(
6635 fdecl::CollectionRef {
6636 name: "c".repeat(256),
6637 }
6638 )),
6639 target_name: Some("d".repeat(256)),
6640 dependency_type: Some(fdecl::DependencyType::Strong),
6641 ..Default::default()
6642 }),
6643 ]);
6644 decl
6645 },
6646 result = Err(ErrorList::new(vec![
6647 Error::field_too_long(DeclType::OfferService, "source.child.name"),
6648 Error::field_too_long(DeclType::OfferService, "source_name"),
6649 Error::field_too_long(DeclType::OfferService, "target.child.name"),
6650 Error::field_too_long(DeclType::OfferService, "target_name"),
6651 Error::field_too_long(DeclType::OfferService, "target.collection.name"),
6652 Error::field_too_long(DeclType::OfferService, "target_name"),
6653 Error::field_too_long(DeclType::OfferProtocol, "source.child.name"),
6654 Error::field_too_long(DeclType::OfferProtocol, "source_name"),
6655 Error::field_too_long(DeclType::OfferProtocol, "target.child.name"),
6656 Error::field_too_long(DeclType::OfferProtocol, "target_name"),
6657 Error::field_too_long(DeclType::OfferProtocol, "target.collection.name"),
6658 Error::field_too_long(DeclType::OfferProtocol, "target_name"),
6659 Error::field_too_long(DeclType::OfferDirectory, "source.child.name"),
6660 Error::field_too_long(DeclType::OfferDirectory, "source_name"),
6661 Error::field_too_long(DeclType::OfferDirectory, "target.child.name"),
6662 Error::field_too_long(DeclType::OfferDirectory, "target_name"),
6663 Error::field_too_long(DeclType::OfferDirectory, "target.collection.name"),
6664 Error::field_too_long(DeclType::OfferDirectory, "target_name"),
6665 Error::field_too_long(DeclType::OfferStorage, "target.child.name"),
6666 Error::field_too_long(DeclType::OfferStorage, "target.collection.name"),
6667 Error::field_too_long(DeclType::OfferRunner, "source.child.name"),
6668 Error::field_too_long(DeclType::OfferRunner, "source_name"),
6669 Error::field_too_long(DeclType::OfferRunner, "target.collection.name"),
6670 Error::field_too_long(DeclType::OfferRunner, "target_name"),
6671 Error::field_too_long(DeclType::OfferResolver, "source.child.name"),
6672 Error::field_too_long(DeclType::OfferResolver, "source_name"),
6673 Error::field_too_long(DeclType::OfferResolver, "target.collection.name"),
6674 Error::field_too_long(DeclType::OfferResolver, "target_name"),
6675 Error::field_too_long(DeclType::OfferDictionary, "source.child.name"),
6676 Error::field_too_long(DeclType::OfferDictionary, "source_name"),
6677 Error::field_too_long(DeclType::OfferDictionary, "target.collection.name"),
6678 Error::field_too_long(DeclType::OfferDictionary, "target_name"),
6679 ])),
6680 },
6681 test_validate_offers_extraneous => {
6682 input = {
6683 let mut decl = new_component_decl();
6684 decl.offers = Some(vec![
6685 fdecl::Offer::Service(fdecl::OfferService {
6686 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6687 name: "logger".to_string(),
6688 collection: Some("modular".to_string()),
6689 })),
6690 source_name: Some("fuchsia.logger.Log".to_string()),
6691 target: Some(fdecl::Ref::Child(
6692 fdecl::ChildRef {
6693 name: "netstack".to_string(),
6694 collection: Some("modular".to_string()),
6695 }
6696 )),
6697 target_name: Some("fuchsia.logger.Log".to_string()),
6698 ..Default::default()
6699 }),
6700 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6701 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6702 name: "logger".to_string(),
6703 collection: Some("modular".to_string()),
6704 })),
6705 source_name: Some("fuchsia.logger.Log".to_string()),
6706 target: Some(fdecl::Ref::Child(
6707 fdecl::ChildRef {
6708 name: "netstack".to_string(),
6709 collection: Some("modular".to_string()),
6710 }
6711 )),
6712 target_name: Some("fuchsia.logger.Log".to_string()),
6713 dependency_type: Some(fdecl::DependencyType::Strong),
6714 ..Default::default()
6715 }),
6716 fdecl::Offer::Directory(fdecl::OfferDirectory {
6717 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6718 name: "logger".to_string(),
6719 collection: Some("modular".to_string()),
6720 })),
6721 source_name: Some("assets".to_string()),
6722 target: Some(fdecl::Ref::Child(
6723 fdecl::ChildRef {
6724 name: "netstack".to_string(),
6725 collection: Some("modular".to_string()),
6726 }
6727 )),
6728 target_name: Some("assets".to_string()),
6729 rights: Some(fio::Operations::CONNECT),
6730 subdir: None,
6731 dependency_type: Some(fdecl::DependencyType::Weak),
6732 ..Default::default()
6733 }),
6734 fdecl::Offer::Storage(fdecl::OfferStorage {
6735 source_name: Some("data".to_string()),
6736 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{ })),
6737 target: Some(fdecl::Ref::Child(
6738 fdecl::ChildRef {
6739 name: "netstack".to_string(),
6740 collection: Some("modular".to_string()),
6741 }
6742 )),
6743 target_name: Some("data".to_string()),
6744 ..Default::default()
6745 }),
6746 fdecl::Offer::Runner(fdecl::OfferRunner {
6747 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6748 name: "logger".to_string(),
6749 collection: Some("modular".to_string()),
6750 })),
6751 source_name: Some("elf".to_string()),
6752 target: Some(fdecl::Ref::Child(
6753 fdecl::ChildRef {
6754 name: "netstack".to_string(),
6755 collection: Some("modular".to_string()),
6756 }
6757 )),
6758 target_name: Some("elf".to_string()),
6759 ..Default::default()
6760 }),
6761 fdecl::Offer::Resolver(fdecl::OfferResolver {
6762 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6763 name: "logger".to_string(),
6764 collection: Some("modular".to_string()),
6765 })),
6766 source_name: Some("pkg".to_string()),
6767 target: Some(fdecl::Ref::Child(
6768 fdecl::ChildRef {
6769 name: "netstack".to_string(),
6770 collection: Some("modular".to_string()),
6771 }
6772 )),
6773 target_name: Some("pkg".to_string()),
6774 ..Default::default()
6775 }),
6776 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6777 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6778 name: "logger".to_string(),
6779 collection: Some("modular".to_string()),
6780 })),
6781 source_name: Some("dict".to_string()),
6782 target: Some(fdecl::Ref::Child(
6783 fdecl::ChildRef {
6784 name: "netstack".to_string(),
6785 collection: Some("modular".to_string()),
6786 }
6787 )),
6788 target_name: Some("dict".to_string()),
6789 dependency_type: Some(fdecl::DependencyType::Strong),
6790 ..Default::default()
6791 }),
6792 ]);
6793 decl.capabilities = Some(vec![
6794 fdecl::Capability::Protocol(fdecl::Protocol {
6795 name: Some("fuchsia.logger.Log".to_string()),
6796 source_path: Some("/svc/logger".to_string()),
6797 ..Default::default()
6798 }),
6799 fdecl::Capability::Directory(fdecl::Directory {
6800 name: Some("assets".to_string()),
6801 source_path: Some("/data/assets".to_string()),
6802 rights: Some(fio::Operations::CONNECT),
6803 ..Default::default()
6804 }),
6805 ]);
6806 decl
6807 },
6808 result = Err(ErrorList::new(vec![
6809 Error::extraneous_field(DeclType::OfferService, "source.child.collection"),
6810 Error::extraneous_field(DeclType::OfferService, "target.child.collection"),
6811 Error::extraneous_field(DeclType::OfferProtocol, "source.child.collection"),
6812 Error::extraneous_field(DeclType::OfferProtocol, "target.child.collection"),
6813 Error::extraneous_field(DeclType::OfferDirectory, "source.child.collection"),
6814 Error::extraneous_field(DeclType::OfferDirectory, "target.child.collection"),
6815 Error::extraneous_field(DeclType::OfferStorage, "target.child.collection"),
6816 Error::extraneous_field(DeclType::OfferRunner, "source.child.collection"),
6817 Error::extraneous_field(DeclType::OfferRunner, "target.child.collection"),
6818 Error::extraneous_field(DeclType::OfferResolver, "source.child.collection"),
6819 Error::extraneous_field(DeclType::OfferResolver, "target.child.collection"),
6820 Error::extraneous_field(DeclType::OfferDictionary, "source.child.collection"),
6821 Error::extraneous_field(DeclType::OfferDictionary, "target.child.collection"),
6822 ])),
6823 },
6824 test_validate_offers_invalid_filtered_service_fields => {
6825 input = {
6826 let mut decl = new_component_decl();
6827 decl.offers = Some(vec![
6828 fdecl::Offer::Service(fdecl::OfferService {
6829 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6830 source_name: Some("fuchsia.logger.Log".to_string()),
6831 target: Some(fdecl::Ref::Child(
6832 fdecl::ChildRef {
6833 name: "logger".to_string(),
6834 collection: None,
6835 }
6836 )),
6837 target_name: Some("fuchsia.logger.Log".to_string()),
6838 source_instance_filter: Some(vec![]),
6839 ..Default::default()
6840 }),
6841 fdecl::Offer::Service(fdecl::OfferService {
6842 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6843 source_name: Some("fuchsia.logger.Log".to_string()),
6844 target: Some(fdecl::Ref::Child(
6845 fdecl::ChildRef {
6846 name: "logger".to_string(),
6847 collection: None,
6848 }
6849 )),
6850 target_name: Some("fuchsia.logger.Log2".to_string()),
6851 source_instance_filter: Some(vec!["^badname".to_string()]),
6852 ..Default::default()
6853 }),
6854 fdecl::Offer::Service(fdecl::OfferService {
6855 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6856 source_name: Some("fuchsia.logger.Log".to_string()),
6857 target: Some(fdecl::Ref::Child(
6858 fdecl::ChildRef {
6859 name: "logger".to_string(),
6860 collection: None,
6861 }
6862 )),
6863 target_name: Some("fuchsia.logger.Log1".to_string()),
6864 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()}]),
6865 ..Default::default()
6866 }),
6867 fdecl::Offer::Service(fdecl::OfferService {
6868 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6869 source_name: Some("fuchsia.logger.Log".to_string()),
6870 target: Some(fdecl::Ref::Child(
6871 fdecl::ChildRef {
6872 name: "logger".to_string(),
6873 collection: None,
6874 }
6875 )),
6876 target_name: Some("fuchsia.logger.Log3".to_string()),
6877 renamed_instances: Some(vec![
6878 fdecl::NameMapping {
6879 source_name: "^badname".to_string(),
6880 target_name: "^badname".to_string(),
6881 }
6882 ]),
6883 ..Default::default()
6884 })
6885 ]);
6886 decl.children = Some(vec![
6887 fdecl::Child {
6888 name: Some("logger".to_string()),
6889 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
6890 startup: Some(fdecl::StartupMode::Lazy),
6891 on_terminate: None,
6892 environment: None,
6893 ..Default::default()
6894 },
6895 ]);
6896 decl
6897 },
6898 result = Err(ErrorList::new(vec![
6899 Error::invalid_field(DeclType::OfferService, "source_instance_filter"),
6900 Error::invalid_field(DeclType::OfferService, "source_instance_filter"),
6901 Error::invalid_field(DeclType::OfferService, "renamed_instances"),
6902 Error::invalid_field(DeclType::OfferService, "renamed_instances.source_name"),
6903 Error::invalid_field(DeclType::OfferService, "renamed_instances.target_name"),
6904 ])),
6905 },
6906 test_validate_offers_invalid_identifiers => {
6907 input = {
6908 let mut decl = new_component_decl();
6909 decl.offers = Some(vec![
6910 fdecl::Offer::Service(fdecl::OfferService {
6911 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6912 name: "^bad".to_string(),
6913 collection: None,
6914 })),
6915 source_name: Some("foo/".to_string()),
6916 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6917 name: "%bad".to_string(),
6918 collection: None,
6919 })),
6920 target_name: Some("/".to_string()),
6921 ..Default::default()
6922 }),
6923 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6924 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6925 name: "^bad".to_string(),
6926 collection: None,
6927 })),
6928 source_name: Some("foo/".to_string()),
6929 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6930 name: "%bad".to_string(),
6931 collection: None,
6932 })),
6933 target_name: Some("/".to_string()),
6934 dependency_type: Some(fdecl::DependencyType::Strong),
6935 ..Default::default()
6936 }),
6937 fdecl::Offer::Directory(fdecl::OfferDirectory {
6938 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6939 name: "^bad".to_string(),
6940 collection: None,
6941 })),
6942 source_name: Some("foo/".to_string()),
6943 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6944 name: "%bad".to_string(),
6945 collection: None,
6946 })),
6947 target_name: Some("/".to_string()),
6948 rights: Some(fio::Operations::CONNECT),
6949 subdir: Some("/foo".to_string()),
6950 dependency_type: Some(fdecl::DependencyType::Strong),
6951 ..Default::default()
6952 }),
6953 fdecl::Offer::Runner(fdecl::OfferRunner {
6954 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6955 name: "^bad".to_string(),
6956 collection: None,
6957 })),
6958 source_name: Some("/path".to_string()),
6959 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6960 name: "%bad".to_string(),
6961 collection: None,
6962 })),
6963 target_name: Some("elf!".to_string()),
6964 ..Default::default()
6965 }),
6966 fdecl::Offer::Resolver(fdecl::OfferResolver {
6967 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6968 name: "^bad".to_string(),
6969 collection: None,
6970 })),
6971 source_name: Some("/path".to_string()),
6972 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6973 name: "%bad".to_string(),
6974 collection: None,
6975 })),
6976 target_name: Some("pkg!".to_string()),
6977 ..Default::default()
6978 }),
6979 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6980 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6981 name: "^bad".to_string(),
6982 collection: None,
6983 })),
6984 source_name: Some("/path".to_string()),
6985 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6986 name: "%bad".to_string(),
6987 collection: None,
6988 })),
6989 target_name: Some("pkg!".to_string()),
6990 dependency_type: Some(fdecl::DependencyType::Strong),
6991 ..Default::default()
6992 }),
6993 ]);
6994 decl
6995 },
6996 result = Err(ErrorList::new(vec![
6997 Error::invalid_field(DeclType::OfferService, "source.child.name"),
6998 Error::invalid_field(DeclType::OfferService, "source_name"),
6999 Error::invalid_field(DeclType::OfferService, "target.child.name"),
7000 Error::invalid_field(DeclType::OfferService, "target_name"),
7001 Error::invalid_field(DeclType::OfferProtocol, "source.child.name"),
7002 Error::invalid_field(DeclType::OfferProtocol, "source_name"),
7003 Error::invalid_field(DeclType::OfferProtocol, "target.child.name"),
7004 Error::invalid_field(DeclType::OfferProtocol, "target_name"),
7005 Error::invalid_field(DeclType::OfferDirectory, "source.child.name"),
7006 Error::invalid_field(DeclType::OfferDirectory, "source_name"),
7007 Error::invalid_field(DeclType::OfferDirectory, "target.child.name"),
7008 Error::invalid_field(DeclType::OfferDirectory, "target_name"),
7009 Error::invalid_field(DeclType::OfferDirectory, "subdir"),
7010 Error::invalid_field(DeclType::OfferRunner, "source.child.name"),
7011 Error::invalid_field(DeclType::OfferRunner, "source_name"),
7012 Error::invalid_field(DeclType::OfferRunner, "target.child.name"),
7013 Error::invalid_field(DeclType::OfferRunner, "target_name"),
7014 Error::invalid_field(DeclType::OfferResolver, "source.child.name"),
7015 Error::invalid_field(DeclType::OfferResolver, "source_name"),
7016 Error::invalid_field(DeclType::OfferResolver, "target.child.name"),
7017 Error::invalid_field(DeclType::OfferResolver, "target_name"),
7018 Error::invalid_field(DeclType::OfferDictionary, "source.child.name"),
7019 Error::invalid_field(DeclType::OfferDictionary, "source_name"),
7020 Error::invalid_field(DeclType::OfferDictionary, "target.child.name"),
7021 Error::invalid_field(DeclType::OfferDictionary, "target_name"),
7022 ])),
7023 },
7024 test_validate_offers_target_equals_source => {
7025 input = {
7026 let mut decl = new_component_decl();
7027 decl.offers = Some(vec![
7028 fdecl::Offer::Service(fdecl::OfferService {
7029 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7030 name: "logger".to_string(),
7031 collection: None,
7032 })),
7033 source_name: Some("logger".to_string()),
7034 target: Some(fdecl::Ref::Child(
7035 fdecl::ChildRef {
7036 name: "logger".to_string(),
7037 collection: None,
7038 }
7039 )),
7040 target_name: Some("logger".to_string()),
7041 ..Default::default()
7042 }),
7043 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7044 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7045 name: "logger".to_string(),
7046 collection: None,
7047 })),
7048 source_name: Some("legacy_logger".to_string()),
7049 target: Some(fdecl::Ref::Child(
7050 fdecl::ChildRef {
7051 name: "logger".to_string(),
7052 collection: None,
7053 }
7054 )),
7055 target_name: Some("weak_legacy_logger".to_string()),
7056 dependency_type: Some(fdecl::DependencyType::Weak),
7057 ..Default::default()
7058 }),
7059 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7060 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7061 name: "logger".to_string(),
7062 collection: None,
7063 })),
7064 source_name: Some("legacy_logger".to_string()),
7065 target: Some(fdecl::Ref::Child(
7066 fdecl::ChildRef {
7067 name: "logger".to_string(),
7068 collection: None,
7069 }
7070 )),
7071 target_name: Some("strong_legacy_logger".to_string()),
7072 dependency_type: Some(fdecl::DependencyType::Strong),
7073 ..Default::default()
7074 }),
7075 fdecl::Offer::Directory(fdecl::OfferDirectory {
7076 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7077 name: "logger".to_string(),
7078 collection: None,
7079 })),
7080 source_name: Some("assets".to_string()),
7081 target: Some(fdecl::Ref::Child(
7082 fdecl::ChildRef {
7083 name: "logger".to_string(),
7084 collection: None,
7085 }
7086 )),
7087 target_name: Some("assets".to_string()),
7088 rights: Some(fio::Operations::CONNECT),
7089 subdir: None,
7090 dependency_type: Some(fdecl::DependencyType::Strong),
7091 ..Default::default()
7092 }),
7093 fdecl::Offer::Runner(fdecl::OfferRunner {
7094 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7095 name: "logger".to_string(),
7096 collection: None,
7097 })),
7098 source_name: Some("web".to_string()),
7099 target: Some(fdecl::Ref::Child(
7100 fdecl::ChildRef {
7101 name: "logger".to_string(),
7102 collection: None,
7103 }
7104 )),
7105 target_name: Some("web".to_string()),
7106 ..Default::default()
7107 }),
7108 fdecl::Offer::Resolver(fdecl::OfferResolver {
7109 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7110 name: "logger".to_string(),
7111 collection: None,
7112 })),
7113 source_name: Some("pkg".to_string()),
7114 target: Some(fdecl::Ref::Child(
7115 fdecl::ChildRef {
7116 name: "logger".to_string(),
7117 collection: None,
7118 }
7119 )),
7120 target_name: Some("pkg".to_string()),
7121 ..Default::default()
7122 }),
7123 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7124 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7125 name: "logger".to_string(),
7126 collection: None,
7127 })),
7128 source_name: Some("dict".to_string()),
7129 target: Some(fdecl::Ref::Child(
7130 fdecl::ChildRef {
7131 name: "logger".to_string(),
7132 collection: None,
7133 }
7134 )),
7135 target_name: Some("dict".to_string()),
7136 dependency_type: Some(fdecl::DependencyType::Strong),
7137 ..Default::default()
7138 }),
7139 ]);
7140 decl.children = Some(vec![fdecl::Child{
7141 name: Some("logger".to_string()),
7142 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
7143 startup: Some(fdecl::StartupMode::Lazy),
7144 on_terminate: None,
7145 environment: None,
7146 ..Default::default()
7147 }]);
7148 decl
7149 },
7150 result = Err(ErrorList::new(vec![
7151 Error::dependency_cycle("{{child logger -> child logger}}"),
7152 ])),
7153 },
7154 test_validate_offers_storage_target_equals_source => {
7155 input = fdecl::Component {
7156 offers: Some(vec![
7157 fdecl::Offer::Storage(fdecl::OfferStorage {
7158 source_name: Some("data".to_string()),
7159 source: Some(fdecl::Ref::Self_(fdecl::SelfRef { })),
7160 target: Some(fdecl::Ref::Child(
7161 fdecl::ChildRef {
7162 name: "logger".to_string(),
7163 collection: None,
7164 }
7165 )),
7166 target_name: Some("data".to_string()),
7167 ..Default::default()
7168 })
7169 ]),
7170 capabilities: Some(vec![
7171 fdecl::Capability::Storage(fdecl::Storage {
7172 name: Some("data".to_string()),
7173 backing_dir: Some("minfs".to_string()),
7174 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7175 name: "logger".to_string(),
7176 collection: None,
7177 })),
7178 subdir: None,
7179 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
7180 ..Default::default()
7181 }),
7182 ]),
7183 children: Some(vec![
7184 fdecl::Child {
7185 name: Some("logger".to_string()),
7186 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
7187 startup: Some(fdecl::StartupMode::Lazy),
7188 on_terminate: None,
7189 environment: None,
7190 ..Default::default()
7191 },
7192 ]),
7193 ..new_component_decl()
7194 },
7195 result = Err(ErrorList::new(vec![
7196 Error::dependency_cycle("{{child logger -> capability data -> child logger}}"),
7197 ])),
7198 },
7199 test_validate_offers_invalid_child => {
7200 input = {
7201 let mut decl = new_component_decl();
7202 decl.offers = Some(vec![
7203 fdecl::Offer::Service(fdecl::OfferService {
7204 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7205 name: "logger".to_string(),
7206 collection: None,
7207 })),
7208 source_name: Some("fuchsia.logger.Log".to_string()),
7209 target: Some(fdecl::Ref::Child(
7210 fdecl::ChildRef {
7211 name: "netstack".to_string(),
7212 collection: None,
7213 }
7214 )),
7215 target_name: Some("fuchsia.logger.Log".to_string()),
7216 ..Default::default()
7217 }),
7218 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7219 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7220 name: "logger".to_string(),
7221 collection: None,
7222 })),
7223 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
7224 target: Some(fdecl::Ref::Child(
7225 fdecl::ChildRef {
7226 name: "netstack".to_string(),
7227 collection: None,
7228 }
7229 )),
7230 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7231 dependency_type: Some(fdecl::DependencyType::Strong),
7232 ..Default::default()
7233 }),
7234 fdecl::Offer::Directory(fdecl::OfferDirectory {
7235 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7236 name: "logger".to_string(),
7237 collection: None,
7238 })),
7239 source_name: Some("assets".to_string()),
7240 target: Some(fdecl::Ref::Collection(
7241 fdecl::CollectionRef { name: "modular".to_string() }
7242 )),
7243 target_name: Some("assets".to_string()),
7244 rights: Some(fio::Operations::CONNECT),
7245 subdir: None,
7246 dependency_type: Some(fdecl::DependencyType::Weak),
7247 ..Default::default()
7248 }),
7249 ]);
7250 decl.capabilities = Some(vec![
7251 fdecl::Capability::Storage(fdecl::Storage {
7252 name: Some("memfs".to_string()),
7253 backing_dir: Some("memfs".to_string()),
7254 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7255 name: "logger".to_string(),
7256 collection: None,
7257 })),
7258 subdir: None,
7259 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
7260 ..Default::default()
7261 }),
7262 ]);
7263 decl.children = Some(vec![
7264 fdecl::Child {
7265 name: Some("netstack".to_string()),
7266 url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
7267 startup: Some(fdecl::StartupMode::Lazy),
7268 on_terminate: None,
7269 environment: None,
7270 ..Default::default()
7271 },
7272 ]);
7273 decl.collections = Some(vec![
7274 fdecl::Collection {
7275 name: Some("modular".to_string()),
7276 durability: Some(fdecl::Durability::Transient),
7277 environment: None,
7278 allowed_offers: Some(fdecl::AllowedOffers::StaticAndDynamic),
7279 allow_long_names: None,
7280 ..Default::default()
7281 },
7282 ]);
7283 decl
7284 },
7285 result = Err(ErrorList::new(vec![
7286 Error::invalid_child(DeclType::Storage, "source", "logger"),
7287 Error::invalid_child(DeclType::OfferService, "source", "logger"),
7288 Error::invalid_child(DeclType::OfferProtocol, "source", "logger"),
7289 Error::invalid_child(DeclType::OfferDirectory, "source", "logger"),
7290 ])),
7291 },
7292 test_validate_offers_invalid_source_capability => {
7293 input = {
7294 fdecl::Component {
7295 offers: Some(vec![
7296 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7297 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
7298 name: "this-storage-doesnt-exist".to_string(),
7299 })),
7300 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
7301 target: Some(fdecl::Ref::Child(
7302 fdecl::ChildRef {
7303 name: "netstack".to_string(),
7304 collection: None,
7305 }
7306 )),
7307 target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
7308 dependency_type: Some(fdecl::DependencyType::Strong),
7309 ..Default::default()
7310 }),
7311 ]),
7312 ..new_component_decl()
7313 }
7314 },
7315 result = Err(ErrorList::new(vec![
7316 Error::invalid_capability(DeclType::OfferProtocol, "source", "this-storage-doesnt-exist"),
7317 Error::invalid_child(DeclType::OfferProtocol, "target", "netstack"),
7318 ])),
7319 },
7320 test_validate_offers_target => {
7321 input = {
7322 let mut decl = new_component_decl();
7323 decl.offers = Some(vec![
7324 fdecl::Offer::Service(fdecl::OfferService {
7325 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
7326 name: "modular".into()
7327 })),
7328 source_name: Some("logger".to_string()),
7329 target: Some(fdecl::Ref::Child(
7330 fdecl::ChildRef {
7331 name: "netstack".to_string(),
7332 collection: None,
7333 }
7334 )),
7335 target_name: Some("fuchsia.logger.Log".to_string()),
7336 ..Default::default()
7337 }),
7338 fdecl::Offer::Service(fdecl::OfferService {
7339 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
7340 name: "modular".into()
7341 })),
7342 source_name: Some("logger".to_string()),
7343 target: Some(fdecl::Ref::Child(
7344 fdecl::ChildRef {
7345 name: "netstack".to_string(),
7346 collection: None,
7347 }
7348 )),
7349 target_name: Some("fuchsia.logger.Log".to_string()),
7350 ..Default::default()
7351 }),
7352 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7353 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7354 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
7355 target: Some(fdecl::Ref::Child(
7356 fdecl::ChildRef {
7357 name: "netstack".to_string(),
7358 collection: None,
7359 }
7360 )),
7361 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7362 dependency_type: Some(fdecl::DependencyType::Strong),
7363 ..Default::default()
7364 }),
7365 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7366 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7367 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
7368 target: Some(fdecl::Ref::Child(
7369 fdecl::ChildRef {
7370 name: "netstack".to_string(),
7371 collection: None,
7372 }
7373 )),
7374 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7375 dependency_type: Some(fdecl::DependencyType::Strong),
7376 ..Default::default()
7377 }),
7378 fdecl::Offer::Directory(fdecl::OfferDirectory {
7379 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7380 source_name: Some("assets".to_string()),
7381 target: Some(fdecl::Ref::Collection(
7382 fdecl::CollectionRef { name: "modular".to_string() }
7383 )),
7384 target_name: Some("assets".to_string()),
7385 rights: Some(fio::Operations::CONNECT),
7386 subdir: None,
7387 dependency_type: Some(fdecl::DependencyType::Strong),
7388 ..Default::default()
7389 }),
7390 fdecl::Offer::Directory(fdecl::OfferDirectory {
7391 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7392 source_name: Some("assets".to_string()),
7393 target: Some(fdecl::Ref::Collection(
7394 fdecl::CollectionRef { name: "modular".to_string() }
7395 )),
7396 target_name: Some("assets".to_string()),
7397 rights: Some(fio::Operations::CONNECT),
7398 subdir: None,
7399 dependency_type: Some(fdecl::DependencyType::Weak),
7400 ..Default::default()
7401 }),
7402 fdecl::Offer::Storage(fdecl::OfferStorage {
7403 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7404 source_name: Some("data".to_string()),
7405 target: Some(fdecl::Ref::Collection(
7406 fdecl::CollectionRef { name: "modular".to_string() }
7407 )),
7408 target_name: Some("data".to_string()),
7409 ..Default::default()
7410 }),
7411 fdecl::Offer::Storage(fdecl::OfferStorage {
7412 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7413 source_name: Some("data".to_string()),
7414 target: Some(fdecl::Ref::Collection(
7415 fdecl::CollectionRef { name: "modular".to_string() }
7416 )),
7417 target_name: Some("data".to_string()),
7418 ..Default::default()
7419 }),
7420 fdecl::Offer::Runner(fdecl::OfferRunner {
7421 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7422 source_name: Some("elf".to_string()),
7423 target: Some(fdecl::Ref::Collection(
7424 fdecl::CollectionRef { name: "modular".to_string() }
7425 )),
7426 target_name: Some("duplicated".to_string()),
7427 ..Default::default()
7428 }),
7429 fdecl::Offer::Runner(fdecl::OfferRunner {
7430 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7431 source_name: Some("elf".to_string()),
7432 target: Some(fdecl::Ref::Collection(
7433 fdecl::CollectionRef { name: "modular".to_string() }
7434 )),
7435 target_name: Some("duplicated".to_string()),
7436 ..Default::default()
7437 }),
7438 fdecl::Offer::Resolver(fdecl::OfferResolver {
7439 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7440 source_name: Some("pkg".to_string()),
7441 target: Some(fdecl::Ref::Collection(
7442 fdecl::CollectionRef { name: "modular".to_string() }
7443 )),
7444 target_name: Some("duplicated".to_string()),
7445 ..Default::default()
7446 }),
7447 fdecl::Offer::EventStream(fdecl::OfferEventStream {
7448 source_name: Some("started".to_string()),
7449 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7450 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "netstack".to_string(), collection: None})),
7451 target_name: Some("started".to_string()),
7452 ..Default::default()
7453 }),
7454 fdecl::Offer::EventStream(fdecl::OfferEventStream {
7455 source_name: Some("started".to_string()),
7456 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7457 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "netstack".to_string(), collection: None})),
7458 target_name: Some("started".to_string()),
7459 ..Default::default()
7460 }),
7461 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7462 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7463 source_name: Some("a".to_string()),
7464 target: Some(fdecl::Ref::Collection(
7465 fdecl::CollectionRef { name: "modular".to_string() }
7466 )),
7467 target_name: Some("dict".to_string()),
7468 dependency_type: Some(fdecl::DependencyType::Strong),
7469 ..Default::default()
7470 }),
7471 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7472 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7473 source_name: Some("b".to_string()),
7474 target: Some(fdecl::Ref::Collection(
7475 fdecl::CollectionRef { name: "modular".to_string() }
7476 )),
7477 target_name: Some("dict".to_string()),
7478 dependency_type: Some(fdecl::DependencyType::Strong),
7479 ..Default::default()
7480 }),
7481 ]);
7482 decl.children = Some(vec![
7483 fdecl::Child{
7484 name: Some("netstack".to_string()),
7485 url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
7486 startup: Some(fdecl::StartupMode::Eager),
7487 on_terminate: None,
7488 environment: None,
7489 ..Default::default()
7490 },
7491 ]);
7492 decl.collections = Some(vec![
7493 fdecl::Collection{
7494 name: Some("modular".to_string()),
7495 durability: Some(fdecl::Durability::Transient),
7496 environment: None,
7497 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7498 allow_long_names: None,
7499 ..Default::default()
7500 },
7501 ]);
7502 decl
7503 },
7504 result = Err(ErrorList::new(vec![
7505 Error::duplicate_field(DeclType::OfferProtocol, "target_name", "fuchsia.logger.LegacyLog"),
7507 Error::duplicate_field(DeclType::OfferDirectory, "target_name", "assets"),
7508 Error::duplicate_field(DeclType::OfferStorage, "target_name", "data"),
7509 Error::duplicate_field(DeclType::OfferRunner, "target_name", "duplicated"),
7510 Error::duplicate_field(DeclType::OfferResolver, "target_name", "duplicated"),
7511 Error::duplicate_field(DeclType::OfferEventStream, "target_name", "started"),
7512 Error::duplicate_field(DeclType::OfferDictionary, "target_name", "dict"),
7513 ])),
7514 },
7515 test_validate_offers_target_invalid => {
7516 input = {
7517 let mut decl = new_component_decl();
7518 decl.offers = Some(vec![
7519 fdecl::Offer::Service(fdecl::OfferService {
7520 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7521 source_name: Some("logger".to_string()),
7522 target: Some(fdecl::Ref::Child(
7523 fdecl::ChildRef {
7524 name: "netstack".to_string(),
7525 collection: None,
7526 }
7527 )),
7528 target_name: Some("fuchsia.logger.Log".to_string()),
7529 ..Default::default()
7530 }),
7531 fdecl::Offer::Service(fdecl::OfferService {
7532 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7533 source_name: Some("logger".to_string()),
7534 target: Some(fdecl::Ref::Collection(
7535 fdecl::CollectionRef { name: "modular".to_string(), }
7536 )),
7537 target_name: Some("fuchsia.logger.Log".to_string()),
7538 ..Default::default()
7539 }),
7540 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7541 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7542 source_name: Some("legacy_logger".to_string()),
7543 target: Some(fdecl::Ref::Child(
7544 fdecl::ChildRef {
7545 name: "netstack".to_string(),
7546 collection: None,
7547 }
7548 )),
7549 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7550 dependency_type: Some(fdecl::DependencyType::Weak),
7551 ..Default::default()
7552 }),
7553 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7554 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7555 source_name: Some("legacy_logger".to_string()),
7556 target: Some(fdecl::Ref::Collection(
7557 fdecl::CollectionRef { name: "modular".to_string(), }
7558 )),
7559 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7560 dependency_type: Some(fdecl::DependencyType::Strong),
7561 ..Default::default()
7562 }),
7563 fdecl::Offer::Directory(fdecl::OfferDirectory {
7564 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7565 source_name: Some("assets".to_string()),
7566 target: Some(fdecl::Ref::Child(
7567 fdecl::ChildRef {
7568 name: "netstack".to_string(),
7569 collection: None,
7570 }
7571 )),
7572 target_name: Some("data".to_string()),
7573 rights: Some(fio::Operations::CONNECT),
7574 subdir: None,
7575 dependency_type: Some(fdecl::DependencyType::Strong),
7576 ..Default::default()
7577 }),
7578 fdecl::Offer::Directory(fdecl::OfferDirectory {
7579 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7580 source_name: Some("assets".to_string()),
7581 target: Some(fdecl::Ref::Collection(
7582 fdecl::CollectionRef { name: "modular".to_string(), }
7583 )),
7584 target_name: Some("data".to_string()),
7585 rights: Some(fio::Operations::CONNECT),
7586 subdir: None,
7587 dependency_type: Some(fdecl::DependencyType::Weak),
7588 ..Default::default()
7589 }),
7590 fdecl::Offer::Storage(fdecl::OfferStorage {
7591 source_name: Some("data".to_string()),
7592 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7593 target: Some(fdecl::Ref::Child(
7594 fdecl::ChildRef {
7595 name: "netstack".to_string(),
7596 collection: None,
7597 }
7598 )),
7599 target_name: Some("data".to_string()),
7600 ..Default::default()
7601 }),
7602 fdecl::Offer::Storage(fdecl::OfferStorage {
7603 source_name: Some("data".to_string()),
7604 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7605 target: Some(fdecl::Ref::Collection(
7606 fdecl::CollectionRef { name: "modular".to_string(), }
7607 )),
7608 target_name: Some("data".to_string()),
7609 ..Default::default()
7610 }),
7611 fdecl::Offer::Runner(fdecl::OfferRunner {
7612 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7613 source_name: Some("elf".to_string()),
7614 target: Some(fdecl::Ref::Child(
7615 fdecl::ChildRef {
7616 name: "netstack".to_string(),
7617 collection: None,
7618 }
7619 )),
7620 target_name: Some("elf".to_string()),
7621 ..Default::default()
7622 }),
7623 fdecl::Offer::Runner(fdecl::OfferRunner {
7624 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7625 source_name: Some("elf".to_string()),
7626 target: Some(fdecl::Ref::Collection(
7627 fdecl::CollectionRef { name: "modular".to_string(), }
7628 )),
7629 target_name: Some("elf".to_string()),
7630 ..Default::default()
7631 }),
7632 fdecl::Offer::Resolver(fdecl::OfferResolver {
7633 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7634 source_name: Some("pkg".to_string()),
7635 target: Some(fdecl::Ref::Child(
7636 fdecl::ChildRef {
7637 name: "netstack".to_string(),
7638 collection: None,
7639 }
7640 )),
7641 target_name: Some("pkg".to_string()),
7642 ..Default::default()
7643 }),
7644 fdecl::Offer::Resolver(fdecl::OfferResolver {
7645 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7646 source_name: Some("pkg".to_string()),
7647 target: Some(fdecl::Ref::Collection(
7648 fdecl::CollectionRef { name: "modular".to_string(), }
7649 )),
7650 target_name: Some("pkg".to_string()),
7651 ..Default::default()
7652 }),
7653 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7654 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7655 source_name: Some("pkg".to_string()),
7656 target: Some(fdecl::Ref::Child(
7657 fdecl::ChildRef {
7658 name: "netstack".to_string(),
7659 collection: None,
7660 }
7661 )),
7662 target_name: Some("pkg".to_string()),
7663 dependency_type: Some(fdecl::DependencyType::Strong),
7664 ..Default::default()
7665 }),
7666 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7667 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7668 source_name: Some("pkg".to_string()),
7669 target: Some(fdecl::Ref::Collection(
7670 fdecl::CollectionRef { name: "modular".to_string(), }
7671 )),
7672 target_name: Some("pkg".to_string()),
7673 dependency_type: Some(fdecl::DependencyType::Strong),
7674 ..Default::default()
7675 }),
7676 ]);
7677 decl
7678 },
7679 result = Err(ErrorList::new(vec![
7680 Error::invalid_child(DeclType::OfferService, "target", "netstack"),
7681 Error::invalid_collection(DeclType::OfferService, "target", "modular"),
7682 Error::invalid_child(DeclType::OfferProtocol, "target", "netstack"),
7683 Error::invalid_collection(DeclType::OfferProtocol, "target", "modular"),
7684 Error::invalid_child(DeclType::OfferDirectory, "target", "netstack"),
7685 Error::invalid_collection(DeclType::OfferDirectory, "target", "modular"),
7686 Error::invalid_child(DeclType::OfferStorage, "target", "netstack"),
7687 Error::invalid_collection(DeclType::OfferStorage, "target", "modular"),
7688 Error::invalid_child(DeclType::OfferRunner, "target", "netstack"),
7689 Error::invalid_collection(DeclType::OfferRunner, "target", "modular"),
7690 Error::invalid_child(DeclType::OfferResolver, "target", "netstack"),
7691 Error::invalid_collection(DeclType::OfferResolver, "target", "modular"),
7692 Error::invalid_child(DeclType::OfferDictionary, "target", "netstack"),
7693 Error::invalid_collection(DeclType::OfferDictionary, "target", "modular"),
7694 ])),
7695 },
7696 test_validate_offers_target_dictionary => {
7697 input = fdecl::Component {
7698 offers: Some(vec![
7699 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7701 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7702 source_name: Some("p".to_string()),
7703 target: Some(fdecl::Ref::Capability(
7704 fdecl::CapabilityRef {
7705 name: "dict".into(),
7706 },
7707 )),
7708 target_name: Some("p".into()),
7709 dependency_type: Some(fdecl::DependencyType::Strong),
7710 ..Default::default()
7711 }),
7712 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7714 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7715 source_name: Some("p".to_string()),
7716 target: Some(fdecl::Ref::Capability(
7717 fdecl::CapabilityRef {
7718 name: "dynamic".into(),
7719 },
7720 )),
7721 target_name: Some("p".into()),
7722 dependency_type: Some(fdecl::DependencyType::Strong),
7723 ..Default::default()
7724 }),
7725 ]),
7726 capabilities: Some(vec![
7727 fdecl::Capability::Dictionary(fdecl::Dictionary {
7728 name: Some("dict".into()),
7729 ..Default::default()
7730 }),
7731 fdecl::Capability::Dictionary(fdecl::Dictionary {
7732 name: Some("dynamic".into()),
7733 source_path: Some("/out/dir".into()),
7734 ..Default::default()
7735 }),
7736 ]),
7737 ..Default::default()
7738 },
7739 result = Err(ErrorList::new(vec![
7740 Error::invalid_field(DeclType::OfferProtocol, "target"),
7741 ])),
7742 },
7743 test_validate_offers_invalid_source_collection => {
7744 input = {
7745 let mut decl = new_component_decl();
7746 decl.collections = Some(vec![
7747 fdecl::Collection {
7748 name: Some("col".to_string()),
7749 durability: Some(fdecl::Durability::Transient),
7750 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7751 allow_long_names: None,
7752 ..Default::default()
7753 }
7754 ]);
7755 decl.children = Some(vec![
7756 fdecl::Child {
7757 name: Some("child".to_string()),
7758 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7759 startup: Some(fdecl::StartupMode::Lazy),
7760 on_terminate: None,
7761 ..Default::default()
7762 }
7763 ]);
7764 decl.offers = Some(vec![
7765 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7766 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7767 source_name: Some("a".to_string()),
7768 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7769 target_name: Some("a".to_string()),
7770 dependency_type: Some(fdecl::DependencyType::Strong),
7771 ..Default::default()
7772 }),
7773 fdecl::Offer::Directory(fdecl::OfferDirectory {
7774 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7775 source_name: Some("b".to_string()),
7776 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7777 target_name: Some("b".to_string()),
7778 rights: Some(fio::Operations::CONNECT),
7779 subdir: None,
7780 dependency_type: Some(fdecl::DependencyType::Strong),
7781 ..Default::default()
7782 }),
7783 fdecl::Offer::Storage(fdecl::OfferStorage {
7784 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7785 source_name: Some("c".to_string()),
7786 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7787 target_name: Some("c".to_string()),
7788 ..Default::default()
7789 }),
7790 fdecl::Offer::Runner(fdecl::OfferRunner {
7791 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7792 source_name: Some("d".to_string()),
7793 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7794 target_name: Some("d".to_string()),
7795 ..Default::default()
7796 }),
7797 fdecl::Offer::Resolver(fdecl::OfferResolver {
7798 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7799 source_name: Some("e".to_string()),
7800 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7801 target_name: Some("e".to_string()),
7802 ..Default::default()
7803 }),
7804 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7805 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7806 source_name: Some("f".to_string()),
7807 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7808 target_name: Some("f".to_string()),
7809 dependency_type: Some(fdecl::DependencyType::Strong),
7810 ..Default::default()
7811 }),
7812 ]);
7813 decl
7814 },
7815 result = Err(ErrorList::new(vec![
7816 Error::invalid_field(DeclType::OfferProtocol, "source"),
7817 Error::invalid_field(DeclType::OfferDirectory, "source"),
7818 Error::invalid_field(DeclType::OfferStorage, "source"),
7819 Error::invalid_field(DeclType::OfferRunner, "source"),
7820 Error::invalid_field(DeclType::OfferResolver, "source"),
7821 Error::invalid_field(DeclType::OfferDictionary, "source"),
7822 ])),
7823 },
7824 test_validate_offers_source_collection => {
7825 input = {
7826 let mut decl = new_component_decl();
7827 decl.collections = Some(vec![
7828 fdecl::Collection {
7829 name: Some("col".to_string()),
7830 durability: Some(fdecl::Durability::Transient),
7831 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7832 allow_long_names: None,
7833 ..Default::default()
7834 }
7835 ]);
7836 decl.children = Some(vec![
7837 fdecl::Child {
7838 name: Some("child".to_string()),
7839 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7840 startup: Some(fdecl::StartupMode::Lazy),
7841 on_terminate: None,
7842 ..Default::default()
7843 }
7844 ]);
7845 decl.offers = Some(vec![
7846 fdecl::Offer::Service(fdecl::OfferService {
7847 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7848 source_name: Some("a".to_string()),
7849 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7850 target_name: Some("a".to_string()),
7851 ..Default::default()
7852 })
7853 ]);
7854 decl
7855 },
7856 result = Ok(()),
7857 },
7858 test_validate_offers_invalid_capability_from_self => {
7859 input = {
7860 let mut decl = new_component_decl();
7861 decl.children = Some(vec![
7862 fdecl::Child {
7863 name: Some("child".to_string()),
7864 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7865 startup: Some(fdecl::StartupMode::Lazy),
7866 ..Default::default()
7867 }
7868 ]);
7869 decl.offers = Some(vec![
7870 fdecl::Offer::Service(fdecl::OfferService {
7871 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7872 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7873 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7874 name: "child".into(),
7875 collection: None
7876 })),
7877 target_name: Some("foo".into()),
7878 ..Default::default()
7879 }),
7880 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7881 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7882 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7883 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7884 name: "child".into(),
7885 collection: None
7886 })),
7887 target_name: Some("bar".into()),
7888 dependency_type: Some(fdecl::DependencyType::Strong),
7889 ..Default::default()
7890 }),
7891 fdecl::Offer::Directory(fdecl::OfferDirectory {
7892 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7893 source_name: Some("dir".into()),
7894 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7895 name: "child".into(),
7896 collection: None
7897 })),
7898 target_name: Some("assets".into()),
7899 dependency_type: Some(fdecl::DependencyType::Strong),
7900 ..Default::default()
7901 }),
7902 fdecl::Offer::Runner(fdecl::OfferRunner {
7903 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7904 source_name: Some("source_elf".into()),
7905 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7906 name: "child".into(),
7907 collection: None
7908 })),
7909 target_name: Some("elf".into()),
7910 ..Default::default()
7911 }),
7912 fdecl::Offer::Resolver(fdecl::OfferResolver {
7913 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7914 source_name: Some("source_pkg".into()),
7915 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7916 name: "child".into(),
7917 collection: None
7918 })),
7919 target_name: Some("pkg".into()),
7920 ..Default::default()
7921 }),
7922 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7923 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7924 source_name: Some("source_dict".into()),
7925 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7926 name: "child".into(),
7927 collection: None
7928 })),
7929 target_name: Some("dict".into()),
7930 dependency_type: Some(fdecl::DependencyType::Strong),
7931 ..Default::default()
7932 }),
7933 fdecl::Offer::Storage(fdecl::OfferStorage {
7934 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7935 source_name: Some("source_storage".into()),
7936 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7937 name: "child".into(),
7938 collection: None
7939 })),
7940 target_name: Some("storage".into()),
7941 ..Default::default()
7942 }),
7943 fdecl::Offer::Config(fdecl::OfferConfiguration {
7944 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7945 source_name: Some("source_config".into()),
7946 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7947 name: "child".into(),
7948 collection: None
7949 })),
7950 target_name: Some("config".into()),
7951 ..Default::default()
7952 }),
7953 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7954 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7955 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7956 source_dictionary: Some("dict/inner".into()),
7957 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7958 name: "child".into(),
7959 collection: None
7960 })),
7961 target_name: Some("baz".into()),
7962 dependency_type: Some(fdecl::DependencyType::Strong),
7963 ..Default::default()
7964 }),
7965 ]);
7966 decl
7967 },
7968 result = Err(ErrorList::new(vec![
7969 Error::invalid_capability(
7970 DeclType::OfferService,
7971 "source",
7972 "fuchsia.some.library.SomeProtocol"),
7973 Error::invalid_capability(
7974 DeclType::OfferProtocol,
7975 "source",
7976 "fuchsia.some.library.SomeProtocol"),
7977 Error::invalid_capability(DeclType::OfferDirectory, "source", "dir"),
7978 Error::invalid_capability(DeclType::OfferRunner, "source", "source_elf"),
7979 Error::invalid_capability(DeclType::OfferResolver, "source", "source_pkg"),
7980 Error::invalid_capability(DeclType::OfferDictionary, "source", "source_dict"),
7981 Error::invalid_capability(DeclType::OfferStorage, "source", "source_storage"),
7982 Error::invalid_capability(DeclType::OfferConfig, "source", "source_config"),
7983 Error::invalid_capability(DeclType::OfferProtocol, "source", "dict"),
7984 ])),
7985 },
7986 test_validate_offers_long_dependency_cycle => {
7987 input = {
7988 let mut decl = new_component_decl();
7989 let dependencies = vec![
7990 ("d", "b"),
7991 ("a", "b"),
7992 ("b", "c"),
7993 ("b", "d"),
7994 ("c", "a"),
7995 ];
7996 let offers = dependencies.into_iter().map(|(from,to)|
7997 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7998 source: Some(fdecl::Ref::Child(
7999 fdecl::ChildRef { name: from.to_string(), collection: None },
8000 )),
8001 source_name: Some(format!("thing_{}", from)),
8002 target: Some(fdecl::Ref::Child(
8003 fdecl::ChildRef { name: to.to_string(), collection: None },
8004 )),
8005 target_name: Some(format!("thing_{}", from)),
8006 dependency_type: Some(fdecl::DependencyType::Strong),
8007 ..Default::default()
8008 })).collect();
8009 let children = ["a", "b", "c", "d"].iter().map(|name| {
8010 fdecl::Child {
8011 name: Some(name.to_string()),
8012 url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
8013 startup: Some(fdecl::StartupMode::Lazy),
8014 on_terminate: None,
8015 environment: None,
8016 ..Default::default()
8017 }
8018 }).collect();
8019 decl.offers = Some(offers);
8020 decl.children = Some(children);
8021 decl
8022 },
8023 result = Err(ErrorList::new(vec![
8024 Error::dependency_cycle("{{child a -> child b -> child c -> child a}, {child b -> child d -> child b}}")
8025 ])),
8026 },
8027 test_validate_offers_not_required_invalid_source_service => {
8028 input = {
8029 let mut decl = generate_offer_different_source_and_availability_decl(
8030 |source, availability, target_name|
8031 fdecl::Offer::Service(fdecl::OfferService {
8032 source: Some(source),
8033 source_name: Some("fuchsia.examples.Echo".to_string()),
8034 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8035 name: "sink".to_string(),
8036 collection: None,
8037 })),
8038 target_name: Some(target_name.into()),
8039 availability: Some(availability),
8040 ..Default::default()
8041 })
8042 );
8043 decl.capabilities = Some(vec![
8044 fdecl::Capability::Service(fdecl::Service {
8045 name: Some("fuchsia.examples.Echo".to_string()),
8046 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
8047 ..Default::default()
8048 }),
8049 ]);
8050 decl
8051 },
8052 result = {
8053 Err(ErrorList::new(vec![
8054 Error::availability_must_be_optional(
8055 DeclType::OfferService,
8056 "availability",
8057 Some(&"fuchsia.examples.Echo".to_string()),
8058 ),
8059 Error::availability_must_be_optional(
8060 DeclType::OfferService,
8061 "availability",
8062 Some(&"fuchsia.examples.Echo".to_string()),
8063 ),
8064 ]))
8065 },
8066 },
8067 test_validate_offers_not_required_invalid_source_protocol => {
8068 input = {
8069 let mut decl = generate_offer_different_source_and_availability_decl(
8070 |source, availability, target_name|
8071 fdecl::Offer::Protocol(fdecl::OfferProtocol {
8072 source: Some(source),
8073 source_name: Some("fuchsia.examples.Echo".to_string()),
8074 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8075 name: "sink".to_string(),
8076 collection: None,
8077 })),
8078 target_name: Some(target_name.into()),
8079 dependency_type: Some(fdecl::DependencyType::Strong),
8080 availability: Some(availability),
8081 ..Default::default()
8082 })
8083 );
8084 decl.capabilities = Some(vec![
8085 fdecl::Capability::Protocol(fdecl::Protocol {
8086 name: Some("fuchsia.examples.Echo".to_string()),
8087 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
8088 ..Default::default()
8089 }),
8090 ]);
8091 decl
8092 },
8093 result = {
8094 Err(ErrorList::new(vec![
8095 Error::availability_must_be_optional(
8096 DeclType::OfferProtocol,
8097 "availability",
8098 Some(&"fuchsia.examples.Echo".to_string()),
8099 ),
8100 Error::availability_must_be_optional(
8101 DeclType::OfferProtocol,
8102 "availability",
8103 Some(&"fuchsia.examples.Echo".to_string()),
8104 ),
8105 ]))
8106 },
8107 },
8108 test_validate_offers_not_required_invalid_source_directory => {
8109 input = {
8110 let mut decl = generate_offer_different_source_and_availability_decl(
8111 |source, availability, target_name|
8112 fdecl::Offer::Directory(fdecl::OfferDirectory {
8113 source: Some(source),
8114 source_name: Some("assets".to_string()),
8115 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8116 name: "sink".to_string(),
8117 collection: None,
8118 })),
8119 target_name: Some(target_name.into()),
8120 rights: Some(fio::Operations::CONNECT),
8121 subdir: None,
8122 dependency_type: Some(fdecl::DependencyType::Weak),
8123 availability: Some(availability),
8124 ..Default::default()
8125 })
8126 );
8127 decl.capabilities = Some(vec![
8128 fdecl::Capability::Directory(fdecl::Directory {
8129 name: Some("assets".to_string()),
8130 source_path: Some("/assets".to_string()),
8131 rights: Some(fio::Operations::CONNECT),
8132 ..Default::default()
8133 }),
8134 ]);
8135 decl
8136 },
8137 result = {
8138 Err(ErrorList::new(vec![
8139 Error::availability_must_be_optional(
8140 DeclType::OfferDirectory,
8141 "availability",
8142 Some(&"assets".to_string()),
8143 ),
8144 Error::availability_must_be_optional(
8145 DeclType::OfferDirectory,
8146 "availability",
8147 Some(&"assets".to_string()),
8148 ),
8149 ]))
8150 },
8151 },
8152 test_validate_offers_not_required_invalid_source_storage => {
8153 input = {
8154 let mut decl = new_component_decl();
8155 decl.children = Some(vec![
8156 fdecl::Child {
8157 name: Some("sink".to_string()),
8158 url: Some("fuchsia-pkg://fuchsia.com/sink#meta/sink.cm".to_string()),
8159 startup: Some(fdecl::StartupMode::Lazy),
8160 on_terminate: None,
8161 environment: None,
8162 ..Default::default()
8163 },
8164 ]);
8165 decl.capabilities = Some(vec![
8166 fdecl::Capability::Storage(fdecl::Storage {
8167 name: Some("data".to_string()),
8168 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8169 backing_dir: Some("minfs".to_string()),
8170 subdir: None,
8171 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
8172 ..Default::default()
8173 }),
8174 ]);
8175 let new_offer = |source: fdecl::Ref, availability: fdecl::Availability,
8176 target_name: &str|
8177 {
8178 fdecl::Offer::Storage(fdecl::OfferStorage {
8179 source: Some(source),
8180 source_name: Some("data".to_string()),
8181 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8182 name: "sink".to_string(),
8183 collection: None,
8184 })),
8185 target_name: Some(target_name.into()),
8186 availability: Some(availability),
8187 ..Default::default()
8188 })
8189 };
8190 decl.offers = Some(vec![
8191 new_offer(
8194 fdecl::Ref::Parent(fdecl::ParentRef {}),
8195 fdecl::Availability::Required,
8196 "data0",
8197 ),
8198 new_offer(
8199 fdecl::Ref::Parent(fdecl::ParentRef {}),
8200 fdecl::Availability::Optional,
8201 "data1",
8202 ),
8203 new_offer(
8204 fdecl::Ref::Parent(fdecl::ParentRef {}),
8205 fdecl::Availability::SameAsTarget,
8206 "data2",
8207 ),
8208 new_offer(
8209 fdecl::Ref::VoidType(fdecl::VoidRef {}),
8210 fdecl::Availability::Optional,
8211 "data3",
8212 ),
8213 new_offer(
8216 fdecl::Ref::Self_(fdecl::SelfRef {}),
8217 fdecl::Availability::Optional,
8218 "data4",
8219 ),
8220 new_offer(
8221 fdecl::Ref::Self_(fdecl::SelfRef {}),
8222 fdecl::Availability::SameAsTarget,
8223 "data5",
8224 ),
8225 new_offer(
8227 fdecl::Ref::VoidType(fdecl::VoidRef {}),
8228 fdecl::Availability::Required,
8229 "data6",
8230 ),
8231 new_offer(
8232 fdecl::Ref::VoidType(fdecl::VoidRef {}),
8233 fdecl::Availability::SameAsTarget,
8234 "data7",
8235 ),
8236 ]);
8237 decl
8238 },
8239 result = {
8240 Err(ErrorList::new(vec![
8241 Error::availability_must_be_optional(
8242 DeclType::OfferStorage,
8243 "availability",
8244 Some(&"data".to_string()),
8245 ),
8246 Error::availability_must_be_optional(
8247 DeclType::OfferStorage,
8248 "availability",
8249 Some(&"data".to_string()),
8250 ),
8251 ]))
8252 },
8253 },
8254
8255 test_validate_offers_valid_service_aggregation => {
8256 input = {
8257 let mut decl = new_component_decl();
8258 decl.offers = Some(vec![
8259 fdecl::Offer::Service(fdecl::OfferService {
8260 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
8261 name: "coll_a".to_string()
8262 })),
8263 source_name: Some("fuchsia.logger.Log".to_string()),
8264 target: Some(fdecl::Ref::Child(
8265 fdecl::ChildRef {
8266 name: "child_c".to_string(),
8267 collection: None,
8268 }
8269 )),
8270 target_name: Some("fuchsia.logger.Log".to_string()),
8271 source_instance_filter: None,
8272 ..Default::default()
8273 }),
8274 fdecl::Offer::Service(fdecl::OfferService {
8275 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
8276 name: "coll_b".to_string()
8277 })),
8278 source_name: Some("fuchsia.logger.Log".to_string()),
8279 target: Some(fdecl::Ref::Child(
8280 fdecl::ChildRef {
8281 name: "child_c".to_string(),
8282 collection: None,
8283 }
8284 )),
8285 target_name: Some("fuchsia.logger.Log".to_string()),
8286 source_instance_filter: Some(vec!["a_different_default".to_string()]),
8287 ..Default::default()
8288 })
8289 ]);
8290 decl.children = Some(vec![
8291 fdecl::Child {
8292 name: Some("child_c".to_string()),
8293 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
8294 startup: Some(fdecl::StartupMode::Lazy),
8295 ..Default::default()
8296 },
8297 ]);
8298 decl.collections = Some(vec![
8299 fdecl::Collection {
8300 name: Some("coll_a".into()),
8301 durability: Some(fdecl::Durability::Transient),
8302 ..Default::default()
8303 },
8304 fdecl::Collection {
8305 name: Some("coll_b".into()),
8306 durability: Some(fdecl::Durability::Transient),
8307 ..Default::default()
8308 },
8309 ]);
8310 decl
8311 },
8312 result = Ok(()),
8313 },
8314
8315 test_validate_source_dictionary => {
8317 input = fdecl::Component {
8318 program: Some(fdecl::Program {
8319 runner: Some("elf".into()),
8320 info: Some(fdata::Dictionary {
8321 entries: None,
8322 ..Default::default()
8323 }),
8324 ..Default::default()
8325 }),
8326 uses: Some(vec![
8327 fdecl::Use::Protocol(fdecl::UseProtocol {
8328 dependency_type: Some(fdecl::DependencyType::Strong),
8329 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8330 source_dictionary: Some("bad//".into()),
8331 source_name: Some("foo".into()),
8332 target_path: Some("/svc/foo".into()),
8333 ..Default::default()
8334 }),
8335 ]),
8336 exposes: Some(vec![
8337 fdecl::Expose::Directory(fdecl::ExposeDirectory {
8338 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8339 name: "missing".into(),
8340 collection: None,
8341 })),
8342 source_dictionary: Some("in/dict".into()),
8343 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8344 source_name: Some("foo".into()),
8345 target_name: Some("bar".into()),
8346 ..Default::default()
8347 }),
8348 ]),
8349 offers: Some(vec![
8350 fdecl::Offer::Service(fdecl::OfferService {
8351 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8352 source_dictionary: Some("bad//".into()),
8353 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8354 name: "child".into(),
8355 collection: None,
8356 })),
8357 source_name: Some("foo".into()),
8358 target_name: Some("bar".into()),
8359 ..Default::default()
8360 }),
8361 ]),
8362 children: Some(vec![
8363 fdecl::Child {
8364 name: Some("child".into()),
8365 url: Some("fuchsia-pkg://child".into()),
8366 startup: Some(fdecl::StartupMode::Lazy),
8367 ..Default::default()
8368 },
8369 ]),
8370 ..Default::default()
8371 },
8372 result = Err(ErrorList::new(vec![
8373 Error::invalid_field(DeclType::UseProtocol, "source_dictionary"),
8374 Error::invalid_child(DeclType::ExposeDirectory, "source", "missing"),
8375 Error::invalid_field(DeclType::OfferService, "source_dictionary"),
8376 ])),
8377 },
8378 test_validate_dictionary_too_long => {
8379 input = fdecl::Component {
8380 program: Some(fdecl::Program {
8381 runner: Some("elf".into()),
8382 info: Some(fdata::Dictionary {
8383 entries: None,
8384 ..Default::default()
8385 }),
8386 ..Default::default()
8387 }),
8388 uses: Some(vec![
8389 fdecl::Use::Protocol(fdecl::UseProtocol {
8390 dependency_type: Some(fdecl::DependencyType::Strong),
8391 source: Some(fdecl::Ref::Parent( fdecl::ParentRef {} )),
8392 source_dictionary: Some("a".repeat(4096)),
8393 source_name: Some("foo".into()),
8394 target_path: Some("/svc/foo".into()),
8395 ..Default::default()
8396 }),
8397 ]),
8398 ..Default::default()
8399 },
8400 result = Err(ErrorList::new(vec![
8401 Error::field_too_long(DeclType::UseProtocol, "source_dictionary"),
8402 ])),
8403 },
8404
8405 test_validate_environment_empty => {
8407 input = {
8408 let mut decl = new_component_decl();
8409 decl.environments = Some(vec![fdecl::Environment {
8410 name: None,
8411 extends: None,
8412 runners: None,
8413 resolvers: None,
8414 stop_timeout_ms: None,
8415 debug_capabilities: None,
8416 ..Default::default()
8417 }]);
8418 decl
8419 },
8420 result = Err(ErrorList::new(vec![
8421 Error::missing_field(DeclType::Environment, "name"),
8422 Error::missing_field(DeclType::Environment, "extends"),
8423 ])),
8424 },
8425
8426 test_validate_environment_no_stop_timeout => {
8427 input = {
8428 let mut decl = new_component_decl();
8429 decl.environments = Some(vec![fdecl::Environment {
8430 name: Some("env".to_string()),
8431 extends: Some(fdecl::EnvironmentExtends::None),
8432 runners: None,
8433 resolvers: None,
8434 stop_timeout_ms: None,
8435 ..Default::default()
8436 }]);
8437 decl
8438 },
8439 result = Err(ErrorList::new(vec![Error::missing_field(DeclType::Environment, "stop_timeout_ms")])),
8440 },
8441
8442 test_validate_environment_extends_stop_timeout => {
8443 input = { let mut decl = new_component_decl();
8444 decl.environments = Some(vec![fdecl::Environment {
8445 name: Some("env".to_string()),
8446 extends: Some(fdecl::EnvironmentExtends::Realm),
8447 runners: None,
8448 resolvers: None,
8449 stop_timeout_ms: None,
8450 ..Default::default()
8451 }]);
8452 decl
8453 },
8454 result = Ok(()),
8455 },
8456 test_validate_environment_long_identifiers => {
8457 input = {
8458 let mut decl = new_component_decl();
8459 decl.environments = Some(vec![fdecl::Environment {
8460 name: Some("a".repeat(256)),
8461 extends: Some(fdecl::EnvironmentExtends::None),
8462 runners: Some(vec![
8463 fdecl::RunnerRegistration {
8464 source_name: Some("a".repeat(256)),
8465 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8466 target_name: Some("a".repeat(256)),
8467 ..Default::default()
8468 },
8469 ]),
8470 resolvers: Some(vec![
8471 fdecl::ResolverRegistration {
8472 resolver: Some("a".repeat(256)),
8473 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8474 scheme: Some("a".repeat(256)),
8475 ..Default::default()
8476 },
8477 ]),
8478 stop_timeout_ms: Some(1234),
8479 ..Default::default()
8480 }]);
8481 decl
8482 },
8483 result = Err(ErrorList::new(vec![
8484 Error::field_too_long(DeclType::Environment, "name"),
8485 Error::field_too_long(DeclType::RunnerRegistration, "source_name"),
8486 Error::field_too_long(DeclType::RunnerRegistration, "target_name"),
8487 Error::field_too_long(DeclType::ResolverRegistration, "resolver"),
8488 Error::field_too_long(DeclType::ResolverRegistration, "scheme"),
8489 ])),
8490 },
8491 test_validate_environment_empty_runner_resolver_fields => {
8492 input = {
8493 let mut decl = new_component_decl();
8494 decl.environments = Some(vec![fdecl::Environment {
8495 name: Some("a".to_string()),
8496 extends: Some(fdecl::EnvironmentExtends::None),
8497 runners: Some(vec![
8498 fdecl::RunnerRegistration {
8499 source_name: None,
8500 source: None,
8501 target_name: None,
8502 ..Default::default()
8503 },
8504 ]),
8505 resolvers: Some(vec![
8506 fdecl::ResolverRegistration {
8507 resolver: None,
8508 source: None,
8509 scheme: None,
8510 ..Default::default()
8511 },
8512 ]),
8513 stop_timeout_ms: Some(1234),
8514 ..Default::default()
8515 }]);
8516 decl
8517 },
8518 result = Err(ErrorList::new(vec![
8519 Error::missing_field(DeclType::RunnerRegistration, "source_name"),
8520 Error::missing_field(DeclType::RunnerRegistration, "source"),
8521 Error::missing_field(DeclType::RunnerRegistration, "target_name"),
8522 Error::missing_field(DeclType::ResolverRegistration, "resolver"),
8523 Error::missing_field(DeclType::ResolverRegistration, "source"),
8524 Error::missing_field(DeclType::ResolverRegistration, "scheme"),
8525 ])),
8526 },
8527 test_validate_environment_invalid_fields => {
8528 input = {
8529 let mut decl = new_component_decl();
8530 decl.environments = Some(vec![fdecl::Environment {
8531 name: Some("a".to_string()),
8532 extends: Some(fdecl::EnvironmentExtends::None),
8533 runners: Some(vec![
8534 fdecl::RunnerRegistration {
8535 source_name: Some("^a".to_string()),
8536 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
8537 target_name: Some("%a".to_string()),
8538 ..Default::default()
8539 },
8540 ]),
8541 resolvers: Some(vec![
8542 fdecl::ResolverRegistration {
8543 resolver: Some("^a".to_string()),
8544 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
8545 scheme: Some("9scheme".to_string()),
8546 ..Default::default()
8547 },
8548 ]),
8549 stop_timeout_ms: Some(1234),
8550 ..Default::default()
8551 }]);
8552 decl
8553 },
8554 result = Err(ErrorList::new(vec![
8555 Error::invalid_field(DeclType::RunnerRegistration, "source_name"),
8556 Error::invalid_field(DeclType::RunnerRegistration, "source"),
8557 Error::invalid_field(DeclType::RunnerRegistration, "target_name"),
8558 Error::invalid_field(DeclType::ResolverRegistration, "resolver"),
8559 Error::invalid_field(DeclType::ResolverRegistration, "source"),
8560 Error::invalid_field(DeclType::ResolverRegistration, "scheme"),
8561 ])),
8562 },
8563 test_validate_environment_missing_runner => {
8564 input = {
8565 let mut decl = new_component_decl();
8566 decl.environments = Some(vec![fdecl::Environment {
8567 name: Some("a".to_string()),
8568 extends: Some(fdecl::EnvironmentExtends::None),
8569 runners: Some(vec![
8570 fdecl::RunnerRegistration {
8571 source_name: Some("dart".to_string()),
8572 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
8573 target_name: Some("dart".to_string()),
8574 ..Default::default()
8575 },
8576 ]),
8577 resolvers: None,
8578 stop_timeout_ms: Some(1234),
8579 ..Default::default()
8580 }]);
8581 decl
8582 },
8583 result = Err(ErrorList::new(vec![
8584 Error::invalid_runner(DeclType::RunnerRegistration, "source_name", "dart"),
8585 ])),
8586 },
8587 test_validate_environment_duplicate_registrations => {
8588 input = {
8589 let mut decl = new_component_decl();
8590 decl.environments = Some(vec![fdecl::Environment {
8591 name: Some("a".to_string()),
8592 extends: Some(fdecl::EnvironmentExtends::None),
8593 runners: Some(vec![
8594 fdecl::RunnerRegistration {
8595 source_name: Some("dart".to_string()),
8596 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8597 target_name: Some("dart".to_string()),
8598 ..Default::default()
8599 },
8600 fdecl::RunnerRegistration {
8601 source_name: Some("other-dart".to_string()),
8602 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8603 target_name: Some("dart".to_string()),
8604 ..Default::default()
8605 },
8606 ]),
8607 resolvers: Some(vec![
8608 fdecl::ResolverRegistration {
8609 resolver: Some("pkg_resolver".to_string()),
8610 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8611 scheme: Some("fuchsia-pkg".to_string()),
8612 ..Default::default()
8613 },
8614 fdecl::ResolverRegistration {
8615 resolver: Some("base_resolver".to_string()),
8616 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8617 scheme: Some("fuchsia-pkg".to_string()),
8618 ..Default::default()
8619 },
8620 ]),
8621 stop_timeout_ms: Some(1234),
8622 ..Default::default()
8623 }]);
8624 decl
8625 },
8626 result = Err(ErrorList::new(vec![
8627 Error::duplicate_field(DeclType::RunnerRegistration, "target_name", "dart"),
8628 Error::duplicate_field(DeclType::ResolverRegistration, "scheme", "fuchsia-pkg"),
8629 ])),
8630 },
8631 test_validate_environment_from_missing_child => {
8632 input = {
8633 let mut decl = new_component_decl();
8634 decl.environments = Some(vec![fdecl::Environment {
8635 name: Some("a".to_string()),
8636 extends: Some(fdecl::EnvironmentExtends::None),
8637 runners: Some(vec![
8638 fdecl::RunnerRegistration {
8639 source_name: Some("elf".to_string()),
8640 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8641 name: "missing".to_string(),
8642 collection: None,
8643 })),
8644 target_name: Some("elf".to_string()),
8645 ..Default::default()
8646 },
8647 ]),
8648 resolvers: Some(vec![
8649 fdecl::ResolverRegistration {
8650 resolver: Some("pkg_resolver".to_string()),
8651 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8652 name: "missing".to_string(),
8653 collection: None,
8654 })),
8655 scheme: Some("fuchsia-pkg".to_string()),
8656 ..Default::default()
8657 },
8658 ]),
8659 stop_timeout_ms: Some(1234),
8660 ..Default::default()
8661 }]);
8662 decl
8663 },
8664 result = Err(ErrorList::new(vec![
8665 Error::invalid_child(DeclType::RunnerRegistration, "source", "missing"),
8666 Error::invalid_child(DeclType::ResolverRegistration, "source", "missing"),
8667 ])),
8668 },
8669 test_validate_environment_runner_child_cycle => {
8670 input = {
8671 let mut decl = new_component_decl();
8672 decl.environments = Some(vec![fdecl::Environment {
8673 name: Some("env".to_string()),
8674 extends: Some(fdecl::EnvironmentExtends::None),
8675 runners: Some(vec![
8676 fdecl::RunnerRegistration {
8677 source_name: Some("elf".to_string()),
8678 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8679 name: "child".to_string(),
8680 collection: None,
8681 })),
8682 target_name: Some("elf".to_string()),
8683 ..Default::default()
8684 },
8685 ]),
8686 resolvers: None,
8687 stop_timeout_ms: Some(1234),
8688 ..Default::default()
8689 }]);
8690 decl.children = Some(vec![fdecl::Child {
8691 name: Some("child".to_string()),
8692 startup: Some(fdecl::StartupMode::Lazy),
8693 on_terminate: None,
8694 url: Some("fuchsia-pkg://child".to_string()),
8695 environment: Some("env".to_string()),
8696 ..Default::default()
8697 }]);
8698 decl
8699 },
8700 result = Err(ErrorList::new(vec![
8701 Error::dependency_cycle(
8702 "{{child child -> environment env -> child child}}"
8703 ),
8704 ])),
8705 },
8706 test_validate_environment_resolver_child_cycle => {
8707 input = {
8708 let mut decl = new_component_decl();
8709 decl.environments = Some(vec![fdecl::Environment {
8710 name: Some("env".to_string()),
8711 extends: Some(fdecl::EnvironmentExtends::None),
8712 runners: None,
8713 resolvers: Some(vec![
8714 fdecl::ResolverRegistration {
8715 resolver: Some("pkg_resolver".to_string()),
8716 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8717 name: "child".to_string(),
8718 collection: None,
8719 })),
8720 scheme: Some("fuchsia-pkg".to_string()),
8721 ..Default::default()
8722 },
8723 ]),
8724 stop_timeout_ms: Some(1234),
8725 ..Default::default()
8726 }]);
8727 decl.children = Some(vec![fdecl::Child {
8728 name: Some("child".to_string()),
8729 startup: Some(fdecl::StartupMode::Lazy),
8730 on_terminate: None,
8731 url: Some("fuchsia-pkg://child".to_string()),
8732 environment: Some("env".to_string()),
8733 ..Default::default()
8734 }]);
8735 decl
8736 },
8737 result = Err(ErrorList::new(vec![
8738 Error::dependency_cycle(
8739 "{{child child -> environment env -> child child}}"
8740 ),
8741 ])),
8742 },
8743 test_validate_environment_resolver_multiple_children_cycle => {
8744 input = {
8745 let mut decl = new_component_decl();
8746 decl.environments = Some(vec![fdecl::Environment {
8747 name: Some("env".to_string()),
8748 extends: Some(fdecl::EnvironmentExtends::None),
8749 runners: None,
8750 resolvers: Some(vec![
8751 fdecl::ResolverRegistration {
8752 resolver: Some("pkg_resolver".to_string()),
8753 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8754 name: "a".to_string(),
8755 collection: None,
8756 })),
8757 scheme: Some("fuchsia-pkg".to_string()),
8758 ..Default::default()
8759 },
8760 ]),
8761 stop_timeout_ms: Some(1234),
8762 ..Default::default()
8763 }]);
8764 decl.children = Some(vec![
8765 fdecl::Child {
8766 name: Some("a".to_string()),
8767 startup: Some(fdecl::StartupMode::Lazy),
8768 on_terminate: None,
8769 url: Some("fuchsia-pkg://child-a".to_string()),
8770 environment: None,
8771 ..Default::default()
8772 },
8773 fdecl::Child {
8774 name: Some("b".to_string()),
8775 startup: Some(fdecl::StartupMode::Lazy),
8776 on_terminate: None,
8777 url: Some("fuchsia-pkg://child-b".to_string()),
8778 environment: Some("env".to_string()),
8779 ..Default::default()
8780 },
8781 ]);
8782 decl.offers = Some(vec![fdecl::Offer::Service(fdecl::OfferService {
8783 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8784 name: "b".to_string(),
8785 collection: None,
8786 })),
8787 source_name: Some("thing".to_string()),
8788 target: Some(fdecl::Ref::Child(
8789 fdecl::ChildRef {
8790 name: "a".to_string(),
8791 collection: None,
8792 }
8793 )),
8794 target_name: Some("thing".to_string()),
8795 ..Default::default()
8796 })]);
8797 decl
8798 },
8799 result = Err(ErrorList::new(vec![
8800 Error::dependency_cycle(
8801 "{{child a -> environment env -> child b -> child a}}"
8802 ),
8803 ])),
8804 },
8805 test_validate_environment_debug_empty => {
8806 input = {
8807 let mut decl = new_component_decl();
8808 decl.environments = Some(vec![
8809 fdecl::Environment {
8810 name: Some("a".to_string()),
8811 extends: Some(fdecl::EnvironmentExtends::None),
8812 stop_timeout_ms: Some(2),
8813 debug_capabilities:Some(vec![
8814 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8815 source: None,
8816 source_name: None,
8817 target_name: None,
8818 ..Default::default()
8819 }),
8820 ]),
8821 ..Default::default()
8822 }]);
8823 decl
8824 },
8825 result = Err(ErrorList::new(vec![
8826 Error::missing_field(DeclType::DebugProtocolRegistration, "source"),
8827 Error::missing_field(DeclType::DebugProtocolRegistration, "source_name"),
8828 Error::missing_field(DeclType::DebugProtocolRegistration, "target_name"),
8829 ])),
8830 },
8831 test_validate_environment_debug_log_identifier => {
8832 input = {
8833 let mut decl = new_component_decl();
8834 decl.environments = Some(vec![
8835 fdecl::Environment {
8836 name: Some("a".to_string()),
8837 extends: Some(fdecl::EnvironmentExtends::None),
8838 stop_timeout_ms: Some(2),
8839 debug_capabilities:Some(vec![
8840 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8841 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8842 name: "a".repeat(256),
8843 collection: None,
8844 })),
8845 source_name: Some(format!("{}", "a".repeat(256))),
8846 target_name: Some(format!("{}", "b".repeat(256))),
8847 ..Default::default()
8848 }),
8849 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8850 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8851 source_name: Some("a".to_string()),
8852 target_name: Some(format!("{}", "b".repeat(256))),
8853 ..Default::default()
8854 }),
8855 ]),
8856 ..Default::default()
8857 }]);
8858 decl
8859 },
8860 result = Err(ErrorList::new(vec![
8861 Error::field_too_long(DeclType::DebugProtocolRegistration, "source.child.name"),
8862 Error::field_too_long(DeclType::DebugProtocolRegistration, "source_name"),
8863 Error::field_too_long(DeclType::DebugProtocolRegistration, "target_name"),
8864 Error::field_too_long(DeclType::DebugProtocolRegistration, "target_name"),
8865 ])),
8866 },
8867 test_validate_environment_debug_log_extraneous => {
8868 input = {
8869 let mut decl = new_component_decl();
8870 decl.environments = Some(vec![
8871 fdecl::Environment {
8872 name: Some("a".to_string()),
8873 extends: Some(fdecl::EnvironmentExtends::None),
8874 stop_timeout_ms: Some(2),
8875 debug_capabilities:Some(vec![
8876 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8877 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8878 name: "logger".to_string(),
8879 collection: Some("modular".to_string()),
8880 })),
8881 source_name: Some("fuchsia.logger.Log".to_string()),
8882 target_name: Some("fuchsia.logger.Log".to_string()),
8883 ..Default::default()
8884 }),
8885 ]),
8886 ..Default::default()
8887 }]);
8888 decl
8889 },
8890 result = Err(ErrorList::new(vec![
8891 Error::extraneous_field(DeclType::DebugProtocolRegistration, "source.child.collection"),
8892 ])),
8893 },
8894 test_validate_environment_debug_log_invalid_identifiers => {
8895 input = {
8896 let mut decl = new_component_decl();
8897 decl.environments = Some(vec![
8898 fdecl::Environment {
8899 name: Some("a".to_string()),
8900 extends: Some(fdecl::EnvironmentExtends::None),
8901 stop_timeout_ms: Some(2),
8902 debug_capabilities:Some(vec![
8903 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8904 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8905 name: "^bad".to_string(),
8906 collection: None,
8907 })),
8908 source_name: Some("foo/".to_string()),
8909 target_name: Some("/".to_string()),
8910 ..Default::default()
8911 }),
8912 ]),
8913 ..Default::default()
8914 }]);
8915 decl
8916 },
8917 result = Err(ErrorList::new(vec![
8918 Error::invalid_field(DeclType::DebugProtocolRegistration, "source.child.name"),
8919 Error::invalid_field(DeclType::DebugProtocolRegistration, "source_name"),
8920 Error::invalid_field(DeclType::DebugProtocolRegistration, "target_name"),
8921 ])),
8922 },
8923 test_validate_environment_debug_log_invalid_child => {
8924 input = {
8925 let mut decl = new_component_decl();
8926 decl.environments = Some(vec![
8927 fdecl::Environment {
8928 name: Some("a".to_string()),
8929 extends: Some(fdecl::EnvironmentExtends::None),
8930 stop_timeout_ms: Some(2),
8931 debug_capabilities:Some(vec![
8932 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8933 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8934 name: "logger".to_string(),
8935 collection: None,
8936 })),
8937 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
8938 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
8939 ..Default::default()
8940 }),
8941 ]),
8942 ..Default::default()
8943 }]);
8944 decl.children = Some(vec![
8945 fdecl::Child {
8946 name: Some("netstack".to_string()),
8947 url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
8948 startup: Some(fdecl::StartupMode::Lazy),
8949 on_terminate: None,
8950 environment: None,
8951 ..Default::default()
8952 },
8953 ]);
8954 decl
8955 },
8956 result = Err(ErrorList::new(vec![
8957 Error::invalid_child(DeclType::DebugProtocolRegistration, "source", "logger"),
8958
8959 ])),
8960 },
8961 test_validate_environment_debug_source_capability => {
8962 input = {
8963 let mut decl = new_component_decl();
8964 decl.environments = Some(vec![
8965 fdecl::Environment {
8966 name: Some("a".to_string()),
8967 extends: Some(fdecl::EnvironmentExtends::None),
8968 stop_timeout_ms: Some(2),
8969 debug_capabilities:Some(vec![
8970 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8971 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
8972 name: "storage".to_string(),
8973 })),
8974 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
8975 target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
8976 ..Default::default()
8977 }),
8978 ]),
8979 ..Default::default()
8980 }]);
8981 decl
8982 },
8983 result = Err(ErrorList::new(vec![
8984 Error::invalid_field(DeclType::DebugProtocolRegistration, "source"),
8985 ])),
8986 },
8987
8988 test_validate_children_empty => {
8990 input = {
8991 let mut decl = new_component_decl();
8992 decl.children = Some(vec![fdecl::Child{
8993 name: None,
8994 url: None,
8995 startup: None,
8996 on_terminate: None,
8997 environment: None,
8998 ..Default::default()
8999 }]);
9000 decl
9001 },
9002 result = Err(ErrorList::new(vec![
9003 Error::missing_field(DeclType::Child, "name"),
9004 Error::missing_field(DeclType::Child, "url"),
9005 Error::missing_field(DeclType::Child, "startup"),
9006 ])),
9008 },
9009 test_validate_children_invalid_identifiers => {
9010 input = {
9011 let mut decl = new_component_decl();
9012 decl.children = Some(vec![fdecl::Child{
9013 name: Some("^bad".to_string()),
9014 url: Some("scheme://invalid-port:99999999/path#frag".to_string()),
9015 startup: Some(fdecl::StartupMode::Lazy),
9016 on_terminate: None,
9017 environment: None,
9018 ..Default::default()
9019 }]);
9020 decl
9021 },
9022 result = Err(ErrorList::new(vec![
9023 Error::invalid_field(DeclType::Child, "name"),
9024 Error::invalid_url(DeclType::Child, "url", "\"scheme://invalid-port:99999999/path#frag\": Malformed URL: InvalidPort."),
9025 ])),
9026 },
9027 test_validate_children_long_identifiers => {
9028 input = {
9029 let mut decl = new_component_decl();
9030 decl.children = Some(vec![fdecl::Child{
9031 name: Some("a".repeat(1025)),
9032 url: Some(format!("fuchsia-pkg://{}", "a".repeat(4083))),
9033 startup: Some(fdecl::StartupMode::Lazy),
9034 on_terminate: None,
9035 environment: Some("a".repeat(1025)),
9036 ..Default::default()
9037 }]);
9038 decl
9039 },
9040 result = Err(ErrorList::new(vec![
9041 Error::field_too_long(DeclType::Child, "name"),
9042 Error::field_too_long(DeclType::Child, "url"),
9043 Error::field_too_long(DeclType::Child, "environment"),
9044 Error::invalid_environment(DeclType::Child, "environment", "a".repeat(1025)),
9045 ])),
9046 },
9047 test_validate_child_references_unknown_env => {
9048 input = {
9049 let mut decl = new_component_decl();
9050 decl.children = Some(vec![fdecl::Child{
9051 name: Some("foo".to_string()),
9052 url: Some("fuchsia-pkg://foo".to_string()),
9053 startup: Some(fdecl::StartupMode::Lazy),
9054 on_terminate: None,
9055 environment: Some("test_env".to_string()),
9056 ..Default::default()
9057 }]);
9058 decl
9059 },
9060 result = Err(ErrorList::new(vec![
9061 Error::invalid_environment(DeclType::Child, "environment", "test_env"),
9062 ])),
9063 },
9064
9065 test_validate_collections_empty => {
9067 input = {
9068 let mut decl = new_component_decl();
9069 decl.collections = Some(vec![fdecl::Collection{
9070 name: None,
9071 durability: None,
9072 environment: None,
9073 allowed_offers: None,
9074 allow_long_names: None,
9075 ..Default::default()
9076 }]);
9077 decl
9078 },
9079 result = Err(ErrorList::new(vec![
9080 Error::missing_field(DeclType::Collection, "name"),
9081 Error::missing_field(DeclType::Collection, "durability"),
9082 ])),
9083 },
9084 test_validate_collections_invalid_identifiers => {
9085 input = {
9086 let mut decl = new_component_decl();
9087 decl.collections = Some(vec![fdecl::Collection{
9088 name: Some("^bad".to_string()),
9089 durability: Some(fdecl::Durability::Transient),
9090 environment: None,
9091 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
9092 allow_long_names: None,
9093 ..Default::default()
9094 }]);
9095 decl
9096 },
9097 result = Err(ErrorList::new(vec![
9098 Error::invalid_field(DeclType::Collection, "name"),
9099 ])),
9100 },
9101 test_validate_collections_long_identifiers => {
9102 input = {
9103 let mut decl = new_component_decl();
9104 decl.collections = Some(vec![fdecl::Collection{
9105 name: Some("a".repeat(1025)),
9106 durability: Some(fdecl::Durability::Transient),
9107 environment: None,
9108 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
9109 allow_long_names: None,
9110 ..Default::default()
9111 }]);
9112 decl
9113 },
9114 result = Err(ErrorList::new(vec![
9115 Error::field_too_long(DeclType::Collection, "name"),
9116 ])),
9117 },
9118 test_validate_collection_references_unknown_env => {
9119 input = {
9120 let mut decl = new_component_decl();
9121 decl.collections = Some(vec![fdecl::Collection {
9122 name: Some("foo".to_string()),
9123 durability: Some(fdecl::Durability::Transient),
9124 environment: Some("test_env".to_string()),
9125 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
9126 allow_long_names: None,
9127 ..Default::default()
9128 }]);
9129 decl
9130 },
9131 result = Err(ErrorList::new(vec![
9132 Error::invalid_environment(DeclType::Collection, "environment", "test_env"),
9133 ])),
9134 },
9135
9136 test_validate_capabilities_empty => {
9138 input = {
9139 let mut decl = new_component_decl();
9140 decl.capabilities = Some(vec![
9141 fdecl::Capability::Service(fdecl::Service {
9142 name: None,
9143 source_path: None,
9144 ..Default::default()
9145 }),
9146 fdecl::Capability::Protocol(fdecl::Protocol {
9147 name: None,
9148 source_path: None,
9149 ..Default::default()
9150 }),
9151 fdecl::Capability::Directory(fdecl::Directory {
9152 name: None,
9153 source_path: None,
9154 rights: None,
9155 ..Default::default()
9156 }),
9157 fdecl::Capability::Storage(fdecl::Storage {
9158 name: None,
9159 source: None,
9160 backing_dir: None,
9161 subdir: None,
9162 storage_id: None,
9163 ..Default::default()
9164 }),
9165 fdecl::Capability::Runner(fdecl::Runner {
9166 name: None,
9167 source_path: None,
9168 ..Default::default()
9169 }),
9170 fdecl::Capability::Resolver(fdecl::Resolver {
9171 name: None,
9172 source_path: None,
9173 ..Default::default()
9174 }),
9175 fdecl::Capability::Dictionary(fdecl::Dictionary {
9176 ..Default::default()
9177 }),
9178 ]);
9179 decl
9180 },
9181 result = Err(ErrorList::new(vec![
9182 Error::missing_field(DeclType::Dictionary, "name"),
9183 Error::missing_field(DeclType::Service, "name"),
9184 Error::missing_field(DeclType::Service, "source_path"),
9185 Error::missing_field(DeclType::Protocol, "name"),
9186 Error::missing_field(DeclType::Protocol, "source_path"),
9187 Error::missing_field(DeclType::Directory, "name"),
9188 Error::missing_field(DeclType::Directory, "source_path"),
9189 Error::missing_field(DeclType::Directory, "rights"),
9190 Error::missing_field(DeclType::Storage, "source"),
9191 Error::missing_field(DeclType::Storage, "name"),
9192 Error::missing_field(DeclType::Storage, "storage_id"),
9193 Error::missing_field(DeclType::Storage, "backing_dir"),
9194 Error::missing_field(DeclType::Runner, "name"),
9195 Error::missing_field(DeclType::Runner, "source_path"),
9196 Error::missing_field(DeclType::Resolver, "name"),
9197 Error::missing_field(DeclType::Resolver, "source_path"),
9198 ])),
9199 },
9200 test_validate_capabilities_invalid_identifiers => {
9201 input = {
9202 let mut decl = new_component_decl();
9203 decl.capabilities = Some(vec![
9204 fdecl::Capability::Service(fdecl::Service {
9205 name: Some("^bad".to_string()),
9206 source_path: Some("&bad".to_string()),
9207 ..Default::default()
9208 }),
9209 fdecl::Capability::Protocol(fdecl::Protocol {
9210 name: Some("^bad".to_string()),
9211 source_path: Some("&bad".to_string()),
9212 ..Default::default()
9213 }),
9214 fdecl::Capability::Directory(fdecl::Directory {
9215 name: Some("^bad".to_string()),
9216 source_path: Some("&bad".to_string()),
9217 rights: Some(fio::Operations::CONNECT),
9218 ..Default::default()
9219 }),
9220 fdecl::Capability::Storage(fdecl::Storage {
9221 name: Some("^bad".to_string()),
9222 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9223 name: "/bad".to_string()
9224 })),
9225 backing_dir: Some("&bad".to_string()),
9226 subdir: None,
9227 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9228 ..Default::default()
9229 }),
9230 fdecl::Capability::Runner(fdecl::Runner {
9231 name: Some("^bad".to_string()),
9232 source_path: Some("&bad".to_string()),
9233 ..Default::default()
9234 }),
9235 fdecl::Capability::Resolver(fdecl::Resolver {
9236 name: Some("^bad".to_string()),
9237 source_path: Some("&bad".to_string()),
9238 ..Default::default()
9239 }),
9240 fdecl::Capability::Dictionary(fdecl::Dictionary {
9241 name: Some("^bad".to_string()),
9242 ..Default::default()
9243 }),
9244 ]);
9245 decl
9246 },
9247 result = Err(ErrorList::new(vec![
9248 Error::invalid_field(DeclType::Dictionary, "name"),
9249 Error::invalid_field(DeclType::Service, "name"),
9250 Error::invalid_field(DeclType::Service, "source_path"),
9251 Error::invalid_field(DeclType::Protocol, "name"),
9252 Error::invalid_field(DeclType::Protocol, "source_path"),
9253 Error::invalid_field(DeclType::Directory, "name"),
9254 Error::invalid_field(DeclType::Directory, "source_path"),
9255 Error::invalid_field(DeclType::Storage, "source"),
9256 Error::invalid_field(DeclType::Storage, "name"),
9257 Error::invalid_field(DeclType::Storage, "backing_dir"),
9258 Error::invalid_field(DeclType::Runner, "name"),
9259 Error::invalid_field(DeclType::Runner, "source_path"),
9260 Error::invalid_field(DeclType::Resolver, "name"),
9261 Error::invalid_field(DeclType::Resolver, "source_path"),
9262 ])),
9263 },
9264 test_validate_capabilities_invalid_child => {
9265 input = {
9266 let mut decl = new_component_decl();
9267 decl.capabilities = Some(vec![
9268 fdecl::Capability::Storage(fdecl::Storage {
9269 name: Some("foo".to_string()),
9270 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9271 name: "invalid".to_string(),
9272 })),
9273 backing_dir: Some("foo".to_string()),
9274 subdir: None,
9275 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9276 ..Default::default()
9277 }),
9278 ]);
9279 decl
9280 },
9281 result = Err(ErrorList::new(vec![
9282 Error::invalid_field(DeclType::Storage, "source"),
9283 ])),
9284 },
9285 test_validate_capabilities_long_identifiers => {
9286 input = {
9287 let mut decl = new_component_decl();
9288 decl.capabilities = Some(vec![
9289 fdecl::Capability::Service(fdecl::Service {
9290 name: Some("a".repeat(256)),
9291 source_path: Some("/c".repeat(2048)),
9292 ..Default::default()
9293 }),
9294 fdecl::Capability::Protocol(fdecl::Protocol {
9295 name: Some("a".repeat(256)),
9296 source_path: Some("/c".repeat(2048)),
9297 ..Default::default()
9298 }),
9299 fdecl::Capability::Directory(fdecl::Directory {
9300 name: Some("a".repeat(256)),
9301 source_path: Some("/c".repeat(2048)),
9302 rights: Some(fio::Operations::CONNECT),
9303 ..Default::default()
9304 }),
9305 fdecl::Capability::Storage(fdecl::Storage {
9306 name: Some("a".repeat(256)),
9307 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
9308 name: "b".repeat(256),
9309 collection: None,
9310 })),
9311 backing_dir: Some(format!("{}", "c".repeat(256))),
9312 subdir: None,
9313 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9314 ..Default::default()
9315 }),
9316 fdecl::Capability::Runner(fdecl::Runner {
9317 name: Some("a".repeat(256)),
9318 source_path: Some("/c".repeat(2048)),
9319 ..Default::default()
9320 }),
9321 fdecl::Capability::Resolver(fdecl::Resolver {
9322 name: Some("a".repeat(256)),
9323 source_path: Some("/c".repeat(2048)),
9324 ..Default::default()
9325 }),
9326 fdecl::Capability::Dictionary(fdecl::Dictionary {
9327 name: Some("a".repeat(256)),
9328 ..Default::default()
9329 }),
9330 ]);
9331 decl
9332 },
9333 result = Err(ErrorList::new(vec![
9334 Error::field_too_long(DeclType::Dictionary, "name"),
9335 Error::field_too_long(DeclType::Service, "name"),
9336 Error::field_too_long(DeclType::Service, "source_path"),
9337 Error::field_too_long(DeclType::Protocol, "name"),
9338 Error::field_too_long(DeclType::Protocol, "source_path"),
9339 Error::field_too_long(DeclType::Directory, "name"),
9340 Error::field_too_long(DeclType::Directory, "source_path"),
9341 Error::field_too_long(DeclType::Storage, "source.child.name"),
9342 Error::field_too_long(DeclType::Storage, "name"),
9343 Error::field_too_long(DeclType::Storage, "backing_dir"),
9344 Error::field_too_long(DeclType::Runner, "name"),
9345 Error::field_too_long(DeclType::Runner, "source_path"),
9346 Error::field_too_long(DeclType::Resolver, "name"),
9347 Error::field_too_long(DeclType::Resolver, "source_path"),
9348 ])),
9349 },
9350 test_validate_capabilities_duplicate_name => {
9351 input = {
9352 let mut decl = new_component_decl();
9353 decl.capabilities = Some(vec![
9354 fdecl::Capability::Service(fdecl::Service {
9355 name: Some("service".to_string()),
9356 source_path: Some("/service".to_string()),
9357 ..Default::default()
9358 }),
9359 fdecl::Capability::Service(fdecl::Service {
9360 name: Some("service".to_string()),
9361 source_path: Some("/service".to_string()),
9362 ..Default::default()
9363 }),
9364 fdecl::Capability::Protocol(fdecl::Protocol {
9365 name: Some("protocol".to_string()),
9366 source_path: Some("/protocol".to_string()),
9367 ..Default::default()
9368 }),
9369 fdecl::Capability::Protocol(fdecl::Protocol {
9370 name: Some("protocol".to_string()),
9371 source_path: Some("/protocol".to_string()),
9372 ..Default::default()
9373 }),
9374 fdecl::Capability::Directory(fdecl::Directory {
9375 name: Some("directory".to_string()),
9376 source_path: Some("/directory".to_string()),
9377 rights: Some(fio::Operations::CONNECT),
9378 ..Default::default()
9379 }),
9380 fdecl::Capability::Directory(fdecl::Directory {
9381 name: Some("directory".to_string()),
9382 source_path: Some("/directory".to_string()),
9383 rights: Some(fio::Operations::CONNECT),
9384 ..Default::default()
9385 }),
9386 fdecl::Capability::Storage(fdecl::Storage {
9387 name: Some("storage".to_string()),
9388 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
9389 backing_dir: Some("directory".to_string()),
9390 subdir: None,
9391 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9392 ..Default::default()
9393 }),
9394 fdecl::Capability::Storage(fdecl::Storage {
9395 name: Some("storage".to_string()),
9396 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
9397 backing_dir: Some("directory".to_string()),
9398 subdir: None,
9399 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9400 ..Default::default()
9401 }),
9402 fdecl::Capability::Runner(fdecl::Runner {
9403 name: Some("runner".to_string()),
9404 source_path: Some("/runner".to_string()),
9405 ..Default::default()
9406 }),
9407 fdecl::Capability::Runner(fdecl::Runner {
9408 name: Some("runner".to_string()),
9409 source_path: Some("/runner".to_string()),
9410 ..Default::default()
9411 }),
9412 fdecl::Capability::Resolver(fdecl::Resolver {
9413 name: Some("resolver".to_string()),
9414 source_path: Some("/resolver".to_string()),
9415 ..Default::default()
9416 }),
9417 fdecl::Capability::Resolver(fdecl::Resolver {
9418 name: Some("resolver".to_string()),
9419 source_path: Some("/resolver".to_string()),
9420 ..Default::default()
9421 }),
9422 fdecl::Capability::Dictionary(fdecl::Dictionary {
9423 name: Some("dictionary".to_string()),
9424 ..Default::default()
9425 }),
9426 fdecl::Capability::Dictionary(fdecl::Dictionary {
9427 name: Some("dictionary".to_string()),
9428 ..Default::default()
9429 }),
9430 ]);
9431 decl
9432 },
9433 result = Err(ErrorList::new(vec![
9434 Error::duplicate_field(DeclType::Dictionary, "name", "dictionary"),
9435 Error::duplicate_field(DeclType::Service, "name", "service"),
9436 Error::duplicate_field(DeclType::Protocol, "name", "protocol"),
9437 Error::duplicate_field(DeclType::Directory, "name", "directory"),
9438 Error::duplicate_field(DeclType::Storage, "name", "storage"),
9439 Error::duplicate_field(DeclType::Runner, "name", "runner"),
9440 Error::duplicate_field(DeclType::Resolver, "name", "resolver"),
9441 ])),
9442 },
9443 test_validate_invalid_service_aggregation_conflicting_filter => {
9444 input = {
9445 let mut decl = new_component_decl();
9446 decl.offers = Some(vec![
9447 fdecl::Offer::Service(fdecl::OfferService {
9448 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9449 name: "coll_a".to_string()
9450 })),
9451 source_name: Some("fuchsia.logger.Log".to_string()),
9452 target: Some(fdecl::Ref::Child(
9453 fdecl::ChildRef {
9454 name: "child_c".to_string(),
9455 collection: None,
9456 }
9457 )),
9458 target_name: Some("fuchsia.logger.Log1".to_string()),
9459 source_instance_filter: Some(vec!["default".to_string()]),
9460 ..Default::default()
9461 }),
9462 fdecl::Offer::Service(fdecl::OfferService {
9463 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9464 name: "coll_b".to_string()
9465 })),
9466 source_name: Some("fuchsia.logger.Log".to_string()),
9467 target: Some(fdecl::Ref::Child(
9468 fdecl::ChildRef {
9469 name: "child_c".to_string(),
9470 collection: None,
9471 }
9472 )),
9473 target_name: Some("fuchsia.logger.Log1".to_string()),
9474 source_instance_filter: Some(vec!["default".to_string()]),
9475 ..Default::default()
9476 }),
9477 ]);
9478 decl.collections = Some(vec![
9479 fdecl::Collection {
9480 name: Some("coll_a".to_string()),
9481 durability: Some(fdecl::Durability::Transient),
9482 ..Default::default()
9483 },
9484 fdecl::Collection {
9485 name: Some("coll_b".to_string()),
9486 durability: Some(fdecl::Durability::Transient),
9487 ..Default::default()
9488 },
9489 ]);
9490 decl.children = Some(vec![
9491 fdecl::Child {
9492 name: Some("child_c".to_string()),
9493 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9494 startup: Some(fdecl::StartupMode::Lazy),
9495 ..Default::default()
9496 },
9497 ]);
9498 decl
9499 },
9500 result = Err(ErrorList::new(vec![
9501 Error::invalid_aggregate_offer("Conflicting source_instance_filter in aggregate \
9502 service offer, instance_name 'default' seen in filter lists multiple times"),
9503 ])),
9504 },
9505
9506 test_validate_invalid_service_aggregation_conflicting_source_name => {
9507 input = {
9508 let mut decl = new_component_decl();
9509 decl.offers = Some(vec![
9510 fdecl::Offer::Service(fdecl::OfferService {
9511 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9512 name: "coll_a".into()
9513 })),
9514 source_name: Some("fuchsia.logger.Log".to_string()),
9515 target: Some(fdecl::Ref::Child(
9516 fdecl::ChildRef {
9517 name: "child_c".to_string(),
9518 collection: None,
9519 }
9520 )),
9521 target_name: Some("fuchsia.logger.Log2".to_string()),
9522 source_instance_filter: Some(vec!["default2".to_string()]),
9523 ..Default::default()
9524 }),
9525 fdecl::Offer::Service(fdecl::OfferService {
9526 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9527 name: "coll_b".into()
9528 })),
9529 source_name: Some("fuchsia.logger.LogAlt".to_string()),
9530 target: Some(fdecl::Ref::Child(
9531 fdecl::ChildRef {
9532 name: "child_c".to_string(),
9533 collection: None,
9534 }
9535 )),
9536 target_name: Some("fuchsia.logger.Log2".to_string()),
9537 source_instance_filter: Some(vec!["default".to_string()]),
9538 ..Default::default()
9539 })
9540 ]);
9541 decl.collections = Some(vec![
9542 fdecl::Collection {
9543 name: Some("coll_a".to_string()),
9544 durability: Some(fdecl::Durability::Transient),
9545 ..Default::default()
9546 },
9547 fdecl::Collection {
9548 name: Some("coll_b".to_string()),
9549 durability: Some(fdecl::Durability::Transient),
9550 ..Default::default()
9551 },
9552 ]);
9553 decl.children = Some(vec![
9554 fdecl::Child {
9555 name: Some("child_c".to_string()),
9556 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9557 startup: Some(fdecl::StartupMode::Lazy),
9558 on_terminate: None,
9559 environment: None,
9560 ..Default::default()
9561 },
9562 ]);
9563 decl
9564 },
9565 result = Err(ErrorList::new(vec![
9566 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."),
9567 ])),
9568 },
9569
9570 test_validate_resolvers_missing_from_offer => {
9571 input = {
9572 let mut decl = new_component_decl();
9573 decl.offers = Some(vec![fdecl::Offer::Resolver(fdecl::OfferResolver {
9574 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
9575 source_name: Some("a".to_string()),
9576 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
9577 target_name: Some("a".to_string()),
9578 ..Default::default()
9579 })]);
9580 decl.children = Some(vec![fdecl::Child {
9581 name: Some("child".to_string()),
9582 url: Some("test:///child".to_string()),
9583 startup: Some(fdecl::StartupMode::Eager),
9584 on_terminate: None,
9585 environment: None,
9586 ..Default::default()
9587 }]);
9588 decl
9589 },
9590 result = Err(ErrorList::new(vec![
9591 Error::invalid_capability(DeclType::OfferResolver, "source", "a"),
9592 ])),
9593 },
9594 test_validate_resolvers_missing_from_expose => {
9595 input = {
9596 let mut decl = new_component_decl();
9597 decl.exposes = Some(vec![fdecl::Expose::Resolver(fdecl::ExposeResolver {
9598 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
9599 source_name: Some("a".to_string()),
9600 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
9601 target_name: Some("a".to_string()),
9602 ..Default::default()
9603 })]);
9604 decl
9605 },
9606 result = Err(ErrorList::new(vec![
9607 Error::invalid_capability(DeclType::ExposeResolver, "source", "a"),
9608 ])),
9609 },
9610
9611 test_validate_config_missing_config => {
9612 input = {
9613 let mut decl = new_component_decl();
9614 decl.config = Some(fdecl::ConfigSchema{
9615 fields: None,
9616 checksum: None,
9617 value_source: None,
9618 ..Default::default()
9619 });
9620 decl
9621 },
9622 result = Err(ErrorList::new(vec![
9623 Error::missing_field(DeclType::ConfigSchema, "fields"),
9624 Error::missing_field(DeclType::ConfigSchema, "checksum"),
9625 Error::missing_field(DeclType::ConfigSchema, "value_source"),
9626 ])),
9627 },
9628
9629 test_validate_config_missing_config_field => {
9630 input = {
9631 let mut decl = new_component_decl();
9632 decl.config = Some(fdecl::ConfigSchema{
9633 fields: Some(vec![
9634 fdecl::ConfigField {
9635 key: None,
9636 type_: None,
9637 ..Default::default()
9638 }
9639 ]),
9640 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9641 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9642 ..Default::default()
9643 });
9644 decl
9645 },
9646 result = Err(ErrorList::new(vec![
9647 Error::missing_field(DeclType::ConfigField, "key"),
9648 Error::missing_field(DeclType::ConfigField, "value_type"),
9649 ])),
9650 },
9651
9652 test_validate_config_bool => {
9653 input = {
9654 let mut decl = new_component_decl();
9655 decl.config = Some(fdecl::ConfigSchema{
9656 fields: Some(vec![
9657 fdecl::ConfigField {
9658 key: Some("test".to_string()),
9659 type_: Some(fdecl::ConfigType {
9660 layout: fdecl::ConfigTypeLayout::Bool,
9661 parameters: Some(vec![]),
9662 constraints: vec![]
9663 }),
9664 ..Default::default()
9665 }
9666 ]),
9667 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9668 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9669 ..Default::default()
9670 });
9671 decl
9672 },
9673 result = Ok(()),
9674 },
9675
9676 test_validate_config_bool_extra_constraint => {
9677 input = {
9678 let mut decl = new_component_decl();
9679 decl.config = Some(fdecl::ConfigSchema{
9680 fields: Some(vec![
9681 fdecl::ConfigField {
9682 key: Some("test".to_string()),
9683 type_: Some(fdecl::ConfigType {
9684 layout: fdecl::ConfigTypeLayout::Bool,
9685 parameters: Some(vec![]),
9686 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9687 }),
9688 ..Default::default()
9689 }
9690 ]),
9691 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9692 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9693 ..Default::default()
9694 });
9695 decl
9696 },
9697 result = Err(ErrorList::new(vec![
9698 Error::extraneous_field(DeclType::ConfigType, "constraints")
9699 ])),
9700 },
9701
9702 test_validate_config_bool_missing_parameters => {
9703 input = {
9704 let mut decl = new_component_decl();
9705 decl.config = Some(fdecl::ConfigSchema{
9706 fields: Some(vec![
9707 fdecl::ConfigField {
9708 key: Some("test".to_string()),
9709 type_: Some(fdecl::ConfigType {
9710 layout: fdecl::ConfigTypeLayout::Bool,
9711 parameters: None,
9712 constraints: vec![]
9713 }),
9714 ..Default::default()
9715 }
9716 ]),
9717 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9718 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9719 ..Default::default()
9720 });
9721 decl
9722 },
9723 result = Err(ErrorList::new(vec![
9724 Error::missing_field(DeclType::ConfigType, "parameters")
9725 ])),
9726 },
9727
9728 test_validate_config_string => {
9729 input = {
9730 let mut decl = new_component_decl();
9731 decl.config = Some(fdecl::ConfigSchema{
9732 fields: Some(vec![
9733 fdecl::ConfigField {
9734 key: Some("test".to_string()),
9735 type_: Some(fdecl::ConfigType {
9736 layout: fdecl::ConfigTypeLayout::String,
9737 parameters: Some(vec![]),
9738 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9739 }),
9740 ..Default::default()
9741 }
9742 ]),
9743 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9744 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9745 ..Default::default()
9746 });
9747 decl
9748 },
9749 result = Ok(()),
9750 },
9751
9752 test_validate_config_string_missing_parameter => {
9753 input = {
9754 let mut decl = new_component_decl();
9755 decl.config = Some(fdecl::ConfigSchema{
9756 fields: Some(vec![
9757 fdecl::ConfigField {
9758 key: Some("test".to_string()),
9759 type_: Some(fdecl::ConfigType {
9760 layout: fdecl::ConfigTypeLayout::String,
9761 parameters: None,
9762 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9763 }),
9764 ..Default::default()
9765 }
9766 ]),
9767 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9768 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9769 ..Default::default()
9770 });
9771 decl
9772 },
9773 result = Err(ErrorList::new(vec![
9774 Error::missing_field(DeclType::ConfigType, "parameters")
9775 ])),
9776 },
9777
9778 test_validate_config_string_missing_constraint => {
9779 input = {
9780 let mut decl = new_component_decl();
9781 decl.config = Some(fdecl::ConfigSchema{
9782 fields: Some(vec![
9783 fdecl::ConfigField {
9784 key: Some("test".to_string()),
9785 type_: Some(fdecl::ConfigType {
9786 layout: fdecl::ConfigTypeLayout::String,
9787 parameters: Some(vec![]),
9788 constraints: vec![]
9789 }),
9790 ..Default::default()
9791 }
9792 ]),
9793 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9794 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9795 ..Default::default()
9796 });
9797 decl
9798 },
9799 result = Err(ErrorList::new(vec![
9800 Error::missing_field(DeclType::ConfigType, "constraints")
9801 ])),
9802 },
9803
9804 test_validate_config_string_extra_constraint => {
9805 input = {
9806 let mut decl = new_component_decl();
9807 decl.config = Some(fdecl::ConfigSchema{
9808 fields: Some(vec![
9809 fdecl::ConfigField {
9810 key: Some("test".to_string()),
9811 type_: Some(fdecl::ConfigType {
9812 layout: fdecl::ConfigTypeLayout::String,
9813 parameters: Some(vec![]),
9814 constraints: vec![fdecl::LayoutConstraint::MaxSize(10), fdecl::LayoutConstraint::MaxSize(10)]
9815 }),
9816 ..Default::default()
9817 }
9818 ]),
9819 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9820 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9821 ..Default::default()
9822 });
9823 decl
9824 },
9825 result = Err(ErrorList::new(vec![
9826 Error::extraneous_field(DeclType::ConfigType, "constraints")
9827 ])),
9828 },
9829
9830 test_validate_config_vector_bool => {
9831 input = {
9832 let mut decl = new_component_decl();
9833 decl.config = Some(fdecl::ConfigSchema{
9834 fields: Some(vec![
9835 fdecl::ConfigField {
9836 key: Some("test".to_string()),
9837 type_: Some(fdecl::ConfigType {
9838 layout: fdecl::ConfigTypeLayout::Vector,
9839 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9840 layout: fdecl::ConfigTypeLayout::Bool,
9841 parameters: Some(vec![]),
9842 constraints: vec![],
9843 })]),
9844 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9845 }),
9846 ..Default::default()
9847 }
9848 ]),
9849 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9850 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9851 ..Default::default()
9852 });
9853 decl
9854 },
9855 result = Ok(()),
9856 },
9857
9858 test_validate_config_vector_extra_parameter => {
9859 input = {
9860 let mut decl = new_component_decl();
9861 decl.config = Some(fdecl::ConfigSchema{
9862 fields: Some(vec![
9863 fdecl::ConfigField {
9864 key: Some("test".to_string()),
9865 type_: Some(fdecl::ConfigType {
9866 layout: fdecl::ConfigTypeLayout::Vector,
9867 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9868 layout: fdecl::ConfigTypeLayout::Bool,
9869 parameters: Some(vec![]),
9870 constraints: vec![],
9871 }), fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9872 layout: fdecl::ConfigTypeLayout::Uint8,
9873 parameters: Some(vec![]),
9874 constraints: vec![],
9875 })]),
9876 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9877 }),
9878 ..Default::default()
9879 }
9880 ]),
9881 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9882 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9883 ..Default::default()
9884 });
9885 decl
9886 },
9887 result = Err(ErrorList::new(vec![
9888 Error::extraneous_field(DeclType::ConfigType, "parameters")
9889 ])),
9890 },
9891
9892 test_validate_config_vector_missing_parameter => {
9893 input = {
9894 let mut decl = new_component_decl();
9895 decl.config = Some(fdecl::ConfigSchema{
9896 fields: Some(vec![
9897 fdecl::ConfigField {
9898 key: Some("test".to_string()),
9899 type_: Some(fdecl::ConfigType {
9900 layout: fdecl::ConfigTypeLayout::Vector,
9901 parameters: Some(vec![]),
9902 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9903 }),
9904 ..Default::default()
9905 }
9906 ]),
9907 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9908 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9909 ..Default::default()
9910 });
9911 decl
9912 },
9913 result = Err(ErrorList::new(vec![
9914 Error::missing_field(DeclType::ConfigType, "parameters")
9915 ])),
9916 },
9917
9918 test_validate_config_vector_string => {
9919 input = {
9920 let mut decl = new_component_decl();
9921 decl.config = Some(fdecl::ConfigSchema{
9922 fields: Some(vec![
9923 fdecl::ConfigField {
9924 key: Some("test".to_string()),
9925 type_: Some(fdecl::ConfigType {
9926 layout: fdecl::ConfigTypeLayout::Vector,
9927 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9928 layout: fdecl::ConfigTypeLayout::String,
9929 parameters: Some(vec![]),
9930 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9931 })]),
9932 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9933 }),
9934 ..Default::default()
9935 }
9936 ]),
9937 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9938 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9939 ..Default::default()
9940 });
9941 decl
9942 },
9943 result = Ok(()),
9944 },
9945
9946 test_validate_config_vector_vector => {
9947 input = {
9948 let mut decl = new_component_decl();
9949 decl.config = Some(fdecl::ConfigSchema{
9950 fields: Some(vec![
9951 fdecl::ConfigField {
9952 key: Some("test".to_string()),
9953 type_: Some(fdecl::ConfigType {
9954 layout: fdecl::ConfigTypeLayout::Vector,
9955 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9956 layout: fdecl::ConfigTypeLayout::Vector,
9957 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9958 layout: fdecl::ConfigTypeLayout::String,
9959 parameters: Some(vec![]),
9960 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9961 })]),
9962 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9963 })]),
9964 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9965 }),
9966 ..Default::default()
9967 }
9968 ]),
9969 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9970 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9971 ..Default::default()
9972 });
9973 decl
9974 },
9975 result = Err(ErrorList::new(vec![
9976 Error::nested_vector()
9977 ])),
9978 },
9979
9980 test_validate_exposes_invalid_aggregation_different_availability => {
9981 input = {
9982 let mut decl = new_component_decl();
9983 decl.exposes = Some(vec![
9984 fdecl::Expose::Service(fdecl::ExposeService {
9985 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9986 name: "coll_a".into()
9987 })),
9988 source_name: Some("fuchsia.logger.Log".to_string()),
9989 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
9990 target_name: Some("fuchsia.logger.Log".to_string()),
9991 availability: Some(fdecl::Availability::Required),
9992 ..Default::default()
9993 }),
9994 fdecl::Expose::Service(fdecl::ExposeService {
9995 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9996 name: "coll_b".into()
9997 })),
9998 source_name: Some("fuchsia.logger.Log".to_string()),
9999 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
10000 target_name: Some("fuchsia.logger.Log".to_string()),
10001 availability: Some(fdecl::Availability::Optional),
10002 ..Default::default()
10003 })
10004 ]);
10005 decl.collections = Some(vec![
10006 fdecl::Collection {
10007 name: Some("coll_a".to_string()),
10008 durability: Some(fdecl::Durability::Transient),
10009 ..Default::default()
10010 },
10011 fdecl::Collection {
10012 name: Some("coll_b".to_string()),
10013 durability: Some(fdecl::Durability::Transient),
10014 ..Default::default()
10015 },
10016 ]);
10017 decl
10018 },
10019 result = Err(ErrorList::new(vec![
10020 Error::DifferentAvailabilityInAggregation(AvailabilityList(vec![
10021 fdecl::Availability::Required,
10022 fdecl::Availability::Optional,
10023 ]))
10024 ])),
10025 },
10026
10027 test_validate_offers_invalid_aggregation_different_availability => {
10028 input = {
10029 let mut decl = new_component_decl();
10030 decl.offers = Some(vec![
10031 fdecl::Offer::Service(fdecl::OfferService {
10032 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10033 name: "coll_a".into()
10034 })),
10035 source_name: Some("fuchsia.logger.Log".to_string()),
10036 target: Some(fdecl::Ref::Child(
10037 fdecl::ChildRef {
10038 name: "child_c".to_string(),
10039 collection: None,
10040 }
10041 )),
10042 target_name: Some("fuchsia.logger.Log".to_string()),
10043 source_instance_filter: Some(vec!["default".to_string()]),
10044 availability: Some(fdecl::Availability::Required),
10045 ..Default::default()
10046 }),
10047 fdecl::Offer::Service(fdecl::OfferService {
10048 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10049 name: "coll_b".into()
10050 })),
10051 source_name: Some("fuchsia.logger.Log".to_string()),
10052 target: Some(fdecl::Ref::Child(
10053 fdecl::ChildRef {
10054 name: "child_c".to_string(),
10055 collection: None,
10056 }
10057 )),
10058 target_name: Some("fuchsia.logger.Log".to_string()),
10059 source_instance_filter: Some(vec!["a_different_default".to_string()]),
10060 availability: Some(fdecl::Availability::Optional),
10061 ..Default::default()
10062 })
10063 ]);
10064 decl.collections = Some(vec![
10065 fdecl::Collection {
10066 name: Some("coll_a".to_string()),
10067 durability: Some(fdecl::Durability::Transient),
10068 ..Default::default()
10069 },
10070 fdecl::Collection {
10071 name: Some("coll_b".to_string()),
10072 durability: Some(fdecl::Durability::Transient),
10073 ..Default::default()
10074 },
10075 ]);
10076 decl.children = Some(vec![
10077 fdecl::Child {
10078 name: Some("child_c".to_string()),
10079 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
10080 startup: Some(fdecl::StartupMode::Lazy),
10081 on_terminate: None,
10082 environment: None,
10083 ..Default::default()
10084 },
10085 ]);
10086 decl
10087 },
10088 result = Err(ErrorList::new(vec![
10089 Error::DifferentAvailabilityInAggregation(AvailabilityList(vec![
10090 fdecl::Availability::Required,
10091 fdecl::Availability::Optional,
10092 ]))
10093 ])),
10094 },
10095 }
10096
10097 test_validate_capabilities! {
10098 test_validate_capabilities_individually_ok => {
10099 input = vec![
10100 fdecl::Capability::Protocol(fdecl::Protocol {
10101 name: Some("foo_svc".into()),
10102 source_path: Some("/svc/foo".into()),
10103 ..Default::default()
10104 }),
10105 fdecl::Capability::Directory(fdecl::Directory {
10106 name: Some("foo_dir".into()),
10107 source_path: Some("/foo".into()),
10108 rights: Some(fio::Operations::CONNECT),
10109 ..Default::default()
10110 }),
10111 ],
10112 as_builtin = false,
10113 result = Ok(()),
10114 },
10115 test_validate_capabilities_individually_err => {
10116 input = vec![
10117 fdecl::Capability::Protocol(fdecl::Protocol {
10118 name: None,
10119 source_path: None,
10120 ..Default::default()
10121 }),
10122 fdecl::Capability::Directory(fdecl::Directory {
10123 name: None,
10124 source_path: None,
10125 rights: None,
10126 ..Default::default()
10127 }),
10128 ],
10129 as_builtin = false,
10130 result = Err(ErrorList::new(vec![
10131 Error::missing_field(DeclType::Protocol, "name"),
10132 Error::missing_field(DeclType::Protocol, "source_path"),
10133 Error::missing_field(DeclType::Directory, "name"),
10134 Error::missing_field(DeclType::Directory, "source_path"),
10135 Error::missing_field(DeclType::Directory, "rights"),
10136 ])),
10137 },
10138 test_validate_builtin_capabilities_individually_ok => {
10139 input = vec![
10140 fdecl::Capability::Protocol(fdecl::Protocol {
10141 name: Some("foo_protocol".into()),
10142 source_path: None,
10143 ..Default::default()
10144 }),
10145 fdecl::Capability::Directory(fdecl::Directory {
10146 name: Some("foo_dir".into()),
10147 source_path: None,
10148 rights: Some(fio::Operations::CONNECT),
10149 ..Default::default()
10150 }),
10151 fdecl::Capability::Service(fdecl::Service {
10152 name: Some("foo_svc".into()),
10153 source_path: None,
10154 ..Default::default()
10155 }),
10156 fdecl::Capability::Runner(fdecl::Runner {
10157 name: Some("foo_runner".into()),
10158 source_path: None,
10159 ..Default::default()
10160 }),
10161 fdecl::Capability::Resolver(fdecl::Resolver {
10162 name: Some("foo_resolver".into()),
10163 source_path: None,
10164 ..Default::default()
10165 }),
10166 ],
10167 as_builtin = true,
10168 result = Ok(()),
10169 },
10170 test_validate_builtin_capabilities_individually_err => {
10171 input = vec![
10172 fdecl::Capability::Protocol(fdecl::Protocol {
10173 name: None,
10174 source_path: Some("/svc/foo".into()),
10175 ..Default::default()
10176 }),
10177 fdecl::Capability::Directory(fdecl::Directory {
10178 name: None,
10179 source_path: Some("/foo".into()),
10180 rights: None,
10181 ..Default::default()
10182 }),
10183 fdecl::Capability::Service(fdecl::Service {
10184 name: None,
10185 source_path: Some("/svc/foo".into()),
10186 ..Default::default()
10187 }),
10188 fdecl::Capability::Runner(fdecl::Runner {
10189 name: None,
10190 source_path: Some("/foo".into()),
10191 ..Default::default()
10192 }),
10193 fdecl::Capability::Resolver(fdecl::Resolver {
10194 name: None,
10195 source_path: Some("/foo".into()),
10196 ..Default::default()
10197 }),
10198 fdecl::Capability::Storage(fdecl::Storage {
10199 name: None,
10200 ..Default::default()
10201 }),
10202 ],
10203 as_builtin = true,
10204 result = Err(ErrorList::new(vec![
10205 Error::missing_field(DeclType::Protocol, "name"),
10206 Error::extraneous_source_path(DeclType::Protocol, "/svc/foo"),
10207 Error::missing_field(DeclType::Directory, "name"),
10208 Error::extraneous_source_path(DeclType::Directory, "/foo"),
10209 Error::missing_field(DeclType::Directory, "rights"),
10210 Error::missing_field(DeclType::Service, "name"),
10211 Error::extraneous_source_path(DeclType::Service, "/svc/foo"),
10212 Error::missing_field(DeclType::Runner, "name"),
10213 Error::extraneous_source_path(DeclType::Runner, "/foo"),
10214 Error::missing_field(DeclType::Resolver, "name"),
10215 Error::extraneous_source_path(DeclType::Resolver, "/foo"),
10216 Error::CapabilityCannotBeBuiltin(DeclType::Storage),
10217 ])),
10218 },
10219 test_validate_delivery_type_ok => {
10220 input = vec![
10221 fdecl::Capability::Protocol(fdecl::Protocol {
10222 name: Some("foo_svc1".into()),
10223 source_path: Some("/svc/foo1".into()),
10224 ..Default::default()
10225 }),
10226 fdecl::Capability::Protocol(fdecl::Protocol {
10227 name: Some("foo_svc2".into()),
10228 source_path: Some("/svc/foo2".into()),
10229 delivery: Some(fdecl::DeliveryType::Immediate),
10230 ..Default::default()
10231 }),
10232 fdecl::Capability::Protocol(fdecl::Protocol {
10233 name: Some("foo_svc3".into()),
10234 source_path: Some("/svc/foo3".into()),
10235 delivery: Some(fdecl::DeliveryType::OnReadable),
10236 ..Default::default()
10237 }),
10238 ],
10239 as_builtin = false,
10240 result = Ok(()),
10241 },
10242 test_validate_delivery_type_err => {
10243 input = vec![
10244 fdecl::Capability::Protocol(fdecl::Protocol {
10245 name: Some("foo_svc".into()),
10246 source_path: Some("/svc/foo".into()),
10247 delivery: Some(fdecl::DeliveryType::unknown()),
10248 ..Default::default()
10249 }),
10250 ],
10251 as_builtin = false,
10252 result = Err(ErrorList::new(vec![
10253 Error::invalid_field(DeclType::Protocol, "delivery"),
10254 ])),
10255 },
10256 }
10257
10258 fn generate_expose_different_source_and_availability_decl(
10261 new_expose: impl Fn(fdecl::Ref, fdecl::Availability, &str) -> fdecl::Expose,
10262 ) -> fdecl::Component {
10263 let mut decl = new_component_decl();
10264 let child = "child";
10265 decl.children = Some(vec![fdecl::Child {
10266 name: Some(child.to_string()),
10267 url: Some("fuchsia-pkg://fuchsia.com/source#meta/source.cm".to_string()),
10268 startup: Some(fdecl::StartupMode::Lazy),
10269 ..Default::default()
10270 }]);
10271 decl.exposes = Some(vec![
10272 new_expose(
10274 fdecl::Ref::Self_(fdecl::SelfRef {}),
10275 fdecl::Availability::Optional,
10276 "fuchsia.examples.Echo1",
10277 ),
10278 new_expose(
10280 fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
10281 fdecl::Availability::Optional,
10282 "fuchsia.examples.Echo2",
10283 ),
10284 new_expose(
10286 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10287 fdecl::Availability::Optional,
10288 "fuchsia.examples.Echo3",
10289 ),
10290 new_expose(
10292 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10293 fdecl::Availability::Optional,
10294 "fuchsia.examples.Echo4",
10295 ),
10296 new_expose(
10298 fdecl::Ref::Self_(fdecl::SelfRef {}),
10299 fdecl::Availability::Transitional,
10300 "fuchsia.examples.Echo5",
10301 ),
10302 new_expose(
10304 fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
10305 fdecl::Availability::Transitional,
10306 "fuchsia.examples.Echo6",
10307 ),
10308 new_expose(
10310 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10311 fdecl::Availability::Transitional,
10312 "fuchsia.examples.Echo7",
10313 ),
10314 new_expose(
10316 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10317 fdecl::Availability::Transitional,
10318 "fuchsia.examples.Echo8",
10319 ),
10320 new_expose(
10322 fdecl::Ref::Self_(fdecl::SelfRef {}),
10323 fdecl::Availability::SameAsTarget,
10324 "fuchsia.examples.Echo9",
10325 ),
10326 new_expose(
10328 fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
10329 fdecl::Availability::SameAsTarget,
10330 "fuchsia.examples.Echo10",
10331 ),
10332 new_expose(
10334 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10335 fdecl::Availability::SameAsTarget,
10336 "fuchsia.examples.Echo11",
10337 ),
10338 new_expose(
10340 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10341 fdecl::Availability::Required,
10342 "fuchsia.examples.Echo12",
10343 ),
10344 new_expose(
10346 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10347 fdecl::Availability::SameAsTarget,
10348 "fuchsia.examples.Echo13",
10349 ),
10350 ]);
10351 decl
10352 }
10353
10354 fn generate_offer_different_source_and_availability_decl(
10357 new_offer: impl Fn(fdecl::Ref, fdecl::Availability, &str) -> fdecl::Offer,
10358 ) -> fdecl::Component {
10359 let mut decl = new_component_decl();
10360 decl.children = Some(vec![
10361 fdecl::Child {
10362 name: Some("source".to_string()),
10363 url: Some("fuchsia-pkg://fuchsia.com/source#meta/source.cm".to_string()),
10364 startup: Some(fdecl::StartupMode::Lazy),
10365 on_terminate: None,
10366 environment: None,
10367 ..Default::default()
10368 },
10369 fdecl::Child {
10370 name: Some("sink".to_string()),
10371 url: Some("fuchsia-pkg://fuchsia.com/sink#meta/sink.cm".to_string()),
10372 startup: Some(fdecl::StartupMode::Lazy),
10373 on_terminate: None,
10374 environment: None,
10375 ..Default::default()
10376 },
10377 ]);
10378 decl.offers = Some(vec![
10379 new_offer(
10382 fdecl::Ref::Parent(fdecl::ParentRef {}),
10383 fdecl::Availability::Required,
10384 "fuchsia.examples.Echo0",
10385 ),
10386 new_offer(
10387 fdecl::Ref::Parent(fdecl::ParentRef {}),
10388 fdecl::Availability::Optional,
10389 "fuchsia.examples.Echo1",
10390 ),
10391 new_offer(
10392 fdecl::Ref::Parent(fdecl::ParentRef {}),
10393 fdecl::Availability::SameAsTarget,
10394 "fuchsia.examples.Echo2",
10395 ),
10396 new_offer(
10397 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10398 fdecl::Availability::Optional,
10399 "fuchsia.examples.Echo3",
10400 ),
10401 new_offer(
10404 fdecl::Ref::Self_(fdecl::SelfRef {}),
10405 fdecl::Availability::Optional,
10406 "fuchsia.examples.Echo4",
10407 ),
10408 new_offer(
10409 fdecl::Ref::Self_(fdecl::SelfRef {}),
10410 fdecl::Availability::SameAsTarget,
10411 "fuchsia.examples.Echo5",
10412 ),
10413 new_offer(
10414 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10415 fdecl::Availability::Optional,
10416 "fuchsia.examples.Echo6",
10417 ),
10418 new_offer(
10419 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10420 fdecl::Availability::SameAsTarget,
10421 "fuchsia.examples.Echo7",
10422 ),
10423 new_offer(
10424 fdecl::Ref::Child(fdecl::ChildRef { name: "source".to_string(), collection: None }),
10425 fdecl::Availability::Optional,
10426 "fuchsia.examples.Echo8",
10427 ),
10428 new_offer(
10429 fdecl::Ref::Child(fdecl::ChildRef { name: "source".to_string(), collection: None }),
10430 fdecl::Availability::SameAsTarget,
10431 "fuchsia.examples.Echo9",
10432 ),
10433 new_offer(
10435 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10436 fdecl::Availability::Required,
10437 "fuchsia.examples.Echo10",
10438 ),
10439 new_offer(
10440 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10441 fdecl::Availability::SameAsTarget,
10442 "fuchsia.examples.Echo11",
10443 ),
10444 ]);
10445 decl
10446 }
10447
10448 #[test]
10449 fn test_validate_dynamic_offers_empty() {
10450 assert_eq!(
10451 validate_dynamic_offers(
10452 vec![],
10453 &mut DirectedGraph::new(),
10454 &vec![],
10455 &fdecl::Component::default()
10456 ),
10457 Ok(())
10458 );
10459 }
10460
10461 #[test]
10462 fn test_validate_dynamic_offers_okay() {
10463 assert_eq!(
10464 validate_dynamic_offers(
10465 vec![],
10466 &mut DirectedGraph::new(),
10467 &vec![
10468 fdecl::Offer::Protocol(fdecl::OfferProtocol {
10469 dependency_type: Some(fdecl::DependencyType::Strong),
10470 source: Some(fdecl::Ref::Self_(fdecl::SelfRef)),
10471 source_name: Some("thing".to_string()),
10472 target_name: Some("thing".repeat(26)),
10473 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10474 name: "foo".to_string(),
10475 collection: Some("foo".to_string()),
10476 })),
10477 ..Default::default()
10478 }),
10479 fdecl::Offer::Service(fdecl::OfferService {
10480 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10481 source_name: Some("thang".repeat(26)),
10482 target_name: Some("thang".repeat(26)),
10483 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10484 name: "foo".to_string(),
10485 collection: Some("foo".to_string()),
10486 })),
10487 ..Default::default()
10488 }),
10489 fdecl::Offer::Directory(fdecl::OfferDirectory {
10490 dependency_type: Some(fdecl::DependencyType::Strong),
10491 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10492 source_name: Some("thung1".repeat(26)),
10493 target_name: Some("thung1".repeat(26)),
10494 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10495 name: "foo".to_string(),
10496 collection: Some("foo".to_string()),
10497 })),
10498 ..Default::default()
10499 }),
10500 fdecl::Offer::Storage(fdecl::OfferStorage {
10501 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10502 source_name: Some("thung2".repeat(26)),
10503 target_name: Some("thung2".repeat(26)),
10504 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10505 name: "foo".to_string(),
10506 collection: Some("foo".to_string()),
10507 })),
10508 ..Default::default()
10509 }),
10510 fdecl::Offer::Runner(fdecl::OfferRunner {
10511 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10512 source_name: Some("thung3".repeat(26)),
10513 target_name: Some("thung3".repeat(26)),
10514 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10515 name: "foo".to_string(),
10516 collection: Some("foo".to_string()),
10517 })),
10518 ..Default::default()
10519 }),
10520 fdecl::Offer::Resolver(fdecl::OfferResolver {
10521 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10522 source_name: Some("thung4".repeat(26)),
10523 target_name: Some("thung4".repeat(26)),
10524 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10525 name: "foo".to_string(),
10526 collection: Some("foo".to_string()),
10527 })),
10528 ..Default::default()
10529 }),
10530 ],
10531 &fdecl::Component {
10532 capabilities: Some(vec![fdecl::Capability::Protocol(fdecl::Protocol {
10533 name: Some("thing".to_string()),
10534 source_path: Some("/svc/foo".into()),
10535 ..Default::default()
10536 }),]),
10537 ..Default::default()
10538 }
10539 ),
10540 Ok(())
10541 );
10542 }
10543
10544 #[test]
10545 fn test_validate_dynamic_offers_valid_service_aggregation() {
10546 assert_eq!(
10547 validate_dynamic_offers(
10548 vec![],
10549 &mut DirectedGraph::new(),
10550 &vec![
10551 fdecl::Offer::Service(fdecl::OfferService {
10552 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10553 name: "child_a".to_string(),
10554 collection: None
10555 })),
10556 source_name: Some("fuchsia.logger.Log".to_string()),
10557 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10558 name: "child_c".to_string(),
10559 collection: None,
10560 })),
10561 target_name: Some("fuchsia.logger.Log".to_string()),
10562 source_instance_filter: Some(vec!["default".to_string()]),
10563 ..Default::default()
10564 }),
10565 fdecl::Offer::Service(fdecl::OfferService {
10566 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10567 name: "child_b".to_string(),
10568 collection: None
10569 })),
10570 source_name: Some("fuchsia.logger.Log".to_string()),
10571 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10572 name: "child_c".to_string(),
10573 collection: None,
10574 })),
10575 target_name: Some("fuchsia.logger.Log".to_string()),
10576 source_instance_filter: Some(vec!["a_different_default".to_string()]),
10577 ..Default::default()
10578 })
10579 ],
10580 &fdecl::Component {
10581 children: Some(vec![
10582 fdecl::Child {
10583 name: Some("child_a".to_string()),
10584 url: Some(
10585 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10586 .to_string()
10587 ),
10588 startup: Some(fdecl::StartupMode::Lazy),
10589 on_terminate: None,
10590 environment: None,
10591 ..Default::default()
10592 },
10593 fdecl::Child {
10594 name: Some("child_b".to_string()),
10595 url: Some(
10596 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10597 .to_string()
10598 ),
10599 startup: Some(fdecl::StartupMode::Lazy),
10600 on_terminate: None,
10601 environment: None,
10602 ..Default::default()
10603 },
10604 fdecl::Child {
10605 name: Some("child_c".to_string()),
10606 url: Some(
10607 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10608 .to_string()
10609 ),
10610 startup: Some(fdecl::StartupMode::Lazy),
10611 on_terminate: None,
10612 environment: None,
10613 ..Default::default()
10614 },
10615 ]),
10616 ..Default::default()
10617 }
10618 ),
10619 Ok(())
10620 );
10621 }
10622
10623 #[test]
10624 fn test_validate_dynamic_service_aggregation_missing_filter() {
10625 assert_eq!(
10626 validate_dynamic_offers(
10627 vec![],
10628 &mut DirectedGraph::new(),
10629 &vec![
10630 fdecl::Offer::Service(fdecl::OfferService {
10631 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10632 name: "child_a".to_string(),
10633 collection: None
10634 })),
10635 source_name: Some("fuchsia.logger.Log".to_string()),
10636 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10637 name: "child_c".to_string(),
10638 collection: None,
10639 })),
10640 target_name: Some("fuchsia.logger.Log".to_string()),
10641 source_instance_filter: Some(vec!["default".to_string()]),
10642 ..Default::default()
10643 }),
10644 fdecl::Offer::Service(fdecl::OfferService {
10645 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10646 name: "child_b".to_string(),
10647 collection: None
10648 })),
10649 source_name: Some("fuchsia.logger.Log".to_string()),
10650 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10651 name: "child_c".to_string(),
10652 collection: None,
10653 })),
10654 target_name: Some("fuchsia.logger.Log".to_string()),
10655 source_instance_filter: None,
10656 ..Default::default()
10657 }),
10658 ],
10659 &fdecl::Component {
10660 children: Some(vec![
10661 fdecl::Child {
10662 name: Some("child_a".to_string()),
10663 url: Some(
10664 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10665 .to_string()
10666 ),
10667 startup: Some(fdecl::StartupMode::Lazy),
10668 ..Default::default()
10669 },
10670 fdecl::Child {
10671 name: Some("child_b".to_string()),
10672 url: Some(
10673 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10674 .to_string()
10675 ),
10676 startup: Some(fdecl::StartupMode::Lazy),
10677 ..Default::default()
10678 },
10679 fdecl::Child {
10680 name: Some("child_c".to_string()),
10681 url: Some(
10682 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10683 .to_string()
10684 ),
10685 startup: Some(fdecl::StartupMode::Lazy),
10686 ..Default::default()
10687 },
10688 ]),
10689 ..Default::default()
10690 },
10691 ),
10692 Err(ErrorList::new(vec![Error::invalid_aggregate_offer(
10693 "source_instance_filter must be set for dynamic aggregate service offers"
10694 ),]))
10695 );
10696 }
10697
10698 #[test]
10699 fn test_validate_dynamic_offers_omit_target() {
10700 assert_eq!(
10701 validate_dynamic_offers(
10702 vec![],
10703 &mut DirectedGraph::new(),
10704 &vec![
10705 fdecl::Offer::Protocol(fdecl::OfferProtocol {
10706 dependency_type: Some(fdecl::DependencyType::Strong),
10707 source: Some(fdecl::Ref::Self_(fdecl::SelfRef)),
10708 source_name: Some("thing".to_string()),
10709 target_name: Some("thing".to_string()),
10710 ..Default::default()
10711 }),
10712 fdecl::Offer::Service(fdecl::OfferService {
10713 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10714 source_name: Some("thang".to_string()),
10715 target_name: Some("thang".to_string()),
10716 ..Default::default()
10717 }),
10718 fdecl::Offer::Directory(fdecl::OfferDirectory {
10719 dependency_type: Some(fdecl::DependencyType::Strong),
10720 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10721 source_name: Some("thung1".to_string()),
10722 target_name: Some("thung1".to_string()),
10723 ..Default::default()
10724 }),
10725 fdecl::Offer::Storage(fdecl::OfferStorage {
10726 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10727 source_name: Some("thung2".to_string()),
10728 target_name: Some("thung2".to_string()),
10729 ..Default::default()
10730 }),
10731 fdecl::Offer::Runner(fdecl::OfferRunner {
10732 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10733 source_name: Some("thung3".to_string()),
10734 target_name: Some("thung3".to_string()),
10735 ..Default::default()
10736 }),
10737 fdecl::Offer::Resolver(fdecl::OfferResolver {
10738 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10739 source_name: Some("thung4".to_string()),
10740 target_name: Some("thung4".to_string()),
10741 ..Default::default()
10742 }),
10743 ],
10744 &fdecl::Component {
10745 capabilities: Some(vec![fdecl::Capability::Protocol(fdecl::Protocol {
10746 name: Some("thing".to_string()),
10747 source_path: Some("/svc/foo".into()),
10748 ..Default::default()
10749 }),]),
10750 ..Default::default()
10751 }
10752 ),
10753 Err(ErrorList::new(vec![
10754 Error::missing_field(DeclType::OfferProtocol, "target"),
10755 Error::missing_field(DeclType::OfferService, "target"),
10756 Error::missing_field(DeclType::OfferDirectory, "target"),
10757 Error::missing_field(DeclType::OfferStorage, "target"),
10758 Error::missing_field(DeclType::OfferRunner, "target"),
10759 Error::missing_field(DeclType::OfferResolver, "target"),
10760 ]))
10761 );
10762 }
10763
10764 #[test]
10765 fn test_validate_dynamic_offers_collection_collision() {
10766 assert_eq!(
10767 validate_dynamic_offers(
10768 vec![],
10769 &mut DirectedGraph::new(),
10770 &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10771 dependency_type: Some(fdecl::DependencyType::Strong),
10772 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10773 source_name: Some("thing".to_string()),
10774 target_name: Some("thing".to_string()),
10775 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10776 name: "child".to_string(),
10777 collection: Some("coll".to_string()),
10778 })),
10779 ..Default::default()
10780 }),],
10781 &fdecl::Component {
10782 offers: Some(vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10783 dependency_type: Some(fdecl::DependencyType::Strong),
10784 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10785 source_name: Some("thing".to_string()),
10786 target_name: Some("thing".to_string()),
10787 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10788 name: "coll".into()
10789 })),
10790 ..Default::default()
10791 }),]),
10792 collections: Some(vec![fdecl::Collection {
10793 name: Some("coll".to_string()),
10794 durability: Some(fdecl::Durability::Transient),
10795 ..Default::default()
10796 },]),
10797 ..Default::default()
10798 }
10799 ),
10800 Err(ErrorList::new(vec![Error::duplicate_field(
10801 DeclType::OfferProtocol,
10802 "target_name",
10803 "thing"
10804 ),]))
10805 );
10806 }
10807
10808 #[test]
10809 fn test_validate_dynamic_offers_cycle_collection_to_static_child() {
10810 assert_eq!(
10811 validate_dynamic_offers(
10812 vec![("dyn", "coll")],
10813 &mut DirectedGraph::new(),
10814 &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10815 source_name: Some("bar".to_string()),
10816 target_name: Some("bar".to_string()),
10817 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10818 name: "static_child".into(),
10819 collection: None,
10820 })),
10821 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10822 name: "dyn".to_string(),
10823 collection: Some("coll".to_string()),
10824 })),
10825 dependency_type: Some(fdecl::DependencyType::Strong),
10826 ..Default::default()
10827 }),],
10828 &fdecl::Component {
10829 offers: Some(vec![fdecl::Offer::Service(fdecl::OfferService {
10830 source_name: Some("foo".to_string()),
10831 target_name: Some("foo".to_string()),
10832 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10833 name: "coll".into(),
10834 })),
10835 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10836 name: "static_child".into(),
10837 collection: None,
10838 })),
10839 ..Default::default()
10840 })]),
10841 children: Some(vec![fdecl::Child {
10842 name: Some("static_child".into()),
10843 url: Some("url#child.cm".into()),
10844 startup: Some(fdecl::StartupMode::Lazy),
10845 ..Default::default()
10846 }]),
10847 collections: Some(vec![fdecl::Collection {
10848 name: Some("coll".into()),
10849 durability: Some(fdecl::Durability::Transient),
10850 ..Default::default()
10851 }]),
10852 ..Default::default()
10853 }
10854 ),
10855 Err(ErrorList::new(vec![Error::dependency_cycle(
10856 "{{child coll:dyn -> collection coll -> child static_child -> child coll:dyn}}"
10857 )]))
10858 );
10859 }
10860
10861 #[test]
10862 fn test_validate_dynamic_offers_cycle_collection_to_dynamic_child() {
10863 assert_eq!(
10864 validate_dynamic_offers(
10865 vec![("dyn", "coll1"), ("dyn", "coll2")],
10866 &mut DirectedGraph::new(),
10867 &vec![
10868 fdecl::Offer::Service(fdecl::OfferService {
10869 source_name: Some("foo".to_string()),
10870 target_name: Some("foo".to_string()),
10871 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10872 name: "coll2".into(),
10873 })),
10874 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10875 name: "dyn".into(),
10876 collection: Some("coll1".into()),
10877 })),
10878 ..Default::default()
10879 }),
10880 fdecl::Offer::Protocol(fdecl::OfferProtocol {
10881 source_name: Some("bar".to_string()),
10882 target_name: Some("bar".to_string()),
10883 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10884 name: "dyn".into(),
10885 collection: Some("coll1".into()),
10886 })),
10887 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10888 name: "dyn".to_string(),
10889 collection: Some("coll2".to_string()),
10890 })),
10891 dependency_type: Some(fdecl::DependencyType::Strong),
10892 ..Default::default()
10893 }),
10894 ],
10895 &fdecl::Component {
10896 collections: Some(vec![
10897 fdecl::Collection {
10898 name: Some("coll1".into()),
10899 durability: Some(fdecl::Durability::Transient),
10900 ..Default::default()
10901 },
10902 fdecl::Collection {
10903 name: Some("coll2".into()),
10904 durability: Some(fdecl::Durability::Transient),
10905 ..Default::default()
10906 },
10907 ]),
10908 ..Default::default()
10909 }
10910 ),
10911 Err(ErrorList::new(vec![Error::dependency_cycle(
10912 "{{child coll1:dyn -> child coll2:dyn -> collection coll2 -> child coll1:dyn}}"
10913 )]))
10914 );
10915 }
10916
10917 #[test]
10918 fn test_validate_dynamic_offers_cycle_collection_to_dynamic_child_between_new_and_existing() {
10919 assert_eq!(
10920 validate_dynamic_offers(
10921 vec![("dyn", "coll1"), ("dyn", "coll2")],
10922 &mut DirectedGraph::from([(
10923 DependencyNode::Child("dyn".into(), Some("coll1".into())),
10924 DependencyNode::Child("dyn".into(), Some("coll2".into())),
10925 )]),
10926 &vec![fdecl::Offer::Service(fdecl::OfferService {
10927 source_name: Some("foo".to_string()),
10928 target_name: Some("foo".to_string()),
10929 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10930 name: "coll2".into(),
10931 })),
10932 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10933 name: "dyn".into(),
10934 collection: Some("coll1".into()),
10935 })),
10936 ..Default::default()
10937 }),],
10938 &fdecl::Component {
10939 collections: Some(vec![
10940 fdecl::Collection {
10941 name: Some("coll1".into()),
10942 durability: Some(fdecl::Durability::Transient),
10943 ..Default::default()
10944 },
10945 fdecl::Collection {
10946 name: Some("coll2".into()),
10947 durability: Some(fdecl::Durability::Transient),
10948 ..Default::default()
10949 },
10950 ]),
10951 ..Default::default()
10952 }
10953 ),
10954 Err(ErrorList::new(vec![Error::dependency_cycle(
10955 "{{child coll1:dyn -> child coll2:dyn -> collection coll2 -> child coll1:dyn}}"
10956 )]))
10957 );
10958 }
10959
10960 #[test]
10961 fn test_validate_dynamic_offers_cycle_collection_to_collection() {
10962 assert_eq!(
10963 validate_dynamic_offers(
10964 vec![("dyn", "coll1"), ("dyn", "coll2")],
10965 &mut DirectedGraph::new(),
10966 &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10967 source_name: Some("bar".to_string()),
10968 target_name: Some("bar".to_string()),
10969 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10970 name: "dyn".into(),
10971 collection: Some("coll2".parse().unwrap()),
10972 })),
10973 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10974 name: "dyn".into(),
10975 collection: Some("coll1".parse().unwrap()),
10976 })),
10977 dependency_type: Some(fdecl::DependencyType::Strong),
10978 ..Default::default()
10979 }),],
10980 &fdecl::Component {
10981 offers: Some(vec![fdecl::Offer::Service(fdecl::OfferService {
10982 source_name: Some("foo".to_string()),
10983 target_name: Some("foo".to_string()),
10984 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10985 name: "coll1".into(),
10986 })),
10987 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10988 name: "coll2".into(),
10989 })),
10990 ..Default::default()
10991 })]),
10992 collections: Some(vec![
10993 fdecl::Collection {
10994 name: Some("coll1".into()),
10995 durability: Some(fdecl::Durability::Transient),
10996 ..Default::default()
10997 },
10998 fdecl::Collection {
10999 name: Some("coll2".into()),
11000 durability: Some(fdecl::Durability::Transient),
11001 ..Default::default()
11002 },
11003 ]),
11004 ..Default::default()
11005 }
11006 ),
11007 Err(ErrorList::new(vec![Error::dependency_cycle(
11008 "{{child coll1:dyn -> collection coll1 -> child coll2:dyn -> child coll1:dyn}}",
11009 )]))
11010 );
11011 }
11012
11013 #[test]
11014 fn test_validate_dynamic_child() {
11015 assert_eq!(
11016 Ok(()),
11017 validate_dynamic_child(&fdecl::Child {
11018 name: Some("a".repeat(MAX_LONG_NAME_LENGTH).to_string()),
11019 url: Some("test:///child".to_string()),
11020 startup: Some(fdecl::StartupMode::Lazy),
11021 on_terminate: None,
11022 environment: None,
11023 ..Default::default()
11024 })
11025 );
11026 }
11027
11028 #[test]
11029 fn test_validate_dynamic_child_environment_is_invalid() {
11030 assert_eq!(
11031 validate_dynamic_child(&fdecl::Child {
11032 name: Some("a".repeat(MAX_LONG_NAME_LENGTH).to_string()),
11033 url: Some("test:///child".to_string()),
11034 startup: Some(fdecl::StartupMode::Lazy),
11035 on_terminate: None,
11036 environment: Some("env".to_string()),
11037 ..Default::default()
11038 }),
11039 Err(ErrorList::new(vec![Error::DynamicChildWithEnvironment]))
11040 );
11041 }
11042
11043 #[test]
11044 fn test_validate_dynamic_offers_missing_stuff() {
11045 assert_eq!(
11046 validate_dynamic_offers(
11047 vec![],
11048 &mut DirectedGraph::new(),
11049 &vec![
11050 fdecl::Offer::Protocol(fdecl::OfferProtocol::default()),
11051 fdecl::Offer::Service(fdecl::OfferService::default()),
11052 fdecl::Offer::Directory(fdecl::OfferDirectory::default()),
11053 fdecl::Offer::Storage(fdecl::OfferStorage::default()),
11054 fdecl::Offer::Runner(fdecl::OfferRunner::default()),
11055 fdecl::Offer::Resolver(fdecl::OfferResolver::default()),
11056 ],
11057 &fdecl::Component::default()
11058 ),
11059 Err(ErrorList::new(vec![
11060 Error::missing_field(DeclType::OfferProtocol, "source"),
11061 Error::missing_field(DeclType::OfferProtocol, "source_name"),
11062 Error::missing_field(DeclType::OfferProtocol, "target"),
11063 Error::missing_field(DeclType::OfferProtocol, "target_name"),
11064 Error::missing_field(DeclType::OfferProtocol, "dependency_type"),
11065 Error::missing_field(DeclType::OfferService, "source"),
11066 Error::missing_field(DeclType::OfferService, "source_name"),
11067 Error::missing_field(DeclType::OfferService, "target"),
11068 Error::missing_field(DeclType::OfferService, "target_name"),
11069 Error::missing_field(DeclType::OfferDirectory, "source"),
11070 Error::missing_field(DeclType::OfferDirectory, "source_name"),
11071 Error::missing_field(DeclType::OfferDirectory, "target"),
11072 Error::missing_field(DeclType::OfferDirectory, "target_name"),
11073 Error::missing_field(DeclType::OfferDirectory, "dependency_type"),
11074 Error::missing_field(DeclType::OfferStorage, "source"),
11075 Error::missing_field(DeclType::OfferStorage, "source_name"),
11076 Error::missing_field(DeclType::OfferStorage, "target"),
11077 Error::missing_field(DeclType::OfferStorage, "target_name"),
11078 Error::missing_field(DeclType::OfferRunner, "source"),
11079 Error::missing_field(DeclType::OfferRunner, "source_name"),
11080 Error::missing_field(DeclType::OfferRunner, "target"),
11081 Error::missing_field(DeclType::OfferRunner, "target_name"),
11082 Error::missing_field(DeclType::OfferResolver, "source"),
11083 Error::missing_field(DeclType::OfferResolver, "source_name"),
11084 Error::missing_field(DeclType::OfferResolver, "target"),
11085 Error::missing_field(DeclType::OfferResolver, "target_name"),
11086 ]))
11087 );
11088 }
11089
11090 test_dependency! {
11091 (test_validate_offers_protocol_dependency_cycle) => {
11092 ty = fdecl::Offer::Protocol,
11093 offer_decl = fdecl::OfferProtocol {
11094 source: None, target: None, source_name: Some(format!("thing")),
11097 target_name: Some(format!("thing")),
11098 dependency_type: Some(fdecl::DependencyType::Strong),
11099 ..Default::default()
11100 },
11101 },
11102 (test_validate_offers_directory_dependency_cycle) => {
11103 ty = fdecl::Offer::Directory,
11104 offer_decl = fdecl::OfferDirectory {
11105 source: None, target: None, source_name: Some(format!("thing")),
11108 target_name: Some(format!("thing")),
11109 rights: Some(fio::Operations::CONNECT),
11110 subdir: None,
11111 dependency_type: Some(fdecl::DependencyType::Strong),
11112 ..Default::default()
11113 },
11114 },
11115 (test_validate_offers_service_dependency_cycle) => {
11116 ty = fdecl::Offer::Service,
11117 offer_decl = fdecl::OfferService {
11118 source: None, target: None, source_name: Some(format!("thing")),
11121 target_name: Some(format!("thing")),
11122 ..Default::default()
11123 },
11124 },
11125 (test_validate_offers_runner_dependency_cycle) => {
11126 ty = fdecl::Offer::Runner,
11127 offer_decl = fdecl::OfferRunner {
11128 source: None, target: None, source_name: Some(format!("thing")),
11131 target_name: Some(format!("thing")),
11132 ..Default::default()
11133 },
11134 },
11135 (test_validate_offers_resolver_dependency_cycle) => {
11136 ty = fdecl::Offer::Resolver,
11137 offer_decl = fdecl::OfferResolver {
11138 source: None, target: None, source_name: Some(format!("thing")),
11141 target_name: Some(format!("thing")),
11142 ..Default::default()
11143 },
11144 },
11145 }
11146 test_weak_dependency! {
11147 (test_validate_offers_protocol_weak_dependency_cycle) => {
11148 ty = fdecl::Offer::Protocol,
11149 offer_decl = fdecl::OfferProtocol {
11150 source: None, target: None, source_name: Some(format!("thing")),
11153 target_name: Some(format!("thing")),
11154 dependency_type: None, ..Default::default()
11156 },
11157 },
11158 (test_validate_offers_directory_weak_dependency_cycle) => {
11159 ty = fdecl::Offer::Directory,
11160 offer_decl = fdecl::OfferDirectory {
11161 source: None, target: None, source_name: Some(format!("thing")),
11164 target_name: Some(format!("thing")),
11165 rights: Some(fio::Operations::CONNECT),
11166 subdir: None,
11167 dependency_type: None, ..Default::default()
11169 },
11170 },
11171 (test_validate_offers_service_weak_dependency_cycle) => {
11172 ty = fdecl::Offer::Service,
11173 offer_decl = fdecl::OfferService {
11174 source: None, target: None, source_name: Some(format!("thing")),
11177 target_name: Some(format!("thing")),
11178 dependency_type: None, ..Default::default()
11180 },
11181 },
11182 }
11183}