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 = "NEXT"))]
746 let has_numbered_handle = false;
747 #[cfg(fuchsia_api_level_at_least = "NEXT")]
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 = "NEXT")]
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 #[derive(Debug, PartialEq, Clone, Copy)]
932 struct PathCapability<'a> {
933 decl: DeclType,
934 dir: &'a Path,
935 use_: &'a fdecl::Use,
936 }
937 let mut used_paths = HashMap::new();
938 for use_ in uses.iter() {
939 let (capability, path) = match use_ {
940 fdecl::Use::Service(fdecl::UseService { target_path: Some(path), .. }) => {
941 let dir = match Path::new(path).parent() {
942 Some(p) => p,
943 None => continue, };
945 (PathCapability { decl: DeclType::UseService, dir, use_ }, path)
946 }
947 fdecl::Use::Protocol(fdecl::UseProtocol { target_path: Some(path), .. }) => {
948 let dir = match Path::new(path).parent() {
949 Some(p) => p,
950 None => continue, };
952 (PathCapability { decl: DeclType::UseProtocol, dir, use_ }, path)
953 }
954 fdecl::Use::Directory(fdecl::UseDirectory { target_path: Some(path), .. }) => (
955 PathCapability { decl: DeclType::UseDirectory, dir: Path::new(path), use_ },
956 path,
957 ),
958 fdecl::Use::Storage(fdecl::UseStorage { target_path: Some(path), .. }) => (
959 PathCapability { decl: DeclType::UseStorage, dir: Path::new(path), use_ },
960 path,
961 ),
962 #[cfg(fuchsia_api_level_at_least = "NEXT")]
963 fdecl::Use::Dictionary(fdecl::UseDictionary {
964 target_path: Some(path), ..
965 }) => (
966 PathCapability { decl: DeclType::UseDictionary, dir: Path::new(path), use_ },
967 path,
968 ),
969 _ => continue,
970 };
971 if used_paths.insert(path, capability).is_some() {
972 self.errors.push(Error::duplicate_field(capability.decl, "target_path", path));
974 }
975 }
976 for ((&path_a, capability_a), (&path_b, capability_b)) in
977 used_paths.iter().tuple_combinations()
978 {
979 if match (capability_a.use_, capability_b.use_) {
980 (fdecl::Use::Directory(_), fdecl::Use::Directory(_))
982 | (fdecl::Use::Storage(_), fdecl::Use::Directory(_))
983 | (fdecl::Use::Directory(_), fdecl::Use::Storage(_))
984 | (fdecl::Use::Storage(_), fdecl::Use::Storage(_)) => {
985 capability_b.dir == capability_a.dir
986 || capability_b.dir.starts_with(capability_a.dir)
987 || capability_a.dir.starts_with(capability_b.dir)
988 }
989
990 (_, fdecl::Use::Directory(_)) | (fdecl::Use::Directory(_), _) => {
992 capability_b.dir == capability_a.dir
993 || capability_b.dir.starts_with(capability_a.dir)
994 || capability_a.dir.starts_with(capability_b.dir)
995 }
996
997 #[cfg(fuchsia_api_level_at_least = "NEXT")]
1000 (fdecl::Use::Dictionary(_), _) => {
1001 capability_b.dir != capability_a.dir
1002 && capability_b.dir.starts_with(capability_a.dir)
1003 }
1004 #[cfg(fuchsia_api_level_at_least = "NEXT")]
1005 (_, fdecl::Use::Dictionary(_)) => {
1006 capability_b.dir != capability_a.dir
1007 && capability_a.dir.starts_with(capability_b.dir)
1008 }
1009
1010 (_, _) => {
1013 capability_b.dir != capability_a.dir
1014 && (capability_b.dir.starts_with(capability_a.dir)
1015 || capability_a.dir.starts_with(capability_b.dir))
1016 }
1017 } {
1018 self.errors.push(Error::invalid_path_overlap(
1019 capability_a.decl,
1020 path_a,
1021 capability_b.decl,
1022 path_b,
1023 ));
1024 }
1025 }
1026 for (used_path, capability) in used_paths.iter() {
1027 if used_path.as_str() == "/pkg" || used_path.starts_with("/pkg/") {
1028 self.errors.push(Error::pkg_path_overlap(capability.decl, *used_path));
1029 }
1030 }
1031 }
1032
1033 fn validate_use_fields(
1034 &mut self,
1035 decl: DeclType,
1036 capability_checker: impl Fn(&Self) -> &dyn Container,
1040 source: Option<&'a fdecl::Ref>,
1041 source_name: Option<&'a String>,
1042 source_dictionary: Option<&'a String>,
1043 target_path: Option<&'a String>,
1044 dependency_type: Option<&fdecl::DependencyType>,
1045 availability: Option<&'a fdecl::Availability>,
1046 ) {
1047 self.validate_use_source(decl, source, source_dictionary);
1048
1049 check_name(source_name, decl, "source_name", &mut self.errors);
1050 if source_dictionary.is_some() {
1051 check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
1052 }
1053 match decl {
1054 DeclType::UseRunner | DeclType::UseConfiguration => {}
1055 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1056 DeclType::UseProtocol => {
1057 if target_path.is_some() {
1058 check_path(target_path, decl, "target_path", &mut self.errors);
1059 } else {
1060 }
1062 }
1063 _ => {
1064 check_path(target_path, decl, "target_path", &mut self.errors);
1065 }
1066 }
1067 check_use_availability(decl, availability, &mut self.errors);
1068
1069 let is_use_from_child = match source {
1071 Some(fdecl::Ref::Child(_)) => true,
1072 _ => false,
1073 };
1074 match (is_use_from_child, dependency_type) {
1075 (false, Some(fdecl::DependencyType::Weak)) => {
1076 self.errors.push(Error::invalid_field(decl, "dependency_type"));
1077 }
1078 _ => {}
1079 }
1080
1081 self.validate_route_from_self(
1082 decl,
1083 source,
1084 source_name,
1085 source_dictionary,
1086 capability_checker,
1087 );
1088 }
1089
1090 fn validate_use_source(
1091 &mut self,
1092 decl: DeclType,
1093 source: Option<&'a fdecl::Ref>,
1094 source_dictionary: Option<&'a String>,
1095 ) {
1096 match (source, source_dictionary) {
1097 (Some(fdecl::Ref::Parent(_)), _) => {}
1099 (Some(fdecl::Ref::Self_(_)), _) => {}
1100 (Some(fdecl::Ref::Child(child)), _) => {
1101 self.validate_child_ref(decl, "source", &child, OfferType::Static);
1102 return;
1103 }
1104 (Some(fdecl::Ref::Framework(_)), None) => {}
1106 (Some(fdecl::Ref::Debug(_)), None) => {}
1107 (Some(fdecl::Ref::Capability(c)), None) => {
1108 self.validate_source_capability(&c, decl, "source");
1109 return;
1110 }
1111 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1112 (Some(fdecl::Ref::Environment(_)), None) => {}
1113 (Some(fdecl::Ref::Collection(collection)), None) if decl == DeclType::UseService => {
1114 self.validate_collection_ref(decl, "source", &collection);
1115 return;
1116 }
1117 (None, _) => self.errors.push(Error::missing_field(decl, "source")),
1119 (_, _) => self.errors.push(Error::invalid_field(decl, "source")),
1121 }
1122 }
1123
1124 fn validate_child_decl(&mut self, child: &'a fdecl::Child) {
1125 if let Err(mut e) = validate_child(child, check_name) {
1126 self.errors.append(&mut e.errs);
1127 }
1128 if let Some(name) = child.name.as_ref() {
1129 let name: &str = name;
1130 if self.all_children.insert(name, child).is_some() {
1131 self.errors.push(Error::duplicate_field(DeclType::Child, "name", name));
1132 }
1133 }
1134 if let Some(environment) = child.environment.as_ref() {
1135 if !self.all_environment_names.contains(environment.as_str()) {
1136 self.errors.push(Error::invalid_environment(
1137 DeclType::Child,
1138 "environment",
1139 environment,
1140 ));
1141 }
1142 }
1143 }
1144
1145 fn validate_collection_decl(&mut self, collection: &'a fdecl::Collection) {
1146 let name = collection.name.as_ref();
1147 if check_name(name, DeclType::Collection, "name", &mut self.errors) {
1148 let name: &str = name.unwrap();
1149 if !self.all_collections.insert(name) {
1150 self.errors.push(Error::duplicate_field(DeclType::Collection, "name", name));
1151 }
1152 }
1153 if collection.durability.is_none() {
1154 self.errors.push(Error::missing_field(DeclType::Collection, "durability"));
1155 }
1156 if let Some(environment) = collection.environment.as_ref() {
1157 if !self.all_environment_names.contains(environment.as_str()) {
1158 self.errors.push(Error::invalid_environment(
1159 DeclType::Collection,
1160 "environment",
1161 environment,
1162 ));
1163 }
1164 }
1165 }
1167
1168 fn validate_environment_decl(&mut self, environment: &'a fdecl::Environment) {
1169 let name = environment.name.as_ref();
1170 check_name(name, DeclType::Environment, "name", &mut self.errors);
1171 if environment.extends.is_none() {
1172 self.errors.push(Error::missing_field(DeclType::Environment, "extends"));
1173 }
1174 if let Some(runners) = environment.runners.as_ref() {
1175 let mut registered_runners = HashSet::new();
1176 for runner in runners {
1177 self.validate_runner_registration(runner, &mut registered_runners);
1178 }
1179 }
1180 if let Some(resolvers) = environment.resolvers.as_ref() {
1181 let mut registered_schemes = HashSet::new();
1182 for resolver in resolvers {
1183 self.validate_resolver_registration(resolver, &mut registered_schemes);
1184 }
1185 }
1186
1187 match environment.extends.as_ref() {
1188 Some(fdecl::EnvironmentExtends::None) => {
1189 if environment.stop_timeout_ms.is_none() {
1190 self.errors
1191 .push(Error::missing_field(DeclType::Environment, "stop_timeout_ms"));
1192 }
1193 }
1194 None | Some(fdecl::EnvironmentExtends::Realm) => {}
1195 }
1196
1197 if let Some(debugs) = environment.debug_capabilities.as_ref() {
1198 for debug in debugs {
1199 self.validate_environment_debug_registration(debug);
1200 }
1201 }
1202 }
1203
1204 fn validate_runner_registration(
1205 &mut self,
1206 runner_registration: &'a fdecl::RunnerRegistration,
1207 runner_names: &mut HashSet<&'a str>,
1208 ) {
1209 check_name(
1210 runner_registration.source_name.as_ref(),
1211 DeclType::RunnerRegistration,
1212 "source_name",
1213 &mut self.errors,
1214 );
1215 self.validate_registration_source(
1216 runner_registration.source.as_ref(),
1217 DeclType::RunnerRegistration,
1218 );
1219 if let (Some(fdecl::Ref::Self_(_)), Some(name)) =
1221 (&runner_registration.source, runner_registration.source_name.as_ref())
1222 {
1223 if !self.all_runners.contains(name as &str) {
1224 self.errors.push(Error::invalid_runner(
1225 DeclType::RunnerRegistration,
1226 "source_name",
1227 name,
1228 ));
1229 }
1230 }
1231
1232 check_name(
1233 runner_registration.target_name.as_ref(),
1234 DeclType::RunnerRegistration,
1235 "target_name",
1236 &mut self.errors,
1237 );
1238 if let Some(name) = runner_registration.target_name.as_ref() {
1239 if !runner_names.insert(name.as_str()) {
1240 self.errors.push(Error::duplicate_field(
1241 DeclType::RunnerRegistration,
1242 "target_name",
1243 name,
1244 ));
1245 }
1246 }
1247 }
1248
1249 fn validate_resolver_registration(
1250 &mut self,
1251 resolver_registration: &'a fdecl::ResolverRegistration,
1252 schemes: &mut HashSet<&'a str>,
1253 ) {
1254 check_name(
1255 resolver_registration.resolver.as_ref(),
1256 DeclType::ResolverRegistration,
1257 "resolver",
1258 &mut self.errors,
1259 );
1260 self.validate_registration_source(
1261 resolver_registration.source.as_ref(),
1262 DeclType::ResolverRegistration,
1263 );
1264 check_url_scheme(
1265 resolver_registration.scheme.as_ref(),
1266 DeclType::ResolverRegistration,
1267 "scheme",
1268 &mut self.errors,
1269 );
1270 if let Some(scheme) = resolver_registration.scheme.as_ref() {
1271 if !schemes.insert(scheme.as_str()) {
1272 self.errors.push(Error::duplicate_field(
1273 DeclType::ResolverRegistration,
1274 "scheme",
1275 scheme,
1276 ));
1277 }
1278 }
1279 }
1280
1281 fn validate_registration_source(&mut self, source: Option<&'a fdecl::Ref>, ty: DeclType) {
1282 match source {
1283 Some(fdecl::Ref::Parent(_)) => {}
1284 Some(fdecl::Ref::Self_(_)) => {}
1285 Some(fdecl::Ref::Child(child_ref)) => {
1286 self.validate_child_ref(ty, "source", &child_ref, OfferType::Static);
1288 }
1289 Some(_) => {
1290 self.errors.push(Error::invalid_field(ty, "source"));
1291 }
1292 None => {
1293 self.errors.push(Error::missing_field(ty, "source"));
1294 }
1295 }
1296 }
1297
1298 fn validate_service_decl(&mut self, service: &'a fdecl::Service, as_builtin: bool) {
1299 if check_name(service.name.as_ref(), DeclType::Service, "name", &mut self.errors) {
1300 let name = service.name.as_ref().unwrap();
1301 if !self.all_capability_ids.insert(name) {
1302 self.errors.push(Error::duplicate_field(DeclType::Service, "name", name.as_str()));
1303 }
1304 self.all_services.insert(name);
1305 }
1306 match as_builtin {
1307 true => {
1308 if let Some(path) = service.source_path.as_ref() {
1309 self.errors.push(Error::extraneous_source_path(DeclType::Service, path))
1310 }
1311 }
1312 false => {
1313 check_path(
1314 service.source_path.as_ref(),
1315 DeclType::Service,
1316 "source_path",
1317 &mut self.errors,
1318 );
1319 }
1320 }
1321 }
1322
1323 fn validate_protocol_decl(&mut self, protocol: &'a fdecl::Protocol, as_builtin: bool) {
1324 if check_name(protocol.name.as_ref(), DeclType::Protocol, "name", &mut self.errors) {
1325 let name = protocol.name.as_ref().unwrap();
1326 if !self.all_capability_ids.insert(name) {
1327 self.errors.push(Error::duplicate_field(DeclType::Protocol, "name", name.as_str()));
1328 }
1329 self.all_protocols.insert(name);
1330 }
1331 match as_builtin {
1332 true => {
1333 if let Some(path) = protocol.source_path.as_ref() {
1334 self.errors.push(Error::extraneous_source_path(DeclType::Protocol, path))
1335 }
1336 }
1337 false => {
1338 check_path(
1339 protocol.source_path.as_ref(),
1340 DeclType::Protocol,
1341 "source_path",
1342 &mut self.errors,
1343 );
1344 }
1345 }
1346
1347 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1348 match protocol.delivery {
1349 Some(delivery) => match cm_types::DeliveryType::try_from(delivery) {
1350 Ok(_) => {}
1351 Err(_) => self.errors.push(Error::invalid_field(DeclType::Protocol, "delivery")),
1352 },
1353 None => {}
1354 }
1355 }
1356
1357 fn validate_directory_decl(&mut self, directory: &'a fdecl::Directory, as_builtin: bool) {
1358 if check_name(directory.name.as_ref(), DeclType::Directory, "name", &mut self.errors) {
1359 let name = directory.name.as_ref().unwrap();
1360 if !self.all_capability_ids.insert(name) {
1361 self.errors.push(Error::duplicate_field(
1362 DeclType::Directory,
1363 "name",
1364 name.as_str(),
1365 ));
1366 }
1367 self.all_directories.insert(name);
1368 }
1369 match as_builtin {
1370 true => {
1371 if let Some(path) = directory.source_path.as_ref() {
1372 self.errors.push(Error::extraneous_source_path(DeclType::Directory, path))
1373 }
1374 }
1375 false => {
1376 check_path(
1377 directory.source_path.as_ref(),
1378 DeclType::Directory,
1379 "source_path",
1380 &mut self.errors,
1381 );
1382 }
1383 }
1384 if directory.rights.is_none() {
1385 self.errors.push(Error::missing_field(DeclType::Directory, "rights"));
1386 }
1387 }
1388
1389 fn validate_storage_decl(&mut self, storage: &'a fdecl::Storage) {
1390 match storage.source.as_ref() {
1391 Some(fdecl::Ref::Parent(_)) => {}
1392 Some(fdecl::Ref::Self_(_)) => {}
1393 Some(fdecl::Ref::Child(child)) => {
1394 let _ =
1395 self.validate_child_ref(DeclType::Storage, "source", &child, OfferType::Static);
1396 }
1397 Some(_) => {
1398 self.errors.push(Error::invalid_field(DeclType::Storage, "source"));
1399 }
1400 None => {
1401 self.errors.push(Error::missing_field(DeclType::Storage, "source"));
1402 }
1403 };
1404 if check_name(storage.name.as_ref(), DeclType::Storage, "name", &mut self.errors) {
1405 let name = storage.name.as_ref().unwrap();
1406 if !self.all_capability_ids.insert(name) {
1407 self.errors.push(Error::duplicate_field(DeclType::Storage, "name", name.as_str()));
1408 }
1409 self.all_storages.insert(name, storage.source.as_ref());
1410 }
1411 if storage.storage_id.is_none() {
1412 self.errors.push(Error::missing_field(DeclType::Storage, "storage_id"));
1413 }
1414 check_name(
1415 storage.backing_dir.as_ref(),
1416 DeclType::Storage,
1417 "backing_dir",
1418 &mut self.errors,
1419 );
1420 }
1421
1422 fn validate_runner_decl(&mut self, runner: &'a fdecl::Runner, as_builtin: bool) {
1423 if check_name(runner.name.as_ref(), DeclType::Runner, "name", &mut self.errors) {
1424 let name = runner.name.as_ref().unwrap();
1425 if !self.all_capability_ids.insert(name) {
1426 self.errors.push(Error::duplicate_field(DeclType::Runner, "name", name.as_str()));
1427 }
1428 self.all_runners.insert(name);
1429 }
1430 match as_builtin {
1431 true => {
1432 if let Some(path) = runner.source_path.as_ref() {
1433 self.errors.push(Error::extraneous_source_path(DeclType::Runner, path))
1434 }
1435 }
1436 false => {
1437 check_path(
1438 runner.source_path.as_ref(),
1439 DeclType::Runner,
1440 "source_path",
1441 &mut self.errors,
1442 );
1443 }
1444 }
1445 }
1446
1447 fn validate_resolver_decl(&mut self, resolver: &'a fdecl::Resolver, as_builtin: bool) {
1448 if check_name(resolver.name.as_ref(), DeclType::Resolver, "name", &mut self.errors) {
1449 let name = resolver.name.as_ref().unwrap();
1450 if !self.all_capability_ids.insert(name) {
1451 self.errors.push(Error::duplicate_field(DeclType::Resolver, "name", name.as_str()));
1452 }
1453 self.all_resolvers.insert(name);
1454 }
1455 match as_builtin {
1456 true => {
1457 if let Some(path) = resolver.source_path.as_ref() {
1458 self.errors.push(Error::extraneous_source_path(DeclType::Resolver, path))
1459 }
1460 }
1461 false => {
1462 check_path(
1463 resolver.source_path.as_ref(),
1464 DeclType::Resolver,
1465 "source_path",
1466 &mut self.errors,
1467 );
1468 }
1469 }
1470 }
1471
1472 fn load_dictionary_names(&mut self, dictionaries: impl Iterator<Item = &'a fdecl::Dictionary>) {
1476 for dictionary in dictionaries {
1477 let decl = DeclType::Dictionary;
1478 if check_name(dictionary.name.as_ref(), decl, "name", &mut self.errors) {
1479 let name = dictionary.name.as_ref().unwrap();
1480 if !self.all_capability_ids.insert(name) {
1481 self.errors.push(Error::duplicate_field(decl, "name", name.as_str()));
1482 }
1483 self.all_dictionaries.insert(name, &dictionary);
1484 }
1485 }
1486 }
1487
1488 fn validate_dictionary_decl(&mut self, dictionary: &'a fdecl::Dictionary) {
1489 let decl = DeclType::Dictionary;
1490 if let Some(path) = dictionary.source_path.as_ref() {
1491 if dictionary.source.is_some() {
1492 self.errors.push(Error::extraneous_field(decl, "source"));
1493 }
1494 check_path(Some(path), DeclType::Dictionary, "source_path", &mut self.errors);
1495 }
1496 }
1497
1498 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1499 fn validate_configuration_decl(&mut self, config: &'a fdecl::Configuration) {
1500 let decl = DeclType::Configuration;
1501 if check_name(config.name.as_ref(), decl, "name", &mut self.errors) {
1502 let name = config.name.as_ref().unwrap();
1503 if !self.all_capability_ids.insert(name) {
1504 self.errors.push(Error::duplicate_field(decl, "name", name.as_str()));
1505 }
1506 self.all_configs.insert(name);
1507 }
1508 }
1509
1510 fn validate_environment_debug_registration(&mut self, debug: &'a fdecl::DebugRegistration) {
1511 match debug {
1512 fdecl::DebugRegistration::Protocol(o) => {
1513 let decl = DeclType::DebugProtocolRegistration;
1514 self.validate_environment_debug_fields(
1515 decl,
1516 o.source.as_ref(),
1517 o.source_name.as_ref(),
1518 o.target_name.as_ref(),
1519 );
1520
1521 if let (Some(fdecl::Ref::Self_(_)), Some(name)) =
1522 (&o.source, o.source_name.as_ref())
1523 {
1524 if !self.all_protocols.contains(&name as &str) {
1525 self.errors.push(Error::invalid_field(decl, "source"));
1526 }
1527 }
1528 }
1529 _ => {
1530 self.errors.push(Error::invalid_field(DeclType::Environment, "debug"));
1531 }
1532 }
1533 }
1534
1535 fn validate_environment_debug_fields(
1536 &mut self,
1537 decl: DeclType,
1538 source: Option<&fdecl::Ref>,
1539 source_name: Option<&String>,
1540 target_name: Option<&'a String>,
1541 ) {
1542 match source {
1544 Some(fdecl::Ref::Parent(_)) => {}
1545 Some(fdecl::Ref::Self_(_)) => {}
1546 Some(fdecl::Ref::Framework(_)) => {}
1547 Some(fdecl::Ref::Child(child)) => {
1548 let _ = self.validate_child_ref(decl, "source", &child, OfferType::Static);
1549 }
1550 Some(_) => self.errors.push(Error::invalid_field(decl, "source")),
1551 None => self.errors.push(Error::missing_field(decl, "source")),
1552 }
1553 check_name(source_name, decl, "source_name", &mut self.errors);
1554 check_name(target_name, decl, "target_name", &mut self.errors);
1555 }
1556
1557 fn validate_event_stream_decl(&mut self, event: &'a fdecl::EventStream) {
1558 if check_name(event.name.as_ref(), DeclType::EventStream, "name", &mut self.errors) {
1559 let name = event.name.as_ref().unwrap();
1560 if !self.all_capability_ids.insert(name) {
1561 self.errors.push(Error::duplicate_field(
1562 DeclType::EventStream,
1563 "name",
1564 name.as_str(),
1565 ));
1566 }
1567 }
1568 }
1569
1570 fn validate_source_collection(
1571 &mut self,
1572 collection: &fdecl::CollectionRef,
1573 decl_type: DeclType,
1574 ) -> bool {
1575 let num_errors = self.errors.len();
1576 if check_name(Some(&collection.name), decl_type, "source.collection.name", &mut self.errors)
1577 && !self.all_collections.contains(&collection.name as &str)
1578 {
1579 self.errors.push(Error::invalid_collection(
1580 decl_type,
1581 "source",
1582 &collection.name as &str,
1583 ));
1584 }
1585 num_errors == self.errors.len()
1586 }
1587
1588 fn validate_filtered_service_fields(
1589 &mut self,
1590 decl_type: DeclType,
1591 source_instance_filter: Option<&Vec<String>>,
1592 renamed_instances: Option<&Vec<fdecl::NameMapping>>,
1593 ) {
1594 if let Some(source_instance_filter) = source_instance_filter {
1595 if source_instance_filter.is_empty() {
1596 self.errors.push(Error::invalid_field(decl_type, "source_instance_filter"));
1599 }
1600 for name in source_instance_filter {
1601 check_name(Some(name), decl_type, "source_instance_filter", &mut self.errors);
1602 }
1603 }
1604 if let Some(renamed_instances) = renamed_instances {
1605 let mut seen_target_names = HashSet::<String>::new();
1607 for mapping in renamed_instances {
1608 check_name(
1609 Some(&mapping.source_name),
1610 decl_type,
1611 "renamed_instances.source_name",
1612 &mut self.errors,
1613 );
1614 check_name(
1615 Some(&mapping.target_name),
1616 decl_type,
1617 "renamed_instances.target_name",
1618 &mut self.errors,
1619 );
1620 if !seen_target_names.insert(mapping.target_name.clone()) {
1621 self.errors.push(Error::invalid_field(decl_type, "renamed_instances"));
1622 break;
1623 }
1624 }
1625 }
1626 }
1627
1628 fn validate_source_capability(
1629 &mut self,
1630 capability: &fdecl::CapabilityRef,
1631 decl_type: DeclType,
1632 field: &str,
1633 ) -> bool {
1634 let num_errors = self.errors.len();
1635 if check_name(Some(&capability.name), decl_type, "source.capability.name", &mut self.errors)
1636 && !self.all_capability_ids.contains(capability.name.as_str())
1637 {
1638 self.errors.push(Error::invalid_capability(decl_type, field, &capability.name));
1639 }
1640 num_errors == self.errors.len()
1641 }
1642
1643 fn make_group_key(
1647 target_name: Option<&'a String>,
1648 target: Option<&'a fdecl::Ref>,
1649 ) -> Option<(&'a str, RefKey<'a>)> {
1650 if target_name.is_none() {
1651 return None;
1652 }
1653 let target_name = target_name.unwrap().as_str();
1654 if target.is_none() {
1655 return None;
1656 }
1657 let target = match target.unwrap() {
1658 fdecl::Ref::Parent(_) => RefKey::Parent,
1659 fdecl::Ref::Self_(_) => RefKey::Self_,
1660 fdecl::Ref::Child(r) => RefKey::Child(r.name.as_str()),
1661 fdecl::Ref::Collection(r) => RefKey::Collection(r.name.as_str()),
1662 fdecl::Ref::Framework(_) => RefKey::Framework,
1663 fdecl::Ref::Capability(_) => RefKey::Capability,
1664 fdecl::Ref::Debug(_) => RefKey::Debug,
1665 fdecl::RefUnknown!() => {
1666 return None;
1667 }
1668 };
1669 Some((target_name, target))
1670 }
1671
1672 fn validate_aggregation_has_same_availability(
1673 &mut self,
1674 route_group: &Vec<impl HasAvailability>,
1675 ) {
1676 let availability_of_sources: BTreeSet<_> =
1678 route_group.iter().map(|r| r.availability()).collect();
1679
1680 if availability_of_sources.len() > 1 {
1682 self.errors.push(Error::different_availability_in_aggregation(
1683 availability_of_sources.into_iter().collect(),
1684 ));
1685 }
1686 }
1687
1688 fn validate_expose_group(&mut self, exposes: &'a [fdecl::Expose]) {
1691 let mut expose_groups: HashMap<_, Vec<fdecl::ExposeService>> = HashMap::new();
1692 let service_exposes = exposes
1693 .into_iter()
1694 .filter_map(|o| if let fdecl::Expose::Service(s) = o { Some(s) } else { None });
1695 for expose in service_exposes {
1696 let key = Self::make_group_key(expose.target_name.as_ref(), expose.target.as_ref());
1697 if let Some(key) = key {
1698 expose_groups.entry(key).or_insert_with(|| vec![]).push(expose.clone());
1699 }
1700 }
1701 for expose_group in expose_groups.into_values() {
1702 if expose_group.len() == 1 {
1703 continue;
1706 }
1707
1708 self.validate_aggregation_has_same_availability(&expose_group);
1709 }
1710 }
1711
1712 fn validate_expose_decl(
1713 &mut self,
1714 expose: &'a fdecl::Expose,
1715 expose_to_parent_ids: &mut HashMap<&'a str, AllowableIds>,
1716 expose_to_framework_ids: &mut HashMap<&'a str, AllowableIds>,
1717 ) {
1718 match expose {
1719 fdecl::Expose::Service(e) => {
1720 let decl = DeclType::ExposeService;
1721 self.validate_expose_fields(
1722 decl,
1723 AllowableIds::Many,
1724 CollectionSource::Allow,
1725 Self::service_checker,
1726 e.source.as_ref(),
1727 e.source_name.as_ref(),
1728 e.source_dictionary.as_ref(),
1729 e.target.as_ref(),
1730 e.target_name.as_ref(),
1731 e.availability.as_ref(),
1732 expose_to_parent_ids,
1733 expose_to_framework_ids,
1734 );
1735 }
1736 fdecl::Expose::Protocol(e) => {
1737 let decl = DeclType::ExposeProtocol;
1738 self.validate_expose_fields(
1739 decl,
1740 AllowableIds::One,
1741 CollectionSource::Deny,
1742 Self::protocol_checker,
1743 e.source.as_ref(),
1744 e.source_name.as_ref(),
1745 e.source_dictionary.as_ref(),
1746 e.target.as_ref(),
1747 e.target_name.as_ref(),
1748 e.availability.as_ref(),
1749 expose_to_parent_ids,
1750 expose_to_framework_ids,
1751 );
1752 }
1753 fdecl::Expose::Directory(e) => {
1754 let decl = DeclType::ExposeDirectory;
1755 self.validate_expose_fields(
1756 decl,
1757 AllowableIds::One,
1758 CollectionSource::Deny,
1759 Self::directory_checker,
1760 e.source.as_ref(),
1761 e.source_name.as_ref(),
1762 e.source_dictionary.as_ref(),
1763 e.target.as_ref(),
1764 e.target_name.as_ref(),
1765 e.availability.as_ref(),
1766 expose_to_parent_ids,
1767 expose_to_framework_ids,
1768 );
1769
1770 match e.target.as_ref() {
1773 Some(fdecl::Ref::Framework(_)) => {
1774 if e.subdir.is_some() {
1775 self.errors.push(Error::invalid_field(decl, "subdir"));
1776 }
1777 }
1778 _ => {}
1779 }
1780
1781 if let Some(subdir) = e.subdir.as_ref() {
1782 check_relative_path(Some(subdir), decl, "subdir", &mut self.errors);
1783 }
1784 }
1785 fdecl::Expose::Runner(e) => {
1786 let decl = DeclType::ExposeRunner;
1787 self.validate_expose_fields(
1788 decl,
1789 AllowableIds::One,
1790 CollectionSource::Deny,
1791 Self::runner_checker,
1792 e.source.as_ref(),
1793 e.source_name.as_ref(),
1794 e.source_dictionary.as_ref(),
1795 e.target.as_ref(),
1796 e.target_name.as_ref(),
1797 Some(&fdecl::Availability::Required),
1798 expose_to_parent_ids,
1799 expose_to_framework_ids,
1800 );
1801 }
1802 fdecl::Expose::Resolver(e) => {
1803 let decl = DeclType::ExposeResolver;
1804 self.validate_expose_fields(
1805 decl,
1806 AllowableIds::One,
1807 CollectionSource::Deny,
1808 Self::resolver_checker,
1809 e.source.as_ref(),
1810 e.source_name.as_ref(),
1811 e.source_dictionary.as_ref(),
1812 e.target.as_ref(),
1813 e.target_name.as_ref(),
1814 Some(&fdecl::Availability::Required),
1815 expose_to_parent_ids,
1816 expose_to_framework_ids,
1817 );
1818 }
1819 fdecl::Expose::Dictionary(e) => {
1820 let decl = DeclType::ExposeDictionary;
1821 self.validate_expose_fields(
1822 decl,
1823 AllowableIds::One,
1824 CollectionSource::Deny,
1825 Self::dictionary_checker,
1826 e.source.as_ref(),
1827 e.source_name.as_ref(),
1828 e.source_dictionary.as_ref(),
1829 e.target.as_ref(),
1830 e.target_name.as_ref(),
1831 e.availability.as_ref(),
1832 expose_to_parent_ids,
1833 expose_to_framework_ids,
1834 );
1835 }
1836 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1837 fdecl::Expose::Config(e) => {
1838 let decl = DeclType::ExposeConfig;
1839 self.validate_expose_fields(
1840 decl,
1841 AllowableIds::One,
1842 CollectionSource::Deny,
1843 Self::config_checker,
1844 e.source.as_ref(),
1845 e.source_name.as_ref(),
1846 None,
1847 e.target.as_ref(),
1848 e.target_name.as_ref(),
1849 e.availability.as_ref(),
1850 expose_to_parent_ids,
1851 expose_to_framework_ids,
1852 );
1853 }
1854 _ => {
1855 self.errors.push(Error::invalid_field(DeclType::Component, "expose"));
1856 }
1857 }
1858 }
1859
1860 fn validate_expose_fields(
1861 &mut self,
1862 decl: DeclType,
1863 allowable_ids: AllowableIds,
1864 collection_source: CollectionSource,
1865 capability_checker: impl Fn(&Self) -> &dyn Container,
1869 source: Option<&fdecl::Ref>,
1870 source_name: Option<&String>,
1871 source_dictionary: Option<&String>,
1872 target: Option<&fdecl::Ref>,
1873 target_name: Option<&'a String>,
1874 availability: Option<&fdecl::Availability>,
1875 expose_to_parent_ids: &mut HashMap<&'a str, AllowableIds>,
1876 expose_to_framework_ids: &mut HashMap<&'a str, AllowableIds>,
1877 ) {
1878 self.validate_expose_source(decl, collection_source, source, source_dictionary);
1879 check_route_availability(decl, availability, source, source_name, &mut self.errors);
1880 match target {
1881 Some(r) => match r {
1882 fdecl::Ref::Parent(_) => {}
1883 fdecl::Ref::Framework(_) => {}
1884 _ => {
1885 self.errors.push(Error::invalid_field(decl, "target"));
1886 }
1887 },
1888 None => {
1889 self.errors.push(Error::missing_field(decl, "target"));
1890 }
1891 }
1892 check_name(source_name, decl, "source_name", &mut self.errors);
1893 if source_dictionary.is_some() {
1894 check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
1895 }
1896 if check_name(target_name, decl, "target_name", &mut self.errors) {
1897 let maybe_ids_set = match target {
1898 Some(fdecl::Ref::Parent(_)) => Some(expose_to_parent_ids),
1899 Some(fdecl::Ref::Framework(_)) => Some(expose_to_framework_ids),
1900 _ => None,
1901 };
1902 if let Some(ids_set) = maybe_ids_set {
1903 let target_name = target_name.unwrap();
1904 if let Some(prev_state) = ids_set.insert(target_name, allowable_ids) {
1905 if prev_state == AllowableIds::One || prev_state != allowable_ids {
1906 self.errors.push(Error::duplicate_field(decl, "target_name", target_name));
1907 }
1908 }
1909 }
1910 }
1911
1912 self.validate_route_from_self(
1913 decl,
1914 source,
1915 source_name,
1916 source_dictionary,
1917 capability_checker,
1918 );
1919 }
1920
1921 fn validate_expose_source(
1922 &mut self,
1923 decl: DeclType,
1924 collection_source: CollectionSource,
1925 source: Option<&fdecl::Ref>,
1926 source_dictionary: Option<&String>,
1927 ) {
1928 match (source, source_dictionary) {
1929 (Some(fdecl::Ref::Self_(_)), _) => {}
1931 (Some(fdecl::Ref::Child(child)), _) => {
1932 let _ = self.validate_child_ref(decl, "source", &child, OfferType::Static);
1933 }
1934 (Some(fdecl::Ref::VoidType(_)), None) => {}
1936 (Some(fdecl::Ref::Framework(_)), None) => {}
1937 (Some(fdecl::Ref::Capability(c)), None) => {
1938 self.validate_source_capability(c, decl, "source");
1939 }
1940 (Some(fdecl::Ref::Collection(c)), None)
1941 if collection_source == CollectionSource::Allow =>
1942 {
1943 self.validate_source_collection(c, decl);
1944 }
1945 (None, _) => {
1947 self.errors.push(Error::missing_field(decl, "source"));
1948 }
1949 (_, _) => {
1951 self.errors.push(Error::invalid_field(decl, "source"));
1952 }
1953 }
1954 }
1955
1956 fn validate_offer_group(&mut self, offers: &'a [fdecl::Offer], offer_type: OfferType) {
1959 let mut offer_groups: HashMap<_, Vec<fdecl::OfferService>> = HashMap::new();
1960 let service_offers = offers
1961 .into_iter()
1962 .filter_map(|o| if let fdecl::Offer::Service(s) = o { Some(s) } else { None });
1963 for offer in service_offers {
1964 let key = Self::make_group_key(offer.target_name.as_ref(), offer.target.as_ref());
1965 if let Some(key) = key {
1966 offer_groups.entry(key).or_insert_with(|| vec![]).push(offer.clone());
1967 }
1968 }
1969 for offer_group in offer_groups.into_values() {
1970 if offer_group.len() == 1 {
1971 continue;
1974 }
1975
1976 self.validate_aggregation_has_same_availability(&offer_group);
1977
1978 let mut source_instance_filter_entries: HashSet<String> = HashSet::new();
1979 let mut service_source_names: HashSet<String> = HashSet::new();
1980 for o in offer_group {
1981 match (o.source_instance_filter, offer_type) {
1983 (Some(source_instance_filter), _) => {
1984 for instance_name in source_instance_filter {
1985 if !source_instance_filter_entries.insert(instance_name.clone()) {
1986 self.errors.push(Error::invalid_aggregate_offer(format!(
1989 "Conflicting source_instance_filter in aggregate service \
1990 offer, instance_name '{}' seen in filter lists multiple times",
1991 instance_name,
1992 )));
1993 }
1994 }
1995 }
1996 (None, OfferType::Static) => {}
1997 (None, OfferType::Dynamic) => {
1998 self.errors.push(Error::invalid_aggregate_offer(
2000 "source_instance_filter must be set for dynamic aggregate service \
2001 offers",
2002 ));
2003 }
2004 }
2005 service_source_names.insert(
2006 o.source_name
2007 .expect("Offer Service declarations must always contain source_name"),
2008 );
2009 }
2010
2011 if service_source_names.len() > 1 {
2012 self.errors.push(Error::invalid_aggregate_offer(format!(
2013 "All aggregate service offers must have the same source_name, saw {}. Use \
2014 renamed_instances to rename instance names to avoid conflict.",
2015 service_source_names.into_iter().sorted().collect::<Vec<String>>().join(", ")
2016 )));
2017 }
2018 }
2019 }
2020
2021 fn validate_offer_decl(&mut self, offer: &'a fdecl::Offer, offer_type: OfferType) {
2022 match offer {
2023 fdecl::Offer::Service(o) => {
2024 let decl = DeclType::OfferService;
2025 self.validate_offer_fields(
2026 decl,
2027 AllowableIds::Many,
2028 CollectionSource::Allow,
2029 Self::service_checker,
2030 o.source.as_ref(),
2031 o.source_name.as_ref(),
2032 o.source_dictionary.as_ref(),
2033 o.target.as_ref(),
2034 o.target_name.as_ref(),
2035 #[cfg(fuchsia_api_level_at_least = "HEAD")]
2036 Some(o.dependency_type.as_ref().unwrap_or(&fdecl::DependencyType::Strong)),
2037 #[cfg(fuchsia_api_level_less_than = "HEAD")]
2038 Some(&fdecl::DependencyType::Strong),
2039 o.availability.as_ref(),
2040 offer_type,
2041 );
2042 self.validate_filtered_service_fields(
2043 decl,
2044 o.source_instance_filter.as_ref(),
2045 o.renamed_instances.as_ref(),
2046 );
2047 }
2048 fdecl::Offer::Protocol(o) => {
2049 let decl = DeclType::OfferProtocol;
2050 self.validate_offer_fields(
2051 decl,
2052 AllowableIds::One,
2053 CollectionSource::Deny,
2054 Self::protocol_checker,
2055 o.source.as_ref(),
2056 o.source_name.as_ref(),
2057 o.source_dictionary.as_ref(),
2058 o.target.as_ref(),
2059 o.target_name.as_ref(),
2060 o.dependency_type.as_ref(),
2061 o.availability.as_ref(),
2062 offer_type,
2063 );
2064 }
2065 fdecl::Offer::Directory(o) => {
2066 let decl = DeclType::OfferDirectory;
2067 self.validate_offer_fields(
2068 decl,
2069 AllowableIds::One,
2070 CollectionSource::Deny,
2071 Self::directory_checker,
2072 o.source.as_ref(),
2073 o.source_name.as_ref(),
2074 o.source_dictionary.as_ref(),
2075 o.target.as_ref(),
2076 o.target_name.as_ref(),
2077 o.dependency_type.as_ref(),
2078 o.availability.as_ref(),
2079 offer_type,
2080 );
2081 if let Some(subdir) = o.subdir.as_ref() {
2082 check_relative_path(
2083 Some(subdir),
2084 DeclType::OfferDirectory,
2085 "subdir",
2086 &mut self.errors,
2087 );
2088 }
2089 }
2090 fdecl::Offer::Storage(o) => {
2091 let decl = DeclType::OfferStorage;
2092 self.validate_storage_offer_fields(
2093 decl,
2094 Self::storage_checker,
2095 o.source.as_ref(),
2096 o.source_name.as_ref(),
2097 o.target.as_ref(),
2098 o.target_name.as_ref(),
2099 o.availability.as_ref(),
2100 offer_type,
2101 );
2102 }
2103 fdecl::Offer::Runner(o) => {
2104 let decl = DeclType::OfferRunner;
2105 self.validate_offer_fields(
2106 decl,
2107 AllowableIds::One,
2108 CollectionSource::Deny,
2109 Self::runner_checker,
2110 o.source.as_ref(),
2111 o.source_name.as_ref(),
2112 o.source_dictionary.as_ref(),
2113 o.target.as_ref(),
2114 o.target_name.as_ref(),
2115 Some(&fdecl::DependencyType::Strong),
2116 Some(&fdecl::Availability::Required),
2117 offer_type,
2118 );
2119 }
2120 fdecl::Offer::Resolver(o) => {
2121 let decl = DeclType::OfferResolver;
2122 self.validate_offer_fields(
2123 decl,
2124 AllowableIds::One,
2125 CollectionSource::Deny,
2126 Self::resolver_checker,
2127 o.source.as_ref(),
2128 o.source_name.as_ref(),
2129 o.source_dictionary.as_ref(),
2130 o.target.as_ref(),
2131 o.target_name.as_ref(),
2132 Some(&fdecl::DependencyType::Strong),
2133 Some(&fdecl::Availability::Required),
2134 offer_type,
2135 );
2136 }
2137 fdecl::Offer::EventStream(e) => {
2138 self.validate_event_stream_offer_fields(e, offer_type);
2139 }
2140 fdecl::Offer::Dictionary(o) => {
2141 let decl = DeclType::OfferDictionary;
2142 self.validate_offer_fields(
2143 decl,
2144 AllowableIds::One,
2145 CollectionSource::Deny,
2146 Self::dictionary_checker,
2147 o.source.as_ref(),
2148 o.source_name.as_ref(),
2149 o.source_dictionary.as_ref(),
2150 o.target.as_ref(),
2151 o.target_name.as_ref(),
2152 o.dependency_type.as_ref(),
2153 o.availability.as_ref(),
2154 offer_type,
2155 );
2156 }
2157 #[cfg(fuchsia_api_level_at_least = "HEAD")]
2158 fdecl::Offer::Config(o) => {
2159 let decl = DeclType::OfferConfig;
2160 self.validate_offer_fields(
2161 decl,
2162 AllowableIds::One,
2163 CollectionSource::Deny,
2164 Self::config_checker,
2165 o.source.as_ref(),
2166 o.source_name.as_ref(),
2167 None,
2168 o.target.as_ref(),
2169 o.target_name.as_ref(),
2170 Some(&fdecl::DependencyType::Strong),
2171 o.availability.as_ref(),
2172 offer_type,
2173 );
2174 }
2175 fdecl::OfferUnknown!() => {
2176 self.errors.push(Error::invalid_field(DeclType::Component, "offer"));
2177 }
2178 }
2179 }
2180
2181 fn validate_offer_fields(
2182 &mut self,
2183 decl: DeclType,
2184 allowable_names: AllowableIds,
2185 collection_source: CollectionSource,
2186 capability_checker: impl Fn(&Self) -> &dyn Container,
2187 source: Option<&'a fdecl::Ref>,
2188 source_name: Option<&'a String>,
2189 source_dictionary: Option<&'a String>,
2190 target: Option<&'a fdecl::Ref>,
2191 target_name: Option<&'a String>,
2192 dependency_type: Option<&'a fdecl::DependencyType>,
2193 availability: Option<&'a fdecl::Availability>,
2194 offer_type: OfferType,
2195 ) {
2196 self.validate_offer_source(decl, collection_source, source, source_dictionary, offer_type);
2197 check_route_availability(decl, availability, source, source_name, &mut self.errors);
2198 check_offer_name(source_name, decl, "source_name", offer_type, &mut self.errors);
2199 if source_dictionary.is_some() {
2200 check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
2201 }
2202 self.validate_offer_target(decl, allowable_names, target, target_name, offer_type);
2203 check_offer_name(target_name, decl, "target_name", offer_type, &mut self.errors);
2204
2205 if dependency_type.is_none() {
2206 self.errors.push(Error::missing_field(decl, "dependency_type"));
2207 }
2208
2209 self.validate_route_from_self(
2210 decl,
2211 source,
2212 source_name,
2213 source_dictionary,
2214 capability_checker,
2215 );
2216 }
2217
2218 fn validate_offer_source(
2219 &mut self,
2220 decl: DeclType,
2221 collection_source: CollectionSource,
2222 source: Option<&'a fdecl::Ref>,
2223 source_dictionary: Option<&'a String>,
2224 offer_type: OfferType,
2225 ) {
2226 match (source, source_dictionary) {
2227 (Some(fdecl::Ref::Parent(_)), _) => {}
2229 (Some(fdecl::Ref::Self_(_)), _) => {}
2230 (Some(fdecl::Ref::Child(child)), _) => {
2231 self.validate_child_ref(decl, "source", &child, offer_type);
2232 }
2233 (Some(fdecl::Ref::VoidType(_)), None) => {}
2235 (Some(fdecl::Ref::Framework(_)), None) => {}
2236 (Some(fdecl::Ref::Capability(c)), None) => {
2237 self.validate_source_capability(c, decl, "source");
2238 }
2239 (Some(fdecl::Ref::Collection(c)), None)
2240 if collection_source == CollectionSource::Allow =>
2241 {
2242 self.validate_source_collection(c, decl);
2243 }
2244 (None, _) => self.errors.push(Error::missing_field(decl, "source")),
2246 (_, _) => self.errors.push(Error::invalid_field(decl, "source")),
2248 }
2249 }
2250
2251 fn validate_storage_offer_fields(
2252 &mut self,
2253 decl: DeclType,
2254 capability_checker: impl Fn(&Self) -> &dyn Container,
2258 source: Option<&'a fdecl::Ref>,
2259 source_name: Option<&'a String>,
2260 target: Option<&'a fdecl::Ref>,
2261 target_name: Option<&'a String>,
2262 availability: Option<&fdecl::Availability>,
2263 offer_type: OfferType,
2264 ) {
2265 match source {
2266 Some(fdecl::Ref::Parent(_) | fdecl::Ref::VoidType(_) | fdecl::Ref::Self_(_)) => {}
2267 Some(_) => {
2268 self.errors.push(Error::invalid_field(decl, "source"));
2269 }
2270 None => {
2271 self.errors.push(Error::missing_field(decl, "source"));
2272 }
2273 }
2274 check_route_availability(decl, availability, source, source_name, &mut self.errors);
2275 check_offer_name(source_name, decl, "source_name", offer_type, &mut self.errors);
2276 self.validate_offer_target(decl, AllowableIds::One, target, target_name, offer_type);
2277 check_offer_name(target_name, decl, "target_name", offer_type, &mut self.errors);
2278
2279 if let (Some(fdecl::Ref::Self_(_)), Some(name)) = (source, source_name) {
2280 if !(capability_checker)(self).contains(name) {
2281 self.errors.push(Error::invalid_capability(decl, "source", name));
2282 }
2283 }
2284 }
2285
2286 fn validate_event_stream_offer_fields(
2287 &mut self,
2288 event_stream: &'a fdecl::OfferEventStream,
2289 offer_type: OfferType,
2290 ) {
2291 let decl = DeclType::OfferEventStream;
2292 check_name(event_stream.source_name.as_ref(), decl, "source_name", &mut self.errors);
2293 if event_stream.target == Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})) {
2294 self.errors.push(Error::invalid_field(DeclType::OfferEventStream, "target"));
2296 }
2297 if let Some(scope) = &event_stream.scope {
2298 if scope.is_empty() {
2299 self.errors.push(Error::invalid_field(decl, "scope"));
2300 }
2301 for value in scope {
2302 match value {
2303 fdecl::Ref::Child(child) => {
2304 self.validate_child_ref(
2305 DeclType::OfferEventStream,
2306 "scope",
2307 &child,
2308 offer_type,
2309 );
2310 }
2311 fdecl::Ref::Collection(collection) => {
2312 self.validate_collection_ref(
2313 DeclType::OfferEventStream,
2314 "scope",
2315 &collection,
2316 );
2317 }
2318 _ => {
2319 self.errors.push(Error::invalid_field(DeclType::OfferEventStream, "scope"));
2320 }
2321 }
2322 }
2323 }
2324 match event_stream.source {
2326 Some(
2327 fdecl::Ref::Parent(_)
2328 | fdecl::Ref::Framework(_)
2329 | fdecl::Ref::Child(_)
2330 | fdecl::Ref::VoidType(_),
2331 ) => {}
2332 Some(_) => {
2333 self.errors.push(Error::invalid_field(decl, "source"));
2334 }
2335 None => {
2336 self.errors.push(Error::missing_field(decl, "source"));
2337 }
2338 };
2339
2340 check_route_availability(
2341 decl,
2342 event_stream.availability.as_ref(),
2343 event_stream.source.as_ref(),
2344 event_stream.source_name.as_ref(),
2345 &mut self.errors,
2346 );
2347
2348 self.validate_offer_target(
2349 decl,
2350 AllowableIds::One,
2351 event_stream.target.as_ref(),
2352 event_stream.target_name.as_ref(),
2353 offer_type,
2354 );
2355 check_name(event_stream.target_name.as_ref(), decl, "target_name", &mut self.errors);
2356 }
2357
2358 fn validate_child_ref(
2360 &mut self,
2361 decl: DeclType,
2362 field_name: &str,
2363 child: &fdecl::ChildRef,
2364 offer_type: OfferType,
2365 ) -> bool {
2366 if offer_type == OfferType::Dynamic && child.collection.is_some() {
2367 return self.validate_dynamic_child_ref(decl, field_name, child);
2368 }
2369 let mut valid = true;
2373 if !check_name(
2374 Some(&child.name),
2375 decl,
2376 &format!("{}.child.name", field_name),
2377 &mut self.errors,
2378 ) {
2379 valid = false;
2380 }
2381 if child.collection.is_some() {
2382 self.errors
2383 .push(Error::extraneous_field(decl, format!("{}.child.collection", field_name)));
2384 valid = false;
2385 }
2386 if !valid {
2387 return false;
2388 }
2389
2390 let name: &str = &child.name;
2392 if !self.all_children.contains_key(name) {
2393 self.errors.push(Error::invalid_child(decl, field_name, name));
2394 return false;
2395 }
2396
2397 true
2398 }
2399
2400 fn validate_dynamic_child_ref(
2405 &mut self,
2406 decl: DeclType,
2407 field_name: &str,
2408 child: &fdecl::ChildRef,
2409 ) -> bool {
2410 let mut valid = true;
2414 if !check_dynamic_name(
2415 Some(&child.name),
2416 decl,
2417 &format!("{}.child.name", field_name),
2418 &mut self.errors,
2419 ) {
2420 valid = false;
2421 }
2422 if !check_name(
2423 child.collection.as_ref(),
2424 decl,
2425 &format!("{}.child.collection", field_name),
2426 &mut self.errors,
2427 ) {
2428 valid = false;
2429 }
2430 valid
2431 }
2432
2433 fn validate_collection_ref(
2435 &mut self,
2436 decl: DeclType,
2437 field_name: &str,
2438 collection: &fdecl::CollectionRef,
2439 ) -> bool {
2440 if !check_name(
2442 Some(&collection.name),
2443 decl,
2444 &format!("{}.collection.name", field_name),
2445 &mut self.errors,
2446 ) {
2447 return false;
2448 }
2449
2450 if !self.all_collections.contains(&collection.name as &str) {
2452 self.errors.push(Error::invalid_collection(decl, field_name, &collection.name as &str));
2453 return false;
2454 }
2455
2456 true
2457 }
2458
2459 fn validate_offer_target(
2460 &mut self,
2461 decl: DeclType,
2462 allowable_names: AllowableIds,
2463 target: Option<&'a fdecl::Ref>,
2464 target_name: Option<&'a String>,
2465 offer_type: OfferType,
2466 ) {
2467 match target {
2468 Some(fdecl::Ref::Child(c)) => {
2469 self.validate_target_child(decl, allowable_names, c, target_name, offer_type);
2470 }
2471 Some(fdecl::Ref::Collection(c)) => {
2472 self.validate_target_collection(decl, allowable_names, c, target_name);
2473 }
2474 Some(fdecl::Ref::Capability(c)) => {
2475 if let Some(d) = self.all_dictionaries.get(&c.name.as_str()) {
2477 if d.source_path.is_some() {
2478 self.errors.push(Error::invalid_field(decl, "target"));
2481 }
2482 } else {
2483 self.errors.push(Error::invalid_field(decl, "target"));
2484 }
2485 }
2486 Some(_) => {
2487 self.errors.push(Error::invalid_field(decl, "target"));
2488 }
2489 None => {
2490 self.errors.push(Error::missing_field(decl, "target"));
2491 }
2492 }
2493 }
2494
2495 fn validate_target_child(
2496 &mut self,
2497 decl: DeclType,
2498 allowable_names: AllowableIds,
2499 child: &'a fdecl::ChildRef,
2500 target_name: Option<&'a String>,
2501 offer_type: OfferType,
2502 ) {
2503 if !self.validate_child_ref(decl, "target", child, offer_type) {
2504 return;
2505 }
2506 if let Some(target_name) = target_name {
2507 let names_for_target = self
2508 .target_ids
2509 .entry(TargetId::Component(
2510 &child.name,
2511 child.collection.as_ref().map(|s| s.as_str()),
2512 ))
2513 .or_insert(HashMap::new());
2514 if let Some(prev_state) = names_for_target.insert(target_name, allowable_names) {
2515 if prev_state == AllowableIds::One || prev_state != allowable_names {
2516 self.errors.push(Error::duplicate_field(
2517 decl,
2518 "target_name",
2519 target_name as &str,
2520 ));
2521 }
2522 }
2523 if let Some(collection) = child.collection.as_ref() {
2524 if let Some(names_for_target) =
2525 self.target_ids.get(&TargetId::Collection(&collection))
2526 {
2527 if names_for_target.contains_key(&target_name.as_str()) {
2528 self.errors.push(Error::duplicate_field(
2530 decl,
2531 "target_name",
2532 target_name as &str,
2533 ));
2534 }
2535 }
2536 }
2537 }
2538 }
2539
2540 fn validate_target_collection(
2541 &mut self,
2542 decl: DeclType,
2543 allowable_names: AllowableIds,
2544 collection: &'a fdecl::CollectionRef,
2545 target_name: Option<&'a String>,
2546 ) {
2547 if !self.validate_collection_ref(decl, "target", &collection) {
2548 return;
2549 }
2550 if let Some(target_name) = target_name {
2551 let names_for_target = self
2552 .target_ids
2553 .entry(TargetId::Collection(&collection.name))
2554 .or_insert(HashMap::new());
2555 if let Some(prev_state) = names_for_target.insert(target_name, allowable_names) {
2556 if prev_state == AllowableIds::One || prev_state != allowable_names {
2557 self.errors.push(Error::duplicate_field(
2558 decl,
2559 "target_name",
2560 target_name as &str,
2561 ));
2562 }
2563 }
2564 }
2565 }
2566
2567 fn validate_route_from_self(
2568 &mut self,
2569 decl: DeclType,
2570 source: Option<&fdecl::Ref>,
2571 source_name: Option<&String>,
2572 source_dictionary: Option<&String>,
2573 capability_checker: impl Fn(&Self) -> &dyn Container,
2574 ) {
2575 let (Some(fdecl::Ref::Self_(_)), Some(name)) = (source, source_name) else {
2576 return;
2577 };
2578 match source_dictionary {
2579 Some(source_dictionary) => {
2580 if let Ok(path) = cm_types::RelativePath::new(source_dictionary) {
2581 if let Some(first_segment) = path.iter_segments().next().map(|s| s.as_str()) {
2582 if !self.all_dictionaries.contains_key(first_segment) {
2583 self.errors.push(Error::invalid_capability(
2584 decl,
2585 "source",
2586 first_segment,
2587 ));
2588 }
2589 }
2590 }
2591 }
2592 None => {
2593 if !(capability_checker)(self).contains(name) {
2594 self.errors.push(Error::invalid_capability(decl, "source", name));
2595 }
2596 }
2597 }
2598 }
2599
2600 fn service_checker(&self) -> &dyn Container {
2603 &self.all_services
2604 }
2605 fn protocol_checker(&self) -> &dyn Container {
2606 &self.all_protocols
2607 }
2608 fn directory_checker(&self) -> &dyn Container {
2609 &self.all_directories
2610 }
2611 fn runner_checker(&self) -> &dyn Container {
2612 &self.all_runners
2613 }
2614 fn resolver_checker(&self) -> &dyn Container {
2615 &self.all_resolvers
2616 }
2617
2618 fn dictionary_checker(&self) -> &dyn Container {
2619 &self.all_dictionaries
2620 }
2621
2622 #[cfg(fuchsia_api_level_at_least = "HEAD")]
2623 fn config_checker(&self) -> &dyn Container {
2624 &self.all_configs
2625 }
2626 fn storage_checker(&self) -> &dyn Container {
2627 &self.all_storages
2628 }
2629 fn event_stream_checker(&self) -> &dyn Container {
2630 struct AlwaysTrueContainer {}
2634 impl Container for AlwaysTrueContainer {
2635 fn contains(&self, _key: &str) -> bool {
2636 true
2637 }
2638 }
2639 static CONTAINER: AlwaysTrueContainer = AlwaysTrueContainer {};
2640 &CONTAINER
2641 }
2642}
2643
2644#[cfg(test)]
2645mod tests {
2646 use super::*;
2647 use cm_types::MAX_LONG_NAME_LENGTH;
2648 use test_case::test_case;
2649 use {
2650 fidl_fuchsia_component_decl as fdecl, fidl_fuchsia_data as fdata, fidl_fuchsia_io as fio,
2651 };
2652
2653 macro_rules! test_validate {
2654 (
2655 $(
2656 $test_name:ident => {
2657 input = $input:expr,
2658 result = $result:expr,
2659 },
2660 )+
2661 ) => {
2662 $(
2663 #[test]
2664 fn $test_name() {
2665 validate_test($input, $result);
2666 }
2667 )+
2668 }
2669 }
2670
2671 macro_rules! test_validate_any_result {
2672 (
2673 $(
2674 $test_name:ident => {
2675 input = $input:expr,
2676 results = $results:expr,
2677 },
2678 )+
2679 ) => {
2680 $(
2681 #[test]
2682 fn $test_name() {
2683 validate_test_any_result($input, $results);
2684 }
2685 )+
2686 }
2687 }
2688
2689 macro_rules! test_validate_values_data {
2690 (
2691 $(
2692 $test_name:ident => {
2693 input = $input:expr,
2694 result = $result:expr,
2695 },
2696 )+
2697 ) => {
2698 $(
2699 #[test]
2700 fn $test_name() {
2701 validate_values_data_test($input, $result);
2702 }
2703 )+
2704 }
2705 }
2706
2707 macro_rules! test_validate_capabilities {
2708 (
2709 $(
2710 $test_name:ident => {
2711 input = $input:expr,
2712 as_builtin = $as_builtin:expr,
2713 result = $result:expr,
2714 },
2715 )+
2716 ) => {
2717 $(
2718 #[test]
2719 fn $test_name() {
2720 validate_capabilities_test($input, $as_builtin, $result);
2721 }
2722 )+
2723 }
2724 }
2725
2726 macro_rules! test_dependency {
2727 (
2728 $(
2729 ($test_name:ident) => {
2730 ty = $ty:expr,
2731 offer_decl = $offer_decl:expr,
2732 },
2733 )+
2734 ) => {
2735 $(
2736 #[test]
2737 fn $test_name() {
2738 let mut decl = new_component_decl();
2739 let dependencies = vec![
2740 ("a", "b"),
2741 ("b", "a"),
2742 ];
2743 let offers = dependencies.into_iter().map(|(from,to)| {
2744 let mut offer_decl = $offer_decl;
2745 offer_decl.source = Some(fdecl::Ref::Child(
2746 fdecl::ChildRef { name: from.to_string(), collection: None },
2747 ));
2748 offer_decl.target = Some(fdecl::Ref::Child(
2749 fdecl::ChildRef { name: to.to_string(), collection: None },
2750 ));
2751 $ty(offer_decl)
2752 }).collect();
2753 let children = ["a", "b"].iter().map(|name| {
2754 fdecl::Child {
2755 name: Some(name.to_string()),
2756 url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
2757 startup: Some(fdecl::StartupMode::Lazy),
2758 on_terminate: None,
2759 environment: None,
2760 ..Default::default()
2761 }
2762 }).collect();
2763 decl.offers = Some(offers);
2764 decl.children = Some(children);
2765 let result = Err(ErrorList::new(vec![
2766 Error::dependency_cycle("{{child a -> child b -> child a}}")
2767 ]));
2768 validate_test(decl, result);
2769 }
2770 )+
2771 }
2772 }
2773
2774 macro_rules! test_weak_dependency {
2775 (
2776 $(
2777 ($test_name:ident) => {
2778 ty = $ty:expr,
2779 offer_decl = $offer_decl:expr,
2780 },
2781 )+
2782 ) => {
2783 $(
2784 #[test_case(fdecl::DependencyType::Weak)]
2785 fn $test_name(weak_dep: fdecl::DependencyType) {
2786 let mut decl = new_component_decl();
2787 let offers = vec![
2788 {
2789 let mut offer_decl = $offer_decl;
2790 offer_decl.source = Some(fdecl::Ref::Child(
2791 fdecl::ChildRef { name: "a".to_string(), collection: None },
2792 ));
2793 offer_decl.target = Some(fdecl::Ref::Child(
2794 fdecl::ChildRef { name: "b".to_string(), collection: None },
2795 ));
2796 offer_decl.dependency_type = Some(fdecl::DependencyType::Strong);
2797 $ty(offer_decl)
2798 },
2799 {
2800 let mut offer_decl = $offer_decl;
2801 offer_decl.source = Some(fdecl::Ref::Child(
2802 fdecl::ChildRef { name: "b".to_string(), collection: None },
2803 ));
2804 offer_decl.target = Some(fdecl::Ref::Child(
2805 fdecl::ChildRef { name: "a".to_string(), collection: None },
2806 ));
2807 offer_decl.dependency_type = Some(weak_dep);
2808 $ty(offer_decl)
2809 },
2810 ];
2811 let children = ["a", "b"].iter().map(|name| {
2812 fdecl::Child {
2813 name: Some(name.to_string()),
2814 url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
2815 startup: Some(fdecl::StartupMode::Lazy),
2816 on_terminate: None,
2817 environment: None,
2818 ..Default::default()
2819 }
2820 }).collect();
2821 decl.offers = Some(offers);
2822 decl.children = Some(children);
2823 let result = Ok(());
2824 validate_test(decl, result);
2825 }
2826 )+
2827 }
2828 }
2829
2830 #[track_caller]
2831 fn validate_test(input: fdecl::Component, expected_res: Result<(), ErrorList>) {
2832 let res = validate(&input, &mut DirectedGraph::new());
2833 assert_eq!(res, expected_res);
2834 }
2835
2836 #[track_caller]
2837 fn validate_test_any_result(input: fdecl::Component, expected_res: Vec<Result<(), ErrorList>>) {
2838 let res = format!("{:?}", validate(&input, &mut DirectedGraph::new()));
2839 let expected_res_debug = format!("{:?}", expected_res);
2840
2841 let matched_exp =
2842 expected_res.into_iter().find(|expected| res == format!("{:?}", expected));
2843
2844 assert!(
2845 matched_exp.is_some(),
2846 "assertion failed: Expected one of:\n{:?}\nActual:\n{:?}",
2847 expected_res_debug,
2848 res
2849 );
2850 }
2851
2852 #[track_caller]
2853 fn validate_values_data_test(
2854 input: fdecl::ConfigValuesData,
2855 expected_res: Result<(), ErrorList>,
2856 ) {
2857 let res = validate_values_data(&input);
2858 assert_eq!(res, expected_res);
2859 }
2860
2861 #[track_caller]
2862 fn validate_capabilities_test(
2863 input: Vec<fdecl::Capability>,
2864 as_builtin: bool,
2865 expected_res: Result<(), ErrorList>,
2866 ) {
2867 let res = validate_capabilities(&input, as_builtin);
2868 assert_eq!(res, expected_res);
2869 }
2870
2871 fn new_component_decl() -> fdecl::Component {
2872 fdecl::Component {
2873 program: None,
2874 uses: None,
2875 exposes: None,
2876 offers: None,
2877 facets: None,
2878 capabilities: None,
2879 children: None,
2880 collections: None,
2881 environments: None,
2882 ..Default::default()
2883 }
2884 }
2885
2886 test_validate_any_result! {
2887 test_validate_use_disallows_nested_dirs => {
2888 input = {
2889 let mut decl = new_component_decl();
2890 decl.uses = Some(vec![
2891 fdecl::Use::Directory(fdecl::UseDirectory {
2892 dependency_type: Some(fdecl::DependencyType::Strong),
2893 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2894 source_name: Some("abc".to_string()),
2895 target_path: Some("/foo/bar".to_string()),
2896 rights: Some(fio::Operations::CONNECT),
2897 subdir: None,
2898 ..Default::default()
2899 }),
2900 fdecl::Use::Directory(fdecl::UseDirectory {
2901 dependency_type: Some(fdecl::DependencyType::Strong),
2902 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2903 source_name: Some("abc".to_string()),
2904 target_path: Some("/foo/bar/baz".to_string()),
2905 rights: Some(fio::Operations::CONNECT),
2906 subdir: None,
2907 ..Default::default()
2908 }),
2909 ]);
2910 decl
2911 },
2912 results = vec![
2913 Err(ErrorList::new(vec![
2914 Error::invalid_path_overlap(
2915 DeclType::UseDirectory, "/foo/bar/baz", DeclType::UseDirectory, "/foo/bar"),
2916 ])),
2917 Err(ErrorList::new(vec![
2918 Error::invalid_path_overlap(
2919 DeclType::UseDirectory, "/foo/bar", DeclType::UseDirectory, "/foo/bar/baz"),
2920 ])),
2921 ],
2922 },
2923 test_validate_use_disallows_nested_dirs_storage => {
2924 input = {
2925 let mut decl = new_component_decl();
2926 decl.uses = Some(vec![
2927 fdecl::Use::Storage(fdecl::UseStorage {
2928 source_name: Some("abc".to_string()),
2929 target_path: Some("/foo/bar".to_string()),
2930 ..Default::default()
2931 }),
2932 fdecl::Use::Storage(fdecl::UseStorage {
2933 source_name: Some("abc".to_string()),
2934 target_path: Some("/foo/bar/baz".to_string()),
2935 ..Default::default()
2936 }),
2937 ]);
2938 decl
2939 },
2940 results = vec![
2941 Err(ErrorList::new(vec![
2942 Error::invalid_path_overlap(
2943 DeclType::UseStorage, "/foo/bar/baz", DeclType::UseStorage, "/foo/bar"),
2944 ])),
2945 Err(ErrorList::new(vec![
2946 Error::invalid_path_overlap(
2947 DeclType::UseStorage, "/foo/bar", DeclType::UseStorage, "/foo/bar/baz"),
2948 ])),
2949 ],
2950 },
2951 test_validate_use_disallows_nested_dirs_directory_and_storage => {
2952 input = {
2953 let mut decl = new_component_decl();
2954 decl.uses = Some(vec![
2955 fdecl::Use::Directory(fdecl::UseDirectory {
2956 dependency_type: Some(fdecl::DependencyType::Strong),
2957 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2958 source_name: Some("abc".to_string()),
2959 target_path: Some("/foo/bar".to_string()),
2960 rights: Some(fio::Operations::CONNECT),
2961 subdir: None,
2962 ..Default::default()
2963 }),
2964 fdecl::Use::Storage(fdecl::UseStorage {
2965 source_name: Some("abc".to_string()),
2966 target_path: Some("/foo/bar/baz".to_string()),
2967 ..Default::default()
2968 }),
2969 ]);
2970 decl
2971 },
2972 results = vec![
2973 Err(ErrorList::new(vec![
2974 Error::invalid_path_overlap(
2975 DeclType::UseStorage, "/foo/bar/baz", DeclType::UseDirectory, "/foo/bar"),
2976 ])),
2977 Err(ErrorList::new(vec![
2978 Error::invalid_path_overlap(
2979 DeclType::UseDirectory, "/foo/bar", DeclType::UseStorage, "/foo/bar/baz"),
2980 ])),
2981 ],
2982 },
2983 test_validate_use_disallows_common_prefixes_protocol => {
2984 input = {
2985 let mut decl = new_component_decl();
2986 decl.uses = Some(vec![
2987 fdecl::Use::Directory(fdecl::UseDirectory {
2988 dependency_type: Some(fdecl::DependencyType::Strong),
2989 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2990 source_name: Some("abc".to_string()),
2991 target_path: Some("/foo/bar".to_string()),
2992 rights: Some(fio::Operations::CONNECT),
2993 subdir: None,
2994 ..Default::default()
2995 }),
2996 fdecl::Use::Protocol(fdecl::UseProtocol {
2997 dependency_type: Some(fdecl::DependencyType::Strong),
2998 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2999 source_name: Some("crow".to_string()),
3000 target_path: Some("/foo/bar/fuchsia.2".to_string()),
3001 ..Default::default()
3002 }),
3003 ]);
3004 decl
3005 },
3006 results = vec![
3007 Err(ErrorList::new(vec![
3008 Error::invalid_path_overlap(
3009 DeclType::UseProtocol, "/foo/bar/fuchsia.2", DeclType::UseDirectory, "/foo/bar"),
3010 ])),
3011 Err(ErrorList::new(vec![
3012 Error::invalid_path_overlap(
3013 DeclType::UseDirectory, "/foo/bar", DeclType::UseProtocol, "/foo/bar/fuchsia.2"),
3014 ])),
3015 ],
3016 },
3017 test_validate_use_disallows_common_prefixes_service => {
3018 input = {
3019 let mut decl = new_component_decl();
3020 decl.uses = Some(vec![
3021 fdecl::Use::Directory(fdecl::UseDirectory {
3022 dependency_type: Some(fdecl::DependencyType::Strong),
3023 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3024 source_name: Some("abc".to_string()),
3025 target_path: Some("/foo/bar".to_string()),
3026 rights: Some(fio::Operations::CONNECT),
3027 subdir: None,
3028 ..Default::default()
3029 }),
3030 fdecl::Use::Service(fdecl::UseService {
3031 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3032 source_name: Some("space".to_string()),
3033 target_path: Some("/foo/bar/baz/fuchsia.logger.Log".to_string()),
3034 dependency_type: Some(fdecl::DependencyType::Strong),
3035 ..Default::default()
3036 }),
3037 ]);
3038 decl
3039 },
3040 results = vec![
3041 Err(ErrorList::new(vec![
3042 Error::invalid_path_overlap(
3043 DeclType::UseService, "/foo/bar/baz/fuchsia.logger.Log", DeclType::UseDirectory, "/foo/bar"),
3044 ])),
3045 Err(ErrorList::new(vec![
3046 Error::invalid_path_overlap(
3047 DeclType::UseDirectory, "/foo/bar", DeclType::UseService, "/foo/bar/baz/fuchsia.logger.Log"),
3048 ])),
3049 ],
3050 },
3051 test_validate_use_disallows_pkg => {
3052 input = {
3053 let mut decl = new_component_decl();
3054 decl.uses = Some(vec![
3055 fdecl::Use::Directory(fdecl::UseDirectory {
3056 dependency_type: Some(fdecl::DependencyType::Strong),
3057 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3058 source_name: Some("abc".to_string()),
3059 target_path: Some("/pkg".to_string()),
3060 rights: Some(fio::Operations::CONNECT),
3061 subdir: None,
3062 ..Default::default()
3063 }),
3064 ]);
3065 decl
3066 },
3067 results = vec![
3068 Err(ErrorList::new(vec![
3069 Error::pkg_path_overlap(DeclType::UseDirectory, "/pkg"),
3070 ])),
3071 ],
3072 },
3073 test_validate_use_disallows_pkg_overlap => {
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/foo".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/foo"),
3092 ])),
3093 ],
3094 },
3095 test_validate_use_optional_config_correct => {
3096 input = {
3097 let mut decl = new_component_decl();
3098 decl.uses = Some(vec![
3099 fdecl::Use::Config(fdecl::UseConfiguration {
3100 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3101 source_name: Some("abc".to_string()),
3102 target_name: Some("foo".to_string()),
3103 availability: Some(fdecl::Availability::Optional),
3104 type_: Some(fdecl::ConfigType {
3105 layout: fdecl::ConfigTypeLayout::Bool,
3106 parameters: Some(Vec::new()),
3107 constraints: Vec::new(),
3108 }),
3109 ..Default::default()
3110 }),
3111 ]);
3112 decl.config = Some(fdecl::ConfigSchema {
3113 fields: Some(vec![fdecl::ConfigField {
3114 key: Some("foo".into()),
3115 type_: Some(fdecl::ConfigType {
3116 layout: fdecl::ConfigTypeLayout::Bool,
3117 parameters: Some(Vec::new()),
3118 constraints: Vec::new(),
3119 }),
3120 mutability: None,
3121 ..Default::default()}]),
3122 checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3123 value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3124 ..Default::default()
3125 });
3126 decl
3127 },
3128 results = vec![Ok(())],
3129 },
3130 test_validate_use_optional_config_no_config_schema => {
3131 input = {
3132 let mut decl = new_component_decl();
3133 decl.uses = Some(vec![
3134 fdecl::Use::Config(fdecl::UseConfiguration {
3135 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3136 source_name: Some("abc".to_string()),
3137 target_name: Some("foo".to_string()),
3138 availability: Some(fdecl::Availability::Optional),
3139 type_: Some(fdecl::ConfigType {
3140 layout: fdecl::ConfigTypeLayout::Bool,
3141 parameters: None,
3142 constraints: Vec::new(),
3143 }),
3144 ..Default::default()
3145 }),
3146 ]);
3147 decl
3148 },
3149 results = vec![
3150 Err(ErrorList::new(vec![
3151 Error::missing_field(DeclType::ConfigField, "config"),
3152 ])),
3153 ],
3154 },
3155 test_validate_use_optional_config_no_config_field => {
3156 input = {
3157 let mut decl = new_component_decl();
3158 decl.uses = Some(vec![
3159 fdecl::Use::Config(fdecl::UseConfiguration {
3160 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3161 source_name: Some("abc".to_string()),
3162 target_name: Some("foo".to_string()),
3163 availability: Some(fdecl::Availability::Optional),
3164 type_: Some(fdecl::ConfigType {
3165 layout: fdecl::ConfigTypeLayout::Bool,
3166 parameters: None,
3167 constraints: Vec::new(),
3168 }),
3169 ..Default::default()
3170 }),
3171 ]);
3172 decl.config = Some(fdecl::ConfigSchema {
3173 fields: Some(vec![]),
3174 checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3175 value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3176 ..Default::default()
3177 });
3178 decl
3179 },
3180 results = vec![
3181 Err(ErrorList::new(vec![
3182 Error::missing_field(DeclType::ConfigField, "foo"),
3183 ])),
3184 ],
3185 },
3186 test_validate_use_optional_config_bad_type => {
3187 input = {
3188 let mut decl = new_component_decl();
3189 decl.uses = Some(vec![
3190 fdecl::Use::Config(fdecl::UseConfiguration {
3191 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3192 source_name: Some("abc".to_string()),
3193 target_name: Some("foo".to_string()),
3194 availability: Some(fdecl::Availability::Optional),
3195 type_: Some(fdecl::ConfigType {
3196 layout: fdecl::ConfigTypeLayout::Bool,
3197 parameters: None,
3198 constraints: Vec::new(),
3199 }),
3200 ..Default::default()
3201 }),
3202 ]);
3203 decl.config = Some(fdecl::ConfigSchema {
3204 fields: Some(vec![fdecl::ConfigField {
3205 key: Some("foo".into()),
3206 type_: Some(fdecl::ConfigType {
3207 layout: fdecl::ConfigTypeLayout::Int16,
3208 parameters: Some(Vec::new()),
3209 constraints: Vec::new(),
3210 }),
3211 mutability: None,
3212 ..Default::default()}]),
3213 checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3214 value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3215 ..Default::default()
3216 });
3217 decl
3218 },
3219 results = vec![
3220 Err(ErrorList::new(vec![
3221 Error::invalid_field(DeclType::ConfigField, "foo"),
3222 ])),
3223 ],
3224 },
3225 }
3226
3227 #[cfg(fuchsia_api_level_at_least = "NEXT")]
3228 test_validate_any_result! {
3229 test_validate_use_dictionary => {
3230 input = {
3231 let mut decl = new_component_decl();
3232 decl.uses = Some(vec![
3233 fdecl::Use::Dictionary(fdecl::UseDictionary {
3234 dependency_type: Some(fdecl::DependencyType::Strong),
3235 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3236 source_name: Some("toolbox".to_string()),
3237 target_path: Some("/svc".to_string()),
3238 availability: Some(fdecl::Availability::Required),
3239 ..Default::default()
3240 }),
3241 ]);
3242 decl
3243 },
3244 results = vec![
3245 Ok(()),
3246 ],
3247 },
3248 test_validate_use_dictionary_invalid_name => {
3249 input = {
3250 let mut decl = new_component_decl();
3251 decl.uses = Some(vec![
3252 fdecl::Use::Dictionary(fdecl::UseDictionary {
3253 dependency_type: Some(fdecl::DependencyType::Strong),
3254 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3255 source_name: Some("toolbox@".to_string()),
3256 target_path: Some("/svc".to_string()),
3257 availability: Some(fdecl::Availability::Required),
3258 ..Default::default()
3259 }),
3260 ]);
3261 decl
3262 },
3263 results = vec![
3264 Err(ErrorList::new(vec![Error::invalid_field(DeclType::UseDictionary, "source_name")])),
3265 ],
3266 },
3267 test_validate_use_dictionary_invalid_path => {
3268 input = {
3269 let mut decl = new_component_decl();
3270 decl.uses = Some(vec![
3271 fdecl::Use::Dictionary(fdecl::UseDictionary {
3272 dependency_type: Some(fdecl::DependencyType::Strong),
3273 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3274 source_name: Some("toolbox".to_string()),
3275 target_path: Some("/svc@".to_string()),
3276 availability: Some(fdecl::Availability::Required),
3277 ..Default::default()
3278 }),
3279 ]);
3280 decl
3281 },
3282 results = vec![
3283 Err(ErrorList::new(vec![Error::field_invalid_segment(DeclType::UseDictionary, "target_path")])),
3284 ],
3285 },
3286 test_validate_use_dictionary_disallows_pkg_overlap => {
3287 input = {
3288 let mut decl = new_component_decl();
3289 decl.uses = Some(vec![
3290 fdecl::Use::Dictionary(fdecl::UseDictionary {
3291 dependency_type: Some(fdecl::DependencyType::Strong),
3292 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3293 source_name: Some("toolbox".to_string()),
3294 target_path: Some("/pkg/toolbox".to_string()),
3295 availability: Some(fdecl::Availability::Required),
3296 ..Default::default()
3297 }),
3298 ]);
3299 decl
3300 },
3301 results = vec![
3302 Err(ErrorList::new(vec![
3303 Error::pkg_path_overlap(DeclType::UseDictionary, "/pkg/toolbox"),
3304 ])),
3305 ],
3306 },
3307 test_validate_use_dictionary_path_overlap_directory => {
3308 input = {
3309 let mut decl = new_component_decl();
3310 decl.uses = Some(vec![
3311 fdecl::Use::Dictionary(fdecl::UseDictionary {
3312 dependency_type: Some(fdecl::DependencyType::Strong),
3313 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3314 source_name: Some("toolbox".to_string()),
3315 target_path: Some("/foo/bar".to_string()),
3316 availability: Some(fdecl::Availability::Required),
3317 ..Default::default()
3318 }),
3319 fdecl::Use::Directory(fdecl::UseDirectory {
3320 dependency_type: Some(fdecl::DependencyType::Strong),
3321 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3322 source_name: Some("data".to_string()),
3323 target_path: Some("/foo".to_string()),
3324 rights: Some(fio::Operations::CONNECT),
3325 subdir: None,
3326 ..Default::default()
3327 }),
3328 ]);
3329 decl
3330 },
3331 results = vec![
3332 Err(ErrorList::new(vec![
3333 Error::invalid_path_overlap(
3334 DeclType::UseDirectory,
3335 "/foo",
3336 DeclType::UseDictionary,
3337 "/foo/bar",
3338 ),
3339 ])),
3340 Err(ErrorList::new(vec![
3341 Error::invalid_path_overlap(
3342 DeclType::UseDictionary,
3343 "/foo/bar",
3344 DeclType::UseDirectory,
3345 "/foo",
3346 ),
3347 ])),
3348 ],
3349 },
3350 test_validate_use_dictionary_path_overlap_protocol => {
3351 input = {
3352 let mut decl = new_component_decl();
3353 decl.uses = Some(vec![
3354 fdecl::Use::Dictionary(fdecl::UseDictionary {
3355 dependency_type: Some(fdecl::DependencyType::Strong),
3356 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3357 source_name: Some("toolbox".to_string()),
3358 target_path: Some("/svc/toolbox".to_string()),
3359 availability: Some(fdecl::Availability::Required),
3360 ..Default::default()
3361 }),
3362 fdecl::Use::Protocol(fdecl::UseProtocol {
3363 dependency_type: Some(fdecl::DependencyType::Strong),
3364 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3365 source_name: Some("fuchsia.examples.Echo".to_string()),
3366 target_path: Some("/svc/fuchsia.examples.Echo".to_string()),
3367 ..Default::default()
3368 }),
3369 ]);
3370 decl
3371 },
3372 results = vec![
3373 Ok(()),
3374 ],
3375 },
3376 test_validate_use_dictionary_path_overlap_protocol_2 => {
3377 input = {
3378 let mut decl = new_component_decl();
3379 decl.uses = Some(vec![
3380 fdecl::Use::Dictionary(fdecl::UseDictionary {
3381 dependency_type: Some(fdecl::DependencyType::Strong),
3382 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3383 source_name: Some("toolbox".to_string()),
3384 target_path: Some("/svc".to_string()),
3385 availability: Some(fdecl::Availability::Required),
3386 ..Default::default()
3387 }),
3388 fdecl::Use::Protocol(fdecl::UseProtocol {
3389 dependency_type: Some(fdecl::DependencyType::Strong),
3390 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3391 source_name: Some("fuchsia.examples.Echo".to_string()),
3392 target_path: Some("/svc/fuchsia.examples.Echo".to_string()),
3393 ..Default::default()
3394 }),
3395 ]);
3396 decl
3397 },
3398 results = vec![
3399 Ok(()),
3400 ],
3401 },
3402 }
3403
3404 test_validate_values_data! {
3405 test_values_data_ok => {
3406 input = fdecl::ConfigValuesData {
3407 values: Some(vec![
3408 fdecl::ConfigValueSpec {
3409 value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(true))),
3410 ..Default::default()
3411 }
3412 ]),
3413 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3414 ..Default::default()
3415 },
3416 result = Ok(()),
3417 },
3418 test_values_data_no_checksum => {
3419 input = fdecl::ConfigValuesData {
3420 values: Some(vec![]),
3421 checksum: None,
3422 ..Default::default()
3423 },
3424 result = Err(ErrorList::new(vec![
3425 Error::missing_field(DeclType::ConfigValuesData, "checksum")
3426 ])),
3427 },
3428 test_values_data_unknown_checksum => {
3429 input = fdecl::ConfigValuesData {
3430 values: Some(vec![]),
3431 checksum: Some(fdecl::ConfigChecksum::unknown_variant_for_testing()),
3432 ..Default::default()
3433 },
3434 result = Err(ErrorList::new(vec![
3435 Error::invalid_field(DeclType::ConfigValuesData, "checksum")
3436 ])),
3437 },
3438 test_values_data_no_values => {
3439 input = fdecl::ConfigValuesData {
3440 values: None,
3441 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3442 ..Default::default()
3443 },
3444 result = Err(ErrorList::new(vec![
3445 Error::missing_field(DeclType::ConfigValuesData, "values")
3446 ])),
3447 },
3448 test_values_data_no_inner_value => {
3449 input = fdecl::ConfigValuesData {
3450 values: Some(vec![
3451 fdecl::ConfigValueSpec::default()
3452 ]),
3453 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3454 ..Default::default()
3455 },
3456 result = Err(ErrorList::new(vec![
3457 Error::missing_field(DeclType::ConfigValueSpec, "value")
3458 ])),
3459 },
3460 test_values_data_unknown_inner_value => {
3461 input = fdecl::ConfigValuesData {
3462 values: Some(vec![
3463 fdecl::ConfigValueSpec {
3464 value: Some(fdecl::ConfigValue::unknown_variant_for_testing()),
3465 ..Default::default()
3466 }
3467 ]),
3468 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3469 ..Default::default()
3470 },
3471 result = Err(ErrorList::new(vec![
3472 Error::invalid_field(DeclType::ConfigValueSpec, "value")
3473 ])),
3474 },
3475 test_values_data_unknown_single_value => {
3476 input = fdecl::ConfigValuesData {
3477 values: Some(vec![
3478 fdecl::ConfigValueSpec {
3479 value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::unknown_variant_for_testing())),
3480 ..Default::default()
3481 }
3482 ]),
3483 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3484 ..Default::default()
3485 },
3486 result = Err(ErrorList::new(vec![
3487 Error::invalid_field(DeclType::ConfigValueSpec, "value")
3488 ])),
3489 },
3490 test_values_data_unknown_list_value => {
3491 input = fdecl::ConfigValuesData {
3492 values: Some(vec![
3493 fdecl::ConfigValueSpec {
3494 value: Some(fdecl::ConfigValue::Vector(fdecl::ConfigVectorValue::unknown_variant_for_testing())),
3495 ..Default::default()
3496 }
3497 ]),
3498 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3499 ..Default::default()
3500 },
3501 result = Err(ErrorList::new(vec![
3502 Error::invalid_field(DeclType::ConfigValueSpec, "value")
3503 ])),
3504 },
3505 }
3506
3507 test_validate! {
3508 test_validate_uses_empty => {
3510 input = {
3511 let mut decl = new_component_decl();
3512 decl.program = Some(fdecl::Program {
3513 runner: Some("elf".to_string()),
3514 info: Some(fdata::Dictionary {
3515 entries: None,
3516 ..Default::default()
3517 }),
3518 ..Default::default()
3519 });
3520 decl.uses = Some(vec![
3521 fdecl::Use::Service(fdecl::UseService {
3522 source: None,
3523 source_name: None,
3524 target_path: None,
3525 dependency_type: None,
3526 ..Default::default()
3527 }),
3528 fdecl::Use::Protocol(fdecl::UseProtocol {
3529 dependency_type: None,
3530 source: None,
3531 source_name: None,
3532 target_path: None,
3533 ..Default::default()
3534 }),
3535 fdecl::Use::Directory(fdecl::UseDirectory {
3536 dependency_type: None,
3537 source: None,
3538 source_name: None,
3539 target_path: None,
3540 rights: None,
3541 subdir: None,
3542 ..Default::default()
3543 }),
3544 fdecl::Use::Storage(fdecl::UseStorage {
3545 source_name: None,
3546 target_path: None,
3547 ..Default::default()
3548 }),
3549 fdecl::Use::EventStream(fdecl::UseEventStream {
3550 source_name: None,
3551 source: None,
3552 target_path: None,
3553 ..Default::default()
3554 }),
3555 fdecl::Use::Runner(fdecl::UseRunner {
3556 source_name: None,
3557 source: None,
3558 ..Default::default()
3559 }),
3560 ]);
3561 decl
3562 },
3563 result = Err(ErrorList::new(vec![
3564 Error::missing_field(DeclType::UseService, "source"),
3565 Error::missing_field(DeclType::UseService, "source_name"),
3566 Error::missing_field(DeclType::UseService, "target_path"),
3567 Error::missing_field(DeclType::UseService, "dependency_type"),
3568 Error::missing_field(DeclType::UseProtocol, "source"),
3569 Error::missing_field(DeclType::UseProtocol, "source_name"),
3570 Error::missing_field(DeclType::UseProtocol, "target_path"),
3571 Error::missing_field(DeclType::UseProtocol, "dependency_type"),
3572 Error::missing_field(DeclType::UseDirectory, "source"),
3573 Error::missing_field(DeclType::UseDirectory, "source_name"),
3574 Error::missing_field(DeclType::UseDirectory, "target_path"),
3575 Error::missing_field(DeclType::UseDirectory, "dependency_type"),
3576 Error::missing_field(DeclType::UseDirectory, "rights"),
3577 Error::missing_field(DeclType::UseStorage, "source_name"),
3578 Error::missing_field(DeclType::UseStorage, "target_path"),
3579 Error::missing_field(DeclType::UseEventStream, "source"),
3580 Error::missing_field(DeclType::UseEventStream, "source_name"),
3581 Error::missing_field(DeclType::UseEventStream, "target_path"),
3582 Error::missing_field(DeclType::UseRunner, "source"),
3583 Error::missing_field(DeclType::UseRunner, "source_name"),
3584 ])),
3585 },
3586 test_validate_missing_program_info => {
3587 input = {
3588 let mut decl = new_component_decl();
3589 decl.program = Some(fdecl::Program {
3590 runner: Some("runner".to_string()),
3591 info: None,
3592 ..Default::default()
3593 });
3594 decl
3595 },
3596 result = Err(ErrorList::new(vec![
3597 Error::missing_field(DeclType::Program, "info")
3598 ])),
3599 },
3600 test_validate_uses_invalid_identifiers => {
3601 input = {
3602 let mut decl = new_component_decl();
3603 decl.uses = Some(vec![
3604 fdecl::Use::Service(fdecl::UseService {
3605 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
3606 name: "^bad".to_string(),
3607 })),
3608 source_name: Some("foo/".to_string()),
3609 target_path: Some("a/foo".to_string()),
3610 dependency_type: Some(fdecl::DependencyType::Strong),
3611 ..Default::default()
3612 }),
3613 fdecl::Use::Protocol(fdecl::UseProtocol {
3614 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3615 name: "^bad".to_string(),
3616 collection: None,
3617 })),
3618 source_name: Some("foo/".to_string()),
3619 target_path: Some("b/foo".to_string()),
3620 dependency_type: Some(fdecl::DependencyType::Strong),
3621 ..Default::default()
3622 }),
3623 fdecl::Use::Directory(fdecl::UseDirectory {
3624 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3625 name: "^bad".to_string(),
3626 collection: None,
3627 })),
3628 source_name: Some("foo/".to_string()),
3629 target_path: Some("c".to_string()),
3630 rights: Some(fio::Operations::CONNECT),
3631 subdir: Some("/foo".to_string()),
3632 dependency_type: Some(fdecl::DependencyType::Strong),
3633 ..Default::default()
3634 }),
3635 fdecl::Use::Storage(fdecl::UseStorage {
3636 source_name: Some("foo/".to_string()),
3637 target_path: Some("d".to_string()),
3638 ..Default::default()
3639 }),
3640 fdecl::Use::EventStream(fdecl::UseEventStream {
3641 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3642 name: "^bad".to_string(),
3643 collection: None,
3644 })),
3645 source_name: Some("foo/".to_string()),
3646 target_path: Some("e".to_string()),
3647 ..Default::default()
3648 }),
3649 fdecl::Use::Runner(fdecl::UseRunner {
3650 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3651 name: "^bad".to_string(),
3652 collection: None,
3653 })),
3654 source_name: Some("foo/".to_string()),
3655 ..Default::default()
3656 }),
3657 ]);
3658 decl
3659 },
3660 result = Err(ErrorList::new(vec![
3661 Error::invalid_field(DeclType::UseService, "source.capability.name"),
3662 Error::invalid_field(DeclType::UseService, "source_name"),
3663 Error::invalid_field(DeclType::UseService, "target_path"),
3664 Error::invalid_field(DeclType::UseProtocol, "source.child.name"),
3665 Error::invalid_field(DeclType::UseProtocol, "source_name"),
3666 Error::invalid_field(DeclType::UseProtocol, "target_path"),
3667 Error::invalid_field(DeclType::UseDirectory, "source.child.name"),
3668 Error::invalid_field(DeclType::UseDirectory, "source_name"),
3669 Error::invalid_field(DeclType::UseDirectory, "target_path"),
3670 Error::invalid_field(DeclType::UseDirectory, "subdir"),
3671 Error::invalid_field(DeclType::UseStorage, "source_name"),
3672 Error::invalid_field(DeclType::UseStorage, "target_path"),
3673 Error::invalid_field(DeclType::UseEventStream, "source.child.name"),
3674 Error::invalid_field(DeclType::UseEventStream, "source_name"),
3675 Error::invalid_field(DeclType::UseEventStream, "target_path"),
3676 Error::invalid_field(DeclType::UseRunner, "source.child.name"),
3677 Error::invalid_field(DeclType::UseRunner, "source_name"),
3678 ])),
3679 },
3680 test_validate_uses_missing_source => {
3681 input = {
3682 fdecl::Component {
3683 uses: Some(vec![
3684 fdecl::Use::Protocol(fdecl::UseProtocol {
3685 dependency_type: Some(fdecl::DependencyType::Strong),
3686 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
3687 name: "this-storage-doesnt-exist".to_string(),
3688 })),
3689 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3690 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3691 ..Default::default()
3692 })
3693 ]),
3694 ..new_component_decl()
3695 }
3696 },
3697 result = Err(ErrorList::new(vec![
3698 Error::invalid_capability(DeclType::UseProtocol, "source", "this-storage-doesnt-exist"),
3699 ])),
3700 },
3701 test_validate_uses_invalid_child => {
3702 input = {
3703 fdecl::Component {
3704 uses: Some(vec![
3705 fdecl::Use::Protocol(fdecl::UseProtocol {
3706 dependency_type: Some(fdecl::DependencyType::Strong),
3707 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3708 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3709 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3710 ..Default::default()
3711 }),
3712 fdecl::Use::Service(fdecl::UseService {
3713 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3714 source_name: Some("service_name".to_string()),
3715 target_path: Some("/svc/service_name".to_string()),
3716 dependency_type: Some(fdecl::DependencyType::Strong),
3717 ..Default::default()
3718 }),
3719 fdecl::Use::Directory(fdecl::UseDirectory {
3720 dependency_type: Some(fdecl::DependencyType::Strong),
3721 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3722 source_name: Some("DirectoryName".to_string()),
3723 target_path: Some("/data/DirectoryName".to_string()),
3724 rights: Some(fio::Operations::CONNECT),
3725 subdir: None,
3726 ..Default::default()
3727 }),
3728 fdecl::Use::Runner(fdecl::UseRunner {
3729 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3730 source_name: Some("RunnerName".to_string()),
3731 ..Default::default()
3732 }),
3733 ]),
3734 ..new_component_decl()
3735 }
3736 },
3737 result = Err(ErrorList::new(vec![
3738 Error::invalid_child(DeclType::UseProtocol, "source", "no-such-child"),
3739 Error::invalid_child(DeclType::UseService, "source", "no-such-child"),
3740 Error::invalid_child(DeclType::UseDirectory, "source", "no-such-child"),
3741 Error::invalid_child(DeclType::UseRunner, "source", "no-such-child"),
3742 ])),
3743 },
3744 test_validate_uses_invalid_capability_from_self => {
3745 input = {
3746 let mut decl = new_component_decl();
3747 decl.uses = Some(vec![
3748 fdecl::Use::Service(fdecl::UseService {
3749 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3750 source_name: Some("fuchsia.some.library.SomeService".into()),
3751 target_path: Some("/svc/foo".into()),
3752 dependency_type: Some(fdecl::DependencyType::Strong),
3753 ..Default::default()
3754 }),
3755 fdecl::Use::Protocol(fdecl::UseProtocol {
3756 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3757 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
3758 target_path: Some("/svc/bar".into()),
3759 dependency_type: Some(fdecl::DependencyType::Strong),
3760 ..Default::default()
3761 }),
3762 fdecl::Use::Directory(fdecl::UseDirectory {
3763 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3764 source_name: Some("dir".into()),
3765 target_path: Some("/assets".into()),
3766 dependency_type: Some(fdecl::DependencyType::Strong),
3767 rights: Some(fio::Operations::CONNECT),
3768 ..Default::default()
3769 }),
3770 fdecl::Use::Runner(fdecl::UseRunner {
3771 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3772 source_name: Some("source_elf".into()),
3773 ..Default::default()
3774 }),
3775 fdecl::Use::Config(fdecl::UseConfiguration {
3776 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3777 source_name: Some("source_config".into()),
3778 target_name: Some("config".into()),
3779 type_: Some(fdecl::ConfigType {
3780 layout: fdecl::ConfigTypeLayout::Bool,
3781 parameters: Some(Vec::new()),
3782 constraints: Vec::new(),
3783 }),
3784 ..Default::default()
3785 }),
3786 fdecl::Use::Protocol(fdecl::UseProtocol {
3787 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3788 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
3789 source_dictionary: Some("dict/inner".into()),
3790 target_path: Some("/svc/baz".into()),
3791 dependency_type: Some(fdecl::DependencyType::Strong),
3792 ..Default::default()
3793 }),
3794 ]);
3795 decl
3796 },
3797 result = Err(ErrorList::new(vec![
3798 Error::invalid_capability(
3799 DeclType::UseService,
3800 "source",
3801 "fuchsia.some.library.SomeService"),
3802 Error::invalid_capability(
3803 DeclType::UseProtocol,
3804 "source",
3805 "fuchsia.some.library.SomeProtocol"),
3806 Error::invalid_capability(DeclType::UseDirectory, "source", "dir"),
3807 Error::invalid_capability(DeclType::UseRunner, "source", "source_elf"),
3808 Error::invalid_capability(DeclType::UseConfiguration, "source", "source_config"),
3809 Error::invalid_capability(DeclType::UseProtocol, "source", "dict"),
3810 ])),
3811 },
3812 test_validate_use_numbered_handle => {
3813 input = {
3814 fdecl::Component {
3815 uses: Some(vec![
3816 fdecl::Use::Protocol(fdecl::UseProtocol {
3818 dependency_type: Some(fdecl::DependencyType::Strong),
3819 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
3820 source_name: Some("fuchsia.logger.LogSink".into()),
3821 numbered_handle: Some(0xab),
3822 ..Default::default()
3823 }),
3824 fdecl::Use::Protocol(fdecl::UseProtocol {
3826 dependency_type: Some(fdecl::DependencyType::Strong),
3827 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
3828 source_name: Some("fuchsia.logger.LogSink".into()),
3829 target_path: Some("/svc/fuchsia.logger.LogSink".into()),
3830 numbered_handle: Some(0xab),
3831 ..Default::default()
3832 }),
3833 ]),
3834 ..new_component_decl()
3835 }
3836 },
3837 result = Err(ErrorList::new(vec![
3838 Error::extraneous_field(DeclType::UseProtocol, "numbered_handle"),
3839 ])),
3840 },
3841 test_validate_use_from_child_offer_to_child_strong_cycle => {
3842 input = {
3843 fdecl::Component {
3844 capabilities: Some(vec![
3845 fdecl::Capability::Service(fdecl::Service {
3846 name: Some("a".to_string()),
3847 source_path: Some("/a".to_string()),
3848 ..Default::default()
3849 })]),
3850 uses: Some(vec![
3851 fdecl::Use::Protocol(fdecl::UseProtocol {
3852 dependency_type: Some(fdecl::DependencyType::Strong),
3853 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3854 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3855 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3856 ..Default::default()
3857 }),
3858 fdecl::Use::Service(fdecl::UseService {
3859 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3860 source_name: Some("service_name".to_string()),
3861 target_path: Some("/svc/service_name".to_string()),
3862 dependency_type: Some(fdecl::DependencyType::Strong),
3863 ..Default::default()
3864 }),
3865 fdecl::Use::Directory(fdecl::UseDirectory {
3866 dependency_type: Some(fdecl::DependencyType::Strong),
3867 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3868 source_name: Some("DirectoryName".to_string()),
3869 target_path: Some("/data/DirectoryName".to_string()),
3870 rights: Some(fio::Operations::CONNECT),
3871 subdir: None,
3872 ..Default::default()
3873 }),
3874 ]),
3875 offers: Some(vec![
3876 fdecl::Offer::Service(fdecl::OfferService {
3877 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3878 source_name: Some("a".to_string()),
3879 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
3880 target_name: Some("a".to_string()),
3881 ..Default::default()
3882 })
3883 ]),
3884 children: Some(vec![
3885 fdecl::Child {
3886 name: Some("child".to_string()),
3887 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3888 startup: Some(fdecl::StartupMode::Lazy),
3889 on_terminate: None,
3890 ..Default::default()
3891 }
3892 ]),
3893 ..new_component_decl()
3894 }
3895 },
3896 result = Err(ErrorList::new(vec![
3897 Error::dependency_cycle("{{self -> child child -> self}}"),
3898 ])),
3899 },
3900 test_validate_use_from_child_storage_no_cycle => {
3901 input = {
3902 fdecl::Component {
3903 capabilities: Some(vec![
3904 fdecl::Capability::Storage(fdecl::Storage {
3905 name: Some("cdata".to_string()),
3906 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None } )),
3907 backing_dir: Some("minfs".to_string()),
3908 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
3909 ..Default::default()
3910 }),
3911 fdecl::Capability::Storage(fdecl::Storage {
3912 name: Some("pdata".to_string()),
3913 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
3914 backing_dir: Some("minfs".to_string()),
3915 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
3916 ..Default::default()
3917 }),
3918 ]),
3919 uses: Some(vec![
3920 fdecl::Use::Protocol(fdecl::UseProtocol {
3921 dependency_type: Some(fdecl::DependencyType::Strong),
3922 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child1".to_string(), collection: None})),
3923 source_name: Some("a".to_string()),
3924 target_path: Some("/svc/a".to_string()),
3925 ..Default::default()
3926 }),
3927 ]),
3928 offers: Some(vec![
3929 fdecl::Offer::Storage(fdecl::OfferStorage {
3930 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3931 source_name: Some("cdata".to_string()),
3932 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3933 target_name: Some("cdata".to_string()),
3934 ..Default::default()
3935 }),
3936 fdecl::Offer::Storage(fdecl::OfferStorage {
3937 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3938 source_name: Some("pdata".to_string()),
3939 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3940 target_name: Some("pdata".to_string()),
3941 ..Default::default()
3942 }),
3943 ]),
3944 children: Some(vec![
3945 fdecl::Child {
3946 name: Some("child1".to_string()),
3947 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3948 startup: Some(fdecl::StartupMode::Lazy),
3949 on_terminate: None,
3950 ..Default::default()
3951 },
3952 fdecl::Child {
3953 name: Some("child2".to_string()),
3954 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
3955 startup: Some(fdecl::StartupMode::Lazy),
3956 on_terminate: None,
3957 ..Default::default()
3958 }
3959 ]),
3960 ..new_component_decl()
3961 }
3962 },
3963 result = Ok(()),
3964 },
3965 test_validate_use_from_child_storage_cycle => {
3966 input = {
3967 fdecl::Component {
3968 capabilities: Some(vec![
3969 fdecl::Capability::Storage(fdecl::Storage {
3970 name: Some("data".to_string()),
3971 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
3972 backing_dir: Some("minfs".to_string()),
3973 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
3974 ..Default::default()
3975 }),
3976 ]),
3977 uses: Some(vec![
3978 fdecl::Use::Protocol(fdecl::UseProtocol {
3979 dependency_type: Some(fdecl::DependencyType::Strong),
3980 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3981 source_name: Some("a".to_string()),
3982 target_path: Some("/svc/a".to_string()),
3983 ..Default::default()
3984 }),
3985 ]),
3986 offers: Some(vec![
3987 fdecl::Offer::Storage(fdecl::OfferStorage {
3988 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3989 source_name: Some("data".to_string()),
3990 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
3991 target_name: Some("data".to_string()),
3992 ..Default::default()
3993 }),
3994 ]),
3995 children: Some(vec![
3996 fdecl::Child {
3997 name: Some("child".to_string()),
3998 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3999 startup: Some(fdecl::StartupMode::Lazy),
4000 on_terminate: None,
4001 ..Default::default()
4002 },
4003 ]),
4004 ..new_component_decl()
4005 }
4006 },
4007 result = Err(ErrorList::new(vec![
4008 Error::dependency_cycle("{{self -> capability data -> child child -> self}}"),
4009 ])),
4010 },
4011 test_validate_storage_strong_cycle_between_children => {
4012 input = {
4013 fdecl::Component {
4014 capabilities: Some(vec![
4015 fdecl::Capability::Storage(fdecl::Storage {
4016 name: Some("data".to_string()),
4017 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None } )),
4018 backing_dir: Some("minfs".to_string()),
4019 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4020 ..Default::default()
4021 })
4022 ]),
4023 offers: Some(vec![
4024 fdecl::Offer::Storage(fdecl::OfferStorage {
4025 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4026 source_name: Some("data".to_string()),
4027 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4028 target_name: Some("data".to_string()),
4029 ..Default::default()
4030 }),
4031 fdecl::Offer::Service(fdecl::OfferService {
4032 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4033 source_name: Some("a".to_string()),
4034 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4035 target_name: Some("a".to_string()),
4036 ..Default::default()
4037 }),
4038 ]),
4039 children: Some(vec![
4040 fdecl::Child {
4041 name: Some("child1".to_string()),
4042 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4043 startup: Some(fdecl::StartupMode::Lazy),
4044 on_terminate: None,
4045 ..Default::default()
4046 },
4047 fdecl::Child {
4048 name: Some("child2".to_string()),
4049 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4050 startup: Some(fdecl::StartupMode::Lazy),
4051 on_terminate: None,
4052 ..Default::default()
4053 }
4054 ]),
4055 ..new_component_decl()
4056 }
4057 },
4058 result = Err(ErrorList::new(vec![
4059 Error::dependency_cycle("{{child child1 -> capability data -> child child2 -> child child1}}"),
4060 ])),
4061 },
4062 test_validate_strong_cycle_between_children_through_environment_debug => {
4063 input = {
4064 fdecl::Component {
4065 environments: Some(vec![
4066 fdecl::Environment {
4067 name: Some("env".to_string()),
4068 extends: Some(fdecl::EnvironmentExtends::Realm),
4069 debug_capabilities: Some(vec![
4070 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
4071 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4072 source_name: Some("fuchsia.foo.Bar".to_string()),
4073 target_name: Some("fuchsia.foo.Bar".to_string()),
4074 ..Default::default()
4075 }),
4076 ]),
4077 ..Default::default()
4078 },
4079 ]),
4080 offers: Some(vec![
4081 fdecl::Offer::Service(fdecl::OfferService {
4082 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4083 source_name: Some("a".to_string()),
4084 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4085 target_name: Some("a".to_string()),
4086 ..Default::default()
4087 }),
4088 ]),
4089 children: Some(vec![
4090 fdecl::Child {
4091 name: Some("child1".to_string()),
4092 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4093 startup: Some(fdecl::StartupMode::Lazy),
4094 on_terminate: None,
4095 ..Default::default()
4096 },
4097 fdecl::Child {
4098 name: Some("child2".to_string()),
4099 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4100 startup: Some(fdecl::StartupMode::Lazy),
4101 environment: Some("env".to_string()),
4102 on_terminate: None,
4103 ..Default::default()
4104 }
4105 ]),
4106 ..new_component_decl()
4107 }
4108 },
4109 result = Err(ErrorList::new(vec![
4110 Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}"),
4111 ])),
4112 },
4113 test_validate_strong_cycle_between_children_through_environment_runner => {
4114 input = {
4115 fdecl::Component {
4116 environments: Some(vec![
4117 fdecl::Environment {
4118 name: Some("env".to_string()),
4119 extends: Some(fdecl::EnvironmentExtends::Realm),
4120 runners: Some(vec![
4121 fdecl::RunnerRegistration {
4122 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4123 source_name: Some("coff".to_string()),
4124 target_name: Some("coff".to_string()),
4125 ..Default::default()
4126 }
4127 ]),
4128 ..Default::default()
4129 },
4130 ]),
4131 offers: Some(vec![
4132 fdecl::Offer::Service(fdecl::OfferService {
4133 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4134 source_name: Some("a".to_string()),
4135 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4136 target_name: Some("a".to_string()),
4137 ..Default::default()
4138 }),
4139 ]),
4140 children: Some(vec![
4141 fdecl::Child {
4142 name: Some("child1".to_string()),
4143 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4144 startup: Some(fdecl::StartupMode::Lazy),
4145 on_terminate: None,
4146 ..Default::default()
4147 },
4148 fdecl::Child {
4149 name: Some("child2".to_string()),
4150 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4151 startup: Some(fdecl::StartupMode::Lazy),
4152 environment: Some("env".to_string()),
4153 on_terminate: None,
4154 ..Default::default()
4155 }
4156 ]),
4157 ..new_component_decl()
4158 }
4159 },
4160 result = Err(ErrorList::new(vec![
4161 Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}"),
4162 ])),
4163 },
4164 test_validate_strong_cycle_between_children_through_environment_resolver => {
4165 input = {
4166 fdecl::Component {
4167 environments: Some(vec![
4168 fdecl::Environment {
4169 name: Some("env".to_string()),
4170 extends: Some(fdecl::EnvironmentExtends::Realm),
4171 resolvers: Some(vec![
4172 fdecl::ResolverRegistration {
4173 resolver: Some("gopher".to_string()),
4174 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4175 scheme: Some("gopher".to_string()),
4176 ..Default::default()
4177 }
4178 ]),
4179 ..Default::default()
4180 },
4181 ]),
4182 offers: Some(vec![
4183 fdecl::Offer::Service(fdecl::OfferService {
4184 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4185 source_name: Some("a".to_string()),
4186 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4187 target_name: Some("a".to_string()),
4188 ..Default::default()
4189 }),
4190 ]),
4191 children: Some(vec![
4192 fdecl::Child {
4193 name: Some("child1".to_string()),
4194 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4195 startup: Some(fdecl::StartupMode::Lazy),
4196 on_terminate: None,
4197 ..Default::default()
4198 },
4199 fdecl::Child {
4200 name: Some("child2".to_string()),
4201 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4202 startup: Some(fdecl::StartupMode::Lazy),
4203 environment: Some("env".to_string()),
4204 on_terminate: None,
4205 ..Default::default()
4206 }
4207 ]),
4208 ..new_component_decl()
4209 }
4210 },
4211 result = Err(ErrorList::new(vec![
4212 Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}"),
4213 ])),
4214 },
4215 test_validate_strong_cycle_between_self_and_two_children => {
4216 input = {
4217 fdecl::Component {
4218 capabilities: Some(vec![
4219 fdecl::Capability::Protocol(fdecl::Protocol {
4220 name: Some("fuchsia.foo.Bar".to_string()),
4221 source_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4222 ..Default::default()
4223 })
4224 ]),
4225 offers: Some(vec![
4226 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4227 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4228 source_name: Some("fuchsia.foo.Bar".to_string()),
4229 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4230 target_name: Some("fuchsia.foo.Bar".to_string()),
4231 dependency_type: Some(fdecl::DependencyType::Strong),
4232 ..Default::default()
4233 }),
4234 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4235 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4236 source_name: Some("fuchsia.bar.Baz".to_string()),
4237 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4238 target_name: Some("fuchsia.bar.Baz".to_string()),
4239 dependency_type: Some(fdecl::DependencyType::Strong),
4240 ..Default::default()
4241 }),
4242 ]),
4243 uses: Some(vec![
4244 fdecl::Use::Protocol(fdecl::UseProtocol {
4245 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child2".to_string(), collection: None})),
4246 source_name: Some("fuchsia.baz.Foo".to_string()),
4247 target_path: Some("/svc/fuchsia.baz.Foo".to_string()),
4248 dependency_type: Some(fdecl::DependencyType::Strong),
4249 ..Default::default()
4250 }),
4251 ]),
4252 children: Some(vec![
4253 fdecl::Child {
4254 name: Some("child1".to_string()),
4255 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4256 startup: Some(fdecl::StartupMode::Lazy),
4257 on_terminate: None,
4258 ..Default::default()
4259 },
4260 fdecl::Child {
4261 name: Some("child2".to_string()),
4262 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4263 startup: Some(fdecl::StartupMode::Lazy),
4264 on_terminate: None,
4265 ..Default::default()
4266 }
4267 ]),
4268 ..new_component_decl()
4269 }
4270 },
4271 result = Err(ErrorList::new(vec![
4272 Error::dependency_cycle("{{self -> child child1 -> child child2 -> self}}"),
4273 ])),
4274 },
4275 test_validate_strong_cycle_with_self_storage => {
4276 input = {
4277 fdecl::Component {
4278 capabilities: Some(vec![
4279 fdecl::Capability::Storage(fdecl::Storage {
4280 name: Some("data".to_string()),
4281 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4282 backing_dir: Some("minfs".to_string()),
4283 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4284 ..Default::default()
4285 }),
4286 fdecl::Capability::Directory(fdecl::Directory {
4287 name: Some("minfs".to_string()),
4288 source_path: Some("/minfs".to_string()),
4289 rights: Some(fio::RW_STAR_DIR),
4290 ..Default::default()
4291 }),
4292 ]),
4293 offers: Some(vec![
4294 fdecl::Offer::Storage(fdecl::OfferStorage {
4295 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4296 source_name: Some("data".to_string()),
4297 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4298 target_name: Some("data".to_string()),
4299 ..Default::default()
4300 }),
4301 ]),
4302 uses: Some(vec![
4303 fdecl::Use::Protocol(fdecl::UseProtocol {
4304 dependency_type: Some(fdecl::DependencyType::Strong),
4305 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4306 source_name: Some("fuchsia.foo.Bar".to_string()),
4307 target_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4308 ..Default::default()
4309 }),
4310 ]),
4311 children: Some(vec![
4312 fdecl::Child {
4313 name: Some("child".to_string()),
4314 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4315 startup: Some(fdecl::StartupMode::Lazy),
4316 ..Default::default()
4317 },
4318 ]),
4319 ..new_component_decl()
4320 }
4321 },
4322 result = Err(ErrorList::new(vec![
4323 Error::dependency_cycle("{{self -> capability data -> child child -> self}}"),
4324 ])),
4325 },
4326 test_validate_strong_cycle_with_self_storage_admin_protocol => {
4327 input = {
4328 fdecl::Component {
4329 capabilities: Some(vec![
4330 fdecl::Capability::Storage(fdecl::Storage {
4331 name: Some("data".to_string()),
4332 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4333 backing_dir: Some("minfs".to_string()),
4334 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4335 ..Default::default()
4336 }),
4337 fdecl::Capability::Directory(fdecl::Directory {
4338 name: Some("minfs".to_string()),
4339 source_path: Some("/minfs".to_string()),
4340 rights: Some(fio::RW_STAR_DIR),
4341 ..Default::default()
4342 }),
4343 ]),
4344 offers: Some(vec![
4345 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4346 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef { name: "data".to_string() })),
4347 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4348 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4349 target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4350 dependency_type: Some(fdecl::DependencyType::Strong),
4351 ..Default::default()
4352 }),
4353 ]),
4354 uses: Some(vec![
4355 fdecl::Use::Protocol(fdecl::UseProtocol {
4356 dependency_type: Some(fdecl::DependencyType::Strong),
4357 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4358 source_name: Some("fuchsia.foo.Bar".to_string()),
4359 target_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4360 ..Default::default()
4361 }),
4362 ]),
4363 children: Some(vec![
4364 fdecl::Child {
4365 name: Some("child".to_string()),
4366 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4367 startup: Some(fdecl::StartupMode::Lazy),
4368 ..Default::default()
4369 },
4370 ]),
4371 ..new_component_decl()
4372 }
4373 },
4374 result = Err(ErrorList::new(vec![
4375 Error::dependency_cycle("{{self -> capability data -> child child -> self}}"),
4376 ])),
4377 },
4378 test_validate_strong_cycle_with_dictionary => {
4379 input = fdecl::Component {
4380 offers: Some(vec![
4381 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
4382 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4383 source_name: Some("dict".into()),
4384 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4385 name: "a".into(),
4386 collection: None,
4387 })),
4388 target_name: Some("dict".into()),
4389 dependency_type: Some(fdecl::DependencyType::Strong),
4390 ..Default::default()
4391 }),
4392 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4393 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4394 name: "b".into(),
4395 collection: None,
4396 })),
4397 source_name: Some("1".into()),
4398 target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4399 name: "dict".into(),
4400 })),
4401 target_name: Some("1".into()),
4402 dependency_type: Some(fdecl::DependencyType::Strong),
4403 ..Default::default()
4404 }),
4405 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4406 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4407 name: "a".into(),
4408 collection: None,
4409 })),
4410 source_name: Some("2".into()),
4411 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4412 name: "b".into(),
4413 collection: None,
4414 })),
4415 target_name: Some("2".into()),
4416 dependency_type: Some(fdecl::DependencyType::Strong),
4417 ..Default::default()
4418 }),
4419 ]),
4420 children: Some(vec![
4421 fdecl::Child {
4422 name: Some("a".into()),
4423 url: Some("fuchsia-pkg://child".into()),
4424 startup: Some(fdecl::StartupMode::Lazy),
4425 ..Default::default()
4426 },
4427 fdecl::Child {
4428 name: Some("b".into()),
4429 url: Some("fuchsia-pkg://child".into()),
4430 startup: Some(fdecl::StartupMode::Lazy),
4431 ..Default::default()
4432 },
4433 ]),
4434 capabilities: Some(vec![
4435 fdecl::Capability::Dictionary(fdecl::Dictionary {
4436 name: Some("dict".into()),
4437 ..Default::default()
4438 }),
4439 ]),
4440 ..Default::default()
4441 },
4442 result = Err(ErrorList::new(vec![
4443 Error::dependency_cycle("{{child a -> child b -> capability dict -> child a}}"),
4444 ])),
4445 },
4446 test_validate_strong_cycle_with_dictionary_indirect => {
4447 input = fdecl::Component {
4448 offers: Some(vec![
4449 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4450 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4451 source_name: Some("3".into()),
4452 source_dictionary: Some("dict".into()),
4453 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4454 name: "a".into(),
4455 collection: None,
4456 })),
4457 target_name: Some("3".into()),
4458 dependency_type: Some(fdecl::DependencyType::Strong),
4459 ..Default::default()
4460 }),
4461 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4462 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4463 name: "b".into(),
4464 collection: None,
4465 })),
4466 source_name: Some("1".into()),
4467 target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4468 name: "dict".into(),
4469 })),
4470 target_name: Some("1".into()),
4471 dependency_type: Some(fdecl::DependencyType::Strong),
4472 ..Default::default()
4473 }),
4474 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4475 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4476 name: "a".into(),
4477 collection: None,
4478 })),
4479 source_name: Some("2".into()),
4480 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4481 name: "b".into(),
4482 collection: None,
4483 })),
4484 target_name: Some("2".into()),
4485 dependency_type: Some(fdecl::DependencyType::Strong),
4486 ..Default::default()
4487 }),
4488 ]),
4489 children: Some(vec![
4490 fdecl::Child {
4491 name: Some("a".into()),
4492 url: Some("fuchsia-pkg://child".into()),
4493 startup: Some(fdecl::StartupMode::Lazy),
4494 ..Default::default()
4495 },
4496 fdecl::Child {
4497 name: Some("b".into()),
4498 url: Some("fuchsia-pkg://child".into()),
4499 startup: Some(fdecl::StartupMode::Lazy),
4500 ..Default::default()
4501 },
4502 ]),
4503 capabilities: Some(vec![
4504 fdecl::Capability::Dictionary(fdecl::Dictionary {
4505 name: Some("dict".into()),
4506 ..Default::default()
4507 }),
4508 ]),
4509 ..Default::default()
4510 },
4511 result = Err(ErrorList::new(vec![
4512 Error::dependency_cycle("{{child a -> child b -> capability dict -> child a}}"),
4513 ])),
4514 },
4515 test_validate_use_dependency_cycle_with_dictionary => {
4516 input = fdecl::Component {
4517 offers: Some(vec![
4518 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
4519 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4520 source_name: Some("dict".into()),
4521 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4522 name: "a".into(),
4523 collection: None,
4524 })),
4525 target_name: Some("dict".into()),
4526 dependency_type: Some(fdecl::DependencyType::Strong),
4527 ..Default::default()
4528 }),
4529 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4530 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4531 source_name: Some("1".into()),
4532 target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4533 name: "dict".into(),
4534 })),
4535 target_name: Some("1".into()),
4536 dependency_type: Some(fdecl::DependencyType::Strong),
4537 ..Default::default()
4538 }),
4539 ]),
4540 children: Some(vec![
4541 fdecl::Child {
4542 name: Some("a".into()),
4543 url: Some("fuchsia-pkg://child".into()),
4544 startup: Some(fdecl::StartupMode::Lazy),
4545 ..Default::default()
4546 },
4547 ]),
4548 uses: Some(vec![
4549 fdecl::Use::Protocol(fdecl::UseProtocol {
4550 dependency_type: Some(fdecl::DependencyType::Strong),
4551 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "a".to_string(), collection: None})),
4552 source_name: Some("2".to_string()),
4553 target_path: Some("/svc/foo".into()),
4554 ..Default::default()
4555 }),
4556 ]),
4557 capabilities: Some(vec![
4558 fdecl::Capability::Dictionary(fdecl::Dictionary {
4559 name: Some("dict".into()),
4560 ..Default::default()
4561 }),
4562 fdecl::Capability::Protocol(fdecl::Protocol {
4563 name: Some("1".to_string()),
4564 source_path: Some("/path".to_string()),
4565 ..Default::default()
4566 }),
4567 ]),
4568 ..Default::default()
4569 },
4570 result = Err(ErrorList::new(vec![
4571 Error::dependency_cycle("{{self -> capability dict -> child a -> self}}"),
4572 ])),
4573 },
4574 test_validate_use_from_child_offer_to_child_weak_cycle => {
4575 input = {
4576 fdecl::Component {
4577 capabilities: Some(vec![
4578 fdecl::Capability::Service(fdecl::Service {
4579 name: Some("a".to_string()),
4580 source_path: Some("/a".to_string()),
4581 ..Default::default()
4582 })]),
4583 uses: Some(vec![
4584 fdecl::Use::Protocol(fdecl::UseProtocol {
4585 dependency_type: Some(fdecl::DependencyType::Weak),
4586 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4587 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4588 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
4589 ..Default::default()
4590 }),
4591 fdecl::Use::Service(fdecl::UseService {
4592 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4593 source_name: Some("service_name".to_string()),
4594 target_path: Some("/svc/service_name".to_string()),
4595 dependency_type: Some(fdecl::DependencyType::Weak),
4596 ..Default::default()
4597 }),
4598 fdecl::Use::Directory(fdecl::UseDirectory {
4599 dependency_type: Some(fdecl::DependencyType::Weak),
4600 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4601 source_name: Some("DirectoryName".to_string()),
4602 target_path: Some("/data/DirectoryName".to_string()),
4603 rights: Some(fio::Operations::CONNECT),
4604 subdir: None,
4605 ..Default::default()
4606 }),
4607 ]),
4608 offers: Some(vec![
4609 fdecl::Offer::Service(fdecl::OfferService {
4610 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4611 source_name: Some("a".to_string()),
4612 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4613 target_name: Some("a".to_string()),
4614 ..Default::default()
4615 })
4616 ]),
4617 children: Some(vec![
4618 fdecl::Child {
4619 name: Some("child".to_string()),
4620 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4621 startup: Some(fdecl::StartupMode::Lazy),
4622 on_terminate: None,
4623 ..Default::default()
4624 }
4625 ]),
4626 ..new_component_decl()
4627 }
4628 },
4629 result = Ok(()),
4630 },
4631 test_validate_expose_from_self_to_framework_and_parent => {
4632 input = {
4633 fdecl::Component {
4634 capabilities: Some(vec![
4635 fdecl::Capability::Protocol(fdecl::Protocol {
4636 name: Some("a".to_string()),
4637 source_path: Some("/a".to_string()),
4638 ..Default::default()
4639 }),
4640 ]),
4641 exposes: Some(vec![
4642 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
4643 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4644 source_name: Some("a".to_string()),
4645 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4646 target_name: Some("a".to_string()),
4647 ..Default::default()
4648 }),
4649 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
4650 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4651 source_name: Some("a".to_string()),
4652 target: Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})),
4653 target_name: Some("a".to_string()),
4654 ..Default::default()
4655 }),
4656 ]),
4657 ..new_component_decl()
4658 }
4659 },
4660 result = Ok(()),
4661 },
4662 test_validate_use_from_not_child_weak => {
4663 input = {
4664 fdecl::Component {
4665 uses: Some(vec![
4666 fdecl::Use::Protocol(fdecl::UseProtocol {
4667 dependency_type: Some(fdecl::DependencyType::Weak),
4668 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4669 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4670 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
4671 ..Default::default()
4672 }),
4673 ]),
4674 ..new_component_decl()
4675 }
4676 },
4677 result = Err(ErrorList::new(vec![
4678 Error::invalid_field(DeclType::UseProtocol, "dependency_type"),
4679 ])),
4680 },
4681 test_validate_event_stream_offer_valid_decls => {
4682 input = {
4683 let mut decl = new_component_decl();
4684 decl.offers = Some(vec![
4685 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4686 source_name: Some("stopped".to_string()),
4687 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4688 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4689 target_name: Some("stopped".to_string()),
4690 ..Default::default()
4691 }),
4692 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4693 source_name: Some("started".to_string()),
4694 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4695 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4696 target_name: Some("started".to_string()),
4697 ..Default::default()
4698 }),
4699 ]);
4700 decl.children = Some(vec![fdecl::Child{
4701 name: Some("test".to_string()),
4702 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4703 startup: Some(fdecl::StartupMode::Lazy),
4704 on_terminate: None,
4705 environment: None,
4706 ..Default::default()
4707 },
4708 fdecl::Child{
4709 name: Some("test2".to_string()),
4710 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4711 startup: Some(fdecl::StartupMode::Lazy),
4712 on_terminate: None,
4713 environment: None,
4714 ..Default::default()
4715 }
4716 ]);
4717 decl
4718 },
4719 result = Ok(()),
4720 },
4721 test_validate_event_stream_offer_to_framework_invalid => {
4722 input = {
4723 let mut decl = new_component_decl();
4724 decl.offers = Some(vec![
4725 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4726 source_name: Some("stopped".to_string()),
4727 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4728 target: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
4729 target_name: Some("stopped".to_string()),
4730 ..Default::default()
4731 }),
4732 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4733 source_name: Some("started".to_string()),
4734 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4735 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4736 target_name: Some("started".to_string()),
4737 ..Default::default()
4738 }),
4739 ]);
4740 decl.children = Some(vec![fdecl::Child{
4741 name: Some("test".to_string()),
4742 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4743 startup: Some(fdecl::StartupMode::Lazy),
4744 on_terminate: None,
4745 environment: None,
4746 ..Default::default()
4747 },
4748 fdecl::Child{
4749 name: Some("test2".to_string()),
4750 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4751 startup: Some(fdecl::StartupMode::Lazy),
4752 on_terminate: None,
4753 environment: None,
4754 ..Default::default()
4755 }
4756 ]);
4757 decl
4758 },
4759 result = Err(ErrorList::new(vec![
4760 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "target".to_string() }),
4761 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "target".to_string() }),
4762 ])),
4763 },
4764 test_validate_event_stream_offer_to_scope_zero_length_invalid => {
4765 input = {
4766 let mut decl = new_component_decl();
4767 decl.offers = Some(vec![
4768 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4769 source_name: Some("started".to_string()),
4770 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4771 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4772 scope: Some(vec![]),
4773 target_name: Some("started".to_string()),
4774 ..Default::default()
4775 }),
4776 ]);
4777 decl.children = Some(vec![fdecl::Child{
4778 name: Some("test".to_string()),
4779 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4780 startup: Some(fdecl::StartupMode::Lazy),
4781 on_terminate: None,
4782 environment: None,
4783 ..Default::default()
4784 },
4785 fdecl::Child{
4786 name: Some("test2".to_string()),
4787 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4788 startup: Some(fdecl::StartupMode::Lazy),
4789 on_terminate: None,
4790 environment: None,
4791 ..Default::default()
4792 }
4793 ]);
4794 decl
4795 },
4796 result = Err(ErrorList::new(vec![
4797 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "scope".to_string() }),
4798 ])),
4799 },
4800 test_validate_event_stream_offer_to_scope_framework_invalid => {
4801 input = {
4802 let mut decl = new_component_decl();
4803 decl.offers = Some(vec![
4804 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4805 source_name: Some("started".to_string()),
4806 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4807 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4808 scope: Some(vec![fdecl::Ref::Framework(fdecl::FrameworkRef{})]),
4809 target_name: Some("started".to_string()),
4810 ..Default::default()
4811 }),
4812 ]);
4813 decl.children = Some(vec![fdecl::Child{
4814 name: Some("test".to_string()),
4815 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4816 startup: Some(fdecl::StartupMode::Lazy),
4817 on_terminate: None,
4818 environment: None,
4819 ..Default::default()
4820 },
4821 fdecl::Child{
4822 name: Some("test2".to_string()),
4823 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4824 startup: Some(fdecl::StartupMode::Lazy),
4825 on_terminate: None,
4826 environment: None,
4827 ..Default::default()
4828 }
4829 ]);
4830 decl
4831 },
4832 result = Err(ErrorList::new(vec![
4833 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "scope".to_string() }),
4834 ])),
4835 },
4836 test_validate_event_stream_offer_to_scope_valid => {
4837 input = {
4838 let mut decl = new_component_decl();
4839 decl.offers = Some(vec![
4840 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4841 source_name: Some("started".to_string()),
4842 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4843 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4844 scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
4845 target_name: Some("started".to_string()),
4846 ..Default::default()
4847 }),
4848 ]);
4849 decl.children = Some(vec![fdecl::Child{
4850 name: Some("test".to_string()),
4851 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4852 startup: Some(fdecl::StartupMode::Lazy),
4853 on_terminate: None,
4854 environment: None,
4855 ..Default::default()
4856 },
4857 fdecl::Child{
4858 name: Some("test2".to_string()),
4859 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4860 startup: Some(fdecl::StartupMode::Lazy),
4861 on_terminate: None,
4862 environment: None,
4863 ..Default::default()
4864 }
4865 ]);
4866 decl
4867 },
4868 result = Ok(()),
4869 },
4870 test_validate_event_stream_offer_to_scope_with_capability_requested => {
4871 input = {
4872 let mut decl = new_component_decl();
4873 decl.offers = Some(vec![
4874 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4875 source_name: Some("capability_requested".to_string()),
4876 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4877 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4878 scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
4879 target_name: Some("started".to_string()),
4880 ..Default::default()
4881 }),
4882 ]);
4883 decl.children = Some(vec![fdecl::Child{
4884 name: Some("test".to_string()),
4885 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4886 startup: Some(fdecl::StartupMode::Lazy),
4887 on_terminate: None,
4888 environment: None,
4889 ..Default::default()
4890 },
4891 fdecl::Child{
4892 name: Some("test2".to_string()),
4893 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4894 startup: Some(fdecl::StartupMode::Lazy),
4895 on_terminate: None,
4896 environment: None,
4897 ..Default::default()
4898 }
4899 ]);
4900 decl
4901 },
4902 result = Ok(()),
4903 },
4904 test_validate_event_stream_offer_with_no_source_name_invalid => {
4905 input = {
4906 let mut decl = new_component_decl();
4907 decl.offers = Some(vec![
4908 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4909 source_name: None,
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::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
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::MissingField(DeclField { decl: DeclType::OfferEventStream, field: "source_name".to_string() }),
4938 ])),
4939 },
4940 test_validate_event_stream_offer_invalid_source => {
4941 input = {
4942 let mut decl = new_component_decl();
4943 decl.offers = Some(vec![
4944 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4945 source_name: Some("stopped".to_string()),
4946 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
4947 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4948 target_name: Some("stopped".to_string()),
4949 ..Default::default()
4950 }),
4951 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4952 source_name: Some("started".to_string()),
4953 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4954 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4955 target_name: Some("started".to_string()),
4956 ..Default::default()
4957 }),
4958 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4959 source_name: Some("capability_requested".to_string()),
4960 source: Some(fdecl::Ref::Debug(fdecl::DebugRef{})),
4961 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4962 target_name: Some("capability_requested".to_string()),
4963 ..Default::default()
4964 }),
4965 ]);
4966 decl.children = Some(vec![fdecl::Child{
4967 name: Some("test".to_string()),
4968 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4969 startup: Some(fdecl::StartupMode::Lazy),
4970 on_terminate: None,
4971 environment: None,
4972 ..Default::default()
4973 },
4974 fdecl::Child{
4975 name: Some("test2".to_string()),
4976 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4977 startup: Some(fdecl::StartupMode::Lazy),
4978 on_terminate: None,
4979 environment: None,
4980 ..Default::default()
4981 }
4982 ]);
4983 decl
4984 },
4985 result = Err(ErrorList::new(vec![
4986 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "source".to_string() }),
4987 ])),
4988 },
4989
4990 test_validate_event_stream_offer_missing_source => {
4991 input = {
4992 let mut decl = new_component_decl();
4993 decl.offers = Some(vec![
4994 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4995 source_name: Some("stopped".to_string()),
4996 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
4997 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4998 target_name: Some("stopped".to_string()),
4999 ..Default::default()
5000 }),
5001 fdecl::Offer::EventStream(fdecl::OfferEventStream {
5002 source_name: Some("started".to_string()),
5003 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5004 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5005 target_name: Some("started".to_string()),
5006 ..Default::default()
5007 }),
5008 fdecl::Offer::EventStream(fdecl::OfferEventStream {
5009 source_name: Some("capability_requested".to_string()),
5010 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5011 target_name: Some("capability_requested".to_string()),
5012 ..Default::default()
5013 }),
5014 ]);
5015 decl.children = Some(vec![fdecl::Child{
5016 name: Some("test".to_string()),
5017 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5018 startup: Some(fdecl::StartupMode::Lazy),
5019 on_terminate: None,
5020 environment: None,
5021 ..Default::default()
5022 },
5023 fdecl::Child{
5024 name: Some("test2".to_string()),
5025 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
5026 startup: Some(fdecl::StartupMode::Lazy),
5027 on_terminate: None,
5028 environment: None,
5029 ..Default::default()
5030 }
5031 ]);
5032 decl
5033 },
5034 result = Err(ErrorList::new(vec![
5035 Error::MissingField(DeclField { decl: DeclType::OfferEventStream, field: "source".to_string() }),
5036 ])),
5037 },
5038 test_validate_event_stream_must_have_target_path => {
5039 input = {
5040 let mut decl = new_component_decl();
5041 decl.uses = Some(vec![
5042 fdecl::Use::EventStream(fdecl::UseEventStream {
5043 source_name: Some("bar".to_string()),
5044 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5045 ..Default::default()
5046 }),
5047 ]);
5048 decl
5049 },
5050 result = Err(ErrorList::new(vec![
5051 Error::MissingField(DeclField { decl: DeclType::UseEventStream, field: "target_path".to_string() })
5052 ])),
5053 },
5054 test_validate_event_stream_must_have_source_names => {
5055 input = {
5056 let mut decl = new_component_decl();
5057 decl.uses = Some(vec![
5058 fdecl::Use::EventStream(fdecl::UseEventStream {
5059 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5060 target_path: Some("/svc/something".to_string()),
5061 ..Default::default()
5062 }),
5063 ]);
5064 decl
5065 },
5066 result = Err(ErrorList::new(vec![
5067 Error::MissingField(DeclField { decl: DeclType::UseEventStream, field: "source_name".to_string() })
5068 ])),
5069 },
5070 test_validate_event_stream_scope_must_be_child_or_collection => {
5071 input = {
5072 let mut decl = new_component_decl();
5073 decl.uses = Some(vec![
5074 fdecl::Use::EventStream(fdecl::UseEventStream {
5075 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5076 target_path: Some("/svc/something".to_string()),
5077 source_name: Some("some_source".to_string()),
5078 scope: Some(vec![fdecl::Ref::Framework(fdecl::FrameworkRef{})]),
5079 ..Default::default()
5080 }),
5081 ]);
5082 decl
5083 },
5084 result = Err(ErrorList::new(vec![
5085 Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "scope".to_string() })
5086 ])),
5087 },
5088 test_validate_event_stream_source_must_be_parent_or_child => {
5089 input = {
5090 let mut decl = new_component_decl();
5091 decl.uses = Some(vec![
5092 fdecl::Use::EventStream(fdecl::UseEventStream {
5093 source: Some(fdecl::Ref::Debug(fdecl::DebugRef{})),
5094 target_path: Some("/svc/something".to_string()),
5095 source_name: Some("some_source".to_string()),
5096 scope: Some(vec![]),
5097 ..Default::default()
5098 }),
5099 fdecl::Use::EventStream(fdecl::UseEventStream {
5100 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
5101 target_path: Some("/svc/something_else".to_string()),
5102 source_name: Some("some_source".to_string()),
5103 scope: Some(vec![]),
5104 ..Default::default()
5105 }),
5106 fdecl::Use::EventStream(fdecl::UseEventStream {
5107 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5108 target_path: Some("/svc/yet_something_else".to_string()),
5109 source_name: Some("some_source".to_string()),
5110 scope: Some(vec![]),
5111 ..Default::default()
5112 }),
5113 ]);
5114 decl
5115 },
5116 result = Err(ErrorList::new(vec![
5117 Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() }),
5118 Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() }),
5119 Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() })
5120 ])),
5121 },
5122 test_validate_no_runner => {
5123 input = {
5124 let mut decl = new_component_decl();
5125 decl.program = Some(fdecl::Program {
5126 runner: None,
5127 info: Some(fdata::Dictionary {
5128 entries: None,
5129 ..Default::default()
5130 }),
5131 ..Default::default()
5132 });
5133 decl
5134 },
5135 result = Err(ErrorList::new(vec![
5136 Error::MissingRunner,
5137 ])),
5138 },
5139 test_validate_uses_runner => {
5140 input = {
5141 let mut decl = new_component_decl();
5142 decl.program = Some(fdecl::Program {
5143 runner: None,
5144 info: Some(fdata::Dictionary {
5145 entries: None,
5146 ..Default::default()
5147 }),
5148 ..Default::default()
5149 });
5150 decl.uses = Some(vec![
5151 fdecl::Use::Runner(fdecl::UseRunner {
5152 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5153 source_name: Some("runner".to_string()),
5154 ..Default::default()
5155 }),
5156 ]);
5157 decl
5158 },
5159 result = Ok(()),
5160 },
5161 test_validate_program_and_uses_runner_match => {
5162 input = {
5163 let mut decl = new_component_decl();
5164 decl.program = Some(fdecl::Program {
5165 runner: Some("runner".to_string()),
5166 info: Some(fdata::Dictionary {
5167 entries: None,
5168 ..Default::default()
5169 }),
5170 ..Default::default()
5171 });
5172 decl.uses = Some(vec![
5173 fdecl::Use::Runner(fdecl::UseRunner {
5174 source: Some(fdecl::Ref::Environment(fdecl::EnvironmentRef {})),
5175 source_name: Some("runner".to_string()),
5176 ..Default::default()
5177 }),
5178 ]);
5179 decl
5180 },
5181 result = Ok(()),
5182 },
5183 test_validate_runner_names_conflict => {
5184 input = {
5185 let mut decl = new_component_decl();
5186 decl.program = Some(fdecl::Program {
5187 runner: Some("runner".to_string()),
5188 info: Some(fdata::Dictionary {
5189 entries: None,
5190 ..Default::default()
5191 }),
5192 ..Default::default()
5193 });
5194 decl.uses = Some(vec![
5195 fdecl::Use::Runner(fdecl::UseRunner {
5196 source: Some(fdecl::Ref::Environment(fdecl::EnvironmentRef {})),
5197 source_name: Some("other.runner".to_string()),
5198 ..Default::default()
5199 }),
5200 ]);
5201 decl
5202 },
5203 result = Err(ErrorList::new(vec![
5204 Error::ConflictingRunners,
5205 ])),
5206 },
5207 test_validate_uses_runner_not_environement => {
5208 input = {
5209 let mut decl = new_component_decl();
5210 decl.program = Some(fdecl::Program {
5211 runner: Some("runner".to_string()),
5212 info: Some(fdata::Dictionary {
5213 entries: None,
5214 ..Default::default()
5215 }),
5216 ..Default::default()
5217 });
5218 decl.uses = Some(vec![
5219 fdecl::Use::Runner(fdecl::UseRunner {
5220 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5221 source_name: Some("runner".to_string()),
5222 ..Default::default()
5223 }),
5224 ]);
5225 decl
5226 },
5227 result = Err(ErrorList::new(vec![
5228 Error::ConflictingRunners,
5229 ])),
5230 },
5231 test_validate_uses_long_identifiers => {
5232 input = {
5233 let mut decl = new_component_decl();
5234 decl.program = Some(fdecl::Program {
5235 runner: Some("elf".to_string()),
5236 info: Some(fdata::Dictionary {
5237 entries: None,
5238 ..Default::default()
5239 }),
5240 ..Default::default()
5241 });
5242 decl.uses = Some(vec![
5243 fdecl::Use::Service(fdecl::UseService {
5244 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5245 source_name: Some(format!("{}", "a".repeat(256))),
5246 target_path: Some("/a".repeat(2048)),
5247 dependency_type: Some(fdecl::DependencyType::Strong),
5248 ..Default::default()
5249 }),
5250 fdecl::Use::Protocol(fdecl::UseProtocol {
5251 dependency_type: Some(fdecl::DependencyType::Strong),
5252 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5253 source_name: Some(format!("{}", "a".repeat(256))),
5254 target_path: Some("/b".repeat(2048)),
5255 ..Default::default()
5256 }),
5257 fdecl::Use::Directory(fdecl::UseDirectory {
5258 dependency_type: Some(fdecl::DependencyType::Strong),
5259 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5260 source_name: Some(format!("{}", "a".repeat(256))),
5261 target_path: Some("/c".repeat(2048)),
5262 rights: Some(fio::Operations::CONNECT),
5263 subdir: None,
5264 ..Default::default()
5265 }),
5266 fdecl::Use::Storage(fdecl::UseStorage {
5267 source_name: Some("cache".to_string()),
5268 target_path: Some("/d".repeat(2048)),
5269 ..Default::default()
5270 }),
5271 ]);
5272 decl
5273 },
5274 result = Err(ErrorList::new(vec![
5275 Error::field_too_long(DeclType::UseService, "source_name"),
5276 Error::field_too_long(DeclType::UseService, "target_path"),
5277 Error::field_too_long(DeclType::UseProtocol, "source_name"),
5278 Error::field_too_long(DeclType::UseProtocol, "target_path"),
5279 Error::field_too_long(DeclType::UseDirectory, "source_name"),
5280 Error::field_too_long(DeclType::UseDirectory, "target_path"),
5281 Error::field_too_long(DeclType::UseStorage, "target_path"),
5282 ])),
5283 },
5284 test_validate_conflicting_paths => {
5285 input = {
5286 let mut decl = new_component_decl();
5287 decl.uses = Some(vec![
5288 fdecl::Use::Service(fdecl::UseService {
5289 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5290 source_name: Some("foo".to_string()),
5291 target_path: Some("/bar".to_string()),
5292 dependency_type: Some(fdecl::DependencyType::Strong),
5293 ..Default::default()
5294 }),
5295 fdecl::Use::Protocol(fdecl::UseProtocol {
5296 dependency_type: Some(fdecl::DependencyType::Strong),
5297 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5298 source_name: Some("space".to_string()),
5299 target_path: Some("/bar".to_string()),
5300 ..Default::default()
5301 }),
5302 fdecl::Use::Directory(fdecl::UseDirectory {
5303 dependency_type: Some(fdecl::DependencyType::Strong),
5304 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5305 source_name: Some("crow".to_string()),
5306 target_path: Some("/bar".to_string()),
5307 rights: Some(fio::Operations::CONNECT),
5308 subdir: None,
5309 ..Default::default()
5310 }),
5311 ]);
5312 decl
5313 },
5314 result = Err(ErrorList::new(vec![
5315 Error::duplicate_field(DeclType::UseProtocol, "target_path", "/bar"),
5316 Error::duplicate_field(DeclType::UseDirectory, "target_path", "/bar"),
5317 ])),
5318 },
5319 test_validate_exposes_empty => {
5321 input = {
5322 let mut decl = new_component_decl();
5323 decl.exposes = Some(vec![
5324 fdecl::Expose::Service(fdecl::ExposeService {
5325 source: None,
5326 source_name: None,
5327 target_name: None,
5328 target: None,
5329 ..Default::default()
5330 }),
5331 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5332 source: None,
5333 source_name: None,
5334 target_name: None,
5335 target: None,
5336 ..Default::default()
5337 }),
5338 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5339 source: None,
5340 source_name: None,
5341 target_name: None,
5342 target: None,
5343 rights: None,
5344 subdir: None,
5345 ..Default::default()
5346 }),
5347 fdecl::Expose::Runner(fdecl::ExposeRunner {
5348 source: None,
5349 source_name: None,
5350 target: None,
5351 target_name: None,
5352 ..Default::default()
5353 }),
5354 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5355 source: None,
5356 source_name: None,
5357 target: None,
5358 target_name: None,
5359 ..Default::default()
5360 }),
5361 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5362 ..Default::default()
5363 }),
5364 ]);
5365 decl
5366 },
5367 result = Err(ErrorList::new(vec![
5368 Error::missing_field(DeclType::ExposeService, "source"),
5369 Error::missing_field(DeclType::ExposeService, "target"),
5370 Error::missing_field(DeclType::ExposeService, "source_name"),
5371 Error::missing_field(DeclType::ExposeService, "target_name"),
5372 Error::missing_field(DeclType::ExposeProtocol, "source"),
5373 Error::missing_field(DeclType::ExposeProtocol, "target"),
5374 Error::missing_field(DeclType::ExposeProtocol, "source_name"),
5375 Error::missing_field(DeclType::ExposeProtocol, "target_name"),
5376 Error::missing_field(DeclType::ExposeDirectory, "source"),
5377 Error::missing_field(DeclType::ExposeDirectory, "target"),
5378 Error::missing_field(DeclType::ExposeDirectory, "source_name"),
5379 Error::missing_field(DeclType::ExposeDirectory, "target_name"),
5380 Error::missing_field(DeclType::ExposeRunner, "source"),
5381 Error::missing_field(DeclType::ExposeRunner, "target"),
5382 Error::missing_field(DeclType::ExposeRunner, "source_name"),
5383 Error::missing_field(DeclType::ExposeRunner, "target_name"),
5384 Error::missing_field(DeclType::ExposeResolver, "source"),
5385 Error::missing_field(DeclType::ExposeResolver, "target"),
5386 Error::missing_field(DeclType::ExposeResolver, "source_name"),
5387 Error::missing_field(DeclType::ExposeResolver, "target_name"),
5388 Error::missing_field(DeclType::ExposeDictionary, "source"),
5389 Error::missing_field(DeclType::ExposeDictionary, "target"),
5390 Error::missing_field(DeclType::ExposeDictionary, "source_name"),
5391 Error::missing_field(DeclType::ExposeDictionary, "target_name"),
5392 ])),
5393 },
5394 test_validate_exposes_extraneous => {
5395 input = {
5396 let mut decl = new_component_decl();
5397 decl.exposes = Some(vec![
5398 fdecl::Expose::Service(fdecl::ExposeService {
5399 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5400 name: "logger".to_string(),
5401 collection: Some("modular".to_string()),
5402 })),
5403 source_name: Some("logger".to_string()),
5404 target_name: Some("logger".to_string()),
5405 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5406 ..Default::default()
5407 }),
5408 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5409 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5410 name: "logger".to_string(),
5411 collection: Some("modular".to_string()),
5412 })),
5413 source_name: Some("legacy_logger".to_string()),
5414 target_name: Some("legacy_logger".to_string()),
5415 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5416 ..Default::default()
5417 }),
5418 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5419 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5420 name: "netstack".to_string(),
5421 collection: Some("modular".to_string()),
5422 })),
5423 source_name: Some("data".to_string()),
5424 target_name: Some("data".to_string()),
5425 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5426 rights: Some(fio::Operations::CONNECT),
5427 subdir: None,
5428 ..Default::default()
5429 }),
5430 fdecl::Expose::Runner(fdecl::ExposeRunner {
5431 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5432 name: "netstack".to_string(),
5433 collection: Some("modular".to_string()),
5434 })),
5435 source_name: Some("elf".to_string()),
5436 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5437 target_name: Some("elf".to_string()),
5438 ..Default::default()
5439 }),
5440 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5441 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5442 name: "netstack".to_string(),
5443 collection: Some("modular".to_string()),
5444 })),
5445 source_name: Some("pkg".to_string()),
5446 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5447 target_name: Some("pkg".to_string()),
5448 ..Default::default()
5449 }),
5450 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5451 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5452 name: "netstack".to_string(),
5453 collection: Some("modular".to_string()),
5454 })),
5455 source_name: Some("dict".to_string()),
5456 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5457 target_name: Some("dict".to_string()),
5458 ..Default::default()
5459 }),
5460 ]);
5461 decl
5462 },
5463 result = Err(ErrorList::new(vec![
5464 Error::extraneous_field(DeclType::ExposeService, "source.child.collection"),
5465 Error::extraneous_field(DeclType::ExposeProtocol, "source.child.collection"),
5466 Error::extraneous_field(DeclType::ExposeDirectory, "source.child.collection"),
5467 Error::extraneous_field(DeclType::ExposeRunner, "source.child.collection"),
5468 Error::extraneous_field(DeclType::ExposeResolver, "source.child.collection"),
5469 Error::extraneous_field(DeclType::ExposeDictionary, "source.child.collection"),
5470 ])),
5471 },
5472 test_validate_exposes_invalid_identifiers => {
5473 input = {
5474 let mut decl = new_component_decl();
5475 decl.exposes = Some(vec![
5476 fdecl::Expose::Service(fdecl::ExposeService {
5477 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5478 name: "^bad".to_string(),
5479 collection: None,
5480 })),
5481 source_name: Some("foo/".to_string()),
5482 target_name: Some("/".to_string()),
5483 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5484 ..Default::default()
5485 }),
5486 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5487 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5488 name: "^bad".to_string(),
5489 collection: None,
5490 })),
5491 source_name: Some("foo/".to_string()),
5492 target_name: Some("/".to_string()),
5493 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5494 ..Default::default()
5495 }),
5496 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5497 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5498 name: "^bad".to_string(),
5499 collection: None,
5500 })),
5501 source_name: Some("foo/".to_string()),
5502 target_name: Some("/".to_string()),
5503 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5504 rights: Some(fio::Operations::CONNECT),
5505 subdir: Some("/foo".to_string()),
5506 ..Default::default()
5507 }),
5508 fdecl::Expose::Runner(fdecl::ExposeRunner {
5509 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5510 name: "^bad".to_string(),
5511 collection: None,
5512 })),
5513 source_name: Some("/path".to_string()),
5514 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5515 target_name: Some("elf!".to_string()),
5516 ..Default::default()
5517 }),
5518 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5519 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5520 name: "^bad".to_string(),
5521 collection: None,
5522 })),
5523 source_name: Some("/path".to_string()),
5524 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5525 target_name: Some("pkg!".to_string()),
5526 ..Default::default()
5527 }),
5528 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5529 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5530 name: "^bad".to_string(),
5531 collection: None,
5532 })),
5533 source_name: Some("/path".to_string()),
5534 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5535 target_name: Some("pkg!".to_string()),
5536 ..Default::default()
5537 }),
5538 ]);
5539 decl
5540 },
5541 result = Err(ErrorList::new(vec![
5542 Error::invalid_field(DeclType::ExposeService, "source.child.name"),
5543 Error::invalid_field(DeclType::ExposeService, "source_name"),
5544 Error::invalid_field(DeclType::ExposeService, "target_name"),
5545 Error::invalid_field(DeclType::ExposeProtocol, "source.child.name"),
5546 Error::invalid_field(DeclType::ExposeProtocol, "source_name"),
5547 Error::invalid_field(DeclType::ExposeProtocol, "target_name"),
5548 Error::invalid_field(DeclType::ExposeDirectory, "source.child.name"),
5549 Error::invalid_field(DeclType::ExposeDirectory, "source_name"),
5550 Error::invalid_field(DeclType::ExposeDirectory, "target_name"),
5551 Error::invalid_field(DeclType::ExposeDirectory, "subdir"),
5552 Error::invalid_field(DeclType::ExposeRunner, "source.child.name"),
5553 Error::invalid_field(DeclType::ExposeRunner, "source_name"),
5554 Error::invalid_field(DeclType::ExposeRunner, "target_name"),
5555 Error::invalid_field(DeclType::ExposeResolver, "source.child.name"),
5556 Error::invalid_field(DeclType::ExposeResolver, "source_name"),
5557 Error::invalid_field(DeclType::ExposeResolver, "target_name"),
5558 Error::invalid_field(DeclType::ExposeDictionary, "source.child.name"),
5559 Error::invalid_field(DeclType::ExposeDictionary, "source_name"),
5560 Error::invalid_field(DeclType::ExposeDictionary, "target_name"),
5561 ])),
5562 },
5563 test_validate_exposes_invalid_source_target => {
5564 input = {
5565 let mut decl = new_component_decl();
5566 decl.children = Some(vec![fdecl::Child{
5567 name: Some("logger".to_string()),
5568 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5569 startup: Some(fdecl::StartupMode::Lazy),
5570 on_terminate: None,
5571 environment: None,
5572 ..Default::default()
5573 }]);
5574 decl.exposes = Some(vec![
5575 fdecl::Expose::Service(fdecl::ExposeService {
5576 source: None,
5577 source_name: Some("a".to_string()),
5578 target_name: Some("b".to_string()),
5579 target: None,
5580 ..Default::default()
5581 }),
5582 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5583 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5584 source_name: Some("c".to_string()),
5585 target_name: Some("d".to_string()),
5586 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5587 ..Default::default()
5588 }),
5589 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5590 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "z".to_string()})),
5591 source_name: Some("e".to_string()),
5592 target_name: Some("f".to_string()),
5593 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "z".to_string()})),
5594 rights: Some(fio::Operations::CONNECT),
5595 subdir: None,
5596 ..Default::default()
5597 }),
5598 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5599 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5600 source_name: Some("g".to_string()),
5601 target_name: Some("h".to_string()),
5602 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5603 rights: Some(fio::Operations::CONNECT),
5604 subdir: None,
5605 ..Default::default()
5606 }),
5607 fdecl::Expose::Runner(fdecl::ExposeRunner {
5608 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5609 source_name: Some("i".to_string()),
5610 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5611 target_name: Some("j".to_string()),
5612 ..Default::default()
5613 }),
5614 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5615 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5616 source_name: Some("k".to_string()),
5617 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5618 target_name: Some("l".to_string()),
5619 ..Default::default()
5620 }),
5621 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5622 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5623 name: "logger".to_string(),
5624 collection: None,
5625 })),
5626 source_name: Some("m".to_string()),
5627 target_name: Some("n".to_string()),
5628 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5629 ..Default::default()
5630 }),
5631 ]);
5632 decl
5633 },
5634 result = Err(ErrorList::new(vec![
5635 Error::missing_field(DeclType::ExposeService, "source"),
5636 Error::missing_field(DeclType::ExposeService, "target"),
5637 Error::invalid_field(DeclType::ExposeProtocol, "source"),
5638 Error::invalid_field(DeclType::ExposeProtocol, "target"),
5639 Error::invalid_field(DeclType::ExposeDirectory, "source"),
5640 Error::invalid_field(DeclType::ExposeDirectory, "target"),
5641 Error::invalid_field(DeclType::ExposeDirectory, "source"),
5642 Error::invalid_field(DeclType::ExposeDirectory, "target"),
5643 Error::invalid_field(DeclType::ExposeRunner, "source"),
5644 Error::invalid_field(DeclType::ExposeRunner, "target"),
5645 Error::invalid_field(DeclType::ExposeResolver, "source"),
5646 Error::invalid_field(DeclType::ExposeResolver, "target"),
5647 Error::invalid_field(DeclType::ExposeDictionary, "target"),
5648 ])),
5649 },
5650 test_validate_exposes_invalid_source_collection => {
5651 input = {
5652 let mut decl = new_component_decl();
5653 decl.collections = Some(vec![fdecl::Collection{
5654 name: Some("col".to_string()),
5655 durability: Some(fdecl::Durability::Transient),
5656 allowed_offers: None,
5657 allow_long_names: None,
5658 ..Default::default()
5659 }]);
5660 decl.exposes = Some(vec![
5661 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5662 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
5663 source_name: Some("a".to_string()),
5664 target_name: Some("a".to_string()),
5665 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5666 ..Default::default()
5667 }),
5668 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5669 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5670 source_name: Some("b".to_string()),
5671 target_name: Some("b".to_string()),
5672 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5673 rights: Some(fio::Operations::CONNECT),
5674 subdir: None,
5675 ..Default::default()
5676 }),
5677 fdecl::Expose::Runner(fdecl::ExposeRunner {
5678 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5679 source_name: Some("c".to_string()),
5680 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5681 target_name: Some("c".to_string()),
5682 ..Default::default()
5683 }),
5684 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5685 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5686 source_name: Some("d".to_string()),
5687 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5688 target_name: Some("d".to_string()),
5689 ..Default::default()
5690 }),
5691 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5692 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5693 source_name: Some("e".to_string()),
5694 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5695 target_name: Some("e".to_string()),
5696 ..Default::default()
5697 }),
5698 ]);
5699 decl
5700 },
5701 result = Err(ErrorList::new(vec![
5702 Error::invalid_field(DeclType::ExposeProtocol, "source"),
5703 Error::invalid_field(DeclType::ExposeDirectory, "source"),
5704 Error::invalid_field(DeclType::ExposeRunner, "source"),
5705 Error::invalid_field(DeclType::ExposeResolver, "source"),
5706 Error::invalid_field(DeclType::ExposeDictionary, "source"),
5707 ])),
5708 },
5709 test_validate_exposes_sources_collection => {
5710 input = {
5711 let mut decl = new_component_decl();
5712 decl.collections = Some(vec![
5713 fdecl::Collection {
5714 name: Some("col".to_string()),
5715 durability: Some(fdecl::Durability::Transient),
5716 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
5717 allow_long_names: None,
5718 ..Default::default()
5719 }
5720 ]);
5721 decl.exposes = Some(vec![
5722 fdecl::Expose::Service(fdecl::ExposeService {
5723 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
5724 source_name: Some("a".to_string()),
5725 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5726 target_name: Some("a".to_string()),
5727 ..Default::default()
5728 })
5729 ]);
5730 decl
5731 },
5732 result = Ok(()),
5733 },
5734 test_validate_exposes_long_identifiers => {
5735 input = {
5736 let mut decl = new_component_decl();
5737 decl.exposes = Some(vec![
5738 fdecl::Expose::Service(fdecl::ExposeService {
5739 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5740 name: "b".repeat(256),
5741 collection: None,
5742 })),
5743 source_name: Some(format!("{}", "a".repeat(1025))),
5744 target_name: Some(format!("{}", "b".repeat(1025))),
5745 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5746 ..Default::default()
5747 }),
5748 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5749 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5750 name: "b".repeat(256),
5751 collection: None,
5752 })),
5753 source_name: Some(format!("{}", "a".repeat(256))),
5754 target_name: Some(format!("{}", "b".repeat(256))),
5755 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5756 ..Default::default()
5757 }),
5758 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5759 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5760 name: "b".repeat(256),
5761 collection: None,
5762 })),
5763 source_name: Some(format!("{}", "a".repeat(256))),
5764 target_name: Some(format!("{}", "b".repeat(256))),
5765 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5766 rights: Some(fio::Operations::CONNECT),
5767 subdir: None,
5768 ..Default::default()
5769 }),
5770 fdecl::Expose::Runner(fdecl::ExposeRunner {
5771 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5772 name: "b".repeat(256),
5773 collection: None,
5774 })),
5775 source_name: Some("a".repeat(256)),
5776 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5777 target_name: Some("b".repeat(256)),
5778 ..Default::default()
5779 }),
5780 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5781 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5782 name: "b".repeat(256),
5783 collection: None,
5784 })),
5785 source_name: Some("a".repeat(256)),
5786 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5787 target_name: Some("b".repeat(256)),
5788 ..Default::default()
5789 }),
5790 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5791 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5792 name: "b".repeat(256),
5793 collection: None,
5794 })),
5795 source_name: Some("a".repeat(256)),
5796 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5797 target_name: Some("b".repeat(256)),
5798 ..Default::default()
5799 }),
5800 ]);
5801 decl
5802 },
5803 result = Err(ErrorList::new(vec![
5804 Error::field_too_long(DeclType::ExposeService, "source.child.name"),
5805 Error::field_too_long(DeclType::ExposeService, "source_name"),
5806 Error::field_too_long(DeclType::ExposeService, "target_name"),
5807 Error::field_too_long(DeclType::ExposeProtocol, "source.child.name"),
5808 Error::field_too_long(DeclType::ExposeProtocol, "source_name"),
5809 Error::field_too_long(DeclType::ExposeProtocol, "target_name"),
5810 Error::field_too_long(DeclType::ExposeDirectory, "source.child.name"),
5811 Error::field_too_long(DeclType::ExposeDirectory, "source_name"),
5812 Error::field_too_long(DeclType::ExposeDirectory, "target_name"),
5813 Error::field_too_long(DeclType::ExposeRunner, "source.child.name"),
5814 Error::field_too_long(DeclType::ExposeRunner, "source_name"),
5815 Error::field_too_long(DeclType::ExposeRunner, "target_name"),
5816 Error::field_too_long(DeclType::ExposeResolver, "source.child.name"),
5817 Error::field_too_long(DeclType::ExposeResolver, "source_name"),
5818 Error::field_too_long(DeclType::ExposeResolver, "target_name"),
5819 Error::field_too_long(DeclType::ExposeDictionary, "source.child.name"),
5820 Error::field_too_long(DeclType::ExposeDictionary, "source_name"),
5821 Error::field_too_long(DeclType::ExposeDictionary, "target_name"),
5822 ])),
5823 },
5824 test_validate_exposes_invalid_child => {
5825 input = {
5826 let mut decl = new_component_decl();
5827 decl.exposes = Some(vec![
5828 fdecl::Expose::Service(fdecl::ExposeService {
5829 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5830 name: "netstack".to_string(),
5831 collection: None,
5832 })),
5833 source_name: Some("fuchsia.logger.Log".to_string()),
5834 target_name: Some("fuchsia.logger.Log".to_string()),
5835 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5836 ..Default::default()
5837 }),
5838 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5839 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5840 name: "netstack".to_string(),
5841 collection: None,
5842 })),
5843 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
5844 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
5845 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5846 ..Default::default()
5847 }),
5848 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5849 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5850 name: "netstack".to_string(),
5851 collection: None,
5852 })),
5853 source_name: Some("data".to_string()),
5854 target_name: Some("data".to_string()),
5855 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5856 rights: Some(fio::Operations::CONNECT),
5857 subdir: None,
5858 ..Default::default()
5859 }),
5860 fdecl::Expose::Runner(fdecl::ExposeRunner {
5861 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5862 name: "netstack".to_string(),
5863 collection: None,
5864 })),
5865 source_name: Some("elf".to_string()),
5866 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5867 target_name: Some("elf".to_string()),
5868 ..Default::default()
5869 }),
5870 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5871 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5872 name: "netstack".to_string(),
5873 collection: None,
5874 })),
5875 source_name: Some("pkg".to_string()),
5876 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5877 target_name: Some("pkg".to_string()),
5878 ..Default::default()
5879 }),
5880 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5881 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5882 name: "netstack".to_string(),
5883 collection: None,
5884 })),
5885 source_name: Some("dict".to_string()),
5886 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5887 target_name: Some("dict".to_string()),
5888 ..Default::default()
5889 }),
5890 ]);
5891 decl
5892 },
5893 result = Err(ErrorList::new(vec![
5894 Error::invalid_child(DeclType::ExposeService, "source", "netstack"),
5895 Error::invalid_child(DeclType::ExposeProtocol, "source", "netstack"),
5896 Error::invalid_child(DeclType::ExposeDirectory, "source", "netstack"),
5897 Error::invalid_child(DeclType::ExposeRunner, "source", "netstack"),
5898 Error::invalid_child(DeclType::ExposeResolver, "source", "netstack"),
5899 Error::invalid_child(DeclType::ExposeDictionary, "source", "netstack"),
5900 ])),
5901 },
5902 test_validate_exposes_invalid_source_capability => {
5903 input = {
5904 fdecl::Component {
5905 exposes: Some(vec![
5906 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5907 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
5908 name: "this-storage-doesnt-exist".to_string(),
5909 })),
5910 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
5911 target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
5912 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5913 ..Default::default()
5914 }),
5915 ]),
5916 ..new_component_decl()
5917 }
5918 },
5919 result = Err(ErrorList::new(vec![
5920 Error::invalid_capability(DeclType::ExposeProtocol, "source", "this-storage-doesnt-exist"),
5921 ])),
5922 },
5923 test_validate_exposes_duplicate_target => {
5924 input = {
5925 let mut decl = new_component_decl();
5926 decl.exposes = Some(vec![
5927 fdecl::Expose::Service(fdecl::ExposeService {
5928 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
5929 name: "coll".into(),
5930 })),
5931 source_name: Some("netstack".to_string()),
5932 target_name: Some("fuchsia.net.Stack".to_string()),
5933 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5934 ..Default::default()
5935 }),
5936 fdecl::Expose::Service(fdecl::ExposeService {
5937 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
5938 name: "coll2".into(),
5939 })),
5940 source_name: Some("netstack2".to_string()),
5941 target_name: Some("fuchsia.net.Stack".to_string()),
5942 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5943 ..Default::default()
5944 }),
5945 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5946 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5947 source_name: Some("fonts".to_string()),
5948 target_name: Some("fuchsia.fonts.Provider".to_string()),
5949 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5950 ..Default::default()
5951 }),
5952 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5953 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5954 source_name: Some("fonts2".to_string()),
5955 target_name: Some("fuchsia.fonts.Provider".to_string()),
5956 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5957 ..Default::default()
5958 }),
5959 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5960 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5961 source_name: Some("assets".to_string()),
5962 target_name: Some("stuff".to_string()),
5963 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5964 rights: None,
5965 subdir: None,
5966 ..Default::default()
5967 }),
5968 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5969 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5970 source_name: Some("assets2".to_string()),
5971 target_name: Some("stuff".to_string()),
5972 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5973 rights: None,
5974 subdir: None,
5975 ..Default::default()
5976 }),
5977 fdecl::Expose::Runner(fdecl::ExposeRunner {
5978 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5979 source_name: Some("source_elf".to_string()),
5980 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5981 target_name: Some("elf".to_string()),
5982 ..Default::default()
5983 }),
5984 fdecl::Expose::Runner(fdecl::ExposeRunner {
5985 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5986 source_name: Some("source_elf".to_string()),
5987 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5988 target_name: Some("elf".to_string()),
5989 ..Default::default()
5990 }),
5991 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5992 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5993 source_name: Some("source_pkg".to_string()),
5994 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5995 target_name: Some("pkg".to_string()),
5996 ..Default::default()
5997 }),
5998 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5999 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6000 source_name: Some("source_pkg".to_string()),
6001 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6002 target_name: Some("pkg".to_string()),
6003 ..Default::default()
6004 }),
6005 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
6006 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6007 source_name: Some("source_dict".to_string()),
6008 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6009 target_name: Some("dict".to_string()),
6010 ..Default::default()
6011 }),
6012 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
6013 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6014 source_name: Some("source_dict".to_string()),
6015 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6016 target_name: Some("dict".to_string()),
6017 ..Default::default()
6018 }),
6019 ]);
6020 decl.collections = Some(vec![
6021 fdecl::Collection {
6022 name: Some("coll".into()),
6023 durability: Some(fdecl::Durability::Transient),
6024 ..Default::default()
6025 },
6026 fdecl::Collection {
6027 name: Some("coll2".into()),
6028 durability: Some(fdecl::Durability::Transient),
6029 ..Default::default()
6030 },
6031 ]);
6032 decl.capabilities = Some(vec![
6033 fdecl::Capability::Service(fdecl::Service {
6034 name: Some("netstack".to_string()),
6035 source_path: Some("/path".to_string()),
6036 ..Default::default()
6037 }),
6038 fdecl::Capability::Service(fdecl::Service {
6039 name: Some("netstack2".to_string()),
6040 source_path: Some("/path".to_string()),
6041 ..Default::default()
6042 }),
6043 fdecl::Capability::Protocol(fdecl::Protocol {
6044 name: Some("fonts".to_string()),
6045 source_path: Some("/path".to_string()),
6046 ..Default::default()
6047 }),
6048 fdecl::Capability::Protocol(fdecl::Protocol {
6049 name: Some("fonts2".to_string()),
6050 source_path: Some("/path".to_string()),
6051 ..Default::default()
6052 }),
6053 fdecl::Capability::Directory(fdecl::Directory {
6054 name: Some("assets".to_string()),
6055 source_path: Some("/path".to_string()),
6056 rights: Some(fio::Operations::CONNECT),
6057 ..Default::default()
6058 }),
6059 fdecl::Capability::Directory(fdecl::Directory {
6060 name: Some("assets2".to_string()),
6061 source_path: Some("/path".to_string()),
6062 rights: Some(fio::Operations::CONNECT),
6063 ..Default::default()
6064 }),
6065 fdecl::Capability::Runner(fdecl::Runner {
6066 name: Some("source_elf".to_string()),
6067 source_path: Some("/path".to_string()),
6068 ..Default::default()
6069 }),
6070 fdecl::Capability::Resolver(fdecl::Resolver {
6071 name: Some("source_pkg".to_string()),
6072 source_path: Some("/path".to_string()),
6073 ..Default::default()
6074 }),
6075 fdecl::Capability::Dictionary(fdecl::Dictionary {
6076 name: Some("source_dict".to_string()),
6077 ..Default::default()
6078 }),
6079 ]);
6080 decl
6081 },
6082 result = Err(ErrorList::new(vec![
6083 Error::duplicate_field(DeclType::ExposeProtocol, "target_name",
6085 "fuchsia.fonts.Provider"),
6086 Error::duplicate_field(DeclType::ExposeDirectory, "target_name",
6087 "stuff"),
6088 Error::duplicate_field(DeclType::ExposeRunner, "target_name",
6089 "elf"),
6090 Error::duplicate_field(DeclType::ExposeResolver, "target_name", "pkg"),
6091 Error::duplicate_field(DeclType::ExposeDictionary, "target_name", "dict"),
6092 ])),
6093 },
6094 test_validate_exposes_invalid_capability_from_self => {
6095 input = {
6096 let mut decl = new_component_decl();
6097 decl.exposes = Some(vec![
6098 fdecl::Expose::Service(fdecl::ExposeService {
6099 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6100 source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
6101 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6102 target_name: Some("foo".to_string()),
6103 ..Default::default()
6104 }),
6105 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6106 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6107 source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
6108 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6109 target_name: Some("bar".to_string()),
6110 ..Default::default()
6111 }),
6112 fdecl::Expose::Directory(fdecl::ExposeDirectory {
6113 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6114 source_name: Some("dir".to_string()),
6115 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6116 target_name: Some("assets".to_string()),
6117 rights: None,
6118 subdir: None,
6119 ..Default::default()
6120 }),
6121 fdecl::Expose::Runner(fdecl::ExposeRunner {
6122 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6123 source_name: Some("source_elf".to_string()),
6124 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6125 target_name: Some("elf".to_string()),
6126 ..Default::default()
6127 }),
6128 fdecl::Expose::Resolver(fdecl::ExposeResolver {
6129 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6130 source_name: Some("source_pkg".to_string()),
6131 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6132 target_name: Some("pkg".to_string()),
6133 ..Default::default()
6134 }),
6135 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
6136 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6137 source_name: Some("source_dict".to_string()),
6138 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6139 target_name: Some("dict".to_string()),
6140 ..Default::default()
6141 }),
6142 fdecl::Expose::Config(fdecl::ExposeConfiguration {
6143 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6144 source_name: Some("source_config".to_string()),
6145 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6146 target_name: Some("config".to_string()),
6147 ..Default::default()
6148 }),
6149 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6150 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6151 source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
6152 source_dictionary: Some("dict/inner".to_string()),
6153 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6154 target_name: Some("baz".to_string()),
6155 ..Default::default()
6156 }),
6157 ]);
6158 decl
6159 },
6160 result = Err(ErrorList::new(vec![
6161 Error::invalid_capability(
6162 DeclType::ExposeService,
6163 "source",
6164 "fuchsia.some.library.SomeProtocol"),
6165 Error::invalid_capability(
6166 DeclType::ExposeProtocol,
6167 "source",
6168 "fuchsia.some.library.SomeProtocol"),
6169 Error::invalid_capability(DeclType::ExposeDirectory, "source", "dir"),
6170 Error::invalid_capability(DeclType::ExposeRunner, "source", "source_elf"),
6171 Error::invalid_capability(DeclType::ExposeResolver, "source", "source_pkg"),
6172 Error::invalid_capability(DeclType::ExposeDictionary, "source", "source_dict"),
6173 Error::invalid_capability(DeclType::ExposeConfig, "source", "source_config"),
6174 Error::invalid_capability(DeclType::ExposeProtocol, "source", "dict"),
6175 ])),
6176 },
6177
6178 test_validate_exposes_availability_service => {
6179 input = {
6180 let mut decl = generate_expose_different_source_and_availability_decl(
6181 |source, availability, target_name|
6182 fdecl::Expose::Service(fdecl::ExposeService {
6183 source: Some(source),
6184 source_name: Some("fuchsia.examples.Echo".to_string()),
6185 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6186 target_name: Some(target_name.to_string()),
6187 availability: Some(availability),
6188 ..Default::default()
6189 })
6190 );
6191 decl.capabilities = Some(vec![
6192 fdecl::Capability::Service(fdecl::Service {
6193 name: Some("fuchsia.examples.Echo".to_string()),
6194 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
6195 ..Default::default()
6196 }),
6197 ]);
6198 decl
6199 },
6200 result = {
6201 Err(ErrorList::new(vec![
6202 Error::availability_must_be_optional(
6203 DeclType::ExposeService,
6204 "availability",
6205 Some(&"fuchsia.examples.Echo".to_string()),
6206 ),
6207 Error::availability_must_be_optional(
6208 DeclType::ExposeService,
6209 "availability",
6210 Some(&"fuchsia.examples.Echo".to_string()),
6211 ),
6212 ]))
6213 },
6214 },
6215 test_validate_exposes_availability_protocol => {
6216 input = {
6217 let mut decl = generate_expose_different_source_and_availability_decl(
6218 |source, availability, target_name|
6219 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6220 source: Some(source),
6221 source_name: Some("fuchsia.examples.Echo".to_string()),
6222 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6223 target_name: Some(target_name.to_string()),
6224 availability: Some(availability),
6225 ..Default::default()
6226 })
6227 );
6228 decl.capabilities = Some(vec![
6229 fdecl::Capability::Protocol(fdecl::Protocol {
6230 name: Some("fuchsia.examples.Echo".to_string()),
6231 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
6232 ..Default::default()
6233 }),
6234 ]);
6235 decl
6236 },
6237 result = {
6238 Err(ErrorList::new(vec![
6239 Error::availability_must_be_optional(
6240 DeclType::ExposeProtocol,
6241 "availability",
6242 Some(&"fuchsia.examples.Echo".to_string()),
6243 ),
6244 Error::availability_must_be_optional(
6245 DeclType::ExposeProtocol,
6246 "availability",
6247 Some(&"fuchsia.examples.Echo".to_string()),
6248 ),
6249 ]))
6250 },
6251 },
6252 test_validate_exposes_availability_directory => {
6253 input = {
6254 let mut decl = generate_expose_different_source_and_availability_decl(
6255 |source, availability, target_name|
6256 fdecl::Expose::Directory(fdecl::ExposeDirectory {
6257 source: Some(source),
6258 source_name: Some("fuchsia.examples.Echo".to_string()),
6259 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6260 target_name: Some(target_name.to_string()),
6261 availability: Some(availability),
6262 ..Default::default()
6263 })
6264 );
6265 decl.capabilities = Some(vec![
6266 fdecl::Capability::Directory(fdecl::Directory {
6267 name: Some("fuchsia.examples.Echo".to_string()),
6268 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
6269 rights: Some(fio::Operations::READ_BYTES),
6270 ..Default::default()
6271 }),
6272 ]);
6273 decl
6274 },
6275 result = {
6276 Err(ErrorList::new(vec![
6277 Error::availability_must_be_optional(
6278 DeclType::ExposeDirectory,
6279 "availability",
6280 Some(&"fuchsia.examples.Echo".to_string()),
6281 ),
6282 Error::availability_must_be_optional(
6283 DeclType::ExposeDirectory,
6284 "availability",
6285 Some(&"fuchsia.examples.Echo".to_string()),
6286 ),
6287 ]))
6288 },
6289 },
6290
6291 test_validate_offers_empty => {
6293 input = {
6294 let mut decl = new_component_decl();
6295 decl.offers = Some(vec![
6296 fdecl::Offer::Service(fdecl::OfferService {
6297 source: None,
6298 source_name: None,
6299 target: None,
6300 target_name: None,
6301 ..Default::default()
6302 }),
6303 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6304 source: None,
6305 source_name: None,
6306 target: None,
6307 target_name: None,
6308 dependency_type: None,
6309 ..Default::default()
6310 }),
6311 fdecl::Offer::Directory(fdecl::OfferDirectory {
6312 source: None,
6313 source_name: None,
6314 target: None,
6315 target_name: None,
6316 rights: None,
6317 subdir: None,
6318 dependency_type: None,
6319 ..Default::default()
6320 }),
6321 fdecl::Offer::Storage(fdecl::OfferStorage {
6322 source_name: None,
6323 source: None,
6324 target: None,
6325 target_name: None,
6326 ..Default::default()
6327 }),
6328 fdecl::Offer::Runner(fdecl::OfferRunner {
6329 source: None,
6330 source_name: None,
6331 target: None,
6332 target_name: None,
6333 ..Default::default()
6334 }),
6335 fdecl::Offer::Resolver(fdecl::OfferResolver {
6336 ..Default::default()
6337 }),
6338 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6339 ..Default::default()
6340 }),
6341 ]);
6342 decl
6343 },
6344 result = Err(ErrorList::new(vec![
6347 Error::missing_field(DeclType::OfferService, "source"),
6348 Error::missing_field(DeclType::OfferService, "source_name"),
6349 Error::missing_field(DeclType::OfferService, "target"),
6350 Error::missing_field(DeclType::OfferService, "target_name"),
6351 Error::missing_field(DeclType::OfferProtocol, "source"),
6353 Error::missing_field(DeclType::OfferProtocol, "source_name"),
6354 Error::missing_field(DeclType::OfferProtocol, "target"),
6355 Error::missing_field(DeclType::OfferProtocol, "target_name"),
6356 Error::missing_field(DeclType::OfferProtocol, "dependency_type"),
6357 Error::missing_field(DeclType::OfferDirectory, "source"),
6359 Error::missing_field(DeclType::OfferDirectory, "source_name"),
6360 Error::missing_field(DeclType::OfferDirectory, "target"),
6361 Error::missing_field(DeclType::OfferDirectory, "target_name"),
6362 Error::missing_field(DeclType::OfferDirectory, "dependency_type"),
6363 Error::missing_field(DeclType::OfferStorage, "source"),
6365 Error::missing_field(DeclType::OfferStorage, "source_name"),
6366 Error::missing_field(DeclType::OfferStorage, "target"),
6367 Error::missing_field(DeclType::OfferStorage, "target_name"),
6368 Error::missing_field(DeclType::OfferRunner, "source"),
6370 Error::missing_field(DeclType::OfferRunner, "source_name"),
6371 Error::missing_field(DeclType::OfferRunner, "target"),
6372 Error::missing_field(DeclType::OfferRunner, "target_name"),
6373 Error::missing_field(DeclType::OfferResolver, "source"),
6375 Error::missing_field(DeclType::OfferResolver, "source_name"),
6376 Error::missing_field(DeclType::OfferResolver, "target"),
6377 Error::missing_field(DeclType::OfferResolver, "target_name"),
6378 Error::missing_field(DeclType::OfferDictionary, "source"),
6379 Error::missing_field(DeclType::OfferDictionary, "source_name"),
6380 Error::missing_field(DeclType::OfferDictionary, "target"),
6381 Error::missing_field(DeclType::OfferDictionary, "target_name"),
6382 Error::missing_field(DeclType::OfferDictionary, "dependency_type"),
6383 ])),
6384 },
6385 test_validate_offers_long_identifiers => {
6386 input = {
6387 let mut decl = new_component_decl();
6388 decl.offers = Some(vec![
6389 fdecl::Offer::Service(fdecl::OfferService {
6390 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6391 name: "a".repeat(256),
6392 collection: None,
6393 })),
6394 source_name: Some(format!("{}", "a".repeat(256))),
6395 target: Some(fdecl::Ref::Child(
6396 fdecl::ChildRef {
6397 name: "b".repeat(256),
6398 collection: None,
6399 }
6400 )),
6401 target_name: Some(format!("{}", "b".repeat(256))),
6402 ..Default::default()
6403 }),
6404 fdecl::Offer::Service(fdecl::OfferService {
6405 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6406 source_name: Some("a".to_string()),
6407 target: Some(fdecl::Ref::Collection(
6408 fdecl::CollectionRef {
6409 name: "b".repeat(256),
6410 }
6411 )),
6412 target_name: Some(format!("{}", "b".repeat(256))),
6413 ..Default::default()
6414 }),
6415 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6416 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6417 name: "a".repeat(256),
6418 collection: None,
6419 })),
6420 source_name: Some(format!("{}", "a".repeat(256))),
6421 target: Some(fdecl::Ref::Child(
6422 fdecl::ChildRef {
6423 name: "b".repeat(256),
6424 collection: None,
6425 }
6426 )),
6427 target_name: Some(format!("{}", "b".repeat(256))),
6428 dependency_type: Some(fdecl::DependencyType::Strong),
6429 ..Default::default()
6430 }),
6431 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6432 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6433 source_name: Some("a".to_string()),
6434 target: Some(fdecl::Ref::Collection(
6435 fdecl::CollectionRef {
6436 name: "b".repeat(256),
6437 }
6438 )),
6439 target_name: Some(format!("{}", "b".repeat(256))),
6440 dependency_type: Some(fdecl::DependencyType::Weak),
6441 ..Default::default()
6442 }),
6443 fdecl::Offer::Directory(fdecl::OfferDirectory {
6444 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6445 name: "a".repeat(256),
6446 collection: None,
6447 })),
6448 source_name: Some(format!("{}", "a".repeat(256))),
6449 target: Some(fdecl::Ref::Child(
6450 fdecl::ChildRef {
6451 name: "b".repeat(256),
6452 collection: None,
6453 }
6454 )),
6455 target_name: Some(format!("{}", "b".repeat(256))),
6456 rights: Some(fio::Operations::CONNECT),
6457 subdir: None,
6458 dependency_type: Some(fdecl::DependencyType::Strong),
6459 ..Default::default()
6460 }),
6461 fdecl::Offer::Directory(fdecl::OfferDirectory {
6462 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6463 source_name: Some("a".to_string()),
6464 target: Some(fdecl::Ref::Collection(
6465 fdecl::CollectionRef {
6466 name: "b".repeat(256),
6467 }
6468 )),
6469 target_name: Some(format!("{}", "b".repeat(256))),
6470 rights: Some(fio::Operations::CONNECT),
6471 subdir: None,
6472 dependency_type: Some(fdecl::DependencyType::Weak),
6473 ..Default::default()
6474 }),
6475 fdecl::Offer::Storage(fdecl::OfferStorage {
6476 source_name: Some("data".to_string()),
6477 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6478 target: Some(fdecl::Ref::Child(
6479 fdecl::ChildRef {
6480 name: "b".repeat(256),
6481 collection: None,
6482 }
6483 )),
6484 target_name: Some("data".to_string()),
6485 ..Default::default()
6486 }),
6487 fdecl::Offer::Storage(fdecl::OfferStorage {
6488 source_name: Some("data".to_string()),
6489 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6490 target: Some(fdecl::Ref::Collection(
6491 fdecl::CollectionRef { name: "b".repeat(256) }
6492 )),
6493 target_name: Some("data".to_string()),
6494 ..Default::default()
6495 }),
6496 fdecl::Offer::Runner(fdecl::OfferRunner {
6497 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6498 name: "a".repeat(256),
6499 collection: None,
6500 })),
6501 source_name: Some("b".repeat(256)),
6502 target: Some(fdecl::Ref::Collection(
6503 fdecl::CollectionRef {
6504 name: "c".repeat(256),
6505 }
6506 )),
6507 target_name: Some("d".repeat(256)),
6508 ..Default::default()
6509 }),
6510 fdecl::Offer::Resolver(fdecl::OfferResolver {
6511 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6512 name: "a".repeat(256),
6513 collection: None,
6514 })),
6515 source_name: Some("b".repeat(256)),
6516 target: Some(fdecl::Ref::Collection(
6517 fdecl::CollectionRef {
6518 name: "c".repeat(256),
6519 }
6520 )),
6521 target_name: Some("d".repeat(256)),
6522 ..Default::default()
6523 }),
6524 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6525 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6526 name: "a".repeat(256),
6527 collection: None,
6528 })),
6529 source_name: Some("b".repeat(256)),
6530 target: Some(fdecl::Ref::Collection(
6531 fdecl::CollectionRef {
6532 name: "c".repeat(256),
6533 }
6534 )),
6535 target_name: Some("d".repeat(256)),
6536 dependency_type: Some(fdecl::DependencyType::Strong),
6537 ..Default::default()
6538 }),
6539 ]);
6540 decl
6541 },
6542 result = Err(ErrorList::new(vec![
6543 Error::field_too_long(DeclType::OfferService, "source.child.name"),
6544 Error::field_too_long(DeclType::OfferService, "source_name"),
6545 Error::field_too_long(DeclType::OfferService, "target.child.name"),
6546 Error::field_too_long(DeclType::OfferService, "target_name"),
6547 Error::field_too_long(DeclType::OfferService, "target.collection.name"),
6548 Error::field_too_long(DeclType::OfferService, "target_name"),
6549 Error::field_too_long(DeclType::OfferProtocol, "source.child.name"),
6550 Error::field_too_long(DeclType::OfferProtocol, "source_name"),
6551 Error::field_too_long(DeclType::OfferProtocol, "target.child.name"),
6552 Error::field_too_long(DeclType::OfferProtocol, "target_name"),
6553 Error::field_too_long(DeclType::OfferProtocol, "target.collection.name"),
6554 Error::field_too_long(DeclType::OfferProtocol, "target_name"),
6555 Error::field_too_long(DeclType::OfferDirectory, "source.child.name"),
6556 Error::field_too_long(DeclType::OfferDirectory, "source_name"),
6557 Error::field_too_long(DeclType::OfferDirectory, "target.child.name"),
6558 Error::field_too_long(DeclType::OfferDirectory, "target_name"),
6559 Error::field_too_long(DeclType::OfferDirectory, "target.collection.name"),
6560 Error::field_too_long(DeclType::OfferDirectory, "target_name"),
6561 Error::field_too_long(DeclType::OfferStorage, "target.child.name"),
6562 Error::field_too_long(DeclType::OfferStorage, "target.collection.name"),
6563 Error::field_too_long(DeclType::OfferRunner, "source.child.name"),
6564 Error::field_too_long(DeclType::OfferRunner, "source_name"),
6565 Error::field_too_long(DeclType::OfferRunner, "target.collection.name"),
6566 Error::field_too_long(DeclType::OfferRunner, "target_name"),
6567 Error::field_too_long(DeclType::OfferResolver, "source.child.name"),
6568 Error::field_too_long(DeclType::OfferResolver, "source_name"),
6569 Error::field_too_long(DeclType::OfferResolver, "target.collection.name"),
6570 Error::field_too_long(DeclType::OfferResolver, "target_name"),
6571 Error::field_too_long(DeclType::OfferDictionary, "source.child.name"),
6572 Error::field_too_long(DeclType::OfferDictionary, "source_name"),
6573 Error::field_too_long(DeclType::OfferDictionary, "target.collection.name"),
6574 Error::field_too_long(DeclType::OfferDictionary, "target_name"),
6575 ])),
6576 },
6577 test_validate_offers_extraneous => {
6578 input = {
6579 let mut decl = new_component_decl();
6580 decl.offers = Some(vec![
6581 fdecl::Offer::Service(fdecl::OfferService {
6582 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6583 name: "logger".to_string(),
6584 collection: Some("modular".to_string()),
6585 })),
6586 source_name: Some("fuchsia.logger.Log".to_string()),
6587 target: Some(fdecl::Ref::Child(
6588 fdecl::ChildRef {
6589 name: "netstack".to_string(),
6590 collection: Some("modular".to_string()),
6591 }
6592 )),
6593 target_name: Some("fuchsia.logger.Log".to_string()),
6594 ..Default::default()
6595 }),
6596 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6597 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6598 name: "logger".to_string(),
6599 collection: Some("modular".to_string()),
6600 })),
6601 source_name: Some("fuchsia.logger.Log".to_string()),
6602 target: Some(fdecl::Ref::Child(
6603 fdecl::ChildRef {
6604 name: "netstack".to_string(),
6605 collection: Some("modular".to_string()),
6606 }
6607 )),
6608 target_name: Some("fuchsia.logger.Log".to_string()),
6609 dependency_type: Some(fdecl::DependencyType::Strong),
6610 ..Default::default()
6611 }),
6612 fdecl::Offer::Directory(fdecl::OfferDirectory {
6613 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6614 name: "logger".to_string(),
6615 collection: Some("modular".to_string()),
6616 })),
6617 source_name: Some("assets".to_string()),
6618 target: Some(fdecl::Ref::Child(
6619 fdecl::ChildRef {
6620 name: "netstack".to_string(),
6621 collection: Some("modular".to_string()),
6622 }
6623 )),
6624 target_name: Some("assets".to_string()),
6625 rights: Some(fio::Operations::CONNECT),
6626 subdir: None,
6627 dependency_type: Some(fdecl::DependencyType::Weak),
6628 ..Default::default()
6629 }),
6630 fdecl::Offer::Storage(fdecl::OfferStorage {
6631 source_name: Some("data".to_string()),
6632 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{ })),
6633 target: Some(fdecl::Ref::Child(
6634 fdecl::ChildRef {
6635 name: "netstack".to_string(),
6636 collection: Some("modular".to_string()),
6637 }
6638 )),
6639 target_name: Some("data".to_string()),
6640 ..Default::default()
6641 }),
6642 fdecl::Offer::Runner(fdecl::OfferRunner {
6643 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6644 name: "logger".to_string(),
6645 collection: Some("modular".to_string()),
6646 })),
6647 source_name: Some("elf".to_string()),
6648 target: Some(fdecl::Ref::Child(
6649 fdecl::ChildRef {
6650 name: "netstack".to_string(),
6651 collection: Some("modular".to_string()),
6652 }
6653 )),
6654 target_name: Some("elf".to_string()),
6655 ..Default::default()
6656 }),
6657 fdecl::Offer::Resolver(fdecl::OfferResolver {
6658 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6659 name: "logger".to_string(),
6660 collection: Some("modular".to_string()),
6661 })),
6662 source_name: Some("pkg".to_string()),
6663 target: Some(fdecl::Ref::Child(
6664 fdecl::ChildRef {
6665 name: "netstack".to_string(),
6666 collection: Some("modular".to_string()),
6667 }
6668 )),
6669 target_name: Some("pkg".to_string()),
6670 ..Default::default()
6671 }),
6672 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6673 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6674 name: "logger".to_string(),
6675 collection: Some("modular".to_string()),
6676 })),
6677 source_name: Some("dict".to_string()),
6678 target: Some(fdecl::Ref::Child(
6679 fdecl::ChildRef {
6680 name: "netstack".to_string(),
6681 collection: Some("modular".to_string()),
6682 }
6683 )),
6684 target_name: Some("dict".to_string()),
6685 dependency_type: Some(fdecl::DependencyType::Strong),
6686 ..Default::default()
6687 }),
6688 ]);
6689 decl.capabilities = Some(vec![
6690 fdecl::Capability::Protocol(fdecl::Protocol {
6691 name: Some("fuchsia.logger.Log".to_string()),
6692 source_path: Some("/svc/logger".to_string()),
6693 ..Default::default()
6694 }),
6695 fdecl::Capability::Directory(fdecl::Directory {
6696 name: Some("assets".to_string()),
6697 source_path: Some("/data/assets".to_string()),
6698 rights: Some(fio::Operations::CONNECT),
6699 ..Default::default()
6700 }),
6701 ]);
6702 decl
6703 },
6704 result = Err(ErrorList::new(vec![
6705 Error::extraneous_field(DeclType::OfferService, "source.child.collection"),
6706 Error::extraneous_field(DeclType::OfferService, "target.child.collection"),
6707 Error::extraneous_field(DeclType::OfferProtocol, "source.child.collection"),
6708 Error::extraneous_field(DeclType::OfferProtocol, "target.child.collection"),
6709 Error::extraneous_field(DeclType::OfferDirectory, "source.child.collection"),
6710 Error::extraneous_field(DeclType::OfferDirectory, "target.child.collection"),
6711 Error::extraneous_field(DeclType::OfferStorage, "target.child.collection"),
6712 Error::extraneous_field(DeclType::OfferRunner, "source.child.collection"),
6713 Error::extraneous_field(DeclType::OfferRunner, "target.child.collection"),
6714 Error::extraneous_field(DeclType::OfferResolver, "source.child.collection"),
6715 Error::extraneous_field(DeclType::OfferResolver, "target.child.collection"),
6716 Error::extraneous_field(DeclType::OfferDictionary, "source.child.collection"),
6717 Error::extraneous_field(DeclType::OfferDictionary, "target.child.collection"),
6718 ])),
6719 },
6720 test_validate_offers_invalid_filtered_service_fields => {
6721 input = {
6722 let mut decl = new_component_decl();
6723 decl.offers = Some(vec![
6724 fdecl::Offer::Service(fdecl::OfferService {
6725 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6726 source_name: Some("fuchsia.logger.Log".to_string()),
6727 target: Some(fdecl::Ref::Child(
6728 fdecl::ChildRef {
6729 name: "logger".to_string(),
6730 collection: None,
6731 }
6732 )),
6733 target_name: Some("fuchsia.logger.Log".to_string()),
6734 source_instance_filter: Some(vec![]),
6735 ..Default::default()
6736 }),
6737 fdecl::Offer::Service(fdecl::OfferService {
6738 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6739 source_name: Some("fuchsia.logger.Log".to_string()),
6740 target: Some(fdecl::Ref::Child(
6741 fdecl::ChildRef {
6742 name: "logger".to_string(),
6743 collection: None,
6744 }
6745 )),
6746 target_name: Some("fuchsia.logger.Log2".to_string()),
6747 source_instance_filter: Some(vec!["^badname".to_string()]),
6748 ..Default::default()
6749 }),
6750 fdecl::Offer::Service(fdecl::OfferService {
6751 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6752 source_name: Some("fuchsia.logger.Log".to_string()),
6753 target: Some(fdecl::Ref::Child(
6754 fdecl::ChildRef {
6755 name: "logger".to_string(),
6756 collection: None,
6757 }
6758 )),
6759 target_name: Some("fuchsia.logger.Log1".to_string()),
6760 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()}]),
6761 ..Default::default()
6762 }),
6763 fdecl::Offer::Service(fdecl::OfferService {
6764 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6765 source_name: Some("fuchsia.logger.Log".to_string()),
6766 target: Some(fdecl::Ref::Child(
6767 fdecl::ChildRef {
6768 name: "logger".to_string(),
6769 collection: None,
6770 }
6771 )),
6772 target_name: Some("fuchsia.logger.Log3".to_string()),
6773 renamed_instances: Some(vec![
6774 fdecl::NameMapping {
6775 source_name: "^badname".to_string(),
6776 target_name: "^badname".to_string(),
6777 }
6778 ]),
6779 ..Default::default()
6780 })
6781 ]);
6782 decl.children = Some(vec![
6783 fdecl::Child {
6784 name: Some("logger".to_string()),
6785 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
6786 startup: Some(fdecl::StartupMode::Lazy),
6787 on_terminate: None,
6788 environment: None,
6789 ..Default::default()
6790 },
6791 ]);
6792 decl
6793 },
6794 result = Err(ErrorList::new(vec![
6795 Error::invalid_field(DeclType::OfferService, "source_instance_filter"),
6796 Error::invalid_field(DeclType::OfferService, "source_instance_filter"),
6797 Error::invalid_field(DeclType::OfferService, "renamed_instances"),
6798 Error::invalid_field(DeclType::OfferService, "renamed_instances.source_name"),
6799 Error::invalid_field(DeclType::OfferService, "renamed_instances.target_name"),
6800 ])),
6801 },
6802 test_validate_offers_invalid_identifiers => {
6803 input = {
6804 let mut decl = new_component_decl();
6805 decl.offers = Some(vec![
6806 fdecl::Offer::Service(fdecl::OfferService {
6807 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6808 name: "^bad".to_string(),
6809 collection: None,
6810 })),
6811 source_name: Some("foo/".to_string()),
6812 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6813 name: "%bad".to_string(),
6814 collection: None,
6815 })),
6816 target_name: Some("/".to_string()),
6817 ..Default::default()
6818 }),
6819 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6820 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6821 name: "^bad".to_string(),
6822 collection: None,
6823 })),
6824 source_name: Some("foo/".to_string()),
6825 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6826 name: "%bad".to_string(),
6827 collection: None,
6828 })),
6829 target_name: Some("/".to_string()),
6830 dependency_type: Some(fdecl::DependencyType::Strong),
6831 ..Default::default()
6832 }),
6833 fdecl::Offer::Directory(fdecl::OfferDirectory {
6834 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6835 name: "^bad".to_string(),
6836 collection: None,
6837 })),
6838 source_name: Some("foo/".to_string()),
6839 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6840 name: "%bad".to_string(),
6841 collection: None,
6842 })),
6843 target_name: Some("/".to_string()),
6844 rights: Some(fio::Operations::CONNECT),
6845 subdir: Some("/foo".to_string()),
6846 dependency_type: Some(fdecl::DependencyType::Strong),
6847 ..Default::default()
6848 }),
6849 fdecl::Offer::Runner(fdecl::OfferRunner {
6850 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6851 name: "^bad".to_string(),
6852 collection: None,
6853 })),
6854 source_name: Some("/path".to_string()),
6855 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6856 name: "%bad".to_string(),
6857 collection: None,
6858 })),
6859 target_name: Some("elf!".to_string()),
6860 ..Default::default()
6861 }),
6862 fdecl::Offer::Resolver(fdecl::OfferResolver {
6863 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6864 name: "^bad".to_string(),
6865 collection: None,
6866 })),
6867 source_name: Some("/path".to_string()),
6868 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6869 name: "%bad".to_string(),
6870 collection: None,
6871 })),
6872 target_name: Some("pkg!".to_string()),
6873 ..Default::default()
6874 }),
6875 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6876 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6877 name: "^bad".to_string(),
6878 collection: None,
6879 })),
6880 source_name: Some("/path".to_string()),
6881 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6882 name: "%bad".to_string(),
6883 collection: None,
6884 })),
6885 target_name: Some("pkg!".to_string()),
6886 dependency_type: Some(fdecl::DependencyType::Strong),
6887 ..Default::default()
6888 }),
6889 ]);
6890 decl
6891 },
6892 result = Err(ErrorList::new(vec![
6893 Error::invalid_field(DeclType::OfferService, "source.child.name"),
6894 Error::invalid_field(DeclType::OfferService, "source_name"),
6895 Error::invalid_field(DeclType::OfferService, "target.child.name"),
6896 Error::invalid_field(DeclType::OfferService, "target_name"),
6897 Error::invalid_field(DeclType::OfferProtocol, "source.child.name"),
6898 Error::invalid_field(DeclType::OfferProtocol, "source_name"),
6899 Error::invalid_field(DeclType::OfferProtocol, "target.child.name"),
6900 Error::invalid_field(DeclType::OfferProtocol, "target_name"),
6901 Error::invalid_field(DeclType::OfferDirectory, "source.child.name"),
6902 Error::invalid_field(DeclType::OfferDirectory, "source_name"),
6903 Error::invalid_field(DeclType::OfferDirectory, "target.child.name"),
6904 Error::invalid_field(DeclType::OfferDirectory, "target_name"),
6905 Error::invalid_field(DeclType::OfferDirectory, "subdir"),
6906 Error::invalid_field(DeclType::OfferRunner, "source.child.name"),
6907 Error::invalid_field(DeclType::OfferRunner, "source_name"),
6908 Error::invalid_field(DeclType::OfferRunner, "target.child.name"),
6909 Error::invalid_field(DeclType::OfferRunner, "target_name"),
6910 Error::invalid_field(DeclType::OfferResolver, "source.child.name"),
6911 Error::invalid_field(DeclType::OfferResolver, "source_name"),
6912 Error::invalid_field(DeclType::OfferResolver, "target.child.name"),
6913 Error::invalid_field(DeclType::OfferResolver, "target_name"),
6914 Error::invalid_field(DeclType::OfferDictionary, "source.child.name"),
6915 Error::invalid_field(DeclType::OfferDictionary, "source_name"),
6916 Error::invalid_field(DeclType::OfferDictionary, "target.child.name"),
6917 Error::invalid_field(DeclType::OfferDictionary, "target_name"),
6918 ])),
6919 },
6920 test_validate_offers_target_equals_source => {
6921 input = {
6922 let mut decl = new_component_decl();
6923 decl.offers = Some(vec![
6924 fdecl::Offer::Service(fdecl::OfferService {
6925 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6926 name: "logger".to_string(),
6927 collection: None,
6928 })),
6929 source_name: Some("logger".to_string()),
6930 target: Some(fdecl::Ref::Child(
6931 fdecl::ChildRef {
6932 name: "logger".to_string(),
6933 collection: None,
6934 }
6935 )),
6936 target_name: Some("logger".to_string()),
6937 ..Default::default()
6938 }),
6939 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6940 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6941 name: "logger".to_string(),
6942 collection: None,
6943 })),
6944 source_name: Some("legacy_logger".to_string()),
6945 target: Some(fdecl::Ref::Child(
6946 fdecl::ChildRef {
6947 name: "logger".to_string(),
6948 collection: None,
6949 }
6950 )),
6951 target_name: Some("weak_legacy_logger".to_string()),
6952 dependency_type: Some(fdecl::DependencyType::Weak),
6953 ..Default::default()
6954 }),
6955 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6956 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6957 name: "logger".to_string(),
6958 collection: None,
6959 })),
6960 source_name: Some("legacy_logger".to_string()),
6961 target: Some(fdecl::Ref::Child(
6962 fdecl::ChildRef {
6963 name: "logger".to_string(),
6964 collection: None,
6965 }
6966 )),
6967 target_name: Some("strong_legacy_logger".to_string()),
6968 dependency_type: Some(fdecl::DependencyType::Strong),
6969 ..Default::default()
6970 }),
6971 fdecl::Offer::Directory(fdecl::OfferDirectory {
6972 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6973 name: "logger".to_string(),
6974 collection: None,
6975 })),
6976 source_name: Some("assets".to_string()),
6977 target: Some(fdecl::Ref::Child(
6978 fdecl::ChildRef {
6979 name: "logger".to_string(),
6980 collection: None,
6981 }
6982 )),
6983 target_name: Some("assets".to_string()),
6984 rights: Some(fio::Operations::CONNECT),
6985 subdir: None,
6986 dependency_type: Some(fdecl::DependencyType::Strong),
6987 ..Default::default()
6988 }),
6989 fdecl::Offer::Runner(fdecl::OfferRunner {
6990 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6991 name: "logger".to_string(),
6992 collection: None,
6993 })),
6994 source_name: Some("web".to_string()),
6995 target: Some(fdecl::Ref::Child(
6996 fdecl::ChildRef {
6997 name: "logger".to_string(),
6998 collection: None,
6999 }
7000 )),
7001 target_name: Some("web".to_string()),
7002 ..Default::default()
7003 }),
7004 fdecl::Offer::Resolver(fdecl::OfferResolver {
7005 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7006 name: "logger".to_string(),
7007 collection: None,
7008 })),
7009 source_name: Some("pkg".to_string()),
7010 target: Some(fdecl::Ref::Child(
7011 fdecl::ChildRef {
7012 name: "logger".to_string(),
7013 collection: None,
7014 }
7015 )),
7016 target_name: Some("pkg".to_string()),
7017 ..Default::default()
7018 }),
7019 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7020 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7021 name: "logger".to_string(),
7022 collection: None,
7023 })),
7024 source_name: Some("dict".to_string()),
7025 target: Some(fdecl::Ref::Child(
7026 fdecl::ChildRef {
7027 name: "logger".to_string(),
7028 collection: None,
7029 }
7030 )),
7031 target_name: Some("dict".to_string()),
7032 dependency_type: Some(fdecl::DependencyType::Strong),
7033 ..Default::default()
7034 }),
7035 ]);
7036 decl.children = Some(vec![fdecl::Child{
7037 name: Some("logger".to_string()),
7038 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
7039 startup: Some(fdecl::StartupMode::Lazy),
7040 on_terminate: None,
7041 environment: None,
7042 ..Default::default()
7043 }]);
7044 decl
7045 },
7046 result = Err(ErrorList::new(vec![
7047 Error::dependency_cycle("{{child logger -> child logger}}"),
7048 ])),
7049 },
7050 test_validate_offers_storage_target_equals_source => {
7051 input = fdecl::Component {
7052 offers: Some(vec![
7053 fdecl::Offer::Storage(fdecl::OfferStorage {
7054 source_name: Some("data".to_string()),
7055 source: Some(fdecl::Ref::Self_(fdecl::SelfRef { })),
7056 target: Some(fdecl::Ref::Child(
7057 fdecl::ChildRef {
7058 name: "logger".to_string(),
7059 collection: None,
7060 }
7061 )),
7062 target_name: Some("data".to_string()),
7063 ..Default::default()
7064 })
7065 ]),
7066 capabilities: Some(vec![
7067 fdecl::Capability::Storage(fdecl::Storage {
7068 name: Some("data".to_string()),
7069 backing_dir: Some("minfs".to_string()),
7070 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7071 name: "logger".to_string(),
7072 collection: None,
7073 })),
7074 subdir: None,
7075 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
7076 ..Default::default()
7077 }),
7078 ]),
7079 children: Some(vec![
7080 fdecl::Child {
7081 name: Some("logger".to_string()),
7082 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
7083 startup: Some(fdecl::StartupMode::Lazy),
7084 on_terminate: None,
7085 environment: None,
7086 ..Default::default()
7087 },
7088 ]),
7089 ..new_component_decl()
7090 },
7091 result = Err(ErrorList::new(vec![
7092 Error::dependency_cycle("{{child logger -> capability data -> child logger}}"),
7093 ])),
7094 },
7095 test_validate_offers_invalid_child => {
7096 input = {
7097 let mut decl = new_component_decl();
7098 decl.offers = Some(vec![
7099 fdecl::Offer::Service(fdecl::OfferService {
7100 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7101 name: "logger".to_string(),
7102 collection: None,
7103 })),
7104 source_name: Some("fuchsia.logger.Log".to_string()),
7105 target: Some(fdecl::Ref::Child(
7106 fdecl::ChildRef {
7107 name: "netstack".to_string(),
7108 collection: None,
7109 }
7110 )),
7111 target_name: Some("fuchsia.logger.Log".to_string()),
7112 ..Default::default()
7113 }),
7114 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7115 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7116 name: "logger".to_string(),
7117 collection: None,
7118 })),
7119 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
7120 target: Some(fdecl::Ref::Child(
7121 fdecl::ChildRef {
7122 name: "netstack".to_string(),
7123 collection: None,
7124 }
7125 )),
7126 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7127 dependency_type: Some(fdecl::DependencyType::Strong),
7128 ..Default::default()
7129 }),
7130 fdecl::Offer::Directory(fdecl::OfferDirectory {
7131 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7132 name: "logger".to_string(),
7133 collection: None,
7134 })),
7135 source_name: Some("assets".to_string()),
7136 target: Some(fdecl::Ref::Collection(
7137 fdecl::CollectionRef { name: "modular".to_string() }
7138 )),
7139 target_name: Some("assets".to_string()),
7140 rights: Some(fio::Operations::CONNECT),
7141 subdir: None,
7142 dependency_type: Some(fdecl::DependencyType::Weak),
7143 ..Default::default()
7144 }),
7145 ]);
7146 decl.capabilities = Some(vec![
7147 fdecl::Capability::Storage(fdecl::Storage {
7148 name: Some("memfs".to_string()),
7149 backing_dir: Some("memfs".to_string()),
7150 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7151 name: "logger".to_string(),
7152 collection: None,
7153 })),
7154 subdir: None,
7155 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
7156 ..Default::default()
7157 }),
7158 ]);
7159 decl.children = Some(vec![
7160 fdecl::Child {
7161 name: Some("netstack".to_string()),
7162 url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
7163 startup: Some(fdecl::StartupMode::Lazy),
7164 on_terminate: None,
7165 environment: None,
7166 ..Default::default()
7167 },
7168 ]);
7169 decl.collections = Some(vec![
7170 fdecl::Collection {
7171 name: Some("modular".to_string()),
7172 durability: Some(fdecl::Durability::Transient),
7173 environment: None,
7174 allowed_offers: Some(fdecl::AllowedOffers::StaticAndDynamic),
7175 allow_long_names: None,
7176 ..Default::default()
7177 },
7178 ]);
7179 decl
7180 },
7181 result = Err(ErrorList::new(vec![
7182 Error::invalid_child(DeclType::Storage, "source", "logger"),
7183 Error::invalid_child(DeclType::OfferService, "source", "logger"),
7184 Error::invalid_child(DeclType::OfferProtocol, "source", "logger"),
7185 Error::invalid_child(DeclType::OfferDirectory, "source", "logger"),
7186 ])),
7187 },
7188 test_validate_offers_invalid_source_capability => {
7189 input = {
7190 fdecl::Component {
7191 offers: Some(vec![
7192 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7193 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
7194 name: "this-storage-doesnt-exist".to_string(),
7195 })),
7196 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
7197 target: Some(fdecl::Ref::Child(
7198 fdecl::ChildRef {
7199 name: "netstack".to_string(),
7200 collection: None,
7201 }
7202 )),
7203 target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
7204 dependency_type: Some(fdecl::DependencyType::Strong),
7205 ..Default::default()
7206 }),
7207 ]),
7208 ..new_component_decl()
7209 }
7210 },
7211 result = Err(ErrorList::new(vec![
7212 Error::invalid_capability(DeclType::OfferProtocol, "source", "this-storage-doesnt-exist"),
7213 Error::invalid_child(DeclType::OfferProtocol, "target", "netstack"),
7214 ])),
7215 },
7216 test_validate_offers_target => {
7217 input = {
7218 let mut decl = new_component_decl();
7219 decl.offers = Some(vec![
7220 fdecl::Offer::Service(fdecl::OfferService {
7221 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
7222 name: "modular".into()
7223 })),
7224 source_name: Some("logger".to_string()),
7225 target: Some(fdecl::Ref::Child(
7226 fdecl::ChildRef {
7227 name: "netstack".to_string(),
7228 collection: None,
7229 }
7230 )),
7231 target_name: Some("fuchsia.logger.Log".to_string()),
7232 ..Default::default()
7233 }),
7234 fdecl::Offer::Service(fdecl::OfferService {
7235 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
7236 name: "modular".into()
7237 })),
7238 source_name: Some("logger".to_string()),
7239 target: Some(fdecl::Ref::Child(
7240 fdecl::ChildRef {
7241 name: "netstack".to_string(),
7242 collection: None,
7243 }
7244 )),
7245 target_name: Some("fuchsia.logger.Log".to_string()),
7246 ..Default::default()
7247 }),
7248 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7249 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7250 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
7251 target: Some(fdecl::Ref::Child(
7252 fdecl::ChildRef {
7253 name: "netstack".to_string(),
7254 collection: None,
7255 }
7256 )),
7257 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7258 dependency_type: Some(fdecl::DependencyType::Strong),
7259 ..Default::default()
7260 }),
7261 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7262 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7263 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
7264 target: Some(fdecl::Ref::Child(
7265 fdecl::ChildRef {
7266 name: "netstack".to_string(),
7267 collection: None,
7268 }
7269 )),
7270 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7271 dependency_type: Some(fdecl::DependencyType::Strong),
7272 ..Default::default()
7273 }),
7274 fdecl::Offer::Directory(fdecl::OfferDirectory {
7275 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7276 source_name: Some("assets".to_string()),
7277 target: Some(fdecl::Ref::Collection(
7278 fdecl::CollectionRef { name: "modular".to_string() }
7279 )),
7280 target_name: Some("assets".to_string()),
7281 rights: Some(fio::Operations::CONNECT),
7282 subdir: None,
7283 dependency_type: Some(fdecl::DependencyType::Strong),
7284 ..Default::default()
7285 }),
7286 fdecl::Offer::Directory(fdecl::OfferDirectory {
7287 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7288 source_name: Some("assets".to_string()),
7289 target: Some(fdecl::Ref::Collection(
7290 fdecl::CollectionRef { name: "modular".to_string() }
7291 )),
7292 target_name: Some("assets".to_string()),
7293 rights: Some(fio::Operations::CONNECT),
7294 subdir: None,
7295 dependency_type: Some(fdecl::DependencyType::Weak),
7296 ..Default::default()
7297 }),
7298 fdecl::Offer::Storage(fdecl::OfferStorage {
7299 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7300 source_name: Some("data".to_string()),
7301 target: Some(fdecl::Ref::Collection(
7302 fdecl::CollectionRef { name: "modular".to_string() }
7303 )),
7304 target_name: Some("data".to_string()),
7305 ..Default::default()
7306 }),
7307 fdecl::Offer::Storage(fdecl::OfferStorage {
7308 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7309 source_name: Some("data".to_string()),
7310 target: Some(fdecl::Ref::Collection(
7311 fdecl::CollectionRef { name: "modular".to_string() }
7312 )),
7313 target_name: Some("data".to_string()),
7314 ..Default::default()
7315 }),
7316 fdecl::Offer::Runner(fdecl::OfferRunner {
7317 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7318 source_name: Some("elf".to_string()),
7319 target: Some(fdecl::Ref::Collection(
7320 fdecl::CollectionRef { name: "modular".to_string() }
7321 )),
7322 target_name: Some("duplicated".to_string()),
7323 ..Default::default()
7324 }),
7325 fdecl::Offer::Runner(fdecl::OfferRunner {
7326 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7327 source_name: Some("elf".to_string()),
7328 target: Some(fdecl::Ref::Collection(
7329 fdecl::CollectionRef { name: "modular".to_string() }
7330 )),
7331 target_name: Some("duplicated".to_string()),
7332 ..Default::default()
7333 }),
7334 fdecl::Offer::Resolver(fdecl::OfferResolver {
7335 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7336 source_name: Some("pkg".to_string()),
7337 target: Some(fdecl::Ref::Collection(
7338 fdecl::CollectionRef { name: "modular".to_string() }
7339 )),
7340 target_name: Some("duplicated".to_string()),
7341 ..Default::default()
7342 }),
7343 fdecl::Offer::EventStream(fdecl::OfferEventStream {
7344 source_name: Some("started".to_string()),
7345 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7346 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "netstack".to_string(), collection: None})),
7347 target_name: Some("started".to_string()),
7348 ..Default::default()
7349 }),
7350 fdecl::Offer::EventStream(fdecl::OfferEventStream {
7351 source_name: Some("started".to_string()),
7352 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7353 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "netstack".to_string(), collection: None})),
7354 target_name: Some("started".to_string()),
7355 ..Default::default()
7356 }),
7357 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7358 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7359 source_name: Some("a".to_string()),
7360 target: Some(fdecl::Ref::Collection(
7361 fdecl::CollectionRef { name: "modular".to_string() }
7362 )),
7363 target_name: Some("dict".to_string()),
7364 dependency_type: Some(fdecl::DependencyType::Strong),
7365 ..Default::default()
7366 }),
7367 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7368 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7369 source_name: Some("b".to_string()),
7370 target: Some(fdecl::Ref::Collection(
7371 fdecl::CollectionRef { name: "modular".to_string() }
7372 )),
7373 target_name: Some("dict".to_string()),
7374 dependency_type: Some(fdecl::DependencyType::Strong),
7375 ..Default::default()
7376 }),
7377 ]);
7378 decl.children = Some(vec![
7379 fdecl::Child{
7380 name: Some("netstack".to_string()),
7381 url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
7382 startup: Some(fdecl::StartupMode::Eager),
7383 on_terminate: None,
7384 environment: None,
7385 ..Default::default()
7386 },
7387 ]);
7388 decl.collections = Some(vec![
7389 fdecl::Collection{
7390 name: Some("modular".to_string()),
7391 durability: Some(fdecl::Durability::Transient),
7392 environment: None,
7393 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7394 allow_long_names: None,
7395 ..Default::default()
7396 },
7397 ]);
7398 decl
7399 },
7400 result = Err(ErrorList::new(vec![
7401 Error::duplicate_field(DeclType::OfferProtocol, "target_name", "fuchsia.logger.LegacyLog"),
7403 Error::duplicate_field(DeclType::OfferDirectory, "target_name", "assets"),
7404 Error::duplicate_field(DeclType::OfferStorage, "target_name", "data"),
7405 Error::duplicate_field(DeclType::OfferRunner, "target_name", "duplicated"),
7406 Error::duplicate_field(DeclType::OfferResolver, "target_name", "duplicated"),
7407 Error::duplicate_field(DeclType::OfferEventStream, "target_name", "started"),
7408 Error::duplicate_field(DeclType::OfferDictionary, "target_name", "dict"),
7409 ])),
7410 },
7411 test_validate_offers_target_invalid => {
7412 input = {
7413 let mut decl = new_component_decl();
7414 decl.offers = Some(vec![
7415 fdecl::Offer::Service(fdecl::OfferService {
7416 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7417 source_name: Some("logger".to_string()),
7418 target: Some(fdecl::Ref::Child(
7419 fdecl::ChildRef {
7420 name: "netstack".to_string(),
7421 collection: None,
7422 }
7423 )),
7424 target_name: Some("fuchsia.logger.Log".to_string()),
7425 ..Default::default()
7426 }),
7427 fdecl::Offer::Service(fdecl::OfferService {
7428 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7429 source_name: Some("logger".to_string()),
7430 target: Some(fdecl::Ref::Collection(
7431 fdecl::CollectionRef { name: "modular".to_string(), }
7432 )),
7433 target_name: Some("fuchsia.logger.Log".to_string()),
7434 ..Default::default()
7435 }),
7436 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7437 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7438 source_name: Some("legacy_logger".to_string()),
7439 target: Some(fdecl::Ref::Child(
7440 fdecl::ChildRef {
7441 name: "netstack".to_string(),
7442 collection: None,
7443 }
7444 )),
7445 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7446 dependency_type: Some(fdecl::DependencyType::Weak),
7447 ..Default::default()
7448 }),
7449 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7450 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7451 source_name: Some("legacy_logger".to_string()),
7452 target: Some(fdecl::Ref::Collection(
7453 fdecl::CollectionRef { name: "modular".to_string(), }
7454 )),
7455 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7456 dependency_type: Some(fdecl::DependencyType::Strong),
7457 ..Default::default()
7458 }),
7459 fdecl::Offer::Directory(fdecl::OfferDirectory {
7460 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7461 source_name: Some("assets".to_string()),
7462 target: Some(fdecl::Ref::Child(
7463 fdecl::ChildRef {
7464 name: "netstack".to_string(),
7465 collection: None,
7466 }
7467 )),
7468 target_name: Some("data".to_string()),
7469 rights: Some(fio::Operations::CONNECT),
7470 subdir: None,
7471 dependency_type: Some(fdecl::DependencyType::Strong),
7472 ..Default::default()
7473 }),
7474 fdecl::Offer::Directory(fdecl::OfferDirectory {
7475 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7476 source_name: Some("assets".to_string()),
7477 target: Some(fdecl::Ref::Collection(
7478 fdecl::CollectionRef { name: "modular".to_string(), }
7479 )),
7480 target_name: Some("data".to_string()),
7481 rights: Some(fio::Operations::CONNECT),
7482 subdir: None,
7483 dependency_type: Some(fdecl::DependencyType::Weak),
7484 ..Default::default()
7485 }),
7486 fdecl::Offer::Storage(fdecl::OfferStorage {
7487 source_name: Some("data".to_string()),
7488 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7489 target: Some(fdecl::Ref::Child(
7490 fdecl::ChildRef {
7491 name: "netstack".to_string(),
7492 collection: None,
7493 }
7494 )),
7495 target_name: Some("data".to_string()),
7496 ..Default::default()
7497 }),
7498 fdecl::Offer::Storage(fdecl::OfferStorage {
7499 source_name: Some("data".to_string()),
7500 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7501 target: Some(fdecl::Ref::Collection(
7502 fdecl::CollectionRef { name: "modular".to_string(), }
7503 )),
7504 target_name: Some("data".to_string()),
7505 ..Default::default()
7506 }),
7507 fdecl::Offer::Runner(fdecl::OfferRunner {
7508 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7509 source_name: Some("elf".to_string()),
7510 target: Some(fdecl::Ref::Child(
7511 fdecl::ChildRef {
7512 name: "netstack".to_string(),
7513 collection: None,
7514 }
7515 )),
7516 target_name: Some("elf".to_string()),
7517 ..Default::default()
7518 }),
7519 fdecl::Offer::Runner(fdecl::OfferRunner {
7520 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7521 source_name: Some("elf".to_string()),
7522 target: Some(fdecl::Ref::Collection(
7523 fdecl::CollectionRef { name: "modular".to_string(), }
7524 )),
7525 target_name: Some("elf".to_string()),
7526 ..Default::default()
7527 }),
7528 fdecl::Offer::Resolver(fdecl::OfferResolver {
7529 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7530 source_name: Some("pkg".to_string()),
7531 target: Some(fdecl::Ref::Child(
7532 fdecl::ChildRef {
7533 name: "netstack".to_string(),
7534 collection: None,
7535 }
7536 )),
7537 target_name: Some("pkg".to_string()),
7538 ..Default::default()
7539 }),
7540 fdecl::Offer::Resolver(fdecl::OfferResolver {
7541 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7542 source_name: Some("pkg".to_string()),
7543 target: Some(fdecl::Ref::Collection(
7544 fdecl::CollectionRef { name: "modular".to_string(), }
7545 )),
7546 target_name: Some("pkg".to_string()),
7547 ..Default::default()
7548 }),
7549 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7550 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7551 source_name: Some("pkg".to_string()),
7552 target: Some(fdecl::Ref::Child(
7553 fdecl::ChildRef {
7554 name: "netstack".to_string(),
7555 collection: None,
7556 }
7557 )),
7558 target_name: Some("pkg".to_string()),
7559 dependency_type: Some(fdecl::DependencyType::Strong),
7560 ..Default::default()
7561 }),
7562 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7563 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7564 source_name: Some("pkg".to_string()),
7565 target: Some(fdecl::Ref::Collection(
7566 fdecl::CollectionRef { name: "modular".to_string(), }
7567 )),
7568 target_name: Some("pkg".to_string()),
7569 dependency_type: Some(fdecl::DependencyType::Strong),
7570 ..Default::default()
7571 }),
7572 ]);
7573 decl
7574 },
7575 result = Err(ErrorList::new(vec![
7576 Error::invalid_child(DeclType::OfferService, "target", "netstack"),
7577 Error::invalid_collection(DeclType::OfferService, "target", "modular"),
7578 Error::invalid_child(DeclType::OfferProtocol, "target", "netstack"),
7579 Error::invalid_collection(DeclType::OfferProtocol, "target", "modular"),
7580 Error::invalid_child(DeclType::OfferDirectory, "target", "netstack"),
7581 Error::invalid_collection(DeclType::OfferDirectory, "target", "modular"),
7582 Error::invalid_child(DeclType::OfferStorage, "target", "netstack"),
7583 Error::invalid_collection(DeclType::OfferStorage, "target", "modular"),
7584 Error::invalid_child(DeclType::OfferRunner, "target", "netstack"),
7585 Error::invalid_collection(DeclType::OfferRunner, "target", "modular"),
7586 Error::invalid_child(DeclType::OfferResolver, "target", "netstack"),
7587 Error::invalid_collection(DeclType::OfferResolver, "target", "modular"),
7588 Error::invalid_child(DeclType::OfferDictionary, "target", "netstack"),
7589 Error::invalid_collection(DeclType::OfferDictionary, "target", "modular"),
7590 ])),
7591 },
7592 test_validate_offers_target_dictionary => {
7593 input = fdecl::Component {
7594 offers: Some(vec![
7595 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7597 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7598 source_name: Some("p".to_string()),
7599 target: Some(fdecl::Ref::Capability(
7600 fdecl::CapabilityRef {
7601 name: "dict".into(),
7602 },
7603 )),
7604 target_name: Some("p".into()),
7605 dependency_type: Some(fdecl::DependencyType::Strong),
7606 ..Default::default()
7607 }),
7608 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7610 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7611 source_name: Some("p".to_string()),
7612 target: Some(fdecl::Ref::Capability(
7613 fdecl::CapabilityRef {
7614 name: "dynamic".into(),
7615 },
7616 )),
7617 target_name: Some("p".into()),
7618 dependency_type: Some(fdecl::DependencyType::Strong),
7619 ..Default::default()
7620 }),
7621 ]),
7622 capabilities: Some(vec![
7623 fdecl::Capability::Dictionary(fdecl::Dictionary {
7624 name: Some("dict".into()),
7625 ..Default::default()
7626 }),
7627 fdecl::Capability::Dictionary(fdecl::Dictionary {
7628 name: Some("dynamic".into()),
7629 source_path: Some("/out/dir".into()),
7630 ..Default::default()
7631 }),
7632 ]),
7633 ..Default::default()
7634 },
7635 result = Err(ErrorList::new(vec![
7636 Error::invalid_field(DeclType::OfferProtocol, "target"),
7637 ])),
7638 },
7639 test_validate_offers_invalid_source_collection => {
7640 input = {
7641 let mut decl = new_component_decl();
7642 decl.collections = Some(vec![
7643 fdecl::Collection {
7644 name: Some("col".to_string()),
7645 durability: Some(fdecl::Durability::Transient),
7646 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7647 allow_long_names: None,
7648 ..Default::default()
7649 }
7650 ]);
7651 decl.children = Some(vec![
7652 fdecl::Child {
7653 name: Some("child".to_string()),
7654 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7655 startup: Some(fdecl::StartupMode::Lazy),
7656 on_terminate: None,
7657 ..Default::default()
7658 }
7659 ]);
7660 decl.offers = Some(vec![
7661 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7662 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7663 source_name: Some("a".to_string()),
7664 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7665 target_name: Some("a".to_string()),
7666 dependency_type: Some(fdecl::DependencyType::Strong),
7667 ..Default::default()
7668 }),
7669 fdecl::Offer::Directory(fdecl::OfferDirectory {
7670 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7671 source_name: Some("b".to_string()),
7672 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7673 target_name: Some("b".to_string()),
7674 rights: Some(fio::Operations::CONNECT),
7675 subdir: None,
7676 dependency_type: Some(fdecl::DependencyType::Strong),
7677 ..Default::default()
7678 }),
7679 fdecl::Offer::Storage(fdecl::OfferStorage {
7680 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7681 source_name: Some("c".to_string()),
7682 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7683 target_name: Some("c".to_string()),
7684 ..Default::default()
7685 }),
7686 fdecl::Offer::Runner(fdecl::OfferRunner {
7687 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7688 source_name: Some("d".to_string()),
7689 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7690 target_name: Some("d".to_string()),
7691 ..Default::default()
7692 }),
7693 fdecl::Offer::Resolver(fdecl::OfferResolver {
7694 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7695 source_name: Some("e".to_string()),
7696 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7697 target_name: Some("e".to_string()),
7698 ..Default::default()
7699 }),
7700 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7701 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7702 source_name: Some("f".to_string()),
7703 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7704 target_name: Some("f".to_string()),
7705 dependency_type: Some(fdecl::DependencyType::Strong),
7706 ..Default::default()
7707 }),
7708 ]);
7709 decl
7710 },
7711 result = Err(ErrorList::new(vec![
7712 Error::invalid_field(DeclType::OfferProtocol, "source"),
7713 Error::invalid_field(DeclType::OfferDirectory, "source"),
7714 Error::invalid_field(DeclType::OfferStorage, "source"),
7715 Error::invalid_field(DeclType::OfferRunner, "source"),
7716 Error::invalid_field(DeclType::OfferResolver, "source"),
7717 Error::invalid_field(DeclType::OfferDictionary, "source"),
7718 ])),
7719 },
7720 test_validate_offers_source_collection => {
7721 input = {
7722 let mut decl = new_component_decl();
7723 decl.collections = Some(vec![
7724 fdecl::Collection {
7725 name: Some("col".to_string()),
7726 durability: Some(fdecl::Durability::Transient),
7727 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7728 allow_long_names: None,
7729 ..Default::default()
7730 }
7731 ]);
7732 decl.children = Some(vec![
7733 fdecl::Child {
7734 name: Some("child".to_string()),
7735 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7736 startup: Some(fdecl::StartupMode::Lazy),
7737 on_terminate: None,
7738 ..Default::default()
7739 }
7740 ]);
7741 decl.offers = Some(vec![
7742 fdecl::Offer::Service(fdecl::OfferService {
7743 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7744 source_name: Some("a".to_string()),
7745 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7746 target_name: Some("a".to_string()),
7747 ..Default::default()
7748 })
7749 ]);
7750 decl
7751 },
7752 result = Ok(()),
7753 },
7754 test_validate_offers_invalid_capability_from_self => {
7755 input = {
7756 let mut decl = new_component_decl();
7757 decl.children = Some(vec![
7758 fdecl::Child {
7759 name: Some("child".to_string()),
7760 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7761 startup: Some(fdecl::StartupMode::Lazy),
7762 ..Default::default()
7763 }
7764 ]);
7765 decl.offers = Some(vec![
7766 fdecl::Offer::Service(fdecl::OfferService {
7767 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7768 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7769 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7770 name: "child".into(),
7771 collection: None
7772 })),
7773 target_name: Some("foo".into()),
7774 ..Default::default()
7775 }),
7776 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7777 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7778 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7779 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7780 name: "child".into(),
7781 collection: None
7782 })),
7783 target_name: Some("bar".into()),
7784 dependency_type: Some(fdecl::DependencyType::Strong),
7785 ..Default::default()
7786 }),
7787 fdecl::Offer::Directory(fdecl::OfferDirectory {
7788 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7789 source_name: Some("dir".into()),
7790 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7791 name: "child".into(),
7792 collection: None
7793 })),
7794 target_name: Some("assets".into()),
7795 dependency_type: Some(fdecl::DependencyType::Strong),
7796 ..Default::default()
7797 }),
7798 fdecl::Offer::Runner(fdecl::OfferRunner {
7799 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7800 source_name: Some("source_elf".into()),
7801 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7802 name: "child".into(),
7803 collection: None
7804 })),
7805 target_name: Some("elf".into()),
7806 ..Default::default()
7807 }),
7808 fdecl::Offer::Resolver(fdecl::OfferResolver {
7809 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7810 source_name: Some("source_pkg".into()),
7811 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7812 name: "child".into(),
7813 collection: None
7814 })),
7815 target_name: Some("pkg".into()),
7816 ..Default::default()
7817 }),
7818 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7819 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7820 source_name: Some("source_dict".into()),
7821 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7822 name: "child".into(),
7823 collection: None
7824 })),
7825 target_name: Some("dict".into()),
7826 dependency_type: Some(fdecl::DependencyType::Strong),
7827 ..Default::default()
7828 }),
7829 fdecl::Offer::Storage(fdecl::OfferStorage {
7830 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7831 source_name: Some("source_storage".into()),
7832 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7833 name: "child".into(),
7834 collection: None
7835 })),
7836 target_name: Some("storage".into()),
7837 ..Default::default()
7838 }),
7839 fdecl::Offer::Config(fdecl::OfferConfiguration {
7840 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7841 source_name: Some("source_config".into()),
7842 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7843 name: "child".into(),
7844 collection: None
7845 })),
7846 target_name: Some("config".into()),
7847 ..Default::default()
7848 }),
7849 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7850 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7851 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7852 source_dictionary: Some("dict/inner".into()),
7853 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7854 name: "child".into(),
7855 collection: None
7856 })),
7857 target_name: Some("baz".into()),
7858 dependency_type: Some(fdecl::DependencyType::Strong),
7859 ..Default::default()
7860 }),
7861 ]);
7862 decl
7863 },
7864 result = Err(ErrorList::new(vec![
7865 Error::invalid_capability(
7866 DeclType::OfferService,
7867 "source",
7868 "fuchsia.some.library.SomeProtocol"),
7869 Error::invalid_capability(
7870 DeclType::OfferProtocol,
7871 "source",
7872 "fuchsia.some.library.SomeProtocol"),
7873 Error::invalid_capability(DeclType::OfferDirectory, "source", "dir"),
7874 Error::invalid_capability(DeclType::OfferRunner, "source", "source_elf"),
7875 Error::invalid_capability(DeclType::OfferResolver, "source", "source_pkg"),
7876 Error::invalid_capability(DeclType::OfferDictionary, "source", "source_dict"),
7877 Error::invalid_capability(DeclType::OfferStorage, "source", "source_storage"),
7878 Error::invalid_capability(DeclType::OfferConfig, "source", "source_config"),
7879 Error::invalid_capability(DeclType::OfferProtocol, "source", "dict"),
7880 ])),
7881 },
7882 test_validate_offers_long_dependency_cycle => {
7883 input = {
7884 let mut decl = new_component_decl();
7885 let dependencies = vec![
7886 ("d", "b"),
7887 ("a", "b"),
7888 ("b", "c"),
7889 ("b", "d"),
7890 ("c", "a"),
7891 ];
7892 let offers = dependencies.into_iter().map(|(from,to)|
7893 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7894 source: Some(fdecl::Ref::Child(
7895 fdecl::ChildRef { name: from.to_string(), collection: None },
7896 )),
7897 source_name: Some(format!("thing_{}", from)),
7898 target: Some(fdecl::Ref::Child(
7899 fdecl::ChildRef { name: to.to_string(), collection: None },
7900 )),
7901 target_name: Some(format!("thing_{}", from)),
7902 dependency_type: Some(fdecl::DependencyType::Strong),
7903 ..Default::default()
7904 })).collect();
7905 let children = ["a", "b", "c", "d"].iter().map(|name| {
7906 fdecl::Child {
7907 name: Some(name.to_string()),
7908 url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
7909 startup: Some(fdecl::StartupMode::Lazy),
7910 on_terminate: None,
7911 environment: None,
7912 ..Default::default()
7913 }
7914 }).collect();
7915 decl.offers = Some(offers);
7916 decl.children = Some(children);
7917 decl
7918 },
7919 result = Err(ErrorList::new(vec![
7920 Error::dependency_cycle("{{child a -> child b -> child c -> child a}, {child b -> child d -> child b}}")
7921 ])),
7922 },
7923 test_validate_offers_not_required_invalid_source_service => {
7924 input = {
7925 let mut decl = generate_offer_different_source_and_availability_decl(
7926 |source, availability, target_name|
7927 fdecl::Offer::Service(fdecl::OfferService {
7928 source: Some(source),
7929 source_name: Some("fuchsia.examples.Echo".to_string()),
7930 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7931 name: "sink".to_string(),
7932 collection: None,
7933 })),
7934 target_name: Some(target_name.into()),
7935 availability: Some(availability),
7936 ..Default::default()
7937 })
7938 );
7939 decl.capabilities = Some(vec![
7940 fdecl::Capability::Service(fdecl::Service {
7941 name: Some("fuchsia.examples.Echo".to_string()),
7942 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
7943 ..Default::default()
7944 }),
7945 ]);
7946 decl
7947 },
7948 result = {
7949 Err(ErrorList::new(vec![
7950 Error::availability_must_be_optional(
7951 DeclType::OfferService,
7952 "availability",
7953 Some(&"fuchsia.examples.Echo".to_string()),
7954 ),
7955 Error::availability_must_be_optional(
7956 DeclType::OfferService,
7957 "availability",
7958 Some(&"fuchsia.examples.Echo".to_string()),
7959 ),
7960 ]))
7961 },
7962 },
7963 test_validate_offers_not_required_invalid_source_protocol => {
7964 input = {
7965 let mut decl = generate_offer_different_source_and_availability_decl(
7966 |source, availability, target_name|
7967 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7968 source: Some(source),
7969 source_name: Some("fuchsia.examples.Echo".to_string()),
7970 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7971 name: "sink".to_string(),
7972 collection: None,
7973 })),
7974 target_name: Some(target_name.into()),
7975 dependency_type: Some(fdecl::DependencyType::Strong),
7976 availability: Some(availability),
7977 ..Default::default()
7978 })
7979 );
7980 decl.capabilities = Some(vec![
7981 fdecl::Capability::Protocol(fdecl::Protocol {
7982 name: Some("fuchsia.examples.Echo".to_string()),
7983 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
7984 ..Default::default()
7985 }),
7986 ]);
7987 decl
7988 },
7989 result = {
7990 Err(ErrorList::new(vec![
7991 Error::availability_must_be_optional(
7992 DeclType::OfferProtocol,
7993 "availability",
7994 Some(&"fuchsia.examples.Echo".to_string()),
7995 ),
7996 Error::availability_must_be_optional(
7997 DeclType::OfferProtocol,
7998 "availability",
7999 Some(&"fuchsia.examples.Echo".to_string()),
8000 ),
8001 ]))
8002 },
8003 },
8004 test_validate_offers_not_required_invalid_source_directory => {
8005 input = {
8006 let mut decl = generate_offer_different_source_and_availability_decl(
8007 |source, availability, target_name|
8008 fdecl::Offer::Directory(fdecl::OfferDirectory {
8009 source: Some(source),
8010 source_name: Some("assets".to_string()),
8011 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8012 name: "sink".to_string(),
8013 collection: None,
8014 })),
8015 target_name: Some(target_name.into()),
8016 rights: Some(fio::Operations::CONNECT),
8017 subdir: None,
8018 dependency_type: Some(fdecl::DependencyType::Weak),
8019 availability: Some(availability),
8020 ..Default::default()
8021 })
8022 );
8023 decl.capabilities = Some(vec![
8024 fdecl::Capability::Directory(fdecl::Directory {
8025 name: Some("assets".to_string()),
8026 source_path: Some("/assets".to_string()),
8027 rights: Some(fio::Operations::CONNECT),
8028 ..Default::default()
8029 }),
8030 ]);
8031 decl
8032 },
8033 result = {
8034 Err(ErrorList::new(vec![
8035 Error::availability_must_be_optional(
8036 DeclType::OfferDirectory,
8037 "availability",
8038 Some(&"assets".to_string()),
8039 ),
8040 Error::availability_must_be_optional(
8041 DeclType::OfferDirectory,
8042 "availability",
8043 Some(&"assets".to_string()),
8044 ),
8045 ]))
8046 },
8047 },
8048 test_validate_offers_not_required_invalid_source_storage => {
8049 input = {
8050 let mut decl = new_component_decl();
8051 decl.children = Some(vec![
8052 fdecl::Child {
8053 name: Some("sink".to_string()),
8054 url: Some("fuchsia-pkg://fuchsia.com/sink#meta/sink.cm".to_string()),
8055 startup: Some(fdecl::StartupMode::Lazy),
8056 on_terminate: None,
8057 environment: None,
8058 ..Default::default()
8059 },
8060 ]);
8061 decl.capabilities = Some(vec![
8062 fdecl::Capability::Storage(fdecl::Storage {
8063 name: Some("data".to_string()),
8064 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8065 backing_dir: Some("minfs".to_string()),
8066 subdir: None,
8067 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
8068 ..Default::default()
8069 }),
8070 ]);
8071 let new_offer = |source: fdecl::Ref, availability: fdecl::Availability,
8072 target_name: &str|
8073 {
8074 fdecl::Offer::Storage(fdecl::OfferStorage {
8075 source: Some(source),
8076 source_name: Some("data".to_string()),
8077 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8078 name: "sink".to_string(),
8079 collection: None,
8080 })),
8081 target_name: Some(target_name.into()),
8082 availability: Some(availability),
8083 ..Default::default()
8084 })
8085 };
8086 decl.offers = Some(vec![
8087 new_offer(
8090 fdecl::Ref::Parent(fdecl::ParentRef {}),
8091 fdecl::Availability::Required,
8092 "data0",
8093 ),
8094 new_offer(
8095 fdecl::Ref::Parent(fdecl::ParentRef {}),
8096 fdecl::Availability::Optional,
8097 "data1",
8098 ),
8099 new_offer(
8100 fdecl::Ref::Parent(fdecl::ParentRef {}),
8101 fdecl::Availability::SameAsTarget,
8102 "data2",
8103 ),
8104 new_offer(
8105 fdecl::Ref::VoidType(fdecl::VoidRef {}),
8106 fdecl::Availability::Optional,
8107 "data3",
8108 ),
8109 new_offer(
8112 fdecl::Ref::Self_(fdecl::SelfRef {}),
8113 fdecl::Availability::Optional,
8114 "data4",
8115 ),
8116 new_offer(
8117 fdecl::Ref::Self_(fdecl::SelfRef {}),
8118 fdecl::Availability::SameAsTarget,
8119 "data5",
8120 ),
8121 new_offer(
8123 fdecl::Ref::VoidType(fdecl::VoidRef {}),
8124 fdecl::Availability::Required,
8125 "data6",
8126 ),
8127 new_offer(
8128 fdecl::Ref::VoidType(fdecl::VoidRef {}),
8129 fdecl::Availability::SameAsTarget,
8130 "data7",
8131 ),
8132 ]);
8133 decl
8134 },
8135 result = {
8136 Err(ErrorList::new(vec![
8137 Error::availability_must_be_optional(
8138 DeclType::OfferStorage,
8139 "availability",
8140 Some(&"data".to_string()),
8141 ),
8142 Error::availability_must_be_optional(
8143 DeclType::OfferStorage,
8144 "availability",
8145 Some(&"data".to_string()),
8146 ),
8147 ]))
8148 },
8149 },
8150
8151 test_validate_offers_valid_service_aggregation => {
8152 input = {
8153 let mut decl = new_component_decl();
8154 decl.offers = Some(vec![
8155 fdecl::Offer::Service(fdecl::OfferService {
8156 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
8157 name: "coll_a".to_string()
8158 })),
8159 source_name: Some("fuchsia.logger.Log".to_string()),
8160 target: Some(fdecl::Ref::Child(
8161 fdecl::ChildRef {
8162 name: "child_c".to_string(),
8163 collection: None,
8164 }
8165 )),
8166 target_name: Some("fuchsia.logger.Log".to_string()),
8167 source_instance_filter: None,
8168 ..Default::default()
8169 }),
8170 fdecl::Offer::Service(fdecl::OfferService {
8171 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
8172 name: "coll_b".to_string()
8173 })),
8174 source_name: Some("fuchsia.logger.Log".to_string()),
8175 target: Some(fdecl::Ref::Child(
8176 fdecl::ChildRef {
8177 name: "child_c".to_string(),
8178 collection: None,
8179 }
8180 )),
8181 target_name: Some("fuchsia.logger.Log".to_string()),
8182 source_instance_filter: Some(vec!["a_different_default".to_string()]),
8183 ..Default::default()
8184 })
8185 ]);
8186 decl.children = Some(vec![
8187 fdecl::Child {
8188 name: Some("child_c".to_string()),
8189 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
8190 startup: Some(fdecl::StartupMode::Lazy),
8191 ..Default::default()
8192 },
8193 ]);
8194 decl.collections = Some(vec![
8195 fdecl::Collection {
8196 name: Some("coll_a".into()),
8197 durability: Some(fdecl::Durability::Transient),
8198 ..Default::default()
8199 },
8200 fdecl::Collection {
8201 name: Some("coll_b".into()),
8202 durability: Some(fdecl::Durability::Transient),
8203 ..Default::default()
8204 },
8205 ]);
8206 decl
8207 },
8208 result = Ok(()),
8209 },
8210
8211 test_validate_source_dictionary => {
8213 input = fdecl::Component {
8214 program: Some(fdecl::Program {
8215 runner: Some("elf".into()),
8216 info: Some(fdata::Dictionary {
8217 entries: None,
8218 ..Default::default()
8219 }),
8220 ..Default::default()
8221 }),
8222 uses: Some(vec![
8223 fdecl::Use::Protocol(fdecl::UseProtocol {
8224 dependency_type: Some(fdecl::DependencyType::Strong),
8225 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8226 source_dictionary: Some("bad//".into()),
8227 source_name: Some("foo".into()),
8228 target_path: Some("/svc/foo".into()),
8229 ..Default::default()
8230 }),
8231 ]),
8232 exposes: Some(vec![
8233 fdecl::Expose::Directory(fdecl::ExposeDirectory {
8234 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8235 name: "missing".into(),
8236 collection: None,
8237 })),
8238 source_dictionary: Some("in/dict".into()),
8239 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8240 source_name: Some("foo".into()),
8241 target_name: Some("bar".into()),
8242 ..Default::default()
8243 }),
8244 ]),
8245 offers: Some(vec![
8246 fdecl::Offer::Service(fdecl::OfferService {
8247 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8248 source_dictionary: Some("bad//".into()),
8249 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8250 name: "child".into(),
8251 collection: None,
8252 })),
8253 source_name: Some("foo".into()),
8254 target_name: Some("bar".into()),
8255 ..Default::default()
8256 }),
8257 ]),
8258 children: Some(vec![
8259 fdecl::Child {
8260 name: Some("child".into()),
8261 url: Some("fuchsia-pkg://child".into()),
8262 startup: Some(fdecl::StartupMode::Lazy),
8263 ..Default::default()
8264 },
8265 ]),
8266 ..Default::default()
8267 },
8268 result = Err(ErrorList::new(vec![
8269 Error::invalid_field(DeclType::UseProtocol, "source_dictionary"),
8270 Error::invalid_child(DeclType::ExposeDirectory, "source", "missing"),
8271 Error::invalid_field(DeclType::OfferService, "source_dictionary"),
8272 ])),
8273 },
8274 test_validate_dictionary_too_long => {
8275 input = fdecl::Component {
8276 program: Some(fdecl::Program {
8277 runner: Some("elf".into()),
8278 info: Some(fdata::Dictionary {
8279 entries: None,
8280 ..Default::default()
8281 }),
8282 ..Default::default()
8283 }),
8284 uses: Some(vec![
8285 fdecl::Use::Protocol(fdecl::UseProtocol {
8286 dependency_type: Some(fdecl::DependencyType::Strong),
8287 source: Some(fdecl::Ref::Parent( fdecl::ParentRef {} )),
8288 source_dictionary: Some("a".repeat(4096)),
8289 source_name: Some("foo".into()),
8290 target_path: Some("/svc/foo".into()),
8291 ..Default::default()
8292 }),
8293 ]),
8294 ..Default::default()
8295 },
8296 result = Err(ErrorList::new(vec![
8297 Error::field_too_long(DeclType::UseProtocol, "source_dictionary"),
8298 ])),
8299 },
8300
8301 test_validate_environment_empty => {
8303 input = {
8304 let mut decl = new_component_decl();
8305 decl.environments = Some(vec![fdecl::Environment {
8306 name: None,
8307 extends: None,
8308 runners: None,
8309 resolvers: None,
8310 stop_timeout_ms: None,
8311 debug_capabilities: None,
8312 ..Default::default()
8313 }]);
8314 decl
8315 },
8316 result = Err(ErrorList::new(vec![
8317 Error::missing_field(DeclType::Environment, "name"),
8318 Error::missing_field(DeclType::Environment, "extends"),
8319 ])),
8320 },
8321
8322 test_validate_environment_no_stop_timeout => {
8323 input = {
8324 let mut decl = new_component_decl();
8325 decl.environments = Some(vec![fdecl::Environment {
8326 name: Some("env".to_string()),
8327 extends: Some(fdecl::EnvironmentExtends::None),
8328 runners: None,
8329 resolvers: None,
8330 stop_timeout_ms: None,
8331 ..Default::default()
8332 }]);
8333 decl
8334 },
8335 result = Err(ErrorList::new(vec![Error::missing_field(DeclType::Environment, "stop_timeout_ms")])),
8336 },
8337
8338 test_validate_environment_extends_stop_timeout => {
8339 input = { let mut decl = new_component_decl();
8340 decl.environments = Some(vec![fdecl::Environment {
8341 name: Some("env".to_string()),
8342 extends: Some(fdecl::EnvironmentExtends::Realm),
8343 runners: None,
8344 resolvers: None,
8345 stop_timeout_ms: None,
8346 ..Default::default()
8347 }]);
8348 decl
8349 },
8350 result = Ok(()),
8351 },
8352 test_validate_environment_long_identifiers => {
8353 input = {
8354 let mut decl = new_component_decl();
8355 decl.environments = Some(vec![fdecl::Environment {
8356 name: Some("a".repeat(256)),
8357 extends: Some(fdecl::EnvironmentExtends::None),
8358 runners: Some(vec![
8359 fdecl::RunnerRegistration {
8360 source_name: Some("a".repeat(256)),
8361 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8362 target_name: Some("a".repeat(256)),
8363 ..Default::default()
8364 },
8365 ]),
8366 resolvers: Some(vec![
8367 fdecl::ResolverRegistration {
8368 resolver: Some("a".repeat(256)),
8369 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8370 scheme: Some("a".repeat(256)),
8371 ..Default::default()
8372 },
8373 ]),
8374 stop_timeout_ms: Some(1234),
8375 ..Default::default()
8376 }]);
8377 decl
8378 },
8379 result = Err(ErrorList::new(vec![
8380 Error::field_too_long(DeclType::Environment, "name"),
8381 Error::field_too_long(DeclType::RunnerRegistration, "source_name"),
8382 Error::field_too_long(DeclType::RunnerRegistration, "target_name"),
8383 Error::field_too_long(DeclType::ResolverRegistration, "resolver"),
8384 Error::field_too_long(DeclType::ResolverRegistration, "scheme"),
8385 ])),
8386 },
8387 test_validate_environment_empty_runner_resolver_fields => {
8388 input = {
8389 let mut decl = new_component_decl();
8390 decl.environments = Some(vec![fdecl::Environment {
8391 name: Some("a".to_string()),
8392 extends: Some(fdecl::EnvironmentExtends::None),
8393 runners: Some(vec![
8394 fdecl::RunnerRegistration {
8395 source_name: None,
8396 source: None,
8397 target_name: None,
8398 ..Default::default()
8399 },
8400 ]),
8401 resolvers: Some(vec![
8402 fdecl::ResolverRegistration {
8403 resolver: None,
8404 source: None,
8405 scheme: None,
8406 ..Default::default()
8407 },
8408 ]),
8409 stop_timeout_ms: Some(1234),
8410 ..Default::default()
8411 }]);
8412 decl
8413 },
8414 result = Err(ErrorList::new(vec![
8415 Error::missing_field(DeclType::RunnerRegistration, "source_name"),
8416 Error::missing_field(DeclType::RunnerRegistration, "source"),
8417 Error::missing_field(DeclType::RunnerRegistration, "target_name"),
8418 Error::missing_field(DeclType::ResolverRegistration, "resolver"),
8419 Error::missing_field(DeclType::ResolverRegistration, "source"),
8420 Error::missing_field(DeclType::ResolverRegistration, "scheme"),
8421 ])),
8422 },
8423 test_validate_environment_invalid_fields => {
8424 input = {
8425 let mut decl = new_component_decl();
8426 decl.environments = Some(vec![fdecl::Environment {
8427 name: Some("a".to_string()),
8428 extends: Some(fdecl::EnvironmentExtends::None),
8429 runners: Some(vec![
8430 fdecl::RunnerRegistration {
8431 source_name: Some("^a".to_string()),
8432 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
8433 target_name: Some("%a".to_string()),
8434 ..Default::default()
8435 },
8436 ]),
8437 resolvers: Some(vec![
8438 fdecl::ResolverRegistration {
8439 resolver: Some("^a".to_string()),
8440 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
8441 scheme: Some("9scheme".to_string()),
8442 ..Default::default()
8443 },
8444 ]),
8445 stop_timeout_ms: Some(1234),
8446 ..Default::default()
8447 }]);
8448 decl
8449 },
8450 result = Err(ErrorList::new(vec![
8451 Error::invalid_field(DeclType::RunnerRegistration, "source_name"),
8452 Error::invalid_field(DeclType::RunnerRegistration, "source"),
8453 Error::invalid_field(DeclType::RunnerRegistration, "target_name"),
8454 Error::invalid_field(DeclType::ResolverRegistration, "resolver"),
8455 Error::invalid_field(DeclType::ResolverRegistration, "source"),
8456 Error::invalid_field(DeclType::ResolverRegistration, "scheme"),
8457 ])),
8458 },
8459 test_validate_environment_missing_runner => {
8460 input = {
8461 let mut decl = new_component_decl();
8462 decl.environments = Some(vec![fdecl::Environment {
8463 name: Some("a".to_string()),
8464 extends: Some(fdecl::EnvironmentExtends::None),
8465 runners: Some(vec![
8466 fdecl::RunnerRegistration {
8467 source_name: Some("dart".to_string()),
8468 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
8469 target_name: Some("dart".to_string()),
8470 ..Default::default()
8471 },
8472 ]),
8473 resolvers: None,
8474 stop_timeout_ms: Some(1234),
8475 ..Default::default()
8476 }]);
8477 decl
8478 },
8479 result = Err(ErrorList::new(vec![
8480 Error::invalid_runner(DeclType::RunnerRegistration, "source_name", "dart"),
8481 ])),
8482 },
8483 test_validate_environment_duplicate_registrations => {
8484 input = {
8485 let mut decl = new_component_decl();
8486 decl.environments = Some(vec![fdecl::Environment {
8487 name: Some("a".to_string()),
8488 extends: Some(fdecl::EnvironmentExtends::None),
8489 runners: Some(vec![
8490 fdecl::RunnerRegistration {
8491 source_name: Some("dart".to_string()),
8492 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8493 target_name: Some("dart".to_string()),
8494 ..Default::default()
8495 },
8496 fdecl::RunnerRegistration {
8497 source_name: Some("other-dart".to_string()),
8498 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8499 target_name: Some("dart".to_string()),
8500 ..Default::default()
8501 },
8502 ]),
8503 resolvers: Some(vec![
8504 fdecl::ResolverRegistration {
8505 resolver: Some("pkg_resolver".to_string()),
8506 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8507 scheme: Some("fuchsia-pkg".to_string()),
8508 ..Default::default()
8509 },
8510 fdecl::ResolverRegistration {
8511 resolver: Some("base_resolver".to_string()),
8512 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8513 scheme: Some("fuchsia-pkg".to_string()),
8514 ..Default::default()
8515 },
8516 ]),
8517 stop_timeout_ms: Some(1234),
8518 ..Default::default()
8519 }]);
8520 decl
8521 },
8522 result = Err(ErrorList::new(vec![
8523 Error::duplicate_field(DeclType::RunnerRegistration, "target_name", "dart"),
8524 Error::duplicate_field(DeclType::ResolverRegistration, "scheme", "fuchsia-pkg"),
8525 ])),
8526 },
8527 test_validate_environment_from_missing_child => {
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("elf".to_string()),
8536 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8537 name: "missing".to_string(),
8538 collection: None,
8539 })),
8540 target_name: Some("elf".to_string()),
8541 ..Default::default()
8542 },
8543 ]),
8544 resolvers: Some(vec![
8545 fdecl::ResolverRegistration {
8546 resolver: Some("pkg_resolver".to_string()),
8547 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8548 name: "missing".to_string(),
8549 collection: None,
8550 })),
8551 scheme: Some("fuchsia-pkg".to_string()),
8552 ..Default::default()
8553 },
8554 ]),
8555 stop_timeout_ms: Some(1234),
8556 ..Default::default()
8557 }]);
8558 decl
8559 },
8560 result = Err(ErrorList::new(vec![
8561 Error::invalid_child(DeclType::RunnerRegistration, "source", "missing"),
8562 Error::invalid_child(DeclType::ResolverRegistration, "source", "missing"),
8563 ])),
8564 },
8565 test_validate_environment_runner_child_cycle => {
8566 input = {
8567 let mut decl = new_component_decl();
8568 decl.environments = Some(vec![fdecl::Environment {
8569 name: Some("env".to_string()),
8570 extends: Some(fdecl::EnvironmentExtends::None),
8571 runners: Some(vec![
8572 fdecl::RunnerRegistration {
8573 source_name: Some("elf".to_string()),
8574 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8575 name: "child".to_string(),
8576 collection: None,
8577 })),
8578 target_name: Some("elf".to_string()),
8579 ..Default::default()
8580 },
8581 ]),
8582 resolvers: None,
8583 stop_timeout_ms: Some(1234),
8584 ..Default::default()
8585 }]);
8586 decl.children = Some(vec![fdecl::Child {
8587 name: Some("child".to_string()),
8588 startup: Some(fdecl::StartupMode::Lazy),
8589 on_terminate: None,
8590 url: Some("fuchsia-pkg://child".to_string()),
8591 environment: Some("env".to_string()),
8592 ..Default::default()
8593 }]);
8594 decl
8595 },
8596 result = Err(ErrorList::new(vec![
8597 Error::dependency_cycle(
8598 "{{child child -> environment env -> child child}}"
8599 ),
8600 ])),
8601 },
8602 test_validate_environment_resolver_child_cycle => {
8603 input = {
8604 let mut decl = new_component_decl();
8605 decl.environments = Some(vec![fdecl::Environment {
8606 name: Some("env".to_string()),
8607 extends: Some(fdecl::EnvironmentExtends::None),
8608 runners: None,
8609 resolvers: Some(vec![
8610 fdecl::ResolverRegistration {
8611 resolver: Some("pkg_resolver".to_string()),
8612 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8613 name: "child".to_string(),
8614 collection: None,
8615 })),
8616 scheme: Some("fuchsia-pkg".to_string()),
8617 ..Default::default()
8618 },
8619 ]),
8620 stop_timeout_ms: Some(1234),
8621 ..Default::default()
8622 }]);
8623 decl.children = Some(vec![fdecl::Child {
8624 name: Some("child".to_string()),
8625 startup: Some(fdecl::StartupMode::Lazy),
8626 on_terminate: None,
8627 url: Some("fuchsia-pkg://child".to_string()),
8628 environment: Some("env".to_string()),
8629 ..Default::default()
8630 }]);
8631 decl
8632 },
8633 result = Err(ErrorList::new(vec![
8634 Error::dependency_cycle(
8635 "{{child child -> environment env -> child child}}"
8636 ),
8637 ])),
8638 },
8639 test_validate_environment_resolver_multiple_children_cycle => {
8640 input = {
8641 let mut decl = new_component_decl();
8642 decl.environments = Some(vec![fdecl::Environment {
8643 name: Some("env".to_string()),
8644 extends: Some(fdecl::EnvironmentExtends::None),
8645 runners: None,
8646 resolvers: Some(vec![
8647 fdecl::ResolverRegistration {
8648 resolver: Some("pkg_resolver".to_string()),
8649 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8650 name: "a".to_string(),
8651 collection: None,
8652 })),
8653 scheme: Some("fuchsia-pkg".to_string()),
8654 ..Default::default()
8655 },
8656 ]),
8657 stop_timeout_ms: Some(1234),
8658 ..Default::default()
8659 }]);
8660 decl.children = Some(vec![
8661 fdecl::Child {
8662 name: Some("a".to_string()),
8663 startup: Some(fdecl::StartupMode::Lazy),
8664 on_terminate: None,
8665 url: Some("fuchsia-pkg://child-a".to_string()),
8666 environment: None,
8667 ..Default::default()
8668 },
8669 fdecl::Child {
8670 name: Some("b".to_string()),
8671 startup: Some(fdecl::StartupMode::Lazy),
8672 on_terminate: None,
8673 url: Some("fuchsia-pkg://child-b".to_string()),
8674 environment: Some("env".to_string()),
8675 ..Default::default()
8676 },
8677 ]);
8678 decl.offers = Some(vec![fdecl::Offer::Service(fdecl::OfferService {
8679 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8680 name: "b".to_string(),
8681 collection: None,
8682 })),
8683 source_name: Some("thing".to_string()),
8684 target: Some(fdecl::Ref::Child(
8685 fdecl::ChildRef {
8686 name: "a".to_string(),
8687 collection: None,
8688 }
8689 )),
8690 target_name: Some("thing".to_string()),
8691 ..Default::default()
8692 })]);
8693 decl
8694 },
8695 result = Err(ErrorList::new(vec![
8696 Error::dependency_cycle(
8697 "{{child a -> environment env -> child b -> child a}}"
8698 ),
8699 ])),
8700 },
8701 test_validate_environment_debug_empty => {
8702 input = {
8703 let mut decl = new_component_decl();
8704 decl.environments = Some(vec![
8705 fdecl::Environment {
8706 name: Some("a".to_string()),
8707 extends: Some(fdecl::EnvironmentExtends::None),
8708 stop_timeout_ms: Some(2),
8709 debug_capabilities:Some(vec![
8710 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8711 source: None,
8712 source_name: None,
8713 target_name: None,
8714 ..Default::default()
8715 }),
8716 ]),
8717 ..Default::default()
8718 }]);
8719 decl
8720 },
8721 result = Err(ErrorList::new(vec![
8722 Error::missing_field(DeclType::DebugProtocolRegistration, "source"),
8723 Error::missing_field(DeclType::DebugProtocolRegistration, "source_name"),
8724 Error::missing_field(DeclType::DebugProtocolRegistration, "target_name"),
8725 ])),
8726 },
8727 test_validate_environment_debug_log_identifier => {
8728 input = {
8729 let mut decl = new_component_decl();
8730 decl.environments = Some(vec![
8731 fdecl::Environment {
8732 name: Some("a".to_string()),
8733 extends: Some(fdecl::EnvironmentExtends::None),
8734 stop_timeout_ms: Some(2),
8735 debug_capabilities:Some(vec![
8736 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8737 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8738 name: "a".repeat(256),
8739 collection: None,
8740 })),
8741 source_name: Some(format!("{}", "a".repeat(256))),
8742 target_name: Some(format!("{}", "b".repeat(256))),
8743 ..Default::default()
8744 }),
8745 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8746 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8747 source_name: Some("a".to_string()),
8748 target_name: Some(format!("{}", "b".repeat(256))),
8749 ..Default::default()
8750 }),
8751 ]),
8752 ..Default::default()
8753 }]);
8754 decl
8755 },
8756 result = Err(ErrorList::new(vec![
8757 Error::field_too_long(DeclType::DebugProtocolRegistration, "source.child.name"),
8758 Error::field_too_long(DeclType::DebugProtocolRegistration, "source_name"),
8759 Error::field_too_long(DeclType::DebugProtocolRegistration, "target_name"),
8760 Error::field_too_long(DeclType::DebugProtocolRegistration, "target_name"),
8761 ])),
8762 },
8763 test_validate_environment_debug_log_extraneous => {
8764 input = {
8765 let mut decl = new_component_decl();
8766 decl.environments = Some(vec![
8767 fdecl::Environment {
8768 name: Some("a".to_string()),
8769 extends: Some(fdecl::EnvironmentExtends::None),
8770 stop_timeout_ms: Some(2),
8771 debug_capabilities:Some(vec![
8772 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8773 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8774 name: "logger".to_string(),
8775 collection: Some("modular".to_string()),
8776 })),
8777 source_name: Some("fuchsia.logger.Log".to_string()),
8778 target_name: Some("fuchsia.logger.Log".to_string()),
8779 ..Default::default()
8780 }),
8781 ]),
8782 ..Default::default()
8783 }]);
8784 decl
8785 },
8786 result = Err(ErrorList::new(vec![
8787 Error::extraneous_field(DeclType::DebugProtocolRegistration, "source.child.collection"),
8788 ])),
8789 },
8790 test_validate_environment_debug_log_invalid_identifiers => {
8791 input = {
8792 let mut decl = new_component_decl();
8793 decl.environments = Some(vec![
8794 fdecl::Environment {
8795 name: Some("a".to_string()),
8796 extends: Some(fdecl::EnvironmentExtends::None),
8797 stop_timeout_ms: Some(2),
8798 debug_capabilities:Some(vec![
8799 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8800 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8801 name: "^bad".to_string(),
8802 collection: None,
8803 })),
8804 source_name: Some("foo/".to_string()),
8805 target_name: Some("/".to_string()),
8806 ..Default::default()
8807 }),
8808 ]),
8809 ..Default::default()
8810 }]);
8811 decl
8812 },
8813 result = Err(ErrorList::new(vec![
8814 Error::invalid_field(DeclType::DebugProtocolRegistration, "source.child.name"),
8815 Error::invalid_field(DeclType::DebugProtocolRegistration, "source_name"),
8816 Error::invalid_field(DeclType::DebugProtocolRegistration, "target_name"),
8817 ])),
8818 },
8819 test_validate_environment_debug_log_invalid_child => {
8820 input = {
8821 let mut decl = new_component_decl();
8822 decl.environments = Some(vec![
8823 fdecl::Environment {
8824 name: Some("a".to_string()),
8825 extends: Some(fdecl::EnvironmentExtends::None),
8826 stop_timeout_ms: Some(2),
8827 debug_capabilities:Some(vec![
8828 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8829 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8830 name: "logger".to_string(),
8831 collection: None,
8832 })),
8833 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
8834 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
8835 ..Default::default()
8836 }),
8837 ]),
8838 ..Default::default()
8839 }]);
8840 decl.children = Some(vec![
8841 fdecl::Child {
8842 name: Some("netstack".to_string()),
8843 url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
8844 startup: Some(fdecl::StartupMode::Lazy),
8845 on_terminate: None,
8846 environment: None,
8847 ..Default::default()
8848 },
8849 ]);
8850 decl
8851 },
8852 result = Err(ErrorList::new(vec![
8853 Error::invalid_child(DeclType::DebugProtocolRegistration, "source", "logger"),
8854
8855 ])),
8856 },
8857 test_validate_environment_debug_source_capability => {
8858 input = {
8859 let mut decl = new_component_decl();
8860 decl.environments = Some(vec![
8861 fdecl::Environment {
8862 name: Some("a".to_string()),
8863 extends: Some(fdecl::EnvironmentExtends::None),
8864 stop_timeout_ms: Some(2),
8865 debug_capabilities:Some(vec![
8866 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8867 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
8868 name: "storage".to_string(),
8869 })),
8870 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
8871 target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
8872 ..Default::default()
8873 }),
8874 ]),
8875 ..Default::default()
8876 }]);
8877 decl
8878 },
8879 result = Err(ErrorList::new(vec![
8880 Error::invalid_field(DeclType::DebugProtocolRegistration, "source"),
8881 ])),
8882 },
8883
8884 test_validate_children_empty => {
8886 input = {
8887 let mut decl = new_component_decl();
8888 decl.children = Some(vec![fdecl::Child{
8889 name: None,
8890 url: None,
8891 startup: None,
8892 on_terminate: None,
8893 environment: None,
8894 ..Default::default()
8895 }]);
8896 decl
8897 },
8898 result = Err(ErrorList::new(vec![
8899 Error::missing_field(DeclType::Child, "name"),
8900 Error::missing_field(DeclType::Child, "url"),
8901 Error::missing_field(DeclType::Child, "startup"),
8902 ])),
8904 },
8905 test_validate_children_invalid_identifiers => {
8906 input = {
8907 let mut decl = new_component_decl();
8908 decl.children = Some(vec![fdecl::Child{
8909 name: Some("^bad".to_string()),
8910 url: Some("scheme://invalid-port:99999999/path#frag".to_string()),
8911 startup: Some(fdecl::StartupMode::Lazy),
8912 on_terminate: None,
8913 environment: None,
8914 ..Default::default()
8915 }]);
8916 decl
8917 },
8918 result = Err(ErrorList::new(vec![
8919 Error::invalid_field(DeclType::Child, "name"),
8920 Error::invalid_url(DeclType::Child, "url", "\"scheme://invalid-port:99999999/path#frag\": Malformed URL: InvalidPort."),
8921 ])),
8922 },
8923 test_validate_children_long_identifiers => {
8924 input = {
8925 let mut decl = new_component_decl();
8926 decl.children = Some(vec![fdecl::Child{
8927 name: Some("a".repeat(1025)),
8928 url: Some(format!("fuchsia-pkg://{}", "a".repeat(4083))),
8929 startup: Some(fdecl::StartupMode::Lazy),
8930 on_terminate: None,
8931 environment: Some("a".repeat(1025)),
8932 ..Default::default()
8933 }]);
8934 decl
8935 },
8936 result = Err(ErrorList::new(vec![
8937 Error::field_too_long(DeclType::Child, "name"),
8938 Error::field_too_long(DeclType::Child, "url"),
8939 Error::field_too_long(DeclType::Child, "environment"),
8940 Error::invalid_environment(DeclType::Child, "environment", "a".repeat(1025)),
8941 ])),
8942 },
8943 test_validate_child_references_unknown_env => {
8944 input = {
8945 let mut decl = new_component_decl();
8946 decl.children = Some(vec![fdecl::Child{
8947 name: Some("foo".to_string()),
8948 url: Some("fuchsia-pkg://foo".to_string()),
8949 startup: Some(fdecl::StartupMode::Lazy),
8950 on_terminate: None,
8951 environment: Some("test_env".to_string()),
8952 ..Default::default()
8953 }]);
8954 decl
8955 },
8956 result = Err(ErrorList::new(vec![
8957 Error::invalid_environment(DeclType::Child, "environment", "test_env"),
8958 ])),
8959 },
8960
8961 test_validate_collections_empty => {
8963 input = {
8964 let mut decl = new_component_decl();
8965 decl.collections = Some(vec![fdecl::Collection{
8966 name: None,
8967 durability: None,
8968 environment: None,
8969 allowed_offers: None,
8970 allow_long_names: None,
8971 ..Default::default()
8972 }]);
8973 decl
8974 },
8975 result = Err(ErrorList::new(vec![
8976 Error::missing_field(DeclType::Collection, "name"),
8977 Error::missing_field(DeclType::Collection, "durability"),
8978 ])),
8979 },
8980 test_validate_collections_invalid_identifiers => {
8981 input = {
8982 let mut decl = new_component_decl();
8983 decl.collections = Some(vec![fdecl::Collection{
8984 name: Some("^bad".to_string()),
8985 durability: Some(fdecl::Durability::Transient),
8986 environment: None,
8987 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
8988 allow_long_names: None,
8989 ..Default::default()
8990 }]);
8991 decl
8992 },
8993 result = Err(ErrorList::new(vec![
8994 Error::invalid_field(DeclType::Collection, "name"),
8995 ])),
8996 },
8997 test_validate_collections_long_identifiers => {
8998 input = {
8999 let mut decl = new_component_decl();
9000 decl.collections = Some(vec![fdecl::Collection{
9001 name: Some("a".repeat(1025)),
9002 durability: Some(fdecl::Durability::Transient),
9003 environment: None,
9004 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
9005 allow_long_names: None,
9006 ..Default::default()
9007 }]);
9008 decl
9009 },
9010 result = Err(ErrorList::new(vec![
9011 Error::field_too_long(DeclType::Collection, "name"),
9012 ])),
9013 },
9014 test_validate_collection_references_unknown_env => {
9015 input = {
9016 let mut decl = new_component_decl();
9017 decl.collections = Some(vec![fdecl::Collection {
9018 name: Some("foo".to_string()),
9019 durability: Some(fdecl::Durability::Transient),
9020 environment: Some("test_env".to_string()),
9021 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
9022 allow_long_names: None,
9023 ..Default::default()
9024 }]);
9025 decl
9026 },
9027 result = Err(ErrorList::new(vec![
9028 Error::invalid_environment(DeclType::Collection, "environment", "test_env"),
9029 ])),
9030 },
9031
9032 test_validate_capabilities_empty => {
9034 input = {
9035 let mut decl = new_component_decl();
9036 decl.capabilities = Some(vec![
9037 fdecl::Capability::Service(fdecl::Service {
9038 name: None,
9039 source_path: None,
9040 ..Default::default()
9041 }),
9042 fdecl::Capability::Protocol(fdecl::Protocol {
9043 name: None,
9044 source_path: None,
9045 ..Default::default()
9046 }),
9047 fdecl::Capability::Directory(fdecl::Directory {
9048 name: None,
9049 source_path: None,
9050 rights: None,
9051 ..Default::default()
9052 }),
9053 fdecl::Capability::Storage(fdecl::Storage {
9054 name: None,
9055 source: None,
9056 backing_dir: None,
9057 subdir: None,
9058 storage_id: None,
9059 ..Default::default()
9060 }),
9061 fdecl::Capability::Runner(fdecl::Runner {
9062 name: None,
9063 source_path: None,
9064 ..Default::default()
9065 }),
9066 fdecl::Capability::Resolver(fdecl::Resolver {
9067 name: None,
9068 source_path: None,
9069 ..Default::default()
9070 }),
9071 fdecl::Capability::Dictionary(fdecl::Dictionary {
9072 ..Default::default()
9073 }),
9074 ]);
9075 decl
9076 },
9077 result = Err(ErrorList::new(vec![
9078 Error::missing_field(DeclType::Dictionary, "name"),
9079 Error::missing_field(DeclType::Service, "name"),
9080 Error::missing_field(DeclType::Service, "source_path"),
9081 Error::missing_field(DeclType::Protocol, "name"),
9082 Error::missing_field(DeclType::Protocol, "source_path"),
9083 Error::missing_field(DeclType::Directory, "name"),
9084 Error::missing_field(DeclType::Directory, "source_path"),
9085 Error::missing_field(DeclType::Directory, "rights"),
9086 Error::missing_field(DeclType::Storage, "source"),
9087 Error::missing_field(DeclType::Storage, "name"),
9088 Error::missing_field(DeclType::Storage, "storage_id"),
9089 Error::missing_field(DeclType::Storage, "backing_dir"),
9090 Error::missing_field(DeclType::Runner, "name"),
9091 Error::missing_field(DeclType::Runner, "source_path"),
9092 Error::missing_field(DeclType::Resolver, "name"),
9093 Error::missing_field(DeclType::Resolver, "source_path"),
9094 ])),
9095 },
9096 test_validate_capabilities_invalid_identifiers => {
9097 input = {
9098 let mut decl = new_component_decl();
9099 decl.capabilities = Some(vec![
9100 fdecl::Capability::Service(fdecl::Service {
9101 name: Some("^bad".to_string()),
9102 source_path: Some("&bad".to_string()),
9103 ..Default::default()
9104 }),
9105 fdecl::Capability::Protocol(fdecl::Protocol {
9106 name: Some("^bad".to_string()),
9107 source_path: Some("&bad".to_string()),
9108 ..Default::default()
9109 }),
9110 fdecl::Capability::Directory(fdecl::Directory {
9111 name: Some("^bad".to_string()),
9112 source_path: Some("&bad".to_string()),
9113 rights: Some(fio::Operations::CONNECT),
9114 ..Default::default()
9115 }),
9116 fdecl::Capability::Storage(fdecl::Storage {
9117 name: Some("^bad".to_string()),
9118 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9119 name: "/bad".to_string()
9120 })),
9121 backing_dir: Some("&bad".to_string()),
9122 subdir: None,
9123 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9124 ..Default::default()
9125 }),
9126 fdecl::Capability::Runner(fdecl::Runner {
9127 name: Some("^bad".to_string()),
9128 source_path: Some("&bad".to_string()),
9129 ..Default::default()
9130 }),
9131 fdecl::Capability::Resolver(fdecl::Resolver {
9132 name: Some("^bad".to_string()),
9133 source_path: Some("&bad".to_string()),
9134 ..Default::default()
9135 }),
9136 fdecl::Capability::Dictionary(fdecl::Dictionary {
9137 name: Some("^bad".to_string()),
9138 ..Default::default()
9139 }),
9140 ]);
9141 decl
9142 },
9143 result = Err(ErrorList::new(vec![
9144 Error::invalid_field(DeclType::Dictionary, "name"),
9145 Error::invalid_field(DeclType::Service, "name"),
9146 Error::invalid_field(DeclType::Service, "source_path"),
9147 Error::invalid_field(DeclType::Protocol, "name"),
9148 Error::invalid_field(DeclType::Protocol, "source_path"),
9149 Error::invalid_field(DeclType::Directory, "name"),
9150 Error::invalid_field(DeclType::Directory, "source_path"),
9151 Error::invalid_field(DeclType::Storage, "source"),
9152 Error::invalid_field(DeclType::Storage, "name"),
9153 Error::invalid_field(DeclType::Storage, "backing_dir"),
9154 Error::invalid_field(DeclType::Runner, "name"),
9155 Error::invalid_field(DeclType::Runner, "source_path"),
9156 Error::invalid_field(DeclType::Resolver, "name"),
9157 Error::invalid_field(DeclType::Resolver, "source_path"),
9158 ])),
9159 },
9160 test_validate_capabilities_invalid_child => {
9161 input = {
9162 let mut decl = new_component_decl();
9163 decl.capabilities = Some(vec![
9164 fdecl::Capability::Storage(fdecl::Storage {
9165 name: Some("foo".to_string()),
9166 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9167 name: "invalid".to_string(),
9168 })),
9169 backing_dir: Some("foo".to_string()),
9170 subdir: None,
9171 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9172 ..Default::default()
9173 }),
9174 ]);
9175 decl
9176 },
9177 result = Err(ErrorList::new(vec![
9178 Error::invalid_field(DeclType::Storage, "source"),
9179 ])),
9180 },
9181 test_validate_capabilities_long_identifiers => {
9182 input = {
9183 let mut decl = new_component_decl();
9184 decl.capabilities = Some(vec![
9185 fdecl::Capability::Service(fdecl::Service {
9186 name: Some("a".repeat(256)),
9187 source_path: Some("/c".repeat(2048)),
9188 ..Default::default()
9189 }),
9190 fdecl::Capability::Protocol(fdecl::Protocol {
9191 name: Some("a".repeat(256)),
9192 source_path: Some("/c".repeat(2048)),
9193 ..Default::default()
9194 }),
9195 fdecl::Capability::Directory(fdecl::Directory {
9196 name: Some("a".repeat(256)),
9197 source_path: Some("/c".repeat(2048)),
9198 rights: Some(fio::Operations::CONNECT),
9199 ..Default::default()
9200 }),
9201 fdecl::Capability::Storage(fdecl::Storage {
9202 name: Some("a".repeat(256)),
9203 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
9204 name: "b".repeat(256),
9205 collection: None,
9206 })),
9207 backing_dir: Some(format!("{}", "c".repeat(256))),
9208 subdir: None,
9209 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9210 ..Default::default()
9211 }),
9212 fdecl::Capability::Runner(fdecl::Runner {
9213 name: Some("a".repeat(256)),
9214 source_path: Some("/c".repeat(2048)),
9215 ..Default::default()
9216 }),
9217 fdecl::Capability::Resolver(fdecl::Resolver {
9218 name: Some("a".repeat(256)),
9219 source_path: Some("/c".repeat(2048)),
9220 ..Default::default()
9221 }),
9222 fdecl::Capability::Dictionary(fdecl::Dictionary {
9223 name: Some("a".repeat(256)),
9224 ..Default::default()
9225 }),
9226 ]);
9227 decl
9228 },
9229 result = Err(ErrorList::new(vec![
9230 Error::field_too_long(DeclType::Dictionary, "name"),
9231 Error::field_too_long(DeclType::Service, "name"),
9232 Error::field_too_long(DeclType::Service, "source_path"),
9233 Error::field_too_long(DeclType::Protocol, "name"),
9234 Error::field_too_long(DeclType::Protocol, "source_path"),
9235 Error::field_too_long(DeclType::Directory, "name"),
9236 Error::field_too_long(DeclType::Directory, "source_path"),
9237 Error::field_too_long(DeclType::Storage, "source.child.name"),
9238 Error::field_too_long(DeclType::Storage, "name"),
9239 Error::field_too_long(DeclType::Storage, "backing_dir"),
9240 Error::field_too_long(DeclType::Runner, "name"),
9241 Error::field_too_long(DeclType::Runner, "source_path"),
9242 Error::field_too_long(DeclType::Resolver, "name"),
9243 Error::field_too_long(DeclType::Resolver, "source_path"),
9244 ])),
9245 },
9246 test_validate_capabilities_duplicate_name => {
9247 input = {
9248 let mut decl = new_component_decl();
9249 decl.capabilities = Some(vec![
9250 fdecl::Capability::Service(fdecl::Service {
9251 name: Some("service".to_string()),
9252 source_path: Some("/service".to_string()),
9253 ..Default::default()
9254 }),
9255 fdecl::Capability::Service(fdecl::Service {
9256 name: Some("service".to_string()),
9257 source_path: Some("/service".to_string()),
9258 ..Default::default()
9259 }),
9260 fdecl::Capability::Protocol(fdecl::Protocol {
9261 name: Some("protocol".to_string()),
9262 source_path: Some("/protocol".to_string()),
9263 ..Default::default()
9264 }),
9265 fdecl::Capability::Protocol(fdecl::Protocol {
9266 name: Some("protocol".to_string()),
9267 source_path: Some("/protocol".to_string()),
9268 ..Default::default()
9269 }),
9270 fdecl::Capability::Directory(fdecl::Directory {
9271 name: Some("directory".to_string()),
9272 source_path: Some("/directory".to_string()),
9273 rights: Some(fio::Operations::CONNECT),
9274 ..Default::default()
9275 }),
9276 fdecl::Capability::Directory(fdecl::Directory {
9277 name: Some("directory".to_string()),
9278 source_path: Some("/directory".to_string()),
9279 rights: Some(fio::Operations::CONNECT),
9280 ..Default::default()
9281 }),
9282 fdecl::Capability::Storage(fdecl::Storage {
9283 name: Some("storage".to_string()),
9284 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
9285 backing_dir: Some("directory".to_string()),
9286 subdir: None,
9287 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9288 ..Default::default()
9289 }),
9290 fdecl::Capability::Storage(fdecl::Storage {
9291 name: Some("storage".to_string()),
9292 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
9293 backing_dir: Some("directory".to_string()),
9294 subdir: None,
9295 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9296 ..Default::default()
9297 }),
9298 fdecl::Capability::Runner(fdecl::Runner {
9299 name: Some("runner".to_string()),
9300 source_path: Some("/runner".to_string()),
9301 ..Default::default()
9302 }),
9303 fdecl::Capability::Runner(fdecl::Runner {
9304 name: Some("runner".to_string()),
9305 source_path: Some("/runner".to_string()),
9306 ..Default::default()
9307 }),
9308 fdecl::Capability::Resolver(fdecl::Resolver {
9309 name: Some("resolver".to_string()),
9310 source_path: Some("/resolver".to_string()),
9311 ..Default::default()
9312 }),
9313 fdecl::Capability::Resolver(fdecl::Resolver {
9314 name: Some("resolver".to_string()),
9315 source_path: Some("/resolver".to_string()),
9316 ..Default::default()
9317 }),
9318 fdecl::Capability::Dictionary(fdecl::Dictionary {
9319 name: Some("dictionary".to_string()),
9320 ..Default::default()
9321 }),
9322 fdecl::Capability::Dictionary(fdecl::Dictionary {
9323 name: Some("dictionary".to_string()),
9324 ..Default::default()
9325 }),
9326 ]);
9327 decl
9328 },
9329 result = Err(ErrorList::new(vec![
9330 Error::duplicate_field(DeclType::Dictionary, "name", "dictionary"),
9331 Error::duplicate_field(DeclType::Service, "name", "service"),
9332 Error::duplicate_field(DeclType::Protocol, "name", "protocol"),
9333 Error::duplicate_field(DeclType::Directory, "name", "directory"),
9334 Error::duplicate_field(DeclType::Storage, "name", "storage"),
9335 Error::duplicate_field(DeclType::Runner, "name", "runner"),
9336 Error::duplicate_field(DeclType::Resolver, "name", "resolver"),
9337 ])),
9338 },
9339 test_validate_invalid_service_aggregation_conflicting_filter => {
9340 input = {
9341 let mut decl = new_component_decl();
9342 decl.offers = Some(vec![
9343 fdecl::Offer::Service(fdecl::OfferService {
9344 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9345 name: "coll_a".to_string()
9346 })),
9347 source_name: Some("fuchsia.logger.Log".to_string()),
9348 target: Some(fdecl::Ref::Child(
9349 fdecl::ChildRef {
9350 name: "child_c".to_string(),
9351 collection: None,
9352 }
9353 )),
9354 target_name: Some("fuchsia.logger.Log1".to_string()),
9355 source_instance_filter: Some(vec!["default".to_string()]),
9356 ..Default::default()
9357 }),
9358 fdecl::Offer::Service(fdecl::OfferService {
9359 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9360 name: "coll_b".to_string()
9361 })),
9362 source_name: Some("fuchsia.logger.Log".to_string()),
9363 target: Some(fdecl::Ref::Child(
9364 fdecl::ChildRef {
9365 name: "child_c".to_string(),
9366 collection: None,
9367 }
9368 )),
9369 target_name: Some("fuchsia.logger.Log1".to_string()),
9370 source_instance_filter: Some(vec!["default".to_string()]),
9371 ..Default::default()
9372 }),
9373 ]);
9374 decl.collections = Some(vec![
9375 fdecl::Collection {
9376 name: Some("coll_a".to_string()),
9377 durability: Some(fdecl::Durability::Transient),
9378 ..Default::default()
9379 },
9380 fdecl::Collection {
9381 name: Some("coll_b".to_string()),
9382 durability: Some(fdecl::Durability::Transient),
9383 ..Default::default()
9384 },
9385 ]);
9386 decl.children = Some(vec![
9387 fdecl::Child {
9388 name: Some("child_c".to_string()),
9389 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9390 startup: Some(fdecl::StartupMode::Lazy),
9391 ..Default::default()
9392 },
9393 ]);
9394 decl
9395 },
9396 result = Err(ErrorList::new(vec![
9397 Error::invalid_aggregate_offer("Conflicting source_instance_filter in aggregate \
9398 service offer, instance_name 'default' seen in filter lists multiple times"),
9399 ])),
9400 },
9401
9402 test_validate_invalid_service_aggregation_conflicting_source_name => {
9403 input = {
9404 let mut decl = new_component_decl();
9405 decl.offers = Some(vec![
9406 fdecl::Offer::Service(fdecl::OfferService {
9407 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9408 name: "coll_a".into()
9409 })),
9410 source_name: Some("fuchsia.logger.Log".to_string()),
9411 target: Some(fdecl::Ref::Child(
9412 fdecl::ChildRef {
9413 name: "child_c".to_string(),
9414 collection: None,
9415 }
9416 )),
9417 target_name: Some("fuchsia.logger.Log2".to_string()),
9418 source_instance_filter: Some(vec!["default2".to_string()]),
9419 ..Default::default()
9420 }),
9421 fdecl::Offer::Service(fdecl::OfferService {
9422 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9423 name: "coll_b".into()
9424 })),
9425 source_name: Some("fuchsia.logger.LogAlt".to_string()),
9426 target: Some(fdecl::Ref::Child(
9427 fdecl::ChildRef {
9428 name: "child_c".to_string(),
9429 collection: None,
9430 }
9431 )),
9432 target_name: Some("fuchsia.logger.Log2".to_string()),
9433 source_instance_filter: Some(vec!["default".to_string()]),
9434 ..Default::default()
9435 })
9436 ]);
9437 decl.collections = Some(vec![
9438 fdecl::Collection {
9439 name: Some("coll_a".to_string()),
9440 durability: Some(fdecl::Durability::Transient),
9441 ..Default::default()
9442 },
9443 fdecl::Collection {
9444 name: Some("coll_b".to_string()),
9445 durability: Some(fdecl::Durability::Transient),
9446 ..Default::default()
9447 },
9448 ]);
9449 decl.children = Some(vec![
9450 fdecl::Child {
9451 name: Some("child_c".to_string()),
9452 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9453 startup: Some(fdecl::StartupMode::Lazy),
9454 on_terminate: None,
9455 environment: None,
9456 ..Default::default()
9457 },
9458 ]);
9459 decl
9460 },
9461 result = Err(ErrorList::new(vec![
9462 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."),
9463 ])),
9464 },
9465
9466 test_validate_resolvers_missing_from_offer => {
9467 input = {
9468 let mut decl = new_component_decl();
9469 decl.offers = Some(vec![fdecl::Offer::Resolver(fdecl::OfferResolver {
9470 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
9471 source_name: Some("a".to_string()),
9472 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
9473 target_name: Some("a".to_string()),
9474 ..Default::default()
9475 })]);
9476 decl.children = Some(vec![fdecl::Child {
9477 name: Some("child".to_string()),
9478 url: Some("test:///child".to_string()),
9479 startup: Some(fdecl::StartupMode::Eager),
9480 on_terminate: None,
9481 environment: None,
9482 ..Default::default()
9483 }]);
9484 decl
9485 },
9486 result = Err(ErrorList::new(vec![
9487 Error::invalid_capability(DeclType::OfferResolver, "source", "a"),
9488 ])),
9489 },
9490 test_validate_resolvers_missing_from_expose => {
9491 input = {
9492 let mut decl = new_component_decl();
9493 decl.exposes = Some(vec![fdecl::Expose::Resolver(fdecl::ExposeResolver {
9494 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
9495 source_name: Some("a".to_string()),
9496 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
9497 target_name: Some("a".to_string()),
9498 ..Default::default()
9499 })]);
9500 decl
9501 },
9502 result = Err(ErrorList::new(vec![
9503 Error::invalid_capability(DeclType::ExposeResolver, "source", "a"),
9504 ])),
9505 },
9506
9507 test_validate_config_missing_config => {
9508 input = {
9509 let mut decl = new_component_decl();
9510 decl.config = Some(fdecl::ConfigSchema{
9511 fields: None,
9512 checksum: None,
9513 value_source: None,
9514 ..Default::default()
9515 });
9516 decl
9517 },
9518 result = Err(ErrorList::new(vec![
9519 Error::missing_field(DeclType::ConfigSchema, "fields"),
9520 Error::missing_field(DeclType::ConfigSchema, "checksum"),
9521 Error::missing_field(DeclType::ConfigSchema, "value_source"),
9522 ])),
9523 },
9524
9525 test_validate_config_missing_config_field => {
9526 input = {
9527 let mut decl = new_component_decl();
9528 decl.config = Some(fdecl::ConfigSchema{
9529 fields: Some(vec![
9530 fdecl::ConfigField {
9531 key: None,
9532 type_: None,
9533 ..Default::default()
9534 }
9535 ]),
9536 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9537 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9538 ..Default::default()
9539 });
9540 decl
9541 },
9542 result = Err(ErrorList::new(vec![
9543 Error::missing_field(DeclType::ConfigField, "key"),
9544 Error::missing_field(DeclType::ConfigField, "value_type"),
9545 ])),
9546 },
9547
9548 test_validate_config_bool => {
9549 input = {
9550 let mut decl = new_component_decl();
9551 decl.config = Some(fdecl::ConfigSchema{
9552 fields: Some(vec![
9553 fdecl::ConfigField {
9554 key: Some("test".to_string()),
9555 type_: Some(fdecl::ConfigType {
9556 layout: fdecl::ConfigTypeLayout::Bool,
9557 parameters: Some(vec![]),
9558 constraints: vec![]
9559 }),
9560 ..Default::default()
9561 }
9562 ]),
9563 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9564 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9565 ..Default::default()
9566 });
9567 decl
9568 },
9569 result = Ok(()),
9570 },
9571
9572 test_validate_config_bool_extra_constraint => {
9573 input = {
9574 let mut decl = new_component_decl();
9575 decl.config = Some(fdecl::ConfigSchema{
9576 fields: Some(vec![
9577 fdecl::ConfigField {
9578 key: Some("test".to_string()),
9579 type_: Some(fdecl::ConfigType {
9580 layout: fdecl::ConfigTypeLayout::Bool,
9581 parameters: Some(vec![]),
9582 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9583 }),
9584 ..Default::default()
9585 }
9586 ]),
9587 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9588 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9589 ..Default::default()
9590 });
9591 decl
9592 },
9593 result = Err(ErrorList::new(vec![
9594 Error::extraneous_field(DeclType::ConfigType, "constraints")
9595 ])),
9596 },
9597
9598 test_validate_config_bool_missing_parameters => {
9599 input = {
9600 let mut decl = new_component_decl();
9601 decl.config = Some(fdecl::ConfigSchema{
9602 fields: Some(vec![
9603 fdecl::ConfigField {
9604 key: Some("test".to_string()),
9605 type_: Some(fdecl::ConfigType {
9606 layout: fdecl::ConfigTypeLayout::Bool,
9607 parameters: None,
9608 constraints: vec![]
9609 }),
9610 ..Default::default()
9611 }
9612 ]),
9613 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9614 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9615 ..Default::default()
9616 });
9617 decl
9618 },
9619 result = Err(ErrorList::new(vec![
9620 Error::missing_field(DeclType::ConfigType, "parameters")
9621 ])),
9622 },
9623
9624 test_validate_config_string => {
9625 input = {
9626 let mut decl = new_component_decl();
9627 decl.config = Some(fdecl::ConfigSchema{
9628 fields: Some(vec![
9629 fdecl::ConfigField {
9630 key: Some("test".to_string()),
9631 type_: Some(fdecl::ConfigType {
9632 layout: fdecl::ConfigTypeLayout::String,
9633 parameters: Some(vec![]),
9634 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9635 }),
9636 ..Default::default()
9637 }
9638 ]),
9639 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9640 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9641 ..Default::default()
9642 });
9643 decl
9644 },
9645 result = Ok(()),
9646 },
9647
9648 test_validate_config_string_missing_parameter => {
9649 input = {
9650 let mut decl = new_component_decl();
9651 decl.config = Some(fdecl::ConfigSchema{
9652 fields: Some(vec![
9653 fdecl::ConfigField {
9654 key: Some("test".to_string()),
9655 type_: Some(fdecl::ConfigType {
9656 layout: fdecl::ConfigTypeLayout::String,
9657 parameters: None,
9658 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9659 }),
9660 ..Default::default()
9661 }
9662 ]),
9663 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9664 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9665 ..Default::default()
9666 });
9667 decl
9668 },
9669 result = Err(ErrorList::new(vec![
9670 Error::missing_field(DeclType::ConfigType, "parameters")
9671 ])),
9672 },
9673
9674 test_validate_config_string_missing_constraint => {
9675 input = {
9676 let mut decl = new_component_decl();
9677 decl.config = Some(fdecl::ConfigSchema{
9678 fields: Some(vec![
9679 fdecl::ConfigField {
9680 key: Some("test".to_string()),
9681 type_: Some(fdecl::ConfigType {
9682 layout: fdecl::ConfigTypeLayout::String,
9683 parameters: Some(vec![]),
9684 constraints: vec![]
9685 }),
9686 ..Default::default()
9687 }
9688 ]),
9689 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9690 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9691 ..Default::default()
9692 });
9693 decl
9694 },
9695 result = Err(ErrorList::new(vec![
9696 Error::missing_field(DeclType::ConfigType, "constraints")
9697 ])),
9698 },
9699
9700 test_validate_config_string_extra_constraint => {
9701 input = {
9702 let mut decl = new_component_decl();
9703 decl.config = Some(fdecl::ConfigSchema{
9704 fields: Some(vec![
9705 fdecl::ConfigField {
9706 key: Some("test".to_string()),
9707 type_: Some(fdecl::ConfigType {
9708 layout: fdecl::ConfigTypeLayout::String,
9709 parameters: Some(vec![]),
9710 constraints: vec![fdecl::LayoutConstraint::MaxSize(10), fdecl::LayoutConstraint::MaxSize(10)]
9711 }),
9712 ..Default::default()
9713 }
9714 ]),
9715 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9716 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9717 ..Default::default()
9718 });
9719 decl
9720 },
9721 result = Err(ErrorList::new(vec![
9722 Error::extraneous_field(DeclType::ConfigType, "constraints")
9723 ])),
9724 },
9725
9726 test_validate_config_vector_bool => {
9727 input = {
9728 let mut decl = new_component_decl();
9729 decl.config = Some(fdecl::ConfigSchema{
9730 fields: Some(vec![
9731 fdecl::ConfigField {
9732 key: Some("test".to_string()),
9733 type_: Some(fdecl::ConfigType {
9734 layout: fdecl::ConfigTypeLayout::Vector,
9735 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9736 layout: fdecl::ConfigTypeLayout::Bool,
9737 parameters: Some(vec![]),
9738 constraints: vec![],
9739 })]),
9740 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9741 }),
9742 ..Default::default()
9743 }
9744 ]),
9745 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9746 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9747 ..Default::default()
9748 });
9749 decl
9750 },
9751 result = Ok(()),
9752 },
9753
9754 test_validate_config_vector_extra_parameter => {
9755 input = {
9756 let mut decl = new_component_decl();
9757 decl.config = Some(fdecl::ConfigSchema{
9758 fields: Some(vec![
9759 fdecl::ConfigField {
9760 key: Some("test".to_string()),
9761 type_: Some(fdecl::ConfigType {
9762 layout: fdecl::ConfigTypeLayout::Vector,
9763 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9764 layout: fdecl::ConfigTypeLayout::Bool,
9765 parameters: Some(vec![]),
9766 constraints: vec![],
9767 }), fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9768 layout: fdecl::ConfigTypeLayout::Uint8,
9769 parameters: Some(vec![]),
9770 constraints: vec![],
9771 })]),
9772 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9773 }),
9774 ..Default::default()
9775 }
9776 ]),
9777 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9778 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9779 ..Default::default()
9780 });
9781 decl
9782 },
9783 result = Err(ErrorList::new(vec![
9784 Error::extraneous_field(DeclType::ConfigType, "parameters")
9785 ])),
9786 },
9787
9788 test_validate_config_vector_missing_parameter => {
9789 input = {
9790 let mut decl = new_component_decl();
9791 decl.config = Some(fdecl::ConfigSchema{
9792 fields: Some(vec![
9793 fdecl::ConfigField {
9794 key: Some("test".to_string()),
9795 type_: Some(fdecl::ConfigType {
9796 layout: fdecl::ConfigTypeLayout::Vector,
9797 parameters: Some(vec![]),
9798 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9799 }),
9800 ..Default::default()
9801 }
9802 ]),
9803 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9804 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9805 ..Default::default()
9806 });
9807 decl
9808 },
9809 result = Err(ErrorList::new(vec![
9810 Error::missing_field(DeclType::ConfigType, "parameters")
9811 ])),
9812 },
9813
9814 test_validate_config_vector_string => {
9815 input = {
9816 let mut decl = new_component_decl();
9817 decl.config = Some(fdecl::ConfigSchema{
9818 fields: Some(vec![
9819 fdecl::ConfigField {
9820 key: Some("test".to_string()),
9821 type_: Some(fdecl::ConfigType {
9822 layout: fdecl::ConfigTypeLayout::Vector,
9823 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9824 layout: fdecl::ConfigTypeLayout::String,
9825 parameters: Some(vec![]),
9826 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9827 })]),
9828 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9829 }),
9830 ..Default::default()
9831 }
9832 ]),
9833 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9834 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9835 ..Default::default()
9836 });
9837 decl
9838 },
9839 result = Ok(()),
9840 },
9841
9842 test_validate_config_vector_vector => {
9843 input = {
9844 let mut decl = new_component_decl();
9845 decl.config = Some(fdecl::ConfigSchema{
9846 fields: Some(vec![
9847 fdecl::ConfigField {
9848 key: Some("test".to_string()),
9849 type_: Some(fdecl::ConfigType {
9850 layout: fdecl::ConfigTypeLayout::Vector,
9851 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9852 layout: fdecl::ConfigTypeLayout::Vector,
9853 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9854 layout: fdecl::ConfigTypeLayout::String,
9855 parameters: Some(vec![]),
9856 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9857 })]),
9858 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9859 })]),
9860 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9861 }),
9862 ..Default::default()
9863 }
9864 ]),
9865 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9866 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9867 ..Default::default()
9868 });
9869 decl
9870 },
9871 result = Err(ErrorList::new(vec![
9872 Error::nested_vector()
9873 ])),
9874 },
9875
9876 test_validate_exposes_invalid_aggregation_different_availability => {
9877 input = {
9878 let mut decl = new_component_decl();
9879 decl.exposes = Some(vec![
9880 fdecl::Expose::Service(fdecl::ExposeService {
9881 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9882 name: "coll_a".into()
9883 })),
9884 source_name: Some("fuchsia.logger.Log".to_string()),
9885 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
9886 target_name: Some("fuchsia.logger.Log".to_string()),
9887 availability: Some(fdecl::Availability::Required),
9888 ..Default::default()
9889 }),
9890 fdecl::Expose::Service(fdecl::ExposeService {
9891 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9892 name: "coll_b".into()
9893 })),
9894 source_name: Some("fuchsia.logger.Log".to_string()),
9895 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
9896 target_name: Some("fuchsia.logger.Log".to_string()),
9897 availability: Some(fdecl::Availability::Optional),
9898 ..Default::default()
9899 })
9900 ]);
9901 decl.collections = Some(vec![
9902 fdecl::Collection {
9903 name: Some("coll_a".to_string()),
9904 durability: Some(fdecl::Durability::Transient),
9905 ..Default::default()
9906 },
9907 fdecl::Collection {
9908 name: Some("coll_b".to_string()),
9909 durability: Some(fdecl::Durability::Transient),
9910 ..Default::default()
9911 },
9912 ]);
9913 decl
9914 },
9915 result = Err(ErrorList::new(vec![
9916 Error::DifferentAvailabilityInAggregation(AvailabilityList(vec![
9917 fdecl::Availability::Required,
9918 fdecl::Availability::Optional,
9919 ]))
9920 ])),
9921 },
9922
9923 test_validate_offers_invalid_aggregation_different_availability => {
9924 input = {
9925 let mut decl = new_component_decl();
9926 decl.offers = Some(vec![
9927 fdecl::Offer::Service(fdecl::OfferService {
9928 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9929 name: "coll_a".into()
9930 })),
9931 source_name: Some("fuchsia.logger.Log".to_string()),
9932 target: Some(fdecl::Ref::Child(
9933 fdecl::ChildRef {
9934 name: "child_c".to_string(),
9935 collection: None,
9936 }
9937 )),
9938 target_name: Some("fuchsia.logger.Log".to_string()),
9939 source_instance_filter: Some(vec!["default".to_string()]),
9940 availability: Some(fdecl::Availability::Required),
9941 ..Default::default()
9942 }),
9943 fdecl::Offer::Service(fdecl::OfferService {
9944 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9945 name: "coll_b".into()
9946 })),
9947 source_name: Some("fuchsia.logger.Log".to_string()),
9948 target: Some(fdecl::Ref::Child(
9949 fdecl::ChildRef {
9950 name: "child_c".to_string(),
9951 collection: None,
9952 }
9953 )),
9954 target_name: Some("fuchsia.logger.Log".to_string()),
9955 source_instance_filter: Some(vec!["a_different_default".to_string()]),
9956 availability: Some(fdecl::Availability::Optional),
9957 ..Default::default()
9958 })
9959 ]);
9960 decl.collections = Some(vec![
9961 fdecl::Collection {
9962 name: Some("coll_a".to_string()),
9963 durability: Some(fdecl::Durability::Transient),
9964 ..Default::default()
9965 },
9966 fdecl::Collection {
9967 name: Some("coll_b".to_string()),
9968 durability: Some(fdecl::Durability::Transient),
9969 ..Default::default()
9970 },
9971 ]);
9972 decl.children = Some(vec![
9973 fdecl::Child {
9974 name: Some("child_c".to_string()),
9975 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9976 startup: Some(fdecl::StartupMode::Lazy),
9977 on_terminate: None,
9978 environment: None,
9979 ..Default::default()
9980 },
9981 ]);
9982 decl
9983 },
9984 result = Err(ErrorList::new(vec![
9985 Error::DifferentAvailabilityInAggregation(AvailabilityList(vec![
9986 fdecl::Availability::Required,
9987 fdecl::Availability::Optional,
9988 ]))
9989 ])),
9990 },
9991 }
9992
9993 test_validate_capabilities! {
9994 test_validate_capabilities_individually_ok => {
9995 input = vec![
9996 fdecl::Capability::Protocol(fdecl::Protocol {
9997 name: Some("foo_svc".into()),
9998 source_path: Some("/svc/foo".into()),
9999 ..Default::default()
10000 }),
10001 fdecl::Capability::Directory(fdecl::Directory {
10002 name: Some("foo_dir".into()),
10003 source_path: Some("/foo".into()),
10004 rights: Some(fio::Operations::CONNECT),
10005 ..Default::default()
10006 }),
10007 ],
10008 as_builtin = false,
10009 result = Ok(()),
10010 },
10011 test_validate_capabilities_individually_err => {
10012 input = vec![
10013 fdecl::Capability::Protocol(fdecl::Protocol {
10014 name: None,
10015 source_path: None,
10016 ..Default::default()
10017 }),
10018 fdecl::Capability::Directory(fdecl::Directory {
10019 name: None,
10020 source_path: None,
10021 rights: None,
10022 ..Default::default()
10023 }),
10024 ],
10025 as_builtin = false,
10026 result = Err(ErrorList::new(vec![
10027 Error::missing_field(DeclType::Protocol, "name"),
10028 Error::missing_field(DeclType::Protocol, "source_path"),
10029 Error::missing_field(DeclType::Directory, "name"),
10030 Error::missing_field(DeclType::Directory, "source_path"),
10031 Error::missing_field(DeclType::Directory, "rights"),
10032 ])),
10033 },
10034 test_validate_builtin_capabilities_individually_ok => {
10035 input = vec![
10036 fdecl::Capability::Protocol(fdecl::Protocol {
10037 name: Some("foo_protocol".into()),
10038 source_path: None,
10039 ..Default::default()
10040 }),
10041 fdecl::Capability::Directory(fdecl::Directory {
10042 name: Some("foo_dir".into()),
10043 source_path: None,
10044 rights: Some(fio::Operations::CONNECT),
10045 ..Default::default()
10046 }),
10047 fdecl::Capability::Service(fdecl::Service {
10048 name: Some("foo_svc".into()),
10049 source_path: None,
10050 ..Default::default()
10051 }),
10052 fdecl::Capability::Runner(fdecl::Runner {
10053 name: Some("foo_runner".into()),
10054 source_path: None,
10055 ..Default::default()
10056 }),
10057 fdecl::Capability::Resolver(fdecl::Resolver {
10058 name: Some("foo_resolver".into()),
10059 source_path: None,
10060 ..Default::default()
10061 }),
10062 ],
10063 as_builtin = true,
10064 result = Ok(()),
10065 },
10066 test_validate_builtin_capabilities_individually_err => {
10067 input = vec![
10068 fdecl::Capability::Protocol(fdecl::Protocol {
10069 name: None,
10070 source_path: Some("/svc/foo".into()),
10071 ..Default::default()
10072 }),
10073 fdecl::Capability::Directory(fdecl::Directory {
10074 name: None,
10075 source_path: Some("/foo".into()),
10076 rights: None,
10077 ..Default::default()
10078 }),
10079 fdecl::Capability::Service(fdecl::Service {
10080 name: None,
10081 source_path: Some("/svc/foo".into()),
10082 ..Default::default()
10083 }),
10084 fdecl::Capability::Runner(fdecl::Runner {
10085 name: None,
10086 source_path: Some("/foo".into()),
10087 ..Default::default()
10088 }),
10089 fdecl::Capability::Resolver(fdecl::Resolver {
10090 name: None,
10091 source_path: Some("/foo".into()),
10092 ..Default::default()
10093 }),
10094 fdecl::Capability::Storage(fdecl::Storage {
10095 name: None,
10096 ..Default::default()
10097 }),
10098 ],
10099 as_builtin = true,
10100 result = Err(ErrorList::new(vec![
10101 Error::missing_field(DeclType::Protocol, "name"),
10102 Error::extraneous_source_path(DeclType::Protocol, "/svc/foo"),
10103 Error::missing_field(DeclType::Directory, "name"),
10104 Error::extraneous_source_path(DeclType::Directory, "/foo"),
10105 Error::missing_field(DeclType::Directory, "rights"),
10106 Error::missing_field(DeclType::Service, "name"),
10107 Error::extraneous_source_path(DeclType::Service, "/svc/foo"),
10108 Error::missing_field(DeclType::Runner, "name"),
10109 Error::extraneous_source_path(DeclType::Runner, "/foo"),
10110 Error::missing_field(DeclType::Resolver, "name"),
10111 Error::extraneous_source_path(DeclType::Resolver, "/foo"),
10112 Error::CapabilityCannotBeBuiltin(DeclType::Storage),
10113 ])),
10114 },
10115 test_validate_delivery_type_ok => {
10116 input = vec![
10117 fdecl::Capability::Protocol(fdecl::Protocol {
10118 name: Some("foo_svc1".into()),
10119 source_path: Some("/svc/foo1".into()),
10120 ..Default::default()
10121 }),
10122 fdecl::Capability::Protocol(fdecl::Protocol {
10123 name: Some("foo_svc2".into()),
10124 source_path: Some("/svc/foo2".into()),
10125 delivery: Some(fdecl::DeliveryType::Immediate),
10126 ..Default::default()
10127 }),
10128 fdecl::Capability::Protocol(fdecl::Protocol {
10129 name: Some("foo_svc3".into()),
10130 source_path: Some("/svc/foo3".into()),
10131 delivery: Some(fdecl::DeliveryType::OnReadable),
10132 ..Default::default()
10133 }),
10134 ],
10135 as_builtin = false,
10136 result = Ok(()),
10137 },
10138 test_validate_delivery_type_err => {
10139 input = vec![
10140 fdecl::Capability::Protocol(fdecl::Protocol {
10141 name: Some("foo_svc".into()),
10142 source_path: Some("/svc/foo".into()),
10143 delivery: Some(fdecl::DeliveryType::unknown()),
10144 ..Default::default()
10145 }),
10146 ],
10147 as_builtin = false,
10148 result = Err(ErrorList::new(vec![
10149 Error::invalid_field(DeclType::Protocol, "delivery"),
10150 ])),
10151 },
10152 }
10153
10154 fn generate_expose_different_source_and_availability_decl(
10157 new_expose: impl Fn(fdecl::Ref, fdecl::Availability, &str) -> fdecl::Expose,
10158 ) -> fdecl::Component {
10159 let mut decl = new_component_decl();
10160 let child = "child";
10161 decl.children = Some(vec![fdecl::Child {
10162 name: Some(child.to_string()),
10163 url: Some("fuchsia-pkg://fuchsia.com/source#meta/source.cm".to_string()),
10164 startup: Some(fdecl::StartupMode::Lazy),
10165 ..Default::default()
10166 }]);
10167 decl.exposes = Some(vec![
10168 new_expose(
10170 fdecl::Ref::Self_(fdecl::SelfRef {}),
10171 fdecl::Availability::Optional,
10172 "fuchsia.examples.Echo1",
10173 ),
10174 new_expose(
10176 fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
10177 fdecl::Availability::Optional,
10178 "fuchsia.examples.Echo2",
10179 ),
10180 new_expose(
10182 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10183 fdecl::Availability::Optional,
10184 "fuchsia.examples.Echo3",
10185 ),
10186 new_expose(
10188 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10189 fdecl::Availability::Optional,
10190 "fuchsia.examples.Echo4",
10191 ),
10192 new_expose(
10194 fdecl::Ref::Self_(fdecl::SelfRef {}),
10195 fdecl::Availability::Transitional,
10196 "fuchsia.examples.Echo5",
10197 ),
10198 new_expose(
10200 fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
10201 fdecl::Availability::Transitional,
10202 "fuchsia.examples.Echo6",
10203 ),
10204 new_expose(
10206 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10207 fdecl::Availability::Transitional,
10208 "fuchsia.examples.Echo7",
10209 ),
10210 new_expose(
10212 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10213 fdecl::Availability::Transitional,
10214 "fuchsia.examples.Echo8",
10215 ),
10216 new_expose(
10218 fdecl::Ref::Self_(fdecl::SelfRef {}),
10219 fdecl::Availability::SameAsTarget,
10220 "fuchsia.examples.Echo9",
10221 ),
10222 new_expose(
10224 fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
10225 fdecl::Availability::SameAsTarget,
10226 "fuchsia.examples.Echo10",
10227 ),
10228 new_expose(
10230 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10231 fdecl::Availability::SameAsTarget,
10232 "fuchsia.examples.Echo11",
10233 ),
10234 new_expose(
10236 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10237 fdecl::Availability::Required,
10238 "fuchsia.examples.Echo12",
10239 ),
10240 new_expose(
10242 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10243 fdecl::Availability::SameAsTarget,
10244 "fuchsia.examples.Echo13",
10245 ),
10246 ]);
10247 decl
10248 }
10249
10250 fn generate_offer_different_source_and_availability_decl(
10253 new_offer: impl Fn(fdecl::Ref, fdecl::Availability, &str) -> fdecl::Offer,
10254 ) -> fdecl::Component {
10255 let mut decl = new_component_decl();
10256 decl.children = Some(vec![
10257 fdecl::Child {
10258 name: Some("source".to_string()),
10259 url: Some("fuchsia-pkg://fuchsia.com/source#meta/source.cm".to_string()),
10260 startup: Some(fdecl::StartupMode::Lazy),
10261 on_terminate: None,
10262 environment: None,
10263 ..Default::default()
10264 },
10265 fdecl::Child {
10266 name: Some("sink".to_string()),
10267 url: Some("fuchsia-pkg://fuchsia.com/sink#meta/sink.cm".to_string()),
10268 startup: Some(fdecl::StartupMode::Lazy),
10269 on_terminate: None,
10270 environment: None,
10271 ..Default::default()
10272 },
10273 ]);
10274 decl.offers = Some(vec![
10275 new_offer(
10278 fdecl::Ref::Parent(fdecl::ParentRef {}),
10279 fdecl::Availability::Required,
10280 "fuchsia.examples.Echo0",
10281 ),
10282 new_offer(
10283 fdecl::Ref::Parent(fdecl::ParentRef {}),
10284 fdecl::Availability::Optional,
10285 "fuchsia.examples.Echo1",
10286 ),
10287 new_offer(
10288 fdecl::Ref::Parent(fdecl::ParentRef {}),
10289 fdecl::Availability::SameAsTarget,
10290 "fuchsia.examples.Echo2",
10291 ),
10292 new_offer(
10293 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10294 fdecl::Availability::Optional,
10295 "fuchsia.examples.Echo3",
10296 ),
10297 new_offer(
10300 fdecl::Ref::Self_(fdecl::SelfRef {}),
10301 fdecl::Availability::Optional,
10302 "fuchsia.examples.Echo4",
10303 ),
10304 new_offer(
10305 fdecl::Ref::Self_(fdecl::SelfRef {}),
10306 fdecl::Availability::SameAsTarget,
10307 "fuchsia.examples.Echo5",
10308 ),
10309 new_offer(
10310 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10311 fdecl::Availability::Optional,
10312 "fuchsia.examples.Echo6",
10313 ),
10314 new_offer(
10315 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10316 fdecl::Availability::SameAsTarget,
10317 "fuchsia.examples.Echo7",
10318 ),
10319 new_offer(
10320 fdecl::Ref::Child(fdecl::ChildRef { name: "source".to_string(), collection: None }),
10321 fdecl::Availability::Optional,
10322 "fuchsia.examples.Echo8",
10323 ),
10324 new_offer(
10325 fdecl::Ref::Child(fdecl::ChildRef { name: "source".to_string(), collection: None }),
10326 fdecl::Availability::SameAsTarget,
10327 "fuchsia.examples.Echo9",
10328 ),
10329 new_offer(
10331 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10332 fdecl::Availability::Required,
10333 "fuchsia.examples.Echo10",
10334 ),
10335 new_offer(
10336 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10337 fdecl::Availability::SameAsTarget,
10338 "fuchsia.examples.Echo11",
10339 ),
10340 ]);
10341 decl
10342 }
10343
10344 #[test]
10345 fn test_validate_dynamic_offers_empty() {
10346 assert_eq!(
10347 validate_dynamic_offers(
10348 vec![],
10349 &mut DirectedGraph::new(),
10350 &vec![],
10351 &fdecl::Component::default()
10352 ),
10353 Ok(())
10354 );
10355 }
10356
10357 #[test]
10358 fn test_validate_dynamic_offers_okay() {
10359 assert_eq!(
10360 validate_dynamic_offers(
10361 vec![],
10362 &mut DirectedGraph::new(),
10363 &vec![
10364 fdecl::Offer::Protocol(fdecl::OfferProtocol {
10365 dependency_type: Some(fdecl::DependencyType::Strong),
10366 source: Some(fdecl::Ref::Self_(fdecl::SelfRef)),
10367 source_name: Some("thing".to_string()),
10368 target_name: Some("thing".repeat(26)),
10369 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10370 name: "foo".to_string(),
10371 collection: Some("foo".to_string()),
10372 })),
10373 ..Default::default()
10374 }),
10375 fdecl::Offer::Service(fdecl::OfferService {
10376 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10377 source_name: Some("thang".repeat(26)),
10378 target_name: Some("thang".repeat(26)),
10379 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10380 name: "foo".to_string(),
10381 collection: Some("foo".to_string()),
10382 })),
10383 ..Default::default()
10384 }),
10385 fdecl::Offer::Directory(fdecl::OfferDirectory {
10386 dependency_type: Some(fdecl::DependencyType::Strong),
10387 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10388 source_name: Some("thung1".repeat(26)),
10389 target_name: Some("thung1".repeat(26)),
10390 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10391 name: "foo".to_string(),
10392 collection: Some("foo".to_string()),
10393 })),
10394 ..Default::default()
10395 }),
10396 fdecl::Offer::Storage(fdecl::OfferStorage {
10397 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10398 source_name: Some("thung2".repeat(26)),
10399 target_name: Some("thung2".repeat(26)),
10400 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10401 name: "foo".to_string(),
10402 collection: Some("foo".to_string()),
10403 })),
10404 ..Default::default()
10405 }),
10406 fdecl::Offer::Runner(fdecl::OfferRunner {
10407 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10408 source_name: Some("thung3".repeat(26)),
10409 target_name: Some("thung3".repeat(26)),
10410 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10411 name: "foo".to_string(),
10412 collection: Some("foo".to_string()),
10413 })),
10414 ..Default::default()
10415 }),
10416 fdecl::Offer::Resolver(fdecl::OfferResolver {
10417 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10418 source_name: Some("thung4".repeat(26)),
10419 target_name: Some("thung4".repeat(26)),
10420 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10421 name: "foo".to_string(),
10422 collection: Some("foo".to_string()),
10423 })),
10424 ..Default::default()
10425 }),
10426 ],
10427 &fdecl::Component {
10428 capabilities: Some(vec![fdecl::Capability::Protocol(fdecl::Protocol {
10429 name: Some("thing".to_string()),
10430 source_path: Some("/svc/foo".into()),
10431 ..Default::default()
10432 }),]),
10433 ..Default::default()
10434 }
10435 ),
10436 Ok(())
10437 );
10438 }
10439
10440 #[test]
10441 fn test_validate_dynamic_offers_valid_service_aggregation() {
10442 assert_eq!(
10443 validate_dynamic_offers(
10444 vec![],
10445 &mut DirectedGraph::new(),
10446 &vec![
10447 fdecl::Offer::Service(fdecl::OfferService {
10448 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10449 name: "child_a".to_string(),
10450 collection: None
10451 })),
10452 source_name: Some("fuchsia.logger.Log".to_string()),
10453 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10454 name: "child_c".to_string(),
10455 collection: None,
10456 })),
10457 target_name: Some("fuchsia.logger.Log".to_string()),
10458 source_instance_filter: Some(vec!["default".to_string()]),
10459 ..Default::default()
10460 }),
10461 fdecl::Offer::Service(fdecl::OfferService {
10462 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10463 name: "child_b".to_string(),
10464 collection: None
10465 })),
10466 source_name: Some("fuchsia.logger.Log".to_string()),
10467 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10468 name: "child_c".to_string(),
10469 collection: None,
10470 })),
10471 target_name: Some("fuchsia.logger.Log".to_string()),
10472 source_instance_filter: Some(vec!["a_different_default".to_string()]),
10473 ..Default::default()
10474 })
10475 ],
10476 &fdecl::Component {
10477 children: Some(vec![
10478 fdecl::Child {
10479 name: Some("child_a".to_string()),
10480 url: Some(
10481 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10482 .to_string()
10483 ),
10484 startup: Some(fdecl::StartupMode::Lazy),
10485 on_terminate: None,
10486 environment: None,
10487 ..Default::default()
10488 },
10489 fdecl::Child {
10490 name: Some("child_b".to_string()),
10491 url: Some(
10492 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10493 .to_string()
10494 ),
10495 startup: Some(fdecl::StartupMode::Lazy),
10496 on_terminate: None,
10497 environment: None,
10498 ..Default::default()
10499 },
10500 fdecl::Child {
10501 name: Some("child_c".to_string()),
10502 url: Some(
10503 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10504 .to_string()
10505 ),
10506 startup: Some(fdecl::StartupMode::Lazy),
10507 on_terminate: None,
10508 environment: None,
10509 ..Default::default()
10510 },
10511 ]),
10512 ..Default::default()
10513 }
10514 ),
10515 Ok(())
10516 );
10517 }
10518
10519 #[test]
10520 fn test_validate_dynamic_service_aggregation_missing_filter() {
10521 assert_eq!(
10522 validate_dynamic_offers(
10523 vec![],
10524 &mut DirectedGraph::new(),
10525 &vec![
10526 fdecl::Offer::Service(fdecl::OfferService {
10527 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10528 name: "child_a".to_string(),
10529 collection: None
10530 })),
10531 source_name: Some("fuchsia.logger.Log".to_string()),
10532 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10533 name: "child_c".to_string(),
10534 collection: None,
10535 })),
10536 target_name: Some("fuchsia.logger.Log".to_string()),
10537 source_instance_filter: Some(vec!["default".to_string()]),
10538 ..Default::default()
10539 }),
10540 fdecl::Offer::Service(fdecl::OfferService {
10541 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10542 name: "child_b".to_string(),
10543 collection: None
10544 })),
10545 source_name: Some("fuchsia.logger.Log".to_string()),
10546 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10547 name: "child_c".to_string(),
10548 collection: None,
10549 })),
10550 target_name: Some("fuchsia.logger.Log".to_string()),
10551 source_instance_filter: None,
10552 ..Default::default()
10553 }),
10554 ],
10555 &fdecl::Component {
10556 children: Some(vec![
10557 fdecl::Child {
10558 name: Some("child_a".to_string()),
10559 url: Some(
10560 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10561 .to_string()
10562 ),
10563 startup: Some(fdecl::StartupMode::Lazy),
10564 ..Default::default()
10565 },
10566 fdecl::Child {
10567 name: Some("child_b".to_string()),
10568 url: Some(
10569 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10570 .to_string()
10571 ),
10572 startup: Some(fdecl::StartupMode::Lazy),
10573 ..Default::default()
10574 },
10575 fdecl::Child {
10576 name: Some("child_c".to_string()),
10577 url: Some(
10578 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10579 .to_string()
10580 ),
10581 startup: Some(fdecl::StartupMode::Lazy),
10582 ..Default::default()
10583 },
10584 ]),
10585 ..Default::default()
10586 },
10587 ),
10588 Err(ErrorList::new(vec![Error::invalid_aggregate_offer(
10589 "source_instance_filter must be set for dynamic aggregate service offers"
10590 ),]))
10591 );
10592 }
10593
10594 #[test]
10595 fn test_validate_dynamic_offers_omit_target() {
10596 assert_eq!(
10597 validate_dynamic_offers(
10598 vec![],
10599 &mut DirectedGraph::new(),
10600 &vec![
10601 fdecl::Offer::Protocol(fdecl::OfferProtocol {
10602 dependency_type: Some(fdecl::DependencyType::Strong),
10603 source: Some(fdecl::Ref::Self_(fdecl::SelfRef)),
10604 source_name: Some("thing".to_string()),
10605 target_name: Some("thing".to_string()),
10606 ..Default::default()
10607 }),
10608 fdecl::Offer::Service(fdecl::OfferService {
10609 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10610 source_name: Some("thang".to_string()),
10611 target_name: Some("thang".to_string()),
10612 ..Default::default()
10613 }),
10614 fdecl::Offer::Directory(fdecl::OfferDirectory {
10615 dependency_type: Some(fdecl::DependencyType::Strong),
10616 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10617 source_name: Some("thung1".to_string()),
10618 target_name: Some("thung1".to_string()),
10619 ..Default::default()
10620 }),
10621 fdecl::Offer::Storage(fdecl::OfferStorage {
10622 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10623 source_name: Some("thung2".to_string()),
10624 target_name: Some("thung2".to_string()),
10625 ..Default::default()
10626 }),
10627 fdecl::Offer::Runner(fdecl::OfferRunner {
10628 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10629 source_name: Some("thung3".to_string()),
10630 target_name: Some("thung3".to_string()),
10631 ..Default::default()
10632 }),
10633 fdecl::Offer::Resolver(fdecl::OfferResolver {
10634 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10635 source_name: Some("thung4".to_string()),
10636 target_name: Some("thung4".to_string()),
10637 ..Default::default()
10638 }),
10639 ],
10640 &fdecl::Component {
10641 capabilities: Some(vec![fdecl::Capability::Protocol(fdecl::Protocol {
10642 name: Some("thing".to_string()),
10643 source_path: Some("/svc/foo".into()),
10644 ..Default::default()
10645 }),]),
10646 ..Default::default()
10647 }
10648 ),
10649 Err(ErrorList::new(vec![
10650 Error::missing_field(DeclType::OfferProtocol, "target"),
10651 Error::missing_field(DeclType::OfferService, "target"),
10652 Error::missing_field(DeclType::OfferDirectory, "target"),
10653 Error::missing_field(DeclType::OfferStorage, "target"),
10654 Error::missing_field(DeclType::OfferRunner, "target"),
10655 Error::missing_field(DeclType::OfferResolver, "target"),
10656 ]))
10657 );
10658 }
10659
10660 #[test]
10661 fn test_validate_dynamic_offers_collection_collision() {
10662 assert_eq!(
10663 validate_dynamic_offers(
10664 vec![],
10665 &mut DirectedGraph::new(),
10666 &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10667 dependency_type: Some(fdecl::DependencyType::Strong),
10668 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10669 source_name: Some("thing".to_string()),
10670 target_name: Some("thing".to_string()),
10671 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10672 name: "child".to_string(),
10673 collection: Some("coll".to_string()),
10674 })),
10675 ..Default::default()
10676 }),],
10677 &fdecl::Component {
10678 offers: Some(vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10679 dependency_type: Some(fdecl::DependencyType::Strong),
10680 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10681 source_name: Some("thing".to_string()),
10682 target_name: Some("thing".to_string()),
10683 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10684 name: "coll".into()
10685 })),
10686 ..Default::default()
10687 }),]),
10688 collections: Some(vec![fdecl::Collection {
10689 name: Some("coll".to_string()),
10690 durability: Some(fdecl::Durability::Transient),
10691 ..Default::default()
10692 },]),
10693 ..Default::default()
10694 }
10695 ),
10696 Err(ErrorList::new(vec![Error::duplicate_field(
10697 DeclType::OfferProtocol,
10698 "target_name",
10699 "thing"
10700 ),]))
10701 );
10702 }
10703
10704 #[test]
10705 fn test_validate_dynamic_offers_cycle_collection_to_static_child() {
10706 assert_eq!(
10707 validate_dynamic_offers(
10708 vec![("dyn", "coll")],
10709 &mut DirectedGraph::new(),
10710 &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10711 source_name: Some("bar".to_string()),
10712 target_name: Some("bar".to_string()),
10713 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10714 name: "static_child".into(),
10715 collection: None,
10716 })),
10717 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10718 name: "dyn".to_string(),
10719 collection: Some("coll".to_string()),
10720 })),
10721 dependency_type: Some(fdecl::DependencyType::Strong),
10722 ..Default::default()
10723 }),],
10724 &fdecl::Component {
10725 offers: Some(vec![fdecl::Offer::Service(fdecl::OfferService {
10726 source_name: Some("foo".to_string()),
10727 target_name: Some("foo".to_string()),
10728 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10729 name: "coll".into(),
10730 })),
10731 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10732 name: "static_child".into(),
10733 collection: None,
10734 })),
10735 ..Default::default()
10736 })]),
10737 children: Some(vec![fdecl::Child {
10738 name: Some("static_child".into()),
10739 url: Some("url#child.cm".into()),
10740 startup: Some(fdecl::StartupMode::Lazy),
10741 ..Default::default()
10742 }]),
10743 collections: Some(vec![fdecl::Collection {
10744 name: Some("coll".into()),
10745 durability: Some(fdecl::Durability::Transient),
10746 ..Default::default()
10747 }]),
10748 ..Default::default()
10749 }
10750 ),
10751 Err(ErrorList::new(vec![Error::dependency_cycle(
10752 "{{child coll:dyn -> collection coll -> child static_child -> child coll:dyn}}"
10753 )]))
10754 );
10755 }
10756
10757 #[test]
10758 fn test_validate_dynamic_offers_cycle_collection_to_dynamic_child() {
10759 assert_eq!(
10760 validate_dynamic_offers(
10761 vec![("dyn", "coll1"), ("dyn", "coll2")],
10762 &mut DirectedGraph::new(),
10763 &vec![
10764 fdecl::Offer::Service(fdecl::OfferService {
10765 source_name: Some("foo".to_string()),
10766 target_name: Some("foo".to_string()),
10767 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10768 name: "coll2".into(),
10769 })),
10770 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10771 name: "dyn".into(),
10772 collection: Some("coll1".into()),
10773 })),
10774 ..Default::default()
10775 }),
10776 fdecl::Offer::Protocol(fdecl::OfferProtocol {
10777 source_name: Some("bar".to_string()),
10778 target_name: Some("bar".to_string()),
10779 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10780 name: "dyn".into(),
10781 collection: Some("coll1".into()),
10782 })),
10783 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10784 name: "dyn".to_string(),
10785 collection: Some("coll2".to_string()),
10786 })),
10787 dependency_type: Some(fdecl::DependencyType::Strong),
10788 ..Default::default()
10789 }),
10790 ],
10791 &fdecl::Component {
10792 collections: Some(vec![
10793 fdecl::Collection {
10794 name: Some("coll1".into()),
10795 durability: Some(fdecl::Durability::Transient),
10796 ..Default::default()
10797 },
10798 fdecl::Collection {
10799 name: Some("coll2".into()),
10800 durability: Some(fdecl::Durability::Transient),
10801 ..Default::default()
10802 },
10803 ]),
10804 ..Default::default()
10805 }
10806 ),
10807 Err(ErrorList::new(vec![Error::dependency_cycle(
10808 "{{child coll1:dyn -> child coll2:dyn -> collection coll2 -> child coll1:dyn}}"
10809 )]))
10810 );
10811 }
10812
10813 #[test]
10814 fn test_validate_dynamic_offers_cycle_collection_to_dynamic_child_between_new_and_existing() {
10815 assert_eq!(
10816 validate_dynamic_offers(
10817 vec![("dyn", "coll1"), ("dyn", "coll2")],
10818 &mut DirectedGraph::from([(
10819 DependencyNode::Child("dyn".into(), Some("coll1".into())),
10820 DependencyNode::Child("dyn".into(), Some("coll2".into())),
10821 )]),
10822 &vec![fdecl::Offer::Service(fdecl::OfferService {
10823 source_name: Some("foo".to_string()),
10824 target_name: Some("foo".to_string()),
10825 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10826 name: "coll2".into(),
10827 })),
10828 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10829 name: "dyn".into(),
10830 collection: Some("coll1".into()),
10831 })),
10832 ..Default::default()
10833 }),],
10834 &fdecl::Component {
10835 collections: Some(vec![
10836 fdecl::Collection {
10837 name: Some("coll1".into()),
10838 durability: Some(fdecl::Durability::Transient),
10839 ..Default::default()
10840 },
10841 fdecl::Collection {
10842 name: Some("coll2".into()),
10843 durability: Some(fdecl::Durability::Transient),
10844 ..Default::default()
10845 },
10846 ]),
10847 ..Default::default()
10848 }
10849 ),
10850 Err(ErrorList::new(vec![Error::dependency_cycle(
10851 "{{child coll1:dyn -> child coll2:dyn -> collection coll2 -> child coll1:dyn}}"
10852 )]))
10853 );
10854 }
10855
10856 #[test]
10857 fn test_validate_dynamic_offers_cycle_collection_to_collection() {
10858 assert_eq!(
10859 validate_dynamic_offers(
10860 vec![("dyn", "coll1"), ("dyn", "coll2")],
10861 &mut DirectedGraph::new(),
10862 &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10863 source_name: Some("bar".to_string()),
10864 target_name: Some("bar".to_string()),
10865 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10866 name: "dyn".into(),
10867 collection: Some("coll2".parse().unwrap()),
10868 })),
10869 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10870 name: "dyn".into(),
10871 collection: Some("coll1".parse().unwrap()),
10872 })),
10873 dependency_type: Some(fdecl::DependencyType::Strong),
10874 ..Default::default()
10875 }),],
10876 &fdecl::Component {
10877 offers: Some(vec![fdecl::Offer::Service(fdecl::OfferService {
10878 source_name: Some("foo".to_string()),
10879 target_name: Some("foo".to_string()),
10880 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10881 name: "coll1".into(),
10882 })),
10883 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10884 name: "coll2".into(),
10885 })),
10886 ..Default::default()
10887 })]),
10888 collections: Some(vec![
10889 fdecl::Collection {
10890 name: Some("coll1".into()),
10891 durability: Some(fdecl::Durability::Transient),
10892 ..Default::default()
10893 },
10894 fdecl::Collection {
10895 name: Some("coll2".into()),
10896 durability: Some(fdecl::Durability::Transient),
10897 ..Default::default()
10898 },
10899 ]),
10900 ..Default::default()
10901 }
10902 ),
10903 Err(ErrorList::new(vec![Error::dependency_cycle(
10904 "{{child coll1:dyn -> collection coll1 -> child coll2:dyn -> child coll1:dyn}}",
10905 )]))
10906 );
10907 }
10908
10909 #[test]
10910 fn test_validate_dynamic_child() {
10911 assert_eq!(
10912 Ok(()),
10913 validate_dynamic_child(&fdecl::Child {
10914 name: Some("a".repeat(MAX_LONG_NAME_LENGTH).to_string()),
10915 url: Some("test:///child".to_string()),
10916 startup: Some(fdecl::StartupMode::Lazy),
10917 on_terminate: None,
10918 environment: None,
10919 ..Default::default()
10920 })
10921 );
10922 }
10923
10924 #[test]
10925 fn test_validate_dynamic_child_environment_is_invalid() {
10926 assert_eq!(
10927 validate_dynamic_child(&fdecl::Child {
10928 name: Some("a".repeat(MAX_LONG_NAME_LENGTH).to_string()),
10929 url: Some("test:///child".to_string()),
10930 startup: Some(fdecl::StartupMode::Lazy),
10931 on_terminate: None,
10932 environment: Some("env".to_string()),
10933 ..Default::default()
10934 }),
10935 Err(ErrorList::new(vec![Error::DynamicChildWithEnvironment]))
10936 );
10937 }
10938
10939 #[test]
10940 fn test_validate_dynamic_offers_missing_stuff() {
10941 assert_eq!(
10942 validate_dynamic_offers(
10943 vec![],
10944 &mut DirectedGraph::new(),
10945 &vec![
10946 fdecl::Offer::Protocol(fdecl::OfferProtocol::default()),
10947 fdecl::Offer::Service(fdecl::OfferService::default()),
10948 fdecl::Offer::Directory(fdecl::OfferDirectory::default()),
10949 fdecl::Offer::Storage(fdecl::OfferStorage::default()),
10950 fdecl::Offer::Runner(fdecl::OfferRunner::default()),
10951 fdecl::Offer::Resolver(fdecl::OfferResolver::default()),
10952 ],
10953 &fdecl::Component::default()
10954 ),
10955 Err(ErrorList::new(vec![
10956 Error::missing_field(DeclType::OfferProtocol, "source"),
10957 Error::missing_field(DeclType::OfferProtocol, "source_name"),
10958 Error::missing_field(DeclType::OfferProtocol, "target"),
10959 Error::missing_field(DeclType::OfferProtocol, "target_name"),
10960 Error::missing_field(DeclType::OfferProtocol, "dependency_type"),
10961 Error::missing_field(DeclType::OfferService, "source"),
10962 Error::missing_field(DeclType::OfferService, "source_name"),
10963 Error::missing_field(DeclType::OfferService, "target"),
10964 Error::missing_field(DeclType::OfferService, "target_name"),
10965 Error::missing_field(DeclType::OfferDirectory, "source"),
10966 Error::missing_field(DeclType::OfferDirectory, "source_name"),
10967 Error::missing_field(DeclType::OfferDirectory, "target"),
10968 Error::missing_field(DeclType::OfferDirectory, "target_name"),
10969 Error::missing_field(DeclType::OfferDirectory, "dependency_type"),
10970 Error::missing_field(DeclType::OfferStorage, "source"),
10971 Error::missing_field(DeclType::OfferStorage, "source_name"),
10972 Error::missing_field(DeclType::OfferStorage, "target"),
10973 Error::missing_field(DeclType::OfferStorage, "target_name"),
10974 Error::missing_field(DeclType::OfferRunner, "source"),
10975 Error::missing_field(DeclType::OfferRunner, "source_name"),
10976 Error::missing_field(DeclType::OfferRunner, "target"),
10977 Error::missing_field(DeclType::OfferRunner, "target_name"),
10978 Error::missing_field(DeclType::OfferResolver, "source"),
10979 Error::missing_field(DeclType::OfferResolver, "source_name"),
10980 Error::missing_field(DeclType::OfferResolver, "target"),
10981 Error::missing_field(DeclType::OfferResolver, "target_name"),
10982 ]))
10983 );
10984 }
10985
10986 test_dependency! {
10987 (test_validate_offers_protocol_dependency_cycle) => {
10988 ty = fdecl::Offer::Protocol,
10989 offer_decl = fdecl::OfferProtocol {
10990 source: None, target: None, source_name: Some(format!("thing")),
10993 target_name: Some(format!("thing")),
10994 dependency_type: Some(fdecl::DependencyType::Strong),
10995 ..Default::default()
10996 },
10997 },
10998 (test_validate_offers_directory_dependency_cycle) => {
10999 ty = fdecl::Offer::Directory,
11000 offer_decl = fdecl::OfferDirectory {
11001 source: None, target: None, source_name: Some(format!("thing")),
11004 target_name: Some(format!("thing")),
11005 rights: Some(fio::Operations::CONNECT),
11006 subdir: None,
11007 dependency_type: Some(fdecl::DependencyType::Strong),
11008 ..Default::default()
11009 },
11010 },
11011 (test_validate_offers_service_dependency_cycle) => {
11012 ty = fdecl::Offer::Service,
11013 offer_decl = fdecl::OfferService {
11014 source: None, target: None, source_name: Some(format!("thing")),
11017 target_name: Some(format!("thing")),
11018 ..Default::default()
11019 },
11020 },
11021 (test_validate_offers_runner_dependency_cycle) => {
11022 ty = fdecl::Offer::Runner,
11023 offer_decl = fdecl::OfferRunner {
11024 source: None, target: None, source_name: Some(format!("thing")),
11027 target_name: Some(format!("thing")),
11028 ..Default::default()
11029 },
11030 },
11031 (test_validate_offers_resolver_dependency_cycle) => {
11032 ty = fdecl::Offer::Resolver,
11033 offer_decl = fdecl::OfferResolver {
11034 source: None, target: None, source_name: Some(format!("thing")),
11037 target_name: Some(format!("thing")),
11038 ..Default::default()
11039 },
11040 },
11041 }
11042 test_weak_dependency! {
11043 (test_validate_offers_protocol_weak_dependency_cycle) => {
11044 ty = fdecl::Offer::Protocol,
11045 offer_decl = fdecl::OfferProtocol {
11046 source: None, target: None, source_name: Some(format!("thing")),
11049 target_name: Some(format!("thing")),
11050 dependency_type: None, ..Default::default()
11052 },
11053 },
11054 (test_validate_offers_directory_weak_dependency_cycle) => {
11055 ty = fdecl::Offer::Directory,
11056 offer_decl = fdecl::OfferDirectory {
11057 source: None, target: None, source_name: Some(format!("thing")),
11060 target_name: Some(format!("thing")),
11061 rights: Some(fio::Operations::CONNECT),
11062 subdir: None,
11063 dependency_type: None, ..Default::default()
11065 },
11066 },
11067 (test_validate_offers_service_weak_dependency_cycle) => {
11068 ty = fdecl::Offer::Service,
11069 offer_decl = fdecl::OfferService {
11070 source: None, target: None, source_name: Some(format!("thing")),
11073 target_name: Some(format!("thing")),
11074 dependency_type: None, ..Default::default()
11076 },
11077 },
11078 }
11079}