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(decl: &fdecl::Component) -> Result<(), ErrorList> {
144 let ctx = ValidationContext::default();
145 ctx.validate(decl, &HashSet::new(), &vec![]).map_err(|errs| ErrorList::new(errs))
146}
147
148fn validate_capabilities(
150 capabilities: &[fdecl::Capability],
151 as_builtin: bool,
152) -> Result<(), ErrorList> {
153 let mut ctx = ValidationContext::default();
154
155 ctx.load_dictionary_names(capabilities.iter().filter_map(|capability| match capability {
156 fdecl::Capability::Dictionary(dictionary_decl) => Some(dictionary_decl),
157 _ => None,
158 }));
159
160 ctx.validate_capability_decls(capabilities, as_builtin);
161 if ctx.errors.is_empty() { Ok(()) } else { Err(ErrorList::new(ctx.errors)) }
162}
163
164pub fn validate_builtin_capabilities(
166 capabilities: &Vec<fdecl::Capability>,
167) -> Result<(), ErrorList> {
168 validate_capabilities(capabilities, true)
169}
170
171pub fn validate_namespace_capabilities(
173 capabilities: &Vec<fdecl::Capability>,
174) -> Result<(), ErrorList> {
175 validate_capabilities(capabilities, false)
176}
177
178type CheckChildNameFn = fn(Option<&String>, DeclType, &str, &mut Vec<Error>) -> bool;
181
182pub fn validate_dynamic_child(child: &fdecl::Child) -> Result<(), ErrorList> {
183 let mut errors = vec![];
184
185 if let Err(mut error_list) = validate_child(child, check_dynamic_name) {
186 errors.append(&mut error_list.errs);
187 }
188
189 if child.environment.is_some() {
190 errors.push(Error::DynamicChildWithEnvironment);
191 }
192
193 if errors.is_empty() { Ok(()) } else { Err(ErrorList { errs: errors }) }
194}
195
196fn validate_child(
199 child: &fdecl::Child,
200 check_child_name: CheckChildNameFn,
201) -> Result<(), ErrorList> {
202 let mut errors = vec![];
203 check_child_name(child.name.as_ref(), DeclType::Child, "name", &mut errors);
204 check_url(child.url.as_ref(), DeclType::Child, "url", &mut errors);
205 if child.startup.is_none() {
206 errors.push(Error::missing_field(DeclType::Child, "startup"));
207 }
208 if child.environment.is_some() {
210 check_name(child.environment.as_ref(), DeclType::Child, "environment", &mut errors);
211 }
212 if errors.is_empty() { Ok(()) } else { Err(ErrorList { errs: errors }) }
213}
214
215pub fn validate_dynamic_offers<'a>(
224 dynamic_children: Vec<(&'a str, &'a str)>,
225 existing_dynamic_offers: &HashSet<(DependencyNode, DependencyNode)>,
226 new_dynamic_offers: &'a Vec<fdecl::Offer>,
227 decl: &'a fdecl::Component,
228) -> Result<(), ErrorList> {
229 let mut ctx = ValidationContext::default();
230 ctx.dynamic_children = dynamic_children;
231 ctx.validate(decl, existing_dynamic_offers, new_dynamic_offers)
232 .map_err(|errs| ErrorList::new(errs))
233}
234
235fn check_offer_name(
236 prop: Option<&String>,
237 decl: DeclType,
238 keyword: &str,
239 offer_type: OfferType,
240 errors: &mut Vec<Error>,
241) -> bool {
242 if offer_type == OfferType::Dynamic {
243 check_dynamic_name(prop, decl, keyword, errors)
244 } else {
245 check_name(prop, decl, keyword, errors)
246 }
247}
248
249#[derive(Default)]
250struct ValidationContext<'a> {
251 all_children: HashMap<&'a str, &'a fdecl::Child>,
252 all_collections: HashSet<&'a str>,
253 all_capability_ids: HashSet<&'a str>,
254 all_storages: HashMap<&'a str, Option<&'a fdecl::Ref>>,
255 all_services: HashSet<&'a str>,
256 all_protocols: HashSet<&'a str>,
257 all_directories: HashSet<&'a str>,
258 all_runners: HashSet<&'a str>,
259 all_resolvers: HashSet<&'a str>,
260 all_dictionaries: HashMap<&'a str, &'a fdecl::Dictionary>,
261
262 #[cfg(fuchsia_api_level_at_least = "HEAD")]
263 all_configs: HashSet<&'a str>,
264
265 all_environment_names: HashSet<&'a str>,
266 dynamic_children: Vec<(&'a str, &'a str)>,
267 strong_dependencies: DirectedGraph<DependencyNode>,
268 target_ids: IdMap<'a>,
269 errors: Vec<Error>,
270}
271
272trait Container {
276 fn contains(&self, key: &str) -> bool;
277}
278
279impl<'a> Container for HashSet<&'a str> {
280 fn contains(&self, key: &str) -> bool {
281 self.contains(key)
282 }
283}
284
285impl<'a, T> Container for HashMap<&'a str, T> {
286 fn contains(&self, key: &str) -> bool {
287 self.contains_key(key)
288 }
289}
290
291impl<'a> ValidationContext<'a> {
292 fn validate(
293 mut self,
294 decl: &'a fdecl::Component,
295 existing_dynamic_offers: &HashSet<(DependencyNode, DependencyNode)>,
296 new_dynamic_offers: &'a Vec<fdecl::Offer>,
297 ) -> Result<(), Vec<Error>> {
298 if let Some(envs) = &decl.environments {
300 self.collect_environment_names(&envs);
301 }
302
303 if let Some(children) = decl.children.as_ref() {
305 for child in children {
306 self.validate_child_decl(&child);
307 }
308 }
309
310 if let Some(collections) = decl.collections.as_ref() {
312 for collection in collections {
313 self.validate_collection_decl(&collection);
314 }
315 }
316
317 if let Some(capabilities) = decl.capabilities.as_ref() {
319 self.load_dictionary_names(capabilities.iter().filter_map(
320 |capability| match capability {
321 fdecl::Capability::Dictionary(dictionary_decl) => Some(dictionary_decl),
322 _ => None,
323 },
324 ));
325 self.validate_capability_decls(capabilities, false);
326 }
327
328 let mut use_runner_name = None;
330 let mut use_runner_source = None;
331 if let Some(uses) = decl.uses.as_ref() {
332 (use_runner_name, use_runner_source) = self.validate_use_decls(uses);
333 }
334
335 if let Some(program) = decl.program.as_ref() {
337 self.validate_program(program, use_runner_name, use_runner_source);
338 }
339
340 if let Some(exposes) = decl.exposes.as_ref() {
342 let mut expose_to_parent_ids = HashMap::new();
343 let mut expose_to_framework_ids = HashMap::new();
344 for expose in exposes.iter() {
345 self.validate_expose_decl(
346 &expose,
347 &mut expose_to_parent_ids,
348 &mut expose_to_framework_ids,
349 );
350 }
351 self.validate_expose_group(&exposes);
352 }
353
354 if let Some(offers) = decl.offers.as_ref() {
356 for offer in offers.iter() {
357 self.validate_offer_decl(&offer, OfferType::Static);
358 }
359 self.validate_offer_group(&offers, OfferType::Static);
360 }
361
362 for dynamic_offer in new_dynamic_offers {
363 self.validate_offer_decl(dynamic_offer, OfferType::Dynamic);
364 cm_graph::add_dependencies_from_offer(
365 &mut self.strong_dependencies,
366 dynamic_offer,
367 &self.dynamic_children,
368 );
369 }
370 self.validate_offer_group(new_dynamic_offers, OfferType::Dynamic);
371
372 if let Some(environment) = decl.environments.as_ref() {
374 for environment in environment {
375 self.validate_environment_decl(&environment);
376 }
377 }
378
379 self.validate_config(decl.config.as_ref(), decl.uses.as_ref());
381
382 cm_graph::generate_dependency_graph(
384 &mut self.strong_dependencies,
385 &decl,
386 &self.dynamic_children,
387 existing_dynamic_offers.clone(),
388 );
389 if let Err(e) = self.strong_dependencies.topological_sort() {
390 self.errors.push(Error::dependency_cycle(e.format_cycle()));
391 }
392
393 if self.errors.is_empty() { Ok(()) } else { Err(self.errors) }
394 }
395
396 fn collect_environment_names(&mut self, envs: &'a [fdecl::Environment]) {
398 for env in envs {
399 if let Some(name) = env.name.as_ref() {
400 if !self.all_environment_names.insert(name) {
401 self.errors.push(Error::duplicate_field(DeclType::Environment, "name", name));
402 }
403 }
404 }
405 }
406
407 fn validate_config(
410 &mut self,
411 config: Option<&fdecl::ConfigSchema>,
412 uses: Option<&Vec<fdecl::Use>>,
413 ) {
414 use std::collections::BTreeMap;
415
416 let optional_use_keys: BTreeMap<String, fdecl::ConfigType> =
418 uses.map_or(BTreeMap::new(), |u| {
419 u.iter()
420 .map(|u| {
421 let fdecl::Use::Config(config) = u else {
422 return None;
423 };
424 if config.availability == Some(fdecl::Availability::Required)
425 || config.availability == None
426 {
427 return None;
428 }
429 if let Some(_) = config.default.as_ref() {
430 return None;
431 }
432 let Some(key) = config.target_name.clone() else {
433 return None;
434 };
435 let Some(value) = config.type_.clone() else {
436 return None;
437 };
438 Some((key, value))
439 })
440 .flatten()
441 .collect()
442 });
443
444 for u in uses.iter().flat_map(|x| x.iter()) {
446 let fdecl::Use::Config(config) = u else { continue };
447 let Some(default) = config.default.as_ref() else { continue };
448 validate_value_spec(&fdecl::ConfigValueSpec {
449 value: Some(default.clone()),
450 ..Default::default()
451 })
452 .map_err(|mut e| self.errors.append(&mut e.errs))
453 .ok();
454 }
455
456 let Some(config) = config else {
457 if !optional_use_keys.is_empty() {
458 self.errors.push(Error::missing_field(DeclType::ConfigField, "config"))
459 }
460 return;
461 };
462
463 if let Some(fields) = &config.fields {
464 for field in fields {
465 if field.key.is_none() {
466 self.errors.push(Error::missing_field(DeclType::ConfigField, "key"));
467 }
468 if let Some(type_) = &field.type_ {
469 self.validate_config_type(type_, true);
470 } else {
471 self.errors.push(Error::missing_field(DeclType::ConfigField, "value_type"));
472 }
473 }
474 } else {
475 self.errors.push(Error::missing_field(DeclType::ConfigSchema, "fields"));
476 }
477
478 if let Some(checksum) = &config.checksum {
479 match checksum {
480 fdecl::ConfigChecksum::Sha256(_) => {}
481 fdecl::ConfigChecksumUnknown!() => {
482 self.errors.push(Error::invalid_field(DeclType::ConfigSchema, "checksum"));
483 }
484 }
485 } else {
486 self.errors.push(Error::missing_field(DeclType::ConfigSchema, "checksum"));
487 }
488
489 'outer: for (key, value) in optional_use_keys.iter() {
490 for field in config.fields.iter().flatten() {
491 if field.key.as_ref() == Some(key) {
492 if field.type_.as_ref() != Some(value) {
493 self.errors.push(Error::invalid_field(DeclType::ConfigField, key.clone()));
494 }
495 continue 'outer;
496 }
497 }
498 self.errors.push(Error::missing_field(DeclType::ConfigField, key.clone()));
499 }
500
501 match config.value_source {
502 None => self.errors.push(Error::missing_field(DeclType::ConfigSchema, "value_source")),
503 #[cfg(fuchsia_api_level_at_least = "HEAD")]
504 Some(fdecl::ConfigValueSource::Capabilities(_)) => {
505 if !optional_use_keys.is_empty() {
506 self.errors
507 .push(Error::invalid_field(DeclType::ConfigValueSource, "ValueSource"))
508 }
509 }
510 Some(fdecl::ConfigValueSource::__SourceBreaking { .. }) => {
511 self.errors.push(Error::invalid_field(DeclType::ConfigValueSource, "ValueSource"))
512 }
513 _ => (),
514 };
515 }
516
517 fn validate_config_type(&mut self, type_: &fdecl::ConfigType, accept_vectors: bool) {
518 match &type_.layout {
519 fdecl::ConfigTypeLayout::Bool
520 | fdecl::ConfigTypeLayout::Uint8
521 | fdecl::ConfigTypeLayout::Uint16
522 | fdecl::ConfigTypeLayout::Uint32
523 | fdecl::ConfigTypeLayout::Uint64
524 | fdecl::ConfigTypeLayout::Int8
525 | fdecl::ConfigTypeLayout::Int16
526 | fdecl::ConfigTypeLayout::Int32
527 | fdecl::ConfigTypeLayout::Int64 => {
528 if let Some(parameters) = &type_.parameters {
530 if !parameters.is_empty() {
531 self.errors
532 .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
533 }
534 } else {
535 self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"));
536 }
537
538 if !type_.constraints.is_empty() {
539 self.errors.push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
540 }
541 }
542 fdecl::ConfigTypeLayout::String => {
543 if let Some(parameters) = &type_.parameters {
545 if !parameters.is_empty() {
546 self.errors
547 .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
548 }
549 } else {
550 self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"));
551 }
552
553 if type_.constraints.is_empty() {
554 self.errors.push(Error::missing_field(DeclType::ConfigType, "constraints"));
555 } else if type_.constraints.len() > 1 {
556 self.errors.push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
557 } else if let fdecl::LayoutConstraint::MaxSize(_) = &type_.constraints[0] {
558 } else {
559 self.errors.push(Error::invalid_field(DeclType::ConfigType, "constraints"));
560 }
561 }
562 fdecl::ConfigTypeLayout::Vector => {
563 if accept_vectors {
564 if let Some(parameters) = &type_.parameters {
566 if parameters.is_empty() {
567 self.errors
568 .push(Error::missing_field(DeclType::ConfigType, "parameters"));
569 } else if parameters.len() > 1 {
570 self.errors
571 .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
572 } else if let fdecl::LayoutParameter::NestedType(nested_type) =
573 ¶meters[0]
574 {
575 self.validate_config_type(nested_type, false);
576 } else {
577 self.errors
578 .push(Error::invalid_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
588 .push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
589 } else if let fdecl::LayoutConstraint::MaxSize(_) = &type_.constraints[0] {
590 } else {
591 self.errors.push(Error::invalid_field(DeclType::ConfigType, "constraints"));
592 }
593 } else {
594 self.errors.push(Error::nested_vector());
595 }
596 }
597 _ => self.errors.push(Error::invalid_field(DeclType::ConfigType, "layout")),
598 }
599 }
600
601 fn validate_capability_decls(
602 &mut self,
603 capabilities: &'a [fdecl::Capability],
604 as_builtin: bool,
605 ) {
606 for capability in capabilities {
607 self.validate_capability_decl(capability, as_builtin);
608 }
609 }
610
611 fn validate_capability_decl(&mut self, capability: &'a fdecl::Capability, as_builtin: bool) {
616 match capability {
617 fdecl::Capability::Service(service) => self.validate_service_decl(&service, as_builtin),
618 fdecl::Capability::Protocol(protocol) => {
619 self.validate_protocol_decl(&protocol, as_builtin)
620 }
621 fdecl::Capability::Directory(directory) => {
622 self.validate_directory_decl(&directory, as_builtin)
623 }
624 fdecl::Capability::Storage(storage) => {
625 if as_builtin {
626 self.errors.push(Error::CapabilityCannotBeBuiltin(DeclType::Storage))
627 } else {
628 self.validate_storage_decl(&storage)
629 }
630 }
631 fdecl::Capability::Runner(runner) => self.validate_runner_decl(&runner, as_builtin),
632 fdecl::Capability::Resolver(resolver) => {
633 self.validate_resolver_decl(&resolver, as_builtin)
634 }
635 fdecl::Capability::EventStream(event) => {
636 if as_builtin {
637 self.validate_event_stream_decl(&event)
638 } else {
639 self.errors.push(Error::CapabilityMustBeBuiltin(DeclType::EventStream))
640 }
641 }
642 fdecl::Capability::Dictionary(dictionary) => {
643 self.validate_dictionary_decl(&dictionary);
644 }
645 #[cfg(fuchsia_api_level_at_least = "HEAD")]
646 fdecl::Capability::Config(config) => {
647 self.validate_configuration_decl(&config);
648 }
649 fdecl::CapabilityUnknown!() => self.errors.push(Error::UnknownCapability),
650 }
651 }
652
653 fn validate_use_decls(
655 &mut self,
656 uses: &'a [fdecl::Use],
657 ) -> (Option<&'a String>, Option<&'a fdecl::Ref>) {
658 for use_ in uses.iter() {
660 self.validate_use_decl(&use_);
661 }
662 self.validate_use_paths(&uses);
663
664 #[cfg(fuchsia_api_level_at_least = "HEAD")]
665 {
666 let mut use_runner_name = None;
667 let mut use_runner_source = None;
668 for use_ in uses.iter() {
669 if let fdecl::Use::Runner(use_runner) = use_ {
670 if use_runner_name.is_some() {
671 self.errors.push(Error::MultipleRunnersUsed);
672 }
673
674 use_runner_name = use_runner.source_name.as_ref();
675 use_runner_source = use_runner.source.as_ref();
676 }
677 }
678 return (use_runner_name, use_runner_source);
679 }
680 #[cfg(fuchsia_api_level_less_than = "HEAD")]
681 return (None, None);
682 }
683
684 fn validate_use_decl(&mut self, use_: &'a fdecl::Use) {
685 match use_ {
686 fdecl::Use::Service(u) => {
687 let decl = DeclType::UseService;
688 self.validate_use_fields(
689 decl,
690 Self::service_checker,
691 u.source.as_ref(),
692 u.source_name.as_ref(),
693 u.source_dictionary.as_ref(),
694 u.target_path.as_ref(),
695 u.dependency_type.as_ref(),
696 u.availability.as_ref(),
697 );
698 if u.dependency_type.is_none() {
699 self.errors.push(Error::missing_field(decl, "dependency_type"));
700 }
701 }
702 fdecl::Use::Protocol(u) => {
703 let decl = DeclType::UseProtocol;
704 self.validate_use_fields(
705 decl,
706 Self::protocol_checker,
707 u.source.as_ref(),
708 u.source_name.as_ref(),
709 u.source_dictionary.as_ref(),
710 u.target_path.as_ref(),
711 u.dependency_type.as_ref(),
712 u.availability.as_ref(),
713 );
714 if u.dependency_type.is_none() {
715 self.errors.push(Error::missing_field(decl, "dependency_type"));
716 }
717 }
718 fdecl::Use::Directory(u) => {
719 let decl = DeclType::UseDirectory;
720 self.validate_use_fields(
721 decl,
722 Self::directory_checker,
723 u.source.as_ref(),
724 u.source_name.as_ref(),
725 u.source_dictionary.as_ref(),
726 u.target_path.as_ref(),
727 u.dependency_type.as_ref(),
728 u.availability.as_ref(),
729 );
730 if u.dependency_type.is_none() {
731 self.errors.push(Error::missing_field(decl, "dependency_type"));
732 }
733 if u.rights.is_none() {
734 self.errors.push(Error::missing_field(DeclType::UseDirectory, "rights"));
735 }
736 if let Some(subdir) = u.subdir.as_ref() {
737 check_relative_path(
738 Some(subdir),
739 DeclType::UseDirectory,
740 "subdir",
741 &mut self.errors,
742 );
743 }
744 }
745 fdecl::Use::Storage(u) => {
746 const SOURCE: Option<fdecl::Ref> = Some(fdecl::Ref::Parent(fdecl::ParentRef {}));
747 const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
748 Some(fdecl::DependencyType::Strong);
749 self.validate_use_fields(
750 DeclType::UseStorage,
751 Self::storage_checker,
752 SOURCE.as_ref(),
753 u.source_name.as_ref(),
754 None,
755 u.target_path.as_ref(),
756 DEPENDENCY_TYPE.as_ref(),
757 u.availability.as_ref(),
758 );
759 }
760 fdecl::Use::EventStream(u) => {
761 const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
762 Some(fdecl::DependencyType::Strong);
763 let decl = DeclType::UseEventStream;
764 self.validate_use_fields(
765 decl,
766 Self::event_stream_checker,
767 u.source.as_ref(),
768 u.source_name.as_ref(),
769 None,
770 u.target_path.as_ref(),
771 DEPENDENCY_TYPE.as_ref(),
772 u.availability.as_ref(),
773 );
774 match u.source {
776 Some(fdecl::Ref::Child(_)) | Some(fdecl::Ref::Parent(_)) => {
777 }
779 Some(fdecl::Ref::Framework(_))
780 | Some(fdecl::Ref::Self_(_))
781 | Some(fdecl::Ref::Debug(_)) => {
782 self.errors.push(Error::invalid_field(decl, "source"));
784 }
785 Some(fdecl::Ref::Collection(_)) | Some(fdecl::RefUnknown!()) | None => {
786 }
788 }
789 if let Some(scope) = &u.scope {
790 for reference in scope {
791 if !matches!(reference, fdecl::Ref::Child(_) | fdecl::Ref::Collection(_)) {
792 self.errors.push(Error::invalid_field(decl, "scope"));
793 }
794 }
795 }
796 }
797 #[cfg(fuchsia_api_level_at_least = "HEAD")]
798 fdecl::Use::Runner(u) => {
799 const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
800 Some(fdecl::DependencyType::Strong);
801 const AVAILABILITY: Option<fdecl::Availability> =
802 Some(fdecl::Availability::Required);
803 let decl = DeclType::UseRunner;
804 self.validate_use_fields(
805 decl,
806 Self::runner_checker,
807 u.source.as_ref(),
808 u.source_name.as_ref(),
809 u.source_dictionary.as_ref(),
810 None,
811 DEPENDENCY_TYPE.as_ref(),
812 AVAILABILITY.as_ref(),
813 );
814 }
815 #[cfg(fuchsia_api_level_at_least = "HEAD")]
816 fdecl::Use::Config(u) => {
817 const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
818 Some(fdecl::DependencyType::Strong);
819 let decl = DeclType::UseConfiguration;
820 self.validate_use_fields(
821 decl,
822 Self::config_checker,
823 u.source.as_ref(),
824 u.source_name.as_ref(),
825 None,
826 None,
827 DEPENDENCY_TYPE.as_ref(),
828 u.availability.as_ref(),
829 );
830 }
831 fdecl::UseUnknown!() => {
832 self.errors.push(Error::invalid_field(DeclType::Component, "use"));
833 }
834 }
835 }
836
837 fn validate_program(
840 &mut self,
841 program: &fdecl::Program,
842 use_runner_name: Option<&String>,
843 _use_runner_source: Option<&fdecl::Ref>,
844 ) {
845 match &program.runner {
846 Some(_) =>
847 {
848 #[cfg(fuchsia_api_level_at_least = "HEAD")]
849 if use_runner_name.is_some() {
850 if use_runner_name != program.runner.as_ref()
851 || _use_runner_source
852 != Some(&fdecl::Ref::Environment(fdecl::EnvironmentRef))
853 {
854 self.errors.push(Error::ConflictingRunners);
855 }
856 }
857 }
858 None => {
859 if use_runner_name.is_none() {
860 self.errors.push(Error::MissingRunner);
861 }
862 }
863 }
864
865 if program.info.is_none() {
866 self.errors.push(Error::missing_field(DeclType::Program, "info"));
867 }
868 }
869
870 fn validate_use_paths(&mut self, uses: &[fdecl::Use]) {
873 #[derive(Debug, PartialEq, Clone, Copy)]
874 struct PathCapability<'a> {
875 decl: DeclType,
876 dir: &'a Path,
877 use_: &'a fdecl::Use,
878 }
879 let mut used_paths = HashMap::new();
880 for use_ in uses.iter() {
881 match use_ {
882 fdecl::Use::Service(fdecl::UseService { target_path: Some(path), .. })
883 | fdecl::Use::Protocol(fdecl::UseProtocol { target_path: Some(path), .. })
884 | fdecl::Use::Directory(fdecl::UseDirectory { target_path: Some(path), .. })
885 | fdecl::Use::Storage(fdecl::UseStorage { target_path: Some(path), .. }) => {
886 let capability = match use_ {
887 fdecl::Use::Service(_) => {
888 let dir = match Path::new(path).parent() {
889 Some(p) => p,
890 None => continue, };
892 PathCapability { decl: DeclType::UseService, dir, use_ }
893 }
894 fdecl::Use::Protocol(_) => {
895 let dir = match Path::new(path).parent() {
896 Some(p) => p,
897 None => continue, };
899 PathCapability { decl: DeclType::UseProtocol, dir, use_ }
900 }
901 fdecl::Use::Directory(_) => PathCapability {
902 decl: DeclType::UseDirectory,
903 dir: Path::new(path),
904 use_,
905 },
906 fdecl::Use::Storage(_) => PathCapability {
907 decl: DeclType::UseStorage,
908 dir: Path::new(path),
909 use_,
910 },
911 _ => unreachable!(),
912 };
913 if used_paths.insert(path, capability).is_some() {
914 self.errors.push(Error::duplicate_field(
916 capability.decl,
917 "target_path",
918 path,
919 ));
920 }
921 }
922 _ => {}
923 }
924 }
925 for ((&path_a, capability_a), (&path_b, capability_b)) in
926 used_paths.iter().tuple_combinations()
927 {
928 if match (capability_a.use_, capability_b.use_) {
929 (fdecl::Use::Directory(_), fdecl::Use::Directory(_))
931 | (fdecl::Use::Storage(_), fdecl::Use::Directory(_))
932 | (fdecl::Use::Directory(_), fdecl::Use::Storage(_))
933 | (fdecl::Use::Storage(_), fdecl::Use::Storage(_)) => {
934 capability_b.dir == capability_a.dir
935 || capability_b.dir.starts_with(capability_a.dir)
936 || capability_a.dir.starts_with(capability_b.dir)
937 }
938
939 (_, fdecl::Use::Directory(_)) | (fdecl::Use::Directory(_), _) => {
941 capability_b.dir == capability_a.dir
942 || capability_b.dir.starts_with(capability_a.dir)
943 || capability_a.dir.starts_with(capability_b.dir)
944 }
945
946 (_, _) => {
949 capability_b.dir != capability_a.dir
950 && (capability_b.dir.starts_with(capability_a.dir)
951 || capability_a.dir.starts_with(capability_b.dir))
952 }
953 } {
954 self.errors.push(Error::invalid_path_overlap(
955 capability_a.decl,
956 path_a,
957 capability_b.decl,
958 path_b,
959 ));
960 }
961 }
962 for (used_path, capability) in used_paths.iter() {
963 if used_path.as_str() == "/pkg" || used_path.starts_with("/pkg/") {
964 self.errors.push(Error::pkg_path_overlap(capability.decl, *used_path));
965 }
966 }
967 }
968
969 fn validate_use_fields(
970 &mut self,
971 decl: DeclType,
972 capability_checker: impl Fn(&Self) -> &dyn Container,
976 source: Option<&'a fdecl::Ref>,
977 source_name: Option<&'a String>,
978 source_dictionary: Option<&'a String>,
979 target_path: Option<&'a String>,
980 dependency_type: Option<&fdecl::DependencyType>,
981 availability: Option<&'a fdecl::Availability>,
982 ) {
983 self.validate_use_source(decl, source, source_dictionary);
984
985 check_name(source_name, decl, "source_name", &mut self.errors);
986 if source_dictionary.is_some() {
987 check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
988 }
989 if decl != DeclType::UseRunner && decl != DeclType::UseConfiguration {
990 check_path(target_path, decl, "target_path", &mut self.errors);
991 }
992 check_use_availability(decl, availability, &mut self.errors);
993
994 let is_use_from_child = match source {
996 Some(fdecl::Ref::Child(_)) => true,
997 _ => false,
998 };
999 match (is_use_from_child, dependency_type) {
1000 (false, Some(fdecl::DependencyType::Weak)) => {
1001 self.errors.push(Error::invalid_field(decl, "dependency_type"));
1002 }
1003 _ => {}
1004 }
1005
1006 self.validate_route_from_self(
1007 decl,
1008 source,
1009 source_name,
1010 source_dictionary,
1011 capability_checker,
1012 );
1013 }
1014
1015 fn validate_use_source(
1016 &mut self,
1017 decl: DeclType,
1018 source: Option<&'a fdecl::Ref>,
1019 source_dictionary: Option<&'a String>,
1020 ) {
1021 match (source, source_dictionary) {
1022 (Some(fdecl::Ref::Parent(_)), _) => {}
1024 (Some(fdecl::Ref::Self_(_)), _) => {}
1025 (Some(fdecl::Ref::Child(child)), _) => {
1026 self.validate_child_ref(decl, "source", &child, OfferType::Static);
1027 return;
1028 }
1029 (Some(fdecl::Ref::Framework(_)), None) => {}
1031 (Some(fdecl::Ref::Debug(_)), None) => {}
1032 (Some(fdecl::Ref::Capability(c)), None) => {
1033 self.validate_source_capability(&c, decl, "source");
1034 return;
1035 }
1036 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1037 (Some(fdecl::Ref::Environment(_)), None) => {}
1038 (Some(fdecl::Ref::Collection(collection)), None) if decl == DeclType::UseService => {
1039 self.validate_collection_ref(decl, "source", &collection);
1040 return;
1041 }
1042 (None, _) => self.errors.push(Error::missing_field(decl, "source")),
1044 (_, _) => self.errors.push(Error::invalid_field(decl, "source")),
1046 }
1047 }
1048
1049 fn validate_child_decl(&mut self, child: &'a fdecl::Child) {
1050 if let Err(mut e) = validate_child(child, check_name) {
1051 self.errors.append(&mut e.errs);
1052 }
1053 if let Some(name) = child.name.as_ref() {
1054 let name: &str = name;
1055 if self.all_children.insert(name, child).is_some() {
1056 self.errors.push(Error::duplicate_field(DeclType::Child, "name", name));
1057 }
1058 }
1059 if let Some(environment) = child.environment.as_ref() {
1060 if !self.all_environment_names.contains(environment.as_str()) {
1061 self.errors.push(Error::invalid_environment(
1062 DeclType::Child,
1063 "environment",
1064 environment,
1065 ));
1066 }
1067 }
1068 }
1069
1070 fn validate_collection_decl(&mut self, collection: &'a fdecl::Collection) {
1071 let name = collection.name.as_ref();
1072 if check_name(name, DeclType::Collection, "name", &mut self.errors) {
1073 let name: &str = name.unwrap();
1074 if !self.all_collections.insert(name) {
1075 self.errors.push(Error::duplicate_field(DeclType::Collection, "name", name));
1076 }
1077 }
1078 if collection.durability.is_none() {
1079 self.errors.push(Error::missing_field(DeclType::Collection, "durability"));
1080 }
1081 if let Some(environment) = collection.environment.as_ref() {
1082 if !self.all_environment_names.contains(environment.as_str()) {
1083 self.errors.push(Error::invalid_environment(
1084 DeclType::Collection,
1085 "environment",
1086 environment,
1087 ));
1088 }
1089 }
1090 }
1092
1093 fn validate_environment_decl(&mut self, environment: &'a fdecl::Environment) {
1094 let name = environment.name.as_ref();
1095 check_name(name, DeclType::Environment, "name", &mut self.errors);
1096 if environment.extends.is_none() {
1097 self.errors.push(Error::missing_field(DeclType::Environment, "extends"));
1098 }
1099 if let Some(runners) = environment.runners.as_ref() {
1100 let mut registered_runners = HashSet::new();
1101 for runner in runners {
1102 self.validate_runner_registration(runner, &mut registered_runners);
1103 }
1104 }
1105 if let Some(resolvers) = environment.resolvers.as_ref() {
1106 let mut registered_schemes = HashSet::new();
1107 for resolver in resolvers {
1108 self.validate_resolver_registration(resolver, &mut registered_schemes);
1109 }
1110 }
1111
1112 match environment.extends.as_ref() {
1113 Some(fdecl::EnvironmentExtends::None) => {
1114 if environment.stop_timeout_ms.is_none() {
1115 self.errors
1116 .push(Error::missing_field(DeclType::Environment, "stop_timeout_ms"));
1117 }
1118 }
1119 None | Some(fdecl::EnvironmentExtends::Realm) => {}
1120 }
1121
1122 if let Some(debugs) = environment.debug_capabilities.as_ref() {
1123 for debug in debugs {
1124 self.validate_environment_debug_registration(debug);
1125 }
1126 }
1127 }
1128
1129 fn validate_runner_registration(
1130 &mut self,
1131 runner_registration: &'a fdecl::RunnerRegistration,
1132 runner_names: &mut HashSet<&'a str>,
1133 ) {
1134 check_name(
1135 runner_registration.source_name.as_ref(),
1136 DeclType::RunnerRegistration,
1137 "source_name",
1138 &mut self.errors,
1139 );
1140 self.validate_registration_source(
1141 runner_registration.source.as_ref(),
1142 DeclType::RunnerRegistration,
1143 );
1144 if let (Some(fdecl::Ref::Self_(_)), Some(name)) =
1146 (&runner_registration.source, runner_registration.source_name.as_ref())
1147 {
1148 if !self.all_runners.contains(name as &str) {
1149 self.errors.push(Error::invalid_runner(
1150 DeclType::RunnerRegistration,
1151 "source_name",
1152 name,
1153 ));
1154 }
1155 }
1156
1157 check_name(
1158 runner_registration.target_name.as_ref(),
1159 DeclType::RunnerRegistration,
1160 "target_name",
1161 &mut self.errors,
1162 );
1163 if let Some(name) = runner_registration.target_name.as_ref() {
1164 if !runner_names.insert(name.as_str()) {
1165 self.errors.push(Error::duplicate_field(
1166 DeclType::RunnerRegistration,
1167 "target_name",
1168 name,
1169 ));
1170 }
1171 }
1172 }
1173
1174 fn validate_resolver_registration(
1175 &mut self,
1176 resolver_registration: &'a fdecl::ResolverRegistration,
1177 schemes: &mut HashSet<&'a str>,
1178 ) {
1179 check_name(
1180 resolver_registration.resolver.as_ref(),
1181 DeclType::ResolverRegistration,
1182 "resolver",
1183 &mut self.errors,
1184 );
1185 self.validate_registration_source(
1186 resolver_registration.source.as_ref(),
1187 DeclType::ResolverRegistration,
1188 );
1189 check_url_scheme(
1190 resolver_registration.scheme.as_ref(),
1191 DeclType::ResolverRegistration,
1192 "scheme",
1193 &mut self.errors,
1194 );
1195 if let Some(scheme) = resolver_registration.scheme.as_ref() {
1196 if !schemes.insert(scheme.as_str()) {
1197 self.errors.push(Error::duplicate_field(
1198 DeclType::ResolverRegistration,
1199 "scheme",
1200 scheme,
1201 ));
1202 }
1203 }
1204 }
1205
1206 fn validate_registration_source(&mut self, source: Option<&'a fdecl::Ref>, ty: DeclType) {
1207 match source {
1208 Some(fdecl::Ref::Parent(_)) => {}
1209 Some(fdecl::Ref::Self_(_)) => {}
1210 Some(fdecl::Ref::Child(child_ref)) => {
1211 self.validate_child_ref(ty, "source", &child_ref, OfferType::Static);
1213 }
1214 Some(_) => {
1215 self.errors.push(Error::invalid_field(ty, "source"));
1216 }
1217 None => {
1218 self.errors.push(Error::missing_field(ty, "source"));
1219 }
1220 }
1221 }
1222
1223 fn validate_service_decl(&mut self, service: &'a fdecl::Service, as_builtin: bool) {
1224 if check_name(service.name.as_ref(), DeclType::Service, "name", &mut self.errors) {
1225 let name = service.name.as_ref().unwrap();
1226 if !self.all_capability_ids.insert(name) {
1227 self.errors.push(Error::duplicate_field(DeclType::Service, "name", name.as_str()));
1228 }
1229 self.all_services.insert(name);
1230 }
1231 match as_builtin {
1232 true => {
1233 if let Some(path) = service.source_path.as_ref() {
1234 self.errors.push(Error::extraneous_source_path(DeclType::Service, path))
1235 }
1236 }
1237 false => {
1238 check_path(
1239 service.source_path.as_ref(),
1240 DeclType::Service,
1241 "source_path",
1242 &mut self.errors,
1243 );
1244 }
1245 }
1246 }
1247
1248 fn validate_protocol_decl(&mut self, protocol: &'a fdecl::Protocol, as_builtin: bool) {
1249 if check_name(protocol.name.as_ref(), DeclType::Protocol, "name", &mut self.errors) {
1250 let name = protocol.name.as_ref().unwrap();
1251 if !self.all_capability_ids.insert(name) {
1252 self.errors.push(Error::duplicate_field(DeclType::Protocol, "name", name.as_str()));
1253 }
1254 self.all_protocols.insert(name);
1255 }
1256 match as_builtin {
1257 true => {
1258 if let Some(path) = protocol.source_path.as_ref() {
1259 self.errors.push(Error::extraneous_source_path(DeclType::Protocol, path))
1260 }
1261 }
1262 false => {
1263 check_path(
1264 protocol.source_path.as_ref(),
1265 DeclType::Protocol,
1266 "source_path",
1267 &mut self.errors,
1268 );
1269 }
1270 }
1271
1272 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1273 match protocol.delivery {
1274 Some(delivery) => match cm_types::DeliveryType::try_from(delivery) {
1275 Ok(_) => {}
1276 Err(_) => self.errors.push(Error::invalid_field(DeclType::Protocol, "delivery")),
1277 },
1278 None => {}
1279 }
1280 }
1281
1282 fn validate_directory_decl(&mut self, directory: &'a fdecl::Directory, as_builtin: bool) {
1283 if check_name(directory.name.as_ref(), DeclType::Directory, "name", &mut self.errors) {
1284 let name = directory.name.as_ref().unwrap();
1285 if !self.all_capability_ids.insert(name) {
1286 self.errors.push(Error::duplicate_field(
1287 DeclType::Directory,
1288 "name",
1289 name.as_str(),
1290 ));
1291 }
1292 self.all_directories.insert(name);
1293 }
1294 match as_builtin {
1295 true => {
1296 if let Some(path) = directory.source_path.as_ref() {
1297 self.errors.push(Error::extraneous_source_path(DeclType::Directory, path))
1298 }
1299 }
1300 false => {
1301 check_path(
1302 directory.source_path.as_ref(),
1303 DeclType::Directory,
1304 "source_path",
1305 &mut self.errors,
1306 );
1307 }
1308 }
1309 if directory.rights.is_none() {
1310 self.errors.push(Error::missing_field(DeclType::Directory, "rights"));
1311 }
1312 }
1313
1314 fn validate_storage_decl(&mut self, storage: &'a fdecl::Storage) {
1315 match storage.source.as_ref() {
1316 Some(fdecl::Ref::Parent(_)) => {}
1317 Some(fdecl::Ref::Self_(_)) => {}
1318 Some(fdecl::Ref::Child(child)) => {
1319 let _ =
1320 self.validate_child_ref(DeclType::Storage, "source", &child, OfferType::Static);
1321 }
1322 Some(_) => {
1323 self.errors.push(Error::invalid_field(DeclType::Storage, "source"));
1324 }
1325 None => {
1326 self.errors.push(Error::missing_field(DeclType::Storage, "source"));
1327 }
1328 };
1329 if check_name(storage.name.as_ref(), DeclType::Storage, "name", &mut self.errors) {
1330 let name = storage.name.as_ref().unwrap();
1331 if !self.all_capability_ids.insert(name) {
1332 self.errors.push(Error::duplicate_field(DeclType::Storage, "name", name.as_str()));
1333 }
1334 self.all_storages.insert(name, storage.source.as_ref());
1335 }
1336 if storage.storage_id.is_none() {
1337 self.errors.push(Error::missing_field(DeclType::Storage, "storage_id"));
1338 }
1339 check_name(
1340 storage.backing_dir.as_ref(),
1341 DeclType::Storage,
1342 "backing_dir",
1343 &mut self.errors,
1344 );
1345 }
1346
1347 fn validate_runner_decl(&mut self, runner: &'a fdecl::Runner, as_builtin: bool) {
1348 if check_name(runner.name.as_ref(), DeclType::Runner, "name", &mut self.errors) {
1349 let name = runner.name.as_ref().unwrap();
1350 if !self.all_capability_ids.insert(name) {
1351 self.errors.push(Error::duplicate_field(DeclType::Runner, "name", name.as_str()));
1352 }
1353 self.all_runners.insert(name);
1354 }
1355 match as_builtin {
1356 true => {
1357 if let Some(path) = runner.source_path.as_ref() {
1358 self.errors.push(Error::extraneous_source_path(DeclType::Runner, path))
1359 }
1360 }
1361 false => {
1362 check_path(
1363 runner.source_path.as_ref(),
1364 DeclType::Runner,
1365 "source_path",
1366 &mut self.errors,
1367 );
1368 }
1369 }
1370 }
1371
1372 fn validate_resolver_decl(&mut self, resolver: &'a fdecl::Resolver, as_builtin: bool) {
1373 if check_name(resolver.name.as_ref(), DeclType::Resolver, "name", &mut self.errors) {
1374 let name = resolver.name.as_ref().unwrap();
1375 if !self.all_capability_ids.insert(name) {
1376 self.errors.push(Error::duplicate_field(DeclType::Resolver, "name", name.as_str()));
1377 }
1378 self.all_resolvers.insert(name);
1379 }
1380 match as_builtin {
1381 true => {
1382 if let Some(path) = resolver.source_path.as_ref() {
1383 self.errors.push(Error::extraneous_source_path(DeclType::Resolver, path))
1384 }
1385 }
1386 false => {
1387 check_path(
1388 resolver.source_path.as_ref(),
1389 DeclType::Resolver,
1390 "source_path",
1391 &mut self.errors,
1392 );
1393 }
1394 }
1395 }
1396
1397 fn load_dictionary_names(&mut self, dictionaries: impl Iterator<Item = &'a fdecl::Dictionary>) {
1401 for dictionary in dictionaries {
1402 let decl = DeclType::Dictionary;
1403 if check_name(dictionary.name.as_ref(), decl, "name", &mut self.errors) {
1404 let name = dictionary.name.as_ref().unwrap();
1405 if !self.all_capability_ids.insert(name) {
1406 self.errors.push(Error::duplicate_field(decl, "name", name.as_str()));
1407 }
1408 self.all_dictionaries.insert(name, &dictionary);
1409 }
1410 }
1411 }
1412
1413 fn validate_dictionary_decl(&mut self, dictionary: &'a fdecl::Dictionary) {
1414 let decl = DeclType::Dictionary;
1415 if let Some(path) = dictionary.source_path.as_ref() {
1416 if dictionary.source.is_some() {
1417 self.errors.push(Error::extraneous_field(decl, "source"));
1418 }
1419 check_path(Some(path), DeclType::Dictionary, "source_path", &mut self.errors);
1420 }
1421 }
1422
1423 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1424 fn validate_configuration_decl(&mut self, config: &'a fdecl::Configuration) {
1425 let decl = DeclType::Configuration;
1426 if check_name(config.name.as_ref(), decl, "name", &mut self.errors) {
1427 let name = config.name.as_ref().unwrap();
1428 if !self.all_capability_ids.insert(name) {
1429 self.errors.push(Error::duplicate_field(decl, "name", name.as_str()));
1430 }
1431 self.all_configs.insert(name);
1432 }
1433 }
1434
1435 fn validate_environment_debug_registration(&mut self, debug: &'a fdecl::DebugRegistration) {
1436 match debug {
1437 fdecl::DebugRegistration::Protocol(o) => {
1438 let decl = DeclType::DebugProtocolRegistration;
1439 self.validate_environment_debug_fields(
1440 decl,
1441 o.source.as_ref(),
1442 o.source_name.as_ref(),
1443 o.target_name.as_ref(),
1444 );
1445
1446 if let (Some(fdecl::Ref::Self_(_)), Some(name)) =
1447 (&o.source, o.source_name.as_ref())
1448 {
1449 if !self.all_protocols.contains(&name as &str) {
1450 self.errors.push(Error::invalid_field(decl, "source"));
1451 }
1452 }
1453 }
1454 _ => {
1455 self.errors.push(Error::invalid_field(DeclType::Environment, "debug"));
1456 }
1457 }
1458 }
1459
1460 fn validate_environment_debug_fields(
1461 &mut self,
1462 decl: DeclType,
1463 source: Option<&fdecl::Ref>,
1464 source_name: Option<&String>,
1465 target_name: Option<&'a String>,
1466 ) {
1467 match source {
1469 Some(fdecl::Ref::Parent(_)) => {}
1470 Some(fdecl::Ref::Self_(_)) => {}
1471 Some(fdecl::Ref::Framework(_)) => {}
1472 Some(fdecl::Ref::Child(child)) => {
1473 let _ = self.validate_child_ref(decl, "source", &child, OfferType::Static);
1474 }
1475 Some(_) => self.errors.push(Error::invalid_field(decl, "source")),
1476 None => self.errors.push(Error::missing_field(decl, "source")),
1477 }
1478 check_name(source_name, decl, "source_name", &mut self.errors);
1479 check_name(target_name, decl, "target_name", &mut self.errors);
1480 }
1481
1482 fn validate_event_stream_decl(&mut self, event: &'a fdecl::EventStream) {
1483 if check_name(event.name.as_ref(), DeclType::EventStream, "name", &mut self.errors) {
1484 let name = event.name.as_ref().unwrap();
1485 if !self.all_capability_ids.insert(name) {
1486 self.errors.push(Error::duplicate_field(
1487 DeclType::EventStream,
1488 "name",
1489 name.as_str(),
1490 ));
1491 }
1492 }
1493 }
1494
1495 fn validate_source_collection(
1496 &mut self,
1497 collection: &fdecl::CollectionRef,
1498 decl_type: DeclType,
1499 ) -> bool {
1500 let num_errors = self.errors.len();
1501 if check_name(Some(&collection.name), decl_type, "source.collection.name", &mut self.errors)
1502 && !self.all_collections.contains(&collection.name as &str)
1503 {
1504 self.errors.push(Error::invalid_collection(
1505 decl_type,
1506 "source",
1507 &collection.name as &str,
1508 ));
1509 }
1510 num_errors == self.errors.len()
1511 }
1512
1513 fn validate_filtered_service_fields(
1514 &mut self,
1515 decl_type: DeclType,
1516 source_instance_filter: Option<&Vec<String>>,
1517 renamed_instances: Option<&Vec<fdecl::NameMapping>>,
1518 ) {
1519 if let Some(source_instance_filter) = source_instance_filter {
1520 if source_instance_filter.is_empty() {
1521 self.errors.push(Error::invalid_field(decl_type, "source_instance_filter"));
1524 }
1525 for name in source_instance_filter {
1526 check_name(Some(name), decl_type, "source_instance_filter", &mut self.errors);
1527 }
1528 }
1529 if let Some(renamed_instances) = renamed_instances {
1530 let mut seen_target_names = HashSet::<String>::new();
1532 for mapping in renamed_instances {
1533 check_name(
1534 Some(&mapping.source_name),
1535 decl_type,
1536 "renamed_instances.source_name",
1537 &mut self.errors,
1538 );
1539 check_name(
1540 Some(&mapping.target_name),
1541 decl_type,
1542 "renamed_instances.target_name",
1543 &mut self.errors,
1544 );
1545 if !seen_target_names.insert(mapping.target_name.clone()) {
1546 self.errors.push(Error::invalid_field(decl_type, "renamed_instances"));
1547 break;
1548 }
1549 }
1550 }
1551 }
1552
1553 fn validate_source_capability(
1554 &mut self,
1555 capability: &fdecl::CapabilityRef,
1556 decl_type: DeclType,
1557 field: &str,
1558 ) -> bool {
1559 let num_errors = self.errors.len();
1560 if check_name(Some(&capability.name), decl_type, "source.capability.name", &mut self.errors)
1561 && !self.all_capability_ids.contains(capability.name.as_str())
1562 {
1563 self.errors.push(Error::invalid_capability(decl_type, field, &capability.name));
1564 }
1565 num_errors == self.errors.len()
1566 }
1567
1568 fn make_group_key(
1572 target_name: Option<&'a String>,
1573 target: Option<&'a fdecl::Ref>,
1574 ) -> Option<(&'a str, RefKey<'a>)> {
1575 if target_name.is_none() {
1576 return None;
1577 }
1578 let target_name = target_name.unwrap().as_str();
1579 if target.is_none() {
1580 return None;
1581 }
1582 let target = match target.unwrap() {
1583 fdecl::Ref::Parent(_) => RefKey::Parent,
1584 fdecl::Ref::Self_(_) => RefKey::Self_,
1585 fdecl::Ref::Child(r) => RefKey::Child(r.name.as_str()),
1586 fdecl::Ref::Collection(r) => RefKey::Collection(r.name.as_str()),
1587 fdecl::Ref::Framework(_) => RefKey::Framework,
1588 fdecl::Ref::Capability(_) => RefKey::Capability,
1589 fdecl::Ref::Debug(_) => RefKey::Debug,
1590 fdecl::RefUnknown!() => {
1591 return None;
1592 }
1593 };
1594 Some((target_name, target))
1595 }
1596
1597 fn validate_aggregation_has_same_availability(
1598 &mut self,
1599 route_group: &Vec<impl HasAvailability>,
1600 ) {
1601 let availability_of_sources: BTreeSet<_> =
1603 route_group.iter().map(|r| r.availability()).collect();
1604
1605 if availability_of_sources.len() > 1 {
1607 self.errors.push(Error::different_availability_in_aggregation(
1608 availability_of_sources.into_iter().collect(),
1609 ));
1610 }
1611 }
1612
1613 fn validate_expose_group(&mut self, exposes: &'a Vec<fdecl::Expose>) {
1616 let mut expose_groups: HashMap<_, Vec<fdecl::ExposeService>> = HashMap::new();
1617 let service_exposes = exposes
1618 .into_iter()
1619 .filter_map(|o| if let fdecl::Expose::Service(s) = o { Some(s) } else { None });
1620 for expose in service_exposes {
1621 let key = Self::make_group_key(expose.target_name.as_ref(), expose.target.as_ref());
1622 if let Some(key) = key {
1623 expose_groups.entry(key).or_insert_with(|| vec![]).push(expose.clone());
1624 }
1625 }
1626 for expose_group in expose_groups.into_values() {
1627 if expose_group.len() == 1 {
1628 continue;
1631 }
1632
1633 self.validate_aggregation_has_same_availability(&expose_group);
1634 }
1635 }
1636
1637 fn validate_expose_decl(
1638 &mut self,
1639 expose: &'a fdecl::Expose,
1640 expose_to_parent_ids: &mut HashMap<&'a str, AllowableIds>,
1641 expose_to_framework_ids: &mut HashMap<&'a str, AllowableIds>,
1642 ) {
1643 match expose {
1644 fdecl::Expose::Service(e) => {
1645 let decl = DeclType::ExposeService;
1646 self.validate_expose_fields(
1647 decl,
1648 AllowableIds::Many,
1649 CollectionSource::Allow,
1650 Self::service_checker,
1651 e.source.as_ref(),
1652 e.source_name.as_ref(),
1653 e.source_dictionary.as_ref(),
1654 e.target.as_ref(),
1655 e.target_name.as_ref(),
1656 e.availability.as_ref(),
1657 expose_to_parent_ids,
1658 expose_to_framework_ids,
1659 );
1660 }
1661 fdecl::Expose::Protocol(e) => {
1662 let decl = DeclType::ExposeProtocol;
1663 self.validate_expose_fields(
1664 decl,
1665 AllowableIds::One,
1666 CollectionSource::Deny,
1667 Self::protocol_checker,
1668 e.source.as_ref(),
1669 e.source_name.as_ref(),
1670 e.source_dictionary.as_ref(),
1671 e.target.as_ref(),
1672 e.target_name.as_ref(),
1673 e.availability.as_ref(),
1674 expose_to_parent_ids,
1675 expose_to_framework_ids,
1676 );
1677 }
1678 fdecl::Expose::Directory(e) => {
1679 let decl = DeclType::ExposeDirectory;
1680 self.validate_expose_fields(
1681 decl,
1682 AllowableIds::One,
1683 CollectionSource::Deny,
1684 Self::directory_checker,
1685 e.source.as_ref(),
1686 e.source_name.as_ref(),
1687 e.source_dictionary.as_ref(),
1688 e.target.as_ref(),
1689 e.target_name.as_ref(),
1690 e.availability.as_ref(),
1691 expose_to_parent_ids,
1692 expose_to_framework_ids,
1693 );
1694
1695 match e.target.as_ref() {
1698 Some(fdecl::Ref::Framework(_)) => {
1699 if e.subdir.is_some() {
1700 self.errors.push(Error::invalid_field(decl, "subdir"));
1701 }
1702 }
1703 _ => {}
1704 }
1705
1706 if let Some(subdir) = e.subdir.as_ref() {
1707 check_relative_path(Some(subdir), decl, "subdir", &mut self.errors);
1708 }
1709 }
1710 fdecl::Expose::Runner(e) => {
1711 let decl = DeclType::ExposeRunner;
1712 self.validate_expose_fields(
1713 decl,
1714 AllowableIds::One,
1715 CollectionSource::Deny,
1716 Self::runner_checker,
1717 e.source.as_ref(),
1718 e.source_name.as_ref(),
1719 e.source_dictionary.as_ref(),
1720 e.target.as_ref(),
1721 e.target_name.as_ref(),
1722 Some(&fdecl::Availability::Required),
1723 expose_to_parent_ids,
1724 expose_to_framework_ids,
1725 );
1726 }
1727 fdecl::Expose::Resolver(e) => {
1728 let decl = DeclType::ExposeResolver;
1729 self.validate_expose_fields(
1730 decl,
1731 AllowableIds::One,
1732 CollectionSource::Deny,
1733 Self::resolver_checker,
1734 e.source.as_ref(),
1735 e.source_name.as_ref(),
1736 e.source_dictionary.as_ref(),
1737 e.target.as_ref(),
1738 e.target_name.as_ref(),
1739 Some(&fdecl::Availability::Required),
1740 expose_to_parent_ids,
1741 expose_to_framework_ids,
1742 );
1743 }
1744 fdecl::Expose::Dictionary(e) => {
1745 let decl = DeclType::ExposeDictionary;
1746 self.validate_expose_fields(
1747 decl,
1748 AllowableIds::One,
1749 CollectionSource::Deny,
1750 Self::dictionary_checker,
1751 e.source.as_ref(),
1752 e.source_name.as_ref(),
1753 e.source_dictionary.as_ref(),
1754 e.target.as_ref(),
1755 e.target_name.as_ref(),
1756 Some(&fdecl::Availability::Required),
1757 expose_to_parent_ids,
1758 expose_to_framework_ids,
1759 );
1760 }
1761 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1762 fdecl::Expose::Config(e) => {
1763 let decl = DeclType::ExposeConfig;
1764 self.validate_expose_fields(
1765 decl,
1766 AllowableIds::One,
1767 CollectionSource::Deny,
1768 Self::config_checker,
1769 e.source.as_ref(),
1770 e.source_name.as_ref(),
1771 None,
1772 e.target.as_ref(),
1773 e.target_name.as_ref(),
1774 e.availability.as_ref(),
1775 expose_to_parent_ids,
1776 expose_to_framework_ids,
1777 );
1778 }
1779 _ => {
1780 self.errors.push(Error::invalid_field(DeclType::Component, "expose"));
1781 }
1782 }
1783 }
1784
1785 fn validate_expose_fields(
1786 &mut self,
1787 decl: DeclType,
1788 allowable_ids: AllowableIds,
1789 collection_source: CollectionSource,
1790 capability_checker: impl Fn(&Self) -> &dyn Container,
1794 source: Option<&fdecl::Ref>,
1795 source_name: Option<&String>,
1796 source_dictionary: Option<&String>,
1797 target: Option<&fdecl::Ref>,
1798 target_name: Option<&'a String>,
1799 availability: Option<&fdecl::Availability>,
1800 expose_to_parent_ids: &mut HashMap<&'a str, AllowableIds>,
1801 expose_to_framework_ids: &mut HashMap<&'a str, AllowableIds>,
1802 ) {
1803 self.validate_expose_source(decl, collection_source, source, source_dictionary);
1804 check_route_availability(decl, availability, source, source_name, &mut self.errors);
1805 match target {
1806 Some(r) => match r {
1807 fdecl::Ref::Parent(_) => {}
1808 fdecl::Ref::Framework(_) => {}
1809 _ => {
1810 self.errors.push(Error::invalid_field(decl, "target"));
1811 }
1812 },
1813 None => {
1814 self.errors.push(Error::missing_field(decl, "target"));
1815 }
1816 }
1817 check_name(source_name, decl, "source_name", &mut self.errors);
1818 if source_dictionary.is_some() {
1819 check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
1820 }
1821 if check_name(target_name, decl, "target_name", &mut self.errors) {
1822 let maybe_ids_set = match target {
1823 Some(fdecl::Ref::Parent(_)) => Some(expose_to_parent_ids),
1824 Some(fdecl::Ref::Framework(_)) => Some(expose_to_framework_ids),
1825 _ => None,
1826 };
1827 if let Some(ids_set) = maybe_ids_set {
1828 let target_name = target_name.unwrap();
1829 if let Some(prev_state) = ids_set.insert(target_name, allowable_ids) {
1830 if prev_state == AllowableIds::One || prev_state != allowable_ids {
1831 self.errors.push(Error::duplicate_field(decl, "target_name", target_name));
1832 }
1833 }
1834 }
1835 }
1836
1837 self.validate_route_from_self(
1838 decl,
1839 source,
1840 source_name,
1841 source_dictionary,
1842 capability_checker,
1843 );
1844 }
1845
1846 fn validate_expose_source(
1847 &mut self,
1848 decl: DeclType,
1849 collection_source: CollectionSource,
1850 source: Option<&fdecl::Ref>,
1851 source_dictionary: Option<&String>,
1852 ) {
1853 match (source, source_dictionary) {
1854 (Some(fdecl::Ref::Self_(_)), _) => {}
1856 (Some(fdecl::Ref::Child(child)), _) => {
1857 let _ = self.validate_child_ref(decl, "source", &child, OfferType::Static);
1858 }
1859 (Some(fdecl::Ref::VoidType(_)), None) => {}
1861 (Some(fdecl::Ref::Framework(_)), None) => {}
1862 (Some(fdecl::Ref::Capability(c)), None) => {
1863 self.validate_source_capability(c, decl, "source");
1864 }
1865 (Some(fdecl::Ref::Collection(c)), None)
1866 if collection_source == CollectionSource::Allow =>
1867 {
1868 self.validate_source_collection(c, decl);
1869 }
1870 (None, _) => {
1872 self.errors.push(Error::missing_field(decl, "source"));
1873 }
1874 (_, _) => {
1876 self.errors.push(Error::invalid_field(decl, "source"));
1877 }
1878 }
1879 }
1880
1881 fn validate_offer_group(&mut self, offers: &'a Vec<fdecl::Offer>, offer_type: OfferType) {
1884 let mut offer_groups: HashMap<_, Vec<fdecl::OfferService>> = HashMap::new();
1885 let service_offers = offers
1886 .into_iter()
1887 .filter_map(|o| if let fdecl::Offer::Service(s) = o { Some(s) } else { None });
1888 for offer in service_offers {
1889 let key = Self::make_group_key(offer.target_name.as_ref(), offer.target.as_ref());
1890 if let Some(key) = key {
1891 offer_groups.entry(key).or_insert_with(|| vec![]).push(offer.clone());
1892 }
1893 }
1894 for offer_group in offer_groups.into_values() {
1895 if offer_group.len() == 1 {
1896 continue;
1899 }
1900
1901 self.validate_aggregation_has_same_availability(&offer_group);
1902
1903 let mut source_instance_filter_entries: HashSet<String> = HashSet::new();
1904 let mut service_source_names: HashSet<String> = HashSet::new();
1905 for o in offer_group {
1906 match (o.source_instance_filter, offer_type) {
1908 (Some(source_instance_filter), _) => {
1909 for instance_name in source_instance_filter {
1910 if !source_instance_filter_entries.insert(instance_name.clone()) {
1911 self.errors.push(Error::invalid_aggregate_offer(format!(
1914 "Conflicting source_instance_filter in aggregate service \
1915 offer, instance_name '{}' seen in filter lists multiple times",
1916 instance_name,
1917 )));
1918 }
1919 }
1920 }
1921 (None, OfferType::Static) => {}
1922 (None, OfferType::Dynamic) => {
1923 self.errors.push(Error::invalid_aggregate_offer(
1925 "source_instance_filter must be set for dynamic aggregate service \
1926 offers",
1927 ));
1928 }
1929 }
1930 service_source_names.insert(
1931 o.source_name
1932 .expect("Offer Service declarations must always contain source_name"),
1933 );
1934 }
1935
1936 if service_source_names.len() > 1 {
1937 self.errors.push(Error::invalid_aggregate_offer(format!(
1938 "All aggregate service offers must have the same source_name, saw {}. Use \
1939 renamed_instances to rename instance names to avoid conflict.",
1940 service_source_names.into_iter().sorted().collect::<Vec<String>>().join(", ")
1941 )));
1942 }
1943 }
1944 }
1945
1946 fn validate_offer_decl(&mut self, offer: &'a fdecl::Offer, offer_type: OfferType) {
1947 match offer {
1948 fdecl::Offer::Service(o) => {
1949 let decl = DeclType::OfferService;
1950 self.validate_offer_fields(
1951 decl,
1952 AllowableIds::Many,
1953 CollectionSource::Allow,
1954 Self::service_checker,
1955 o.source.as_ref(),
1956 o.source_name.as_ref(),
1957 o.source_dictionary.as_ref(),
1958 o.target.as_ref(),
1959 o.target_name.as_ref(),
1960 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1961 Some(o.dependency_type.as_ref().unwrap_or(&fdecl::DependencyType::Strong)),
1962 #[cfg(fuchsia_api_level_less_than = "HEAD")]
1963 Some(&fdecl::DependencyType::Strong),
1964 o.availability.as_ref(),
1965 offer_type,
1966 );
1967 self.validate_filtered_service_fields(
1968 decl,
1969 o.source_instance_filter.as_ref(),
1970 o.renamed_instances.as_ref(),
1971 );
1972 }
1973 fdecl::Offer::Protocol(o) => {
1974 let decl = DeclType::OfferProtocol;
1975 self.validate_offer_fields(
1976 decl,
1977 AllowableIds::One,
1978 CollectionSource::Deny,
1979 Self::protocol_checker,
1980 o.source.as_ref(),
1981 o.source_name.as_ref(),
1982 o.source_dictionary.as_ref(),
1983 o.target.as_ref(),
1984 o.target_name.as_ref(),
1985 o.dependency_type.as_ref(),
1986 o.availability.as_ref(),
1987 offer_type,
1988 );
1989 }
1990 fdecl::Offer::Directory(o) => {
1991 let decl = DeclType::OfferDirectory;
1992 self.validate_offer_fields(
1993 decl,
1994 AllowableIds::One,
1995 CollectionSource::Deny,
1996 Self::directory_checker,
1997 o.source.as_ref(),
1998 o.source_name.as_ref(),
1999 o.source_dictionary.as_ref(),
2000 o.target.as_ref(),
2001 o.target_name.as_ref(),
2002 o.dependency_type.as_ref(),
2003 o.availability.as_ref(),
2004 offer_type,
2005 );
2006 if let Some(subdir) = o.subdir.as_ref() {
2007 check_relative_path(
2008 Some(subdir),
2009 DeclType::OfferDirectory,
2010 "subdir",
2011 &mut self.errors,
2012 );
2013 }
2014 }
2015 fdecl::Offer::Storage(o) => {
2016 let decl = DeclType::OfferStorage;
2017 self.validate_storage_offer_fields(
2018 decl,
2019 Self::storage_checker,
2020 o.source.as_ref(),
2021 o.source_name.as_ref(),
2022 o.target.as_ref(),
2023 o.target_name.as_ref(),
2024 o.availability.as_ref(),
2025 offer_type,
2026 );
2027 }
2028 fdecl::Offer::Runner(o) => {
2029 let decl = DeclType::OfferRunner;
2030 self.validate_offer_fields(
2031 decl,
2032 AllowableIds::One,
2033 CollectionSource::Deny,
2034 Self::runner_checker,
2035 o.source.as_ref(),
2036 o.source_name.as_ref(),
2037 o.source_dictionary.as_ref(),
2038 o.target.as_ref(),
2039 o.target_name.as_ref(),
2040 Some(&fdecl::DependencyType::Strong),
2041 Some(&fdecl::Availability::Required),
2042 offer_type,
2043 );
2044 }
2045 fdecl::Offer::Resolver(o) => {
2046 let decl = DeclType::OfferResolver;
2047 self.validate_offer_fields(
2048 decl,
2049 AllowableIds::One,
2050 CollectionSource::Deny,
2051 Self::resolver_checker,
2052 o.source.as_ref(),
2053 o.source_name.as_ref(),
2054 o.source_dictionary.as_ref(),
2055 o.target.as_ref(),
2056 o.target_name.as_ref(),
2057 Some(&fdecl::DependencyType::Strong),
2058 Some(&fdecl::Availability::Required),
2059 offer_type,
2060 );
2061 }
2062 fdecl::Offer::EventStream(e) => {
2063 self.validate_event_stream_offer_fields(e, offer_type);
2064 }
2065 fdecl::Offer::Dictionary(o) => {
2066 let decl = DeclType::OfferDictionary;
2067 self.validate_offer_fields(
2068 decl,
2069 AllowableIds::One,
2070 CollectionSource::Deny,
2071 Self::dictionary_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 }
2082 #[cfg(fuchsia_api_level_at_least = "HEAD")]
2083 fdecl::Offer::Config(o) => {
2084 let decl = DeclType::OfferConfig;
2085 self.validate_offer_fields(
2086 decl,
2087 AllowableIds::One,
2088 CollectionSource::Deny,
2089 Self::config_checker,
2090 o.source.as_ref(),
2091 o.source_name.as_ref(),
2092 None,
2093 o.target.as_ref(),
2094 o.target_name.as_ref(),
2095 Some(&fdecl::DependencyType::Strong),
2096 o.availability.as_ref(),
2097 offer_type,
2098 );
2099 }
2100 fdecl::OfferUnknown!() => {
2101 self.errors.push(Error::invalid_field(DeclType::Component, "offer"));
2102 }
2103 }
2104 }
2105
2106 fn validate_offer_fields(
2107 &mut self,
2108 decl: DeclType,
2109 allowable_names: AllowableIds,
2110 collection_source: CollectionSource,
2111 capability_checker: impl Fn(&Self) -> &dyn Container,
2112 source: Option<&'a fdecl::Ref>,
2113 source_name: Option<&'a String>,
2114 source_dictionary: Option<&'a String>,
2115 target: Option<&'a fdecl::Ref>,
2116 target_name: Option<&'a String>,
2117 dependency_type: Option<&'a fdecl::DependencyType>,
2118 availability: Option<&'a fdecl::Availability>,
2119 offer_type: OfferType,
2120 ) {
2121 self.validate_offer_source(decl, collection_source, source, source_dictionary, offer_type);
2122 check_route_availability(decl, availability, source, source_name, &mut self.errors);
2123 check_offer_name(source_name, decl, "source_name", offer_type, &mut self.errors);
2124 if source_dictionary.is_some() {
2125 check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
2126 }
2127 self.validate_offer_target(decl, allowable_names, target, target_name, offer_type);
2128 check_offer_name(target_name, decl, "target_name", offer_type, &mut self.errors);
2129
2130 if dependency_type.is_none() {
2131 self.errors.push(Error::missing_field(decl, "dependency_type"));
2132 }
2133
2134 self.validate_route_from_self(
2135 decl,
2136 source,
2137 source_name,
2138 source_dictionary,
2139 capability_checker,
2140 );
2141 }
2142
2143 fn validate_offer_source(
2144 &mut self,
2145 decl: DeclType,
2146 collection_source: CollectionSource,
2147 source: Option<&'a fdecl::Ref>,
2148 source_dictionary: Option<&'a String>,
2149 offer_type: OfferType,
2150 ) {
2151 match (source, source_dictionary) {
2152 (Some(fdecl::Ref::Parent(_)), _) => {}
2154 (Some(fdecl::Ref::Self_(_)), _) => {}
2155 (Some(fdecl::Ref::Child(child)), _) => {
2156 self.validate_child_ref(decl, "source", &child, offer_type);
2157 }
2158 (Some(fdecl::Ref::VoidType(_)), None) => {}
2160 (Some(fdecl::Ref::Framework(_)), None) => {}
2161 (Some(fdecl::Ref::Capability(c)), None) => {
2162 self.validate_source_capability(c, decl, "source");
2163 }
2164 (Some(fdecl::Ref::Collection(c)), None)
2165 if collection_source == CollectionSource::Allow =>
2166 {
2167 self.validate_source_collection(c, decl);
2168 }
2169 (None, _) => self.errors.push(Error::missing_field(decl, "source")),
2171 (_, _) => self.errors.push(Error::invalid_field(decl, "source")),
2173 }
2174 }
2175
2176 fn validate_storage_offer_fields(
2177 &mut self,
2178 decl: DeclType,
2179 capability_checker: impl Fn(&Self) -> &dyn Container,
2183 source: Option<&'a fdecl::Ref>,
2184 source_name: Option<&'a String>,
2185 target: Option<&'a fdecl::Ref>,
2186 target_name: Option<&'a String>,
2187 availability: Option<&fdecl::Availability>,
2188 offer_type: OfferType,
2189 ) {
2190 match source {
2191 Some(fdecl::Ref::Parent(_) | fdecl::Ref::VoidType(_) | fdecl::Ref::Self_(_)) => {}
2192 Some(_) => {
2193 self.errors.push(Error::invalid_field(decl, "source"));
2194 }
2195 None => {
2196 self.errors.push(Error::missing_field(decl, "source"));
2197 }
2198 }
2199 check_route_availability(decl, availability, source, source_name, &mut self.errors);
2200 check_offer_name(source_name, decl, "source_name", offer_type, &mut self.errors);
2201 self.validate_offer_target(decl, AllowableIds::One, target, target_name, offer_type);
2202 check_offer_name(target_name, decl, "target_name", offer_type, &mut self.errors);
2203
2204 if let (Some(fdecl::Ref::Self_(_)), Some(name)) = (source, source_name) {
2205 if !(capability_checker)(self).contains(name) {
2206 self.errors.push(Error::invalid_capability(decl, "source", name));
2207 }
2208 }
2209 }
2210
2211 fn validate_event_stream_offer_fields(
2212 &mut self,
2213 event_stream: &'a fdecl::OfferEventStream,
2214 offer_type: OfferType,
2215 ) {
2216 let decl = DeclType::OfferEventStream;
2217 check_name(event_stream.source_name.as_ref(), decl, "source_name", &mut self.errors);
2218 if event_stream.target == Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})) {
2219 self.errors.push(Error::invalid_field(DeclType::OfferEventStream, "target"));
2221 }
2222 if let Some(scope) = &event_stream.scope {
2223 if scope.is_empty() {
2224 self.errors.push(Error::invalid_field(decl, "scope"));
2225 }
2226 for value in scope {
2227 match value {
2228 fdecl::Ref::Child(child) => {
2229 self.validate_child_ref(
2230 DeclType::OfferEventStream,
2231 "scope",
2232 &child,
2233 offer_type,
2234 );
2235 }
2236 fdecl::Ref::Collection(collection) => {
2237 self.validate_collection_ref(
2238 DeclType::OfferEventStream,
2239 "scope",
2240 &collection,
2241 );
2242 }
2243 _ => {
2244 self.errors.push(Error::invalid_field(DeclType::OfferEventStream, "scope"));
2245 }
2246 }
2247 }
2248 }
2249 match event_stream.source {
2251 Some(
2252 fdecl::Ref::Parent(_)
2253 | fdecl::Ref::Framework(_)
2254 | fdecl::Ref::Child(_)
2255 | fdecl::Ref::VoidType(_),
2256 ) => {}
2257 Some(_) => {
2258 self.errors.push(Error::invalid_field(decl, "source"));
2259 }
2260 None => {
2261 self.errors.push(Error::missing_field(decl, "source"));
2262 }
2263 };
2264
2265 check_route_availability(
2266 decl,
2267 event_stream.availability.as_ref(),
2268 event_stream.source.as_ref(),
2269 event_stream.source_name.as_ref(),
2270 &mut self.errors,
2271 );
2272
2273 self.validate_offer_target(
2274 decl,
2275 AllowableIds::One,
2276 event_stream.target.as_ref(),
2277 event_stream.target_name.as_ref(),
2278 offer_type,
2279 );
2280 check_name(event_stream.target_name.as_ref(), decl, "target_name", &mut self.errors);
2281 }
2282
2283 fn validate_child_ref(
2285 &mut self,
2286 decl: DeclType,
2287 field_name: &str,
2288 child: &fdecl::ChildRef,
2289 offer_type: OfferType,
2290 ) -> bool {
2291 if offer_type == OfferType::Dynamic && child.collection.is_some() {
2292 return self.validate_dynamic_child_ref(decl, field_name, child);
2293 }
2294 let mut valid = true;
2298 if !check_name(
2299 Some(&child.name),
2300 decl,
2301 &format!("{}.child.name", field_name),
2302 &mut self.errors,
2303 ) {
2304 valid = false;
2305 }
2306 if child.collection.is_some() {
2307 self.errors
2308 .push(Error::extraneous_field(decl, format!("{}.child.collection", field_name)));
2309 valid = false;
2310 }
2311 if !valid {
2312 return false;
2313 }
2314
2315 let name: &str = &child.name;
2317 if !self.all_children.contains_key(name) {
2318 self.errors.push(Error::invalid_child(decl, field_name, name));
2319 return false;
2320 }
2321
2322 true
2323 }
2324
2325 fn validate_dynamic_child_ref(
2330 &mut self,
2331 decl: DeclType,
2332 field_name: &str,
2333 child: &fdecl::ChildRef,
2334 ) -> bool {
2335 let mut valid = true;
2339 if !check_dynamic_name(
2340 Some(&child.name),
2341 decl,
2342 &format!("{}.child.name", field_name),
2343 &mut self.errors,
2344 ) {
2345 valid = false;
2346 }
2347 if !check_name(
2348 child.collection.as_ref(),
2349 decl,
2350 &format!("{}.child.collection", field_name),
2351 &mut self.errors,
2352 ) {
2353 valid = false;
2354 }
2355 valid
2356 }
2357
2358 fn validate_collection_ref(
2360 &mut self,
2361 decl: DeclType,
2362 field_name: &str,
2363 collection: &fdecl::CollectionRef,
2364 ) -> bool {
2365 if !check_name(
2367 Some(&collection.name),
2368 decl,
2369 &format!("{}.collection.name", field_name),
2370 &mut self.errors,
2371 ) {
2372 return false;
2373 }
2374
2375 if !self.all_collections.contains(&collection.name as &str) {
2377 self.errors.push(Error::invalid_collection(decl, field_name, &collection.name as &str));
2378 return false;
2379 }
2380
2381 true
2382 }
2383
2384 fn validate_offer_target(
2385 &mut self,
2386 decl: DeclType,
2387 allowable_names: AllowableIds,
2388 target: Option<&'a fdecl::Ref>,
2389 target_name: Option<&'a String>,
2390 offer_type: OfferType,
2391 ) {
2392 match target {
2393 Some(fdecl::Ref::Child(c)) => {
2394 self.validate_target_child(decl, allowable_names, c, target_name, offer_type);
2395 }
2396 Some(fdecl::Ref::Collection(c)) => {
2397 self.validate_target_collection(decl, allowable_names, c, target_name);
2398 }
2399 Some(fdecl::Ref::Capability(c)) => {
2400 if let Some(d) = self.all_dictionaries.get(&c.name.as_str()) {
2402 if d.source_path.is_some() {
2403 self.errors.push(Error::invalid_field(decl, "target"));
2406 }
2407 } else {
2408 self.errors.push(Error::invalid_field(decl, "target"));
2409 }
2410 }
2411 Some(_) => {
2412 self.errors.push(Error::invalid_field(decl, "target"));
2413 }
2414 None => {
2415 self.errors.push(Error::missing_field(decl, "target"));
2416 }
2417 }
2418 }
2419
2420 fn validate_target_child(
2421 &mut self,
2422 decl: DeclType,
2423 allowable_names: AllowableIds,
2424 child: &'a fdecl::ChildRef,
2425 target_name: Option<&'a String>,
2426 offer_type: OfferType,
2427 ) {
2428 if !self.validate_child_ref(decl, "target", child, offer_type) {
2429 return;
2430 }
2431 if let Some(target_name) = target_name {
2432 let names_for_target = self
2433 .target_ids
2434 .entry(TargetId::Component(
2435 &child.name,
2436 child.collection.as_ref().map(|s| s.as_str()),
2437 ))
2438 .or_insert(HashMap::new());
2439 if let Some(prev_state) = names_for_target.insert(target_name, allowable_names) {
2440 if prev_state == AllowableIds::One || prev_state != allowable_names {
2441 self.errors.push(Error::duplicate_field(
2442 decl,
2443 "target_name",
2444 target_name as &str,
2445 ));
2446 }
2447 }
2448 if let Some(collection) = child.collection.as_ref() {
2449 if let Some(names_for_target) =
2450 self.target_ids.get(&TargetId::Collection(&collection))
2451 {
2452 if names_for_target.contains_key(&target_name.as_str()) {
2453 self.errors.push(Error::duplicate_field(
2455 decl,
2456 "target_name",
2457 target_name as &str,
2458 ));
2459 }
2460 }
2461 }
2462 }
2463 }
2464
2465 fn validate_target_collection(
2466 &mut self,
2467 decl: DeclType,
2468 allowable_names: AllowableIds,
2469 collection: &'a fdecl::CollectionRef,
2470 target_name: Option<&'a String>,
2471 ) {
2472 if !self.validate_collection_ref(decl, "target", &collection) {
2473 return;
2474 }
2475 if let Some(target_name) = target_name {
2476 let names_for_target = self
2477 .target_ids
2478 .entry(TargetId::Collection(&collection.name))
2479 .or_insert(HashMap::new());
2480 if let Some(prev_state) = names_for_target.insert(target_name, allowable_names) {
2481 if prev_state == AllowableIds::One || prev_state != allowable_names {
2482 self.errors.push(Error::duplicate_field(
2483 decl,
2484 "target_name",
2485 target_name as &str,
2486 ));
2487 }
2488 }
2489 }
2490 }
2491
2492 fn validate_route_from_self(
2493 &mut self,
2494 decl: DeclType,
2495 source: Option<&fdecl::Ref>,
2496 source_name: Option<&String>,
2497 source_dictionary: Option<&String>,
2498 capability_checker: impl Fn(&Self) -> &dyn Container,
2499 ) {
2500 let (Some(fdecl::Ref::Self_(_)), Some(name)) = (source, source_name) else {
2501 return;
2502 };
2503 match source_dictionary {
2504 Some(source_dictionary) => {
2505 if let Ok(path) = cm_types::RelativePath::new(source_dictionary) {
2506 if let Some(first_segment) = path.iter_segments().next().map(|s| s.as_str()) {
2507 if !self.all_dictionaries.contains_key(first_segment) {
2508 self.errors.push(Error::invalid_capability(
2509 decl,
2510 "source",
2511 first_segment,
2512 ));
2513 }
2514 }
2515 }
2516 }
2517 None => {
2518 if !(capability_checker)(self).contains(name) {
2519 self.errors.push(Error::invalid_capability(decl, "source", name));
2520 }
2521 }
2522 }
2523 }
2524
2525 fn service_checker(&self) -> &dyn Container {
2528 &self.all_services
2529 }
2530 fn protocol_checker(&self) -> &dyn Container {
2531 &self.all_protocols
2532 }
2533 fn directory_checker(&self) -> &dyn Container {
2534 &self.all_directories
2535 }
2536 fn runner_checker(&self) -> &dyn Container {
2537 &self.all_runners
2538 }
2539 fn resolver_checker(&self) -> &dyn Container {
2540 &self.all_resolvers
2541 }
2542
2543 fn dictionary_checker(&self) -> &dyn Container {
2544 &self.all_dictionaries
2545 }
2546
2547 #[cfg(fuchsia_api_level_at_least = "HEAD")]
2548 fn config_checker(&self) -> &dyn Container {
2549 &self.all_configs
2550 }
2551 fn storage_checker(&self) -> &dyn Container {
2552 &self.all_storages
2553 }
2554 fn event_stream_checker(&self) -> &dyn Container {
2555 struct AlwaysTrueContainer {}
2559 impl Container for AlwaysTrueContainer {
2560 fn contains(&self, _key: &str) -> bool {
2561 true
2562 }
2563 }
2564 static CONTAINER: AlwaysTrueContainer = AlwaysTrueContainer {};
2565 &CONTAINER
2566 }
2567}
2568
2569#[cfg(test)]
2570mod tests {
2571 use super::*;
2572 use cm_types::MAX_LONG_NAME_LENGTH;
2573 use test_case::test_case;
2574 use {
2575 fidl_fuchsia_component_decl as fdecl, fidl_fuchsia_data as fdata, fidl_fuchsia_io as fio,
2576 };
2577
2578 macro_rules! test_validate {
2579 (
2580 $(
2581 $test_name:ident => {
2582 input = $input:expr,
2583 result = $result:expr,
2584 },
2585 )+
2586 ) => {
2587 $(
2588 #[test]
2589 fn $test_name() {
2590 validate_test($input, $result);
2591 }
2592 )+
2593 }
2594 }
2595
2596 macro_rules! test_validate_any_result {
2597 (
2598 $(
2599 $test_name:ident => {
2600 input = $input:expr,
2601 results = $results:expr,
2602 },
2603 )+
2604 ) => {
2605 $(
2606 #[test]
2607 fn $test_name() {
2608 validate_test_any_result($input, $results);
2609 }
2610 )+
2611 }
2612 }
2613
2614 macro_rules! test_validate_values_data {
2615 (
2616 $(
2617 $test_name:ident => {
2618 input = $input:expr,
2619 result = $result:expr,
2620 },
2621 )+
2622 ) => {
2623 $(
2624 #[test]
2625 fn $test_name() {
2626 validate_values_data_test($input, $result);
2627 }
2628 )+
2629 }
2630 }
2631
2632 macro_rules! test_validate_capabilities {
2633 (
2634 $(
2635 $test_name:ident => {
2636 input = $input:expr,
2637 as_builtin = $as_builtin:expr,
2638 result = $result:expr,
2639 },
2640 )+
2641 ) => {
2642 $(
2643 #[test]
2644 fn $test_name() {
2645 validate_capabilities_test($input, $as_builtin, $result);
2646 }
2647 )+
2648 }
2649 }
2650
2651 macro_rules! test_dependency {
2652 (
2653 $(
2654 ($test_name:ident) => {
2655 ty = $ty:expr,
2656 offer_decl = $offer_decl:expr,
2657 },
2658 )+
2659 ) => {
2660 $(
2661 #[test]
2662 fn $test_name() {
2663 let mut decl = new_component_decl();
2664 let dependencies = vec![
2665 ("a", "b"),
2666 ("b", "a"),
2667 ];
2668 let offers = dependencies.into_iter().map(|(from,to)| {
2669 let mut offer_decl = $offer_decl;
2670 offer_decl.source = Some(fdecl::Ref::Child(
2671 fdecl::ChildRef { name: from.to_string(), collection: None },
2672 ));
2673 offer_decl.target = Some(fdecl::Ref::Child(
2674 fdecl::ChildRef { name: to.to_string(), collection: None },
2675 ));
2676 $ty(offer_decl)
2677 }).collect();
2678 let children = ["a", "b"].iter().map(|name| {
2679 fdecl::Child {
2680 name: Some(name.to_string()),
2681 url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
2682 startup: Some(fdecl::StartupMode::Lazy),
2683 on_terminate: None,
2684 environment: None,
2685 ..Default::default()
2686 }
2687 }).collect();
2688 decl.offers = Some(offers);
2689 decl.children = Some(children);
2690 let result = Err(ErrorList::new(vec![
2691 Error::dependency_cycle("{{child a -> child b -> child a}}")
2692 ]));
2693 validate_test(decl, result);
2694 }
2695 )+
2696 }
2697 }
2698
2699 macro_rules! test_weak_dependency {
2700 (
2701 $(
2702 ($test_name:ident) => {
2703 ty = $ty:expr,
2704 offer_decl = $offer_decl:expr,
2705 },
2706 )+
2707 ) => {
2708 $(
2709 #[test_case(fdecl::DependencyType::Weak)]
2710 fn $test_name(weak_dep: fdecl::DependencyType) {
2711 let mut decl = new_component_decl();
2712 let offers = vec![
2713 {
2714 let mut offer_decl = $offer_decl;
2715 offer_decl.source = Some(fdecl::Ref::Child(
2716 fdecl::ChildRef { name: "a".to_string(), collection: None },
2717 ));
2718 offer_decl.target = Some(fdecl::Ref::Child(
2719 fdecl::ChildRef { name: "b".to_string(), collection: None },
2720 ));
2721 offer_decl.dependency_type = Some(fdecl::DependencyType::Strong);
2722 $ty(offer_decl)
2723 },
2724 {
2725 let mut offer_decl = $offer_decl;
2726 offer_decl.source = Some(fdecl::Ref::Child(
2727 fdecl::ChildRef { name: "b".to_string(), collection: None },
2728 ));
2729 offer_decl.target = Some(fdecl::Ref::Child(
2730 fdecl::ChildRef { name: "a".to_string(), collection: None },
2731 ));
2732 offer_decl.dependency_type = Some(weak_dep);
2733 $ty(offer_decl)
2734 },
2735 ];
2736 let children = ["a", "b"].iter().map(|name| {
2737 fdecl::Child {
2738 name: Some(name.to_string()),
2739 url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
2740 startup: Some(fdecl::StartupMode::Lazy),
2741 on_terminate: None,
2742 environment: None,
2743 ..Default::default()
2744 }
2745 }).collect();
2746 decl.offers = Some(offers);
2747 decl.children = Some(children);
2748 let result = Ok(());
2749 validate_test(decl, result);
2750 }
2751 )+
2752 }
2753 }
2754
2755 #[track_caller]
2756 fn validate_test(input: fdecl::Component, expected_res: Result<(), ErrorList>) {
2757 let res = validate(&input);
2758 assert_eq!(res, expected_res);
2759 }
2760
2761 #[track_caller]
2762 fn validate_test_any_result(input: fdecl::Component, expected_res: Vec<Result<(), ErrorList>>) {
2763 let res = format!("{:?}", validate(&input));
2764 let expected_res_debug = format!("{:?}", expected_res);
2765
2766 let matched_exp =
2767 expected_res.into_iter().find(|expected| res == format!("{:?}", expected));
2768
2769 assert!(
2770 matched_exp.is_some(),
2771 "assertion failed: Expected one of:\n{:?}\nActual:\n{:?}",
2772 expected_res_debug,
2773 res
2774 );
2775 }
2776
2777 #[track_caller]
2778 fn validate_values_data_test(
2779 input: fdecl::ConfigValuesData,
2780 expected_res: Result<(), ErrorList>,
2781 ) {
2782 let res = validate_values_data(&input);
2783 assert_eq!(res, expected_res);
2784 }
2785
2786 #[track_caller]
2787 fn validate_capabilities_test(
2788 input: Vec<fdecl::Capability>,
2789 as_builtin: bool,
2790 expected_res: Result<(), ErrorList>,
2791 ) {
2792 let res = validate_capabilities(&input, as_builtin);
2793 assert_eq!(res, expected_res);
2794 }
2795
2796 fn new_component_decl() -> fdecl::Component {
2797 fdecl::Component {
2798 program: None,
2799 uses: None,
2800 exposes: None,
2801 offers: None,
2802 facets: None,
2803 capabilities: None,
2804 children: None,
2805 collections: None,
2806 environments: None,
2807 ..Default::default()
2808 }
2809 }
2810
2811 test_validate_any_result! {
2812 test_validate_use_disallows_nested_dirs => {
2813 input = {
2814 let mut decl = new_component_decl();
2815 decl.uses = Some(vec![
2816 fdecl::Use::Directory(fdecl::UseDirectory {
2817 dependency_type: Some(fdecl::DependencyType::Strong),
2818 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2819 source_name: Some("abc".to_string()),
2820 target_path: Some("/foo/bar".to_string()),
2821 rights: Some(fio::Operations::CONNECT),
2822 subdir: None,
2823 ..Default::default()
2824 }),
2825 fdecl::Use::Directory(fdecl::UseDirectory {
2826 dependency_type: Some(fdecl::DependencyType::Strong),
2827 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2828 source_name: Some("abc".to_string()),
2829 target_path: Some("/foo/bar/baz".to_string()),
2830 rights: Some(fio::Operations::CONNECT),
2831 subdir: None,
2832 ..Default::default()
2833 }),
2834 ]);
2835 decl
2836 },
2837 results = vec![
2838 Err(ErrorList::new(vec![
2839 Error::invalid_path_overlap(
2840 DeclType::UseDirectory, "/foo/bar/baz", DeclType::UseDirectory, "/foo/bar"),
2841 ])),
2842 Err(ErrorList::new(vec![
2843 Error::invalid_path_overlap(
2844 DeclType::UseDirectory, "/foo/bar", DeclType::UseDirectory, "/foo/bar/baz"),
2845 ])),
2846 ],
2847 },
2848 test_validate_use_disallows_nested_dirs_storage => {
2849 input = {
2850 let mut decl = new_component_decl();
2851 decl.uses = Some(vec![
2852 fdecl::Use::Storage(fdecl::UseStorage {
2853 source_name: Some("abc".to_string()),
2854 target_path: Some("/foo/bar".to_string()),
2855 ..Default::default()
2856 }),
2857 fdecl::Use::Storage(fdecl::UseStorage {
2858 source_name: Some("abc".to_string()),
2859 target_path: Some("/foo/bar/baz".to_string()),
2860 ..Default::default()
2861 }),
2862 ]);
2863 decl
2864 },
2865 results = vec![
2866 Err(ErrorList::new(vec![
2867 Error::invalid_path_overlap(
2868 DeclType::UseStorage, "/foo/bar/baz", DeclType::UseStorage, "/foo/bar"),
2869 ])),
2870 Err(ErrorList::new(vec![
2871 Error::invalid_path_overlap(
2872 DeclType::UseStorage, "/foo/bar", DeclType::UseStorage, "/foo/bar/baz"),
2873 ])),
2874 ],
2875 },
2876 test_validate_use_disallows_nested_dirs_directory_and_storage => {
2877 input = {
2878 let mut decl = new_component_decl();
2879 decl.uses = Some(vec![
2880 fdecl::Use::Directory(fdecl::UseDirectory {
2881 dependency_type: Some(fdecl::DependencyType::Strong),
2882 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2883 source_name: Some("abc".to_string()),
2884 target_path: Some("/foo/bar".to_string()),
2885 rights: Some(fio::Operations::CONNECT),
2886 subdir: None,
2887 ..Default::default()
2888 }),
2889 fdecl::Use::Storage(fdecl::UseStorage {
2890 source_name: Some("abc".to_string()),
2891 target_path: Some("/foo/bar/baz".to_string()),
2892 ..Default::default()
2893 }),
2894 ]);
2895 decl
2896 },
2897 results = vec![
2898 Err(ErrorList::new(vec![
2899 Error::invalid_path_overlap(
2900 DeclType::UseStorage, "/foo/bar/baz", DeclType::UseDirectory, "/foo/bar"),
2901 ])),
2902 Err(ErrorList::new(vec![
2903 Error::invalid_path_overlap(
2904 DeclType::UseDirectory, "/foo/bar", DeclType::UseStorage, "/foo/bar/baz"),
2905 ])),
2906 ],
2907 },
2908 test_validate_use_disallows_common_prefixes_protocol => {
2909 input = {
2910 let mut decl = new_component_decl();
2911 decl.uses = Some(vec![
2912 fdecl::Use::Directory(fdecl::UseDirectory {
2913 dependency_type: Some(fdecl::DependencyType::Strong),
2914 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2915 source_name: Some("abc".to_string()),
2916 target_path: Some("/foo/bar".to_string()),
2917 rights: Some(fio::Operations::CONNECT),
2918 subdir: None,
2919 ..Default::default()
2920 }),
2921 fdecl::Use::Protocol(fdecl::UseProtocol {
2922 dependency_type: Some(fdecl::DependencyType::Strong),
2923 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2924 source_name: Some("crow".to_string()),
2925 target_path: Some("/foo/bar/fuchsia.2".to_string()),
2926 ..Default::default()
2927 }),
2928 ]);
2929 decl
2930 },
2931 results = vec![
2932 Err(ErrorList::new(vec![
2933 Error::invalid_path_overlap(
2934 DeclType::UseProtocol, "/foo/bar/fuchsia.2", DeclType::UseDirectory, "/foo/bar"),
2935 ])),
2936 Err(ErrorList::new(vec![
2937 Error::invalid_path_overlap(
2938 DeclType::UseDirectory, "/foo/bar", DeclType::UseProtocol, "/foo/bar/fuchsia.2"),
2939 ])),
2940 ],
2941 },
2942 test_validate_use_disallows_common_prefixes_service => {
2943 input = {
2944 let mut decl = new_component_decl();
2945 decl.uses = Some(vec![
2946 fdecl::Use::Directory(fdecl::UseDirectory {
2947 dependency_type: Some(fdecl::DependencyType::Strong),
2948 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2949 source_name: Some("abc".to_string()),
2950 target_path: Some("/foo/bar".to_string()),
2951 rights: Some(fio::Operations::CONNECT),
2952 subdir: None,
2953 ..Default::default()
2954 }),
2955 fdecl::Use::Service(fdecl::UseService {
2956 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2957 source_name: Some("space".to_string()),
2958 target_path: Some("/foo/bar/baz/fuchsia.logger.Log".to_string()),
2959 dependency_type: Some(fdecl::DependencyType::Strong),
2960 ..Default::default()
2961 }),
2962 ]);
2963 decl
2964 },
2965 results = vec![
2966 Err(ErrorList::new(vec![
2967 Error::invalid_path_overlap(
2968 DeclType::UseService, "/foo/bar/baz/fuchsia.logger.Log", DeclType::UseDirectory, "/foo/bar"),
2969 ])),
2970 Err(ErrorList::new(vec![
2971 Error::invalid_path_overlap(
2972 DeclType::UseDirectory, "/foo/bar", DeclType::UseService, "/foo/bar/baz/fuchsia.logger.Log"),
2973 ])),
2974 ],
2975 },
2976 test_validate_use_disallows_pkg => {
2977 input = {
2978 let mut decl = new_component_decl();
2979 decl.uses = Some(vec![
2980 fdecl::Use::Directory(fdecl::UseDirectory {
2981 dependency_type: Some(fdecl::DependencyType::Strong),
2982 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2983 source_name: Some("abc".to_string()),
2984 target_path: Some("/pkg".to_string()),
2985 rights: Some(fio::Operations::CONNECT),
2986 subdir: None,
2987 ..Default::default()
2988 }),
2989 ]);
2990 decl
2991 },
2992 results = vec![
2993 Err(ErrorList::new(vec![
2994 Error::pkg_path_overlap(DeclType::UseDirectory, "/pkg"),
2995 ])),
2996 ],
2997 },
2998 test_validate_use_disallows_pkg_overlap => {
2999 input = {
3000 let mut decl = new_component_decl();
3001 decl.uses = Some(vec![
3002 fdecl::Use::Directory(fdecl::UseDirectory {
3003 dependency_type: Some(fdecl::DependencyType::Strong),
3004 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3005 source_name: Some("abc".to_string()),
3006 target_path: Some("/pkg/foo".to_string()),
3007 rights: Some(fio::Operations::CONNECT),
3008 subdir: None,
3009 ..Default::default()
3010 }),
3011 ]);
3012 decl
3013 },
3014 results = vec![
3015 Err(ErrorList::new(vec![
3016 Error::pkg_path_overlap(DeclType::UseDirectory, "/pkg/foo"),
3017 ])),
3018 ],
3019 },
3020 test_validate_use_optional_config_correct => {
3021 input = {
3022 let mut decl = new_component_decl();
3023 decl.uses = Some(vec![
3024 fdecl::Use::Config(fdecl::UseConfiguration {
3025 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3026 source_name: Some("abc".to_string()),
3027 target_name: Some("foo".to_string()),
3028 availability: Some(fdecl::Availability::Optional),
3029 type_: Some(fdecl::ConfigType {
3030 layout: fdecl::ConfigTypeLayout::Bool,
3031 parameters: Some(Vec::new()),
3032 constraints: Vec::new(),
3033 }),
3034 ..Default::default()
3035 }),
3036 ]);
3037 decl.config = Some(fdecl::ConfigSchema {
3038 fields: Some(vec![fdecl::ConfigField {
3039 key: Some("foo".into()),
3040 type_: Some(fdecl::ConfigType {
3041 layout: fdecl::ConfigTypeLayout::Bool,
3042 parameters: Some(Vec::new()),
3043 constraints: Vec::new(),
3044 }),
3045 mutability: None,
3046 ..Default::default()}]),
3047 checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3048 value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3049 ..Default::default()
3050 });
3051 decl
3052 },
3053 results = vec![Ok(())],
3054 },
3055 test_validate_use_optional_config_no_config_schema => {
3056 input = {
3057 let mut decl = new_component_decl();
3058 decl.uses = Some(vec![
3059 fdecl::Use::Config(fdecl::UseConfiguration {
3060 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3061 source_name: Some("abc".to_string()),
3062 target_name: Some("foo".to_string()),
3063 availability: Some(fdecl::Availability::Optional),
3064 type_: Some(fdecl::ConfigType {
3065 layout: fdecl::ConfigTypeLayout::Bool,
3066 parameters: None,
3067 constraints: Vec::new(),
3068 }),
3069 ..Default::default()
3070 }),
3071 ]);
3072 decl
3073 },
3074 results = vec![
3075 Err(ErrorList::new(vec![
3076 Error::missing_field(DeclType::ConfigField, "config"),
3077 ])),
3078 ],
3079 },
3080 test_validate_use_optional_config_no_config_field => {
3081 input = {
3082 let mut decl = new_component_decl();
3083 decl.uses = Some(vec![
3084 fdecl::Use::Config(fdecl::UseConfiguration {
3085 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3086 source_name: Some("abc".to_string()),
3087 target_name: Some("foo".to_string()),
3088 availability: Some(fdecl::Availability::Optional),
3089 type_: Some(fdecl::ConfigType {
3090 layout: fdecl::ConfigTypeLayout::Bool,
3091 parameters: None,
3092 constraints: Vec::new(),
3093 }),
3094 ..Default::default()
3095 }),
3096 ]);
3097 decl.config = Some(fdecl::ConfigSchema {
3098 fields: Some(vec![]),
3099 checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3100 value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3101 ..Default::default()
3102 });
3103 decl
3104 },
3105 results = vec![
3106 Err(ErrorList::new(vec![
3107 Error::missing_field(DeclType::ConfigField, "foo"),
3108 ])),
3109 ],
3110 },
3111 test_validate_use_optional_config_bad_type => {
3112 input = {
3113 let mut decl = new_component_decl();
3114 decl.uses = Some(vec![
3115 fdecl::Use::Config(fdecl::UseConfiguration {
3116 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3117 source_name: Some("abc".to_string()),
3118 target_name: Some("foo".to_string()),
3119 availability: Some(fdecl::Availability::Optional),
3120 type_: Some(fdecl::ConfigType {
3121 layout: fdecl::ConfigTypeLayout::Bool,
3122 parameters: None,
3123 constraints: Vec::new(),
3124 }),
3125 ..Default::default()
3126 }),
3127 ]);
3128 decl.config = Some(fdecl::ConfigSchema {
3129 fields: Some(vec![fdecl::ConfigField {
3130 key: Some("foo".into()),
3131 type_: Some(fdecl::ConfigType {
3132 layout: fdecl::ConfigTypeLayout::Int16,
3133 parameters: Some(Vec::new()),
3134 constraints: Vec::new(),
3135 }),
3136 mutability: None,
3137 ..Default::default()}]),
3138 checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3139 value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3140 ..Default::default()
3141 });
3142 decl
3143 },
3144 results = vec![
3145 Err(ErrorList::new(vec![
3146 Error::invalid_field(DeclType::ConfigField, "foo"),
3147 ])),
3148 ],
3149 },
3150 }
3151
3152 test_validate_values_data! {
3153 test_values_data_ok => {
3154 input = fdecl::ConfigValuesData {
3155 values: Some(vec![
3156 fdecl::ConfigValueSpec {
3157 value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(true))),
3158 ..Default::default()
3159 }
3160 ]),
3161 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3162 ..Default::default()
3163 },
3164 result = Ok(()),
3165 },
3166 test_values_data_no_checksum => {
3167 input = fdecl::ConfigValuesData {
3168 values: Some(vec![]),
3169 checksum: None,
3170 ..Default::default()
3171 },
3172 result = Err(ErrorList::new(vec![
3173 Error::missing_field(DeclType::ConfigValuesData, "checksum")
3174 ])),
3175 },
3176 test_values_data_unknown_checksum => {
3177 input = fdecl::ConfigValuesData {
3178 values: Some(vec![]),
3179 checksum: Some(fdecl::ConfigChecksum::unknown_variant_for_testing()),
3180 ..Default::default()
3181 },
3182 result = Err(ErrorList::new(vec![
3183 Error::invalid_field(DeclType::ConfigValuesData, "checksum")
3184 ])),
3185 },
3186 test_values_data_no_values => {
3187 input = fdecl::ConfigValuesData {
3188 values: None,
3189 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3190 ..Default::default()
3191 },
3192 result = Err(ErrorList::new(vec![
3193 Error::missing_field(DeclType::ConfigValuesData, "values")
3194 ])),
3195 },
3196 test_values_data_no_inner_value => {
3197 input = fdecl::ConfigValuesData {
3198 values: Some(vec![
3199 fdecl::ConfigValueSpec::default()
3200 ]),
3201 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3202 ..Default::default()
3203 },
3204 result = Err(ErrorList::new(vec![
3205 Error::missing_field(DeclType::ConfigValueSpec, "value")
3206 ])),
3207 },
3208 test_values_data_unknown_inner_value => {
3209 input = fdecl::ConfigValuesData {
3210 values: Some(vec![
3211 fdecl::ConfigValueSpec {
3212 value: Some(fdecl::ConfigValue::unknown_variant_for_testing()),
3213 ..Default::default()
3214 }
3215 ]),
3216 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3217 ..Default::default()
3218 },
3219 result = Err(ErrorList::new(vec![
3220 Error::invalid_field(DeclType::ConfigValueSpec, "value")
3221 ])),
3222 },
3223 test_values_data_unknown_single_value => {
3224 input = fdecl::ConfigValuesData {
3225 values: Some(vec![
3226 fdecl::ConfigValueSpec {
3227 value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::unknown_variant_for_testing())),
3228 ..Default::default()
3229 }
3230 ]),
3231 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3232 ..Default::default()
3233 },
3234 result = Err(ErrorList::new(vec![
3235 Error::invalid_field(DeclType::ConfigValueSpec, "value")
3236 ])),
3237 },
3238 test_values_data_unknown_list_value => {
3239 input = fdecl::ConfigValuesData {
3240 values: Some(vec![
3241 fdecl::ConfigValueSpec {
3242 value: Some(fdecl::ConfigValue::Vector(fdecl::ConfigVectorValue::unknown_variant_for_testing())),
3243 ..Default::default()
3244 }
3245 ]),
3246 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3247 ..Default::default()
3248 },
3249 result = Err(ErrorList::new(vec![
3250 Error::invalid_field(DeclType::ConfigValueSpec, "value")
3251 ])),
3252 },
3253 }
3254
3255 test_validate! {
3256 test_validate_uses_empty => {
3258 input = {
3259 let mut decl = new_component_decl();
3260 decl.program = Some(fdecl::Program {
3261 runner: Some("elf".to_string()),
3262 info: Some(fdata::Dictionary {
3263 entries: None,
3264 ..Default::default()
3265 }),
3266 ..Default::default()
3267 });
3268 decl.uses = Some(vec![
3269 fdecl::Use::Service(fdecl::UseService {
3270 source: None,
3271 source_name: None,
3272 target_path: None,
3273 dependency_type: None,
3274 ..Default::default()
3275 }),
3276 fdecl::Use::Protocol(fdecl::UseProtocol {
3277 dependency_type: None,
3278 source: None,
3279 source_name: None,
3280 target_path: None,
3281 ..Default::default()
3282 }),
3283 fdecl::Use::Directory(fdecl::UseDirectory {
3284 dependency_type: None,
3285 source: None,
3286 source_name: None,
3287 target_path: None,
3288 rights: None,
3289 subdir: None,
3290 ..Default::default()
3291 }),
3292 fdecl::Use::Storage(fdecl::UseStorage {
3293 source_name: None,
3294 target_path: None,
3295 ..Default::default()
3296 }),
3297 fdecl::Use::EventStream(fdecl::UseEventStream {
3298 source_name: None,
3299 source: None,
3300 target_path: None,
3301 ..Default::default()
3302 }),
3303 fdecl::Use::Runner(fdecl::UseRunner {
3304 source_name: None,
3305 source: None,
3306 ..Default::default()
3307 }),
3308 ]);
3309 decl
3310 },
3311 result = Err(ErrorList::new(vec![
3312 Error::missing_field(DeclType::UseService, "source"),
3313 Error::missing_field(DeclType::UseService, "source_name"),
3314 Error::missing_field(DeclType::UseService, "target_path"),
3315 Error::missing_field(DeclType::UseService, "dependency_type"),
3316 Error::missing_field(DeclType::UseProtocol, "source"),
3317 Error::missing_field(DeclType::UseProtocol, "source_name"),
3318 Error::missing_field(DeclType::UseProtocol, "target_path"),
3319 Error::missing_field(DeclType::UseProtocol, "dependency_type"),
3320 Error::missing_field(DeclType::UseDirectory, "source"),
3321 Error::missing_field(DeclType::UseDirectory, "source_name"),
3322 Error::missing_field(DeclType::UseDirectory, "target_path"),
3323 Error::missing_field(DeclType::UseDirectory, "dependency_type"),
3324 Error::missing_field(DeclType::UseDirectory, "rights"),
3325 Error::missing_field(DeclType::UseStorage, "source_name"),
3326 Error::missing_field(DeclType::UseStorage, "target_path"),
3327 Error::missing_field(DeclType::UseEventStream, "source"),
3328 Error::missing_field(DeclType::UseEventStream, "source_name"),
3329 Error::missing_field(DeclType::UseEventStream, "target_path"),
3330 Error::missing_field(DeclType::UseRunner, "source"),
3331 Error::missing_field(DeclType::UseRunner, "source_name"),
3332 ])),
3333 },
3334 test_validate_missing_program_info => {
3335 input = {
3336 let mut decl = new_component_decl();
3337 decl.program = Some(fdecl::Program {
3338 runner: Some("runner".to_string()),
3339 info: None,
3340 ..Default::default()
3341 });
3342 decl
3343 },
3344 result = Err(ErrorList::new(vec![
3345 Error::missing_field(DeclType::Program, "info")
3346 ])),
3347 },
3348 test_validate_uses_invalid_identifiers => {
3349 input = {
3350 let mut decl = new_component_decl();
3351 decl.uses = Some(vec![
3352 fdecl::Use::Service(fdecl::UseService {
3353 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
3354 name: "^bad".to_string(),
3355 })),
3356 source_name: Some("foo/".to_string()),
3357 target_path: Some("a/foo".to_string()),
3358 dependency_type: Some(fdecl::DependencyType::Strong),
3359 ..Default::default()
3360 }),
3361 fdecl::Use::Protocol(fdecl::UseProtocol {
3362 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3363 name: "^bad".to_string(),
3364 collection: None,
3365 })),
3366 source_name: Some("foo/".to_string()),
3367 target_path: Some("b/foo".to_string()),
3368 dependency_type: Some(fdecl::DependencyType::Strong),
3369 ..Default::default()
3370 }),
3371 fdecl::Use::Directory(fdecl::UseDirectory {
3372 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3373 name: "^bad".to_string(),
3374 collection: None,
3375 })),
3376 source_name: Some("foo/".to_string()),
3377 target_path: Some("c".to_string()),
3378 rights: Some(fio::Operations::CONNECT),
3379 subdir: Some("/foo".to_string()),
3380 dependency_type: Some(fdecl::DependencyType::Strong),
3381 ..Default::default()
3382 }),
3383 fdecl::Use::Storage(fdecl::UseStorage {
3384 source_name: Some("foo/".to_string()),
3385 target_path: Some("d".to_string()),
3386 ..Default::default()
3387 }),
3388 fdecl::Use::EventStream(fdecl::UseEventStream {
3389 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3390 name: "^bad".to_string(),
3391 collection: None,
3392 })),
3393 source_name: Some("foo/".to_string()),
3394 target_path: Some("e".to_string()),
3395 ..Default::default()
3396 }),
3397 fdecl::Use::Runner(fdecl::UseRunner {
3398 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3399 name: "^bad".to_string(),
3400 collection: None,
3401 })),
3402 source_name: Some("foo/".to_string()),
3403 ..Default::default()
3404 }),
3405 ]);
3406 decl
3407 },
3408 result = Err(ErrorList::new(vec![
3409 Error::invalid_field(DeclType::UseService, "source.capability.name"),
3410 Error::invalid_field(DeclType::UseService, "source_name"),
3411 Error::invalid_field(DeclType::UseService, "target_path"),
3412 Error::invalid_field(DeclType::UseProtocol, "source.child.name"),
3413 Error::invalid_field(DeclType::UseProtocol, "source_name"),
3414 Error::invalid_field(DeclType::UseProtocol, "target_path"),
3415 Error::invalid_field(DeclType::UseDirectory, "source.child.name"),
3416 Error::invalid_field(DeclType::UseDirectory, "source_name"),
3417 Error::invalid_field(DeclType::UseDirectory, "target_path"),
3418 Error::invalid_field(DeclType::UseDirectory, "subdir"),
3419 Error::invalid_field(DeclType::UseStorage, "source_name"),
3420 Error::invalid_field(DeclType::UseStorage, "target_path"),
3421 Error::invalid_field(DeclType::UseEventStream, "source.child.name"),
3422 Error::invalid_field(DeclType::UseEventStream, "source_name"),
3423 Error::invalid_field(DeclType::UseEventStream, "target_path"),
3424 Error::invalid_field(DeclType::UseRunner, "source.child.name"),
3425 Error::invalid_field(DeclType::UseRunner, "source_name"),
3426 ])),
3427 },
3428 test_validate_uses_missing_source => {
3429 input = {
3430 fdecl::Component {
3431 uses: Some(vec![
3432 fdecl::Use::Protocol(fdecl::UseProtocol {
3433 dependency_type: Some(fdecl::DependencyType::Strong),
3434 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
3435 name: "this-storage-doesnt-exist".to_string(),
3436 })),
3437 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3438 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3439 ..Default::default()
3440 })
3441 ]),
3442 ..new_component_decl()
3443 }
3444 },
3445 result = Err(ErrorList::new(vec![
3446 Error::invalid_capability(DeclType::UseProtocol, "source", "this-storage-doesnt-exist"),
3447 ])),
3448 },
3449 test_validate_uses_invalid_child => {
3450 input = {
3451 fdecl::Component {
3452 uses: Some(vec![
3453 fdecl::Use::Protocol(fdecl::UseProtocol {
3454 dependency_type: Some(fdecl::DependencyType::Strong),
3455 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3456 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3457 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3458 ..Default::default()
3459 }),
3460 fdecl::Use::Service(fdecl::UseService {
3461 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3462 source_name: Some("service_name".to_string()),
3463 target_path: Some("/svc/service_name".to_string()),
3464 dependency_type: Some(fdecl::DependencyType::Strong),
3465 ..Default::default()
3466 }),
3467 fdecl::Use::Directory(fdecl::UseDirectory {
3468 dependency_type: Some(fdecl::DependencyType::Strong),
3469 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3470 source_name: Some("DirectoryName".to_string()),
3471 target_path: Some("/data/DirectoryName".to_string()),
3472 rights: Some(fio::Operations::CONNECT),
3473 subdir: None,
3474 ..Default::default()
3475 }),
3476 fdecl::Use::Runner(fdecl::UseRunner {
3477 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3478 source_name: Some("RunnerName".to_string()),
3479 ..Default::default()
3480 }),
3481 ]),
3482 ..new_component_decl()
3483 }
3484 },
3485 result = Err(ErrorList::new(vec![
3486 Error::invalid_child(DeclType::UseProtocol, "source", "no-such-child"),
3487 Error::invalid_child(DeclType::UseService, "source", "no-such-child"),
3488 Error::invalid_child(DeclType::UseDirectory, "source", "no-such-child"),
3489 Error::invalid_child(DeclType::UseRunner, "source", "no-such-child"),
3490 ])),
3491 },
3492 test_validate_uses_invalid_capability_from_self => {
3493 input = {
3494 let mut decl = new_component_decl();
3495 decl.uses = Some(vec![
3496 fdecl::Use::Service(fdecl::UseService {
3497 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3498 source_name: Some("fuchsia.some.library.SomeService".into()),
3499 target_path: Some("/svc/foo".into()),
3500 dependency_type: Some(fdecl::DependencyType::Strong),
3501 ..Default::default()
3502 }),
3503 fdecl::Use::Protocol(fdecl::UseProtocol {
3504 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3505 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
3506 target_path: Some("/svc/bar".into()),
3507 dependency_type: Some(fdecl::DependencyType::Strong),
3508 ..Default::default()
3509 }),
3510 fdecl::Use::Directory(fdecl::UseDirectory {
3511 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3512 source_name: Some("dir".into()),
3513 target_path: Some("/assets".into()),
3514 dependency_type: Some(fdecl::DependencyType::Strong),
3515 rights: Some(fio::Operations::CONNECT),
3516 ..Default::default()
3517 }),
3518 fdecl::Use::Runner(fdecl::UseRunner {
3519 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3520 source_name: Some("source_elf".into()),
3521 ..Default::default()
3522 }),
3523 fdecl::Use::Config(fdecl::UseConfiguration {
3524 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3525 source_name: Some("source_config".into()),
3526 target_name: Some("config".into()),
3527 type_: Some(fdecl::ConfigType {
3528 layout: fdecl::ConfigTypeLayout::Bool,
3529 parameters: Some(Vec::new()),
3530 constraints: Vec::new(),
3531 }),
3532 ..Default::default()
3533 }),
3534 fdecl::Use::Protocol(fdecl::UseProtocol {
3535 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3536 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
3537 source_dictionary: Some("dict/inner".into()),
3538 target_path: Some("/svc/baz".into()),
3539 dependency_type: Some(fdecl::DependencyType::Strong),
3540 ..Default::default()
3541 }),
3542 ]);
3543 decl
3544 },
3545 result = Err(ErrorList::new(vec![
3546 Error::invalid_capability(
3547 DeclType::UseService,
3548 "source",
3549 "fuchsia.some.library.SomeService"),
3550 Error::invalid_capability(
3551 DeclType::UseProtocol,
3552 "source",
3553 "fuchsia.some.library.SomeProtocol"),
3554 Error::invalid_capability(DeclType::UseDirectory, "source", "dir"),
3555 Error::invalid_capability(DeclType::UseRunner, "source", "source_elf"),
3556 Error::invalid_capability(DeclType::UseConfiguration, "source", "source_config"),
3557 Error::invalid_capability(DeclType::UseProtocol, "source", "dict"),
3558 ])),
3559 },
3560 test_validate_use_from_child_offer_to_child_strong_cycle => {
3561 input = {
3562 fdecl::Component {
3563 capabilities: Some(vec![
3564 fdecl::Capability::Service(fdecl::Service {
3565 name: Some("a".to_string()),
3566 source_path: Some("/a".to_string()),
3567 ..Default::default()
3568 })]),
3569 uses: Some(vec![
3570 fdecl::Use::Protocol(fdecl::UseProtocol {
3571 dependency_type: Some(fdecl::DependencyType::Strong),
3572 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3573 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3574 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3575 ..Default::default()
3576 }),
3577 fdecl::Use::Service(fdecl::UseService {
3578 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3579 source_name: Some("service_name".to_string()),
3580 target_path: Some("/svc/service_name".to_string()),
3581 dependency_type: Some(fdecl::DependencyType::Strong),
3582 ..Default::default()
3583 }),
3584 fdecl::Use::Directory(fdecl::UseDirectory {
3585 dependency_type: Some(fdecl::DependencyType::Strong),
3586 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3587 source_name: Some("DirectoryName".to_string()),
3588 target_path: Some("/data/DirectoryName".to_string()),
3589 rights: Some(fio::Operations::CONNECT),
3590 subdir: None,
3591 ..Default::default()
3592 }),
3593 ]),
3594 offers: Some(vec![
3595 fdecl::Offer::Service(fdecl::OfferService {
3596 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3597 source_name: Some("a".to_string()),
3598 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
3599 target_name: Some("a".to_string()),
3600 ..Default::default()
3601 })
3602 ]),
3603 children: Some(vec![
3604 fdecl::Child {
3605 name: Some("child".to_string()),
3606 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3607 startup: Some(fdecl::StartupMode::Lazy),
3608 on_terminate: None,
3609 ..Default::default()
3610 }
3611 ]),
3612 ..new_component_decl()
3613 }
3614 },
3615 result = Err(ErrorList::new(vec![
3616 Error::dependency_cycle("{{self -> child child -> self}}"),
3617 ])),
3618 },
3619 test_validate_use_from_child_storage_no_cycle => {
3620 input = {
3621 fdecl::Component {
3622 capabilities: Some(vec![
3623 fdecl::Capability::Storage(fdecl::Storage {
3624 name: Some("cdata".to_string()),
3625 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None } )),
3626 backing_dir: Some("minfs".to_string()),
3627 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
3628 ..Default::default()
3629 }),
3630 fdecl::Capability::Storage(fdecl::Storage {
3631 name: Some("pdata".to_string()),
3632 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
3633 backing_dir: Some("minfs".to_string()),
3634 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
3635 ..Default::default()
3636 }),
3637 ]),
3638 uses: Some(vec![
3639 fdecl::Use::Protocol(fdecl::UseProtocol {
3640 dependency_type: Some(fdecl::DependencyType::Strong),
3641 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child1".to_string(), collection: None})),
3642 source_name: Some("a".to_string()),
3643 target_path: Some("/svc/a".to_string()),
3644 ..Default::default()
3645 }),
3646 ]),
3647 offers: Some(vec![
3648 fdecl::Offer::Storage(fdecl::OfferStorage {
3649 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3650 source_name: Some("cdata".to_string()),
3651 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3652 target_name: Some("cdata".to_string()),
3653 ..Default::default()
3654 }),
3655 fdecl::Offer::Storage(fdecl::OfferStorage {
3656 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3657 source_name: Some("pdata".to_string()),
3658 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3659 target_name: Some("pdata".to_string()),
3660 ..Default::default()
3661 }),
3662 ]),
3663 children: Some(vec![
3664 fdecl::Child {
3665 name: Some("child1".to_string()),
3666 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3667 startup: Some(fdecl::StartupMode::Lazy),
3668 on_terminate: None,
3669 ..Default::default()
3670 },
3671 fdecl::Child {
3672 name: Some("child2".to_string()),
3673 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
3674 startup: Some(fdecl::StartupMode::Lazy),
3675 on_terminate: None,
3676 ..Default::default()
3677 }
3678 ]),
3679 ..new_component_decl()
3680 }
3681 },
3682 result = Ok(()),
3683 },
3684 test_validate_use_from_child_storage_cycle => {
3685 input = {
3686 fdecl::Component {
3687 capabilities: Some(vec![
3688 fdecl::Capability::Storage(fdecl::Storage {
3689 name: Some("data".to_string()),
3690 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
3691 backing_dir: Some("minfs".to_string()),
3692 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
3693 ..Default::default()
3694 }),
3695 ]),
3696 uses: Some(vec![
3697 fdecl::Use::Protocol(fdecl::UseProtocol {
3698 dependency_type: Some(fdecl::DependencyType::Strong),
3699 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3700 source_name: Some("a".to_string()),
3701 target_path: Some("/svc/a".to_string()),
3702 ..Default::default()
3703 }),
3704 ]),
3705 offers: Some(vec![
3706 fdecl::Offer::Storage(fdecl::OfferStorage {
3707 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3708 source_name: Some("data".to_string()),
3709 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
3710 target_name: Some("data".to_string()),
3711 ..Default::default()
3712 }),
3713 ]),
3714 children: Some(vec![
3715 fdecl::Child {
3716 name: Some("child".to_string()),
3717 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3718 startup: Some(fdecl::StartupMode::Lazy),
3719 on_terminate: None,
3720 ..Default::default()
3721 },
3722 ]),
3723 ..new_component_decl()
3724 }
3725 },
3726 result = Err(ErrorList::new(vec![
3727 Error::dependency_cycle("{{self -> capability data -> child child -> self}}"),
3728 ])),
3729 },
3730 test_validate_storage_strong_cycle_between_children => {
3731 input = {
3732 fdecl::Component {
3733 capabilities: Some(vec![
3734 fdecl::Capability::Storage(fdecl::Storage {
3735 name: Some("data".to_string()),
3736 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None } )),
3737 backing_dir: Some("minfs".to_string()),
3738 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
3739 ..Default::default()
3740 })
3741 ]),
3742 offers: Some(vec![
3743 fdecl::Offer::Storage(fdecl::OfferStorage {
3744 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3745 source_name: Some("data".to_string()),
3746 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
3747 target_name: Some("data".to_string()),
3748 ..Default::default()
3749 }),
3750 fdecl::Offer::Service(fdecl::OfferService {
3751 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
3752 source_name: Some("a".to_string()),
3753 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3754 target_name: Some("a".to_string()),
3755 ..Default::default()
3756 }),
3757 ]),
3758 children: Some(vec![
3759 fdecl::Child {
3760 name: Some("child1".to_string()),
3761 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3762 startup: Some(fdecl::StartupMode::Lazy),
3763 on_terminate: None,
3764 ..Default::default()
3765 },
3766 fdecl::Child {
3767 name: Some("child2".to_string()),
3768 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
3769 startup: Some(fdecl::StartupMode::Lazy),
3770 on_terminate: None,
3771 ..Default::default()
3772 }
3773 ]),
3774 ..new_component_decl()
3775 }
3776 },
3777 result = Err(ErrorList::new(vec![
3778 Error::dependency_cycle("{{child child1 -> capability data -> child child2 -> child child1}}"),
3779 ])),
3780 },
3781 test_validate_strong_cycle_between_children_through_environment_debug => {
3782 input = {
3783 fdecl::Component {
3784 environments: Some(vec![
3785 fdecl::Environment {
3786 name: Some("env".to_string()),
3787 extends: Some(fdecl::EnvironmentExtends::Realm),
3788 debug_capabilities: Some(vec![
3789 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
3790 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3791 source_name: Some("fuchsia.foo.Bar".to_string()),
3792 target_name: Some("fuchsia.foo.Bar".to_string()),
3793 ..Default::default()
3794 }),
3795 ]),
3796 ..Default::default()
3797 },
3798 ]),
3799 offers: Some(vec![
3800 fdecl::Offer::Service(fdecl::OfferService {
3801 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
3802 source_name: Some("a".to_string()),
3803 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3804 target_name: Some("a".to_string()),
3805 ..Default::default()
3806 }),
3807 ]),
3808 children: Some(vec![
3809 fdecl::Child {
3810 name: Some("child1".to_string()),
3811 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3812 startup: Some(fdecl::StartupMode::Lazy),
3813 on_terminate: None,
3814 ..Default::default()
3815 },
3816 fdecl::Child {
3817 name: Some("child2".to_string()),
3818 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
3819 startup: Some(fdecl::StartupMode::Lazy),
3820 environment: Some("env".to_string()),
3821 on_terminate: None,
3822 ..Default::default()
3823 }
3824 ]),
3825 ..new_component_decl()
3826 }
3827 },
3828 result = Err(ErrorList::new(vec![
3829 Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}"),
3830 ])),
3831 },
3832 test_validate_strong_cycle_between_children_through_environment_runner => {
3833 input = {
3834 fdecl::Component {
3835 environments: Some(vec![
3836 fdecl::Environment {
3837 name: Some("env".to_string()),
3838 extends: Some(fdecl::EnvironmentExtends::Realm),
3839 runners: Some(vec![
3840 fdecl::RunnerRegistration {
3841 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3842 source_name: Some("coff".to_string()),
3843 target_name: Some("coff".to_string()),
3844 ..Default::default()
3845 }
3846 ]),
3847 ..Default::default()
3848 },
3849 ]),
3850 offers: Some(vec![
3851 fdecl::Offer::Service(fdecl::OfferService {
3852 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
3853 source_name: Some("a".to_string()),
3854 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3855 target_name: Some("a".to_string()),
3856 ..Default::default()
3857 }),
3858 ]),
3859 children: Some(vec![
3860 fdecl::Child {
3861 name: Some("child1".to_string()),
3862 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3863 startup: Some(fdecl::StartupMode::Lazy),
3864 on_terminate: None,
3865 ..Default::default()
3866 },
3867 fdecl::Child {
3868 name: Some("child2".to_string()),
3869 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
3870 startup: Some(fdecl::StartupMode::Lazy),
3871 environment: Some("env".to_string()),
3872 on_terminate: None,
3873 ..Default::default()
3874 }
3875 ]),
3876 ..new_component_decl()
3877 }
3878 },
3879 result = Err(ErrorList::new(vec![
3880 Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}"),
3881 ])),
3882 },
3883 test_validate_strong_cycle_between_children_through_environment_resolver => {
3884 input = {
3885 fdecl::Component {
3886 environments: Some(vec![
3887 fdecl::Environment {
3888 name: Some("env".to_string()),
3889 extends: Some(fdecl::EnvironmentExtends::Realm),
3890 resolvers: Some(vec![
3891 fdecl::ResolverRegistration {
3892 resolver: Some("gopher".to_string()),
3893 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3894 scheme: Some("gopher".to_string()),
3895 ..Default::default()
3896 }
3897 ]),
3898 ..Default::default()
3899 },
3900 ]),
3901 offers: Some(vec![
3902 fdecl::Offer::Service(fdecl::OfferService {
3903 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
3904 source_name: Some("a".to_string()),
3905 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3906 target_name: Some("a".to_string()),
3907 ..Default::default()
3908 }),
3909 ]),
3910 children: Some(vec![
3911 fdecl::Child {
3912 name: Some("child1".to_string()),
3913 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3914 startup: Some(fdecl::StartupMode::Lazy),
3915 on_terminate: None,
3916 ..Default::default()
3917 },
3918 fdecl::Child {
3919 name: Some("child2".to_string()),
3920 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
3921 startup: Some(fdecl::StartupMode::Lazy),
3922 environment: Some("env".to_string()),
3923 on_terminate: None,
3924 ..Default::default()
3925 }
3926 ]),
3927 ..new_component_decl()
3928 }
3929 },
3930 result = Err(ErrorList::new(vec![
3931 Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}"),
3932 ])),
3933 },
3934 test_validate_strong_cycle_between_self_and_two_children => {
3935 input = {
3936 fdecl::Component {
3937 capabilities: Some(vec![
3938 fdecl::Capability::Protocol(fdecl::Protocol {
3939 name: Some("fuchsia.foo.Bar".to_string()),
3940 source_path: Some("/svc/fuchsia.foo.Bar".to_string()),
3941 ..Default::default()
3942 })
3943 ]),
3944 offers: Some(vec![
3945 fdecl::Offer::Protocol(fdecl::OfferProtocol {
3946 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3947 source_name: Some("fuchsia.foo.Bar".to_string()),
3948 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3949 target_name: Some("fuchsia.foo.Bar".to_string()),
3950 dependency_type: Some(fdecl::DependencyType::Strong),
3951 ..Default::default()
3952 }),
3953 fdecl::Offer::Protocol(fdecl::OfferProtocol {
3954 source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3955 source_name: Some("fuchsia.bar.Baz".to_string()),
3956 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
3957 target_name: Some("fuchsia.bar.Baz".to_string()),
3958 dependency_type: Some(fdecl::DependencyType::Strong),
3959 ..Default::default()
3960 }),
3961 ]),
3962 uses: Some(vec![
3963 fdecl::Use::Protocol(fdecl::UseProtocol {
3964 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child2".to_string(), collection: None})),
3965 source_name: Some("fuchsia.baz.Foo".to_string()),
3966 target_path: Some("/svc/fuchsia.baz.Foo".to_string()),
3967 dependency_type: Some(fdecl::DependencyType::Strong),
3968 ..Default::default()
3969 }),
3970 ]),
3971 children: Some(vec![
3972 fdecl::Child {
3973 name: Some("child1".to_string()),
3974 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3975 startup: Some(fdecl::StartupMode::Lazy),
3976 on_terminate: None,
3977 ..Default::default()
3978 },
3979 fdecl::Child {
3980 name: Some("child2".to_string()),
3981 url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
3982 startup: Some(fdecl::StartupMode::Lazy),
3983 on_terminate: None,
3984 ..Default::default()
3985 }
3986 ]),
3987 ..new_component_decl()
3988 }
3989 },
3990 result = Err(ErrorList::new(vec![
3991 Error::dependency_cycle("{{self -> child child1 -> child child2 -> self}}"),
3992 ])),
3993 },
3994 test_validate_strong_cycle_with_self_storage => {
3995 input = {
3996 fdecl::Component {
3997 capabilities: Some(vec![
3998 fdecl::Capability::Storage(fdecl::Storage {
3999 name: Some("data".to_string()),
4000 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4001 backing_dir: Some("minfs".to_string()),
4002 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4003 ..Default::default()
4004 }),
4005 fdecl::Capability::Directory(fdecl::Directory {
4006 name: Some("minfs".to_string()),
4007 source_path: Some("/minfs".to_string()),
4008 rights: Some(fio::RW_STAR_DIR),
4009 ..Default::default()
4010 }),
4011 ]),
4012 offers: Some(vec![
4013 fdecl::Offer::Storage(fdecl::OfferStorage {
4014 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4015 source_name: Some("data".to_string()),
4016 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4017 target_name: Some("data".to_string()),
4018 ..Default::default()
4019 }),
4020 ]),
4021 uses: Some(vec![
4022 fdecl::Use::Protocol(fdecl::UseProtocol {
4023 dependency_type: Some(fdecl::DependencyType::Strong),
4024 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4025 source_name: Some("fuchsia.foo.Bar".to_string()),
4026 target_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4027 ..Default::default()
4028 }),
4029 ]),
4030 children: Some(vec![
4031 fdecl::Child {
4032 name: Some("child".to_string()),
4033 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4034 startup: Some(fdecl::StartupMode::Lazy),
4035 ..Default::default()
4036 },
4037 ]),
4038 ..new_component_decl()
4039 }
4040 },
4041 result = Err(ErrorList::new(vec![
4042 Error::dependency_cycle("{{self -> capability data -> child child -> self}}"),
4043 ])),
4044 },
4045 test_validate_strong_cycle_with_self_storage_admin_protocol => {
4046 input = {
4047 fdecl::Component {
4048 capabilities: Some(vec![
4049 fdecl::Capability::Storage(fdecl::Storage {
4050 name: Some("data".to_string()),
4051 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4052 backing_dir: Some("minfs".to_string()),
4053 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4054 ..Default::default()
4055 }),
4056 fdecl::Capability::Directory(fdecl::Directory {
4057 name: Some("minfs".to_string()),
4058 source_path: Some("/minfs".to_string()),
4059 rights: Some(fio::RW_STAR_DIR),
4060 ..Default::default()
4061 }),
4062 ]),
4063 offers: Some(vec![
4064 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4065 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef { name: "data".to_string() })),
4066 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4067 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4068 target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4069 dependency_type: Some(fdecl::DependencyType::Strong),
4070 ..Default::default()
4071 }),
4072 ]),
4073 uses: Some(vec![
4074 fdecl::Use::Protocol(fdecl::UseProtocol {
4075 dependency_type: Some(fdecl::DependencyType::Strong),
4076 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4077 source_name: Some("fuchsia.foo.Bar".to_string()),
4078 target_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4079 ..Default::default()
4080 }),
4081 ]),
4082 children: Some(vec![
4083 fdecl::Child {
4084 name: Some("child".to_string()),
4085 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4086 startup: Some(fdecl::StartupMode::Lazy),
4087 ..Default::default()
4088 },
4089 ]),
4090 ..new_component_decl()
4091 }
4092 },
4093 result = Err(ErrorList::new(vec![
4094 Error::dependency_cycle("{{self -> capability data -> child child -> self}}"),
4095 ])),
4096 },
4097 test_validate_strong_cycle_with_dictionary => {
4098 input = fdecl::Component {
4099 offers: Some(vec![
4100 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
4101 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4102 source_name: Some("dict".into()),
4103 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4104 name: "a".into(),
4105 collection: None,
4106 })),
4107 target_name: Some("dict".into()),
4108 dependency_type: Some(fdecl::DependencyType::Strong),
4109 ..Default::default()
4110 }),
4111 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4112 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4113 name: "b".into(),
4114 collection: None,
4115 })),
4116 source_name: Some("1".into()),
4117 target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4118 name: "dict".into(),
4119 })),
4120 target_name: Some("1".into()),
4121 dependency_type: Some(fdecl::DependencyType::Strong),
4122 ..Default::default()
4123 }),
4124 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4125 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4126 name: "a".into(),
4127 collection: None,
4128 })),
4129 source_name: Some("2".into()),
4130 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4131 name: "b".into(),
4132 collection: None,
4133 })),
4134 target_name: Some("2".into()),
4135 dependency_type: Some(fdecl::DependencyType::Strong),
4136 ..Default::default()
4137 }),
4138 ]),
4139 children: Some(vec![
4140 fdecl::Child {
4141 name: Some("a".into()),
4142 url: Some("fuchsia-pkg://child".into()),
4143 startup: Some(fdecl::StartupMode::Lazy),
4144 ..Default::default()
4145 },
4146 fdecl::Child {
4147 name: Some("b".into()),
4148 url: Some("fuchsia-pkg://child".into()),
4149 startup: Some(fdecl::StartupMode::Lazy),
4150 ..Default::default()
4151 },
4152 ]),
4153 capabilities: Some(vec![
4154 fdecl::Capability::Dictionary(fdecl::Dictionary {
4155 name: Some("dict".into()),
4156 ..Default::default()
4157 }),
4158 ]),
4159 ..Default::default()
4160 },
4161 result = Err(ErrorList::new(vec![
4162 Error::dependency_cycle("{{child a -> child b -> capability dict -> child a}}"),
4163 ])),
4164 },
4165 test_validate_strong_cycle_with_dictionary_indirect => {
4166 input = fdecl::Component {
4167 offers: Some(vec![
4168 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4169 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4170 source_name: Some("3".into()),
4171 source_dictionary: Some("dict".into()),
4172 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4173 name: "a".into(),
4174 collection: None,
4175 })),
4176 target_name: Some("3".into()),
4177 dependency_type: Some(fdecl::DependencyType::Strong),
4178 ..Default::default()
4179 }),
4180 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4181 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4182 name: "b".into(),
4183 collection: None,
4184 })),
4185 source_name: Some("1".into()),
4186 target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4187 name: "dict".into(),
4188 })),
4189 target_name: Some("1".into()),
4190 dependency_type: Some(fdecl::DependencyType::Strong),
4191 ..Default::default()
4192 }),
4193 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4194 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4195 name: "a".into(),
4196 collection: None,
4197 })),
4198 source_name: Some("2".into()),
4199 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4200 name: "b".into(),
4201 collection: None,
4202 })),
4203 target_name: Some("2".into()),
4204 dependency_type: Some(fdecl::DependencyType::Strong),
4205 ..Default::default()
4206 }),
4207 ]),
4208 children: Some(vec![
4209 fdecl::Child {
4210 name: Some("a".into()),
4211 url: Some("fuchsia-pkg://child".into()),
4212 startup: Some(fdecl::StartupMode::Lazy),
4213 ..Default::default()
4214 },
4215 fdecl::Child {
4216 name: Some("b".into()),
4217 url: Some("fuchsia-pkg://child".into()),
4218 startup: Some(fdecl::StartupMode::Lazy),
4219 ..Default::default()
4220 },
4221 ]),
4222 capabilities: Some(vec![
4223 fdecl::Capability::Dictionary(fdecl::Dictionary {
4224 name: Some("dict".into()),
4225 ..Default::default()
4226 }),
4227 ]),
4228 ..Default::default()
4229 },
4230 result = Err(ErrorList::new(vec![
4231 Error::dependency_cycle("{{child a -> child b -> capability dict -> child a}}"),
4232 ])),
4233 },
4234 test_validate_use_dependency_cycle_with_dictionary => {
4235 input = fdecl::Component {
4236 offers: Some(vec![
4237 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
4238 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4239 source_name: Some("dict".into()),
4240 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4241 name: "a".into(),
4242 collection: None,
4243 })),
4244 target_name: Some("dict".into()),
4245 dependency_type: Some(fdecl::DependencyType::Strong),
4246 ..Default::default()
4247 }),
4248 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4249 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4250 source_name: Some("1".into()),
4251 target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4252 name: "dict".into(),
4253 })),
4254 target_name: Some("1".into()),
4255 dependency_type: Some(fdecl::DependencyType::Strong),
4256 ..Default::default()
4257 }),
4258 ]),
4259 children: Some(vec![
4260 fdecl::Child {
4261 name: Some("a".into()),
4262 url: Some("fuchsia-pkg://child".into()),
4263 startup: Some(fdecl::StartupMode::Lazy),
4264 ..Default::default()
4265 },
4266 ]),
4267 uses: Some(vec![
4268 fdecl::Use::Protocol(fdecl::UseProtocol {
4269 dependency_type: Some(fdecl::DependencyType::Strong),
4270 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "a".to_string(), collection: None})),
4271 source_name: Some("2".to_string()),
4272 target_path: Some("/svc/foo".into()),
4273 ..Default::default()
4274 }),
4275 ]),
4276 capabilities: Some(vec![
4277 fdecl::Capability::Dictionary(fdecl::Dictionary {
4278 name: Some("dict".into()),
4279 ..Default::default()
4280 }),
4281 fdecl::Capability::Protocol(fdecl::Protocol {
4282 name: Some("1".to_string()),
4283 source_path: Some("/path".to_string()),
4284 ..Default::default()
4285 }),
4286 ]),
4287 ..Default::default()
4288 },
4289 result = Err(ErrorList::new(vec![
4290 Error::dependency_cycle("{{self -> capability dict -> child a -> self}}"),
4291 ])),
4292 },
4293 test_validate_use_from_child_offer_to_child_weak_cycle => {
4294 input = {
4295 fdecl::Component {
4296 capabilities: Some(vec![
4297 fdecl::Capability::Service(fdecl::Service {
4298 name: Some("a".to_string()),
4299 source_path: Some("/a".to_string()),
4300 ..Default::default()
4301 })]),
4302 uses: Some(vec![
4303 fdecl::Use::Protocol(fdecl::UseProtocol {
4304 dependency_type: Some(fdecl::DependencyType::Weak),
4305 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4306 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4307 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
4308 ..Default::default()
4309 }),
4310 fdecl::Use::Service(fdecl::UseService {
4311 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4312 source_name: Some("service_name".to_string()),
4313 target_path: Some("/svc/service_name".to_string()),
4314 dependency_type: Some(fdecl::DependencyType::Weak),
4315 ..Default::default()
4316 }),
4317 fdecl::Use::Directory(fdecl::UseDirectory {
4318 dependency_type: Some(fdecl::DependencyType::Weak),
4319 source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4320 source_name: Some("DirectoryName".to_string()),
4321 target_path: Some("/data/DirectoryName".to_string()),
4322 rights: Some(fio::Operations::CONNECT),
4323 subdir: None,
4324 ..Default::default()
4325 }),
4326 ]),
4327 offers: Some(vec![
4328 fdecl::Offer::Service(fdecl::OfferService {
4329 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4330 source_name: Some("a".to_string()),
4331 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4332 target_name: Some("a".to_string()),
4333 ..Default::default()
4334 })
4335 ]),
4336 children: Some(vec![
4337 fdecl::Child {
4338 name: Some("child".to_string()),
4339 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4340 startup: Some(fdecl::StartupMode::Lazy),
4341 on_terminate: None,
4342 ..Default::default()
4343 }
4344 ]),
4345 ..new_component_decl()
4346 }
4347 },
4348 result = Ok(()),
4349 },
4350 test_validate_expose_from_self_to_framework_and_parent => {
4351 input = {
4352 fdecl::Component {
4353 capabilities: Some(vec![
4354 fdecl::Capability::Protocol(fdecl::Protocol {
4355 name: Some("a".to_string()),
4356 source_path: Some("/a".to_string()),
4357 ..Default::default()
4358 }),
4359 ]),
4360 exposes: Some(vec![
4361 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
4362 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4363 source_name: Some("a".to_string()),
4364 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4365 target_name: Some("a".to_string()),
4366 ..Default::default()
4367 }),
4368 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
4369 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4370 source_name: Some("a".to_string()),
4371 target: Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})),
4372 target_name: Some("a".to_string()),
4373 ..Default::default()
4374 }),
4375 ]),
4376 ..new_component_decl()
4377 }
4378 },
4379 result = Ok(()),
4380 },
4381 test_validate_use_from_not_child_weak => {
4382 input = {
4383 fdecl::Component {
4384 uses: Some(vec![
4385 fdecl::Use::Protocol(fdecl::UseProtocol {
4386 dependency_type: Some(fdecl::DependencyType::Weak),
4387 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4388 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4389 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
4390 ..Default::default()
4391 }),
4392 ]),
4393 ..new_component_decl()
4394 }
4395 },
4396 result = Err(ErrorList::new(vec![
4397 Error::invalid_field(DeclType::UseProtocol, "dependency_type"),
4398 ])),
4399 },
4400 test_validate_event_stream_offer_valid_decls => {
4401 input = {
4402 let mut decl = new_component_decl();
4403 decl.offers = Some(vec![
4404 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4405 source_name: Some("stopped".to_string()),
4406 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4407 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4408 target_name: Some("stopped".to_string()),
4409 ..Default::default()
4410 }),
4411 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4412 source_name: Some("started".to_string()),
4413 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4414 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4415 target_name: Some("started".to_string()),
4416 ..Default::default()
4417 }),
4418 ]);
4419 decl.children = Some(vec![fdecl::Child{
4420 name: Some("test".to_string()),
4421 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4422 startup: Some(fdecl::StartupMode::Lazy),
4423 on_terminate: None,
4424 environment: None,
4425 ..Default::default()
4426 },
4427 fdecl::Child{
4428 name: Some("test2".to_string()),
4429 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4430 startup: Some(fdecl::StartupMode::Lazy),
4431 on_terminate: None,
4432 environment: None,
4433 ..Default::default()
4434 }
4435 ]);
4436 decl
4437 },
4438 result = Ok(()),
4439 },
4440 test_validate_event_stream_offer_to_framework_invalid => {
4441 input = {
4442 let mut decl = new_component_decl();
4443 decl.offers = Some(vec![
4444 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4445 source_name: Some("stopped".to_string()),
4446 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4447 target: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
4448 target_name: Some("stopped".to_string()),
4449 ..Default::default()
4450 }),
4451 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4452 source_name: Some("started".to_string()),
4453 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4454 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4455 target_name: Some("started".to_string()),
4456 ..Default::default()
4457 }),
4458 ]);
4459 decl.children = Some(vec![fdecl::Child{
4460 name: Some("test".to_string()),
4461 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4462 startup: Some(fdecl::StartupMode::Lazy),
4463 on_terminate: None,
4464 environment: None,
4465 ..Default::default()
4466 },
4467 fdecl::Child{
4468 name: Some("test2".to_string()),
4469 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4470 startup: Some(fdecl::StartupMode::Lazy),
4471 on_terminate: None,
4472 environment: None,
4473 ..Default::default()
4474 }
4475 ]);
4476 decl
4477 },
4478 result = Err(ErrorList::new(vec![
4479 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "target".to_string() }),
4480 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "target".to_string() }),
4481 ])),
4482 },
4483 test_validate_event_stream_offer_to_scope_zero_length_invalid => {
4484 input = {
4485 let mut decl = new_component_decl();
4486 decl.offers = Some(vec![
4487 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4488 source_name: Some("started".to_string()),
4489 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4490 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4491 scope: Some(vec![]),
4492 target_name: Some("started".to_string()),
4493 ..Default::default()
4494 }),
4495 ]);
4496 decl.children = Some(vec![fdecl::Child{
4497 name: Some("test".to_string()),
4498 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4499 startup: Some(fdecl::StartupMode::Lazy),
4500 on_terminate: None,
4501 environment: None,
4502 ..Default::default()
4503 },
4504 fdecl::Child{
4505 name: Some("test2".to_string()),
4506 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4507 startup: Some(fdecl::StartupMode::Lazy),
4508 on_terminate: None,
4509 environment: None,
4510 ..Default::default()
4511 }
4512 ]);
4513 decl
4514 },
4515 result = Err(ErrorList::new(vec![
4516 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "scope".to_string() }),
4517 ])),
4518 },
4519 test_validate_event_stream_offer_to_scope_framework_invalid => {
4520 input = {
4521 let mut decl = new_component_decl();
4522 decl.offers = Some(vec![
4523 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4524 source_name: Some("started".to_string()),
4525 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4526 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4527 scope: Some(vec![fdecl::Ref::Framework(fdecl::FrameworkRef{})]),
4528 target_name: Some("started".to_string()),
4529 ..Default::default()
4530 }),
4531 ]);
4532 decl.children = Some(vec![fdecl::Child{
4533 name: Some("test".to_string()),
4534 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4535 startup: Some(fdecl::StartupMode::Lazy),
4536 on_terminate: None,
4537 environment: None,
4538 ..Default::default()
4539 },
4540 fdecl::Child{
4541 name: Some("test2".to_string()),
4542 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4543 startup: Some(fdecl::StartupMode::Lazy),
4544 on_terminate: None,
4545 environment: None,
4546 ..Default::default()
4547 }
4548 ]);
4549 decl
4550 },
4551 result = Err(ErrorList::new(vec![
4552 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "scope".to_string() }),
4553 ])),
4554 },
4555 test_validate_event_stream_offer_to_scope_valid => {
4556 input = {
4557 let mut decl = new_component_decl();
4558 decl.offers = Some(vec![
4559 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4560 source_name: Some("started".to_string()),
4561 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4562 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4563 scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
4564 target_name: Some("started".to_string()),
4565 ..Default::default()
4566 }),
4567 ]);
4568 decl.children = Some(vec![fdecl::Child{
4569 name: Some("test".to_string()),
4570 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4571 startup: Some(fdecl::StartupMode::Lazy),
4572 on_terminate: None,
4573 environment: None,
4574 ..Default::default()
4575 },
4576 fdecl::Child{
4577 name: Some("test2".to_string()),
4578 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4579 startup: Some(fdecl::StartupMode::Lazy),
4580 on_terminate: None,
4581 environment: None,
4582 ..Default::default()
4583 }
4584 ]);
4585 decl
4586 },
4587 result = Ok(()),
4588 },
4589 test_validate_event_stream_offer_to_scope_with_capability_requested => {
4590 input = {
4591 let mut decl = new_component_decl();
4592 decl.offers = Some(vec![
4593 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4594 source_name: Some("capability_requested".to_string()),
4595 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4596 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4597 scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
4598 target_name: Some("started".to_string()),
4599 ..Default::default()
4600 }),
4601 ]);
4602 decl.children = Some(vec![fdecl::Child{
4603 name: Some("test".to_string()),
4604 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4605 startup: Some(fdecl::StartupMode::Lazy),
4606 on_terminate: None,
4607 environment: None,
4608 ..Default::default()
4609 },
4610 fdecl::Child{
4611 name: Some("test2".to_string()),
4612 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4613 startup: Some(fdecl::StartupMode::Lazy),
4614 on_terminate: None,
4615 environment: None,
4616 ..Default::default()
4617 }
4618 ]);
4619 decl
4620 },
4621 result = Ok(()),
4622 },
4623 test_validate_event_stream_offer_with_no_source_name_invalid => {
4624 input = {
4625 let mut decl = new_component_decl();
4626 decl.offers = Some(vec![
4627 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4628 source_name: None,
4629 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4630 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4631 scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
4632 target_name: Some("started".to_string()),
4633 ..Default::default()
4634 }),
4635 ]);
4636 decl.children = Some(vec![fdecl::Child{
4637 name: Some("test".to_string()),
4638 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4639 startup: Some(fdecl::StartupMode::Lazy),
4640 on_terminate: None,
4641 environment: None,
4642 ..Default::default()
4643 },
4644 fdecl::Child{
4645 name: Some("test2".to_string()),
4646 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4647 startup: Some(fdecl::StartupMode::Lazy),
4648 on_terminate: None,
4649 environment: None,
4650 ..Default::default()
4651 }
4652 ]);
4653 decl
4654 },
4655 result = Err(ErrorList::new(vec![
4656 Error::MissingField(DeclField { decl: DeclType::OfferEventStream, field: "source_name".to_string() }),
4657 ])),
4658 },
4659 test_validate_event_stream_offer_invalid_source => {
4660 input = {
4661 let mut decl = new_component_decl();
4662 decl.offers = Some(vec![
4663 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4664 source_name: Some("stopped".to_string()),
4665 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
4666 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4667 target_name: Some("stopped".to_string()),
4668 ..Default::default()
4669 }),
4670 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4671 source_name: Some("started".to_string()),
4672 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4673 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4674 target_name: Some("started".to_string()),
4675 ..Default::default()
4676 }),
4677 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4678 source_name: Some("capability_requested".to_string()),
4679 source: Some(fdecl::Ref::Debug(fdecl::DebugRef{})),
4680 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4681 target_name: Some("capability_requested".to_string()),
4682 ..Default::default()
4683 }),
4684 ]);
4685 decl.children = Some(vec![fdecl::Child{
4686 name: Some("test".to_string()),
4687 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4688 startup: Some(fdecl::StartupMode::Lazy),
4689 on_terminate: None,
4690 environment: None,
4691 ..Default::default()
4692 },
4693 fdecl::Child{
4694 name: Some("test2".to_string()),
4695 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4696 startup: Some(fdecl::StartupMode::Lazy),
4697 on_terminate: None,
4698 environment: None,
4699 ..Default::default()
4700 }
4701 ]);
4702 decl
4703 },
4704 result = Err(ErrorList::new(vec![
4705 Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "source".to_string() }),
4706 ])),
4707 },
4708
4709 test_validate_event_stream_offer_missing_source => {
4710 input = {
4711 let mut decl = new_component_decl();
4712 decl.offers = Some(vec![
4713 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4714 source_name: Some("stopped".to_string()),
4715 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
4716 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4717 target_name: Some("stopped".to_string()),
4718 ..Default::default()
4719 }),
4720 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4721 source_name: Some("started".to_string()),
4722 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4723 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4724 target_name: Some("started".to_string()),
4725 ..Default::default()
4726 }),
4727 fdecl::Offer::EventStream(fdecl::OfferEventStream {
4728 source_name: Some("capability_requested".to_string()),
4729 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4730 target_name: Some("capability_requested".to_string()),
4731 ..Default::default()
4732 }),
4733 ]);
4734 decl.children = Some(vec![fdecl::Child{
4735 name: Some("test".to_string()),
4736 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4737 startup: Some(fdecl::StartupMode::Lazy),
4738 on_terminate: None,
4739 environment: None,
4740 ..Default::default()
4741 },
4742 fdecl::Child{
4743 name: Some("test2".to_string()),
4744 url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4745 startup: Some(fdecl::StartupMode::Lazy),
4746 on_terminate: None,
4747 environment: None,
4748 ..Default::default()
4749 }
4750 ]);
4751 decl
4752 },
4753 result = Err(ErrorList::new(vec![
4754 Error::MissingField(DeclField { decl: DeclType::OfferEventStream, field: "source".to_string() }),
4755 ])),
4756 },
4757 test_validate_event_stream_must_have_target_path => {
4758 input = {
4759 let mut decl = new_component_decl();
4760 decl.uses = Some(vec![
4761 fdecl::Use::EventStream(fdecl::UseEventStream {
4762 source_name: Some("bar".to_string()),
4763 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4764 ..Default::default()
4765 }),
4766 ]);
4767 decl
4768 },
4769 result = Err(ErrorList::new(vec![
4770 Error::MissingField(DeclField { decl: DeclType::UseEventStream, field: "target_path".to_string() })
4771 ])),
4772 },
4773 test_validate_event_stream_must_have_source_names => {
4774 input = {
4775 let mut decl = new_component_decl();
4776 decl.uses = Some(vec![
4777 fdecl::Use::EventStream(fdecl::UseEventStream {
4778 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4779 target_path: Some("/svc/something".to_string()),
4780 ..Default::default()
4781 }),
4782 ]);
4783 decl
4784 },
4785 result = Err(ErrorList::new(vec![
4786 Error::MissingField(DeclField { decl: DeclType::UseEventStream, field: "source_name".to_string() })
4787 ])),
4788 },
4789 test_validate_event_stream_scope_must_be_child_or_collection => {
4790 input = {
4791 let mut decl = new_component_decl();
4792 decl.uses = Some(vec![
4793 fdecl::Use::EventStream(fdecl::UseEventStream {
4794 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4795 target_path: Some("/svc/something".to_string()),
4796 source_name: Some("some_source".to_string()),
4797 scope: Some(vec![fdecl::Ref::Framework(fdecl::FrameworkRef{})]),
4798 ..Default::default()
4799 }),
4800 ]);
4801 decl
4802 },
4803 result = Err(ErrorList::new(vec![
4804 Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "scope".to_string() })
4805 ])),
4806 },
4807 test_validate_event_stream_source_must_be_parent_or_child => {
4808 input = {
4809 let mut decl = new_component_decl();
4810 decl.uses = Some(vec![
4811 fdecl::Use::EventStream(fdecl::UseEventStream {
4812 source: Some(fdecl::Ref::Debug(fdecl::DebugRef{})),
4813 target_path: Some("/svc/something".to_string()),
4814 source_name: Some("some_source".to_string()),
4815 scope: Some(vec![]),
4816 ..Default::default()
4817 }),
4818 fdecl::Use::EventStream(fdecl::UseEventStream {
4819 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
4820 target_path: Some("/svc/something_else".to_string()),
4821 source_name: Some("some_source".to_string()),
4822 scope: Some(vec![]),
4823 ..Default::default()
4824 }),
4825 fdecl::Use::EventStream(fdecl::UseEventStream {
4826 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4827 target_path: Some("/svc/yet_something_else".to_string()),
4828 source_name: Some("some_source".to_string()),
4829 scope: Some(vec![]),
4830 ..Default::default()
4831 }),
4832 ]);
4833 decl
4834 },
4835 result = Err(ErrorList::new(vec![
4836 Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() }),
4837 Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() }),
4838 Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() })
4839 ])),
4840 },
4841 test_validate_no_runner => {
4842 input = {
4843 let mut decl = new_component_decl();
4844 decl.program = Some(fdecl::Program {
4845 runner: None,
4846 info: Some(fdata::Dictionary {
4847 entries: None,
4848 ..Default::default()
4849 }),
4850 ..Default::default()
4851 });
4852 decl
4853 },
4854 result = Err(ErrorList::new(vec![
4855 Error::MissingRunner,
4856 ])),
4857 },
4858 test_validate_uses_runner => {
4859 input = {
4860 let mut decl = new_component_decl();
4861 decl.program = Some(fdecl::Program {
4862 runner: None,
4863 info: Some(fdata::Dictionary {
4864 entries: None,
4865 ..Default::default()
4866 }),
4867 ..Default::default()
4868 });
4869 decl.uses = Some(vec![
4870 fdecl::Use::Runner(fdecl::UseRunner {
4871 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4872 source_name: Some("runner".to_string()),
4873 ..Default::default()
4874 }),
4875 ]);
4876 decl
4877 },
4878 result = Ok(()),
4879 },
4880 test_validate_program_and_uses_runner_match => {
4881 input = {
4882 let mut decl = new_component_decl();
4883 decl.program = Some(fdecl::Program {
4884 runner: Some("runner".to_string()),
4885 info: Some(fdata::Dictionary {
4886 entries: None,
4887 ..Default::default()
4888 }),
4889 ..Default::default()
4890 });
4891 decl.uses = Some(vec![
4892 fdecl::Use::Runner(fdecl::UseRunner {
4893 source: Some(fdecl::Ref::Environment(fdecl::EnvironmentRef {})),
4894 source_name: Some("runner".to_string()),
4895 ..Default::default()
4896 }),
4897 ]);
4898 decl
4899 },
4900 result = Ok(()),
4901 },
4902 test_validate_runner_names_conflict => {
4903 input = {
4904 let mut decl = new_component_decl();
4905 decl.program = Some(fdecl::Program {
4906 runner: Some("runner".to_string()),
4907 info: Some(fdata::Dictionary {
4908 entries: None,
4909 ..Default::default()
4910 }),
4911 ..Default::default()
4912 });
4913 decl.uses = Some(vec![
4914 fdecl::Use::Runner(fdecl::UseRunner {
4915 source: Some(fdecl::Ref::Environment(fdecl::EnvironmentRef {})),
4916 source_name: Some("other.runner".to_string()),
4917 ..Default::default()
4918 }),
4919 ]);
4920 decl
4921 },
4922 result = Err(ErrorList::new(vec![
4923 Error::ConflictingRunners,
4924 ])),
4925 },
4926 test_validate_uses_runner_not_environement => {
4927 input = {
4928 let mut decl = new_component_decl();
4929 decl.program = Some(fdecl::Program {
4930 runner: Some("runner".to_string()),
4931 info: Some(fdata::Dictionary {
4932 entries: None,
4933 ..Default::default()
4934 }),
4935 ..Default::default()
4936 });
4937 decl.uses = Some(vec![
4938 fdecl::Use::Runner(fdecl::UseRunner {
4939 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4940 source_name: Some("runner".to_string()),
4941 ..Default::default()
4942 }),
4943 ]);
4944 decl
4945 },
4946 result = Err(ErrorList::new(vec![
4947 Error::ConflictingRunners,
4948 ])),
4949 },
4950 test_validate_uses_long_identifiers => {
4951 input = {
4952 let mut decl = new_component_decl();
4953 decl.program = Some(fdecl::Program {
4954 runner: Some("elf".to_string()),
4955 info: Some(fdata::Dictionary {
4956 entries: None,
4957 ..Default::default()
4958 }),
4959 ..Default::default()
4960 });
4961 decl.uses = Some(vec![
4962 fdecl::Use::Service(fdecl::UseService {
4963 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4964 source_name: Some(format!("{}", "a".repeat(256))),
4965 target_path: Some("/a".repeat(2048)),
4966 dependency_type: Some(fdecl::DependencyType::Strong),
4967 ..Default::default()
4968 }),
4969 fdecl::Use::Protocol(fdecl::UseProtocol {
4970 dependency_type: Some(fdecl::DependencyType::Strong),
4971 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4972 source_name: Some(format!("{}", "a".repeat(256))),
4973 target_path: Some("/b".repeat(2048)),
4974 ..Default::default()
4975 }),
4976 fdecl::Use::Directory(fdecl::UseDirectory {
4977 dependency_type: Some(fdecl::DependencyType::Strong),
4978 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4979 source_name: Some(format!("{}", "a".repeat(256))),
4980 target_path: Some("/c".repeat(2048)),
4981 rights: Some(fio::Operations::CONNECT),
4982 subdir: None,
4983 ..Default::default()
4984 }),
4985 fdecl::Use::Storage(fdecl::UseStorage {
4986 source_name: Some("cache".to_string()),
4987 target_path: Some("/d".repeat(2048)),
4988 ..Default::default()
4989 }),
4990 ]);
4991 decl
4992 },
4993 result = Err(ErrorList::new(vec![
4994 Error::field_too_long(DeclType::UseService, "source_name"),
4995 Error::field_too_long(DeclType::UseService, "target_path"),
4996 Error::field_too_long(DeclType::UseProtocol, "source_name"),
4997 Error::field_too_long(DeclType::UseProtocol, "target_path"),
4998 Error::field_too_long(DeclType::UseDirectory, "source_name"),
4999 Error::field_too_long(DeclType::UseDirectory, "target_path"),
5000 Error::field_too_long(DeclType::UseStorage, "target_path"),
5001 ])),
5002 },
5003 test_validate_conflicting_paths => {
5004 input = {
5005 let mut decl = new_component_decl();
5006 decl.uses = Some(vec![
5007 fdecl::Use::Service(fdecl::UseService {
5008 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5009 source_name: Some("foo".to_string()),
5010 target_path: Some("/bar".to_string()),
5011 dependency_type: Some(fdecl::DependencyType::Strong),
5012 ..Default::default()
5013 }),
5014 fdecl::Use::Protocol(fdecl::UseProtocol {
5015 dependency_type: Some(fdecl::DependencyType::Strong),
5016 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5017 source_name: Some("space".to_string()),
5018 target_path: Some("/bar".to_string()),
5019 ..Default::default()
5020 }),
5021 fdecl::Use::Directory(fdecl::UseDirectory {
5022 dependency_type: Some(fdecl::DependencyType::Strong),
5023 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5024 source_name: Some("crow".to_string()),
5025 target_path: Some("/bar".to_string()),
5026 rights: Some(fio::Operations::CONNECT),
5027 subdir: None,
5028 ..Default::default()
5029 }),
5030 ]);
5031 decl
5032 },
5033 result = Err(ErrorList::new(vec![
5034 Error::duplicate_field(DeclType::UseProtocol, "target_path", "/bar"),
5035 Error::duplicate_field(DeclType::UseDirectory, "target_path", "/bar"),
5036 ])),
5037 },
5038 test_validate_exposes_empty => {
5040 input = {
5041 let mut decl = new_component_decl();
5042 decl.exposes = Some(vec![
5043 fdecl::Expose::Service(fdecl::ExposeService {
5044 source: None,
5045 source_name: None,
5046 target_name: None,
5047 target: None,
5048 ..Default::default()
5049 }),
5050 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5051 source: None,
5052 source_name: None,
5053 target_name: None,
5054 target: None,
5055 ..Default::default()
5056 }),
5057 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5058 source: None,
5059 source_name: None,
5060 target_name: None,
5061 target: None,
5062 rights: None,
5063 subdir: None,
5064 ..Default::default()
5065 }),
5066 fdecl::Expose::Runner(fdecl::ExposeRunner {
5067 source: None,
5068 source_name: None,
5069 target: None,
5070 target_name: None,
5071 ..Default::default()
5072 }),
5073 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5074 source: None,
5075 source_name: None,
5076 target: None,
5077 target_name: None,
5078 ..Default::default()
5079 }),
5080 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5081 ..Default::default()
5082 }),
5083 ]);
5084 decl
5085 },
5086 result = Err(ErrorList::new(vec![
5087 Error::missing_field(DeclType::ExposeService, "source"),
5088 Error::missing_field(DeclType::ExposeService, "target"),
5089 Error::missing_field(DeclType::ExposeService, "source_name"),
5090 Error::missing_field(DeclType::ExposeService, "target_name"),
5091 Error::missing_field(DeclType::ExposeProtocol, "source"),
5092 Error::missing_field(DeclType::ExposeProtocol, "target"),
5093 Error::missing_field(DeclType::ExposeProtocol, "source_name"),
5094 Error::missing_field(DeclType::ExposeProtocol, "target_name"),
5095 Error::missing_field(DeclType::ExposeDirectory, "source"),
5096 Error::missing_field(DeclType::ExposeDirectory, "target"),
5097 Error::missing_field(DeclType::ExposeDirectory, "source_name"),
5098 Error::missing_field(DeclType::ExposeDirectory, "target_name"),
5099 Error::missing_field(DeclType::ExposeRunner, "source"),
5100 Error::missing_field(DeclType::ExposeRunner, "target"),
5101 Error::missing_field(DeclType::ExposeRunner, "source_name"),
5102 Error::missing_field(DeclType::ExposeRunner, "target_name"),
5103 Error::missing_field(DeclType::ExposeResolver, "source"),
5104 Error::missing_field(DeclType::ExposeResolver, "target"),
5105 Error::missing_field(DeclType::ExposeResolver, "source_name"),
5106 Error::missing_field(DeclType::ExposeResolver, "target_name"),
5107 Error::missing_field(DeclType::ExposeDictionary, "source"),
5108 Error::missing_field(DeclType::ExposeDictionary, "target"),
5109 Error::missing_field(DeclType::ExposeDictionary, "source_name"),
5110 Error::missing_field(DeclType::ExposeDictionary, "target_name"),
5111 ])),
5112 },
5113 test_validate_exposes_extraneous => {
5114 input = {
5115 let mut decl = new_component_decl();
5116 decl.exposes = Some(vec![
5117 fdecl::Expose::Service(fdecl::ExposeService {
5118 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5119 name: "logger".to_string(),
5120 collection: Some("modular".to_string()),
5121 })),
5122 source_name: Some("logger".to_string()),
5123 target_name: Some("logger".to_string()),
5124 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5125 ..Default::default()
5126 }),
5127 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5128 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5129 name: "logger".to_string(),
5130 collection: Some("modular".to_string()),
5131 })),
5132 source_name: Some("legacy_logger".to_string()),
5133 target_name: Some("legacy_logger".to_string()),
5134 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5135 ..Default::default()
5136 }),
5137 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5138 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5139 name: "netstack".to_string(),
5140 collection: Some("modular".to_string()),
5141 })),
5142 source_name: Some("data".to_string()),
5143 target_name: Some("data".to_string()),
5144 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5145 rights: Some(fio::Operations::CONNECT),
5146 subdir: None,
5147 ..Default::default()
5148 }),
5149 fdecl::Expose::Runner(fdecl::ExposeRunner {
5150 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5151 name: "netstack".to_string(),
5152 collection: Some("modular".to_string()),
5153 })),
5154 source_name: Some("elf".to_string()),
5155 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5156 target_name: Some("elf".to_string()),
5157 ..Default::default()
5158 }),
5159 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5160 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5161 name: "netstack".to_string(),
5162 collection: Some("modular".to_string()),
5163 })),
5164 source_name: Some("pkg".to_string()),
5165 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5166 target_name: Some("pkg".to_string()),
5167 ..Default::default()
5168 }),
5169 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5170 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5171 name: "netstack".to_string(),
5172 collection: Some("modular".to_string()),
5173 })),
5174 source_name: Some("dict".to_string()),
5175 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5176 target_name: Some("dict".to_string()),
5177 ..Default::default()
5178 }),
5179 ]);
5180 decl
5181 },
5182 result = Err(ErrorList::new(vec![
5183 Error::extraneous_field(DeclType::ExposeService, "source.child.collection"),
5184 Error::extraneous_field(DeclType::ExposeProtocol, "source.child.collection"),
5185 Error::extraneous_field(DeclType::ExposeDirectory, "source.child.collection"),
5186 Error::extraneous_field(DeclType::ExposeRunner, "source.child.collection"),
5187 Error::extraneous_field(DeclType::ExposeResolver, "source.child.collection"),
5188 Error::extraneous_field(DeclType::ExposeDictionary, "source.child.collection"),
5189 ])),
5190 },
5191 test_validate_exposes_invalid_identifiers => {
5192 input = {
5193 let mut decl = new_component_decl();
5194 decl.exposes = Some(vec![
5195 fdecl::Expose::Service(fdecl::ExposeService {
5196 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5197 name: "^bad".to_string(),
5198 collection: None,
5199 })),
5200 source_name: Some("foo/".to_string()),
5201 target_name: Some("/".to_string()),
5202 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5203 ..Default::default()
5204 }),
5205 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5206 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5207 name: "^bad".to_string(),
5208 collection: None,
5209 })),
5210 source_name: Some("foo/".to_string()),
5211 target_name: Some("/".to_string()),
5212 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5213 ..Default::default()
5214 }),
5215 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5216 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5217 name: "^bad".to_string(),
5218 collection: None,
5219 })),
5220 source_name: Some("foo/".to_string()),
5221 target_name: Some("/".to_string()),
5222 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5223 rights: Some(fio::Operations::CONNECT),
5224 subdir: Some("/foo".to_string()),
5225 ..Default::default()
5226 }),
5227 fdecl::Expose::Runner(fdecl::ExposeRunner {
5228 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5229 name: "^bad".to_string(),
5230 collection: None,
5231 })),
5232 source_name: Some("/path".to_string()),
5233 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5234 target_name: Some("elf!".to_string()),
5235 ..Default::default()
5236 }),
5237 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5238 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5239 name: "^bad".to_string(),
5240 collection: None,
5241 })),
5242 source_name: Some("/path".to_string()),
5243 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5244 target_name: Some("pkg!".to_string()),
5245 ..Default::default()
5246 }),
5247 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5248 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5249 name: "^bad".to_string(),
5250 collection: None,
5251 })),
5252 source_name: Some("/path".to_string()),
5253 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5254 target_name: Some("pkg!".to_string()),
5255 ..Default::default()
5256 }),
5257 ]);
5258 decl
5259 },
5260 result = Err(ErrorList::new(vec![
5261 Error::invalid_field(DeclType::ExposeService, "source.child.name"),
5262 Error::invalid_field(DeclType::ExposeService, "source_name"),
5263 Error::invalid_field(DeclType::ExposeService, "target_name"),
5264 Error::invalid_field(DeclType::ExposeProtocol, "source.child.name"),
5265 Error::invalid_field(DeclType::ExposeProtocol, "source_name"),
5266 Error::invalid_field(DeclType::ExposeProtocol, "target_name"),
5267 Error::invalid_field(DeclType::ExposeDirectory, "source.child.name"),
5268 Error::invalid_field(DeclType::ExposeDirectory, "source_name"),
5269 Error::invalid_field(DeclType::ExposeDirectory, "target_name"),
5270 Error::invalid_field(DeclType::ExposeDirectory, "subdir"),
5271 Error::invalid_field(DeclType::ExposeRunner, "source.child.name"),
5272 Error::invalid_field(DeclType::ExposeRunner, "source_name"),
5273 Error::invalid_field(DeclType::ExposeRunner, "target_name"),
5274 Error::invalid_field(DeclType::ExposeResolver, "source.child.name"),
5275 Error::invalid_field(DeclType::ExposeResolver, "source_name"),
5276 Error::invalid_field(DeclType::ExposeResolver, "target_name"),
5277 Error::invalid_field(DeclType::ExposeDictionary, "source.child.name"),
5278 Error::invalid_field(DeclType::ExposeDictionary, "source_name"),
5279 Error::invalid_field(DeclType::ExposeDictionary, "target_name"),
5280 ])),
5281 },
5282 test_validate_exposes_invalid_source_target => {
5283 input = {
5284 let mut decl = new_component_decl();
5285 decl.children = Some(vec![fdecl::Child{
5286 name: Some("logger".to_string()),
5287 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5288 startup: Some(fdecl::StartupMode::Lazy),
5289 on_terminate: None,
5290 environment: None,
5291 ..Default::default()
5292 }]);
5293 decl.exposes = Some(vec![
5294 fdecl::Expose::Service(fdecl::ExposeService {
5295 source: None,
5296 source_name: Some("a".to_string()),
5297 target_name: Some("b".to_string()),
5298 target: None,
5299 ..Default::default()
5300 }),
5301 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5302 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5303 source_name: Some("c".to_string()),
5304 target_name: Some("d".to_string()),
5305 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5306 ..Default::default()
5307 }),
5308 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5309 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "z".to_string()})),
5310 source_name: Some("e".to_string()),
5311 target_name: Some("f".to_string()),
5312 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "z".to_string()})),
5313 rights: Some(fio::Operations::CONNECT),
5314 subdir: None,
5315 ..Default::default()
5316 }),
5317 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5318 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5319 source_name: Some("g".to_string()),
5320 target_name: Some("h".to_string()),
5321 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5322 rights: Some(fio::Operations::CONNECT),
5323 subdir: None,
5324 ..Default::default()
5325 }),
5326 fdecl::Expose::Runner(fdecl::ExposeRunner {
5327 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5328 source_name: Some("i".to_string()),
5329 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5330 target_name: Some("j".to_string()),
5331 ..Default::default()
5332 }),
5333 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5334 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5335 source_name: Some("k".to_string()),
5336 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5337 target_name: Some("l".to_string()),
5338 ..Default::default()
5339 }),
5340 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5341 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5342 name: "logger".to_string(),
5343 collection: None,
5344 })),
5345 source_name: Some("m".to_string()),
5346 target_name: Some("n".to_string()),
5347 target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5348 ..Default::default()
5349 }),
5350 ]);
5351 decl
5352 },
5353 result = Err(ErrorList::new(vec![
5354 Error::missing_field(DeclType::ExposeService, "source"),
5355 Error::missing_field(DeclType::ExposeService, "target"),
5356 Error::invalid_field(DeclType::ExposeProtocol, "source"),
5357 Error::invalid_field(DeclType::ExposeProtocol, "target"),
5358 Error::invalid_field(DeclType::ExposeDirectory, "source"),
5359 Error::invalid_field(DeclType::ExposeDirectory, "target"),
5360 Error::invalid_field(DeclType::ExposeDirectory, "source"),
5361 Error::invalid_field(DeclType::ExposeDirectory, "target"),
5362 Error::invalid_field(DeclType::ExposeRunner, "source"),
5363 Error::invalid_field(DeclType::ExposeRunner, "target"),
5364 Error::invalid_field(DeclType::ExposeResolver, "source"),
5365 Error::invalid_field(DeclType::ExposeResolver, "target"),
5366 Error::invalid_field(DeclType::ExposeDictionary, "target"),
5367 ])),
5368 },
5369 test_validate_exposes_invalid_source_collection => {
5370 input = {
5371 let mut decl = new_component_decl();
5372 decl.collections = Some(vec![fdecl::Collection{
5373 name: Some("col".to_string()),
5374 durability: Some(fdecl::Durability::Transient),
5375 allowed_offers: None,
5376 allow_long_names: None,
5377 ..Default::default()
5378 }]);
5379 decl.exposes = Some(vec![
5380 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5381 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
5382 source_name: Some("a".to_string()),
5383 target_name: Some("a".to_string()),
5384 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5385 ..Default::default()
5386 }),
5387 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5388 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5389 source_name: Some("b".to_string()),
5390 target_name: Some("b".to_string()),
5391 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5392 rights: Some(fio::Operations::CONNECT),
5393 subdir: None,
5394 ..Default::default()
5395 }),
5396 fdecl::Expose::Runner(fdecl::ExposeRunner {
5397 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5398 source_name: Some("c".to_string()),
5399 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5400 target_name: Some("c".to_string()),
5401 ..Default::default()
5402 }),
5403 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5404 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5405 source_name: Some("d".to_string()),
5406 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5407 target_name: Some("d".to_string()),
5408 ..Default::default()
5409 }),
5410 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5411 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5412 source_name: Some("e".to_string()),
5413 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5414 target_name: Some("e".to_string()),
5415 ..Default::default()
5416 }),
5417 ]);
5418 decl
5419 },
5420 result = Err(ErrorList::new(vec![
5421 Error::invalid_field(DeclType::ExposeProtocol, "source"),
5422 Error::invalid_field(DeclType::ExposeDirectory, "source"),
5423 Error::invalid_field(DeclType::ExposeRunner, "source"),
5424 Error::invalid_field(DeclType::ExposeResolver, "source"),
5425 Error::invalid_field(DeclType::ExposeDictionary, "source"),
5426 ])),
5427 },
5428 test_validate_exposes_sources_collection => {
5429 input = {
5430 let mut decl = new_component_decl();
5431 decl.collections = Some(vec![
5432 fdecl::Collection {
5433 name: Some("col".to_string()),
5434 durability: Some(fdecl::Durability::Transient),
5435 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
5436 allow_long_names: None,
5437 ..Default::default()
5438 }
5439 ]);
5440 decl.exposes = Some(vec![
5441 fdecl::Expose::Service(fdecl::ExposeService {
5442 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
5443 source_name: Some("a".to_string()),
5444 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5445 target_name: Some("a".to_string()),
5446 ..Default::default()
5447 })
5448 ]);
5449 decl
5450 },
5451 result = Ok(()),
5452 },
5453 test_validate_exposes_long_identifiers => {
5454 input = {
5455 let mut decl = new_component_decl();
5456 decl.exposes = Some(vec![
5457 fdecl::Expose::Service(fdecl::ExposeService {
5458 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5459 name: "b".repeat(256),
5460 collection: None,
5461 })),
5462 source_name: Some(format!("{}", "a".repeat(1025))),
5463 target_name: Some(format!("{}", "b".repeat(1025))),
5464 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5465 ..Default::default()
5466 }),
5467 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5468 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5469 name: "b".repeat(256),
5470 collection: None,
5471 })),
5472 source_name: Some(format!("{}", "a".repeat(256))),
5473 target_name: Some(format!("{}", "b".repeat(256))),
5474 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5475 ..Default::default()
5476 }),
5477 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5478 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5479 name: "b".repeat(256),
5480 collection: None,
5481 })),
5482 source_name: Some(format!("{}", "a".repeat(256))),
5483 target_name: Some(format!("{}", "b".repeat(256))),
5484 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5485 rights: Some(fio::Operations::CONNECT),
5486 subdir: None,
5487 ..Default::default()
5488 }),
5489 fdecl::Expose::Runner(fdecl::ExposeRunner {
5490 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5491 name: "b".repeat(256),
5492 collection: None,
5493 })),
5494 source_name: Some("a".repeat(256)),
5495 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5496 target_name: Some("b".repeat(256)),
5497 ..Default::default()
5498 }),
5499 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5500 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5501 name: "b".repeat(256),
5502 collection: None,
5503 })),
5504 source_name: Some("a".repeat(256)),
5505 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5506 target_name: Some("b".repeat(256)),
5507 ..Default::default()
5508 }),
5509 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5510 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5511 name: "b".repeat(256),
5512 collection: None,
5513 })),
5514 source_name: Some("a".repeat(256)),
5515 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5516 target_name: Some("b".repeat(256)),
5517 ..Default::default()
5518 }),
5519 ]);
5520 decl
5521 },
5522 result = Err(ErrorList::new(vec![
5523 Error::field_too_long(DeclType::ExposeService, "source.child.name"),
5524 Error::field_too_long(DeclType::ExposeService, "source_name"),
5525 Error::field_too_long(DeclType::ExposeService, "target_name"),
5526 Error::field_too_long(DeclType::ExposeProtocol, "source.child.name"),
5527 Error::field_too_long(DeclType::ExposeProtocol, "source_name"),
5528 Error::field_too_long(DeclType::ExposeProtocol, "target_name"),
5529 Error::field_too_long(DeclType::ExposeDirectory, "source.child.name"),
5530 Error::field_too_long(DeclType::ExposeDirectory, "source_name"),
5531 Error::field_too_long(DeclType::ExposeDirectory, "target_name"),
5532 Error::field_too_long(DeclType::ExposeRunner, "source.child.name"),
5533 Error::field_too_long(DeclType::ExposeRunner, "source_name"),
5534 Error::field_too_long(DeclType::ExposeRunner, "target_name"),
5535 Error::field_too_long(DeclType::ExposeResolver, "source.child.name"),
5536 Error::field_too_long(DeclType::ExposeResolver, "source_name"),
5537 Error::field_too_long(DeclType::ExposeResolver, "target_name"),
5538 Error::field_too_long(DeclType::ExposeDictionary, "source.child.name"),
5539 Error::field_too_long(DeclType::ExposeDictionary, "source_name"),
5540 Error::field_too_long(DeclType::ExposeDictionary, "target_name"),
5541 ])),
5542 },
5543 test_validate_exposes_invalid_child => {
5544 input = {
5545 let mut decl = new_component_decl();
5546 decl.exposes = Some(vec![
5547 fdecl::Expose::Service(fdecl::ExposeService {
5548 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5549 name: "netstack".to_string(),
5550 collection: None,
5551 })),
5552 source_name: Some("fuchsia.logger.Log".to_string()),
5553 target_name: Some("fuchsia.logger.Log".to_string()),
5554 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5555 ..Default::default()
5556 }),
5557 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5558 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5559 name: "netstack".to_string(),
5560 collection: None,
5561 })),
5562 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
5563 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
5564 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5565 ..Default::default()
5566 }),
5567 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5568 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5569 name: "netstack".to_string(),
5570 collection: None,
5571 })),
5572 source_name: Some("data".to_string()),
5573 target_name: Some("data".to_string()),
5574 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5575 rights: Some(fio::Operations::CONNECT),
5576 subdir: None,
5577 ..Default::default()
5578 }),
5579 fdecl::Expose::Runner(fdecl::ExposeRunner {
5580 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5581 name: "netstack".to_string(),
5582 collection: None,
5583 })),
5584 source_name: Some("elf".to_string()),
5585 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5586 target_name: Some("elf".to_string()),
5587 ..Default::default()
5588 }),
5589 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5590 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5591 name: "netstack".to_string(),
5592 collection: None,
5593 })),
5594 source_name: Some("pkg".to_string()),
5595 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5596 target_name: Some("pkg".to_string()),
5597 ..Default::default()
5598 }),
5599 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5600 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5601 name: "netstack".to_string(),
5602 collection: None,
5603 })),
5604 source_name: Some("dict".to_string()),
5605 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5606 target_name: Some("dict".to_string()),
5607 ..Default::default()
5608 }),
5609 ]);
5610 decl
5611 },
5612 result = Err(ErrorList::new(vec![
5613 Error::invalid_child(DeclType::ExposeService, "source", "netstack"),
5614 Error::invalid_child(DeclType::ExposeProtocol, "source", "netstack"),
5615 Error::invalid_child(DeclType::ExposeDirectory, "source", "netstack"),
5616 Error::invalid_child(DeclType::ExposeRunner, "source", "netstack"),
5617 Error::invalid_child(DeclType::ExposeResolver, "source", "netstack"),
5618 Error::invalid_child(DeclType::ExposeDictionary, "source", "netstack"),
5619 ])),
5620 },
5621 test_validate_exposes_invalid_source_capability => {
5622 input = {
5623 fdecl::Component {
5624 exposes: Some(vec![
5625 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5626 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
5627 name: "this-storage-doesnt-exist".to_string(),
5628 })),
5629 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
5630 target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
5631 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5632 ..Default::default()
5633 }),
5634 ]),
5635 ..new_component_decl()
5636 }
5637 },
5638 result = Err(ErrorList::new(vec![
5639 Error::invalid_capability(DeclType::ExposeProtocol, "source", "this-storage-doesnt-exist"),
5640 ])),
5641 },
5642 test_validate_exposes_duplicate_target => {
5643 input = {
5644 let mut decl = new_component_decl();
5645 decl.exposes = Some(vec![
5646 fdecl::Expose::Service(fdecl::ExposeService {
5647 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
5648 name: "coll".into(),
5649 })),
5650 source_name: Some("netstack".to_string()),
5651 target_name: Some("fuchsia.net.Stack".to_string()),
5652 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5653 ..Default::default()
5654 }),
5655 fdecl::Expose::Service(fdecl::ExposeService {
5656 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
5657 name: "coll2".into(),
5658 })),
5659 source_name: Some("netstack2".to_string()),
5660 target_name: Some("fuchsia.net.Stack".to_string()),
5661 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5662 ..Default::default()
5663 }),
5664 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5665 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5666 source_name: Some("fonts".to_string()),
5667 target_name: Some("fuchsia.fonts.Provider".to_string()),
5668 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5669 ..Default::default()
5670 }),
5671 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5672 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5673 source_name: Some("fonts2".to_string()),
5674 target_name: Some("fuchsia.fonts.Provider".to_string()),
5675 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5676 ..Default::default()
5677 }),
5678 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5679 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5680 source_name: Some("assets".to_string()),
5681 target_name: Some("stuff".to_string()),
5682 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5683 rights: None,
5684 subdir: None,
5685 ..Default::default()
5686 }),
5687 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5688 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5689 source_name: Some("assets2".to_string()),
5690 target_name: Some("stuff".to_string()),
5691 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5692 rights: None,
5693 subdir: None,
5694 ..Default::default()
5695 }),
5696 fdecl::Expose::Runner(fdecl::ExposeRunner {
5697 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5698 source_name: Some("source_elf".to_string()),
5699 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5700 target_name: Some("elf".to_string()),
5701 ..Default::default()
5702 }),
5703 fdecl::Expose::Runner(fdecl::ExposeRunner {
5704 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5705 source_name: Some("source_elf".to_string()),
5706 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5707 target_name: Some("elf".to_string()),
5708 ..Default::default()
5709 }),
5710 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5711 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5712 source_name: Some("source_pkg".to_string()),
5713 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5714 target_name: Some("pkg".to_string()),
5715 ..Default::default()
5716 }),
5717 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5718 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5719 source_name: Some("source_pkg".to_string()),
5720 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5721 target_name: Some("pkg".to_string()),
5722 ..Default::default()
5723 }),
5724 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5725 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5726 source_name: Some("source_dict".to_string()),
5727 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5728 target_name: Some("dict".to_string()),
5729 ..Default::default()
5730 }),
5731 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5732 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5733 source_name: Some("source_dict".to_string()),
5734 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5735 target_name: Some("dict".to_string()),
5736 ..Default::default()
5737 }),
5738 ]);
5739 decl.collections = Some(vec![
5740 fdecl::Collection {
5741 name: Some("coll".into()),
5742 durability: Some(fdecl::Durability::Transient),
5743 ..Default::default()
5744 },
5745 fdecl::Collection {
5746 name: Some("coll2".into()),
5747 durability: Some(fdecl::Durability::Transient),
5748 ..Default::default()
5749 },
5750 ]);
5751 decl.capabilities = Some(vec![
5752 fdecl::Capability::Service(fdecl::Service {
5753 name: Some("netstack".to_string()),
5754 source_path: Some("/path".to_string()),
5755 ..Default::default()
5756 }),
5757 fdecl::Capability::Service(fdecl::Service {
5758 name: Some("netstack2".to_string()),
5759 source_path: Some("/path".to_string()),
5760 ..Default::default()
5761 }),
5762 fdecl::Capability::Protocol(fdecl::Protocol {
5763 name: Some("fonts".to_string()),
5764 source_path: Some("/path".to_string()),
5765 ..Default::default()
5766 }),
5767 fdecl::Capability::Protocol(fdecl::Protocol {
5768 name: Some("fonts2".to_string()),
5769 source_path: Some("/path".to_string()),
5770 ..Default::default()
5771 }),
5772 fdecl::Capability::Directory(fdecl::Directory {
5773 name: Some("assets".to_string()),
5774 source_path: Some("/path".to_string()),
5775 rights: Some(fio::Operations::CONNECT),
5776 ..Default::default()
5777 }),
5778 fdecl::Capability::Directory(fdecl::Directory {
5779 name: Some("assets2".to_string()),
5780 source_path: Some("/path".to_string()),
5781 rights: Some(fio::Operations::CONNECT),
5782 ..Default::default()
5783 }),
5784 fdecl::Capability::Runner(fdecl::Runner {
5785 name: Some("source_elf".to_string()),
5786 source_path: Some("/path".to_string()),
5787 ..Default::default()
5788 }),
5789 fdecl::Capability::Resolver(fdecl::Resolver {
5790 name: Some("source_pkg".to_string()),
5791 source_path: Some("/path".to_string()),
5792 ..Default::default()
5793 }),
5794 fdecl::Capability::Dictionary(fdecl::Dictionary {
5795 name: Some("source_dict".to_string()),
5796 ..Default::default()
5797 }),
5798 ]);
5799 decl
5800 },
5801 result = Err(ErrorList::new(vec![
5802 Error::duplicate_field(DeclType::ExposeProtocol, "target_name",
5804 "fuchsia.fonts.Provider"),
5805 Error::duplicate_field(DeclType::ExposeDirectory, "target_name",
5806 "stuff"),
5807 Error::duplicate_field(DeclType::ExposeRunner, "target_name",
5808 "elf"),
5809 Error::duplicate_field(DeclType::ExposeResolver, "target_name", "pkg"),
5810 Error::duplicate_field(DeclType::ExposeDictionary, "target_name", "dict"),
5811 ])),
5812 },
5813 test_validate_exposes_invalid_capability_from_self => {
5814 input = {
5815 let mut decl = new_component_decl();
5816 decl.exposes = Some(vec![
5817 fdecl::Expose::Service(fdecl::ExposeService {
5818 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5819 source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
5820 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5821 target_name: Some("foo".to_string()),
5822 ..Default::default()
5823 }),
5824 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5825 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5826 source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
5827 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5828 target_name: Some("bar".to_string()),
5829 ..Default::default()
5830 }),
5831 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5832 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5833 source_name: Some("dir".to_string()),
5834 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5835 target_name: Some("assets".to_string()),
5836 rights: None,
5837 subdir: None,
5838 ..Default::default()
5839 }),
5840 fdecl::Expose::Runner(fdecl::ExposeRunner {
5841 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5842 source_name: Some("source_elf".to_string()),
5843 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5844 target_name: Some("elf".to_string()),
5845 ..Default::default()
5846 }),
5847 fdecl::Expose::Resolver(fdecl::ExposeResolver {
5848 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5849 source_name: Some("source_pkg".to_string()),
5850 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5851 target_name: Some("pkg".to_string()),
5852 ..Default::default()
5853 }),
5854 fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5855 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5856 source_name: Some("source_dict".to_string()),
5857 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5858 target_name: Some("dict".to_string()),
5859 ..Default::default()
5860 }),
5861 fdecl::Expose::Config(fdecl::ExposeConfiguration {
5862 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5863 source_name: Some("source_config".to_string()),
5864 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5865 target_name: Some("config".to_string()),
5866 ..Default::default()
5867 }),
5868 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5869 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5870 source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
5871 source_dictionary: Some("dict/inner".to_string()),
5872 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5873 target_name: Some("baz".to_string()),
5874 ..Default::default()
5875 }),
5876 ]);
5877 decl
5878 },
5879 result = Err(ErrorList::new(vec![
5880 Error::invalid_capability(
5881 DeclType::ExposeService,
5882 "source",
5883 "fuchsia.some.library.SomeProtocol"),
5884 Error::invalid_capability(
5885 DeclType::ExposeProtocol,
5886 "source",
5887 "fuchsia.some.library.SomeProtocol"),
5888 Error::invalid_capability(DeclType::ExposeDirectory, "source", "dir"),
5889 Error::invalid_capability(DeclType::ExposeRunner, "source", "source_elf"),
5890 Error::invalid_capability(DeclType::ExposeResolver, "source", "source_pkg"),
5891 Error::invalid_capability(DeclType::ExposeDictionary, "source", "source_dict"),
5892 Error::invalid_capability(DeclType::ExposeConfig, "source", "source_config"),
5893 Error::invalid_capability(DeclType::ExposeProtocol, "source", "dict"),
5894 ])),
5895 },
5896
5897 test_validate_exposes_availability_service => {
5898 input = {
5899 let mut decl = generate_expose_different_source_and_availability_decl(
5900 |source, availability, target_name|
5901 fdecl::Expose::Service(fdecl::ExposeService {
5902 source: Some(source),
5903 source_name: Some("fuchsia.examples.Echo".to_string()),
5904 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5905 target_name: Some(target_name.to_string()),
5906 availability: Some(availability),
5907 ..Default::default()
5908 })
5909 );
5910 decl.capabilities = Some(vec![
5911 fdecl::Capability::Service(fdecl::Service {
5912 name: Some("fuchsia.examples.Echo".to_string()),
5913 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
5914 ..Default::default()
5915 }),
5916 ]);
5917 decl
5918 },
5919 result = {
5920 Err(ErrorList::new(vec![
5921 Error::availability_must_be_optional(
5922 DeclType::ExposeService,
5923 "availability",
5924 Some(&"fuchsia.examples.Echo".to_string()),
5925 ),
5926 Error::availability_must_be_optional(
5927 DeclType::ExposeService,
5928 "availability",
5929 Some(&"fuchsia.examples.Echo".to_string()),
5930 ),
5931 ]))
5932 },
5933 },
5934 test_validate_exposes_availability_protocol => {
5935 input = {
5936 let mut decl = generate_expose_different_source_and_availability_decl(
5937 |source, availability, target_name|
5938 fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5939 source: Some(source),
5940 source_name: Some("fuchsia.examples.Echo".to_string()),
5941 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5942 target_name: Some(target_name.to_string()),
5943 availability: Some(availability),
5944 ..Default::default()
5945 })
5946 );
5947 decl.capabilities = Some(vec![
5948 fdecl::Capability::Protocol(fdecl::Protocol {
5949 name: Some("fuchsia.examples.Echo".to_string()),
5950 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
5951 ..Default::default()
5952 }),
5953 ]);
5954 decl
5955 },
5956 result = {
5957 Err(ErrorList::new(vec![
5958 Error::availability_must_be_optional(
5959 DeclType::ExposeProtocol,
5960 "availability",
5961 Some(&"fuchsia.examples.Echo".to_string()),
5962 ),
5963 Error::availability_must_be_optional(
5964 DeclType::ExposeProtocol,
5965 "availability",
5966 Some(&"fuchsia.examples.Echo".to_string()),
5967 ),
5968 ]))
5969 },
5970 },
5971 test_validate_exposes_availability_directory => {
5972 input = {
5973 let mut decl = generate_expose_different_source_and_availability_decl(
5974 |source, availability, target_name|
5975 fdecl::Expose::Directory(fdecl::ExposeDirectory {
5976 source: Some(source),
5977 source_name: Some("fuchsia.examples.Echo".to_string()),
5978 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5979 target_name: Some(target_name.to_string()),
5980 availability: Some(availability),
5981 ..Default::default()
5982 })
5983 );
5984 decl.capabilities = Some(vec![
5985 fdecl::Capability::Directory(fdecl::Directory {
5986 name: Some("fuchsia.examples.Echo".to_string()),
5987 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
5988 rights: Some(fio::Operations::READ_BYTES),
5989 ..Default::default()
5990 }),
5991 ]);
5992 decl
5993 },
5994 result = {
5995 Err(ErrorList::new(vec![
5996 Error::availability_must_be_optional(
5997 DeclType::ExposeDirectory,
5998 "availability",
5999 Some(&"fuchsia.examples.Echo".to_string()),
6000 ),
6001 Error::availability_must_be_optional(
6002 DeclType::ExposeDirectory,
6003 "availability",
6004 Some(&"fuchsia.examples.Echo".to_string()),
6005 ),
6006 ]))
6007 },
6008 },
6009
6010 test_validate_offers_empty => {
6012 input = {
6013 let mut decl = new_component_decl();
6014 decl.offers = Some(vec![
6015 fdecl::Offer::Service(fdecl::OfferService {
6016 source: None,
6017 source_name: None,
6018 target: None,
6019 target_name: None,
6020 ..Default::default()
6021 }),
6022 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6023 source: None,
6024 source_name: None,
6025 target: None,
6026 target_name: None,
6027 dependency_type: None,
6028 ..Default::default()
6029 }),
6030 fdecl::Offer::Directory(fdecl::OfferDirectory {
6031 source: None,
6032 source_name: None,
6033 target: None,
6034 target_name: None,
6035 rights: None,
6036 subdir: None,
6037 dependency_type: None,
6038 ..Default::default()
6039 }),
6040 fdecl::Offer::Storage(fdecl::OfferStorage {
6041 source_name: None,
6042 source: None,
6043 target: None,
6044 target_name: None,
6045 ..Default::default()
6046 }),
6047 fdecl::Offer::Runner(fdecl::OfferRunner {
6048 source: None,
6049 source_name: None,
6050 target: None,
6051 target_name: None,
6052 ..Default::default()
6053 }),
6054 fdecl::Offer::Resolver(fdecl::OfferResolver {
6055 ..Default::default()
6056 }),
6057 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6058 ..Default::default()
6059 }),
6060 ]);
6061 decl
6062 },
6063 result = Err(ErrorList::new(vec![
6066 Error::missing_field(DeclType::OfferService, "source"),
6067 Error::missing_field(DeclType::OfferService, "source_name"),
6068 Error::missing_field(DeclType::OfferService, "target"),
6069 Error::missing_field(DeclType::OfferService, "target_name"),
6070 Error::missing_field(DeclType::OfferProtocol, "source"),
6072 Error::missing_field(DeclType::OfferProtocol, "source_name"),
6073 Error::missing_field(DeclType::OfferProtocol, "target"),
6074 Error::missing_field(DeclType::OfferProtocol, "target_name"),
6075 Error::missing_field(DeclType::OfferProtocol, "dependency_type"),
6076 Error::missing_field(DeclType::OfferDirectory, "source"),
6078 Error::missing_field(DeclType::OfferDirectory, "source_name"),
6079 Error::missing_field(DeclType::OfferDirectory, "target"),
6080 Error::missing_field(DeclType::OfferDirectory, "target_name"),
6081 Error::missing_field(DeclType::OfferDirectory, "dependency_type"),
6082 Error::missing_field(DeclType::OfferStorage, "source"),
6084 Error::missing_field(DeclType::OfferStorage, "source_name"),
6085 Error::missing_field(DeclType::OfferStorage, "target"),
6086 Error::missing_field(DeclType::OfferStorage, "target_name"),
6087 Error::missing_field(DeclType::OfferRunner, "source"),
6089 Error::missing_field(DeclType::OfferRunner, "source_name"),
6090 Error::missing_field(DeclType::OfferRunner, "target"),
6091 Error::missing_field(DeclType::OfferRunner, "target_name"),
6092 Error::missing_field(DeclType::OfferResolver, "source"),
6094 Error::missing_field(DeclType::OfferResolver, "source_name"),
6095 Error::missing_field(DeclType::OfferResolver, "target"),
6096 Error::missing_field(DeclType::OfferResolver, "target_name"),
6097 Error::missing_field(DeclType::OfferDictionary, "source"),
6098 Error::missing_field(DeclType::OfferDictionary, "source_name"),
6099 Error::missing_field(DeclType::OfferDictionary, "target"),
6100 Error::missing_field(DeclType::OfferDictionary, "target_name"),
6101 Error::missing_field(DeclType::OfferDictionary, "dependency_type"),
6102 ])),
6103 },
6104 test_validate_offers_long_identifiers => {
6105 input = {
6106 let mut decl = new_component_decl();
6107 decl.offers = Some(vec![
6108 fdecl::Offer::Service(fdecl::OfferService {
6109 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6110 name: "a".repeat(256),
6111 collection: None,
6112 })),
6113 source_name: Some(format!("{}", "a".repeat(256))),
6114 target: Some(fdecl::Ref::Child(
6115 fdecl::ChildRef {
6116 name: "b".repeat(256),
6117 collection: None,
6118 }
6119 )),
6120 target_name: Some(format!("{}", "b".repeat(256))),
6121 ..Default::default()
6122 }),
6123 fdecl::Offer::Service(fdecl::OfferService {
6124 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6125 source_name: Some("a".to_string()),
6126 target: Some(fdecl::Ref::Collection(
6127 fdecl::CollectionRef {
6128 name: "b".repeat(256),
6129 }
6130 )),
6131 target_name: Some(format!("{}", "b".repeat(256))),
6132 ..Default::default()
6133 }),
6134 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6135 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6136 name: "a".repeat(256),
6137 collection: None,
6138 })),
6139 source_name: Some(format!("{}", "a".repeat(256))),
6140 target: Some(fdecl::Ref::Child(
6141 fdecl::ChildRef {
6142 name: "b".repeat(256),
6143 collection: None,
6144 }
6145 )),
6146 target_name: Some(format!("{}", "b".repeat(256))),
6147 dependency_type: Some(fdecl::DependencyType::Strong),
6148 ..Default::default()
6149 }),
6150 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6151 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6152 source_name: Some("a".to_string()),
6153 target: Some(fdecl::Ref::Collection(
6154 fdecl::CollectionRef {
6155 name: "b".repeat(256),
6156 }
6157 )),
6158 target_name: Some(format!("{}", "b".repeat(256))),
6159 dependency_type: Some(fdecl::DependencyType::Weak),
6160 ..Default::default()
6161 }),
6162 fdecl::Offer::Directory(fdecl::OfferDirectory {
6163 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6164 name: "a".repeat(256),
6165 collection: None,
6166 })),
6167 source_name: Some(format!("{}", "a".repeat(256))),
6168 target: Some(fdecl::Ref::Child(
6169 fdecl::ChildRef {
6170 name: "b".repeat(256),
6171 collection: None,
6172 }
6173 )),
6174 target_name: Some(format!("{}", "b".repeat(256))),
6175 rights: Some(fio::Operations::CONNECT),
6176 subdir: None,
6177 dependency_type: Some(fdecl::DependencyType::Strong),
6178 ..Default::default()
6179 }),
6180 fdecl::Offer::Directory(fdecl::OfferDirectory {
6181 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6182 source_name: Some("a".to_string()),
6183 target: Some(fdecl::Ref::Collection(
6184 fdecl::CollectionRef {
6185 name: "b".repeat(256),
6186 }
6187 )),
6188 target_name: Some(format!("{}", "b".repeat(256))),
6189 rights: Some(fio::Operations::CONNECT),
6190 subdir: None,
6191 dependency_type: Some(fdecl::DependencyType::Weak),
6192 ..Default::default()
6193 }),
6194 fdecl::Offer::Storage(fdecl::OfferStorage {
6195 source_name: Some("data".to_string()),
6196 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6197 target: Some(fdecl::Ref::Child(
6198 fdecl::ChildRef {
6199 name: "b".repeat(256),
6200 collection: None,
6201 }
6202 )),
6203 target_name: Some("data".to_string()),
6204 ..Default::default()
6205 }),
6206 fdecl::Offer::Storage(fdecl::OfferStorage {
6207 source_name: Some("data".to_string()),
6208 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6209 target: Some(fdecl::Ref::Collection(
6210 fdecl::CollectionRef { name: "b".repeat(256) }
6211 )),
6212 target_name: Some("data".to_string()),
6213 ..Default::default()
6214 }),
6215 fdecl::Offer::Runner(fdecl::OfferRunner {
6216 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6217 name: "a".repeat(256),
6218 collection: None,
6219 })),
6220 source_name: Some("b".repeat(256)),
6221 target: Some(fdecl::Ref::Collection(
6222 fdecl::CollectionRef {
6223 name: "c".repeat(256),
6224 }
6225 )),
6226 target_name: Some("d".repeat(256)),
6227 ..Default::default()
6228 }),
6229 fdecl::Offer::Resolver(fdecl::OfferResolver {
6230 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6231 name: "a".repeat(256),
6232 collection: None,
6233 })),
6234 source_name: Some("b".repeat(256)),
6235 target: Some(fdecl::Ref::Collection(
6236 fdecl::CollectionRef {
6237 name: "c".repeat(256),
6238 }
6239 )),
6240 target_name: Some("d".repeat(256)),
6241 ..Default::default()
6242 }),
6243 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6244 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6245 name: "a".repeat(256),
6246 collection: None,
6247 })),
6248 source_name: Some("b".repeat(256)),
6249 target: Some(fdecl::Ref::Collection(
6250 fdecl::CollectionRef {
6251 name: "c".repeat(256),
6252 }
6253 )),
6254 target_name: Some("d".repeat(256)),
6255 dependency_type: Some(fdecl::DependencyType::Strong),
6256 ..Default::default()
6257 }),
6258 ]);
6259 decl
6260 },
6261 result = Err(ErrorList::new(vec![
6262 Error::field_too_long(DeclType::OfferService, "source.child.name"),
6263 Error::field_too_long(DeclType::OfferService, "source_name"),
6264 Error::field_too_long(DeclType::OfferService, "target.child.name"),
6265 Error::field_too_long(DeclType::OfferService, "target_name"),
6266 Error::field_too_long(DeclType::OfferService, "target.collection.name"),
6267 Error::field_too_long(DeclType::OfferService, "target_name"),
6268 Error::field_too_long(DeclType::OfferProtocol, "source.child.name"),
6269 Error::field_too_long(DeclType::OfferProtocol, "source_name"),
6270 Error::field_too_long(DeclType::OfferProtocol, "target.child.name"),
6271 Error::field_too_long(DeclType::OfferProtocol, "target_name"),
6272 Error::field_too_long(DeclType::OfferProtocol, "target.collection.name"),
6273 Error::field_too_long(DeclType::OfferProtocol, "target_name"),
6274 Error::field_too_long(DeclType::OfferDirectory, "source.child.name"),
6275 Error::field_too_long(DeclType::OfferDirectory, "source_name"),
6276 Error::field_too_long(DeclType::OfferDirectory, "target.child.name"),
6277 Error::field_too_long(DeclType::OfferDirectory, "target_name"),
6278 Error::field_too_long(DeclType::OfferDirectory, "target.collection.name"),
6279 Error::field_too_long(DeclType::OfferDirectory, "target_name"),
6280 Error::field_too_long(DeclType::OfferStorage, "target.child.name"),
6281 Error::field_too_long(DeclType::OfferStorage, "target.collection.name"),
6282 Error::field_too_long(DeclType::OfferRunner, "source.child.name"),
6283 Error::field_too_long(DeclType::OfferRunner, "source_name"),
6284 Error::field_too_long(DeclType::OfferRunner, "target.collection.name"),
6285 Error::field_too_long(DeclType::OfferRunner, "target_name"),
6286 Error::field_too_long(DeclType::OfferResolver, "source.child.name"),
6287 Error::field_too_long(DeclType::OfferResolver, "source_name"),
6288 Error::field_too_long(DeclType::OfferResolver, "target.collection.name"),
6289 Error::field_too_long(DeclType::OfferResolver, "target_name"),
6290 Error::field_too_long(DeclType::OfferDictionary, "source.child.name"),
6291 Error::field_too_long(DeclType::OfferDictionary, "source_name"),
6292 Error::field_too_long(DeclType::OfferDictionary, "target.collection.name"),
6293 Error::field_too_long(DeclType::OfferDictionary, "target_name"),
6294 ])),
6295 },
6296 test_validate_offers_extraneous => {
6297 input = {
6298 let mut decl = new_component_decl();
6299 decl.offers = Some(vec![
6300 fdecl::Offer::Service(fdecl::OfferService {
6301 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6302 name: "logger".to_string(),
6303 collection: Some("modular".to_string()),
6304 })),
6305 source_name: Some("fuchsia.logger.Log".to_string()),
6306 target: Some(fdecl::Ref::Child(
6307 fdecl::ChildRef {
6308 name: "netstack".to_string(),
6309 collection: Some("modular".to_string()),
6310 }
6311 )),
6312 target_name: Some("fuchsia.logger.Log".to_string()),
6313 ..Default::default()
6314 }),
6315 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6316 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6317 name: "logger".to_string(),
6318 collection: Some("modular".to_string()),
6319 })),
6320 source_name: Some("fuchsia.logger.Log".to_string()),
6321 target: Some(fdecl::Ref::Child(
6322 fdecl::ChildRef {
6323 name: "netstack".to_string(),
6324 collection: Some("modular".to_string()),
6325 }
6326 )),
6327 target_name: Some("fuchsia.logger.Log".to_string()),
6328 dependency_type: Some(fdecl::DependencyType::Strong),
6329 ..Default::default()
6330 }),
6331 fdecl::Offer::Directory(fdecl::OfferDirectory {
6332 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6333 name: "logger".to_string(),
6334 collection: Some("modular".to_string()),
6335 })),
6336 source_name: Some("assets".to_string()),
6337 target: Some(fdecl::Ref::Child(
6338 fdecl::ChildRef {
6339 name: "netstack".to_string(),
6340 collection: Some("modular".to_string()),
6341 }
6342 )),
6343 target_name: Some("assets".to_string()),
6344 rights: Some(fio::Operations::CONNECT),
6345 subdir: None,
6346 dependency_type: Some(fdecl::DependencyType::Weak),
6347 ..Default::default()
6348 }),
6349 fdecl::Offer::Storage(fdecl::OfferStorage {
6350 source_name: Some("data".to_string()),
6351 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{ })),
6352 target: Some(fdecl::Ref::Child(
6353 fdecl::ChildRef {
6354 name: "netstack".to_string(),
6355 collection: Some("modular".to_string()),
6356 }
6357 )),
6358 target_name: Some("data".to_string()),
6359 ..Default::default()
6360 }),
6361 fdecl::Offer::Runner(fdecl::OfferRunner {
6362 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6363 name: "logger".to_string(),
6364 collection: Some("modular".to_string()),
6365 })),
6366 source_name: Some("elf".to_string()),
6367 target: Some(fdecl::Ref::Child(
6368 fdecl::ChildRef {
6369 name: "netstack".to_string(),
6370 collection: Some("modular".to_string()),
6371 }
6372 )),
6373 target_name: Some("elf".to_string()),
6374 ..Default::default()
6375 }),
6376 fdecl::Offer::Resolver(fdecl::OfferResolver {
6377 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6378 name: "logger".to_string(),
6379 collection: Some("modular".to_string()),
6380 })),
6381 source_name: Some("pkg".to_string()),
6382 target: Some(fdecl::Ref::Child(
6383 fdecl::ChildRef {
6384 name: "netstack".to_string(),
6385 collection: Some("modular".to_string()),
6386 }
6387 )),
6388 target_name: Some("pkg".to_string()),
6389 ..Default::default()
6390 }),
6391 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6392 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6393 name: "logger".to_string(),
6394 collection: Some("modular".to_string()),
6395 })),
6396 source_name: Some("dict".to_string()),
6397 target: Some(fdecl::Ref::Child(
6398 fdecl::ChildRef {
6399 name: "netstack".to_string(),
6400 collection: Some("modular".to_string()),
6401 }
6402 )),
6403 target_name: Some("dict".to_string()),
6404 dependency_type: Some(fdecl::DependencyType::Strong),
6405 ..Default::default()
6406 }),
6407 ]);
6408 decl.capabilities = Some(vec![
6409 fdecl::Capability::Protocol(fdecl::Protocol {
6410 name: Some("fuchsia.logger.Log".to_string()),
6411 source_path: Some("/svc/logger".to_string()),
6412 ..Default::default()
6413 }),
6414 fdecl::Capability::Directory(fdecl::Directory {
6415 name: Some("assets".to_string()),
6416 source_path: Some("/data/assets".to_string()),
6417 rights: Some(fio::Operations::CONNECT),
6418 ..Default::default()
6419 }),
6420 ]);
6421 decl
6422 },
6423 result = Err(ErrorList::new(vec![
6424 Error::extraneous_field(DeclType::OfferService, "source.child.collection"),
6425 Error::extraneous_field(DeclType::OfferService, "target.child.collection"),
6426 Error::extraneous_field(DeclType::OfferProtocol, "source.child.collection"),
6427 Error::extraneous_field(DeclType::OfferProtocol, "target.child.collection"),
6428 Error::extraneous_field(DeclType::OfferDirectory, "source.child.collection"),
6429 Error::extraneous_field(DeclType::OfferDirectory, "target.child.collection"),
6430 Error::extraneous_field(DeclType::OfferStorage, "target.child.collection"),
6431 Error::extraneous_field(DeclType::OfferRunner, "source.child.collection"),
6432 Error::extraneous_field(DeclType::OfferRunner, "target.child.collection"),
6433 Error::extraneous_field(DeclType::OfferResolver, "source.child.collection"),
6434 Error::extraneous_field(DeclType::OfferResolver, "target.child.collection"),
6435 Error::extraneous_field(DeclType::OfferDictionary, "source.child.collection"),
6436 Error::extraneous_field(DeclType::OfferDictionary, "target.child.collection"),
6437 ])),
6438 },
6439 test_validate_offers_invalid_filtered_service_fields => {
6440 input = {
6441 let mut decl = new_component_decl();
6442 decl.offers = Some(vec![
6443 fdecl::Offer::Service(fdecl::OfferService {
6444 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6445 source_name: Some("fuchsia.logger.Log".to_string()),
6446 target: Some(fdecl::Ref::Child(
6447 fdecl::ChildRef {
6448 name: "logger".to_string(),
6449 collection: None,
6450 }
6451 )),
6452 target_name: Some("fuchsia.logger.Log".to_string()),
6453 source_instance_filter: Some(vec![]),
6454 ..Default::default()
6455 }),
6456 fdecl::Offer::Service(fdecl::OfferService {
6457 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6458 source_name: Some("fuchsia.logger.Log".to_string()),
6459 target: Some(fdecl::Ref::Child(
6460 fdecl::ChildRef {
6461 name: "logger".to_string(),
6462 collection: None,
6463 }
6464 )),
6465 target_name: Some("fuchsia.logger.Log2".to_string()),
6466 source_instance_filter: Some(vec!["^badname".to_string()]),
6467 ..Default::default()
6468 }),
6469 fdecl::Offer::Service(fdecl::OfferService {
6470 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6471 source_name: Some("fuchsia.logger.Log".to_string()),
6472 target: Some(fdecl::Ref::Child(
6473 fdecl::ChildRef {
6474 name: "logger".to_string(),
6475 collection: None,
6476 }
6477 )),
6478 target_name: Some("fuchsia.logger.Log1".to_string()),
6479 renamed_instances: Some(vec![fdecl::NameMapping{source_name: "a".to_string(), target_name: "b".to_string()}, fdecl::NameMapping{source_name: "c".to_string(), target_name: "b".to_string()}]),
6480 ..Default::default()
6481 }),
6482 fdecl::Offer::Service(fdecl::OfferService {
6483 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6484 source_name: Some("fuchsia.logger.Log".to_string()),
6485 target: Some(fdecl::Ref::Child(
6486 fdecl::ChildRef {
6487 name: "logger".to_string(),
6488 collection: None,
6489 }
6490 )),
6491 target_name: Some("fuchsia.logger.Log3".to_string()),
6492 renamed_instances: Some(vec![
6493 fdecl::NameMapping {
6494 source_name: "^badname".to_string(),
6495 target_name: "^badname".to_string(),
6496 }
6497 ]),
6498 ..Default::default()
6499 })
6500 ]);
6501 decl.children = Some(vec![
6502 fdecl::Child {
6503 name: Some("logger".to_string()),
6504 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
6505 startup: Some(fdecl::StartupMode::Lazy),
6506 on_terminate: None,
6507 environment: None,
6508 ..Default::default()
6509 },
6510 ]);
6511 decl
6512 },
6513 result = Err(ErrorList::new(vec![
6514 Error::invalid_field(DeclType::OfferService, "source_instance_filter"),
6515 Error::invalid_field(DeclType::OfferService, "source_instance_filter"),
6516 Error::invalid_field(DeclType::OfferService, "renamed_instances"),
6517 Error::invalid_field(DeclType::OfferService, "renamed_instances.source_name"),
6518 Error::invalid_field(DeclType::OfferService, "renamed_instances.target_name"),
6519 ])),
6520 },
6521 test_validate_offers_invalid_identifiers => {
6522 input = {
6523 let mut decl = new_component_decl();
6524 decl.offers = Some(vec![
6525 fdecl::Offer::Service(fdecl::OfferService {
6526 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6527 name: "^bad".to_string(),
6528 collection: None,
6529 })),
6530 source_name: Some("foo/".to_string()),
6531 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6532 name: "%bad".to_string(),
6533 collection: None,
6534 })),
6535 target_name: Some("/".to_string()),
6536 ..Default::default()
6537 }),
6538 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6539 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6540 name: "^bad".to_string(),
6541 collection: None,
6542 })),
6543 source_name: Some("foo/".to_string()),
6544 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6545 name: "%bad".to_string(),
6546 collection: None,
6547 })),
6548 target_name: Some("/".to_string()),
6549 dependency_type: Some(fdecl::DependencyType::Strong),
6550 ..Default::default()
6551 }),
6552 fdecl::Offer::Directory(fdecl::OfferDirectory {
6553 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6554 name: "^bad".to_string(),
6555 collection: None,
6556 })),
6557 source_name: Some("foo/".to_string()),
6558 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6559 name: "%bad".to_string(),
6560 collection: None,
6561 })),
6562 target_name: Some("/".to_string()),
6563 rights: Some(fio::Operations::CONNECT),
6564 subdir: Some("/foo".to_string()),
6565 dependency_type: Some(fdecl::DependencyType::Strong),
6566 ..Default::default()
6567 }),
6568 fdecl::Offer::Runner(fdecl::OfferRunner {
6569 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6570 name: "^bad".to_string(),
6571 collection: None,
6572 })),
6573 source_name: Some("/path".to_string()),
6574 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6575 name: "%bad".to_string(),
6576 collection: None,
6577 })),
6578 target_name: Some("elf!".to_string()),
6579 ..Default::default()
6580 }),
6581 fdecl::Offer::Resolver(fdecl::OfferResolver {
6582 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6583 name: "^bad".to_string(),
6584 collection: None,
6585 })),
6586 source_name: Some("/path".to_string()),
6587 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6588 name: "%bad".to_string(),
6589 collection: None,
6590 })),
6591 target_name: Some("pkg!".to_string()),
6592 ..Default::default()
6593 }),
6594 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6595 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6596 name: "^bad".to_string(),
6597 collection: None,
6598 })),
6599 source_name: Some("/path".to_string()),
6600 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6601 name: "%bad".to_string(),
6602 collection: None,
6603 })),
6604 target_name: Some("pkg!".to_string()),
6605 dependency_type: Some(fdecl::DependencyType::Strong),
6606 ..Default::default()
6607 }),
6608 ]);
6609 decl
6610 },
6611 result = Err(ErrorList::new(vec![
6612 Error::invalid_field(DeclType::OfferService, "source.child.name"),
6613 Error::invalid_field(DeclType::OfferService, "source_name"),
6614 Error::invalid_field(DeclType::OfferService, "target.child.name"),
6615 Error::invalid_field(DeclType::OfferService, "target_name"),
6616 Error::invalid_field(DeclType::OfferProtocol, "source.child.name"),
6617 Error::invalid_field(DeclType::OfferProtocol, "source_name"),
6618 Error::invalid_field(DeclType::OfferProtocol, "target.child.name"),
6619 Error::invalid_field(DeclType::OfferProtocol, "target_name"),
6620 Error::invalid_field(DeclType::OfferDirectory, "source.child.name"),
6621 Error::invalid_field(DeclType::OfferDirectory, "source_name"),
6622 Error::invalid_field(DeclType::OfferDirectory, "target.child.name"),
6623 Error::invalid_field(DeclType::OfferDirectory, "target_name"),
6624 Error::invalid_field(DeclType::OfferDirectory, "subdir"),
6625 Error::invalid_field(DeclType::OfferRunner, "source.child.name"),
6626 Error::invalid_field(DeclType::OfferRunner, "source_name"),
6627 Error::invalid_field(DeclType::OfferRunner, "target.child.name"),
6628 Error::invalid_field(DeclType::OfferRunner, "target_name"),
6629 Error::invalid_field(DeclType::OfferResolver, "source.child.name"),
6630 Error::invalid_field(DeclType::OfferResolver, "source_name"),
6631 Error::invalid_field(DeclType::OfferResolver, "target.child.name"),
6632 Error::invalid_field(DeclType::OfferResolver, "target_name"),
6633 Error::invalid_field(DeclType::OfferDictionary, "source.child.name"),
6634 Error::invalid_field(DeclType::OfferDictionary, "source_name"),
6635 Error::invalid_field(DeclType::OfferDictionary, "target.child.name"),
6636 Error::invalid_field(DeclType::OfferDictionary, "target_name"),
6637 ])),
6638 },
6639 test_validate_offers_target_equals_source => {
6640 input = {
6641 let mut decl = new_component_decl();
6642 decl.offers = Some(vec![
6643 fdecl::Offer::Service(fdecl::OfferService {
6644 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6645 name: "logger".to_string(),
6646 collection: None,
6647 })),
6648 source_name: Some("logger".to_string()),
6649 target: Some(fdecl::Ref::Child(
6650 fdecl::ChildRef {
6651 name: "logger".to_string(),
6652 collection: None,
6653 }
6654 )),
6655 target_name: Some("logger".to_string()),
6656 ..Default::default()
6657 }),
6658 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6659 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6660 name: "logger".to_string(),
6661 collection: None,
6662 })),
6663 source_name: Some("legacy_logger".to_string()),
6664 target: Some(fdecl::Ref::Child(
6665 fdecl::ChildRef {
6666 name: "logger".to_string(),
6667 collection: None,
6668 }
6669 )),
6670 target_name: Some("weak_legacy_logger".to_string()),
6671 dependency_type: Some(fdecl::DependencyType::Weak),
6672 ..Default::default()
6673 }),
6674 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6675 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6676 name: "logger".to_string(),
6677 collection: None,
6678 })),
6679 source_name: Some("legacy_logger".to_string()),
6680 target: Some(fdecl::Ref::Child(
6681 fdecl::ChildRef {
6682 name: "logger".to_string(),
6683 collection: None,
6684 }
6685 )),
6686 target_name: Some("strong_legacy_logger".to_string()),
6687 dependency_type: Some(fdecl::DependencyType::Strong),
6688 ..Default::default()
6689 }),
6690 fdecl::Offer::Directory(fdecl::OfferDirectory {
6691 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6692 name: "logger".to_string(),
6693 collection: None,
6694 })),
6695 source_name: Some("assets".to_string()),
6696 target: Some(fdecl::Ref::Child(
6697 fdecl::ChildRef {
6698 name: "logger".to_string(),
6699 collection: None,
6700 }
6701 )),
6702 target_name: Some("assets".to_string()),
6703 rights: Some(fio::Operations::CONNECT),
6704 subdir: None,
6705 dependency_type: Some(fdecl::DependencyType::Strong),
6706 ..Default::default()
6707 }),
6708 fdecl::Offer::Runner(fdecl::OfferRunner {
6709 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6710 name: "logger".to_string(),
6711 collection: None,
6712 })),
6713 source_name: Some("web".to_string()),
6714 target: Some(fdecl::Ref::Child(
6715 fdecl::ChildRef {
6716 name: "logger".to_string(),
6717 collection: None,
6718 }
6719 )),
6720 target_name: Some("web".to_string()),
6721 ..Default::default()
6722 }),
6723 fdecl::Offer::Resolver(fdecl::OfferResolver {
6724 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6725 name: "logger".to_string(),
6726 collection: None,
6727 })),
6728 source_name: Some("pkg".to_string()),
6729 target: Some(fdecl::Ref::Child(
6730 fdecl::ChildRef {
6731 name: "logger".to_string(),
6732 collection: None,
6733 }
6734 )),
6735 target_name: Some("pkg".to_string()),
6736 ..Default::default()
6737 }),
6738 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6739 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6740 name: "logger".to_string(),
6741 collection: None,
6742 })),
6743 source_name: Some("dict".to_string()),
6744 target: Some(fdecl::Ref::Child(
6745 fdecl::ChildRef {
6746 name: "logger".to_string(),
6747 collection: None,
6748 }
6749 )),
6750 target_name: Some("dict".to_string()),
6751 dependency_type: Some(fdecl::DependencyType::Strong),
6752 ..Default::default()
6753 }),
6754 ]);
6755 decl.children = Some(vec![fdecl::Child{
6756 name: Some("logger".to_string()),
6757 url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
6758 startup: Some(fdecl::StartupMode::Lazy),
6759 on_terminate: None,
6760 environment: None,
6761 ..Default::default()
6762 }]);
6763 decl
6764 },
6765 result = Err(ErrorList::new(vec![
6766 Error::dependency_cycle("{{child logger -> child logger}}"),
6767 ])),
6768 },
6769 test_validate_offers_storage_target_equals_source => {
6770 input = fdecl::Component {
6771 offers: Some(vec![
6772 fdecl::Offer::Storage(fdecl::OfferStorage {
6773 source_name: Some("data".to_string()),
6774 source: Some(fdecl::Ref::Self_(fdecl::SelfRef { })),
6775 target: Some(fdecl::Ref::Child(
6776 fdecl::ChildRef {
6777 name: "logger".to_string(),
6778 collection: None,
6779 }
6780 )),
6781 target_name: Some("data".to_string()),
6782 ..Default::default()
6783 })
6784 ]),
6785 capabilities: Some(vec![
6786 fdecl::Capability::Storage(fdecl::Storage {
6787 name: Some("data".to_string()),
6788 backing_dir: Some("minfs".to_string()),
6789 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6790 name: "logger".to_string(),
6791 collection: None,
6792 })),
6793 subdir: None,
6794 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
6795 ..Default::default()
6796 }),
6797 ]),
6798 children: Some(vec![
6799 fdecl::Child {
6800 name: Some("logger".to_string()),
6801 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
6802 startup: Some(fdecl::StartupMode::Lazy),
6803 on_terminate: None,
6804 environment: None,
6805 ..Default::default()
6806 },
6807 ]),
6808 ..new_component_decl()
6809 },
6810 result = Err(ErrorList::new(vec![
6811 Error::dependency_cycle("{{child logger -> capability data -> child logger}}"),
6812 ])),
6813 },
6814 test_validate_offers_invalid_child => {
6815 input = {
6816 let mut decl = new_component_decl();
6817 decl.offers = Some(vec![
6818 fdecl::Offer::Service(fdecl::OfferService {
6819 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6820 name: "logger".to_string(),
6821 collection: None,
6822 })),
6823 source_name: Some("fuchsia.logger.Log".to_string()),
6824 target: Some(fdecl::Ref::Child(
6825 fdecl::ChildRef {
6826 name: "netstack".to_string(),
6827 collection: None,
6828 }
6829 )),
6830 target_name: Some("fuchsia.logger.Log".to_string()),
6831 ..Default::default()
6832 }),
6833 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6834 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6835 name: "logger".to_string(),
6836 collection: None,
6837 })),
6838 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
6839 target: Some(fdecl::Ref::Child(
6840 fdecl::ChildRef {
6841 name: "netstack".to_string(),
6842 collection: None,
6843 }
6844 )),
6845 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
6846 dependency_type: Some(fdecl::DependencyType::Strong),
6847 ..Default::default()
6848 }),
6849 fdecl::Offer::Directory(fdecl::OfferDirectory {
6850 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6851 name: "logger".to_string(),
6852 collection: None,
6853 })),
6854 source_name: Some("assets".to_string()),
6855 target: Some(fdecl::Ref::Collection(
6856 fdecl::CollectionRef { name: "modular".to_string() }
6857 )),
6858 target_name: Some("assets".to_string()),
6859 rights: Some(fio::Operations::CONNECT),
6860 subdir: None,
6861 dependency_type: Some(fdecl::DependencyType::Weak),
6862 ..Default::default()
6863 }),
6864 ]);
6865 decl.capabilities = Some(vec![
6866 fdecl::Capability::Storage(fdecl::Storage {
6867 name: Some("memfs".to_string()),
6868 backing_dir: Some("memfs".to_string()),
6869 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6870 name: "logger".to_string(),
6871 collection: None,
6872 })),
6873 subdir: None,
6874 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
6875 ..Default::default()
6876 }),
6877 ]);
6878 decl.children = Some(vec![
6879 fdecl::Child {
6880 name: Some("netstack".to_string()),
6881 url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
6882 startup: Some(fdecl::StartupMode::Lazy),
6883 on_terminate: None,
6884 environment: None,
6885 ..Default::default()
6886 },
6887 ]);
6888 decl.collections = Some(vec![
6889 fdecl::Collection {
6890 name: Some("modular".to_string()),
6891 durability: Some(fdecl::Durability::Transient),
6892 environment: None,
6893 allowed_offers: Some(fdecl::AllowedOffers::StaticAndDynamic),
6894 allow_long_names: None,
6895 ..Default::default()
6896 },
6897 ]);
6898 decl
6899 },
6900 result = Err(ErrorList::new(vec![
6901 Error::invalid_child(DeclType::Storage, "source", "logger"),
6902 Error::invalid_child(DeclType::OfferService, "source", "logger"),
6903 Error::invalid_child(DeclType::OfferProtocol, "source", "logger"),
6904 Error::invalid_child(DeclType::OfferDirectory, "source", "logger"),
6905 ])),
6906 },
6907 test_validate_offers_invalid_source_capability => {
6908 input = {
6909 fdecl::Component {
6910 offers: Some(vec![
6911 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6912 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
6913 name: "this-storage-doesnt-exist".to_string(),
6914 })),
6915 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
6916 target: Some(fdecl::Ref::Child(
6917 fdecl::ChildRef {
6918 name: "netstack".to_string(),
6919 collection: None,
6920 }
6921 )),
6922 target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
6923 dependency_type: Some(fdecl::DependencyType::Strong),
6924 ..Default::default()
6925 }),
6926 ]),
6927 ..new_component_decl()
6928 }
6929 },
6930 result = Err(ErrorList::new(vec![
6931 Error::invalid_capability(DeclType::OfferProtocol, "source", "this-storage-doesnt-exist"),
6932 Error::invalid_child(DeclType::OfferProtocol, "target", "netstack"),
6933 ])),
6934 },
6935 test_validate_offers_target => {
6936 input = {
6937 let mut decl = new_component_decl();
6938 decl.offers = Some(vec![
6939 fdecl::Offer::Service(fdecl::OfferService {
6940 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6941 name: "modular".into()
6942 })),
6943 source_name: Some("logger".to_string()),
6944 target: Some(fdecl::Ref::Child(
6945 fdecl::ChildRef {
6946 name: "netstack".to_string(),
6947 collection: None,
6948 }
6949 )),
6950 target_name: Some("fuchsia.logger.Log".to_string()),
6951 ..Default::default()
6952 }),
6953 fdecl::Offer::Service(fdecl::OfferService {
6954 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6955 name: "modular".into()
6956 })),
6957 source_name: Some("logger".to_string()),
6958 target: Some(fdecl::Ref::Child(
6959 fdecl::ChildRef {
6960 name: "netstack".to_string(),
6961 collection: None,
6962 }
6963 )),
6964 target_name: Some("fuchsia.logger.Log".to_string()),
6965 ..Default::default()
6966 }),
6967 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6968 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6969 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
6970 target: Some(fdecl::Ref::Child(
6971 fdecl::ChildRef {
6972 name: "netstack".to_string(),
6973 collection: None,
6974 }
6975 )),
6976 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
6977 dependency_type: Some(fdecl::DependencyType::Strong),
6978 ..Default::default()
6979 }),
6980 fdecl::Offer::Protocol(fdecl::OfferProtocol {
6981 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6982 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
6983 target: Some(fdecl::Ref::Child(
6984 fdecl::ChildRef {
6985 name: "netstack".to_string(),
6986 collection: None,
6987 }
6988 )),
6989 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
6990 dependency_type: Some(fdecl::DependencyType::Strong),
6991 ..Default::default()
6992 }),
6993 fdecl::Offer::Directory(fdecl::OfferDirectory {
6994 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6995 source_name: Some("assets".to_string()),
6996 target: Some(fdecl::Ref::Collection(
6997 fdecl::CollectionRef { name: "modular".to_string() }
6998 )),
6999 target_name: Some("assets".to_string()),
7000 rights: Some(fio::Operations::CONNECT),
7001 subdir: None,
7002 dependency_type: Some(fdecl::DependencyType::Strong),
7003 ..Default::default()
7004 }),
7005 fdecl::Offer::Directory(fdecl::OfferDirectory {
7006 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7007 source_name: Some("assets".to_string()),
7008 target: Some(fdecl::Ref::Collection(
7009 fdecl::CollectionRef { name: "modular".to_string() }
7010 )),
7011 target_name: Some("assets".to_string()),
7012 rights: Some(fio::Operations::CONNECT),
7013 subdir: None,
7014 dependency_type: Some(fdecl::DependencyType::Weak),
7015 ..Default::default()
7016 }),
7017 fdecl::Offer::Storage(fdecl::OfferStorage {
7018 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7019 source_name: Some("data".to_string()),
7020 target: Some(fdecl::Ref::Collection(
7021 fdecl::CollectionRef { name: "modular".to_string() }
7022 )),
7023 target_name: Some("data".to_string()),
7024 ..Default::default()
7025 }),
7026 fdecl::Offer::Storage(fdecl::OfferStorage {
7027 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7028 source_name: Some("data".to_string()),
7029 target: Some(fdecl::Ref::Collection(
7030 fdecl::CollectionRef { name: "modular".to_string() }
7031 )),
7032 target_name: Some("data".to_string()),
7033 ..Default::default()
7034 }),
7035 fdecl::Offer::Runner(fdecl::OfferRunner {
7036 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7037 source_name: Some("elf".to_string()),
7038 target: Some(fdecl::Ref::Collection(
7039 fdecl::CollectionRef { name: "modular".to_string() }
7040 )),
7041 target_name: Some("duplicated".to_string()),
7042 ..Default::default()
7043 }),
7044 fdecl::Offer::Runner(fdecl::OfferRunner {
7045 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7046 source_name: Some("elf".to_string()),
7047 target: Some(fdecl::Ref::Collection(
7048 fdecl::CollectionRef { name: "modular".to_string() }
7049 )),
7050 target_name: Some("duplicated".to_string()),
7051 ..Default::default()
7052 }),
7053 fdecl::Offer::Resolver(fdecl::OfferResolver {
7054 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7055 source_name: Some("pkg".to_string()),
7056 target: Some(fdecl::Ref::Collection(
7057 fdecl::CollectionRef { name: "modular".to_string() }
7058 )),
7059 target_name: Some("duplicated".to_string()),
7060 ..Default::default()
7061 }),
7062 fdecl::Offer::EventStream(fdecl::OfferEventStream {
7063 source_name: Some("started".to_string()),
7064 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7065 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "netstack".to_string(), collection: None})),
7066 target_name: Some("started".to_string()),
7067 ..Default::default()
7068 }),
7069 fdecl::Offer::EventStream(fdecl::OfferEventStream {
7070 source_name: Some("started".to_string()),
7071 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7072 target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "netstack".to_string(), collection: None})),
7073 target_name: Some("started".to_string()),
7074 ..Default::default()
7075 }),
7076 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7077 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7078 source_name: Some("a".to_string()),
7079 target: Some(fdecl::Ref::Collection(
7080 fdecl::CollectionRef { name: "modular".to_string() }
7081 )),
7082 target_name: Some("dict".to_string()),
7083 dependency_type: Some(fdecl::DependencyType::Strong),
7084 ..Default::default()
7085 }),
7086 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7087 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7088 source_name: Some("b".to_string()),
7089 target: Some(fdecl::Ref::Collection(
7090 fdecl::CollectionRef { name: "modular".to_string() }
7091 )),
7092 target_name: Some("dict".to_string()),
7093 dependency_type: Some(fdecl::DependencyType::Strong),
7094 ..Default::default()
7095 }),
7096 ]);
7097 decl.children = Some(vec![
7098 fdecl::Child{
7099 name: Some("netstack".to_string()),
7100 url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
7101 startup: Some(fdecl::StartupMode::Eager),
7102 on_terminate: None,
7103 environment: None,
7104 ..Default::default()
7105 },
7106 ]);
7107 decl.collections = Some(vec![
7108 fdecl::Collection{
7109 name: Some("modular".to_string()),
7110 durability: Some(fdecl::Durability::Transient),
7111 environment: None,
7112 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7113 allow_long_names: None,
7114 ..Default::default()
7115 },
7116 ]);
7117 decl
7118 },
7119 result = Err(ErrorList::new(vec![
7120 Error::duplicate_field(DeclType::OfferProtocol, "target_name", "fuchsia.logger.LegacyLog"),
7122 Error::duplicate_field(DeclType::OfferDirectory, "target_name", "assets"),
7123 Error::duplicate_field(DeclType::OfferStorage, "target_name", "data"),
7124 Error::duplicate_field(DeclType::OfferRunner, "target_name", "duplicated"),
7125 Error::duplicate_field(DeclType::OfferResolver, "target_name", "duplicated"),
7126 Error::duplicate_field(DeclType::OfferEventStream, "target_name", "started"),
7127 Error::duplicate_field(DeclType::OfferDictionary, "target_name", "dict"),
7128 ])),
7129 },
7130 test_validate_offers_target_invalid => {
7131 input = {
7132 let mut decl = new_component_decl();
7133 decl.offers = Some(vec![
7134 fdecl::Offer::Service(fdecl::OfferService {
7135 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7136 source_name: Some("logger".to_string()),
7137 target: Some(fdecl::Ref::Child(
7138 fdecl::ChildRef {
7139 name: "netstack".to_string(),
7140 collection: None,
7141 }
7142 )),
7143 target_name: Some("fuchsia.logger.Log".to_string()),
7144 ..Default::default()
7145 }),
7146 fdecl::Offer::Service(fdecl::OfferService {
7147 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7148 source_name: Some("logger".to_string()),
7149 target: Some(fdecl::Ref::Collection(
7150 fdecl::CollectionRef { name: "modular".to_string(), }
7151 )),
7152 target_name: Some("fuchsia.logger.Log".to_string()),
7153 ..Default::default()
7154 }),
7155 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7156 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7157 source_name: Some("legacy_logger".to_string()),
7158 target: Some(fdecl::Ref::Child(
7159 fdecl::ChildRef {
7160 name: "netstack".to_string(),
7161 collection: None,
7162 }
7163 )),
7164 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7165 dependency_type: Some(fdecl::DependencyType::Weak),
7166 ..Default::default()
7167 }),
7168 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7169 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7170 source_name: Some("legacy_logger".to_string()),
7171 target: Some(fdecl::Ref::Collection(
7172 fdecl::CollectionRef { name: "modular".to_string(), }
7173 )),
7174 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7175 dependency_type: Some(fdecl::DependencyType::Strong),
7176 ..Default::default()
7177 }),
7178 fdecl::Offer::Directory(fdecl::OfferDirectory {
7179 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7180 source_name: Some("assets".to_string()),
7181 target: Some(fdecl::Ref::Child(
7182 fdecl::ChildRef {
7183 name: "netstack".to_string(),
7184 collection: None,
7185 }
7186 )),
7187 target_name: Some("data".to_string()),
7188 rights: Some(fio::Operations::CONNECT),
7189 subdir: None,
7190 dependency_type: Some(fdecl::DependencyType::Strong),
7191 ..Default::default()
7192 }),
7193 fdecl::Offer::Directory(fdecl::OfferDirectory {
7194 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7195 source_name: Some("assets".to_string()),
7196 target: Some(fdecl::Ref::Collection(
7197 fdecl::CollectionRef { name: "modular".to_string(), }
7198 )),
7199 target_name: Some("data".to_string()),
7200 rights: Some(fio::Operations::CONNECT),
7201 subdir: None,
7202 dependency_type: Some(fdecl::DependencyType::Weak),
7203 ..Default::default()
7204 }),
7205 fdecl::Offer::Storage(fdecl::OfferStorage {
7206 source_name: Some("data".to_string()),
7207 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7208 target: Some(fdecl::Ref::Child(
7209 fdecl::ChildRef {
7210 name: "netstack".to_string(),
7211 collection: None,
7212 }
7213 )),
7214 target_name: Some("data".to_string()),
7215 ..Default::default()
7216 }),
7217 fdecl::Offer::Storage(fdecl::OfferStorage {
7218 source_name: Some("data".to_string()),
7219 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7220 target: Some(fdecl::Ref::Collection(
7221 fdecl::CollectionRef { name: "modular".to_string(), }
7222 )),
7223 target_name: Some("data".to_string()),
7224 ..Default::default()
7225 }),
7226 fdecl::Offer::Runner(fdecl::OfferRunner {
7227 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7228 source_name: Some("elf".to_string()),
7229 target: Some(fdecl::Ref::Child(
7230 fdecl::ChildRef {
7231 name: "netstack".to_string(),
7232 collection: None,
7233 }
7234 )),
7235 target_name: Some("elf".to_string()),
7236 ..Default::default()
7237 }),
7238 fdecl::Offer::Runner(fdecl::OfferRunner {
7239 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7240 source_name: Some("elf".to_string()),
7241 target: Some(fdecl::Ref::Collection(
7242 fdecl::CollectionRef { name: "modular".to_string(), }
7243 )),
7244 target_name: Some("elf".to_string()),
7245 ..Default::default()
7246 }),
7247 fdecl::Offer::Resolver(fdecl::OfferResolver {
7248 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7249 source_name: Some("pkg".to_string()),
7250 target: Some(fdecl::Ref::Child(
7251 fdecl::ChildRef {
7252 name: "netstack".to_string(),
7253 collection: None,
7254 }
7255 )),
7256 target_name: Some("pkg".to_string()),
7257 ..Default::default()
7258 }),
7259 fdecl::Offer::Resolver(fdecl::OfferResolver {
7260 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7261 source_name: Some("pkg".to_string()),
7262 target: Some(fdecl::Ref::Collection(
7263 fdecl::CollectionRef { name: "modular".to_string(), }
7264 )),
7265 target_name: Some("pkg".to_string()),
7266 ..Default::default()
7267 }),
7268 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7269 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7270 source_name: Some("pkg".to_string()),
7271 target: Some(fdecl::Ref::Child(
7272 fdecl::ChildRef {
7273 name: "netstack".to_string(),
7274 collection: None,
7275 }
7276 )),
7277 target_name: Some("pkg".to_string()),
7278 dependency_type: Some(fdecl::DependencyType::Strong),
7279 ..Default::default()
7280 }),
7281 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7282 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7283 source_name: Some("pkg".to_string()),
7284 target: Some(fdecl::Ref::Collection(
7285 fdecl::CollectionRef { name: "modular".to_string(), }
7286 )),
7287 target_name: Some("pkg".to_string()),
7288 dependency_type: Some(fdecl::DependencyType::Strong),
7289 ..Default::default()
7290 }),
7291 ]);
7292 decl
7293 },
7294 result = Err(ErrorList::new(vec![
7295 Error::invalid_child(DeclType::OfferService, "target", "netstack"),
7296 Error::invalid_collection(DeclType::OfferService, "target", "modular"),
7297 Error::invalid_child(DeclType::OfferProtocol, "target", "netstack"),
7298 Error::invalid_collection(DeclType::OfferProtocol, "target", "modular"),
7299 Error::invalid_child(DeclType::OfferDirectory, "target", "netstack"),
7300 Error::invalid_collection(DeclType::OfferDirectory, "target", "modular"),
7301 Error::invalid_child(DeclType::OfferStorage, "target", "netstack"),
7302 Error::invalid_collection(DeclType::OfferStorage, "target", "modular"),
7303 Error::invalid_child(DeclType::OfferRunner, "target", "netstack"),
7304 Error::invalid_collection(DeclType::OfferRunner, "target", "modular"),
7305 Error::invalid_child(DeclType::OfferResolver, "target", "netstack"),
7306 Error::invalid_collection(DeclType::OfferResolver, "target", "modular"),
7307 Error::invalid_child(DeclType::OfferDictionary, "target", "netstack"),
7308 Error::invalid_collection(DeclType::OfferDictionary, "target", "modular"),
7309 ])),
7310 },
7311 test_validate_offers_target_dictionary => {
7312 input = fdecl::Component {
7313 offers: Some(vec![
7314 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7316 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7317 source_name: Some("p".to_string()),
7318 target: Some(fdecl::Ref::Capability(
7319 fdecl::CapabilityRef {
7320 name: "dict".into(),
7321 },
7322 )),
7323 target_name: Some("p".into()),
7324 dependency_type: Some(fdecl::DependencyType::Strong),
7325 ..Default::default()
7326 }),
7327 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7329 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7330 source_name: Some("p".to_string()),
7331 target: Some(fdecl::Ref::Capability(
7332 fdecl::CapabilityRef {
7333 name: "dynamic".into(),
7334 },
7335 )),
7336 target_name: Some("p".into()),
7337 dependency_type: Some(fdecl::DependencyType::Strong),
7338 ..Default::default()
7339 }),
7340 ]),
7341 capabilities: Some(vec![
7342 fdecl::Capability::Dictionary(fdecl::Dictionary {
7343 name: Some("dict".into()),
7344 ..Default::default()
7345 }),
7346 fdecl::Capability::Dictionary(fdecl::Dictionary {
7347 name: Some("dynamic".into()),
7348 source_path: Some("/out/dir".into()),
7349 ..Default::default()
7350 }),
7351 ]),
7352 ..Default::default()
7353 },
7354 result = Err(ErrorList::new(vec![
7355 Error::invalid_field(DeclType::OfferProtocol, "target"),
7356 ])),
7357 },
7358 test_validate_offers_invalid_source_collection => {
7359 input = {
7360 let mut decl = new_component_decl();
7361 decl.collections = Some(vec![
7362 fdecl::Collection {
7363 name: Some("col".to_string()),
7364 durability: Some(fdecl::Durability::Transient),
7365 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7366 allow_long_names: None,
7367 ..Default::default()
7368 }
7369 ]);
7370 decl.children = Some(vec![
7371 fdecl::Child {
7372 name: Some("child".to_string()),
7373 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7374 startup: Some(fdecl::StartupMode::Lazy),
7375 on_terminate: None,
7376 ..Default::default()
7377 }
7378 ]);
7379 decl.offers = Some(vec![
7380 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7381 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7382 source_name: Some("a".to_string()),
7383 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7384 target_name: Some("a".to_string()),
7385 dependency_type: Some(fdecl::DependencyType::Strong),
7386 ..Default::default()
7387 }),
7388 fdecl::Offer::Directory(fdecl::OfferDirectory {
7389 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7390 source_name: Some("b".to_string()),
7391 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7392 target_name: Some("b".to_string()),
7393 rights: Some(fio::Operations::CONNECT),
7394 subdir: None,
7395 dependency_type: Some(fdecl::DependencyType::Strong),
7396 ..Default::default()
7397 }),
7398 fdecl::Offer::Storage(fdecl::OfferStorage {
7399 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7400 source_name: Some("c".to_string()),
7401 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7402 target_name: Some("c".to_string()),
7403 ..Default::default()
7404 }),
7405 fdecl::Offer::Runner(fdecl::OfferRunner {
7406 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7407 source_name: Some("d".to_string()),
7408 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7409 target_name: Some("d".to_string()),
7410 ..Default::default()
7411 }),
7412 fdecl::Offer::Resolver(fdecl::OfferResolver {
7413 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7414 source_name: Some("e".to_string()),
7415 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7416 target_name: Some("e".to_string()),
7417 ..Default::default()
7418 }),
7419 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7420 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7421 source_name: Some("f".to_string()),
7422 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7423 target_name: Some("f".to_string()),
7424 dependency_type: Some(fdecl::DependencyType::Strong),
7425 ..Default::default()
7426 }),
7427 ]);
7428 decl
7429 },
7430 result = Err(ErrorList::new(vec![
7431 Error::invalid_field(DeclType::OfferProtocol, "source"),
7432 Error::invalid_field(DeclType::OfferDirectory, "source"),
7433 Error::invalid_field(DeclType::OfferStorage, "source"),
7434 Error::invalid_field(DeclType::OfferRunner, "source"),
7435 Error::invalid_field(DeclType::OfferResolver, "source"),
7436 Error::invalid_field(DeclType::OfferDictionary, "source"),
7437 ])),
7438 },
7439 test_validate_offers_source_collection => {
7440 input = {
7441 let mut decl = new_component_decl();
7442 decl.collections = Some(vec![
7443 fdecl::Collection {
7444 name: Some("col".to_string()),
7445 durability: Some(fdecl::Durability::Transient),
7446 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7447 allow_long_names: None,
7448 ..Default::default()
7449 }
7450 ]);
7451 decl.children = Some(vec![
7452 fdecl::Child {
7453 name: Some("child".to_string()),
7454 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7455 startup: Some(fdecl::StartupMode::Lazy),
7456 on_terminate: None,
7457 ..Default::default()
7458 }
7459 ]);
7460 decl.offers = Some(vec![
7461 fdecl::Offer::Service(fdecl::OfferService {
7462 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7463 source_name: Some("a".to_string()),
7464 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7465 target_name: Some("a".to_string()),
7466 ..Default::default()
7467 })
7468 ]);
7469 decl
7470 },
7471 result = Ok(()),
7472 },
7473 test_validate_offers_invalid_capability_from_self => {
7474 input = {
7475 let mut decl = new_component_decl();
7476 decl.children = Some(vec![
7477 fdecl::Child {
7478 name: Some("child".to_string()),
7479 url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7480 startup: Some(fdecl::StartupMode::Lazy),
7481 ..Default::default()
7482 }
7483 ]);
7484 decl.offers = Some(vec![
7485 fdecl::Offer::Service(fdecl::OfferService {
7486 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7487 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7488 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7489 name: "child".into(),
7490 collection: None
7491 })),
7492 target_name: Some("foo".into()),
7493 ..Default::default()
7494 }),
7495 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7496 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7497 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7498 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7499 name: "child".into(),
7500 collection: None
7501 })),
7502 target_name: Some("bar".into()),
7503 dependency_type: Some(fdecl::DependencyType::Strong),
7504 ..Default::default()
7505 }),
7506 fdecl::Offer::Directory(fdecl::OfferDirectory {
7507 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7508 source_name: Some("dir".into()),
7509 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7510 name: "child".into(),
7511 collection: None
7512 })),
7513 target_name: Some("assets".into()),
7514 dependency_type: Some(fdecl::DependencyType::Strong),
7515 ..Default::default()
7516 }),
7517 fdecl::Offer::Runner(fdecl::OfferRunner {
7518 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7519 source_name: Some("source_elf".into()),
7520 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7521 name: "child".into(),
7522 collection: None
7523 })),
7524 target_name: Some("elf".into()),
7525 ..Default::default()
7526 }),
7527 fdecl::Offer::Resolver(fdecl::OfferResolver {
7528 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7529 source_name: Some("source_pkg".into()),
7530 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7531 name: "child".into(),
7532 collection: None
7533 })),
7534 target_name: Some("pkg".into()),
7535 ..Default::default()
7536 }),
7537 fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7538 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7539 source_name: Some("source_dict".into()),
7540 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7541 name: "child".into(),
7542 collection: None
7543 })),
7544 target_name: Some("dict".into()),
7545 dependency_type: Some(fdecl::DependencyType::Strong),
7546 ..Default::default()
7547 }),
7548 fdecl::Offer::Storage(fdecl::OfferStorage {
7549 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7550 source_name: Some("source_storage".into()),
7551 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7552 name: "child".into(),
7553 collection: None
7554 })),
7555 target_name: Some("storage".into()),
7556 ..Default::default()
7557 }),
7558 fdecl::Offer::Config(fdecl::OfferConfiguration {
7559 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7560 source_name: Some("source_config".into()),
7561 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7562 name: "child".into(),
7563 collection: None
7564 })),
7565 target_name: Some("config".into()),
7566 ..Default::default()
7567 }),
7568 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7569 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7570 source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7571 source_dictionary: Some("dict/inner".into()),
7572 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7573 name: "child".into(),
7574 collection: None
7575 })),
7576 target_name: Some("baz".into()),
7577 dependency_type: Some(fdecl::DependencyType::Strong),
7578 ..Default::default()
7579 }),
7580 ]);
7581 decl
7582 },
7583 result = Err(ErrorList::new(vec![
7584 Error::invalid_capability(
7585 DeclType::OfferService,
7586 "source",
7587 "fuchsia.some.library.SomeProtocol"),
7588 Error::invalid_capability(
7589 DeclType::OfferProtocol,
7590 "source",
7591 "fuchsia.some.library.SomeProtocol"),
7592 Error::invalid_capability(DeclType::OfferDirectory, "source", "dir"),
7593 Error::invalid_capability(DeclType::OfferRunner, "source", "source_elf"),
7594 Error::invalid_capability(DeclType::OfferResolver, "source", "source_pkg"),
7595 Error::invalid_capability(DeclType::OfferDictionary, "source", "source_dict"),
7596 Error::invalid_capability(DeclType::OfferStorage, "source", "source_storage"),
7597 Error::invalid_capability(DeclType::OfferConfig, "source", "source_config"),
7598 Error::invalid_capability(DeclType::OfferProtocol, "source", "dict"),
7599 ])),
7600 },
7601 test_validate_offers_long_dependency_cycle => {
7602 input = {
7603 let mut decl = new_component_decl();
7604 let dependencies = vec![
7605 ("d", "b"),
7606 ("a", "b"),
7607 ("b", "c"),
7608 ("b", "d"),
7609 ("c", "a"),
7610 ];
7611 let offers = dependencies.into_iter().map(|(from,to)|
7612 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7613 source: Some(fdecl::Ref::Child(
7614 fdecl::ChildRef { name: from.to_string(), collection: None },
7615 )),
7616 source_name: Some(format!("thing_{}", from)),
7617 target: Some(fdecl::Ref::Child(
7618 fdecl::ChildRef { name: to.to_string(), collection: None },
7619 )),
7620 target_name: Some(format!("thing_{}", from)),
7621 dependency_type: Some(fdecl::DependencyType::Strong),
7622 ..Default::default()
7623 })).collect();
7624 let children = ["a", "b", "c", "d"].iter().map(|name| {
7625 fdecl::Child {
7626 name: Some(name.to_string()),
7627 url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
7628 startup: Some(fdecl::StartupMode::Lazy),
7629 on_terminate: None,
7630 environment: None,
7631 ..Default::default()
7632 }
7633 }).collect();
7634 decl.offers = Some(offers);
7635 decl.children = Some(children);
7636 decl
7637 },
7638 result = Err(ErrorList::new(vec![
7639 Error::dependency_cycle("{{child a -> child b -> child c -> child a}, {child b -> child d -> child b}}")
7640 ])),
7641 },
7642 test_validate_offers_not_required_invalid_source_service => {
7643 input = {
7644 let mut decl = generate_offer_different_source_and_availability_decl(
7645 |source, availability, target_name|
7646 fdecl::Offer::Service(fdecl::OfferService {
7647 source: Some(source),
7648 source_name: Some("fuchsia.examples.Echo".to_string()),
7649 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7650 name: "sink".to_string(),
7651 collection: None,
7652 })),
7653 target_name: Some(target_name.into()),
7654 availability: Some(availability),
7655 ..Default::default()
7656 })
7657 );
7658 decl.capabilities = Some(vec![
7659 fdecl::Capability::Service(fdecl::Service {
7660 name: Some("fuchsia.examples.Echo".to_string()),
7661 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
7662 ..Default::default()
7663 }),
7664 ]);
7665 decl
7666 },
7667 result = {
7668 Err(ErrorList::new(vec![
7669 Error::availability_must_be_optional(
7670 DeclType::OfferService,
7671 "availability",
7672 Some(&"fuchsia.examples.Echo".to_string()),
7673 ),
7674 Error::availability_must_be_optional(
7675 DeclType::OfferService,
7676 "availability",
7677 Some(&"fuchsia.examples.Echo".to_string()),
7678 ),
7679 ]))
7680 },
7681 },
7682 test_validate_offers_not_required_invalid_source_protocol => {
7683 input = {
7684 let mut decl = generate_offer_different_source_and_availability_decl(
7685 |source, availability, target_name|
7686 fdecl::Offer::Protocol(fdecl::OfferProtocol {
7687 source: Some(source),
7688 source_name: Some("fuchsia.examples.Echo".to_string()),
7689 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7690 name: "sink".to_string(),
7691 collection: None,
7692 })),
7693 target_name: Some(target_name.into()),
7694 dependency_type: Some(fdecl::DependencyType::Strong),
7695 availability: Some(availability),
7696 ..Default::default()
7697 })
7698 );
7699 decl.capabilities = Some(vec![
7700 fdecl::Capability::Protocol(fdecl::Protocol {
7701 name: Some("fuchsia.examples.Echo".to_string()),
7702 source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
7703 ..Default::default()
7704 }),
7705 ]);
7706 decl
7707 },
7708 result = {
7709 Err(ErrorList::new(vec![
7710 Error::availability_must_be_optional(
7711 DeclType::OfferProtocol,
7712 "availability",
7713 Some(&"fuchsia.examples.Echo".to_string()),
7714 ),
7715 Error::availability_must_be_optional(
7716 DeclType::OfferProtocol,
7717 "availability",
7718 Some(&"fuchsia.examples.Echo".to_string()),
7719 ),
7720 ]))
7721 },
7722 },
7723 test_validate_offers_not_required_invalid_source_directory => {
7724 input = {
7725 let mut decl = generate_offer_different_source_and_availability_decl(
7726 |source, availability, target_name|
7727 fdecl::Offer::Directory(fdecl::OfferDirectory {
7728 source: Some(source),
7729 source_name: Some("assets".to_string()),
7730 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7731 name: "sink".to_string(),
7732 collection: None,
7733 })),
7734 target_name: Some(target_name.into()),
7735 rights: Some(fio::Operations::CONNECT),
7736 subdir: None,
7737 dependency_type: Some(fdecl::DependencyType::Weak),
7738 availability: Some(availability),
7739 ..Default::default()
7740 })
7741 );
7742 decl.capabilities = Some(vec![
7743 fdecl::Capability::Directory(fdecl::Directory {
7744 name: Some("assets".to_string()),
7745 source_path: Some("/assets".to_string()),
7746 rights: Some(fio::Operations::CONNECT),
7747 ..Default::default()
7748 }),
7749 ]);
7750 decl
7751 },
7752 result = {
7753 Err(ErrorList::new(vec![
7754 Error::availability_must_be_optional(
7755 DeclType::OfferDirectory,
7756 "availability",
7757 Some(&"assets".to_string()),
7758 ),
7759 Error::availability_must_be_optional(
7760 DeclType::OfferDirectory,
7761 "availability",
7762 Some(&"assets".to_string()),
7763 ),
7764 ]))
7765 },
7766 },
7767 test_validate_offers_not_required_invalid_source_storage => {
7768 input = {
7769 let mut decl = new_component_decl();
7770 decl.children = Some(vec![
7771 fdecl::Child {
7772 name: Some("sink".to_string()),
7773 url: Some("fuchsia-pkg://fuchsia.com/sink#meta/sink.cm".to_string()),
7774 startup: Some(fdecl::StartupMode::Lazy),
7775 on_terminate: None,
7776 environment: None,
7777 ..Default::default()
7778 },
7779 ]);
7780 decl.capabilities = Some(vec![
7781 fdecl::Capability::Storage(fdecl::Storage {
7782 name: Some("data".to_string()),
7783 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
7784 backing_dir: Some("minfs".to_string()),
7785 subdir: None,
7786 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
7787 ..Default::default()
7788 }),
7789 ]);
7790 let new_offer = |source: fdecl::Ref, availability: fdecl::Availability,
7791 target_name: &str|
7792 {
7793 fdecl::Offer::Storage(fdecl::OfferStorage {
7794 source: Some(source),
7795 source_name: Some("data".to_string()),
7796 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7797 name: "sink".to_string(),
7798 collection: None,
7799 })),
7800 target_name: Some(target_name.into()),
7801 availability: Some(availability),
7802 ..Default::default()
7803 })
7804 };
7805 decl.offers = Some(vec![
7806 new_offer(
7809 fdecl::Ref::Parent(fdecl::ParentRef {}),
7810 fdecl::Availability::Required,
7811 "data0",
7812 ),
7813 new_offer(
7814 fdecl::Ref::Parent(fdecl::ParentRef {}),
7815 fdecl::Availability::Optional,
7816 "data1",
7817 ),
7818 new_offer(
7819 fdecl::Ref::Parent(fdecl::ParentRef {}),
7820 fdecl::Availability::SameAsTarget,
7821 "data2",
7822 ),
7823 new_offer(
7824 fdecl::Ref::VoidType(fdecl::VoidRef {}),
7825 fdecl::Availability::Optional,
7826 "data3",
7827 ),
7828 new_offer(
7831 fdecl::Ref::Self_(fdecl::SelfRef {}),
7832 fdecl::Availability::Optional,
7833 "data4",
7834 ),
7835 new_offer(
7836 fdecl::Ref::Self_(fdecl::SelfRef {}),
7837 fdecl::Availability::SameAsTarget,
7838 "data5",
7839 ),
7840 new_offer(
7842 fdecl::Ref::VoidType(fdecl::VoidRef {}),
7843 fdecl::Availability::Required,
7844 "data6",
7845 ),
7846 new_offer(
7847 fdecl::Ref::VoidType(fdecl::VoidRef {}),
7848 fdecl::Availability::SameAsTarget,
7849 "data7",
7850 ),
7851 ]);
7852 decl
7853 },
7854 result = {
7855 Err(ErrorList::new(vec![
7856 Error::availability_must_be_optional(
7857 DeclType::OfferStorage,
7858 "availability",
7859 Some(&"data".to_string()),
7860 ),
7861 Error::availability_must_be_optional(
7862 DeclType::OfferStorage,
7863 "availability",
7864 Some(&"data".to_string()),
7865 ),
7866 ]))
7867 },
7868 },
7869
7870 test_validate_offers_valid_service_aggregation => {
7871 input = {
7872 let mut decl = new_component_decl();
7873 decl.offers = Some(vec![
7874 fdecl::Offer::Service(fdecl::OfferService {
7875 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
7876 name: "coll_a".to_string()
7877 })),
7878 source_name: Some("fuchsia.logger.Log".to_string()),
7879 target: Some(fdecl::Ref::Child(
7880 fdecl::ChildRef {
7881 name: "child_c".to_string(),
7882 collection: None,
7883 }
7884 )),
7885 target_name: Some("fuchsia.logger.Log".to_string()),
7886 source_instance_filter: None,
7887 ..Default::default()
7888 }),
7889 fdecl::Offer::Service(fdecl::OfferService {
7890 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
7891 name: "coll_b".to_string()
7892 })),
7893 source_name: Some("fuchsia.logger.Log".to_string()),
7894 target: Some(fdecl::Ref::Child(
7895 fdecl::ChildRef {
7896 name: "child_c".to_string(),
7897 collection: None,
7898 }
7899 )),
7900 target_name: Some("fuchsia.logger.Log".to_string()),
7901 source_instance_filter: Some(vec!["a_different_default".to_string()]),
7902 ..Default::default()
7903 })
7904 ]);
7905 decl.children = Some(vec![
7906 fdecl::Child {
7907 name: Some("child_c".to_string()),
7908 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
7909 startup: Some(fdecl::StartupMode::Lazy),
7910 ..Default::default()
7911 },
7912 ]);
7913 decl.collections = Some(vec![
7914 fdecl::Collection {
7915 name: Some("coll_a".into()),
7916 durability: Some(fdecl::Durability::Transient),
7917 ..Default::default()
7918 },
7919 fdecl::Collection {
7920 name: Some("coll_b".into()),
7921 durability: Some(fdecl::Durability::Transient),
7922 ..Default::default()
7923 },
7924 ]);
7925 decl
7926 },
7927 result = Ok(()),
7928 },
7929
7930 test_validate_source_dictionary => {
7932 input = fdecl::Component {
7933 program: Some(fdecl::Program {
7934 runner: Some("elf".into()),
7935 info: Some(fdata::Dictionary {
7936 entries: None,
7937 ..Default::default()
7938 }),
7939 ..Default::default()
7940 }),
7941 uses: Some(vec![
7942 fdecl::Use::Protocol(fdecl::UseProtocol {
7943 dependency_type: Some(fdecl::DependencyType::Strong),
7944 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
7945 source_dictionary: Some("bad//".into()),
7946 source_name: Some("foo".into()),
7947 target_path: Some("/svc/foo".into()),
7948 ..Default::default()
7949 }),
7950 ]),
7951 exposes: Some(vec![
7952 fdecl::Expose::Directory(fdecl::ExposeDirectory {
7953 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7954 name: "missing".into(),
7955 collection: None,
7956 })),
7957 source_dictionary: Some("in/dict".into()),
7958 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
7959 source_name: Some("foo".into()),
7960 target_name: Some("bar".into()),
7961 ..Default::default()
7962 }),
7963 ]),
7964 offers: Some(vec![
7965 fdecl::Offer::Service(fdecl::OfferService {
7966 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
7967 source_dictionary: Some("bad//".into()),
7968 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7969 name: "child".into(),
7970 collection: None,
7971 })),
7972 source_name: Some("foo".into()),
7973 target_name: Some("bar".into()),
7974 ..Default::default()
7975 }),
7976 ]),
7977 children: Some(vec![
7978 fdecl::Child {
7979 name: Some("child".into()),
7980 url: Some("fuchsia-pkg://child".into()),
7981 startup: Some(fdecl::StartupMode::Lazy),
7982 ..Default::default()
7983 },
7984 ]),
7985 ..Default::default()
7986 },
7987 result = Err(ErrorList::new(vec![
7988 Error::invalid_field(DeclType::UseProtocol, "source_dictionary"),
7989 Error::invalid_child(DeclType::ExposeDirectory, "source", "missing"),
7990 Error::invalid_field(DeclType::OfferService, "source_dictionary"),
7991 ])),
7992 },
7993 test_validate_dictionary_too_long => {
7994 input = fdecl::Component {
7995 program: Some(fdecl::Program {
7996 runner: Some("elf".into()),
7997 info: Some(fdata::Dictionary {
7998 entries: None,
7999 ..Default::default()
8000 }),
8001 ..Default::default()
8002 }),
8003 uses: Some(vec![
8004 fdecl::Use::Protocol(fdecl::UseProtocol {
8005 dependency_type: Some(fdecl::DependencyType::Strong),
8006 source: Some(fdecl::Ref::Parent( fdecl::ParentRef {} )),
8007 source_dictionary: Some("a".repeat(4096)),
8008 source_name: Some("foo".into()),
8009 target_path: Some("/svc/foo".into()),
8010 ..Default::default()
8011 }),
8012 ]),
8013 ..Default::default()
8014 },
8015 result = Err(ErrorList::new(vec![
8016 Error::field_too_long(DeclType::UseProtocol, "source_dictionary"),
8017 ])),
8018 },
8019
8020 test_validate_environment_empty => {
8022 input = {
8023 let mut decl = new_component_decl();
8024 decl.environments = Some(vec![fdecl::Environment {
8025 name: None,
8026 extends: None,
8027 runners: None,
8028 resolvers: None,
8029 stop_timeout_ms: None,
8030 debug_capabilities: None,
8031 ..Default::default()
8032 }]);
8033 decl
8034 },
8035 result = Err(ErrorList::new(vec![
8036 Error::missing_field(DeclType::Environment, "name"),
8037 Error::missing_field(DeclType::Environment, "extends"),
8038 ])),
8039 },
8040
8041 test_validate_environment_no_stop_timeout => {
8042 input = {
8043 let mut decl = new_component_decl();
8044 decl.environments = Some(vec![fdecl::Environment {
8045 name: Some("env".to_string()),
8046 extends: Some(fdecl::EnvironmentExtends::None),
8047 runners: None,
8048 resolvers: None,
8049 stop_timeout_ms: None,
8050 ..Default::default()
8051 }]);
8052 decl
8053 },
8054 result = Err(ErrorList::new(vec![Error::missing_field(DeclType::Environment, "stop_timeout_ms")])),
8055 },
8056
8057 test_validate_environment_extends_stop_timeout => {
8058 input = { let mut decl = new_component_decl();
8059 decl.environments = Some(vec![fdecl::Environment {
8060 name: Some("env".to_string()),
8061 extends: Some(fdecl::EnvironmentExtends::Realm),
8062 runners: None,
8063 resolvers: None,
8064 stop_timeout_ms: None,
8065 ..Default::default()
8066 }]);
8067 decl
8068 },
8069 result = Ok(()),
8070 },
8071 test_validate_environment_long_identifiers => {
8072 input = {
8073 let mut decl = new_component_decl();
8074 decl.environments = Some(vec![fdecl::Environment {
8075 name: Some("a".repeat(256)),
8076 extends: Some(fdecl::EnvironmentExtends::None),
8077 runners: Some(vec![
8078 fdecl::RunnerRegistration {
8079 source_name: Some("a".repeat(256)),
8080 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8081 target_name: Some("a".repeat(256)),
8082 ..Default::default()
8083 },
8084 ]),
8085 resolvers: Some(vec![
8086 fdecl::ResolverRegistration {
8087 resolver: Some("a".repeat(256)),
8088 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8089 scheme: Some("a".repeat(256)),
8090 ..Default::default()
8091 },
8092 ]),
8093 stop_timeout_ms: Some(1234),
8094 ..Default::default()
8095 }]);
8096 decl
8097 },
8098 result = Err(ErrorList::new(vec![
8099 Error::field_too_long(DeclType::Environment, "name"),
8100 Error::field_too_long(DeclType::RunnerRegistration, "source_name"),
8101 Error::field_too_long(DeclType::RunnerRegistration, "target_name"),
8102 Error::field_too_long(DeclType::ResolverRegistration, "resolver"),
8103 Error::field_too_long(DeclType::ResolverRegistration, "scheme"),
8104 ])),
8105 },
8106 test_validate_environment_empty_runner_resolver_fields => {
8107 input = {
8108 let mut decl = new_component_decl();
8109 decl.environments = Some(vec![fdecl::Environment {
8110 name: Some("a".to_string()),
8111 extends: Some(fdecl::EnvironmentExtends::None),
8112 runners: Some(vec![
8113 fdecl::RunnerRegistration {
8114 source_name: None,
8115 source: None,
8116 target_name: None,
8117 ..Default::default()
8118 },
8119 ]),
8120 resolvers: Some(vec![
8121 fdecl::ResolverRegistration {
8122 resolver: None,
8123 source: None,
8124 scheme: None,
8125 ..Default::default()
8126 },
8127 ]),
8128 stop_timeout_ms: Some(1234),
8129 ..Default::default()
8130 }]);
8131 decl
8132 },
8133 result = Err(ErrorList::new(vec![
8134 Error::missing_field(DeclType::RunnerRegistration, "source_name"),
8135 Error::missing_field(DeclType::RunnerRegistration, "source"),
8136 Error::missing_field(DeclType::RunnerRegistration, "target_name"),
8137 Error::missing_field(DeclType::ResolverRegistration, "resolver"),
8138 Error::missing_field(DeclType::ResolverRegistration, "source"),
8139 Error::missing_field(DeclType::ResolverRegistration, "scheme"),
8140 ])),
8141 },
8142 test_validate_environment_invalid_fields => {
8143 input = {
8144 let mut decl = new_component_decl();
8145 decl.environments = Some(vec![fdecl::Environment {
8146 name: Some("a".to_string()),
8147 extends: Some(fdecl::EnvironmentExtends::None),
8148 runners: Some(vec![
8149 fdecl::RunnerRegistration {
8150 source_name: Some("^a".to_string()),
8151 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
8152 target_name: Some("%a".to_string()),
8153 ..Default::default()
8154 },
8155 ]),
8156 resolvers: Some(vec![
8157 fdecl::ResolverRegistration {
8158 resolver: Some("^a".to_string()),
8159 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
8160 scheme: Some("9scheme".to_string()),
8161 ..Default::default()
8162 },
8163 ]),
8164 stop_timeout_ms: Some(1234),
8165 ..Default::default()
8166 }]);
8167 decl
8168 },
8169 result = Err(ErrorList::new(vec![
8170 Error::invalid_field(DeclType::RunnerRegistration, "source_name"),
8171 Error::invalid_field(DeclType::RunnerRegistration, "source"),
8172 Error::invalid_field(DeclType::RunnerRegistration, "target_name"),
8173 Error::invalid_field(DeclType::ResolverRegistration, "resolver"),
8174 Error::invalid_field(DeclType::ResolverRegistration, "source"),
8175 Error::invalid_field(DeclType::ResolverRegistration, "scheme"),
8176 ])),
8177 },
8178 test_validate_environment_missing_runner => {
8179 input = {
8180 let mut decl = new_component_decl();
8181 decl.environments = Some(vec![fdecl::Environment {
8182 name: Some("a".to_string()),
8183 extends: Some(fdecl::EnvironmentExtends::None),
8184 runners: Some(vec![
8185 fdecl::RunnerRegistration {
8186 source_name: Some("dart".to_string()),
8187 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
8188 target_name: Some("dart".to_string()),
8189 ..Default::default()
8190 },
8191 ]),
8192 resolvers: None,
8193 stop_timeout_ms: Some(1234),
8194 ..Default::default()
8195 }]);
8196 decl
8197 },
8198 result = Err(ErrorList::new(vec![
8199 Error::invalid_runner(DeclType::RunnerRegistration, "source_name", "dart"),
8200 ])),
8201 },
8202 test_validate_environment_duplicate_registrations => {
8203 input = {
8204 let mut decl = new_component_decl();
8205 decl.environments = Some(vec![fdecl::Environment {
8206 name: Some("a".to_string()),
8207 extends: Some(fdecl::EnvironmentExtends::None),
8208 runners: Some(vec![
8209 fdecl::RunnerRegistration {
8210 source_name: Some("dart".to_string()),
8211 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8212 target_name: Some("dart".to_string()),
8213 ..Default::default()
8214 },
8215 fdecl::RunnerRegistration {
8216 source_name: Some("other-dart".to_string()),
8217 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8218 target_name: Some("dart".to_string()),
8219 ..Default::default()
8220 },
8221 ]),
8222 resolvers: Some(vec![
8223 fdecl::ResolverRegistration {
8224 resolver: Some("pkg_resolver".to_string()),
8225 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8226 scheme: Some("fuchsia-pkg".to_string()),
8227 ..Default::default()
8228 },
8229 fdecl::ResolverRegistration {
8230 resolver: Some("base_resolver".to_string()),
8231 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8232 scheme: Some("fuchsia-pkg".to_string()),
8233 ..Default::default()
8234 },
8235 ]),
8236 stop_timeout_ms: Some(1234),
8237 ..Default::default()
8238 }]);
8239 decl
8240 },
8241 result = Err(ErrorList::new(vec![
8242 Error::duplicate_field(DeclType::RunnerRegistration, "target_name", "dart"),
8243 Error::duplicate_field(DeclType::ResolverRegistration, "scheme", "fuchsia-pkg"),
8244 ])),
8245 },
8246 test_validate_environment_from_missing_child => {
8247 input = {
8248 let mut decl = new_component_decl();
8249 decl.environments = Some(vec![fdecl::Environment {
8250 name: Some("a".to_string()),
8251 extends: Some(fdecl::EnvironmentExtends::None),
8252 runners: Some(vec![
8253 fdecl::RunnerRegistration {
8254 source_name: Some("elf".to_string()),
8255 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8256 name: "missing".to_string(),
8257 collection: None,
8258 })),
8259 target_name: Some("elf".to_string()),
8260 ..Default::default()
8261 },
8262 ]),
8263 resolvers: Some(vec![
8264 fdecl::ResolverRegistration {
8265 resolver: Some("pkg_resolver".to_string()),
8266 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8267 name: "missing".to_string(),
8268 collection: None,
8269 })),
8270 scheme: Some("fuchsia-pkg".to_string()),
8271 ..Default::default()
8272 },
8273 ]),
8274 stop_timeout_ms: Some(1234),
8275 ..Default::default()
8276 }]);
8277 decl
8278 },
8279 result = Err(ErrorList::new(vec![
8280 Error::invalid_child(DeclType::RunnerRegistration, "source", "missing"),
8281 Error::invalid_child(DeclType::ResolverRegistration, "source", "missing"),
8282 ])),
8283 },
8284 test_validate_environment_runner_child_cycle => {
8285 input = {
8286 let mut decl = new_component_decl();
8287 decl.environments = Some(vec![fdecl::Environment {
8288 name: Some("env".to_string()),
8289 extends: Some(fdecl::EnvironmentExtends::None),
8290 runners: Some(vec![
8291 fdecl::RunnerRegistration {
8292 source_name: Some("elf".to_string()),
8293 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8294 name: "child".to_string(),
8295 collection: None,
8296 })),
8297 target_name: Some("elf".to_string()),
8298 ..Default::default()
8299 },
8300 ]),
8301 resolvers: None,
8302 stop_timeout_ms: Some(1234),
8303 ..Default::default()
8304 }]);
8305 decl.children = Some(vec![fdecl::Child {
8306 name: Some("child".to_string()),
8307 startup: Some(fdecl::StartupMode::Lazy),
8308 on_terminate: None,
8309 url: Some("fuchsia-pkg://child".to_string()),
8310 environment: Some("env".to_string()),
8311 ..Default::default()
8312 }]);
8313 decl
8314 },
8315 result = Err(ErrorList::new(vec![
8316 Error::dependency_cycle(
8317 "{{child child -> environment env -> child child}}"
8318 ),
8319 ])),
8320 },
8321 test_validate_environment_resolver_child_cycle => {
8322 input = {
8323 let mut decl = new_component_decl();
8324 decl.environments = Some(vec![fdecl::Environment {
8325 name: Some("env".to_string()),
8326 extends: Some(fdecl::EnvironmentExtends::None),
8327 runners: None,
8328 resolvers: Some(vec![
8329 fdecl::ResolverRegistration {
8330 resolver: Some("pkg_resolver".to_string()),
8331 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8332 name: "child".to_string(),
8333 collection: None,
8334 })),
8335 scheme: Some("fuchsia-pkg".to_string()),
8336 ..Default::default()
8337 },
8338 ]),
8339 stop_timeout_ms: Some(1234),
8340 ..Default::default()
8341 }]);
8342 decl.children = Some(vec![fdecl::Child {
8343 name: Some("child".to_string()),
8344 startup: Some(fdecl::StartupMode::Lazy),
8345 on_terminate: None,
8346 url: Some("fuchsia-pkg://child".to_string()),
8347 environment: Some("env".to_string()),
8348 ..Default::default()
8349 }]);
8350 decl
8351 },
8352 result = Err(ErrorList::new(vec![
8353 Error::dependency_cycle(
8354 "{{child child -> environment env -> child child}}"
8355 ),
8356 ])),
8357 },
8358 test_validate_environment_resolver_multiple_children_cycle => {
8359 input = {
8360 let mut decl = new_component_decl();
8361 decl.environments = Some(vec![fdecl::Environment {
8362 name: Some("env".to_string()),
8363 extends: Some(fdecl::EnvironmentExtends::None),
8364 runners: None,
8365 resolvers: Some(vec![
8366 fdecl::ResolverRegistration {
8367 resolver: Some("pkg_resolver".to_string()),
8368 source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8369 name: "a".to_string(),
8370 collection: None,
8371 })),
8372 scheme: Some("fuchsia-pkg".to_string()),
8373 ..Default::default()
8374 },
8375 ]),
8376 stop_timeout_ms: Some(1234),
8377 ..Default::default()
8378 }]);
8379 decl.children = Some(vec![
8380 fdecl::Child {
8381 name: Some("a".to_string()),
8382 startup: Some(fdecl::StartupMode::Lazy),
8383 on_terminate: None,
8384 url: Some("fuchsia-pkg://child-a".to_string()),
8385 environment: None,
8386 ..Default::default()
8387 },
8388 fdecl::Child {
8389 name: Some("b".to_string()),
8390 startup: Some(fdecl::StartupMode::Lazy),
8391 on_terminate: None,
8392 url: Some("fuchsia-pkg://child-b".to_string()),
8393 environment: Some("env".to_string()),
8394 ..Default::default()
8395 },
8396 ]);
8397 decl.offers = Some(vec![fdecl::Offer::Service(fdecl::OfferService {
8398 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8399 name: "b".to_string(),
8400 collection: None,
8401 })),
8402 source_name: Some("thing".to_string()),
8403 target: Some(fdecl::Ref::Child(
8404 fdecl::ChildRef {
8405 name: "a".to_string(),
8406 collection: None,
8407 }
8408 )),
8409 target_name: Some("thing".to_string()),
8410 ..Default::default()
8411 })]);
8412 decl
8413 },
8414 result = Err(ErrorList::new(vec![
8415 Error::dependency_cycle(
8416 "{{child a -> environment env -> child b -> child a}}"
8417 ),
8418 ])),
8419 },
8420 test_validate_environment_debug_empty => {
8421 input = {
8422 let mut decl = new_component_decl();
8423 decl.environments = Some(vec![
8424 fdecl::Environment {
8425 name: Some("a".to_string()),
8426 extends: Some(fdecl::EnvironmentExtends::None),
8427 stop_timeout_ms: Some(2),
8428 debug_capabilities:Some(vec![
8429 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8430 source: None,
8431 source_name: None,
8432 target_name: None,
8433 ..Default::default()
8434 }),
8435 ]),
8436 ..Default::default()
8437 }]);
8438 decl
8439 },
8440 result = Err(ErrorList::new(vec![
8441 Error::missing_field(DeclType::DebugProtocolRegistration, "source"),
8442 Error::missing_field(DeclType::DebugProtocolRegistration, "source_name"),
8443 Error::missing_field(DeclType::DebugProtocolRegistration, "target_name"),
8444 ])),
8445 },
8446 test_validate_environment_debug_log_identifier => {
8447 input = {
8448 let mut decl = new_component_decl();
8449 decl.environments = Some(vec![
8450 fdecl::Environment {
8451 name: Some("a".to_string()),
8452 extends: Some(fdecl::EnvironmentExtends::None),
8453 stop_timeout_ms: Some(2),
8454 debug_capabilities:Some(vec![
8455 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8456 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8457 name: "a".repeat(256),
8458 collection: None,
8459 })),
8460 source_name: Some(format!("{}", "a".repeat(256))),
8461 target_name: Some(format!("{}", "b".repeat(256))),
8462 ..Default::default()
8463 }),
8464 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8465 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8466 source_name: Some("a".to_string()),
8467 target_name: Some(format!("{}", "b".repeat(256))),
8468 ..Default::default()
8469 }),
8470 ]),
8471 ..Default::default()
8472 }]);
8473 decl
8474 },
8475 result = Err(ErrorList::new(vec![
8476 Error::field_too_long(DeclType::DebugProtocolRegistration, "source.child.name"),
8477 Error::field_too_long(DeclType::DebugProtocolRegistration, "source_name"),
8478 Error::field_too_long(DeclType::DebugProtocolRegistration, "target_name"),
8479 Error::field_too_long(DeclType::DebugProtocolRegistration, "target_name"),
8480 ])),
8481 },
8482 test_validate_environment_debug_log_extraneous => {
8483 input = {
8484 let mut decl = new_component_decl();
8485 decl.environments = Some(vec![
8486 fdecl::Environment {
8487 name: Some("a".to_string()),
8488 extends: Some(fdecl::EnvironmentExtends::None),
8489 stop_timeout_ms: Some(2),
8490 debug_capabilities:Some(vec![
8491 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8492 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8493 name: "logger".to_string(),
8494 collection: Some("modular".to_string()),
8495 })),
8496 source_name: Some("fuchsia.logger.Log".to_string()),
8497 target_name: Some("fuchsia.logger.Log".to_string()),
8498 ..Default::default()
8499 }),
8500 ]),
8501 ..Default::default()
8502 }]);
8503 decl
8504 },
8505 result = Err(ErrorList::new(vec![
8506 Error::extraneous_field(DeclType::DebugProtocolRegistration, "source.child.collection"),
8507 ])),
8508 },
8509 test_validate_environment_debug_log_invalid_identifiers => {
8510 input = {
8511 let mut decl = new_component_decl();
8512 decl.environments = Some(vec![
8513 fdecl::Environment {
8514 name: Some("a".to_string()),
8515 extends: Some(fdecl::EnvironmentExtends::None),
8516 stop_timeout_ms: Some(2),
8517 debug_capabilities:Some(vec![
8518 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8519 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8520 name: "^bad".to_string(),
8521 collection: None,
8522 })),
8523 source_name: Some("foo/".to_string()),
8524 target_name: Some("/".to_string()),
8525 ..Default::default()
8526 }),
8527 ]),
8528 ..Default::default()
8529 }]);
8530 decl
8531 },
8532 result = Err(ErrorList::new(vec![
8533 Error::invalid_field(DeclType::DebugProtocolRegistration, "source.child.name"),
8534 Error::invalid_field(DeclType::DebugProtocolRegistration, "source_name"),
8535 Error::invalid_field(DeclType::DebugProtocolRegistration, "target_name"),
8536 ])),
8537 },
8538 test_validate_environment_debug_log_invalid_child => {
8539 input = {
8540 let mut decl = new_component_decl();
8541 decl.environments = Some(vec![
8542 fdecl::Environment {
8543 name: Some("a".to_string()),
8544 extends: Some(fdecl::EnvironmentExtends::None),
8545 stop_timeout_ms: Some(2),
8546 debug_capabilities:Some(vec![
8547 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8548 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8549 name: "logger".to_string(),
8550 collection: None,
8551 })),
8552 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
8553 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
8554 ..Default::default()
8555 }),
8556 ]),
8557 ..Default::default()
8558 }]);
8559 decl.children = Some(vec![
8560 fdecl::Child {
8561 name: Some("netstack".to_string()),
8562 url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
8563 startup: Some(fdecl::StartupMode::Lazy),
8564 on_terminate: None,
8565 environment: None,
8566 ..Default::default()
8567 },
8568 ]);
8569 decl
8570 },
8571 result = Err(ErrorList::new(vec![
8572 Error::invalid_child(DeclType::DebugProtocolRegistration, "source", "logger"),
8573
8574 ])),
8575 },
8576 test_validate_environment_debug_source_capability => {
8577 input = {
8578 let mut decl = new_component_decl();
8579 decl.environments = Some(vec![
8580 fdecl::Environment {
8581 name: Some("a".to_string()),
8582 extends: Some(fdecl::EnvironmentExtends::None),
8583 stop_timeout_ms: Some(2),
8584 debug_capabilities:Some(vec![
8585 fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8586 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
8587 name: "storage".to_string(),
8588 })),
8589 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
8590 target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
8591 ..Default::default()
8592 }),
8593 ]),
8594 ..Default::default()
8595 }]);
8596 decl
8597 },
8598 result = Err(ErrorList::new(vec![
8599 Error::invalid_field(DeclType::DebugProtocolRegistration, "source"),
8600 ])),
8601 },
8602
8603 test_validate_children_empty => {
8605 input = {
8606 let mut decl = new_component_decl();
8607 decl.children = Some(vec![fdecl::Child{
8608 name: None,
8609 url: None,
8610 startup: None,
8611 on_terminate: None,
8612 environment: None,
8613 ..Default::default()
8614 }]);
8615 decl
8616 },
8617 result = Err(ErrorList::new(vec![
8618 Error::missing_field(DeclType::Child, "name"),
8619 Error::missing_field(DeclType::Child, "url"),
8620 Error::missing_field(DeclType::Child, "startup"),
8621 ])),
8623 },
8624 test_validate_children_invalid_identifiers => {
8625 input = {
8626 let mut decl = new_component_decl();
8627 decl.children = Some(vec![fdecl::Child{
8628 name: Some("^bad".to_string()),
8629 url: Some("scheme://invalid-port:99999999/path#frag".to_string()),
8630 startup: Some(fdecl::StartupMode::Lazy),
8631 on_terminate: None,
8632 environment: None,
8633 ..Default::default()
8634 }]);
8635 decl
8636 },
8637 result = Err(ErrorList::new(vec![
8638 Error::invalid_field(DeclType::Child, "name"),
8639 Error::invalid_url(DeclType::Child, "url", "\"scheme://invalid-port:99999999/path#frag\": Malformed URL: InvalidPort."),
8640 ])),
8641 },
8642 test_validate_children_long_identifiers => {
8643 input = {
8644 let mut decl = new_component_decl();
8645 decl.children = Some(vec![fdecl::Child{
8646 name: Some("a".repeat(1025)),
8647 url: Some(format!("fuchsia-pkg://{}", "a".repeat(4083))),
8648 startup: Some(fdecl::StartupMode::Lazy),
8649 on_terminate: None,
8650 environment: Some("a".repeat(1025)),
8651 ..Default::default()
8652 }]);
8653 decl
8654 },
8655 result = Err(ErrorList::new(vec![
8656 Error::field_too_long(DeclType::Child, "name"),
8657 Error::field_too_long(DeclType::Child, "url"),
8658 Error::field_too_long(DeclType::Child, "environment"),
8659 Error::invalid_environment(DeclType::Child, "environment", "a".repeat(1025)),
8660 ])),
8661 },
8662 test_validate_child_references_unknown_env => {
8663 input = {
8664 let mut decl = new_component_decl();
8665 decl.children = Some(vec![fdecl::Child{
8666 name: Some("foo".to_string()),
8667 url: Some("fuchsia-pkg://foo".to_string()),
8668 startup: Some(fdecl::StartupMode::Lazy),
8669 on_terminate: None,
8670 environment: Some("test_env".to_string()),
8671 ..Default::default()
8672 }]);
8673 decl
8674 },
8675 result = Err(ErrorList::new(vec![
8676 Error::invalid_environment(DeclType::Child, "environment", "test_env"),
8677 ])),
8678 },
8679
8680 test_validate_collections_empty => {
8682 input = {
8683 let mut decl = new_component_decl();
8684 decl.collections = Some(vec![fdecl::Collection{
8685 name: None,
8686 durability: None,
8687 environment: None,
8688 allowed_offers: None,
8689 allow_long_names: None,
8690 ..Default::default()
8691 }]);
8692 decl
8693 },
8694 result = Err(ErrorList::new(vec![
8695 Error::missing_field(DeclType::Collection, "name"),
8696 Error::missing_field(DeclType::Collection, "durability"),
8697 ])),
8698 },
8699 test_validate_collections_invalid_identifiers => {
8700 input = {
8701 let mut decl = new_component_decl();
8702 decl.collections = Some(vec![fdecl::Collection{
8703 name: Some("^bad".to_string()),
8704 durability: Some(fdecl::Durability::Transient),
8705 environment: None,
8706 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
8707 allow_long_names: None,
8708 ..Default::default()
8709 }]);
8710 decl
8711 },
8712 result = Err(ErrorList::new(vec![
8713 Error::invalid_field(DeclType::Collection, "name"),
8714 ])),
8715 },
8716 test_validate_collections_long_identifiers => {
8717 input = {
8718 let mut decl = new_component_decl();
8719 decl.collections = Some(vec![fdecl::Collection{
8720 name: Some("a".repeat(1025)),
8721 durability: Some(fdecl::Durability::Transient),
8722 environment: None,
8723 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
8724 allow_long_names: None,
8725 ..Default::default()
8726 }]);
8727 decl
8728 },
8729 result = Err(ErrorList::new(vec![
8730 Error::field_too_long(DeclType::Collection, "name"),
8731 ])),
8732 },
8733 test_validate_collection_references_unknown_env => {
8734 input = {
8735 let mut decl = new_component_decl();
8736 decl.collections = Some(vec![fdecl::Collection {
8737 name: Some("foo".to_string()),
8738 durability: Some(fdecl::Durability::Transient),
8739 environment: Some("test_env".to_string()),
8740 allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
8741 allow_long_names: None,
8742 ..Default::default()
8743 }]);
8744 decl
8745 },
8746 result = Err(ErrorList::new(vec![
8747 Error::invalid_environment(DeclType::Collection, "environment", "test_env"),
8748 ])),
8749 },
8750
8751 test_validate_capabilities_empty => {
8753 input = {
8754 let mut decl = new_component_decl();
8755 decl.capabilities = Some(vec![
8756 fdecl::Capability::Service(fdecl::Service {
8757 name: None,
8758 source_path: None,
8759 ..Default::default()
8760 }),
8761 fdecl::Capability::Protocol(fdecl::Protocol {
8762 name: None,
8763 source_path: None,
8764 ..Default::default()
8765 }),
8766 fdecl::Capability::Directory(fdecl::Directory {
8767 name: None,
8768 source_path: None,
8769 rights: None,
8770 ..Default::default()
8771 }),
8772 fdecl::Capability::Storage(fdecl::Storage {
8773 name: None,
8774 source: None,
8775 backing_dir: None,
8776 subdir: None,
8777 storage_id: None,
8778 ..Default::default()
8779 }),
8780 fdecl::Capability::Runner(fdecl::Runner {
8781 name: None,
8782 source_path: None,
8783 ..Default::default()
8784 }),
8785 fdecl::Capability::Resolver(fdecl::Resolver {
8786 name: None,
8787 source_path: None,
8788 ..Default::default()
8789 }),
8790 fdecl::Capability::Dictionary(fdecl::Dictionary {
8791 ..Default::default()
8792 }),
8793 ]);
8794 decl
8795 },
8796 result = Err(ErrorList::new(vec![
8797 Error::missing_field(DeclType::Dictionary, "name"),
8798 Error::missing_field(DeclType::Service, "name"),
8799 Error::missing_field(DeclType::Service, "source_path"),
8800 Error::missing_field(DeclType::Protocol, "name"),
8801 Error::missing_field(DeclType::Protocol, "source_path"),
8802 Error::missing_field(DeclType::Directory, "name"),
8803 Error::missing_field(DeclType::Directory, "source_path"),
8804 Error::missing_field(DeclType::Directory, "rights"),
8805 Error::missing_field(DeclType::Storage, "source"),
8806 Error::missing_field(DeclType::Storage, "name"),
8807 Error::missing_field(DeclType::Storage, "storage_id"),
8808 Error::missing_field(DeclType::Storage, "backing_dir"),
8809 Error::missing_field(DeclType::Runner, "name"),
8810 Error::missing_field(DeclType::Runner, "source_path"),
8811 Error::missing_field(DeclType::Resolver, "name"),
8812 Error::missing_field(DeclType::Resolver, "source_path"),
8813 ])),
8814 },
8815 test_validate_capabilities_invalid_identifiers => {
8816 input = {
8817 let mut decl = new_component_decl();
8818 decl.capabilities = Some(vec![
8819 fdecl::Capability::Service(fdecl::Service {
8820 name: Some("^bad".to_string()),
8821 source_path: Some("&bad".to_string()),
8822 ..Default::default()
8823 }),
8824 fdecl::Capability::Protocol(fdecl::Protocol {
8825 name: Some("^bad".to_string()),
8826 source_path: Some("&bad".to_string()),
8827 ..Default::default()
8828 }),
8829 fdecl::Capability::Directory(fdecl::Directory {
8830 name: Some("^bad".to_string()),
8831 source_path: Some("&bad".to_string()),
8832 rights: Some(fio::Operations::CONNECT),
8833 ..Default::default()
8834 }),
8835 fdecl::Capability::Storage(fdecl::Storage {
8836 name: Some("^bad".to_string()),
8837 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
8838 name: "/bad".to_string()
8839 })),
8840 backing_dir: Some("&bad".to_string()),
8841 subdir: None,
8842 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
8843 ..Default::default()
8844 }),
8845 fdecl::Capability::Runner(fdecl::Runner {
8846 name: Some("^bad".to_string()),
8847 source_path: Some("&bad".to_string()),
8848 ..Default::default()
8849 }),
8850 fdecl::Capability::Resolver(fdecl::Resolver {
8851 name: Some("^bad".to_string()),
8852 source_path: Some("&bad".to_string()),
8853 ..Default::default()
8854 }),
8855 fdecl::Capability::Dictionary(fdecl::Dictionary {
8856 name: Some("^bad".to_string()),
8857 ..Default::default()
8858 }),
8859 ]);
8860 decl
8861 },
8862 result = Err(ErrorList::new(vec![
8863 Error::invalid_field(DeclType::Dictionary, "name"),
8864 Error::invalid_field(DeclType::Service, "name"),
8865 Error::invalid_field(DeclType::Service, "source_path"),
8866 Error::invalid_field(DeclType::Protocol, "name"),
8867 Error::invalid_field(DeclType::Protocol, "source_path"),
8868 Error::invalid_field(DeclType::Directory, "name"),
8869 Error::invalid_field(DeclType::Directory, "source_path"),
8870 Error::invalid_field(DeclType::Storage, "source"),
8871 Error::invalid_field(DeclType::Storage, "name"),
8872 Error::invalid_field(DeclType::Storage, "backing_dir"),
8873 Error::invalid_field(DeclType::Runner, "name"),
8874 Error::invalid_field(DeclType::Runner, "source_path"),
8875 Error::invalid_field(DeclType::Resolver, "name"),
8876 Error::invalid_field(DeclType::Resolver, "source_path"),
8877 ])),
8878 },
8879 test_validate_capabilities_invalid_child => {
8880 input = {
8881 let mut decl = new_component_decl();
8882 decl.capabilities = Some(vec![
8883 fdecl::Capability::Storage(fdecl::Storage {
8884 name: Some("foo".to_string()),
8885 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
8886 name: "invalid".to_string(),
8887 })),
8888 backing_dir: Some("foo".to_string()),
8889 subdir: None,
8890 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
8891 ..Default::default()
8892 }),
8893 ]);
8894 decl
8895 },
8896 result = Err(ErrorList::new(vec![
8897 Error::invalid_field(DeclType::Storage, "source"),
8898 ])),
8899 },
8900 test_validate_capabilities_long_identifiers => {
8901 input = {
8902 let mut decl = new_component_decl();
8903 decl.capabilities = Some(vec![
8904 fdecl::Capability::Service(fdecl::Service {
8905 name: Some("a".repeat(256)),
8906 source_path: Some("/c".repeat(2048)),
8907 ..Default::default()
8908 }),
8909 fdecl::Capability::Protocol(fdecl::Protocol {
8910 name: Some("a".repeat(256)),
8911 source_path: Some("/c".repeat(2048)),
8912 ..Default::default()
8913 }),
8914 fdecl::Capability::Directory(fdecl::Directory {
8915 name: Some("a".repeat(256)),
8916 source_path: Some("/c".repeat(2048)),
8917 rights: Some(fio::Operations::CONNECT),
8918 ..Default::default()
8919 }),
8920 fdecl::Capability::Storage(fdecl::Storage {
8921 name: Some("a".repeat(256)),
8922 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8923 name: "b".repeat(256),
8924 collection: None,
8925 })),
8926 backing_dir: Some(format!("{}", "c".repeat(256))),
8927 subdir: None,
8928 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
8929 ..Default::default()
8930 }),
8931 fdecl::Capability::Runner(fdecl::Runner {
8932 name: Some("a".repeat(256)),
8933 source_path: Some("/c".repeat(2048)),
8934 ..Default::default()
8935 }),
8936 fdecl::Capability::Resolver(fdecl::Resolver {
8937 name: Some("a".repeat(256)),
8938 source_path: Some("/c".repeat(2048)),
8939 ..Default::default()
8940 }),
8941 fdecl::Capability::Dictionary(fdecl::Dictionary {
8942 name: Some("a".repeat(256)),
8943 ..Default::default()
8944 }),
8945 ]);
8946 decl
8947 },
8948 result = Err(ErrorList::new(vec![
8949 Error::field_too_long(DeclType::Dictionary, "name"),
8950 Error::field_too_long(DeclType::Service, "name"),
8951 Error::field_too_long(DeclType::Service, "source_path"),
8952 Error::field_too_long(DeclType::Protocol, "name"),
8953 Error::field_too_long(DeclType::Protocol, "source_path"),
8954 Error::field_too_long(DeclType::Directory, "name"),
8955 Error::field_too_long(DeclType::Directory, "source_path"),
8956 Error::field_too_long(DeclType::Storage, "source.child.name"),
8957 Error::field_too_long(DeclType::Storage, "name"),
8958 Error::field_too_long(DeclType::Storage, "backing_dir"),
8959 Error::field_too_long(DeclType::Runner, "name"),
8960 Error::field_too_long(DeclType::Runner, "source_path"),
8961 Error::field_too_long(DeclType::Resolver, "name"),
8962 Error::field_too_long(DeclType::Resolver, "source_path"),
8963 ])),
8964 },
8965 test_validate_capabilities_duplicate_name => {
8966 input = {
8967 let mut decl = new_component_decl();
8968 decl.capabilities = Some(vec![
8969 fdecl::Capability::Service(fdecl::Service {
8970 name: Some("service".to_string()),
8971 source_path: Some("/service".to_string()),
8972 ..Default::default()
8973 }),
8974 fdecl::Capability::Service(fdecl::Service {
8975 name: Some("service".to_string()),
8976 source_path: Some("/service".to_string()),
8977 ..Default::default()
8978 }),
8979 fdecl::Capability::Protocol(fdecl::Protocol {
8980 name: Some("protocol".to_string()),
8981 source_path: Some("/protocol".to_string()),
8982 ..Default::default()
8983 }),
8984 fdecl::Capability::Protocol(fdecl::Protocol {
8985 name: Some("protocol".to_string()),
8986 source_path: Some("/protocol".to_string()),
8987 ..Default::default()
8988 }),
8989 fdecl::Capability::Directory(fdecl::Directory {
8990 name: Some("directory".to_string()),
8991 source_path: Some("/directory".to_string()),
8992 rights: Some(fio::Operations::CONNECT),
8993 ..Default::default()
8994 }),
8995 fdecl::Capability::Directory(fdecl::Directory {
8996 name: Some("directory".to_string()),
8997 source_path: Some("/directory".to_string()),
8998 rights: Some(fio::Operations::CONNECT),
8999 ..Default::default()
9000 }),
9001 fdecl::Capability::Storage(fdecl::Storage {
9002 name: Some("storage".to_string()),
9003 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
9004 backing_dir: Some("directory".to_string()),
9005 subdir: None,
9006 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9007 ..Default::default()
9008 }),
9009 fdecl::Capability::Storage(fdecl::Storage {
9010 name: Some("storage".to_string()),
9011 source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
9012 backing_dir: Some("directory".to_string()),
9013 subdir: None,
9014 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9015 ..Default::default()
9016 }),
9017 fdecl::Capability::Runner(fdecl::Runner {
9018 name: Some("runner".to_string()),
9019 source_path: Some("/runner".to_string()),
9020 ..Default::default()
9021 }),
9022 fdecl::Capability::Runner(fdecl::Runner {
9023 name: Some("runner".to_string()),
9024 source_path: Some("/runner".to_string()),
9025 ..Default::default()
9026 }),
9027 fdecl::Capability::Resolver(fdecl::Resolver {
9028 name: Some("resolver".to_string()),
9029 source_path: Some("/resolver".to_string()),
9030 ..Default::default()
9031 }),
9032 fdecl::Capability::Resolver(fdecl::Resolver {
9033 name: Some("resolver".to_string()),
9034 source_path: Some("/resolver".to_string()),
9035 ..Default::default()
9036 }),
9037 fdecl::Capability::Dictionary(fdecl::Dictionary {
9038 name: Some("dictionary".to_string()),
9039 ..Default::default()
9040 }),
9041 fdecl::Capability::Dictionary(fdecl::Dictionary {
9042 name: Some("dictionary".to_string()),
9043 ..Default::default()
9044 }),
9045 ]);
9046 decl
9047 },
9048 result = Err(ErrorList::new(vec![
9049 Error::duplicate_field(DeclType::Dictionary, "name", "dictionary"),
9050 Error::duplicate_field(DeclType::Service, "name", "service"),
9051 Error::duplicate_field(DeclType::Protocol, "name", "protocol"),
9052 Error::duplicate_field(DeclType::Directory, "name", "directory"),
9053 Error::duplicate_field(DeclType::Storage, "name", "storage"),
9054 Error::duplicate_field(DeclType::Runner, "name", "runner"),
9055 Error::duplicate_field(DeclType::Resolver, "name", "resolver"),
9056 ])),
9057 },
9058 test_validate_invalid_service_aggregation_conflicting_filter => {
9059 input = {
9060 let mut decl = new_component_decl();
9061 decl.offers = Some(vec![
9062 fdecl::Offer::Service(fdecl::OfferService {
9063 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9064 name: "coll_a".to_string()
9065 })),
9066 source_name: Some("fuchsia.logger.Log".to_string()),
9067 target: Some(fdecl::Ref::Child(
9068 fdecl::ChildRef {
9069 name: "child_c".to_string(),
9070 collection: None,
9071 }
9072 )),
9073 target_name: Some("fuchsia.logger.Log1".to_string()),
9074 source_instance_filter: Some(vec!["default".to_string()]),
9075 ..Default::default()
9076 }),
9077 fdecl::Offer::Service(fdecl::OfferService {
9078 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9079 name: "coll_b".to_string()
9080 })),
9081 source_name: Some("fuchsia.logger.Log".to_string()),
9082 target: Some(fdecl::Ref::Child(
9083 fdecl::ChildRef {
9084 name: "child_c".to_string(),
9085 collection: None,
9086 }
9087 )),
9088 target_name: Some("fuchsia.logger.Log1".to_string()),
9089 source_instance_filter: Some(vec!["default".to_string()]),
9090 ..Default::default()
9091 }),
9092 ]);
9093 decl.collections = Some(vec![
9094 fdecl::Collection {
9095 name: Some("coll_a".to_string()),
9096 durability: Some(fdecl::Durability::Transient),
9097 ..Default::default()
9098 },
9099 fdecl::Collection {
9100 name: Some("coll_b".to_string()),
9101 durability: Some(fdecl::Durability::Transient),
9102 ..Default::default()
9103 },
9104 ]);
9105 decl.children = Some(vec![
9106 fdecl::Child {
9107 name: Some("child_c".to_string()),
9108 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9109 startup: Some(fdecl::StartupMode::Lazy),
9110 ..Default::default()
9111 },
9112 ]);
9113 decl
9114 },
9115 result = Err(ErrorList::new(vec![
9116 Error::invalid_aggregate_offer("Conflicting source_instance_filter in aggregate \
9117 service offer, instance_name 'default' seen in filter lists multiple times"),
9118 ])),
9119 },
9120
9121 test_validate_invalid_service_aggregation_conflicting_source_name => {
9122 input = {
9123 let mut decl = new_component_decl();
9124 decl.offers = Some(vec![
9125 fdecl::Offer::Service(fdecl::OfferService {
9126 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9127 name: "coll_a".into()
9128 })),
9129 source_name: Some("fuchsia.logger.Log".to_string()),
9130 target: Some(fdecl::Ref::Child(
9131 fdecl::ChildRef {
9132 name: "child_c".to_string(),
9133 collection: None,
9134 }
9135 )),
9136 target_name: Some("fuchsia.logger.Log2".to_string()),
9137 source_instance_filter: Some(vec!["default2".to_string()]),
9138 ..Default::default()
9139 }),
9140 fdecl::Offer::Service(fdecl::OfferService {
9141 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9142 name: "coll_b".into()
9143 })),
9144 source_name: Some("fuchsia.logger.LogAlt".to_string()),
9145 target: Some(fdecl::Ref::Child(
9146 fdecl::ChildRef {
9147 name: "child_c".to_string(),
9148 collection: None,
9149 }
9150 )),
9151 target_name: Some("fuchsia.logger.Log2".to_string()),
9152 source_instance_filter: Some(vec!["default".to_string()]),
9153 ..Default::default()
9154 })
9155 ]);
9156 decl.collections = Some(vec![
9157 fdecl::Collection {
9158 name: Some("coll_a".to_string()),
9159 durability: Some(fdecl::Durability::Transient),
9160 ..Default::default()
9161 },
9162 fdecl::Collection {
9163 name: Some("coll_b".to_string()),
9164 durability: Some(fdecl::Durability::Transient),
9165 ..Default::default()
9166 },
9167 ]);
9168 decl.children = Some(vec![
9169 fdecl::Child {
9170 name: Some("child_c".to_string()),
9171 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9172 startup: Some(fdecl::StartupMode::Lazy),
9173 on_terminate: None,
9174 environment: None,
9175 ..Default::default()
9176 },
9177 ]);
9178 decl
9179 },
9180 result = Err(ErrorList::new(vec![
9181 Error::invalid_aggregate_offer("All aggregate service offers must have the same source_name, saw fuchsia.logger.Log, fuchsia.logger.LogAlt. Use renamed_instances to rename instance names to avoid conflict."),
9182 ])),
9183 },
9184
9185 test_validate_resolvers_missing_from_offer => {
9186 input = {
9187 let mut decl = new_component_decl();
9188 decl.offers = Some(vec![fdecl::Offer::Resolver(fdecl::OfferResolver {
9189 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
9190 source_name: Some("a".to_string()),
9191 target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
9192 target_name: Some("a".to_string()),
9193 ..Default::default()
9194 })]);
9195 decl.children = Some(vec![fdecl::Child {
9196 name: Some("child".to_string()),
9197 url: Some("test:///child".to_string()),
9198 startup: Some(fdecl::StartupMode::Eager),
9199 on_terminate: None,
9200 environment: None,
9201 ..Default::default()
9202 }]);
9203 decl
9204 },
9205 result = Err(ErrorList::new(vec![
9206 Error::invalid_capability(DeclType::OfferResolver, "source", "a"),
9207 ])),
9208 },
9209 test_validate_resolvers_missing_from_expose => {
9210 input = {
9211 let mut decl = new_component_decl();
9212 decl.exposes = Some(vec![fdecl::Expose::Resolver(fdecl::ExposeResolver {
9213 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
9214 source_name: Some("a".to_string()),
9215 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
9216 target_name: Some("a".to_string()),
9217 ..Default::default()
9218 })]);
9219 decl
9220 },
9221 result = Err(ErrorList::new(vec![
9222 Error::invalid_capability(DeclType::ExposeResolver, "source", "a"),
9223 ])),
9224 },
9225
9226 test_validate_config_missing_config => {
9227 input = {
9228 let mut decl = new_component_decl();
9229 decl.config = Some(fdecl::ConfigSchema{
9230 fields: None,
9231 checksum: None,
9232 value_source: None,
9233 ..Default::default()
9234 });
9235 decl
9236 },
9237 result = Err(ErrorList::new(vec![
9238 Error::missing_field(DeclType::ConfigSchema, "fields"),
9239 Error::missing_field(DeclType::ConfigSchema, "checksum"),
9240 Error::missing_field(DeclType::ConfigSchema, "value_source"),
9241 ])),
9242 },
9243
9244 test_validate_config_missing_config_field => {
9245 input = {
9246 let mut decl = new_component_decl();
9247 decl.config = Some(fdecl::ConfigSchema{
9248 fields: Some(vec![
9249 fdecl::ConfigField {
9250 key: None,
9251 type_: None,
9252 ..Default::default()
9253 }
9254 ]),
9255 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9256 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9257 ..Default::default()
9258 });
9259 decl
9260 },
9261 result = Err(ErrorList::new(vec![
9262 Error::missing_field(DeclType::ConfigField, "key"),
9263 Error::missing_field(DeclType::ConfigField, "value_type"),
9264 ])),
9265 },
9266
9267 test_validate_config_bool => {
9268 input = {
9269 let mut decl = new_component_decl();
9270 decl.config = Some(fdecl::ConfigSchema{
9271 fields: Some(vec![
9272 fdecl::ConfigField {
9273 key: Some("test".to_string()),
9274 type_: Some(fdecl::ConfigType {
9275 layout: fdecl::ConfigTypeLayout::Bool,
9276 parameters: Some(vec![]),
9277 constraints: vec![]
9278 }),
9279 ..Default::default()
9280 }
9281 ]),
9282 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9283 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9284 ..Default::default()
9285 });
9286 decl
9287 },
9288 result = Ok(()),
9289 },
9290
9291 test_validate_config_bool_extra_constraint => {
9292 input = {
9293 let mut decl = new_component_decl();
9294 decl.config = Some(fdecl::ConfigSchema{
9295 fields: Some(vec![
9296 fdecl::ConfigField {
9297 key: Some("test".to_string()),
9298 type_: Some(fdecl::ConfigType {
9299 layout: fdecl::ConfigTypeLayout::Bool,
9300 parameters: Some(vec![]),
9301 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9302 }),
9303 ..Default::default()
9304 }
9305 ]),
9306 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9307 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9308 ..Default::default()
9309 });
9310 decl
9311 },
9312 result = Err(ErrorList::new(vec![
9313 Error::extraneous_field(DeclType::ConfigType, "constraints")
9314 ])),
9315 },
9316
9317 test_validate_config_bool_missing_parameters => {
9318 input = {
9319 let mut decl = new_component_decl();
9320 decl.config = Some(fdecl::ConfigSchema{
9321 fields: Some(vec![
9322 fdecl::ConfigField {
9323 key: Some("test".to_string()),
9324 type_: Some(fdecl::ConfigType {
9325 layout: fdecl::ConfigTypeLayout::Bool,
9326 parameters: None,
9327 constraints: vec![]
9328 }),
9329 ..Default::default()
9330 }
9331 ]),
9332 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9333 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9334 ..Default::default()
9335 });
9336 decl
9337 },
9338 result = Err(ErrorList::new(vec![
9339 Error::missing_field(DeclType::ConfigType, "parameters")
9340 ])),
9341 },
9342
9343 test_validate_config_string => {
9344 input = {
9345 let mut decl = new_component_decl();
9346 decl.config = Some(fdecl::ConfigSchema{
9347 fields: Some(vec![
9348 fdecl::ConfigField {
9349 key: Some("test".to_string()),
9350 type_: Some(fdecl::ConfigType {
9351 layout: fdecl::ConfigTypeLayout::String,
9352 parameters: Some(vec![]),
9353 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9354 }),
9355 ..Default::default()
9356 }
9357 ]),
9358 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9359 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9360 ..Default::default()
9361 });
9362 decl
9363 },
9364 result = Ok(()),
9365 },
9366
9367 test_validate_config_string_missing_parameter => {
9368 input = {
9369 let mut decl = new_component_decl();
9370 decl.config = Some(fdecl::ConfigSchema{
9371 fields: Some(vec![
9372 fdecl::ConfigField {
9373 key: Some("test".to_string()),
9374 type_: Some(fdecl::ConfigType {
9375 layout: fdecl::ConfigTypeLayout::String,
9376 parameters: None,
9377 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9378 }),
9379 ..Default::default()
9380 }
9381 ]),
9382 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9383 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9384 ..Default::default()
9385 });
9386 decl
9387 },
9388 result = Err(ErrorList::new(vec![
9389 Error::missing_field(DeclType::ConfigType, "parameters")
9390 ])),
9391 },
9392
9393 test_validate_config_string_missing_constraint => {
9394 input = {
9395 let mut decl = new_component_decl();
9396 decl.config = Some(fdecl::ConfigSchema{
9397 fields: Some(vec![
9398 fdecl::ConfigField {
9399 key: Some("test".to_string()),
9400 type_: Some(fdecl::ConfigType {
9401 layout: fdecl::ConfigTypeLayout::String,
9402 parameters: Some(vec![]),
9403 constraints: vec![]
9404 }),
9405 ..Default::default()
9406 }
9407 ]),
9408 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9409 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9410 ..Default::default()
9411 });
9412 decl
9413 },
9414 result = Err(ErrorList::new(vec![
9415 Error::missing_field(DeclType::ConfigType, "constraints")
9416 ])),
9417 },
9418
9419 test_validate_config_string_extra_constraint => {
9420 input = {
9421 let mut decl = new_component_decl();
9422 decl.config = Some(fdecl::ConfigSchema{
9423 fields: Some(vec![
9424 fdecl::ConfigField {
9425 key: Some("test".to_string()),
9426 type_: Some(fdecl::ConfigType {
9427 layout: fdecl::ConfigTypeLayout::String,
9428 parameters: Some(vec![]),
9429 constraints: vec![fdecl::LayoutConstraint::MaxSize(10), fdecl::LayoutConstraint::MaxSize(10)]
9430 }),
9431 ..Default::default()
9432 }
9433 ]),
9434 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9435 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9436 ..Default::default()
9437 });
9438 decl
9439 },
9440 result = Err(ErrorList::new(vec![
9441 Error::extraneous_field(DeclType::ConfigType, "constraints")
9442 ])),
9443 },
9444
9445 test_validate_config_vector_bool => {
9446 input = {
9447 let mut decl = new_component_decl();
9448 decl.config = Some(fdecl::ConfigSchema{
9449 fields: Some(vec![
9450 fdecl::ConfigField {
9451 key: Some("test".to_string()),
9452 type_: Some(fdecl::ConfigType {
9453 layout: fdecl::ConfigTypeLayout::Vector,
9454 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9455 layout: fdecl::ConfigTypeLayout::Bool,
9456 parameters: Some(vec![]),
9457 constraints: vec![],
9458 })]),
9459 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9460 }),
9461 ..Default::default()
9462 }
9463 ]),
9464 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9465 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9466 ..Default::default()
9467 });
9468 decl
9469 },
9470 result = Ok(()),
9471 },
9472
9473 test_validate_config_vector_extra_parameter => {
9474 input = {
9475 let mut decl = new_component_decl();
9476 decl.config = Some(fdecl::ConfigSchema{
9477 fields: Some(vec![
9478 fdecl::ConfigField {
9479 key: Some("test".to_string()),
9480 type_: Some(fdecl::ConfigType {
9481 layout: fdecl::ConfigTypeLayout::Vector,
9482 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9483 layout: fdecl::ConfigTypeLayout::Bool,
9484 parameters: Some(vec![]),
9485 constraints: vec![],
9486 }), fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9487 layout: fdecl::ConfigTypeLayout::Uint8,
9488 parameters: Some(vec![]),
9489 constraints: vec![],
9490 })]),
9491 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9492 }),
9493 ..Default::default()
9494 }
9495 ]),
9496 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9497 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9498 ..Default::default()
9499 });
9500 decl
9501 },
9502 result = Err(ErrorList::new(vec![
9503 Error::extraneous_field(DeclType::ConfigType, "parameters")
9504 ])),
9505 },
9506
9507 test_validate_config_vector_missing_parameter => {
9508 input = {
9509 let mut decl = new_component_decl();
9510 decl.config = Some(fdecl::ConfigSchema{
9511 fields: Some(vec![
9512 fdecl::ConfigField {
9513 key: Some("test".to_string()),
9514 type_: Some(fdecl::ConfigType {
9515 layout: fdecl::ConfigTypeLayout::Vector,
9516 parameters: Some(vec![]),
9517 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9518 }),
9519 ..Default::default()
9520 }
9521 ]),
9522 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9523 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9524 ..Default::default()
9525 });
9526 decl
9527 },
9528 result = Err(ErrorList::new(vec![
9529 Error::missing_field(DeclType::ConfigType, "parameters")
9530 ])),
9531 },
9532
9533 test_validate_config_vector_string => {
9534 input = {
9535 let mut decl = new_component_decl();
9536 decl.config = Some(fdecl::ConfigSchema{
9537 fields: Some(vec![
9538 fdecl::ConfigField {
9539 key: Some("test".to_string()),
9540 type_: Some(fdecl::ConfigType {
9541 layout: fdecl::ConfigTypeLayout::Vector,
9542 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9543 layout: fdecl::ConfigTypeLayout::String,
9544 parameters: Some(vec![]),
9545 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9546 })]),
9547 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9548 }),
9549 ..Default::default()
9550 }
9551 ]),
9552 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9553 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9554 ..Default::default()
9555 });
9556 decl
9557 },
9558 result = Ok(()),
9559 },
9560
9561 test_validate_config_vector_vector => {
9562 input = {
9563 let mut decl = new_component_decl();
9564 decl.config = Some(fdecl::ConfigSchema{
9565 fields: Some(vec![
9566 fdecl::ConfigField {
9567 key: Some("test".to_string()),
9568 type_: Some(fdecl::ConfigType {
9569 layout: fdecl::ConfigTypeLayout::Vector,
9570 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9571 layout: fdecl::ConfigTypeLayout::Vector,
9572 parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9573 layout: fdecl::ConfigTypeLayout::String,
9574 parameters: Some(vec![]),
9575 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9576 })]),
9577 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9578 })]),
9579 constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9580 }),
9581 ..Default::default()
9582 }
9583 ]),
9584 checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9585 value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9586 ..Default::default()
9587 });
9588 decl
9589 },
9590 result = Err(ErrorList::new(vec![
9591 Error::nested_vector()
9592 ])),
9593 },
9594
9595 test_validate_exposes_invalid_aggregation_different_availability => {
9596 input = {
9597 let mut decl = new_component_decl();
9598 decl.exposes = Some(vec![
9599 fdecl::Expose::Service(fdecl::ExposeService {
9600 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9601 name: "coll_a".into()
9602 })),
9603 source_name: Some("fuchsia.logger.Log".to_string()),
9604 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
9605 target_name: Some("fuchsia.logger.Log".to_string()),
9606 availability: Some(fdecl::Availability::Required),
9607 ..Default::default()
9608 }),
9609 fdecl::Expose::Service(fdecl::ExposeService {
9610 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9611 name: "coll_b".into()
9612 })),
9613 source_name: Some("fuchsia.logger.Log".to_string()),
9614 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
9615 target_name: Some("fuchsia.logger.Log".to_string()),
9616 availability: Some(fdecl::Availability::Optional),
9617 ..Default::default()
9618 })
9619 ]);
9620 decl.collections = Some(vec![
9621 fdecl::Collection {
9622 name: Some("coll_a".to_string()),
9623 durability: Some(fdecl::Durability::Transient),
9624 ..Default::default()
9625 },
9626 fdecl::Collection {
9627 name: Some("coll_b".to_string()),
9628 durability: Some(fdecl::Durability::Transient),
9629 ..Default::default()
9630 },
9631 ]);
9632 decl
9633 },
9634 result = Err(ErrorList::new(vec![
9635 Error::DifferentAvailabilityInAggregation(AvailabilityList(vec![
9636 fdecl::Availability::Required,
9637 fdecl::Availability::Optional,
9638 ]))
9639 ])),
9640 },
9641
9642 test_validate_offers_invalid_aggregation_different_availability => {
9643 input = {
9644 let mut decl = new_component_decl();
9645 decl.offers = Some(vec![
9646 fdecl::Offer::Service(fdecl::OfferService {
9647 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9648 name: "coll_a".into()
9649 })),
9650 source_name: Some("fuchsia.logger.Log".to_string()),
9651 target: Some(fdecl::Ref::Child(
9652 fdecl::ChildRef {
9653 name: "child_c".to_string(),
9654 collection: None,
9655 }
9656 )),
9657 target_name: Some("fuchsia.logger.Log".to_string()),
9658 source_instance_filter: Some(vec!["default".to_string()]),
9659 availability: Some(fdecl::Availability::Required),
9660 ..Default::default()
9661 }),
9662 fdecl::Offer::Service(fdecl::OfferService {
9663 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9664 name: "coll_b".into()
9665 })),
9666 source_name: Some("fuchsia.logger.Log".to_string()),
9667 target: Some(fdecl::Ref::Child(
9668 fdecl::ChildRef {
9669 name: "child_c".to_string(),
9670 collection: None,
9671 }
9672 )),
9673 target_name: Some("fuchsia.logger.Log".to_string()),
9674 source_instance_filter: Some(vec!["a_different_default".to_string()]),
9675 availability: Some(fdecl::Availability::Optional),
9676 ..Default::default()
9677 })
9678 ]);
9679 decl.collections = Some(vec![
9680 fdecl::Collection {
9681 name: Some("coll_a".to_string()),
9682 durability: Some(fdecl::Durability::Transient),
9683 ..Default::default()
9684 },
9685 fdecl::Collection {
9686 name: Some("coll_b".to_string()),
9687 durability: Some(fdecl::Durability::Transient),
9688 ..Default::default()
9689 },
9690 ]);
9691 decl.children = Some(vec![
9692 fdecl::Child {
9693 name: Some("child_c".to_string()),
9694 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9695 startup: Some(fdecl::StartupMode::Lazy),
9696 on_terminate: None,
9697 environment: None,
9698 ..Default::default()
9699 },
9700 ]);
9701 decl
9702 },
9703 result = Err(ErrorList::new(vec![
9704 Error::DifferentAvailabilityInAggregation(AvailabilityList(vec![
9705 fdecl::Availability::Required,
9706 fdecl::Availability::Optional,
9707 ]))
9708 ])),
9709 },
9710 }
9711
9712 test_validate_capabilities! {
9713 test_validate_capabilities_individually_ok => {
9714 input = vec![
9715 fdecl::Capability::Protocol(fdecl::Protocol {
9716 name: Some("foo_svc".into()),
9717 source_path: Some("/svc/foo".into()),
9718 ..Default::default()
9719 }),
9720 fdecl::Capability::Directory(fdecl::Directory {
9721 name: Some("foo_dir".into()),
9722 source_path: Some("/foo".into()),
9723 rights: Some(fio::Operations::CONNECT),
9724 ..Default::default()
9725 }),
9726 ],
9727 as_builtin = false,
9728 result = Ok(()),
9729 },
9730 test_validate_capabilities_individually_err => {
9731 input = vec![
9732 fdecl::Capability::Protocol(fdecl::Protocol {
9733 name: None,
9734 source_path: None,
9735 ..Default::default()
9736 }),
9737 fdecl::Capability::Directory(fdecl::Directory {
9738 name: None,
9739 source_path: None,
9740 rights: None,
9741 ..Default::default()
9742 }),
9743 ],
9744 as_builtin = false,
9745 result = Err(ErrorList::new(vec![
9746 Error::missing_field(DeclType::Protocol, "name"),
9747 Error::missing_field(DeclType::Protocol, "source_path"),
9748 Error::missing_field(DeclType::Directory, "name"),
9749 Error::missing_field(DeclType::Directory, "source_path"),
9750 Error::missing_field(DeclType::Directory, "rights"),
9751 ])),
9752 },
9753 test_validate_builtin_capabilities_individually_ok => {
9754 input = vec![
9755 fdecl::Capability::Protocol(fdecl::Protocol {
9756 name: Some("foo_protocol".into()),
9757 source_path: None,
9758 ..Default::default()
9759 }),
9760 fdecl::Capability::Directory(fdecl::Directory {
9761 name: Some("foo_dir".into()),
9762 source_path: None,
9763 rights: Some(fio::Operations::CONNECT),
9764 ..Default::default()
9765 }),
9766 fdecl::Capability::Service(fdecl::Service {
9767 name: Some("foo_svc".into()),
9768 source_path: None,
9769 ..Default::default()
9770 }),
9771 fdecl::Capability::Runner(fdecl::Runner {
9772 name: Some("foo_runner".into()),
9773 source_path: None,
9774 ..Default::default()
9775 }),
9776 fdecl::Capability::Resolver(fdecl::Resolver {
9777 name: Some("foo_resolver".into()),
9778 source_path: None,
9779 ..Default::default()
9780 }),
9781 ],
9782 as_builtin = true,
9783 result = Ok(()),
9784 },
9785 test_validate_builtin_capabilities_individually_err => {
9786 input = vec![
9787 fdecl::Capability::Protocol(fdecl::Protocol {
9788 name: None,
9789 source_path: Some("/svc/foo".into()),
9790 ..Default::default()
9791 }),
9792 fdecl::Capability::Directory(fdecl::Directory {
9793 name: None,
9794 source_path: Some("/foo".into()),
9795 rights: None,
9796 ..Default::default()
9797 }),
9798 fdecl::Capability::Service(fdecl::Service {
9799 name: None,
9800 source_path: Some("/svc/foo".into()),
9801 ..Default::default()
9802 }),
9803 fdecl::Capability::Runner(fdecl::Runner {
9804 name: None,
9805 source_path: Some("/foo".into()),
9806 ..Default::default()
9807 }),
9808 fdecl::Capability::Resolver(fdecl::Resolver {
9809 name: None,
9810 source_path: Some("/foo".into()),
9811 ..Default::default()
9812 }),
9813 fdecl::Capability::Storage(fdecl::Storage {
9814 name: None,
9815 ..Default::default()
9816 }),
9817 ],
9818 as_builtin = true,
9819 result = Err(ErrorList::new(vec![
9820 Error::missing_field(DeclType::Protocol, "name"),
9821 Error::extraneous_source_path(DeclType::Protocol, "/svc/foo"),
9822 Error::missing_field(DeclType::Directory, "name"),
9823 Error::extraneous_source_path(DeclType::Directory, "/foo"),
9824 Error::missing_field(DeclType::Directory, "rights"),
9825 Error::missing_field(DeclType::Service, "name"),
9826 Error::extraneous_source_path(DeclType::Service, "/svc/foo"),
9827 Error::missing_field(DeclType::Runner, "name"),
9828 Error::extraneous_source_path(DeclType::Runner, "/foo"),
9829 Error::missing_field(DeclType::Resolver, "name"),
9830 Error::extraneous_source_path(DeclType::Resolver, "/foo"),
9831 Error::CapabilityCannotBeBuiltin(DeclType::Storage),
9832 ])),
9833 },
9834 test_validate_delivery_type_ok => {
9835 input = vec![
9836 fdecl::Capability::Protocol(fdecl::Protocol {
9837 name: Some("foo_svc1".into()),
9838 source_path: Some("/svc/foo1".into()),
9839 ..Default::default()
9840 }),
9841 fdecl::Capability::Protocol(fdecl::Protocol {
9842 name: Some("foo_svc2".into()),
9843 source_path: Some("/svc/foo2".into()),
9844 delivery: Some(fdecl::DeliveryType::Immediate),
9845 ..Default::default()
9846 }),
9847 fdecl::Capability::Protocol(fdecl::Protocol {
9848 name: Some("foo_svc3".into()),
9849 source_path: Some("/svc/foo3".into()),
9850 delivery: Some(fdecl::DeliveryType::OnReadable),
9851 ..Default::default()
9852 }),
9853 ],
9854 as_builtin = false,
9855 result = Ok(()),
9856 },
9857 test_validate_delivery_type_err => {
9858 input = vec![
9859 fdecl::Capability::Protocol(fdecl::Protocol {
9860 name: Some("foo_svc".into()),
9861 source_path: Some("/svc/foo".into()),
9862 delivery: Some(fdecl::DeliveryType::unknown()),
9863 ..Default::default()
9864 }),
9865 ],
9866 as_builtin = false,
9867 result = Err(ErrorList::new(vec![
9868 Error::invalid_field(DeclType::Protocol, "delivery"),
9869 ])),
9870 },
9871 }
9872
9873 fn generate_expose_different_source_and_availability_decl(
9876 new_expose: impl Fn(fdecl::Ref, fdecl::Availability, &str) -> fdecl::Expose,
9877 ) -> fdecl::Component {
9878 let mut decl = new_component_decl();
9879 let child = "child";
9880 decl.children = Some(vec![fdecl::Child {
9881 name: Some(child.to_string()),
9882 url: Some("fuchsia-pkg://fuchsia.com/source#meta/source.cm".to_string()),
9883 startup: Some(fdecl::StartupMode::Lazy),
9884 ..Default::default()
9885 }]);
9886 decl.exposes = Some(vec![
9887 new_expose(
9889 fdecl::Ref::Self_(fdecl::SelfRef {}),
9890 fdecl::Availability::Optional,
9891 "fuchsia.examples.Echo1",
9892 ),
9893 new_expose(
9895 fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
9896 fdecl::Availability::Optional,
9897 "fuchsia.examples.Echo2",
9898 ),
9899 new_expose(
9901 fdecl::Ref::VoidType(fdecl::VoidRef {}),
9902 fdecl::Availability::Optional,
9903 "fuchsia.examples.Echo3",
9904 ),
9905 new_expose(
9907 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
9908 fdecl::Availability::Optional,
9909 "fuchsia.examples.Echo4",
9910 ),
9911 new_expose(
9913 fdecl::Ref::Self_(fdecl::SelfRef {}),
9914 fdecl::Availability::Transitional,
9915 "fuchsia.examples.Echo5",
9916 ),
9917 new_expose(
9919 fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
9920 fdecl::Availability::Transitional,
9921 "fuchsia.examples.Echo6",
9922 ),
9923 new_expose(
9925 fdecl::Ref::VoidType(fdecl::VoidRef {}),
9926 fdecl::Availability::Transitional,
9927 "fuchsia.examples.Echo7",
9928 ),
9929 new_expose(
9931 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
9932 fdecl::Availability::Transitional,
9933 "fuchsia.examples.Echo8",
9934 ),
9935 new_expose(
9937 fdecl::Ref::Self_(fdecl::SelfRef {}),
9938 fdecl::Availability::SameAsTarget,
9939 "fuchsia.examples.Echo9",
9940 ),
9941 new_expose(
9943 fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
9944 fdecl::Availability::SameAsTarget,
9945 "fuchsia.examples.Echo10",
9946 ),
9947 new_expose(
9949 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
9950 fdecl::Availability::SameAsTarget,
9951 "fuchsia.examples.Echo11",
9952 ),
9953 new_expose(
9955 fdecl::Ref::VoidType(fdecl::VoidRef {}),
9956 fdecl::Availability::Required,
9957 "fuchsia.examples.Echo12",
9958 ),
9959 new_expose(
9961 fdecl::Ref::VoidType(fdecl::VoidRef {}),
9962 fdecl::Availability::SameAsTarget,
9963 "fuchsia.examples.Echo13",
9964 ),
9965 ]);
9966 decl
9967 }
9968
9969 fn generate_offer_different_source_and_availability_decl(
9972 new_offer: impl Fn(fdecl::Ref, fdecl::Availability, &str) -> fdecl::Offer,
9973 ) -> fdecl::Component {
9974 let mut decl = new_component_decl();
9975 decl.children = Some(vec![
9976 fdecl::Child {
9977 name: Some("source".to_string()),
9978 url: Some("fuchsia-pkg://fuchsia.com/source#meta/source.cm".to_string()),
9979 startup: Some(fdecl::StartupMode::Lazy),
9980 on_terminate: None,
9981 environment: None,
9982 ..Default::default()
9983 },
9984 fdecl::Child {
9985 name: Some("sink".to_string()),
9986 url: Some("fuchsia-pkg://fuchsia.com/sink#meta/sink.cm".to_string()),
9987 startup: Some(fdecl::StartupMode::Lazy),
9988 on_terminate: None,
9989 environment: None,
9990 ..Default::default()
9991 },
9992 ]);
9993 decl.offers = Some(vec![
9994 new_offer(
9997 fdecl::Ref::Parent(fdecl::ParentRef {}),
9998 fdecl::Availability::Required,
9999 "fuchsia.examples.Echo0",
10000 ),
10001 new_offer(
10002 fdecl::Ref::Parent(fdecl::ParentRef {}),
10003 fdecl::Availability::Optional,
10004 "fuchsia.examples.Echo1",
10005 ),
10006 new_offer(
10007 fdecl::Ref::Parent(fdecl::ParentRef {}),
10008 fdecl::Availability::SameAsTarget,
10009 "fuchsia.examples.Echo2",
10010 ),
10011 new_offer(
10012 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10013 fdecl::Availability::Optional,
10014 "fuchsia.examples.Echo3",
10015 ),
10016 new_offer(
10019 fdecl::Ref::Self_(fdecl::SelfRef {}),
10020 fdecl::Availability::Optional,
10021 "fuchsia.examples.Echo4",
10022 ),
10023 new_offer(
10024 fdecl::Ref::Self_(fdecl::SelfRef {}),
10025 fdecl::Availability::SameAsTarget,
10026 "fuchsia.examples.Echo5",
10027 ),
10028 new_offer(
10029 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10030 fdecl::Availability::Optional,
10031 "fuchsia.examples.Echo6",
10032 ),
10033 new_offer(
10034 fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10035 fdecl::Availability::SameAsTarget,
10036 "fuchsia.examples.Echo7",
10037 ),
10038 new_offer(
10039 fdecl::Ref::Child(fdecl::ChildRef { name: "source".to_string(), collection: None }),
10040 fdecl::Availability::Optional,
10041 "fuchsia.examples.Echo8",
10042 ),
10043 new_offer(
10044 fdecl::Ref::Child(fdecl::ChildRef { name: "source".to_string(), collection: None }),
10045 fdecl::Availability::SameAsTarget,
10046 "fuchsia.examples.Echo9",
10047 ),
10048 new_offer(
10050 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10051 fdecl::Availability::Required,
10052 "fuchsia.examples.Echo10",
10053 ),
10054 new_offer(
10055 fdecl::Ref::VoidType(fdecl::VoidRef {}),
10056 fdecl::Availability::SameAsTarget,
10057 "fuchsia.examples.Echo11",
10058 ),
10059 ]);
10060 decl
10061 }
10062
10063 #[test]
10064 fn test_validate_dynamic_offers_empty() {
10065 assert_eq!(
10066 validate_dynamic_offers(vec![], &HashSet::new(), &vec![], &fdecl::Component::default()),
10067 Ok(())
10068 );
10069 }
10070
10071 #[test]
10072 fn test_validate_dynamic_offers_okay() {
10073 assert_eq!(
10074 validate_dynamic_offers(
10075 vec![],
10076 &HashSet::new(),
10077 &vec![
10078 fdecl::Offer::Protocol(fdecl::OfferProtocol {
10079 dependency_type: Some(fdecl::DependencyType::Strong),
10080 source: Some(fdecl::Ref::Self_(fdecl::SelfRef)),
10081 source_name: Some("thing".to_string()),
10082 target_name: Some("thing".repeat(26)),
10083 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10084 name: "foo".to_string(),
10085 collection: Some("foo".to_string()),
10086 })),
10087 ..Default::default()
10088 }),
10089 fdecl::Offer::Service(fdecl::OfferService {
10090 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10091 source_name: Some("thang".repeat(26)),
10092 target_name: Some("thang".repeat(26)),
10093 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10094 name: "foo".to_string(),
10095 collection: Some("foo".to_string()),
10096 })),
10097 ..Default::default()
10098 }),
10099 fdecl::Offer::Directory(fdecl::OfferDirectory {
10100 dependency_type: Some(fdecl::DependencyType::Strong),
10101 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10102 source_name: Some("thung1".repeat(26)),
10103 target_name: Some("thung1".repeat(26)),
10104 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10105 name: "foo".to_string(),
10106 collection: Some("foo".to_string()),
10107 })),
10108 ..Default::default()
10109 }),
10110 fdecl::Offer::Storage(fdecl::OfferStorage {
10111 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10112 source_name: Some("thung2".repeat(26)),
10113 target_name: Some("thung2".repeat(26)),
10114 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10115 name: "foo".to_string(),
10116 collection: Some("foo".to_string()),
10117 })),
10118 ..Default::default()
10119 }),
10120 fdecl::Offer::Runner(fdecl::OfferRunner {
10121 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10122 source_name: Some("thung3".repeat(26)),
10123 target_name: Some("thung3".repeat(26)),
10124 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10125 name: "foo".to_string(),
10126 collection: Some("foo".to_string()),
10127 })),
10128 ..Default::default()
10129 }),
10130 fdecl::Offer::Resolver(fdecl::OfferResolver {
10131 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10132 source_name: Some("thung4".repeat(26)),
10133 target_name: Some("thung4".repeat(26)),
10134 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10135 name: "foo".to_string(),
10136 collection: Some("foo".to_string()),
10137 })),
10138 ..Default::default()
10139 }),
10140 ],
10141 &fdecl::Component {
10142 capabilities: Some(vec![fdecl::Capability::Protocol(fdecl::Protocol {
10143 name: Some("thing".to_string()),
10144 source_path: Some("/svc/foo".into()),
10145 ..Default::default()
10146 }),]),
10147 ..Default::default()
10148 }
10149 ),
10150 Ok(())
10151 );
10152 }
10153
10154 #[test]
10155 fn test_validate_dynamic_offers_valid_service_aggregation() {
10156 assert_eq!(
10157 validate_dynamic_offers(
10158 vec![],
10159 &HashSet::new(),
10160 &vec![
10161 fdecl::Offer::Service(fdecl::OfferService {
10162 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10163 name: "child_a".to_string(),
10164 collection: None
10165 })),
10166 source_name: Some("fuchsia.logger.Log".to_string()),
10167 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10168 name: "child_c".to_string(),
10169 collection: None,
10170 })),
10171 target_name: Some("fuchsia.logger.Log".to_string()),
10172 source_instance_filter: Some(vec!["default".to_string()]),
10173 ..Default::default()
10174 }),
10175 fdecl::Offer::Service(fdecl::OfferService {
10176 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10177 name: "child_b".to_string(),
10178 collection: None
10179 })),
10180 source_name: Some("fuchsia.logger.Log".to_string()),
10181 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10182 name: "child_c".to_string(),
10183 collection: None,
10184 })),
10185 target_name: Some("fuchsia.logger.Log".to_string()),
10186 source_instance_filter: Some(vec!["a_different_default".to_string()]),
10187 ..Default::default()
10188 })
10189 ],
10190 &fdecl::Component {
10191 children: Some(vec![
10192 fdecl::Child {
10193 name: Some("child_a".to_string()),
10194 url: Some(
10195 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10196 .to_string()
10197 ),
10198 startup: Some(fdecl::StartupMode::Lazy),
10199 on_terminate: None,
10200 environment: None,
10201 ..Default::default()
10202 },
10203 fdecl::Child {
10204 name: Some("child_b".to_string()),
10205 url: Some(
10206 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10207 .to_string()
10208 ),
10209 startup: Some(fdecl::StartupMode::Lazy),
10210 on_terminate: None,
10211 environment: None,
10212 ..Default::default()
10213 },
10214 fdecl::Child {
10215 name: Some("child_c".to_string()),
10216 url: Some(
10217 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10218 .to_string()
10219 ),
10220 startup: Some(fdecl::StartupMode::Lazy),
10221 on_terminate: None,
10222 environment: None,
10223 ..Default::default()
10224 },
10225 ]),
10226 ..Default::default()
10227 }
10228 ),
10229 Ok(())
10230 );
10231 }
10232
10233 #[test]
10234 fn test_validate_dynamic_service_aggregation_missing_filter() {
10235 assert_eq!(
10236 validate_dynamic_offers(
10237 vec![],
10238 &HashSet::new(),
10239 &vec![
10240 fdecl::Offer::Service(fdecl::OfferService {
10241 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10242 name: "child_a".to_string(),
10243 collection: None
10244 })),
10245 source_name: Some("fuchsia.logger.Log".to_string()),
10246 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10247 name: "child_c".to_string(),
10248 collection: None,
10249 })),
10250 target_name: Some("fuchsia.logger.Log".to_string()),
10251 source_instance_filter: Some(vec!["default".to_string()]),
10252 ..Default::default()
10253 }),
10254 fdecl::Offer::Service(fdecl::OfferService {
10255 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10256 name: "child_b".to_string(),
10257 collection: None
10258 })),
10259 source_name: Some("fuchsia.logger.Log".to_string()),
10260 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10261 name: "child_c".to_string(),
10262 collection: None,
10263 })),
10264 target_name: Some("fuchsia.logger.Log".to_string()),
10265 source_instance_filter: None,
10266 ..Default::default()
10267 }),
10268 ],
10269 &fdecl::Component {
10270 children: Some(vec![
10271 fdecl::Child {
10272 name: Some("child_a".to_string()),
10273 url: Some(
10274 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10275 .to_string()
10276 ),
10277 startup: Some(fdecl::StartupMode::Lazy),
10278 ..Default::default()
10279 },
10280 fdecl::Child {
10281 name: Some("child_b".to_string()),
10282 url: Some(
10283 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10284 .to_string()
10285 ),
10286 startup: Some(fdecl::StartupMode::Lazy),
10287 ..Default::default()
10288 },
10289 fdecl::Child {
10290 name: Some("child_c".to_string()),
10291 url: Some(
10292 "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10293 .to_string()
10294 ),
10295 startup: Some(fdecl::StartupMode::Lazy),
10296 ..Default::default()
10297 },
10298 ]),
10299 ..Default::default()
10300 },
10301 ),
10302 Err(ErrorList::new(vec![Error::invalid_aggregate_offer(
10303 "source_instance_filter must be set for dynamic aggregate service offers"
10304 ),]))
10305 );
10306 }
10307
10308 #[test]
10309 fn test_validate_dynamic_offers_omit_target() {
10310 assert_eq!(
10311 validate_dynamic_offers(
10312 vec![],
10313 &HashSet::new(),
10314 &vec![
10315 fdecl::Offer::Protocol(fdecl::OfferProtocol {
10316 dependency_type: Some(fdecl::DependencyType::Strong),
10317 source: Some(fdecl::Ref::Self_(fdecl::SelfRef)),
10318 source_name: Some("thing".to_string()),
10319 target_name: Some("thing".to_string()),
10320 ..Default::default()
10321 }),
10322 fdecl::Offer::Service(fdecl::OfferService {
10323 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10324 source_name: Some("thang".to_string()),
10325 target_name: Some("thang".to_string()),
10326 ..Default::default()
10327 }),
10328 fdecl::Offer::Directory(fdecl::OfferDirectory {
10329 dependency_type: Some(fdecl::DependencyType::Strong),
10330 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10331 source_name: Some("thung1".to_string()),
10332 target_name: Some("thung1".to_string()),
10333 ..Default::default()
10334 }),
10335 fdecl::Offer::Storage(fdecl::OfferStorage {
10336 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10337 source_name: Some("thung2".to_string()),
10338 target_name: Some("thung2".to_string()),
10339 ..Default::default()
10340 }),
10341 fdecl::Offer::Runner(fdecl::OfferRunner {
10342 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10343 source_name: Some("thung3".to_string()),
10344 target_name: Some("thung3".to_string()),
10345 ..Default::default()
10346 }),
10347 fdecl::Offer::Resolver(fdecl::OfferResolver {
10348 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10349 source_name: Some("thung4".to_string()),
10350 target_name: Some("thung4".to_string()),
10351 ..Default::default()
10352 }),
10353 ],
10354 &fdecl::Component {
10355 capabilities: Some(vec![fdecl::Capability::Protocol(fdecl::Protocol {
10356 name: Some("thing".to_string()),
10357 source_path: Some("/svc/foo".into()),
10358 ..Default::default()
10359 }),]),
10360 ..Default::default()
10361 }
10362 ),
10363 Err(ErrorList::new(vec![
10364 Error::missing_field(DeclType::OfferProtocol, "target"),
10365 Error::missing_field(DeclType::OfferService, "target"),
10366 Error::missing_field(DeclType::OfferDirectory, "target"),
10367 Error::missing_field(DeclType::OfferStorage, "target"),
10368 Error::missing_field(DeclType::OfferRunner, "target"),
10369 Error::missing_field(DeclType::OfferResolver, "target"),
10370 ]))
10371 );
10372 }
10373
10374 #[test]
10375 fn test_validate_dynamic_offers_collection_collision() {
10376 assert_eq!(
10377 validate_dynamic_offers(
10378 vec![],
10379 &HashSet::new(),
10380 &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10381 dependency_type: Some(fdecl::DependencyType::Strong),
10382 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10383 source_name: Some("thing".to_string()),
10384 target_name: Some("thing".to_string()),
10385 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10386 name: "child".to_string(),
10387 collection: Some("coll".to_string()),
10388 })),
10389 ..Default::default()
10390 }),],
10391 &fdecl::Component {
10392 offers: Some(vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10393 dependency_type: Some(fdecl::DependencyType::Strong),
10394 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10395 source_name: Some("thing".to_string()),
10396 target_name: Some("thing".to_string()),
10397 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10398 name: "coll".into()
10399 })),
10400 ..Default::default()
10401 }),]),
10402 collections: Some(vec![fdecl::Collection {
10403 name: Some("coll".to_string()),
10404 durability: Some(fdecl::Durability::Transient),
10405 ..Default::default()
10406 },]),
10407 ..Default::default()
10408 }
10409 ),
10410 Err(ErrorList::new(vec![Error::duplicate_field(
10411 DeclType::OfferProtocol,
10412 "target_name",
10413 "thing"
10414 ),]))
10415 );
10416 }
10417
10418 #[test]
10419 fn test_validate_dynamic_offers_cycle_collection_to_static_child() {
10420 assert_eq!(
10421 validate_dynamic_offers(
10422 vec![("dyn", "coll")],
10423 &HashSet::new(),
10424 &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10425 source_name: Some("bar".to_string()),
10426 target_name: Some("bar".to_string()),
10427 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10428 name: "static_child".into(),
10429 collection: None,
10430 })),
10431 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10432 name: "dyn".to_string(),
10433 collection: Some("coll".to_string()),
10434 })),
10435 dependency_type: Some(fdecl::DependencyType::Strong),
10436 ..Default::default()
10437 }),],
10438 &fdecl::Component {
10439 offers: Some(vec![fdecl::Offer::Service(fdecl::OfferService {
10440 source_name: Some("foo".to_string()),
10441 target_name: Some("foo".to_string()),
10442 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10443 name: "coll".into(),
10444 })),
10445 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10446 name: "static_child".into(),
10447 collection: None,
10448 })),
10449 ..Default::default()
10450 })]),
10451 children: Some(vec![fdecl::Child {
10452 name: Some("static_child".into()),
10453 url: Some("url#child.cm".into()),
10454 startup: Some(fdecl::StartupMode::Lazy),
10455 ..Default::default()
10456 }]),
10457 collections: Some(vec![fdecl::Collection {
10458 name: Some("coll".into()),
10459 durability: Some(fdecl::Durability::Transient),
10460 ..Default::default()
10461 }]),
10462 ..Default::default()
10463 }
10464 ),
10465 Err(ErrorList::new(vec![Error::dependency_cycle(
10466 "{{child coll:dyn -> collection coll -> child static_child -> child coll:dyn}}"
10467 )]))
10468 );
10469 }
10470
10471 #[test]
10472 fn test_validate_dynamic_offers_cycle_collection_to_dynamic_child() {
10473 assert_eq!(
10474 validate_dynamic_offers(
10475 vec![("dyn", "coll1"), ("dyn", "coll2")],
10476 &HashSet::new(),
10477 &vec![
10478 fdecl::Offer::Service(fdecl::OfferService {
10479 source_name: Some("foo".to_string()),
10480 target_name: Some("foo".to_string()),
10481 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10482 name: "coll2".into(),
10483 })),
10484 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10485 name: "dyn".into(),
10486 collection: Some("coll1".into()),
10487 })),
10488 ..Default::default()
10489 }),
10490 fdecl::Offer::Protocol(fdecl::OfferProtocol {
10491 source_name: Some("bar".to_string()),
10492 target_name: Some("bar".to_string()),
10493 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10494 name: "dyn".into(),
10495 collection: Some("coll1".into()),
10496 })),
10497 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10498 name: "dyn".to_string(),
10499 collection: Some("coll2".to_string()),
10500 })),
10501 dependency_type: Some(fdecl::DependencyType::Strong),
10502 ..Default::default()
10503 }),
10504 ],
10505 &fdecl::Component {
10506 collections: Some(vec![
10507 fdecl::Collection {
10508 name: Some("coll1".into()),
10509 durability: Some(fdecl::Durability::Transient),
10510 ..Default::default()
10511 },
10512 fdecl::Collection {
10513 name: Some("coll2".into()),
10514 durability: Some(fdecl::Durability::Transient),
10515 ..Default::default()
10516 },
10517 ]),
10518 ..Default::default()
10519 }
10520 ),
10521 Err(ErrorList::new(vec![Error::dependency_cycle(
10522 "{{child coll1:dyn -> child coll2:dyn -> collection coll2 -> child coll1:dyn}}"
10523 )]))
10524 );
10525 }
10526
10527 #[test]
10528 fn test_validate_dynamic_offers_cycle_collection_to_dynamic_child_between_new_and_existing() {
10529 assert_eq!(
10530 validate_dynamic_offers(
10531 vec![("dyn", "coll1"), ("dyn", "coll2")],
10532 &HashSet::from([(
10533 DependencyNode::Child("dyn".into(), Some("coll1".into())),
10534 DependencyNode::Child("dyn".into(), Some("coll2".into())),
10535 )]),
10536 &vec![fdecl::Offer::Service(fdecl::OfferService {
10537 source_name: Some("foo".to_string()),
10538 target_name: Some("foo".to_string()),
10539 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10540 name: "coll2".into(),
10541 })),
10542 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10543 name: "dyn".into(),
10544 collection: Some("coll1".into()),
10545 })),
10546 ..Default::default()
10547 }),],
10548 &fdecl::Component {
10549 collections: Some(vec![
10550 fdecl::Collection {
10551 name: Some("coll1".into()),
10552 durability: Some(fdecl::Durability::Transient),
10553 ..Default::default()
10554 },
10555 fdecl::Collection {
10556 name: Some("coll2".into()),
10557 durability: Some(fdecl::Durability::Transient),
10558 ..Default::default()
10559 },
10560 ]),
10561 ..Default::default()
10562 }
10563 ),
10564 Err(ErrorList::new(vec![Error::dependency_cycle(
10565 "{{child coll1:dyn -> child coll2:dyn -> collection coll2 -> child coll1:dyn}}"
10566 )]))
10567 );
10568 }
10569
10570 #[test]
10571 fn test_validate_dynamic_offers_cycle_collection_to_collection() {
10572 assert_eq!(
10573 validate_dynamic_offers(
10574 vec![("dyn", "coll1"), ("dyn", "coll2")],
10575 &HashSet::new(),
10576 &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10577 source_name: Some("bar".to_string()),
10578 target_name: Some("bar".to_string()),
10579 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10580 name: "dyn".into(),
10581 collection: Some("coll2".parse().unwrap()),
10582 })),
10583 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10584 name: "dyn".into(),
10585 collection: Some("coll1".parse().unwrap()),
10586 })),
10587 dependency_type: Some(fdecl::DependencyType::Strong),
10588 ..Default::default()
10589 }),],
10590 &fdecl::Component {
10591 offers: Some(vec![fdecl::Offer::Service(fdecl::OfferService {
10592 source_name: Some("foo".to_string()),
10593 target_name: Some("foo".to_string()),
10594 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10595 name: "coll1".into(),
10596 })),
10597 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10598 name: "coll2".into(),
10599 })),
10600 ..Default::default()
10601 })]),
10602 collections: Some(vec![
10603 fdecl::Collection {
10604 name: Some("coll1".into()),
10605 durability: Some(fdecl::Durability::Transient),
10606 ..Default::default()
10607 },
10608 fdecl::Collection {
10609 name: Some("coll2".into()),
10610 durability: Some(fdecl::Durability::Transient),
10611 ..Default::default()
10612 },
10613 ]),
10614 ..Default::default()
10615 }
10616 ),
10617 Err(ErrorList::new(vec![Error::dependency_cycle(
10618 "{{child coll1:dyn -> collection coll1 -> child coll2:dyn -> child coll1:dyn}}",
10619 )]))
10620 );
10621 }
10622
10623 #[test]
10624 fn test_validate_dynamic_child() {
10625 assert_eq!(
10626 Ok(()),
10627 validate_dynamic_child(&fdecl::Child {
10628 name: Some("a".repeat(MAX_LONG_NAME_LENGTH).to_string()),
10629 url: Some("test:///child".to_string()),
10630 startup: Some(fdecl::StartupMode::Lazy),
10631 on_terminate: None,
10632 environment: None,
10633 ..Default::default()
10634 })
10635 );
10636 }
10637
10638 #[test]
10639 fn test_validate_dynamic_child_environment_is_invalid() {
10640 assert_eq!(
10641 validate_dynamic_child(&fdecl::Child {
10642 name: Some("a".repeat(MAX_LONG_NAME_LENGTH).to_string()),
10643 url: Some("test:///child".to_string()),
10644 startup: Some(fdecl::StartupMode::Lazy),
10645 on_terminate: None,
10646 environment: Some("env".to_string()),
10647 ..Default::default()
10648 }),
10649 Err(ErrorList::new(vec![Error::DynamicChildWithEnvironment]))
10650 );
10651 }
10652
10653 #[test]
10654 fn test_validate_dynamic_offers_missing_stuff() {
10655 assert_eq!(
10656 validate_dynamic_offers(
10657 vec![],
10658 &HashSet::new(),
10659 &vec![
10660 fdecl::Offer::Protocol(fdecl::OfferProtocol::default()),
10661 fdecl::Offer::Service(fdecl::OfferService::default()),
10662 fdecl::Offer::Directory(fdecl::OfferDirectory::default()),
10663 fdecl::Offer::Storage(fdecl::OfferStorage::default()),
10664 fdecl::Offer::Runner(fdecl::OfferRunner::default()),
10665 fdecl::Offer::Resolver(fdecl::OfferResolver::default()),
10666 ],
10667 &fdecl::Component::default()
10668 ),
10669 Err(ErrorList::new(vec![
10670 Error::missing_field(DeclType::OfferProtocol, "source"),
10671 Error::missing_field(DeclType::OfferProtocol, "source_name"),
10672 Error::missing_field(DeclType::OfferProtocol, "target"),
10673 Error::missing_field(DeclType::OfferProtocol, "target_name"),
10674 Error::missing_field(DeclType::OfferProtocol, "dependency_type"),
10675 Error::missing_field(DeclType::OfferService, "source"),
10676 Error::missing_field(DeclType::OfferService, "source_name"),
10677 Error::missing_field(DeclType::OfferService, "target"),
10678 Error::missing_field(DeclType::OfferService, "target_name"),
10679 Error::missing_field(DeclType::OfferDirectory, "source"),
10680 Error::missing_field(DeclType::OfferDirectory, "source_name"),
10681 Error::missing_field(DeclType::OfferDirectory, "target"),
10682 Error::missing_field(DeclType::OfferDirectory, "target_name"),
10683 Error::missing_field(DeclType::OfferDirectory, "dependency_type"),
10684 Error::missing_field(DeclType::OfferStorage, "source"),
10685 Error::missing_field(DeclType::OfferStorage, "source_name"),
10686 Error::missing_field(DeclType::OfferStorage, "target"),
10687 Error::missing_field(DeclType::OfferStorage, "target_name"),
10688 Error::missing_field(DeclType::OfferRunner, "source"),
10689 Error::missing_field(DeclType::OfferRunner, "source_name"),
10690 Error::missing_field(DeclType::OfferRunner, "target"),
10691 Error::missing_field(DeclType::OfferRunner, "target_name"),
10692 Error::missing_field(DeclType::OfferResolver, "source"),
10693 Error::missing_field(DeclType::OfferResolver, "source_name"),
10694 Error::missing_field(DeclType::OfferResolver, "target"),
10695 Error::missing_field(DeclType::OfferResolver, "target_name"),
10696 ]))
10697 );
10698 }
10699
10700 test_dependency! {
10701 (test_validate_offers_protocol_dependency_cycle) => {
10702 ty = fdecl::Offer::Protocol,
10703 offer_decl = fdecl::OfferProtocol {
10704 source: None, target: None, source_name: Some(format!("thing")),
10707 target_name: Some(format!("thing")),
10708 dependency_type: Some(fdecl::DependencyType::Strong),
10709 ..Default::default()
10710 },
10711 },
10712 (test_validate_offers_directory_dependency_cycle) => {
10713 ty = fdecl::Offer::Directory,
10714 offer_decl = fdecl::OfferDirectory {
10715 source: None, target: None, source_name: Some(format!("thing")),
10718 target_name: Some(format!("thing")),
10719 rights: Some(fio::Operations::CONNECT),
10720 subdir: None,
10721 dependency_type: Some(fdecl::DependencyType::Strong),
10722 ..Default::default()
10723 },
10724 },
10725 (test_validate_offers_service_dependency_cycle) => {
10726 ty = fdecl::Offer::Service,
10727 offer_decl = fdecl::OfferService {
10728 source: None, target: None, source_name: Some(format!("thing")),
10731 target_name: Some(format!("thing")),
10732 ..Default::default()
10733 },
10734 },
10735 (test_validate_offers_runner_dependency_cycle) => {
10736 ty = fdecl::Offer::Runner,
10737 offer_decl = fdecl::OfferRunner {
10738 source: None, target: None, source_name: Some(format!("thing")),
10741 target_name: Some(format!("thing")),
10742 ..Default::default()
10743 },
10744 },
10745 (test_validate_offers_resolver_dependency_cycle) => {
10746 ty = fdecl::Offer::Resolver,
10747 offer_decl = fdecl::OfferResolver {
10748 source: None, target: None, source_name: Some(format!("thing")),
10751 target_name: Some(format!("thing")),
10752 ..Default::default()
10753 },
10754 },
10755 }
10756 test_weak_dependency! {
10757 (test_validate_offers_protocol_weak_dependency_cycle) => {
10758 ty = fdecl::Offer::Protocol,
10759 offer_decl = fdecl::OfferProtocol {
10760 source: None, target: None, source_name: Some(format!("thing")),
10763 target_name: Some(format!("thing")),
10764 dependency_type: None, ..Default::default()
10766 },
10767 },
10768 (test_validate_offers_directory_weak_dependency_cycle) => {
10769 ty = fdecl::Offer::Directory,
10770 offer_decl = fdecl::OfferDirectory {
10771 source: None, target: None, source_name: Some(format!("thing")),
10774 target_name: Some(format!("thing")),
10775 rights: Some(fio::Operations::CONNECT),
10776 subdir: None,
10777 dependency_type: None, ..Default::default()
10779 },
10780 },
10781 (test_validate_offers_service_weak_dependency_cycle) => {
10782 ty = fdecl::Offer::Service,
10783 offer_decl = fdecl::OfferService {
10784 source: None, target: None, source_name: Some(format!("thing")),
10787 target_name: Some(format!("thing")),
10788 dependency_type: None, ..Default::default()
10790 },
10791 },
10792 }
10793}