cm_fidl_validator/
lib.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5pub(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
37/// Validates Configuration Value Spec.
38///
39/// For now, this simply verifies that all semantically required fields are present.
40pub 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
85/// Validates Configuration Values Data.
86///
87/// The Value Data may ultimately originate from a CVF file, or be directly constructed by the
88/// caller. Either way, Value Data should always be validated before it's used. For now, this
89/// simply verifies that all semantically required fields are present.
90///
91/// This method does not validate value data against a configuration schema.
92pub 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// `fdecl::Ref` is not hashable, so define this equivalent type for use in maps
119#[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
130/// Validates a Component.
131///
132/// The Component may ultimately originate from a CM file, or be directly constructed by the
133/// caller. Either way, a Component should always be validated before it's used. Examples
134/// of what is validated (which may evolve in the future):
135///
136/// - That all semantically required fields are present
137/// - That a child_name referenced in a source actually exists in the list of children
138/// - That there are no duplicate target paths.
139/// - That only weak-dependency capabilities may be offered back to the
140///   component that exposed them.
141///
142/// `dependencies` is an output parameter that captures the capability dependency graph derived
143/// from `decl`. Callers may use this to traverse the capability graph in topological order.
144///
145/// All checks are local to this Component.
146pub fn validate<'a>(
147    decl: &'a fdecl::Component,
148    dependencies: &'a mut DirectedGraph<DependencyNode>,
149) -> Result<(), ErrorList> {
150    let ctx = ValidationContext::new(dependencies);
151    ctx.validate(decl, &[]).map_err(|errs| ErrorList::new(errs))
152}
153
154/// Validates a list of namespace or builtin Capabilities.
155fn validate_capabilities(
156    capabilities: &[fdecl::Capability],
157    as_builtin: bool,
158) -> Result<(), ErrorList> {
159    let mut deps = DirectedGraph::new();
160    let mut ctx = ValidationContext::new(&mut deps);
161
162    ctx.load_dictionary_names(capabilities.iter().filter_map(|capability| match capability {
163        fdecl::Capability::Dictionary(dictionary_decl) => Some(dictionary_decl),
164        _ => None,
165    }));
166
167    ctx.validate_capability_decls(capabilities, as_builtin);
168    if ctx.errors.is_empty() { Ok(()) } else { Err(ErrorList::new(ctx.errors)) }
169}
170
171// Validate builtin capabilities.
172pub fn validate_builtin_capabilities(
173    capabilities: &Vec<fdecl::Capability>,
174) -> Result<(), ErrorList> {
175    validate_capabilities(capabilities, true)
176}
177
178// Validate namespace capabilities.
179pub fn validate_namespace_capabilities(
180    capabilities: &Vec<fdecl::Capability>,
181) -> Result<(), ErrorList> {
182    validate_capabilities(capabilities, false)
183}
184
185/// An interface to call into either `check_dynamic_name()` or `check_name()`, depending on the context
186/// of the caller.
187type CheckChildNameFn = fn(Option<&String>, DeclType, &str, &mut Vec<Error>) -> bool;
188
189pub fn validate_dynamic_child(child: &fdecl::Child) -> Result<(), ErrorList> {
190    let mut errors = vec![];
191
192    if let Err(mut error_list) = validate_child(child, check_dynamic_name) {
193        errors.append(&mut error_list.errs);
194    }
195
196    if child.environment.is_some() {
197        errors.push(Error::DynamicChildWithEnvironment);
198    }
199
200    if errors.is_empty() { Ok(()) } else { Err(ErrorList { errs: errors }) }
201}
202
203/// Validates an independent Child. Performs the same validation on it as `validate`. A
204/// `check_name_fn` is passed into specify the function used to validate the child name.
205fn validate_child(
206    child: &fdecl::Child,
207    check_child_name: CheckChildNameFn,
208) -> Result<(), ErrorList> {
209    let mut errors = vec![];
210    check_child_name(child.name.as_ref(), DeclType::Child, "name", &mut errors);
211    check_url(child.url.as_ref(), DeclType::Child, "url", &mut errors);
212    if child.startup.is_none() {
213        errors.push(Error::missing_field(DeclType::Child, "startup"));
214    }
215    // Allow `on_terminate` to be unset since the default is almost always desired.
216    if child.environment.is_some() {
217        check_name(child.environment.as_ref(), DeclType::Child, "environment", &mut errors);
218    }
219    if errors.is_empty() { Ok(()) } else { Err(ErrorList { errs: errors }) }
220}
221
222/// Validates a collection of dynamic offers. Dynamic offers differ from static
223/// offers, in that
224///
225/// 1. a dynamic offer's `target` field must be omitted;
226/// 2. a dynamic offer's `source` _may_ be a dynamic child;
227/// 3. since this crate isn't really designed to handle dynamic children, we
228///    disable the checks that ensure that the source/target exist, and that the
229///    offers don't introduce any cycles.
230///
231/// `dependencies` is an output parameter that captures new dependencies from `new_dynamic_offers`.
232/// Any existing dependency edges are preserved. The input may be non empty and is normally the
233/// output of the previous [`validate`] or [`validate_dynamic_offers`].
234pub fn validate_dynamic_offers<'a>(
235    dynamic_children: Vec<(&'a str, &'a str)>,
236    dependencies: &mut DirectedGraph<DependencyNode>,
237    new_dynamic_offers: &'a [fdecl::Offer],
238    decl: &'a fdecl::Component,
239) -> Result<(), ErrorList> {
240    let mut ctx = ValidationContext::new(dependencies);
241    ctx.dynamic_children = dynamic_children;
242    ctx.validate(decl, new_dynamic_offers).map_err(|errs| ErrorList::new(errs))
243}
244
245fn check_offer_name(
246    prop: Option<&String>,
247    decl: DeclType,
248    keyword: &str,
249    offer_type: OfferType,
250    errors: &mut Vec<Error>,
251) -> bool {
252    if offer_type == OfferType::Dynamic {
253        check_dynamic_name(prop, decl, keyword, errors)
254    } else {
255        check_name(prop, decl, keyword, errors)
256    }
257}
258
259struct ValidationContext<'a> {
260    all_children: HashMap<&'a str, &'a fdecl::Child>,
261    all_collections: HashSet<&'a str>,
262    all_capability_ids: HashSet<&'a str>,
263    all_storages: HashMap<&'a str, Option<&'a fdecl::Ref>>,
264    all_services: HashSet<&'a str>,
265    all_protocols: HashSet<&'a str>,
266    all_directories: HashSet<&'a str>,
267    all_runners: HashSet<&'a str>,
268    all_resolvers: HashSet<&'a str>,
269    all_dictionaries: HashMap<&'a str, &'a fdecl::Dictionary>,
270
271    #[cfg(fuchsia_api_level_at_least = "HEAD")]
272    all_configs: HashSet<&'a str>,
273
274    all_environment_names: HashSet<&'a str>,
275    dynamic_children: Vec<(&'a str, &'a str)>,
276    strong_dependencies: &'a mut DirectedGraph<DependencyNode>,
277    target_ids: IdMap<'a>,
278    errors: Vec<Error>,
279}
280
281/// [Container] provides a capability type agnostic trait to check for the existence of a
282/// capability definition of a particular type. This is useful for writing common validation
283/// functions.
284trait Container {
285    fn contains(&self, key: &str) -> bool;
286}
287
288impl<'a> Container for HashSet<&'a str> {
289    fn contains(&self, key: &str) -> bool {
290        self.contains(key)
291    }
292}
293
294impl<'a, T> Container for HashMap<&'a str, T> {
295    fn contains(&self, key: &str) -> bool {
296        self.contains_key(key)
297    }
298}
299
300impl<'a> ValidationContext<'a> {
301    fn new(strong_dependencies: &'a mut DirectedGraph<DependencyNode>) -> Self {
302        Self {
303            strong_dependencies,
304            all_children: Default::default(),
305            all_collections: Default::default(),
306            all_capability_ids: Default::default(),
307            all_storages: Default::default(),
308            all_services: Default::default(),
309            all_protocols: Default::default(),
310            all_directories: Default::default(),
311            all_runners: Default::default(),
312            all_resolvers: Default::default(),
313            all_dictionaries: Default::default(),
314
315            #[cfg(fuchsia_api_level_at_least = "HEAD")]
316            all_configs: Default::default(),
317
318            all_environment_names: Default::default(),
319            dynamic_children: Default::default(),
320            target_ids: Default::default(),
321            errors: Default::default(),
322        }
323    }
324
325    fn validate(
326        mut self,
327        decl: &'a fdecl::Component,
328        new_dynamic_offers: &'a [fdecl::Offer],
329    ) -> Result<(), Vec<Error>> {
330        // Collect all environment names first, so that references to them can be checked.
331        if let Some(envs) = &decl.environments {
332            self.collect_environment_names(&envs);
333        }
334
335        // Validate "children" and build the set of all children.
336        if let Some(children) = decl.children.as_ref() {
337            for child in children {
338                self.validate_child_decl(&child);
339            }
340        }
341
342        // Validate "collections" and build the set of all collections.
343        if let Some(collections) = decl.collections.as_ref() {
344            for collection in collections {
345                self.validate_collection_decl(&collection);
346            }
347        }
348
349        // Validate "capabilities" and build the set of all capabilities.
350        if let Some(capabilities) = decl.capabilities.as_ref() {
351            self.load_dictionary_names(capabilities.iter().filter_map(
352                |capability| match capability {
353                    fdecl::Capability::Dictionary(dictionary_decl) => Some(dictionary_decl),
354                    _ => None,
355                },
356            ));
357            self.validate_capability_decls(capabilities, false);
358        }
359
360        // Validate "uses".
361        let mut use_runner_name = None;
362        let mut use_runner_source = None;
363        if let Some(uses) = decl.uses.as_ref() {
364            (use_runner_name, use_runner_source) = self.validate_use_decls(uses);
365        }
366
367        // Validate "program".
368        if let Some(program) = decl.program.as_ref() {
369            self.validate_program(program, use_runner_name, use_runner_source);
370        }
371
372        // Validate "exposes".
373        if let Some(exposes) = decl.exposes.as_ref() {
374            let mut expose_to_parent_ids = HashMap::new();
375            let mut expose_to_framework_ids = HashMap::new();
376            for expose in exposes.iter() {
377                self.validate_expose_decl(
378                    &expose,
379                    &mut expose_to_parent_ids,
380                    &mut expose_to_framework_ids,
381                );
382            }
383            self.validate_expose_group(&exposes);
384        }
385
386        // Validate "offers".
387        if let Some(offers) = decl.offers.as_ref() {
388            for offer in offers.iter() {
389                self.validate_offer_decl(&offer, OfferType::Static);
390            }
391            self.validate_offer_group(&offers, OfferType::Static);
392        }
393
394        for dynamic_offer in new_dynamic_offers {
395            self.validate_offer_decl(dynamic_offer, OfferType::Dynamic);
396            cm_graph::add_dependencies_from_offer(
397                &mut self.strong_dependencies,
398                dynamic_offer,
399                &self.dynamic_children,
400            );
401        }
402        self.validate_offer_group(new_dynamic_offers, OfferType::Dynamic);
403
404        // Validate "environments" after all other declarations are processed.
405        if let Some(environment) = decl.environments.as_ref() {
406            for environment in environment {
407                self.validate_environment_decl(&environment);
408            }
409        }
410
411        // Validate "config"
412        self.validate_config(decl.config.as_ref(), decl.uses.as_ref());
413
414        // Check that there are no strong cyclical dependencies
415        cm_graph::generate_dependency_graph(
416            &mut self.strong_dependencies,
417            &decl,
418            &self.dynamic_children,
419        );
420        if let Err(e) = self.strong_dependencies.topological_sort() {
421            self.errors.push(Error::dependency_cycle(e.format_cycle()));
422        }
423
424        if self.errors.is_empty() { Ok(()) } else { Err(self.errors) }
425    }
426
427    /// Collects all the environment names, watching for duplicates.
428    fn collect_environment_names(&mut self, envs: &'a [fdecl::Environment]) {
429        for env in envs {
430            if let Some(name) = env.name.as_ref() {
431                if !self.all_environment_names.insert(name) {
432                    self.errors.push(Error::duplicate_field(DeclType::Environment, "name", name));
433                }
434            }
435        }
436    }
437
438    // Validates a config schema. Checks that each field's layout matches the expected constraints
439    // and properties.
440    fn validate_config(
441        &mut self,
442        config: Option<&fdecl::ConfigSchema>,
443        uses: Option<&Vec<fdecl::Use>>,
444    ) {
445        use std::collections::BTreeMap;
446
447        // Get all of the `use` configs that are optional without a default.
448        let optional_use_keys: BTreeMap<String, fdecl::ConfigType> =
449            uses.map_or(BTreeMap::new(), |u| {
450                u.iter()
451                    .map(|u| {
452                        let fdecl::Use::Config(config) = u else {
453                            return None;
454                        };
455                        if config.availability == Some(fdecl::Availability::Required)
456                            || config.availability == None
457                        {
458                            return None;
459                        }
460                        if let Some(_) = config.default.as_ref() {
461                            return None;
462                        }
463                        let Some(key) = config.target_name.clone() else {
464                            return None;
465                        };
466                        let Some(value) = config.type_.clone() else {
467                            return None;
468                        };
469                        Some((key, value))
470                    })
471                    .flatten()
472                    .collect()
473            });
474
475        // Validate default values in use configs.
476        for u in uses.iter().flat_map(|x| x.iter()) {
477            let fdecl::Use::Config(config) = u else { continue };
478            let Some(default) = config.default.as_ref() else { continue };
479            validate_value_spec(&fdecl::ConfigValueSpec {
480                value: Some(default.clone()),
481                ..Default::default()
482            })
483            .map_err(|mut e| self.errors.append(&mut e.errs))
484            .ok();
485        }
486
487        let Some(config) = config else {
488            if !optional_use_keys.is_empty() {
489                self.errors.push(Error::missing_field(DeclType::ConfigField, "config"))
490            }
491            return;
492        };
493
494        if let Some(fields) = &config.fields {
495            for field in fields {
496                if field.key.is_none() {
497                    self.errors.push(Error::missing_field(DeclType::ConfigField, "key"));
498                }
499                if let Some(type_) = &field.type_ {
500                    self.validate_config_type(type_, true);
501                } else {
502                    self.errors.push(Error::missing_field(DeclType::ConfigField, "value_type"));
503                }
504            }
505        } else {
506            self.errors.push(Error::missing_field(DeclType::ConfigSchema, "fields"));
507        }
508
509        if let Some(checksum) = &config.checksum {
510            match checksum {
511                fdecl::ConfigChecksum::Sha256(_) => {}
512                fdecl::ConfigChecksumUnknown!() => {
513                    self.errors.push(Error::invalid_field(DeclType::ConfigSchema, "checksum"));
514                }
515            }
516        } else {
517            self.errors.push(Error::missing_field(DeclType::ConfigSchema, "checksum"));
518        }
519
520        'outer: for (key, value) in optional_use_keys.iter() {
521            for field in config.fields.iter().flatten() {
522                if field.key.as_ref() == Some(key) {
523                    if field.type_.as_ref() != Some(value) {
524                        self.errors.push(Error::invalid_field(DeclType::ConfigField, key.clone()));
525                    }
526                    continue 'outer;
527                }
528            }
529            self.errors.push(Error::missing_field(DeclType::ConfigField, key.clone()));
530        }
531
532        match config.value_source {
533            None => self.errors.push(Error::missing_field(DeclType::ConfigSchema, "value_source")),
534            #[cfg(fuchsia_api_level_at_least = "HEAD")]
535            Some(fdecl::ConfigValueSource::Capabilities(_)) => {
536                if !optional_use_keys.is_empty() {
537                    self.errors
538                        .push(Error::invalid_field(DeclType::ConfigValueSource, "ValueSource"))
539                }
540            }
541            Some(fdecl::ConfigValueSource::__SourceBreaking { .. }) => {
542                self.errors.push(Error::invalid_field(DeclType::ConfigValueSource, "ValueSource"))
543            }
544            _ => (),
545        };
546    }
547
548    fn validate_config_type(&mut self, type_: &fdecl::ConfigType, accept_vectors: bool) {
549        match &type_.layout {
550            fdecl::ConfigTypeLayout::Bool
551            | fdecl::ConfigTypeLayout::Uint8
552            | fdecl::ConfigTypeLayout::Uint16
553            | fdecl::ConfigTypeLayout::Uint32
554            | fdecl::ConfigTypeLayout::Uint64
555            | fdecl::ConfigTypeLayout::Int8
556            | fdecl::ConfigTypeLayout::Int16
557            | fdecl::ConfigTypeLayout::Int32
558            | fdecl::ConfigTypeLayout::Int64 => {
559                // These layouts have no parameters or constraints
560                if let Some(parameters) = &type_.parameters {
561                    if !parameters.is_empty() {
562                        self.errors
563                            .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
564                    }
565                } else {
566                    self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"));
567                }
568
569                if !type_.constraints.is_empty() {
570                    self.errors.push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
571                }
572            }
573            fdecl::ConfigTypeLayout::String => {
574                // String has exactly one constraint and no parameter
575                if let Some(parameters) = &type_.parameters {
576                    if !parameters.is_empty() {
577                        self.errors
578                            .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
579                    }
580                } else {
581                    self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"));
582                }
583
584                if type_.constraints.is_empty() {
585                    self.errors.push(Error::missing_field(DeclType::ConfigType, "constraints"));
586                } else if type_.constraints.len() > 1 {
587                    self.errors.push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
588                } else if let fdecl::LayoutConstraint::MaxSize(_) = &type_.constraints[0] {
589                } else {
590                    self.errors.push(Error::invalid_field(DeclType::ConfigType, "constraints"));
591                }
592            }
593            fdecl::ConfigTypeLayout::Vector => {
594                if accept_vectors {
595                    // Vector has exactly one constraint and one parameter
596                    if let Some(parameters) = &type_.parameters {
597                        if parameters.is_empty() {
598                            self.errors
599                                .push(Error::missing_field(DeclType::ConfigType, "parameters"));
600                        } else if parameters.len() > 1 {
601                            self.errors
602                                .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
603                        } else if let fdecl::LayoutParameter::NestedType(nested_type) =
604                            &parameters[0]
605                        {
606                            self.validate_config_type(nested_type, false);
607                        } else {
608                            self.errors
609                                .push(Error::invalid_field(DeclType::ConfigType, "parameters"));
610                        }
611                    } else {
612                        self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"))
613                    }
614
615                    if type_.constraints.is_empty() {
616                        self.errors.push(Error::missing_field(DeclType::ConfigType, "constraints"));
617                    } else if type_.constraints.len() > 1 {
618                        self.errors
619                            .push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
620                    } else if let fdecl::LayoutConstraint::MaxSize(_) = &type_.constraints[0] {
621                    } else {
622                        self.errors.push(Error::invalid_field(DeclType::ConfigType, "constraints"));
623                    }
624                } else {
625                    self.errors.push(Error::nested_vector());
626                }
627            }
628            _ => self.errors.push(Error::invalid_field(DeclType::ConfigType, "layout")),
629        }
630    }
631
632    fn validate_capability_decls(
633        &mut self,
634        capabilities: &'a [fdecl::Capability],
635        as_builtin: bool,
636    ) {
637        for capability in capabilities {
638            self.validate_capability_decl(capability, as_builtin);
639        }
640    }
641
642    /// Validates an individual capability declaration as either a built-in capability or (if
643    /// `as_builtin = false`) as a component or namespace capability.
644    // Storage capabilities are not currently allowed as built-ins, but there's no deep reason for this.
645    // Update this method to allow built-in storage capabilities as needed.
646    fn validate_capability_decl(&mut self, capability: &'a fdecl::Capability, as_builtin: bool) {
647        match capability {
648            fdecl::Capability::Service(service) => self.validate_service_decl(&service, as_builtin),
649            fdecl::Capability::Protocol(protocol) => {
650                self.validate_protocol_decl(&protocol, as_builtin)
651            }
652            fdecl::Capability::Directory(directory) => {
653                self.validate_directory_decl(&directory, as_builtin)
654            }
655            fdecl::Capability::Storage(storage) => {
656                if as_builtin {
657                    self.errors.push(Error::CapabilityCannotBeBuiltin(DeclType::Storage))
658                } else {
659                    self.validate_storage_decl(&storage)
660                }
661            }
662            fdecl::Capability::Runner(runner) => self.validate_runner_decl(&runner, as_builtin),
663            fdecl::Capability::Resolver(resolver) => {
664                self.validate_resolver_decl(&resolver, as_builtin)
665            }
666            fdecl::Capability::EventStream(event) => {
667                if as_builtin {
668                    self.validate_event_stream_decl(&event)
669                } else {
670                    self.errors.push(Error::CapabilityMustBeBuiltin(DeclType::EventStream))
671                }
672            }
673            fdecl::Capability::Dictionary(dictionary) => {
674                self.validate_dictionary_decl(&dictionary);
675            }
676            #[cfg(fuchsia_api_level_at_least = "HEAD")]
677            fdecl::Capability::Config(config) => {
678                self.validate_configuration_decl(&config);
679            }
680            fdecl::CapabilityUnknown!() => self.errors.push(Error::UnknownCapability),
681        }
682    }
683
684    /// Returns the `source_name` and `source` of the runner in `uses`, if present.
685    fn validate_use_decls(
686        &mut self,
687        uses: &'a [fdecl::Use],
688    ) -> (Option<&'a String>, Option<&'a fdecl::Ref>) {
689        // Validate individual fields.
690        for use_ in uses.iter() {
691            self.validate_use_decl(&use_);
692        }
693        self.validate_use_paths(&uses);
694
695        #[cfg(fuchsia_api_level_at_least = "HEAD")]
696        {
697            let mut use_runner_name = None;
698            let mut use_runner_source = None;
699            for use_ in uses.iter() {
700                if let fdecl::Use::Runner(use_runner) = use_ {
701                    if use_runner_name.is_some() {
702                        self.errors.push(Error::MultipleRunnersUsed);
703                    }
704
705                    use_runner_name = use_runner.source_name.as_ref();
706                    use_runner_source = use_runner.source.as_ref();
707                }
708            }
709            return (use_runner_name, use_runner_source);
710        }
711        #[cfg(fuchsia_api_level_less_than = "HEAD")]
712        return (None, None);
713    }
714
715    fn validate_use_decl(&mut self, use_: &'a fdecl::Use) {
716        match use_ {
717            fdecl::Use::Service(u) => {
718                let decl = DeclType::UseService;
719                self.validate_use_fields(
720                    decl,
721                    Self::service_checker,
722                    u.source.as_ref(),
723                    u.source_name.as_ref(),
724                    u.source_dictionary.as_ref(),
725                    u.target_path.as_ref(),
726                    u.dependency_type.as_ref(),
727                    u.availability.as_ref(),
728                );
729                if u.dependency_type.is_none() {
730                    self.errors.push(Error::missing_field(decl, "dependency_type"));
731                }
732            }
733            fdecl::Use::Protocol(u) => {
734                let decl = DeclType::UseProtocol;
735                self.validate_use_fields(
736                    decl,
737                    Self::protocol_checker,
738                    u.source.as_ref(),
739                    u.source_name.as_ref(),
740                    u.source_dictionary.as_ref(),
741                    u.target_path.as_ref(),
742                    u.dependency_type.as_ref(),
743                    u.availability.as_ref(),
744                );
745                #[cfg(not(fuchsia_api_level_at_least = "NEXT"))]
746                let has_numbered_handle = false;
747                #[cfg(fuchsia_api_level_at_least = "NEXT")]
748                let has_numbered_handle = u.numbered_handle.is_some();
749                if u.target_path.is_none() && !has_numbered_handle {
750                    self.errors.push(Error::missing_field(decl, "target_path"));
751                }
752                if has_numbered_handle && u.target_path.is_some() {
753                    self.errors.push(Error::extraneous_field(decl, "numbered_handle"));
754                }
755                if u.dependency_type.is_none() {
756                    self.errors.push(Error::missing_field(decl, "dependency_type"));
757                }
758            }
759            fdecl::Use::Directory(u) => {
760                let decl = DeclType::UseDirectory;
761                self.validate_use_fields(
762                    decl,
763                    Self::directory_checker,
764                    u.source.as_ref(),
765                    u.source_name.as_ref(),
766                    u.source_dictionary.as_ref(),
767                    u.target_path.as_ref(),
768                    u.dependency_type.as_ref(),
769                    u.availability.as_ref(),
770                );
771                if u.dependency_type.is_none() {
772                    self.errors.push(Error::missing_field(decl, "dependency_type"));
773                }
774                if u.rights.is_none() {
775                    self.errors.push(Error::missing_field(DeclType::UseDirectory, "rights"));
776                }
777                if let Some(subdir) = u.subdir.as_ref() {
778                    check_relative_path(
779                        Some(subdir),
780                        DeclType::UseDirectory,
781                        "subdir",
782                        &mut self.errors,
783                    );
784                }
785            }
786            fdecl::Use::Storage(u) => {
787                const SOURCE: Option<fdecl::Ref> = Some(fdecl::Ref::Parent(fdecl::ParentRef {}));
788                const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
789                    Some(fdecl::DependencyType::Strong);
790                self.validate_use_fields(
791                    DeclType::UseStorage,
792                    Self::storage_checker,
793                    SOURCE.as_ref(),
794                    u.source_name.as_ref(),
795                    None,
796                    u.target_path.as_ref(),
797                    DEPENDENCY_TYPE.as_ref(),
798                    u.availability.as_ref(),
799                );
800            }
801            fdecl::Use::EventStream(u) => {
802                const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
803                    Some(fdecl::DependencyType::Strong);
804                let decl = DeclType::UseEventStream;
805                self.validate_use_fields(
806                    decl,
807                    Self::event_stream_checker,
808                    u.source.as_ref(),
809                    u.source_name.as_ref(),
810                    None,
811                    u.target_path.as_ref(),
812                    DEPENDENCY_TYPE.as_ref(),
813                    u.availability.as_ref(),
814                );
815                // Additional validation.
816                match u.source {
817                    Some(fdecl::Ref::Child(_)) | Some(fdecl::Ref::Parent(_)) => {
818                        // Allowed.
819                    }
820                    Some(fdecl::Ref::Framework(_))
821                    | Some(fdecl::Ref::Self_(_))
822                    | Some(fdecl::Ref::Debug(_)) => {
823                        // Allowed in general but not for event streams, add an error.
824                        self.errors.push(Error::invalid_field(decl, "source"));
825                    }
826                    Some(fdecl::Ref::Collection(_)) | Some(fdecl::RefUnknown!()) | None => {
827                        // Already handled by validate_use_fields.
828                    }
829                }
830                if let Some(scope) = &u.scope {
831                    for reference in scope {
832                        if !matches!(reference, fdecl::Ref::Child(_) | fdecl::Ref::Collection(_)) {
833                            self.errors.push(Error::invalid_field(decl, "scope"));
834                        }
835                    }
836                }
837            }
838            #[cfg(fuchsia_api_level_at_least = "HEAD")]
839            fdecl::Use::Runner(u) => {
840                const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
841                    Some(fdecl::DependencyType::Strong);
842                const AVAILABILITY: Option<fdecl::Availability> =
843                    Some(fdecl::Availability::Required);
844                let decl = DeclType::UseRunner;
845                self.validate_use_fields(
846                    decl,
847                    Self::runner_checker,
848                    u.source.as_ref(),
849                    u.source_name.as_ref(),
850                    u.source_dictionary.as_ref(),
851                    None,
852                    DEPENDENCY_TYPE.as_ref(),
853                    AVAILABILITY.as_ref(),
854                );
855            }
856            #[cfg(fuchsia_api_level_at_least = "HEAD")]
857            fdecl::Use::Config(u) => {
858                const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
859                    Some(fdecl::DependencyType::Strong);
860                let decl = DeclType::UseConfiguration;
861                self.validate_use_fields(
862                    decl,
863                    Self::config_checker,
864                    u.source.as_ref(),
865                    u.source_name.as_ref(),
866                    None,
867                    None,
868                    DEPENDENCY_TYPE.as_ref(),
869                    u.availability.as_ref(),
870                );
871            }
872            #[cfg(fuchsia_api_level_at_least = "NEXT")]
873            fdecl::Use::Dictionary(u) => {
874                let decl = DeclType::UseDictionary;
875                self.validate_use_fields(
876                    decl,
877                    Self::dictionary_checker,
878                    u.source.as_ref(),
879                    u.source_name.as_ref(),
880                    u.source_dictionary.as_ref(),
881                    u.target_path.as_ref(),
882                    u.dependency_type.as_ref(),
883                    u.availability.as_ref(),
884                );
885                if u.dependency_type.is_none() {
886                    self.errors.push(Error::missing_field(decl, "dependency_type"));
887                }
888            }
889            fdecl::UseUnknown!() => {
890                self.errors.push(Error::invalid_field(DeclType::Component, "use"));
891            }
892        }
893    }
894
895    /// Validates the "program" declaration. This does not check runner-specific properties
896    /// since those are checked by the runner.
897    fn validate_program(
898        &mut self,
899        program: &fdecl::Program,
900        use_runner_name: Option<&String>,
901        _use_runner_source: Option<&fdecl::Ref>,
902    ) {
903        match &program.runner {
904            Some(_) =>
905            {
906                #[cfg(fuchsia_api_level_at_least = "HEAD")]
907                if use_runner_name.is_some() {
908                    if use_runner_name != program.runner.as_ref()
909                        || _use_runner_source
910                            != Some(&fdecl::Ref::Environment(fdecl::EnvironmentRef))
911                    {
912                        self.errors.push(Error::ConflictingRunners);
913                    }
914                }
915            }
916            None => {
917                if use_runner_name.is_none() {
918                    self.errors.push(Error::MissingRunner);
919                }
920            }
921        }
922
923        if program.info.is_none() {
924            self.errors.push(Error::missing_field(DeclType::Program, "info"));
925        }
926    }
927
928    /// Validates that paths-based capabilities (service, directory, protocol)
929    /// are different, are not prefixes of each other, and do not collide "/pkg".
930    fn validate_use_paths(&mut self, uses: &[fdecl::Use]) {
931        #[derive(Debug, PartialEq, Clone, Copy)]
932        struct PathCapability<'a> {
933            decl: DeclType,
934            dir: &'a Path,
935            use_: &'a fdecl::Use,
936        }
937        let mut used_paths = HashMap::new();
938        for use_ in uses.iter() {
939            let (capability, path) = match use_ {
940                fdecl::Use::Service(fdecl::UseService { target_path: Some(path), .. }) => {
941                    let dir = match Path::new(path).parent() {
942                        Some(p) => p,
943                        None => continue, // Invalid path, validated elsewhere
944                    };
945                    (PathCapability { decl: DeclType::UseService, dir, use_ }, path)
946                }
947                fdecl::Use::Protocol(fdecl::UseProtocol { target_path: Some(path), .. }) => {
948                    let dir = match Path::new(path).parent() {
949                        Some(p) => p,
950                        None => continue, // Invalid path, validated elsewhere
951                    };
952                    (PathCapability { decl: DeclType::UseProtocol, dir, use_ }, path)
953                }
954                fdecl::Use::Directory(fdecl::UseDirectory { target_path: Some(path), .. }) => (
955                    PathCapability { decl: DeclType::UseDirectory, dir: Path::new(path), use_ },
956                    path,
957                ),
958                fdecl::Use::Storage(fdecl::UseStorage { target_path: Some(path), .. }) => (
959                    PathCapability { decl: DeclType::UseStorage, dir: Path::new(path), use_ },
960                    path,
961                ),
962                #[cfg(fuchsia_api_level_at_least = "NEXT")]
963                fdecl::Use::Dictionary(fdecl::UseDictionary {
964                    target_path: Some(path), ..
965                }) => (
966                    PathCapability { decl: DeclType::UseDictionary, dir: Path::new(path), use_ },
967                    path,
968                ),
969                _ => continue,
970            };
971            if used_paths.insert(path, capability).is_some() {
972                // Disallow multiple capabilities for the same path.
973                self.errors.push(Error::duplicate_field(capability.decl, "target_path", path));
974            }
975        }
976        for ((&path_a, capability_a), (&path_b, capability_b)) in
977            used_paths.iter().tuple_combinations()
978        {
979            if match (capability_a.use_, capability_b.use_) {
980                // Directories and storage can't be the same or partially overlap.
981                (fdecl::Use::Directory(_), fdecl::Use::Directory(_))
982                | (fdecl::Use::Storage(_), fdecl::Use::Directory(_))
983                | (fdecl::Use::Directory(_), fdecl::Use::Storage(_))
984                | (fdecl::Use::Storage(_), fdecl::Use::Storage(_)) => {
985                    capability_b.dir == capability_a.dir
986                        || capability_b.dir.starts_with(capability_a.dir)
987                        || capability_a.dir.starts_with(capability_b.dir)
988                }
989
990                // Protocols, services, and dictionaries can't overlap with Directories.
991                (_, fdecl::Use::Directory(_)) | (fdecl::Use::Directory(_), _) => {
992                    capability_b.dir == capability_a.dir
993                        || capability_b.dir.starts_with(capability_a.dir)
994                        || capability_a.dir.starts_with(capability_b.dir)
995                }
996
997                // Dictionary capabilities can be prefixes of protocols and services, but not the
998                // other way around.
999                #[cfg(fuchsia_api_level_at_least = "NEXT")]
1000                (fdecl::Use::Dictionary(_), _) => {
1001                    capability_b.dir != capability_a.dir
1002                        && capability_b.dir.starts_with(capability_a.dir)
1003                }
1004                #[cfg(fuchsia_api_level_at_least = "NEXT")]
1005                (_, fdecl::Use::Dictionary(_)) => {
1006                    capability_b.dir != capability_a.dir
1007                        && capability_a.dir.starts_with(capability_b.dir)
1008                }
1009
1010                // Protocols and services containing directories may be same, but
1011                // partial overlap is disallowed.
1012                (_, _) => {
1013                    capability_b.dir != capability_a.dir
1014                        && (capability_b.dir.starts_with(capability_a.dir)
1015                            || capability_a.dir.starts_with(capability_b.dir))
1016                }
1017            } {
1018                self.errors.push(Error::invalid_path_overlap(
1019                    capability_a.decl,
1020                    path_a,
1021                    capability_b.decl,
1022                    path_b,
1023                ));
1024            }
1025        }
1026        for (used_path, capability) in used_paths.iter() {
1027            if used_path.as_str() == "/pkg" || used_path.starts_with("/pkg/") {
1028                self.errors.push(Error::pkg_path_overlap(capability.decl, *used_path));
1029            }
1030        }
1031    }
1032
1033    fn validate_use_fields(
1034        &mut self,
1035        decl: DeclType,
1036        // This takes a callback that returns a [Container], instead of the &[Container] directly,
1037        // to avoid a borrow checker error that would occur from a simultaneous borrow on
1038        // &mut self.
1039        capability_checker: impl Fn(&Self) -> &dyn Container,
1040        source: Option<&'a fdecl::Ref>,
1041        source_name: Option<&'a String>,
1042        source_dictionary: Option<&'a String>,
1043        target_path: Option<&'a String>,
1044        dependency_type: Option<&fdecl::DependencyType>,
1045        availability: Option<&'a fdecl::Availability>,
1046    ) {
1047        self.validate_use_source(decl, source, source_dictionary);
1048
1049        check_name(source_name, decl, "source_name", &mut self.errors);
1050        if source_dictionary.is_some() {
1051            check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
1052        }
1053        match decl {
1054            DeclType::UseRunner | DeclType::UseConfiguration => {}
1055            #[cfg(fuchsia_api_level_at_least = "HEAD")]
1056            DeclType::UseProtocol => {
1057                if target_path.is_some() {
1058                    check_path(target_path, decl, "target_path", &mut self.errors);
1059                } else {
1060                    // Expect this use to contain numbered_handle, verified by the caller.
1061                }
1062            }
1063            _ => {
1064                check_path(target_path, decl, "target_path", &mut self.errors);
1065            }
1066        }
1067        check_use_availability(decl, availability, &mut self.errors);
1068
1069        // Only allow `weak` dependency with `use from child`.
1070        let is_use_from_child = match source {
1071            Some(fdecl::Ref::Child(_)) => true,
1072            _ => false,
1073        };
1074        match (is_use_from_child, dependency_type) {
1075            (false, Some(fdecl::DependencyType::Weak)) => {
1076                self.errors.push(Error::invalid_field(decl, "dependency_type"));
1077            }
1078            _ => {}
1079        }
1080
1081        self.validate_route_from_self(
1082            decl,
1083            source,
1084            source_name,
1085            source_dictionary,
1086            capability_checker,
1087        );
1088    }
1089
1090    fn validate_use_source(
1091        &mut self,
1092        decl: DeclType,
1093        source: Option<&'a fdecl::Ref>,
1094        source_dictionary: Option<&'a String>,
1095    ) {
1096        match (source, source_dictionary) {
1097            // These sources support source_dictionary.
1098            (Some(fdecl::Ref::Parent(_)), _) => {}
1099            (Some(fdecl::Ref::Self_(_)), _) => {}
1100            (Some(fdecl::Ref::Child(child)), _) => {
1101                self.validate_child_ref(decl, "source", &child, OfferType::Static);
1102                return;
1103            }
1104            // These sources don't.
1105            (Some(fdecl::Ref::Framework(_)), None) => {}
1106            (Some(fdecl::Ref::Debug(_)), None) => {}
1107            (Some(fdecl::Ref::Capability(c)), None) => {
1108                self.validate_source_capability(&c, decl, "source");
1109                return;
1110            }
1111            #[cfg(fuchsia_api_level_at_least = "HEAD")]
1112            (Some(fdecl::Ref::Environment(_)), None) => {}
1113            (Some(fdecl::Ref::Collection(collection)), None) if decl == DeclType::UseService => {
1114                self.validate_collection_ref(decl, "source", &collection);
1115                return;
1116            }
1117            // `source` is required.
1118            (None, _) => self.errors.push(Error::missing_field(decl, "source")),
1119            // Any combination that was not recognized above must be invalid.
1120            (_, _) => self.errors.push(Error::invalid_field(decl, "source")),
1121        }
1122    }
1123
1124    fn validate_child_decl(&mut self, child: &'a fdecl::Child) {
1125        if let Err(mut e) = validate_child(child, check_name) {
1126            self.errors.append(&mut e.errs);
1127        }
1128        if let Some(name) = child.name.as_ref() {
1129            let name: &str = name;
1130            if self.all_children.insert(name, child).is_some() {
1131                self.errors.push(Error::duplicate_field(DeclType::Child, "name", name));
1132            }
1133        }
1134        if let Some(environment) = child.environment.as_ref() {
1135            if !self.all_environment_names.contains(environment.as_str()) {
1136                self.errors.push(Error::invalid_environment(
1137                    DeclType::Child,
1138                    "environment",
1139                    environment,
1140                ));
1141            }
1142        }
1143    }
1144
1145    fn validate_collection_decl(&mut self, collection: &'a fdecl::Collection) {
1146        let name = collection.name.as_ref();
1147        if check_name(name, DeclType::Collection, "name", &mut self.errors) {
1148            let name: &str = name.unwrap();
1149            if !self.all_collections.insert(name) {
1150                self.errors.push(Error::duplicate_field(DeclType::Collection, "name", name));
1151            }
1152        }
1153        if collection.durability.is_none() {
1154            self.errors.push(Error::missing_field(DeclType::Collection, "durability"));
1155        }
1156        if let Some(environment) = collection.environment.as_ref() {
1157            if !self.all_environment_names.contains(environment.as_str()) {
1158                self.errors.push(Error::invalid_environment(
1159                    DeclType::Collection,
1160                    "environment",
1161                    environment,
1162                ));
1163            }
1164        }
1165        // Allow `allowed_offers` & `allow_long_names` to be unset/unvalidated, for backwards compatibility.
1166    }
1167
1168    fn validate_environment_decl(&mut self, environment: &'a fdecl::Environment) {
1169        let name = environment.name.as_ref();
1170        check_name(name, DeclType::Environment, "name", &mut self.errors);
1171        if environment.extends.is_none() {
1172            self.errors.push(Error::missing_field(DeclType::Environment, "extends"));
1173        }
1174        if let Some(runners) = environment.runners.as_ref() {
1175            let mut registered_runners = HashSet::new();
1176            for runner in runners {
1177                self.validate_runner_registration(runner, &mut registered_runners);
1178            }
1179        }
1180        if let Some(resolvers) = environment.resolvers.as_ref() {
1181            let mut registered_schemes = HashSet::new();
1182            for resolver in resolvers {
1183                self.validate_resolver_registration(resolver, &mut registered_schemes);
1184            }
1185        }
1186
1187        match environment.extends.as_ref() {
1188            Some(fdecl::EnvironmentExtends::None) => {
1189                if environment.stop_timeout_ms.is_none() {
1190                    self.errors
1191                        .push(Error::missing_field(DeclType::Environment, "stop_timeout_ms"));
1192                }
1193            }
1194            None | Some(fdecl::EnvironmentExtends::Realm) => {}
1195        }
1196
1197        if let Some(debugs) = environment.debug_capabilities.as_ref() {
1198            for debug in debugs {
1199                self.validate_environment_debug_registration(debug);
1200            }
1201        }
1202    }
1203
1204    fn validate_runner_registration(
1205        &mut self,
1206        runner_registration: &'a fdecl::RunnerRegistration,
1207        runner_names: &mut HashSet<&'a str>,
1208    ) {
1209        check_name(
1210            runner_registration.source_name.as_ref(),
1211            DeclType::RunnerRegistration,
1212            "source_name",
1213            &mut self.errors,
1214        );
1215        self.validate_registration_source(
1216            runner_registration.source.as_ref(),
1217            DeclType::RunnerRegistration,
1218        );
1219        // If the source is `self`, ensure we have a corresponding Runner.
1220        if let (Some(fdecl::Ref::Self_(_)), Some(name)) =
1221            (&runner_registration.source, runner_registration.source_name.as_ref())
1222        {
1223            if !self.all_runners.contains(name as &str) {
1224                self.errors.push(Error::invalid_runner(
1225                    DeclType::RunnerRegistration,
1226                    "source_name",
1227                    name,
1228                ));
1229            }
1230        }
1231
1232        check_name(
1233            runner_registration.target_name.as_ref(),
1234            DeclType::RunnerRegistration,
1235            "target_name",
1236            &mut self.errors,
1237        );
1238        if let Some(name) = runner_registration.target_name.as_ref() {
1239            if !runner_names.insert(name.as_str()) {
1240                self.errors.push(Error::duplicate_field(
1241                    DeclType::RunnerRegistration,
1242                    "target_name",
1243                    name,
1244                ));
1245            }
1246        }
1247    }
1248
1249    fn validate_resolver_registration(
1250        &mut self,
1251        resolver_registration: &'a fdecl::ResolverRegistration,
1252        schemes: &mut HashSet<&'a str>,
1253    ) {
1254        check_name(
1255            resolver_registration.resolver.as_ref(),
1256            DeclType::ResolverRegistration,
1257            "resolver",
1258            &mut self.errors,
1259        );
1260        self.validate_registration_source(
1261            resolver_registration.source.as_ref(),
1262            DeclType::ResolverRegistration,
1263        );
1264        check_url_scheme(
1265            resolver_registration.scheme.as_ref(),
1266            DeclType::ResolverRegistration,
1267            "scheme",
1268            &mut self.errors,
1269        );
1270        if let Some(scheme) = resolver_registration.scheme.as_ref() {
1271            if !schemes.insert(scheme.as_str()) {
1272                self.errors.push(Error::duplicate_field(
1273                    DeclType::ResolverRegistration,
1274                    "scheme",
1275                    scheme,
1276                ));
1277            }
1278        }
1279    }
1280
1281    fn validate_registration_source(&mut self, source: Option<&'a fdecl::Ref>, ty: DeclType) {
1282        match source {
1283            Some(fdecl::Ref::Parent(_)) => {}
1284            Some(fdecl::Ref::Self_(_)) => {}
1285            Some(fdecl::Ref::Child(child_ref)) => {
1286                // Make sure the child is valid.
1287                self.validate_child_ref(ty, "source", &child_ref, OfferType::Static);
1288            }
1289            Some(_) => {
1290                self.errors.push(Error::invalid_field(ty, "source"));
1291            }
1292            None => {
1293                self.errors.push(Error::missing_field(ty, "source"));
1294            }
1295        }
1296    }
1297
1298    fn validate_service_decl(&mut self, service: &'a fdecl::Service, as_builtin: bool) {
1299        if check_name(service.name.as_ref(), DeclType::Service, "name", &mut self.errors) {
1300            let name = service.name.as_ref().unwrap();
1301            if !self.all_capability_ids.insert(name) {
1302                self.errors.push(Error::duplicate_field(DeclType::Service, "name", name.as_str()));
1303            }
1304            self.all_services.insert(name);
1305        }
1306        match as_builtin {
1307            true => {
1308                if let Some(path) = service.source_path.as_ref() {
1309                    self.errors.push(Error::extraneous_source_path(DeclType::Service, path))
1310                }
1311            }
1312            false => {
1313                check_path(
1314                    service.source_path.as_ref(),
1315                    DeclType::Service,
1316                    "source_path",
1317                    &mut self.errors,
1318                );
1319            }
1320        }
1321    }
1322
1323    fn validate_protocol_decl(&mut self, protocol: &'a fdecl::Protocol, as_builtin: bool) {
1324        if check_name(protocol.name.as_ref(), DeclType::Protocol, "name", &mut self.errors) {
1325            let name = protocol.name.as_ref().unwrap();
1326            if !self.all_capability_ids.insert(name) {
1327                self.errors.push(Error::duplicate_field(DeclType::Protocol, "name", name.as_str()));
1328            }
1329            self.all_protocols.insert(name);
1330        }
1331        match as_builtin {
1332            true => {
1333                if let Some(path) = protocol.source_path.as_ref() {
1334                    self.errors.push(Error::extraneous_source_path(DeclType::Protocol, path))
1335                }
1336            }
1337            false => {
1338                check_path(
1339                    protocol.source_path.as_ref(),
1340                    DeclType::Protocol,
1341                    "source_path",
1342                    &mut self.errors,
1343                );
1344            }
1345        }
1346
1347        #[cfg(fuchsia_api_level_at_least = "HEAD")]
1348        match protocol.delivery {
1349            Some(delivery) => match cm_types::DeliveryType::try_from(delivery) {
1350                Ok(_) => {}
1351                Err(_) => self.errors.push(Error::invalid_field(DeclType::Protocol, "delivery")),
1352            },
1353            None => {}
1354        }
1355    }
1356
1357    fn validate_directory_decl(&mut self, directory: &'a fdecl::Directory, as_builtin: bool) {
1358        if check_name(directory.name.as_ref(), DeclType::Directory, "name", &mut self.errors) {
1359            let name = directory.name.as_ref().unwrap();
1360            if !self.all_capability_ids.insert(name) {
1361                self.errors.push(Error::duplicate_field(
1362                    DeclType::Directory,
1363                    "name",
1364                    name.as_str(),
1365                ));
1366            }
1367            self.all_directories.insert(name);
1368        }
1369        match as_builtin {
1370            true => {
1371                if let Some(path) = directory.source_path.as_ref() {
1372                    self.errors.push(Error::extraneous_source_path(DeclType::Directory, path))
1373                }
1374            }
1375            false => {
1376                check_path(
1377                    directory.source_path.as_ref(),
1378                    DeclType::Directory,
1379                    "source_path",
1380                    &mut self.errors,
1381                );
1382            }
1383        }
1384        if directory.rights.is_none() {
1385            self.errors.push(Error::missing_field(DeclType::Directory, "rights"));
1386        }
1387    }
1388
1389    fn validate_storage_decl(&mut self, storage: &'a fdecl::Storage) {
1390        match storage.source.as_ref() {
1391            Some(fdecl::Ref::Parent(_)) => {}
1392            Some(fdecl::Ref::Self_(_)) => {}
1393            Some(fdecl::Ref::Child(child)) => {
1394                let _ =
1395                    self.validate_child_ref(DeclType::Storage, "source", &child, OfferType::Static);
1396            }
1397            Some(_) => {
1398                self.errors.push(Error::invalid_field(DeclType::Storage, "source"));
1399            }
1400            None => {
1401                self.errors.push(Error::missing_field(DeclType::Storage, "source"));
1402            }
1403        };
1404        if check_name(storage.name.as_ref(), DeclType::Storage, "name", &mut self.errors) {
1405            let name = storage.name.as_ref().unwrap();
1406            if !self.all_capability_ids.insert(name) {
1407                self.errors.push(Error::duplicate_field(DeclType::Storage, "name", name.as_str()));
1408            }
1409            self.all_storages.insert(name, storage.source.as_ref());
1410        }
1411        if storage.storage_id.is_none() {
1412            self.errors.push(Error::missing_field(DeclType::Storage, "storage_id"));
1413        }
1414        check_name(
1415            storage.backing_dir.as_ref(),
1416            DeclType::Storage,
1417            "backing_dir",
1418            &mut self.errors,
1419        );
1420    }
1421
1422    fn validate_runner_decl(&mut self, runner: &'a fdecl::Runner, as_builtin: bool) {
1423        if check_name(runner.name.as_ref(), DeclType::Runner, "name", &mut self.errors) {
1424            let name = runner.name.as_ref().unwrap();
1425            if !self.all_capability_ids.insert(name) {
1426                self.errors.push(Error::duplicate_field(DeclType::Runner, "name", name.as_str()));
1427            }
1428            self.all_runners.insert(name);
1429        }
1430        match as_builtin {
1431            true => {
1432                if let Some(path) = runner.source_path.as_ref() {
1433                    self.errors.push(Error::extraneous_source_path(DeclType::Runner, path))
1434                }
1435            }
1436            false => {
1437                check_path(
1438                    runner.source_path.as_ref(),
1439                    DeclType::Runner,
1440                    "source_path",
1441                    &mut self.errors,
1442                );
1443            }
1444        }
1445    }
1446
1447    fn validate_resolver_decl(&mut self, resolver: &'a fdecl::Resolver, as_builtin: bool) {
1448        if check_name(resolver.name.as_ref(), DeclType::Resolver, "name", &mut self.errors) {
1449            let name = resolver.name.as_ref().unwrap();
1450            if !self.all_capability_ids.insert(name) {
1451                self.errors.push(Error::duplicate_field(DeclType::Resolver, "name", name.as_str()));
1452            }
1453            self.all_resolvers.insert(name);
1454        }
1455        match as_builtin {
1456            true => {
1457                if let Some(path) = resolver.source_path.as_ref() {
1458                    self.errors.push(Error::extraneous_source_path(DeclType::Resolver, path))
1459                }
1460            }
1461            false => {
1462                check_path(
1463                    resolver.source_path.as_ref(),
1464                    DeclType::Resolver,
1465                    "source_path",
1466                    &mut self.errors,
1467                );
1468            }
1469        }
1470    }
1471
1472    // Dictionaries can reference other dictionaries in the same manifest, so before processing any
1473    // dictionary declarations this function should be called to do a first pass to pre-populate
1474    // the dictionary map.
1475    fn load_dictionary_names(&mut self, dictionaries: impl Iterator<Item = &'a fdecl::Dictionary>) {
1476        for dictionary in dictionaries {
1477            let decl = DeclType::Dictionary;
1478            if check_name(dictionary.name.as_ref(), decl, "name", &mut self.errors) {
1479                let name = dictionary.name.as_ref().unwrap();
1480                if !self.all_capability_ids.insert(name) {
1481                    self.errors.push(Error::duplicate_field(decl, "name", name.as_str()));
1482                }
1483                self.all_dictionaries.insert(name, &dictionary);
1484            }
1485        }
1486    }
1487
1488    fn validate_dictionary_decl(&mut self, dictionary: &'a fdecl::Dictionary) {
1489        let decl = DeclType::Dictionary;
1490        if let Some(path) = dictionary.source_path.as_ref() {
1491            if dictionary.source.is_some() {
1492                self.errors.push(Error::extraneous_field(decl, "source"));
1493            }
1494            check_path(Some(path), DeclType::Dictionary, "source_path", &mut self.errors);
1495        }
1496    }
1497
1498    #[cfg(fuchsia_api_level_at_least = "HEAD")]
1499    fn validate_configuration_decl(&mut self, config: &'a fdecl::Configuration) {
1500        let decl = DeclType::Configuration;
1501        if check_name(config.name.as_ref(), decl, "name", &mut self.errors) {
1502            let name = config.name.as_ref().unwrap();
1503            if !self.all_capability_ids.insert(name) {
1504                self.errors.push(Error::duplicate_field(decl, "name", name.as_str()));
1505            }
1506            self.all_configs.insert(name);
1507        }
1508    }
1509
1510    fn validate_environment_debug_registration(&mut self, debug: &'a fdecl::DebugRegistration) {
1511        match debug {
1512            fdecl::DebugRegistration::Protocol(o) => {
1513                let decl = DeclType::DebugProtocolRegistration;
1514                self.validate_environment_debug_fields(
1515                    decl,
1516                    o.source.as_ref(),
1517                    o.source_name.as_ref(),
1518                    o.target_name.as_ref(),
1519                );
1520
1521                if let (Some(fdecl::Ref::Self_(_)), Some(name)) =
1522                    (&o.source, o.source_name.as_ref())
1523                {
1524                    if !self.all_protocols.contains(&name as &str) {
1525                        self.errors.push(Error::invalid_field(decl, "source"));
1526                    }
1527                }
1528            }
1529            _ => {
1530                self.errors.push(Error::invalid_field(DeclType::Environment, "debug"));
1531            }
1532        }
1533    }
1534
1535    fn validate_environment_debug_fields(
1536        &mut self,
1537        decl: DeclType,
1538        source: Option<&fdecl::Ref>,
1539        source_name: Option<&String>,
1540        target_name: Option<&'a String>,
1541    ) {
1542        // We don't support "source" from "capability" for now.
1543        match source {
1544            Some(fdecl::Ref::Parent(_)) => {}
1545            Some(fdecl::Ref::Self_(_)) => {}
1546            Some(fdecl::Ref::Framework(_)) => {}
1547            Some(fdecl::Ref::Child(child)) => {
1548                let _ = self.validate_child_ref(decl, "source", &child, OfferType::Static);
1549            }
1550            Some(_) => self.errors.push(Error::invalid_field(decl, "source")),
1551            None => self.errors.push(Error::missing_field(decl, "source")),
1552        }
1553        check_name(source_name, decl, "source_name", &mut self.errors);
1554        check_name(target_name, decl, "target_name", &mut self.errors);
1555    }
1556
1557    fn validate_event_stream_decl(&mut self, event: &'a fdecl::EventStream) {
1558        if check_name(event.name.as_ref(), DeclType::EventStream, "name", &mut self.errors) {
1559            let name = event.name.as_ref().unwrap();
1560            if !self.all_capability_ids.insert(name) {
1561                self.errors.push(Error::duplicate_field(
1562                    DeclType::EventStream,
1563                    "name",
1564                    name.as_str(),
1565                ));
1566            }
1567        }
1568    }
1569
1570    fn validate_source_collection(
1571        &mut self,
1572        collection: &fdecl::CollectionRef,
1573        decl_type: DeclType,
1574    ) -> bool {
1575        let num_errors = self.errors.len();
1576        if check_name(Some(&collection.name), decl_type, "source.collection.name", &mut self.errors)
1577            && !self.all_collections.contains(&collection.name as &str)
1578        {
1579            self.errors.push(Error::invalid_collection(
1580                decl_type,
1581                "source",
1582                &collection.name as &str,
1583            ));
1584        }
1585        num_errors == self.errors.len()
1586    }
1587
1588    fn validate_filtered_service_fields(
1589        &mut self,
1590        decl_type: DeclType,
1591        source_instance_filter: Option<&Vec<String>>,
1592        renamed_instances: Option<&Vec<fdecl::NameMapping>>,
1593    ) {
1594        if let Some(source_instance_filter) = source_instance_filter {
1595            if source_instance_filter.is_empty() {
1596                // if the  source_instance_filter is empty the offered service will have 0 instances,
1597                // which means the offer shouldn't have been created at all.
1598                self.errors.push(Error::invalid_field(decl_type, "source_instance_filter"));
1599            }
1600            for name in source_instance_filter {
1601                check_name(Some(name), decl_type, "source_instance_filter", &mut self.errors);
1602            }
1603        }
1604        if let Some(renamed_instances) = renamed_instances {
1605            // Multiple sources shouldn't map to the same target name
1606            let mut seen_target_names = HashSet::<String>::new();
1607            for mapping in renamed_instances {
1608                check_name(
1609                    Some(&mapping.source_name),
1610                    decl_type,
1611                    "renamed_instances.source_name",
1612                    &mut self.errors,
1613                );
1614                check_name(
1615                    Some(&mapping.target_name),
1616                    decl_type,
1617                    "renamed_instances.target_name",
1618                    &mut self.errors,
1619                );
1620                if !seen_target_names.insert(mapping.target_name.clone()) {
1621                    self.errors.push(Error::invalid_field(decl_type, "renamed_instances"));
1622                    break;
1623                }
1624            }
1625        }
1626    }
1627
1628    fn validate_source_capability(
1629        &mut self,
1630        capability: &fdecl::CapabilityRef,
1631        decl_type: DeclType,
1632        field: &str,
1633    ) -> bool {
1634        let num_errors = self.errors.len();
1635        if check_name(Some(&capability.name), decl_type, "source.capability.name", &mut self.errors)
1636            && !self.all_capability_ids.contains(capability.name.as_str())
1637        {
1638            self.errors.push(Error::invalid_capability(decl_type, field, &capability.name));
1639        }
1640        num_errors == self.errors.len()
1641    }
1642
1643    /// Return a key that can be used in `HashMap` to group aggregate declarations.
1644    ///
1645    /// Returns `None` if the input resembles an invalid declaration.
1646    fn make_group_key(
1647        target_name: Option<&'a String>,
1648        target: Option<&'a fdecl::Ref>,
1649    ) -> Option<(&'a str, RefKey<'a>)> {
1650        if target_name.is_none() {
1651            return None;
1652        }
1653        let target_name = target_name.unwrap().as_str();
1654        if target.is_none() {
1655            return None;
1656        }
1657        let target = match target.unwrap() {
1658            fdecl::Ref::Parent(_) => RefKey::Parent,
1659            fdecl::Ref::Self_(_) => RefKey::Self_,
1660            fdecl::Ref::Child(r) => RefKey::Child(r.name.as_str()),
1661            fdecl::Ref::Collection(r) => RefKey::Collection(r.name.as_str()),
1662            fdecl::Ref::Framework(_) => RefKey::Framework,
1663            fdecl::Ref::Capability(_) => RefKey::Capability,
1664            fdecl::Ref::Debug(_) => RefKey::Debug,
1665            fdecl::RefUnknown!() => {
1666                return None;
1667            }
1668        };
1669        Some((target_name, target))
1670    }
1671
1672    fn validate_aggregation_has_same_availability(
1673        &mut self,
1674        route_group: &Vec<impl HasAvailability>,
1675    ) {
1676        // Use `BtreeSet` for stable ordering of items in error message.
1677        let availability_of_sources: BTreeSet<_> =
1678            route_group.iter().map(|r| r.availability()).collect();
1679
1680        // All sources that feed into an aggregation operation should have the same availability.
1681        if availability_of_sources.len() > 1 {
1682            self.errors.push(Error::different_availability_in_aggregation(
1683                availability_of_sources.into_iter().collect(),
1684            ));
1685        }
1686    }
1687
1688    // Checks a group of expose decls to confirm that any duplicate exposes are
1689    // valid aggregate expose declarations.
1690    fn validate_expose_group(&mut self, exposes: &'a [fdecl::Expose]) {
1691        let mut expose_groups: HashMap<_, Vec<fdecl::ExposeService>> = HashMap::new();
1692        let service_exposes = exposes
1693            .into_iter()
1694            .filter_map(|o| if let fdecl::Expose::Service(s) = o { Some(s) } else { None });
1695        for expose in service_exposes {
1696            let key = Self::make_group_key(expose.target_name.as_ref(), expose.target.as_ref());
1697            if let Some(key) = key {
1698                expose_groups.entry(key).or_insert_with(|| vec![]).push(expose.clone());
1699            }
1700        }
1701        for expose_group in expose_groups.into_values() {
1702            if expose_group.len() == 1 {
1703                // If there are not multiple exposes for a (target_name, target) pair then there are
1704                // no aggregation conditions to check.
1705                continue;
1706            }
1707
1708            self.validate_aggregation_has_same_availability(&expose_group);
1709        }
1710    }
1711
1712    fn validate_expose_decl(
1713        &mut self,
1714        expose: &'a fdecl::Expose,
1715        expose_to_parent_ids: &mut HashMap<&'a str, AllowableIds>,
1716        expose_to_framework_ids: &mut HashMap<&'a str, AllowableIds>,
1717    ) {
1718        match expose {
1719            fdecl::Expose::Service(e) => {
1720                let decl = DeclType::ExposeService;
1721                self.validate_expose_fields(
1722                    decl,
1723                    AllowableIds::Many,
1724                    CollectionSource::Allow,
1725                    Self::service_checker,
1726                    e.source.as_ref(),
1727                    e.source_name.as_ref(),
1728                    e.source_dictionary.as_ref(),
1729                    e.target.as_ref(),
1730                    e.target_name.as_ref(),
1731                    e.availability.as_ref(),
1732                    expose_to_parent_ids,
1733                    expose_to_framework_ids,
1734                );
1735            }
1736            fdecl::Expose::Protocol(e) => {
1737                let decl = DeclType::ExposeProtocol;
1738                self.validate_expose_fields(
1739                    decl,
1740                    AllowableIds::One,
1741                    CollectionSource::Deny,
1742                    Self::protocol_checker,
1743                    e.source.as_ref(),
1744                    e.source_name.as_ref(),
1745                    e.source_dictionary.as_ref(),
1746                    e.target.as_ref(),
1747                    e.target_name.as_ref(),
1748                    e.availability.as_ref(),
1749                    expose_to_parent_ids,
1750                    expose_to_framework_ids,
1751                );
1752            }
1753            fdecl::Expose::Directory(e) => {
1754                let decl = DeclType::ExposeDirectory;
1755                self.validate_expose_fields(
1756                    decl,
1757                    AllowableIds::One,
1758                    CollectionSource::Deny,
1759                    Self::directory_checker,
1760                    e.source.as_ref(),
1761                    e.source_name.as_ref(),
1762                    e.source_dictionary.as_ref(),
1763                    e.target.as_ref(),
1764                    e.target_name.as_ref(),
1765                    e.availability.as_ref(),
1766                    expose_to_parent_ids,
1767                    expose_to_framework_ids,
1768                );
1769
1770                // Subdir makes sense when routing, but when exposing to framework the subdirectory
1771                // can be exposed directly.
1772                match e.target.as_ref() {
1773                    Some(fdecl::Ref::Framework(_)) => {
1774                        if e.subdir.is_some() {
1775                            self.errors.push(Error::invalid_field(decl, "subdir"));
1776                        }
1777                    }
1778                    _ => {}
1779                }
1780
1781                if let Some(subdir) = e.subdir.as_ref() {
1782                    check_relative_path(Some(subdir), decl, "subdir", &mut self.errors);
1783                }
1784            }
1785            fdecl::Expose::Runner(e) => {
1786                let decl = DeclType::ExposeRunner;
1787                self.validate_expose_fields(
1788                    decl,
1789                    AllowableIds::One,
1790                    CollectionSource::Deny,
1791                    Self::runner_checker,
1792                    e.source.as_ref(),
1793                    e.source_name.as_ref(),
1794                    e.source_dictionary.as_ref(),
1795                    e.target.as_ref(),
1796                    e.target_name.as_ref(),
1797                    Some(&fdecl::Availability::Required),
1798                    expose_to_parent_ids,
1799                    expose_to_framework_ids,
1800                );
1801            }
1802            fdecl::Expose::Resolver(e) => {
1803                let decl = DeclType::ExposeResolver;
1804                self.validate_expose_fields(
1805                    decl,
1806                    AllowableIds::One,
1807                    CollectionSource::Deny,
1808                    Self::resolver_checker,
1809                    e.source.as_ref(),
1810                    e.source_name.as_ref(),
1811                    e.source_dictionary.as_ref(),
1812                    e.target.as_ref(),
1813                    e.target_name.as_ref(),
1814                    Some(&fdecl::Availability::Required),
1815                    expose_to_parent_ids,
1816                    expose_to_framework_ids,
1817                );
1818            }
1819            fdecl::Expose::Dictionary(e) => {
1820                let decl = DeclType::ExposeDictionary;
1821                self.validate_expose_fields(
1822                    decl,
1823                    AllowableIds::One,
1824                    CollectionSource::Deny,
1825                    Self::dictionary_checker,
1826                    e.source.as_ref(),
1827                    e.source_name.as_ref(),
1828                    e.source_dictionary.as_ref(),
1829                    e.target.as_ref(),
1830                    e.target_name.as_ref(),
1831                    e.availability.as_ref(),
1832                    expose_to_parent_ids,
1833                    expose_to_framework_ids,
1834                );
1835            }
1836            #[cfg(fuchsia_api_level_at_least = "HEAD")]
1837            fdecl::Expose::Config(e) => {
1838                let decl = DeclType::ExposeConfig;
1839                self.validate_expose_fields(
1840                    decl,
1841                    AllowableIds::One,
1842                    CollectionSource::Deny,
1843                    Self::config_checker,
1844                    e.source.as_ref(),
1845                    e.source_name.as_ref(),
1846                    None,
1847                    e.target.as_ref(),
1848                    e.target_name.as_ref(),
1849                    e.availability.as_ref(),
1850                    expose_to_parent_ids,
1851                    expose_to_framework_ids,
1852                );
1853            }
1854            _ => {
1855                self.errors.push(Error::invalid_field(DeclType::Component, "expose"));
1856            }
1857        }
1858    }
1859
1860    fn validate_expose_fields(
1861        &mut self,
1862        decl: DeclType,
1863        allowable_ids: AllowableIds,
1864        collection_source: CollectionSource,
1865        // This takes a callback that returns a [Container], instead of the &[Container] directly,
1866        // to avoid a borrow checker error that would occur from a simultaneous borrow on
1867        // &mut self.
1868        capability_checker: impl Fn(&Self) -> &dyn Container,
1869        source: Option<&fdecl::Ref>,
1870        source_name: Option<&String>,
1871        source_dictionary: Option<&String>,
1872        target: Option<&fdecl::Ref>,
1873        target_name: Option<&'a String>,
1874        availability: Option<&fdecl::Availability>,
1875        expose_to_parent_ids: &mut HashMap<&'a str, AllowableIds>,
1876        expose_to_framework_ids: &mut HashMap<&'a str, AllowableIds>,
1877    ) {
1878        self.validate_expose_source(decl, collection_source, source, source_dictionary);
1879        check_route_availability(decl, availability, source, source_name, &mut self.errors);
1880        match target {
1881            Some(r) => match r {
1882                fdecl::Ref::Parent(_) => {}
1883                fdecl::Ref::Framework(_) => {}
1884                _ => {
1885                    self.errors.push(Error::invalid_field(decl, "target"));
1886                }
1887            },
1888            None => {
1889                self.errors.push(Error::missing_field(decl, "target"));
1890            }
1891        }
1892        check_name(source_name, decl, "source_name", &mut self.errors);
1893        if source_dictionary.is_some() {
1894            check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
1895        }
1896        if check_name(target_name, decl, "target_name", &mut self.errors) {
1897            let maybe_ids_set = match target {
1898                Some(fdecl::Ref::Parent(_)) => Some(expose_to_parent_ids),
1899                Some(fdecl::Ref::Framework(_)) => Some(expose_to_framework_ids),
1900                _ => None,
1901            };
1902            if let Some(ids_set) = maybe_ids_set {
1903                let target_name = target_name.unwrap();
1904                if let Some(prev_state) = ids_set.insert(target_name, allowable_ids) {
1905                    if prev_state == AllowableIds::One || prev_state != allowable_ids {
1906                        self.errors.push(Error::duplicate_field(decl, "target_name", target_name));
1907                    }
1908                }
1909            }
1910        }
1911
1912        self.validate_route_from_self(
1913            decl,
1914            source,
1915            source_name,
1916            source_dictionary,
1917            capability_checker,
1918        );
1919    }
1920
1921    fn validate_expose_source(
1922        &mut self,
1923        decl: DeclType,
1924        collection_source: CollectionSource,
1925        source: Option<&fdecl::Ref>,
1926        source_dictionary: Option<&String>,
1927    ) {
1928        match (source, source_dictionary) {
1929            // These sources support source_dictionary.
1930            (Some(fdecl::Ref::Self_(_)), _) => {}
1931            (Some(fdecl::Ref::Child(child)), _) => {
1932                let _ = self.validate_child_ref(decl, "source", &child, OfferType::Static);
1933            }
1934            // These sources don't.
1935            (Some(fdecl::Ref::VoidType(_)), None) => {}
1936            (Some(fdecl::Ref::Framework(_)), None) => {}
1937            (Some(fdecl::Ref::Capability(c)), None) => {
1938                self.validate_source_capability(c, decl, "source");
1939            }
1940            (Some(fdecl::Ref::Collection(c)), None)
1941                if collection_source == CollectionSource::Allow =>
1942            {
1943                self.validate_source_collection(c, decl);
1944            }
1945            // `source` is required.
1946            (None, _) => {
1947                self.errors.push(Error::missing_field(decl, "source"));
1948            }
1949            // Any combination that was not recognized above must be invalid.
1950            (_, _) => {
1951                self.errors.push(Error::invalid_field(decl, "source"));
1952            }
1953        }
1954    }
1955
1956    // Checks a group of offer decls to confirm that any duplicate offers are
1957    // valid aggregate offer declarations.
1958    fn validate_offer_group(&mut self, offers: &'a [fdecl::Offer], offer_type: OfferType) {
1959        let mut offer_groups: HashMap<_, Vec<fdecl::OfferService>> = HashMap::new();
1960        let service_offers = offers
1961            .into_iter()
1962            .filter_map(|o| if let fdecl::Offer::Service(s) = o { Some(s) } else { None });
1963        for offer in service_offers {
1964            let key = Self::make_group_key(offer.target_name.as_ref(), offer.target.as_ref());
1965            if let Some(key) = key {
1966                offer_groups.entry(key).or_insert_with(|| vec![]).push(offer.clone());
1967            }
1968        }
1969        for offer_group in offer_groups.into_values() {
1970            if offer_group.len() == 1 {
1971                // If there are not multiple offers for a (target_name, target) pair then there are
1972                // no aggregation conditions to check.
1973                continue;
1974            }
1975
1976            self.validate_aggregation_has_same_availability(&offer_group);
1977
1978            let mut source_instance_filter_entries: HashSet<String> = HashSet::new();
1979            let mut service_source_names: HashSet<String> = HashSet::new();
1980            for o in offer_group {
1981                // Currently only service capabilities can be aggregated
1982                match (o.source_instance_filter, offer_type) {
1983                    (Some(source_instance_filter), _) => {
1984                        for instance_name in source_instance_filter {
1985                            if !source_instance_filter_entries.insert(instance_name.clone()) {
1986                                // If the source instance in the filter has been seen before this
1987                                // means there is a conflicting aggregate service offer.
1988                                self.errors.push(Error::invalid_aggregate_offer(format!(
1989                                    "Conflicting source_instance_filter in aggregate service \
1990                                    offer, instance_name '{}' seen in filter lists multiple times",
1991                                    instance_name,
1992                                )));
1993                            }
1994                        }
1995                    }
1996                    (None, OfferType::Static) => {}
1997                    (None, OfferType::Dynamic) => {
1998                        // Dynamic offers must include a filter.
1999                        self.errors.push(Error::invalid_aggregate_offer(
2000                            "source_instance_filter must be set for dynamic aggregate service \
2001                            offers",
2002                        ));
2003                    }
2004                }
2005                service_source_names.insert(
2006                    o.source_name
2007                        .expect("Offer Service declarations must always contain source_name"),
2008                );
2009            }
2010
2011            if service_source_names.len() > 1 {
2012                self.errors.push(Error::invalid_aggregate_offer(format!(
2013                    "All aggregate service offers must have the same source_name, saw {}. Use \
2014                    renamed_instances to rename instance names to avoid conflict.",
2015                    service_source_names.into_iter().sorted().collect::<Vec<String>>().join(", ")
2016                )));
2017            }
2018        }
2019    }
2020
2021    fn validate_offer_decl(&mut self, offer: &'a fdecl::Offer, offer_type: OfferType) {
2022        match offer {
2023            fdecl::Offer::Service(o) => {
2024                let decl = DeclType::OfferService;
2025                self.validate_offer_fields(
2026                    decl,
2027                    AllowableIds::Many,
2028                    CollectionSource::Allow,
2029                    Self::service_checker,
2030                    o.source.as_ref(),
2031                    o.source_name.as_ref(),
2032                    o.source_dictionary.as_ref(),
2033                    o.target.as_ref(),
2034                    o.target_name.as_ref(),
2035                    #[cfg(fuchsia_api_level_at_least = "HEAD")]
2036                    Some(o.dependency_type.as_ref().unwrap_or(&fdecl::DependencyType::Strong)),
2037                    #[cfg(fuchsia_api_level_less_than = "HEAD")]
2038                    Some(&fdecl::DependencyType::Strong),
2039                    o.availability.as_ref(),
2040                    offer_type,
2041                );
2042                self.validate_filtered_service_fields(
2043                    decl,
2044                    o.source_instance_filter.as_ref(),
2045                    o.renamed_instances.as_ref(),
2046                );
2047            }
2048            fdecl::Offer::Protocol(o) => {
2049                let decl = DeclType::OfferProtocol;
2050                self.validate_offer_fields(
2051                    decl,
2052                    AllowableIds::One,
2053                    CollectionSource::Deny,
2054                    Self::protocol_checker,
2055                    o.source.as_ref(),
2056                    o.source_name.as_ref(),
2057                    o.source_dictionary.as_ref(),
2058                    o.target.as_ref(),
2059                    o.target_name.as_ref(),
2060                    o.dependency_type.as_ref(),
2061                    o.availability.as_ref(),
2062                    offer_type,
2063                );
2064            }
2065            fdecl::Offer::Directory(o) => {
2066                let decl = DeclType::OfferDirectory;
2067                self.validate_offer_fields(
2068                    decl,
2069                    AllowableIds::One,
2070                    CollectionSource::Deny,
2071                    Self::directory_checker,
2072                    o.source.as_ref(),
2073                    o.source_name.as_ref(),
2074                    o.source_dictionary.as_ref(),
2075                    o.target.as_ref(),
2076                    o.target_name.as_ref(),
2077                    o.dependency_type.as_ref(),
2078                    o.availability.as_ref(),
2079                    offer_type,
2080                );
2081                if let Some(subdir) = o.subdir.as_ref() {
2082                    check_relative_path(
2083                        Some(subdir),
2084                        DeclType::OfferDirectory,
2085                        "subdir",
2086                        &mut self.errors,
2087                    );
2088                }
2089            }
2090            fdecl::Offer::Storage(o) => {
2091                let decl = DeclType::OfferStorage;
2092                self.validate_storage_offer_fields(
2093                    decl,
2094                    Self::storage_checker,
2095                    o.source.as_ref(),
2096                    o.source_name.as_ref(),
2097                    o.target.as_ref(),
2098                    o.target_name.as_ref(),
2099                    o.availability.as_ref(),
2100                    offer_type,
2101                );
2102            }
2103            fdecl::Offer::Runner(o) => {
2104                let decl = DeclType::OfferRunner;
2105                self.validate_offer_fields(
2106                    decl,
2107                    AllowableIds::One,
2108                    CollectionSource::Deny,
2109                    Self::runner_checker,
2110                    o.source.as_ref(),
2111                    o.source_name.as_ref(),
2112                    o.source_dictionary.as_ref(),
2113                    o.target.as_ref(),
2114                    o.target_name.as_ref(),
2115                    Some(&fdecl::DependencyType::Strong),
2116                    Some(&fdecl::Availability::Required),
2117                    offer_type,
2118                );
2119            }
2120            fdecl::Offer::Resolver(o) => {
2121                let decl = DeclType::OfferResolver;
2122                self.validate_offer_fields(
2123                    decl,
2124                    AllowableIds::One,
2125                    CollectionSource::Deny,
2126                    Self::resolver_checker,
2127                    o.source.as_ref(),
2128                    o.source_name.as_ref(),
2129                    o.source_dictionary.as_ref(),
2130                    o.target.as_ref(),
2131                    o.target_name.as_ref(),
2132                    Some(&fdecl::DependencyType::Strong),
2133                    Some(&fdecl::Availability::Required),
2134                    offer_type,
2135                );
2136            }
2137            fdecl::Offer::EventStream(e) => {
2138                self.validate_event_stream_offer_fields(e, offer_type);
2139            }
2140            fdecl::Offer::Dictionary(o) => {
2141                let decl = DeclType::OfferDictionary;
2142                self.validate_offer_fields(
2143                    decl,
2144                    AllowableIds::One,
2145                    CollectionSource::Deny,
2146                    Self::dictionary_checker,
2147                    o.source.as_ref(),
2148                    o.source_name.as_ref(),
2149                    o.source_dictionary.as_ref(),
2150                    o.target.as_ref(),
2151                    o.target_name.as_ref(),
2152                    o.dependency_type.as_ref(),
2153                    o.availability.as_ref(),
2154                    offer_type,
2155                );
2156            }
2157            #[cfg(fuchsia_api_level_at_least = "HEAD")]
2158            fdecl::Offer::Config(o) => {
2159                let decl = DeclType::OfferConfig;
2160                self.validate_offer_fields(
2161                    decl,
2162                    AllowableIds::One,
2163                    CollectionSource::Deny,
2164                    Self::config_checker,
2165                    o.source.as_ref(),
2166                    o.source_name.as_ref(),
2167                    None,
2168                    o.target.as_ref(),
2169                    o.target_name.as_ref(),
2170                    Some(&fdecl::DependencyType::Strong),
2171                    o.availability.as_ref(),
2172                    offer_type,
2173                );
2174            }
2175            fdecl::OfferUnknown!() => {
2176                self.errors.push(Error::invalid_field(DeclType::Component, "offer"));
2177            }
2178        }
2179    }
2180
2181    fn validate_offer_fields(
2182        &mut self,
2183        decl: DeclType,
2184        allowable_names: AllowableIds,
2185        collection_source: CollectionSource,
2186        capability_checker: impl Fn(&Self) -> &dyn Container,
2187        source: Option<&'a fdecl::Ref>,
2188        source_name: Option<&'a String>,
2189        source_dictionary: Option<&'a String>,
2190        target: Option<&'a fdecl::Ref>,
2191        target_name: Option<&'a String>,
2192        dependency_type: Option<&'a fdecl::DependencyType>,
2193        availability: Option<&'a fdecl::Availability>,
2194        offer_type: OfferType,
2195    ) {
2196        self.validate_offer_source(decl, collection_source, source, source_dictionary, offer_type);
2197        check_route_availability(decl, availability, source, source_name, &mut self.errors);
2198        check_offer_name(source_name, decl, "source_name", offer_type, &mut self.errors);
2199        if source_dictionary.is_some() {
2200            check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
2201        }
2202        self.validate_offer_target(decl, allowable_names, target, target_name, offer_type);
2203        check_offer_name(target_name, decl, "target_name", offer_type, &mut self.errors);
2204
2205        if dependency_type.is_none() {
2206            self.errors.push(Error::missing_field(decl, "dependency_type"));
2207        }
2208
2209        self.validate_route_from_self(
2210            decl,
2211            source,
2212            source_name,
2213            source_dictionary,
2214            capability_checker,
2215        );
2216    }
2217
2218    fn validate_offer_source(
2219        &mut self,
2220        decl: DeclType,
2221        collection_source: CollectionSource,
2222        source: Option<&'a fdecl::Ref>,
2223        source_dictionary: Option<&'a String>,
2224        offer_type: OfferType,
2225    ) {
2226        match (source, source_dictionary) {
2227            // These sources support source_dictionary.
2228            (Some(fdecl::Ref::Parent(_)), _) => {}
2229            (Some(fdecl::Ref::Self_(_)), _) => {}
2230            (Some(fdecl::Ref::Child(child)), _) => {
2231                self.validate_child_ref(decl, "source", &child, offer_type);
2232            }
2233            // These sources don't.
2234            (Some(fdecl::Ref::VoidType(_)), None) => {}
2235            (Some(fdecl::Ref::Framework(_)), None) => {}
2236            (Some(fdecl::Ref::Capability(c)), None) => {
2237                self.validate_source_capability(c, decl, "source");
2238            }
2239            (Some(fdecl::Ref::Collection(c)), None)
2240                if collection_source == CollectionSource::Allow =>
2241            {
2242                self.validate_source_collection(c, decl);
2243            }
2244            // `source` is required.
2245            (None, _) => self.errors.push(Error::missing_field(decl, "source")),
2246            // Any combination that was not recognized above must be invalid.
2247            (_, _) => self.errors.push(Error::invalid_field(decl, "source")),
2248        }
2249    }
2250
2251    fn validate_storage_offer_fields(
2252        &mut self,
2253        decl: DeclType,
2254        // This takes a callback that returns a [Container], instead of the &[Container] directly,
2255        // to avoid a borrow checker error that would occur from a simultaneous borrow on
2256        // &mut self.
2257        capability_checker: impl Fn(&Self) -> &dyn Container,
2258        source: Option<&'a fdecl::Ref>,
2259        source_name: Option<&'a String>,
2260        target: Option<&'a fdecl::Ref>,
2261        target_name: Option<&'a String>,
2262        availability: Option<&fdecl::Availability>,
2263        offer_type: OfferType,
2264    ) {
2265        match source {
2266            Some(fdecl::Ref::Parent(_) | fdecl::Ref::VoidType(_) | fdecl::Ref::Self_(_)) => {}
2267            Some(_) => {
2268                self.errors.push(Error::invalid_field(decl, "source"));
2269            }
2270            None => {
2271                self.errors.push(Error::missing_field(decl, "source"));
2272            }
2273        }
2274        check_route_availability(decl, availability, source, source_name, &mut self.errors);
2275        check_offer_name(source_name, decl, "source_name", offer_type, &mut self.errors);
2276        self.validate_offer_target(decl, AllowableIds::One, target, target_name, offer_type);
2277        check_offer_name(target_name, decl, "target_name", offer_type, &mut self.errors);
2278
2279        if let (Some(fdecl::Ref::Self_(_)), Some(name)) = (source, source_name) {
2280            if !(capability_checker)(self).contains(name) {
2281                self.errors.push(Error::invalid_capability(decl, "source", name));
2282            }
2283        }
2284    }
2285
2286    fn validate_event_stream_offer_fields(
2287        &mut self,
2288        event_stream: &'a fdecl::OfferEventStream,
2289        offer_type: OfferType,
2290    ) {
2291        let decl = DeclType::OfferEventStream;
2292        check_name(event_stream.source_name.as_ref(), decl, "source_name", &mut self.errors);
2293        if event_stream.target == Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})) {
2294            // Expose to framework from framework is never valid.
2295            self.errors.push(Error::invalid_field(DeclType::OfferEventStream, "target"));
2296        }
2297        if let Some(scope) = &event_stream.scope {
2298            if scope.is_empty() {
2299                self.errors.push(Error::invalid_field(decl, "scope"));
2300            }
2301            for value in scope {
2302                match value {
2303                    fdecl::Ref::Child(child) => {
2304                        self.validate_child_ref(
2305                            DeclType::OfferEventStream,
2306                            "scope",
2307                            &child,
2308                            offer_type,
2309                        );
2310                    }
2311                    fdecl::Ref::Collection(collection) => {
2312                        self.validate_collection_ref(
2313                            DeclType::OfferEventStream,
2314                            "scope",
2315                            &collection,
2316                        );
2317                    }
2318                    _ => {
2319                        self.errors.push(Error::invalid_field(DeclType::OfferEventStream, "scope"));
2320                    }
2321                }
2322            }
2323        }
2324        // Only parent, framework, child, and void are valid.
2325        match event_stream.source {
2326            Some(
2327                fdecl::Ref::Parent(_)
2328                | fdecl::Ref::Framework(_)
2329                | fdecl::Ref::Child(_)
2330                | fdecl::Ref::VoidType(_),
2331            ) => {}
2332            Some(_) => {
2333                self.errors.push(Error::invalid_field(decl, "source"));
2334            }
2335            None => {
2336                self.errors.push(Error::missing_field(decl, "source"));
2337            }
2338        };
2339
2340        check_route_availability(
2341            decl,
2342            event_stream.availability.as_ref(),
2343            event_stream.source.as_ref(),
2344            event_stream.source_name.as_ref(),
2345            &mut self.errors,
2346        );
2347
2348        self.validate_offer_target(
2349            decl,
2350            AllowableIds::One,
2351            event_stream.target.as_ref(),
2352            event_stream.target_name.as_ref(),
2353            offer_type,
2354        );
2355        check_name(event_stream.target_name.as_ref(), decl, "target_name", &mut self.errors);
2356    }
2357
2358    /// Check a `ChildRef` contains a valid child that exists.
2359    fn validate_child_ref(
2360        &mut self,
2361        decl: DeclType,
2362        field_name: &str,
2363        child: &fdecl::ChildRef,
2364        offer_type: OfferType,
2365    ) -> bool {
2366        if offer_type == OfferType::Dynamic && child.collection.is_some() {
2367            return self.validate_dynamic_child_ref(decl, field_name, child);
2368        }
2369        // Ensure the name is valid, and the reference refers to a static child.
2370        //
2371        // We attempt to list all errors if possible.
2372        let mut valid = true;
2373        if !check_name(
2374            Some(&child.name),
2375            decl,
2376            &format!("{}.child.name", field_name),
2377            &mut self.errors,
2378        ) {
2379            valid = false;
2380        }
2381        if child.collection.is_some() {
2382            self.errors
2383                .push(Error::extraneous_field(decl, format!("{}.child.collection", field_name)));
2384            valid = false;
2385        }
2386        if !valid {
2387            return false;
2388        }
2389
2390        // Ensure the child exists.
2391        let name: &str = &child.name;
2392        if !self.all_children.contains_key(name) {
2393            self.errors.push(Error::invalid_child(decl, field_name, name));
2394            return false;
2395        }
2396
2397        true
2398    }
2399
2400    /// Check a `ChildRef` contains a valid dynamic child.
2401    ///
2402    /// The manifest we're validating doesn't contain dynamic children so we can't check if the dynamic
2403    /// child actually exists, but we can confirm things like the name is valid.
2404    fn validate_dynamic_child_ref(
2405        &mut self,
2406        decl: DeclType,
2407        field_name: &str,
2408        child: &fdecl::ChildRef,
2409    ) -> bool {
2410        // Ensure the name is valid.
2411        //
2412        // We attempt to list all errors if possible.
2413        let mut valid = true;
2414        if !check_dynamic_name(
2415            Some(&child.name),
2416            decl,
2417            &format!("{}.child.name", field_name),
2418            &mut self.errors,
2419        ) {
2420            valid = false;
2421        }
2422        if !check_name(
2423            child.collection.as_ref(),
2424            decl,
2425            &format!("{}.child.collection", field_name),
2426            &mut self.errors,
2427        ) {
2428            valid = false;
2429        }
2430        valid
2431    }
2432
2433    /// Check a `CollectionRef` is valid and refers to an existing collection.
2434    fn validate_collection_ref(
2435        &mut self,
2436        decl: DeclType,
2437        field_name: &str,
2438        collection: &fdecl::CollectionRef,
2439    ) -> bool {
2440        // Ensure the name is valid.
2441        if !check_name(
2442            Some(&collection.name),
2443            decl,
2444            &format!("{}.collection.name", field_name),
2445            &mut self.errors,
2446        ) {
2447            return false;
2448        }
2449
2450        // Ensure the collection exists.
2451        if !self.all_collections.contains(&collection.name as &str) {
2452            self.errors.push(Error::invalid_collection(decl, field_name, &collection.name as &str));
2453            return false;
2454        }
2455
2456        true
2457    }
2458
2459    fn validate_offer_target(
2460        &mut self,
2461        decl: DeclType,
2462        allowable_names: AllowableIds,
2463        target: Option<&'a fdecl::Ref>,
2464        target_name: Option<&'a String>,
2465        offer_type: OfferType,
2466    ) {
2467        match target {
2468            Some(fdecl::Ref::Child(c)) => {
2469                self.validate_target_child(decl, allowable_names, c, target_name, offer_type);
2470            }
2471            Some(fdecl::Ref::Collection(c)) => {
2472                self.validate_target_collection(decl, allowable_names, c, target_name);
2473            }
2474            Some(fdecl::Ref::Capability(c)) => {
2475                // Only offers to dictionary capabilities are valid.
2476                if let Some(d) = self.all_dictionaries.get(&c.name.as_str()) {
2477                    if d.source_path.is_some() {
2478                        // If `source_path` is present that means this is an offer into a
2479                        // dynamic dictionary, which is not allowed.
2480                        self.errors.push(Error::invalid_field(decl, "target"));
2481                    }
2482                } else {
2483                    self.errors.push(Error::invalid_field(decl, "target"));
2484                }
2485            }
2486            Some(_) => {
2487                self.errors.push(Error::invalid_field(decl, "target"));
2488            }
2489            None => {
2490                self.errors.push(Error::missing_field(decl, "target"));
2491            }
2492        }
2493    }
2494
2495    fn validate_target_child(
2496        &mut self,
2497        decl: DeclType,
2498        allowable_names: AllowableIds,
2499        child: &'a fdecl::ChildRef,
2500        target_name: Option<&'a String>,
2501        offer_type: OfferType,
2502    ) {
2503        if !self.validate_child_ref(decl, "target", child, offer_type) {
2504            return;
2505        }
2506        if let Some(target_name) = target_name {
2507            let names_for_target = self
2508                .target_ids
2509                .entry(TargetId::Component(
2510                    &child.name,
2511                    child.collection.as_ref().map(|s| s.as_str()),
2512                ))
2513                .or_insert(HashMap::new());
2514            if let Some(prev_state) = names_for_target.insert(target_name, allowable_names) {
2515                if prev_state == AllowableIds::One || prev_state != allowable_names {
2516                    self.errors.push(Error::duplicate_field(
2517                        decl,
2518                        "target_name",
2519                        target_name as &str,
2520                    ));
2521                }
2522            }
2523            if let Some(collection) = child.collection.as_ref() {
2524                if let Some(names_for_target) =
2525                    self.target_ids.get(&TargetId::Collection(&collection))
2526                {
2527                    if names_for_target.contains_key(&target_name.as_str()) {
2528                        // This dynamic offer conflicts with a static offer to the same collection.
2529                        self.errors.push(Error::duplicate_field(
2530                            decl,
2531                            "target_name",
2532                            target_name as &str,
2533                        ));
2534                    }
2535                }
2536            }
2537        }
2538    }
2539
2540    fn validate_target_collection(
2541        &mut self,
2542        decl: DeclType,
2543        allowable_names: AllowableIds,
2544        collection: &'a fdecl::CollectionRef,
2545        target_name: Option<&'a String>,
2546    ) {
2547        if !self.validate_collection_ref(decl, "target", &collection) {
2548            return;
2549        }
2550        if let Some(target_name) = target_name {
2551            let names_for_target = self
2552                .target_ids
2553                .entry(TargetId::Collection(&collection.name))
2554                .or_insert(HashMap::new());
2555            if let Some(prev_state) = names_for_target.insert(target_name, allowable_names) {
2556                if prev_state == AllowableIds::One || prev_state != allowable_names {
2557                    self.errors.push(Error::duplicate_field(
2558                        decl,
2559                        "target_name",
2560                        target_name as &str,
2561                    ));
2562                }
2563            }
2564        }
2565    }
2566
2567    fn validate_route_from_self(
2568        &mut self,
2569        decl: DeclType,
2570        source: Option<&fdecl::Ref>,
2571        source_name: Option<&String>,
2572        source_dictionary: Option<&String>,
2573        capability_checker: impl Fn(&Self) -> &dyn Container,
2574    ) {
2575        let (Some(fdecl::Ref::Self_(_)), Some(name)) = (source, source_name) else {
2576            return;
2577        };
2578        match source_dictionary {
2579            Some(source_dictionary) => {
2580                if let Ok(path) = cm_types::RelativePath::new(source_dictionary) {
2581                    if let Some(first_segment) = path.iter_segments().next().map(|s| s.as_str()) {
2582                        if !self.all_dictionaries.contains_key(first_segment) {
2583                            self.errors.push(Error::invalid_capability(
2584                                decl,
2585                                "source",
2586                                first_segment,
2587                            ));
2588                        }
2589                    }
2590                }
2591            }
2592            None => {
2593                if !(capability_checker)(self).contains(name) {
2594                    self.errors.push(Error::invalid_capability(decl, "source", name));
2595                }
2596            }
2597        }
2598    }
2599
2600    // The following functions can be used to convert a type-specific collection of capabilities
2601    // into [Container].
2602    fn service_checker(&self) -> &dyn Container {
2603        &self.all_services
2604    }
2605    fn protocol_checker(&self) -> &dyn Container {
2606        &self.all_protocols
2607    }
2608    fn directory_checker(&self) -> &dyn Container {
2609        &self.all_directories
2610    }
2611    fn runner_checker(&self) -> &dyn Container {
2612        &self.all_runners
2613    }
2614    fn resolver_checker(&self) -> &dyn Container {
2615        &self.all_resolvers
2616    }
2617
2618    fn dictionary_checker(&self) -> &dyn Container {
2619        &self.all_dictionaries
2620    }
2621
2622    #[cfg(fuchsia_api_level_at_least = "HEAD")]
2623    fn config_checker(&self) -> &dyn Container {
2624        &self.all_configs
2625    }
2626    fn storage_checker(&self) -> &dyn Container {
2627        &self.all_storages
2628    }
2629    fn event_stream_checker(&self) -> &dyn Container {
2630        // Components can't define their own event streams. If someone tries to route an event
2631        // stream from Self it should generate some other error. So just return `true` to bypass
2632        // the logic.
2633        struct AlwaysTrueContainer {}
2634        impl Container for AlwaysTrueContainer {
2635            fn contains(&self, _key: &str) -> bool {
2636                true
2637            }
2638        }
2639        static CONTAINER: AlwaysTrueContainer = AlwaysTrueContainer {};
2640        &CONTAINER
2641    }
2642}
2643
2644#[cfg(test)]
2645mod tests {
2646    use super::*;
2647    use cm_types::MAX_LONG_NAME_LENGTH;
2648    use test_case::test_case;
2649    use {
2650        fidl_fuchsia_component_decl as fdecl, fidl_fuchsia_data as fdata, fidl_fuchsia_io as fio,
2651    };
2652
2653    macro_rules! test_validate {
2654        (
2655            $(
2656                $test_name:ident => {
2657                    input = $input:expr,
2658                    result = $result:expr,
2659                },
2660            )+
2661        ) => {
2662            $(
2663                #[test]
2664                fn $test_name() {
2665                    validate_test($input, $result);
2666                }
2667            )+
2668        }
2669    }
2670
2671    macro_rules! test_validate_any_result {
2672        (
2673            $(
2674                $test_name:ident => {
2675                    input = $input:expr,
2676                    results = $results:expr,
2677                },
2678            )+
2679        ) => {
2680            $(
2681                #[test]
2682                fn $test_name() {
2683                    validate_test_any_result($input, $results);
2684                }
2685            )+
2686        }
2687    }
2688
2689    macro_rules! test_validate_values_data {
2690        (
2691            $(
2692                $test_name:ident => {
2693                    input = $input:expr,
2694                    result = $result:expr,
2695                },
2696            )+
2697        ) => {
2698            $(
2699                #[test]
2700                fn $test_name() {
2701                    validate_values_data_test($input, $result);
2702                }
2703            )+
2704        }
2705    }
2706
2707    macro_rules! test_validate_capabilities {
2708        (
2709            $(
2710                $test_name:ident => {
2711                    input = $input:expr,
2712                    as_builtin = $as_builtin:expr,
2713                    result = $result:expr,
2714                },
2715            )+
2716        ) => {
2717            $(
2718                #[test]
2719                fn $test_name() {
2720                    validate_capabilities_test($input, $as_builtin, $result);
2721                }
2722            )+
2723        }
2724    }
2725
2726    macro_rules! test_dependency {
2727        (
2728            $(
2729                ($test_name:ident) => {
2730                    ty = $ty:expr,
2731                    offer_decl = $offer_decl:expr,
2732                },
2733            )+
2734        ) => {
2735            $(
2736                #[test]
2737                fn $test_name() {
2738                    let mut decl = new_component_decl();
2739                    let dependencies = vec![
2740                        ("a", "b"),
2741                        ("b", "a"),
2742                    ];
2743                    let offers = dependencies.into_iter().map(|(from,to)| {
2744                        let mut offer_decl = $offer_decl;
2745                        offer_decl.source = Some(fdecl::Ref::Child(
2746                           fdecl::ChildRef { name: from.to_string(), collection: None },
2747                        ));
2748                        offer_decl.target = Some(fdecl::Ref::Child(
2749                           fdecl::ChildRef { name: to.to_string(), collection: None },
2750                        ));
2751                        $ty(offer_decl)
2752                    }).collect();
2753                    let children = ["a", "b"].iter().map(|name| {
2754                        fdecl::Child {
2755                            name: Some(name.to_string()),
2756                            url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
2757                            startup: Some(fdecl::StartupMode::Lazy),
2758                            on_terminate: None,
2759                            environment: None,
2760                            ..Default::default()
2761                        }
2762                    }).collect();
2763                    decl.offers = Some(offers);
2764                    decl.children = Some(children);
2765                    let result = Err(ErrorList::new(vec![
2766                        Error::dependency_cycle("{{child a -> child b -> child a}}")
2767                    ]));
2768                    validate_test(decl, result);
2769                }
2770            )+
2771        }
2772    }
2773
2774    macro_rules! test_weak_dependency {
2775        (
2776            $(
2777                ($test_name:ident) => {
2778                    ty = $ty:expr,
2779                    offer_decl = $offer_decl:expr,
2780                },
2781            )+
2782        ) => {
2783            $(
2784                #[test_case(fdecl::DependencyType::Weak)]
2785                fn $test_name(weak_dep: fdecl::DependencyType) {
2786                    let mut decl = new_component_decl();
2787                    let offers = vec![
2788                        {
2789                            let mut offer_decl = $offer_decl;
2790                            offer_decl.source = Some(fdecl::Ref::Child(
2791                               fdecl::ChildRef { name: "a".to_string(), collection: None },
2792                            ));
2793                            offer_decl.target = Some(fdecl::Ref::Child(
2794                               fdecl::ChildRef { name: "b".to_string(), collection: None },
2795                            ));
2796                            offer_decl.dependency_type = Some(fdecl::DependencyType::Strong);
2797                            $ty(offer_decl)
2798                        },
2799                        {
2800                            let mut offer_decl = $offer_decl;
2801                            offer_decl.source = Some(fdecl::Ref::Child(
2802                               fdecl::ChildRef { name: "b".to_string(), collection: None },
2803                            ));
2804                            offer_decl.target = Some(fdecl::Ref::Child(
2805                               fdecl::ChildRef { name: "a".to_string(), collection: None },
2806                            ));
2807                            offer_decl.dependency_type = Some(weak_dep);
2808                            $ty(offer_decl)
2809                        },
2810                    ];
2811                    let children = ["a", "b"].iter().map(|name| {
2812                        fdecl::Child {
2813                            name: Some(name.to_string()),
2814                            url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
2815                            startup: Some(fdecl::StartupMode::Lazy),
2816                            on_terminate: None,
2817                            environment: None,
2818                            ..Default::default()
2819                        }
2820                    }).collect();
2821                    decl.offers = Some(offers);
2822                    decl.children = Some(children);
2823                    let result = Ok(());
2824                    validate_test(decl, result);
2825                }
2826            )+
2827        }
2828    }
2829
2830    #[track_caller]
2831    fn validate_test(input: fdecl::Component, expected_res: Result<(), ErrorList>) {
2832        let res = validate(&input, &mut DirectedGraph::new());
2833        assert_eq!(res, expected_res);
2834    }
2835
2836    #[track_caller]
2837    fn validate_test_any_result(input: fdecl::Component, expected_res: Vec<Result<(), ErrorList>>) {
2838        let res = format!("{:?}", validate(&input, &mut DirectedGraph::new()));
2839        let expected_res_debug = format!("{:?}", expected_res);
2840
2841        let matched_exp =
2842            expected_res.into_iter().find(|expected| res == format!("{:?}", expected));
2843
2844        assert!(
2845            matched_exp.is_some(),
2846            "assertion failed: Expected one of:\n{:?}\nActual:\n{:?}",
2847            expected_res_debug,
2848            res
2849        );
2850    }
2851
2852    #[track_caller]
2853    fn validate_values_data_test(
2854        input: fdecl::ConfigValuesData,
2855        expected_res: Result<(), ErrorList>,
2856    ) {
2857        let res = validate_values_data(&input);
2858        assert_eq!(res, expected_res);
2859    }
2860
2861    #[track_caller]
2862    fn validate_capabilities_test(
2863        input: Vec<fdecl::Capability>,
2864        as_builtin: bool,
2865        expected_res: Result<(), ErrorList>,
2866    ) {
2867        let res = validate_capabilities(&input, as_builtin);
2868        assert_eq!(res, expected_res);
2869    }
2870
2871    fn new_component_decl() -> fdecl::Component {
2872        fdecl::Component {
2873            program: None,
2874            uses: None,
2875            exposes: None,
2876            offers: None,
2877            facets: None,
2878            capabilities: None,
2879            children: None,
2880            collections: None,
2881            environments: None,
2882            ..Default::default()
2883        }
2884    }
2885
2886    test_validate_any_result! {
2887        test_validate_use_disallows_nested_dirs => {
2888            input = {
2889                let mut decl = new_component_decl();
2890                decl.uses = Some(vec![
2891                    fdecl::Use::Directory(fdecl::UseDirectory {
2892                        dependency_type: Some(fdecl::DependencyType::Strong),
2893                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2894                        source_name: Some("abc".to_string()),
2895                        target_path: Some("/foo/bar".to_string()),
2896                        rights: Some(fio::Operations::CONNECT),
2897                        subdir: None,
2898                        ..Default::default()
2899                    }),
2900                    fdecl::Use::Directory(fdecl::UseDirectory {
2901                        dependency_type: Some(fdecl::DependencyType::Strong),
2902                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2903                        source_name: Some("abc".to_string()),
2904                        target_path: Some("/foo/bar/baz".to_string()),
2905                        rights: Some(fio::Operations::CONNECT),
2906                        subdir: None,
2907                        ..Default::default()
2908                    }),
2909                ]);
2910                decl
2911            },
2912            results = vec![
2913                Err(ErrorList::new(vec![
2914                    Error::invalid_path_overlap(
2915                        DeclType::UseDirectory, "/foo/bar/baz", DeclType::UseDirectory, "/foo/bar"),
2916                ])),
2917                Err(ErrorList::new(vec![
2918                    Error::invalid_path_overlap(
2919                        DeclType::UseDirectory, "/foo/bar", DeclType::UseDirectory, "/foo/bar/baz"),
2920                ])),
2921            ],
2922        },
2923        test_validate_use_disallows_nested_dirs_storage => {
2924            input = {
2925                let mut decl = new_component_decl();
2926                decl.uses = Some(vec![
2927                    fdecl::Use::Storage(fdecl::UseStorage {
2928                        source_name: Some("abc".to_string()),
2929                        target_path: Some("/foo/bar".to_string()),
2930                        ..Default::default()
2931                    }),
2932                    fdecl::Use::Storage(fdecl::UseStorage {
2933                        source_name: Some("abc".to_string()),
2934                        target_path: Some("/foo/bar/baz".to_string()),
2935                        ..Default::default()
2936                    }),
2937                ]);
2938                decl
2939            },
2940            results = vec![
2941                Err(ErrorList::new(vec![
2942                    Error::invalid_path_overlap(
2943                        DeclType::UseStorage, "/foo/bar/baz", DeclType::UseStorage, "/foo/bar"),
2944                ])),
2945                Err(ErrorList::new(vec![
2946                    Error::invalid_path_overlap(
2947                        DeclType::UseStorage, "/foo/bar", DeclType::UseStorage, "/foo/bar/baz"),
2948                ])),
2949            ],
2950        },
2951        test_validate_use_disallows_nested_dirs_directory_and_storage => {
2952            input = {
2953                let mut decl = new_component_decl();
2954                decl.uses = Some(vec![
2955                    fdecl::Use::Directory(fdecl::UseDirectory {
2956                        dependency_type: Some(fdecl::DependencyType::Strong),
2957                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2958                        source_name: Some("abc".to_string()),
2959                        target_path: Some("/foo/bar".to_string()),
2960                        rights: Some(fio::Operations::CONNECT),
2961                        subdir: None,
2962                        ..Default::default()
2963                    }),
2964                    fdecl::Use::Storage(fdecl::UseStorage {
2965                        source_name: Some("abc".to_string()),
2966                        target_path: Some("/foo/bar/baz".to_string()),
2967                        ..Default::default()
2968                    }),
2969                ]);
2970                decl
2971            },
2972            results = vec![
2973                Err(ErrorList::new(vec![
2974                    Error::invalid_path_overlap(
2975                        DeclType::UseStorage, "/foo/bar/baz", DeclType::UseDirectory, "/foo/bar"),
2976                ])),
2977                Err(ErrorList::new(vec![
2978                    Error::invalid_path_overlap(
2979                        DeclType::UseDirectory, "/foo/bar", DeclType::UseStorage, "/foo/bar/baz"),
2980                ])),
2981            ],
2982        },
2983        test_validate_use_disallows_common_prefixes_protocol => {
2984            input = {
2985                let mut decl = new_component_decl();
2986                decl.uses = Some(vec![
2987                    fdecl::Use::Directory(fdecl::UseDirectory {
2988                        dependency_type: Some(fdecl::DependencyType::Strong),
2989                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2990                        source_name: Some("abc".to_string()),
2991                        target_path: Some("/foo/bar".to_string()),
2992                        rights: Some(fio::Operations::CONNECT),
2993                        subdir: None,
2994                        ..Default::default()
2995                    }),
2996                    fdecl::Use::Protocol(fdecl::UseProtocol {
2997                        dependency_type: Some(fdecl::DependencyType::Strong),
2998                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2999                        source_name: Some("crow".to_string()),
3000                        target_path: Some("/foo/bar/fuchsia.2".to_string()),
3001                        ..Default::default()
3002                    }),
3003                ]);
3004                decl
3005            },
3006            results = vec![
3007                Err(ErrorList::new(vec![
3008                    Error::invalid_path_overlap(
3009                        DeclType::UseProtocol, "/foo/bar/fuchsia.2", DeclType::UseDirectory, "/foo/bar"),
3010                ])),
3011                Err(ErrorList::new(vec![
3012                    Error::invalid_path_overlap(
3013                        DeclType::UseDirectory, "/foo/bar", DeclType::UseProtocol, "/foo/bar/fuchsia.2"),
3014                ])),
3015            ],
3016        },
3017        test_validate_use_disallows_common_prefixes_service => {
3018            input = {
3019                let mut decl = new_component_decl();
3020                decl.uses = Some(vec![
3021                    fdecl::Use::Directory(fdecl::UseDirectory {
3022                        dependency_type: Some(fdecl::DependencyType::Strong),
3023                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3024                        source_name: Some("abc".to_string()),
3025                        target_path: Some("/foo/bar".to_string()),
3026                        rights: Some(fio::Operations::CONNECT),
3027                        subdir: None,
3028                        ..Default::default()
3029                    }),
3030                    fdecl::Use::Service(fdecl::UseService {
3031                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3032                        source_name: Some("space".to_string()),
3033                        target_path: Some("/foo/bar/baz/fuchsia.logger.Log".to_string()),
3034                        dependency_type: Some(fdecl::DependencyType::Strong),
3035                        ..Default::default()
3036                    }),
3037                ]);
3038                decl
3039            },
3040            results = vec![
3041                Err(ErrorList::new(vec![
3042                    Error::invalid_path_overlap(
3043                        DeclType::UseService, "/foo/bar/baz/fuchsia.logger.Log", DeclType::UseDirectory, "/foo/bar"),
3044                ])),
3045                Err(ErrorList::new(vec![
3046                    Error::invalid_path_overlap(
3047                        DeclType::UseDirectory, "/foo/bar", DeclType::UseService, "/foo/bar/baz/fuchsia.logger.Log"),
3048                ])),
3049            ],
3050        },
3051        test_validate_use_disallows_pkg => {
3052            input = {
3053                let mut decl = new_component_decl();
3054                decl.uses = Some(vec![
3055                    fdecl::Use::Directory(fdecl::UseDirectory {
3056                        dependency_type: Some(fdecl::DependencyType::Strong),
3057                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3058                        source_name: Some("abc".to_string()),
3059                        target_path: Some("/pkg".to_string()),
3060                        rights: Some(fio::Operations::CONNECT),
3061                        subdir: None,
3062                        ..Default::default()
3063                    }),
3064                ]);
3065                decl
3066            },
3067            results = vec![
3068                Err(ErrorList::new(vec![
3069                    Error::pkg_path_overlap(DeclType::UseDirectory, "/pkg"),
3070                ])),
3071            ],
3072        },
3073        test_validate_use_disallows_pkg_overlap => {
3074            input = {
3075                let mut decl = new_component_decl();
3076                decl.uses = Some(vec![
3077                    fdecl::Use::Directory(fdecl::UseDirectory {
3078                        dependency_type: Some(fdecl::DependencyType::Strong),
3079                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3080                        source_name: Some("abc".to_string()),
3081                        target_path: Some("/pkg/foo".to_string()),
3082                        rights: Some(fio::Operations::CONNECT),
3083                        subdir: None,
3084                        ..Default::default()
3085                    }),
3086                ]);
3087                decl
3088            },
3089            results = vec![
3090                Err(ErrorList::new(vec![
3091                    Error::pkg_path_overlap(DeclType::UseDirectory, "/pkg/foo"),
3092                ])),
3093            ],
3094        },
3095        test_validate_use_optional_config_correct => {
3096            input = {
3097                let mut decl = new_component_decl();
3098                decl.uses = Some(vec![
3099                    fdecl::Use::Config(fdecl::UseConfiguration {
3100                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3101                        source_name: Some("abc".to_string()),
3102                        target_name: Some("foo".to_string()),
3103                        availability: Some(fdecl::Availability::Optional),
3104                        type_: Some(fdecl::ConfigType {
3105                            layout: fdecl::ConfigTypeLayout::Bool,
3106                            parameters: Some(Vec::new()),
3107                            constraints: Vec::new(),
3108                        }),
3109                        ..Default::default()
3110                    }),
3111                ]);
3112                decl.config = Some(fdecl::ConfigSchema {
3113                    fields: Some(vec![fdecl::ConfigField {
3114                        key: Some("foo".into()),
3115                        type_: Some(fdecl::ConfigType {
3116                            layout: fdecl::ConfigTypeLayout::Bool,
3117                            parameters: Some(Vec::new()),
3118                            constraints: Vec::new(),
3119                        }),
3120                        mutability: None,
3121                        ..Default::default()}]),
3122                    checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3123                    value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3124                    ..Default::default()
3125                     });
3126                decl
3127            },
3128            results = vec![Ok(())],
3129        },
3130        test_validate_use_optional_config_no_config_schema => {
3131            input = {
3132                let mut decl = new_component_decl();
3133                decl.uses = Some(vec![
3134                    fdecl::Use::Config(fdecl::UseConfiguration {
3135                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3136                        source_name: Some("abc".to_string()),
3137                        target_name: Some("foo".to_string()),
3138                        availability: Some(fdecl::Availability::Optional),
3139                        type_: Some(fdecl::ConfigType {
3140                            layout: fdecl::ConfigTypeLayout::Bool,
3141                            parameters: None,
3142                            constraints: Vec::new(),
3143                        }),
3144                        ..Default::default()
3145                    }),
3146                ]);
3147                decl
3148            },
3149            results = vec![
3150                Err(ErrorList::new(vec![
3151                    Error::missing_field(DeclType::ConfigField, "config"),
3152                ])),
3153            ],
3154        },
3155        test_validate_use_optional_config_no_config_field => {
3156            input = {
3157                let mut decl = new_component_decl();
3158                decl.uses = Some(vec![
3159                    fdecl::Use::Config(fdecl::UseConfiguration {
3160                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3161                        source_name: Some("abc".to_string()),
3162                        target_name: Some("foo".to_string()),
3163                        availability: Some(fdecl::Availability::Optional),
3164                        type_: Some(fdecl::ConfigType {
3165                            layout: fdecl::ConfigTypeLayout::Bool,
3166                            parameters: None,
3167                            constraints: Vec::new(),
3168                        }),
3169                        ..Default::default()
3170                    }),
3171                ]);
3172                decl.config = Some(fdecl::ConfigSchema {
3173                    fields: Some(vec![]),
3174                    checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3175                    value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3176                    ..Default::default()
3177                     });
3178                decl
3179            },
3180            results = vec![
3181                Err(ErrorList::new(vec![
3182                    Error::missing_field(DeclType::ConfigField, "foo"),
3183                ])),
3184            ],
3185        },
3186        test_validate_use_optional_config_bad_type => {
3187            input = {
3188                let mut decl = new_component_decl();
3189                decl.uses = Some(vec![
3190                    fdecl::Use::Config(fdecl::UseConfiguration {
3191                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3192                        source_name: Some("abc".to_string()),
3193                        target_name: Some("foo".to_string()),
3194                        availability: Some(fdecl::Availability::Optional),
3195                        type_: Some(fdecl::ConfigType {
3196                            layout: fdecl::ConfigTypeLayout::Bool,
3197                            parameters: None,
3198                            constraints: Vec::new(),
3199                        }),
3200                        ..Default::default()
3201                    }),
3202                ]);
3203                decl.config = Some(fdecl::ConfigSchema {
3204                    fields: Some(vec![fdecl::ConfigField {
3205                        key: Some("foo".into()),
3206                        type_: Some(fdecl::ConfigType {
3207                            layout: fdecl::ConfigTypeLayout::Int16,
3208                            parameters: Some(Vec::new()),
3209                            constraints: Vec::new(),
3210                        }),
3211                        mutability: None,
3212                        ..Default::default()}]),
3213                    checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3214                    value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3215                    ..Default::default()
3216                     });
3217                decl
3218            },
3219            results = vec![
3220                Err(ErrorList::new(vec![
3221                    Error::invalid_field(DeclType::ConfigField, "foo"),
3222                ])),
3223            ],
3224        },
3225    }
3226
3227    #[cfg(fuchsia_api_level_at_least = "NEXT")]
3228    test_validate_any_result! {
3229        test_validate_use_dictionary => {
3230            input = {
3231                let mut decl = new_component_decl();
3232                decl.uses = Some(vec![
3233                    fdecl::Use::Dictionary(fdecl::UseDictionary {
3234                        dependency_type: Some(fdecl::DependencyType::Strong),
3235                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3236                        source_name: Some("toolbox".to_string()),
3237                        target_path: Some("/svc".to_string()),
3238                        availability: Some(fdecl::Availability::Required),
3239                        ..Default::default()
3240                    }),
3241                ]);
3242                decl
3243            },
3244            results = vec![
3245                Ok(()),
3246            ],
3247        },
3248        test_validate_use_dictionary_invalid_name => {
3249            input = {
3250                let mut decl = new_component_decl();
3251                decl.uses = Some(vec![
3252                    fdecl::Use::Dictionary(fdecl::UseDictionary {
3253                        dependency_type: Some(fdecl::DependencyType::Strong),
3254                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3255                        source_name: Some("toolbox@".to_string()),
3256                        target_path: Some("/svc".to_string()),
3257                        availability: Some(fdecl::Availability::Required),
3258                        ..Default::default()
3259                    }),
3260                ]);
3261                decl
3262            },
3263            results = vec![
3264                Err(ErrorList::new(vec![Error::invalid_field(DeclType::UseDictionary, "source_name")])),
3265            ],
3266        },
3267        test_validate_use_dictionary_invalid_path => {
3268            input = {
3269                let mut decl = new_component_decl();
3270                decl.uses = Some(vec![
3271                    fdecl::Use::Dictionary(fdecl::UseDictionary {
3272                        dependency_type: Some(fdecl::DependencyType::Strong),
3273                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3274                        source_name: Some("toolbox".to_string()),
3275                        target_path: Some("/svc@".to_string()),
3276                        availability: Some(fdecl::Availability::Required),
3277                        ..Default::default()
3278                    }),
3279                ]);
3280                decl
3281            },
3282            results = vec![
3283                Err(ErrorList::new(vec![Error::field_invalid_segment(DeclType::UseDictionary, "target_path")])),
3284            ],
3285        },
3286        test_validate_use_dictionary_disallows_pkg_overlap => {
3287            input = {
3288                let mut decl = new_component_decl();
3289                decl.uses = Some(vec![
3290                    fdecl::Use::Dictionary(fdecl::UseDictionary {
3291                        dependency_type: Some(fdecl::DependencyType::Strong),
3292                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3293                        source_name: Some("toolbox".to_string()),
3294                        target_path: Some("/pkg/toolbox".to_string()),
3295                        availability: Some(fdecl::Availability::Required),
3296                        ..Default::default()
3297                    }),
3298                ]);
3299                decl
3300            },
3301            results = vec![
3302                Err(ErrorList::new(vec![
3303                    Error::pkg_path_overlap(DeclType::UseDictionary, "/pkg/toolbox"),
3304                ])),
3305            ],
3306        },
3307        test_validate_use_dictionary_path_overlap_directory => {
3308            input = {
3309                let mut decl = new_component_decl();
3310                decl.uses = Some(vec![
3311                    fdecl::Use::Dictionary(fdecl::UseDictionary {
3312                        dependency_type: Some(fdecl::DependencyType::Strong),
3313                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3314                        source_name: Some("toolbox".to_string()),
3315                        target_path: Some("/foo/bar".to_string()),
3316                        availability: Some(fdecl::Availability::Required),
3317                        ..Default::default()
3318                    }),
3319                    fdecl::Use::Directory(fdecl::UseDirectory {
3320                        dependency_type: Some(fdecl::DependencyType::Strong),
3321                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3322                        source_name: Some("data".to_string()),
3323                        target_path: Some("/foo".to_string()),
3324                        rights: Some(fio::Operations::CONNECT),
3325                        subdir: None,
3326                        ..Default::default()
3327                    }),
3328                ]);
3329                decl
3330            },
3331            results = vec![
3332                Err(ErrorList::new(vec![
3333                    Error::invalid_path_overlap(
3334                        DeclType::UseDirectory,
3335                        "/foo",
3336                        DeclType::UseDictionary,
3337                        "/foo/bar",
3338                    ),
3339                ])),
3340                Err(ErrorList::new(vec![
3341                    Error::invalid_path_overlap(
3342                        DeclType::UseDictionary,
3343                        "/foo/bar",
3344                        DeclType::UseDirectory,
3345                        "/foo",
3346                    ),
3347                ])),
3348            ],
3349        },
3350        test_validate_use_dictionary_path_overlap_protocol => {
3351            input = {
3352                let mut decl = new_component_decl();
3353                decl.uses = Some(vec![
3354                    fdecl::Use::Dictionary(fdecl::UseDictionary {
3355                        dependency_type: Some(fdecl::DependencyType::Strong),
3356                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3357                        source_name: Some("toolbox".to_string()),
3358                        target_path: Some("/svc/toolbox".to_string()),
3359                        availability: Some(fdecl::Availability::Required),
3360                        ..Default::default()
3361                    }),
3362                    fdecl::Use::Protocol(fdecl::UseProtocol {
3363                        dependency_type: Some(fdecl::DependencyType::Strong),
3364                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3365                        source_name: Some("fuchsia.examples.Echo".to_string()),
3366                        target_path: Some("/svc/fuchsia.examples.Echo".to_string()),
3367                        ..Default::default()
3368                    }),
3369                ]);
3370                decl
3371            },
3372            results = vec![
3373                Ok(()),
3374            ],
3375        },
3376        test_validate_use_dictionary_path_overlap_protocol_2 => {
3377            input = {
3378                let mut decl = new_component_decl();
3379                decl.uses = Some(vec![
3380                    fdecl::Use::Dictionary(fdecl::UseDictionary {
3381                        dependency_type: Some(fdecl::DependencyType::Strong),
3382                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3383                        source_name: Some("toolbox".to_string()),
3384                        target_path: Some("/svc".to_string()),
3385                        availability: Some(fdecl::Availability::Required),
3386                        ..Default::default()
3387                    }),
3388                    fdecl::Use::Protocol(fdecl::UseProtocol {
3389                        dependency_type: Some(fdecl::DependencyType::Strong),
3390                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3391                        source_name: Some("fuchsia.examples.Echo".to_string()),
3392                        target_path: Some("/svc/fuchsia.examples.Echo".to_string()),
3393                        ..Default::default()
3394                    }),
3395                ]);
3396                decl
3397            },
3398            results = vec![
3399                Ok(()),
3400            ],
3401        },
3402    }
3403
3404    test_validate_values_data! {
3405        test_values_data_ok => {
3406            input = fdecl::ConfigValuesData {
3407                values: Some(vec![
3408                    fdecl::ConfigValueSpec {
3409                        value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(true))),
3410                        ..Default::default()
3411                    }
3412                ]),
3413                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3414                ..Default::default()
3415            },
3416            result = Ok(()),
3417        },
3418        test_values_data_no_checksum => {
3419            input = fdecl::ConfigValuesData {
3420                values: Some(vec![]),
3421                checksum: None,
3422                ..Default::default()
3423            },
3424            result = Err(ErrorList::new(vec![
3425                Error::missing_field(DeclType::ConfigValuesData, "checksum")
3426            ])),
3427        },
3428        test_values_data_unknown_checksum => {
3429            input = fdecl::ConfigValuesData {
3430                values: Some(vec![]),
3431                checksum: Some(fdecl::ConfigChecksum::unknown_variant_for_testing()),
3432                ..Default::default()
3433            },
3434            result = Err(ErrorList::new(vec![
3435                Error::invalid_field(DeclType::ConfigValuesData, "checksum")
3436            ])),
3437        },
3438        test_values_data_no_values => {
3439            input = fdecl::ConfigValuesData {
3440                values: None,
3441                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3442                ..Default::default()
3443            },
3444            result = Err(ErrorList::new(vec![
3445                Error::missing_field(DeclType::ConfigValuesData, "values")
3446            ])),
3447        },
3448        test_values_data_no_inner_value => {
3449            input = fdecl::ConfigValuesData {
3450                values: Some(vec![
3451                    fdecl::ConfigValueSpec::default()
3452                ]),
3453                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3454                ..Default::default()
3455            },
3456            result = Err(ErrorList::new(vec![
3457                Error::missing_field(DeclType::ConfigValueSpec, "value")
3458            ])),
3459        },
3460        test_values_data_unknown_inner_value => {
3461            input = fdecl::ConfigValuesData {
3462                values: Some(vec![
3463                    fdecl::ConfigValueSpec {
3464                        value: Some(fdecl::ConfigValue::unknown_variant_for_testing()),
3465                        ..Default::default()
3466                    }
3467                ]),
3468                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3469                ..Default::default()
3470            },
3471            result = Err(ErrorList::new(vec![
3472                Error::invalid_field(DeclType::ConfigValueSpec, "value")
3473            ])),
3474        },
3475        test_values_data_unknown_single_value => {
3476            input = fdecl::ConfigValuesData {
3477                values: Some(vec![
3478                    fdecl::ConfigValueSpec {
3479                        value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::unknown_variant_for_testing())),
3480                        ..Default::default()
3481                    }
3482                ]),
3483                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3484                ..Default::default()
3485            },
3486            result = Err(ErrorList::new(vec![
3487                Error::invalid_field(DeclType::ConfigValueSpec, "value")
3488            ])),
3489        },
3490        test_values_data_unknown_list_value => {
3491            input = fdecl::ConfigValuesData {
3492                values: Some(vec![
3493                    fdecl::ConfigValueSpec {
3494                        value: Some(fdecl::ConfigValue::Vector(fdecl::ConfigVectorValue::unknown_variant_for_testing())),
3495                        ..Default::default()
3496                    }
3497                ]),
3498                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3499                ..Default::default()
3500            },
3501            result = Err(ErrorList::new(vec![
3502                Error::invalid_field(DeclType::ConfigValueSpec, "value")
3503            ])),
3504        },
3505    }
3506
3507    test_validate! {
3508        // uses
3509        test_validate_uses_empty => {
3510            input = {
3511                let mut decl = new_component_decl();
3512                decl.program = Some(fdecl::Program {
3513                    runner: Some("elf".to_string()),
3514                    info: Some(fdata::Dictionary {
3515                        entries: None,
3516                        ..Default::default()
3517                    }),
3518                    ..Default::default()
3519                });
3520                decl.uses = Some(vec![
3521                    fdecl::Use::Service(fdecl::UseService {
3522                        source: None,
3523                        source_name: None,
3524                        target_path: None,
3525                        dependency_type: None,
3526                        ..Default::default()
3527                    }),
3528                    fdecl::Use::Protocol(fdecl::UseProtocol {
3529                        dependency_type: None,
3530                        source: None,
3531                        source_name: None,
3532                        target_path: None,
3533                        ..Default::default()
3534                    }),
3535                    fdecl::Use::Directory(fdecl::UseDirectory {
3536                        dependency_type: None,
3537                        source: None,
3538                        source_name: None,
3539                        target_path: None,
3540                        rights: None,
3541                        subdir: None,
3542                        ..Default::default()
3543                    }),
3544                    fdecl::Use::Storage(fdecl::UseStorage {
3545                        source_name: None,
3546                        target_path: None,
3547                        ..Default::default()
3548                    }),
3549                    fdecl::Use::EventStream(fdecl::UseEventStream {
3550                        source_name: None,
3551                        source: None,
3552                        target_path: None,
3553                        ..Default::default()
3554                    }),
3555                    fdecl::Use::Runner(fdecl::UseRunner {
3556                        source_name: None,
3557                        source: None,
3558                        ..Default::default()
3559                    }),
3560                ]);
3561                decl
3562            },
3563            result = Err(ErrorList::new(vec![
3564                Error::missing_field(DeclType::UseService, "source"),
3565                Error::missing_field(DeclType::UseService, "source_name"),
3566                Error::missing_field(DeclType::UseService, "target_path"),
3567                Error::missing_field(DeclType::UseService, "dependency_type"),
3568                Error::missing_field(DeclType::UseProtocol, "source"),
3569                Error::missing_field(DeclType::UseProtocol, "source_name"),
3570                Error::missing_field(DeclType::UseProtocol, "target_path"),
3571                Error::missing_field(DeclType::UseProtocol, "dependency_type"),
3572                Error::missing_field(DeclType::UseDirectory, "source"),
3573                Error::missing_field(DeclType::UseDirectory, "source_name"),
3574                Error::missing_field(DeclType::UseDirectory, "target_path"),
3575                Error::missing_field(DeclType::UseDirectory, "dependency_type"),
3576                Error::missing_field(DeclType::UseDirectory, "rights"),
3577                Error::missing_field(DeclType::UseStorage, "source_name"),
3578                Error::missing_field(DeclType::UseStorage, "target_path"),
3579                Error::missing_field(DeclType::UseEventStream, "source"),
3580                Error::missing_field(DeclType::UseEventStream, "source_name"),
3581                Error::missing_field(DeclType::UseEventStream, "target_path"),
3582                Error::missing_field(DeclType::UseRunner, "source"),
3583                Error::missing_field(DeclType::UseRunner, "source_name"),
3584            ])),
3585        },
3586        test_validate_missing_program_info => {
3587            input = {
3588                let mut decl = new_component_decl();
3589                decl.program = Some(fdecl::Program {
3590                    runner: Some("runner".to_string()),
3591                    info: None,
3592                    ..Default::default()
3593                });
3594                decl
3595            },
3596            result = Err(ErrorList::new(vec![
3597                Error::missing_field(DeclType::Program, "info")
3598            ])),
3599        },
3600        test_validate_uses_invalid_identifiers => {
3601            input = {
3602                let mut decl = new_component_decl();
3603                decl.uses = Some(vec![
3604                    fdecl::Use::Service(fdecl::UseService {
3605                        source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
3606                            name: "^bad".to_string(),
3607                        })),
3608                        source_name: Some("foo/".to_string()),
3609                        target_path: Some("a/foo".to_string()),
3610                        dependency_type: Some(fdecl::DependencyType::Strong),
3611                        ..Default::default()
3612                    }),
3613                    fdecl::Use::Protocol(fdecl::UseProtocol {
3614                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3615                            name: "^bad".to_string(),
3616                            collection: None,
3617                        })),
3618                        source_name: Some("foo/".to_string()),
3619                        target_path: Some("b/foo".to_string()),
3620                        dependency_type: Some(fdecl::DependencyType::Strong),
3621                        ..Default::default()
3622                    }),
3623                    fdecl::Use::Directory(fdecl::UseDirectory {
3624                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3625                            name: "^bad".to_string(),
3626                            collection: None,
3627                        })),
3628                        source_name: Some("foo/".to_string()),
3629                        target_path: Some("c".to_string()),
3630                        rights: Some(fio::Operations::CONNECT),
3631                        subdir: Some("/foo".to_string()),
3632                        dependency_type: Some(fdecl::DependencyType::Strong),
3633                        ..Default::default()
3634                    }),
3635                    fdecl::Use::Storage(fdecl::UseStorage {
3636                        source_name: Some("foo/".to_string()),
3637                        target_path: Some("d".to_string()),
3638                        ..Default::default()
3639                    }),
3640                    fdecl::Use::EventStream(fdecl::UseEventStream {
3641                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3642                            name: "^bad".to_string(),
3643                            collection: None,
3644                        })),
3645                        source_name: Some("foo/".to_string()),
3646                        target_path: Some("e".to_string()),
3647                        ..Default::default()
3648                    }),
3649                    fdecl::Use::Runner(fdecl::UseRunner {
3650                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3651                            name: "^bad".to_string(),
3652                            collection: None,
3653                        })),
3654                        source_name: Some("foo/".to_string()),
3655                        ..Default::default()
3656                    }),
3657                ]);
3658                decl
3659            },
3660            result = Err(ErrorList::new(vec![
3661                Error::invalid_field(DeclType::UseService, "source.capability.name"),
3662                Error::invalid_field(DeclType::UseService, "source_name"),
3663                Error::invalid_field(DeclType::UseService, "target_path"),
3664                Error::invalid_field(DeclType::UseProtocol, "source.child.name"),
3665                Error::invalid_field(DeclType::UseProtocol, "source_name"),
3666                Error::invalid_field(DeclType::UseProtocol, "target_path"),
3667                Error::invalid_field(DeclType::UseDirectory, "source.child.name"),
3668                Error::invalid_field(DeclType::UseDirectory, "source_name"),
3669                Error::invalid_field(DeclType::UseDirectory, "target_path"),
3670                Error::invalid_field(DeclType::UseDirectory, "subdir"),
3671                Error::invalid_field(DeclType::UseStorage, "source_name"),
3672                Error::invalid_field(DeclType::UseStorage, "target_path"),
3673                Error::invalid_field(DeclType::UseEventStream, "source.child.name"),
3674                Error::invalid_field(DeclType::UseEventStream, "source_name"),
3675                Error::invalid_field(DeclType::UseEventStream, "target_path"),
3676                Error::invalid_field(DeclType::UseRunner, "source.child.name"),
3677                Error::invalid_field(DeclType::UseRunner, "source_name"),
3678            ])),
3679        },
3680        test_validate_uses_missing_source => {
3681            input = {
3682                fdecl::Component {
3683                    uses: Some(vec![
3684                        fdecl::Use::Protocol(fdecl::UseProtocol {
3685                            dependency_type: Some(fdecl::DependencyType::Strong),
3686                            source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
3687                                name: "this-storage-doesnt-exist".to_string(),
3688                            })),
3689                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3690                            target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3691                            ..Default::default()
3692                        })
3693                    ]),
3694                    ..new_component_decl()
3695                }
3696            },
3697            result = Err(ErrorList::new(vec![
3698                Error::invalid_capability(DeclType::UseProtocol, "source", "this-storage-doesnt-exist"),
3699            ])),
3700        },
3701        test_validate_uses_invalid_child => {
3702            input = {
3703                fdecl::Component {
3704                    uses: Some(vec![
3705                        fdecl::Use::Protocol(fdecl::UseProtocol {
3706                            dependency_type: Some(fdecl::DependencyType::Strong),
3707                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3708                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3709                            target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3710                            ..Default::default()
3711                        }),
3712                        fdecl::Use::Service(fdecl::UseService {
3713                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3714                            source_name: Some("service_name".to_string()),
3715                            target_path: Some("/svc/service_name".to_string()),
3716                            dependency_type: Some(fdecl::DependencyType::Strong),
3717                            ..Default::default()
3718                        }),
3719                        fdecl::Use::Directory(fdecl::UseDirectory {
3720                            dependency_type: Some(fdecl::DependencyType::Strong),
3721                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3722                            source_name: Some("DirectoryName".to_string()),
3723                            target_path: Some("/data/DirectoryName".to_string()),
3724                            rights: Some(fio::Operations::CONNECT),
3725                            subdir: None,
3726                            ..Default::default()
3727                        }),
3728                        fdecl::Use::Runner(fdecl::UseRunner {
3729                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3730                            source_name: Some("RunnerName".to_string()),
3731                            ..Default::default()
3732                        }),
3733                    ]),
3734                    ..new_component_decl()
3735                }
3736            },
3737            result = Err(ErrorList::new(vec![
3738                Error::invalid_child(DeclType::UseProtocol, "source", "no-such-child"),
3739                Error::invalid_child(DeclType::UseService, "source", "no-such-child"),
3740                Error::invalid_child(DeclType::UseDirectory, "source", "no-such-child"),
3741                Error::invalid_child(DeclType::UseRunner, "source", "no-such-child"),
3742            ])),
3743        },
3744        test_validate_uses_invalid_capability_from_self => {
3745            input = {
3746                let mut decl = new_component_decl();
3747                decl.uses = Some(vec![
3748                    fdecl::Use::Service(fdecl::UseService {
3749                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3750                        source_name: Some("fuchsia.some.library.SomeService".into()),
3751                        target_path: Some("/svc/foo".into()),
3752                        dependency_type: Some(fdecl::DependencyType::Strong),
3753                        ..Default::default()
3754                    }),
3755                    fdecl::Use::Protocol(fdecl::UseProtocol {
3756                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3757                        source_name: Some("fuchsia.some.library.SomeProtocol".into()),
3758                        target_path: Some("/svc/bar".into()),
3759                        dependency_type: Some(fdecl::DependencyType::Strong),
3760                        ..Default::default()
3761                    }),
3762                    fdecl::Use::Directory(fdecl::UseDirectory {
3763                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3764                        source_name: Some("dir".into()),
3765                        target_path: Some("/assets".into()),
3766                        dependency_type: Some(fdecl::DependencyType::Strong),
3767                        rights: Some(fio::Operations::CONNECT),
3768                        ..Default::default()
3769                    }),
3770                    fdecl::Use::Runner(fdecl::UseRunner {
3771                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3772                        source_name: Some("source_elf".into()),
3773                        ..Default::default()
3774                    }),
3775                    fdecl::Use::Config(fdecl::UseConfiguration {
3776                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3777                        source_name: Some("source_config".into()),
3778                        target_name: Some("config".into()),
3779                        type_: Some(fdecl::ConfigType {
3780                            layout: fdecl::ConfigTypeLayout::Bool,
3781                            parameters: Some(Vec::new()),
3782                            constraints: Vec::new(),
3783                        }),
3784                        ..Default::default()
3785                    }),
3786                    fdecl::Use::Protocol(fdecl::UseProtocol {
3787                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3788                        source_name: Some("fuchsia.some.library.SomeProtocol".into()),
3789                        source_dictionary: Some("dict/inner".into()),
3790                        target_path: Some("/svc/baz".into()),
3791                        dependency_type: Some(fdecl::DependencyType::Strong),
3792                        ..Default::default()
3793                    }),
3794                ]);
3795                decl
3796            },
3797            result = Err(ErrorList::new(vec![
3798                Error::invalid_capability(
3799                    DeclType::UseService,
3800                    "source",
3801                    "fuchsia.some.library.SomeService"),
3802                Error::invalid_capability(
3803                    DeclType::UseProtocol,
3804                    "source",
3805                    "fuchsia.some.library.SomeProtocol"),
3806                Error::invalid_capability(DeclType::UseDirectory, "source", "dir"),
3807                Error::invalid_capability(DeclType::UseRunner, "source", "source_elf"),
3808                Error::invalid_capability(DeclType::UseConfiguration, "source", "source_config"),
3809                Error::invalid_capability(DeclType::UseProtocol, "source", "dict"),
3810            ])),
3811        },
3812        test_validate_use_numbered_handle => {
3813            input = {
3814                fdecl::Component {
3815                    uses: Some(vec![
3816                        // Valid
3817                        fdecl::Use::Protocol(fdecl::UseProtocol {
3818                            dependency_type: Some(fdecl::DependencyType::Strong),
3819                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
3820                            source_name: Some("fuchsia.logger.LogSink".into()),
3821                            numbered_handle: Some(0xab),
3822                            ..Default::default()
3823                        }),
3824                        // Invalid
3825                        fdecl::Use::Protocol(fdecl::UseProtocol {
3826                            dependency_type: Some(fdecl::DependencyType::Strong),
3827                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
3828                            source_name: Some("fuchsia.logger.LogSink".into()),
3829                            target_path: Some("/svc/fuchsia.logger.LogSink".into()),
3830                            numbered_handle: Some(0xab),
3831                            ..Default::default()
3832                        }),
3833                    ]),
3834                    ..new_component_decl()
3835                }
3836            },
3837            result = Err(ErrorList::new(vec![
3838                Error::extraneous_field(DeclType::UseProtocol, "numbered_handle"),
3839            ])),
3840        },
3841        test_validate_use_from_child_offer_to_child_strong_cycle => {
3842            input = {
3843                fdecl::Component {
3844                    capabilities: Some(vec![
3845                        fdecl::Capability::Service(fdecl::Service {
3846                            name: Some("a".to_string()),
3847                            source_path: Some("/a".to_string()),
3848                            ..Default::default()
3849                        })]),
3850                    uses: Some(vec![
3851                        fdecl::Use::Protocol(fdecl::UseProtocol {
3852                            dependency_type: Some(fdecl::DependencyType::Strong),
3853                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3854                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3855                            target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3856                            ..Default::default()
3857                        }),
3858                        fdecl::Use::Service(fdecl::UseService {
3859                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3860                            source_name: Some("service_name".to_string()),
3861                            target_path: Some("/svc/service_name".to_string()),
3862                            dependency_type: Some(fdecl::DependencyType::Strong),
3863                            ..Default::default()
3864                        }),
3865                        fdecl::Use::Directory(fdecl::UseDirectory {
3866                            dependency_type: Some(fdecl::DependencyType::Strong),
3867                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3868                            source_name: Some("DirectoryName".to_string()),
3869                            target_path: Some("/data/DirectoryName".to_string()),
3870                            rights: Some(fio::Operations::CONNECT),
3871                            subdir: None,
3872                            ..Default::default()
3873                        }),
3874                    ]),
3875                    offers: Some(vec![
3876                        fdecl::Offer::Service(fdecl::OfferService {
3877                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3878                            source_name: Some("a".to_string()),
3879                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
3880                            target_name: Some("a".to_string()),
3881                            ..Default::default()
3882                        })
3883                    ]),
3884                    children: Some(vec![
3885                        fdecl::Child {
3886                            name: Some("child".to_string()),
3887                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3888                            startup: Some(fdecl::StartupMode::Lazy),
3889                            on_terminate: None,
3890                            ..Default::default()
3891                        }
3892                    ]),
3893                    ..new_component_decl()
3894                }
3895            },
3896            result = Err(ErrorList::new(vec![
3897                Error::dependency_cycle("{{self -> child child -> self}}"),
3898            ])),
3899        },
3900        test_validate_use_from_child_storage_no_cycle => {
3901            input = {
3902                fdecl::Component {
3903                    capabilities: Some(vec![
3904                        fdecl::Capability::Storage(fdecl::Storage {
3905                            name: Some("cdata".to_string()),
3906                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None } )),
3907                            backing_dir: Some("minfs".to_string()),
3908                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
3909                            ..Default::default()
3910                        }),
3911                        fdecl::Capability::Storage(fdecl::Storage {
3912                            name: Some("pdata".to_string()),
3913                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
3914                            backing_dir: Some("minfs".to_string()),
3915                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
3916                            ..Default::default()
3917                        }),
3918                    ]),
3919                    uses: Some(vec![
3920                        fdecl::Use::Protocol(fdecl::UseProtocol {
3921                            dependency_type: Some(fdecl::DependencyType::Strong),
3922                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child1".to_string(), collection: None})),
3923                            source_name: Some("a".to_string()),
3924                            target_path: Some("/svc/a".to_string()),
3925                            ..Default::default()
3926                        }),
3927                    ]),
3928                    offers: Some(vec![
3929                        fdecl::Offer::Storage(fdecl::OfferStorage {
3930                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3931                            source_name: Some("cdata".to_string()),
3932                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3933                            target_name: Some("cdata".to_string()),
3934                            ..Default::default()
3935                        }),
3936                        fdecl::Offer::Storage(fdecl::OfferStorage {
3937                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3938                            source_name: Some("pdata".to_string()),
3939                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3940                            target_name: Some("pdata".to_string()),
3941                            ..Default::default()
3942                        }),
3943                    ]),
3944                    children: Some(vec![
3945                        fdecl::Child {
3946                            name: Some("child1".to_string()),
3947                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3948                            startup: Some(fdecl::StartupMode::Lazy),
3949                            on_terminate: None,
3950                            ..Default::default()
3951                        },
3952                        fdecl::Child {
3953                            name: Some("child2".to_string()),
3954                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
3955                            startup: Some(fdecl::StartupMode::Lazy),
3956                            on_terminate: None,
3957                            ..Default::default()
3958                        }
3959                    ]),
3960                    ..new_component_decl()
3961                }
3962            },
3963            result = Ok(()),
3964        },
3965        test_validate_use_from_child_storage_cycle => {
3966            input = {
3967                fdecl::Component {
3968                    capabilities: Some(vec![
3969                        fdecl::Capability::Storage(fdecl::Storage {
3970                            name: Some("data".to_string()),
3971                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
3972                            backing_dir: Some("minfs".to_string()),
3973                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
3974                            ..Default::default()
3975                        }),
3976                    ]),
3977                    uses: Some(vec![
3978                        fdecl::Use::Protocol(fdecl::UseProtocol {
3979                            dependency_type: Some(fdecl::DependencyType::Strong),
3980                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3981                            source_name: Some("a".to_string()),
3982                            target_path: Some("/svc/a".to_string()),
3983                            ..Default::default()
3984                        }),
3985                    ]),
3986                    offers: Some(vec![
3987                        fdecl::Offer::Storage(fdecl::OfferStorage {
3988                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3989                            source_name: Some("data".to_string()),
3990                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
3991                            target_name: Some("data".to_string()),
3992                            ..Default::default()
3993                        }),
3994                    ]),
3995                    children: Some(vec![
3996                        fdecl::Child {
3997                            name: Some("child".to_string()),
3998                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3999                            startup: Some(fdecl::StartupMode::Lazy),
4000                            on_terminate: None,
4001                            ..Default::default()
4002                        },
4003                    ]),
4004                    ..new_component_decl()
4005                }
4006            },
4007            result = Err(ErrorList::new(vec![
4008                Error::dependency_cycle("{{self -> capability data -> child child -> self}}"),
4009            ])),
4010        },
4011        test_validate_storage_strong_cycle_between_children => {
4012            input = {
4013                fdecl::Component {
4014                    capabilities: Some(vec![
4015                        fdecl::Capability::Storage(fdecl::Storage {
4016                            name: Some("data".to_string()),
4017                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None } )),
4018                            backing_dir: Some("minfs".to_string()),
4019                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4020                            ..Default::default()
4021                        })
4022                    ]),
4023                    offers: Some(vec![
4024                        fdecl::Offer::Storage(fdecl::OfferStorage {
4025                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4026                            source_name: Some("data".to_string()),
4027                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4028                            target_name: Some("data".to_string()),
4029                            ..Default::default()
4030                        }),
4031                        fdecl::Offer::Service(fdecl::OfferService {
4032                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4033                            source_name: Some("a".to_string()),
4034                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4035                            target_name: Some("a".to_string()),
4036                            ..Default::default()
4037                        }),
4038                    ]),
4039                    children: Some(vec![
4040                        fdecl::Child {
4041                            name: Some("child1".to_string()),
4042                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4043                            startup: Some(fdecl::StartupMode::Lazy),
4044                            on_terminate: None,
4045                            ..Default::default()
4046                        },
4047                        fdecl::Child {
4048                            name: Some("child2".to_string()),
4049                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4050                            startup: Some(fdecl::StartupMode::Lazy),
4051                            on_terminate: None,
4052                            ..Default::default()
4053                        }
4054                    ]),
4055                    ..new_component_decl()
4056                }
4057            },
4058            result = Err(ErrorList::new(vec![
4059                Error::dependency_cycle("{{child child1 -> capability data -> child child2 -> child child1}}"),
4060            ])),
4061        },
4062        test_validate_strong_cycle_between_children_through_environment_debug => {
4063            input = {
4064                fdecl::Component {
4065                    environments: Some(vec![
4066                        fdecl::Environment {
4067                            name: Some("env".to_string()),
4068                            extends: Some(fdecl::EnvironmentExtends::Realm),
4069                            debug_capabilities: Some(vec![
4070                                fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
4071                                    source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4072                                    source_name: Some("fuchsia.foo.Bar".to_string()),
4073                                    target_name: Some("fuchsia.foo.Bar".to_string()),
4074                                    ..Default::default()
4075                                }),
4076                            ]),
4077                            ..Default::default()
4078                        },
4079                    ]),
4080                    offers: Some(vec![
4081                        fdecl::Offer::Service(fdecl::OfferService {
4082                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4083                            source_name: Some("a".to_string()),
4084                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4085                            target_name: Some("a".to_string()),
4086                            ..Default::default()
4087                        }),
4088                    ]),
4089                    children: Some(vec![
4090                        fdecl::Child {
4091                            name: Some("child1".to_string()),
4092                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4093                            startup: Some(fdecl::StartupMode::Lazy),
4094                            on_terminate: None,
4095                            ..Default::default()
4096                        },
4097                        fdecl::Child {
4098                            name: Some("child2".to_string()),
4099                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4100                            startup: Some(fdecl::StartupMode::Lazy),
4101                            environment: Some("env".to_string()),
4102                            on_terminate: None,
4103                            ..Default::default()
4104                        }
4105                    ]),
4106                    ..new_component_decl()
4107                }
4108            },
4109            result = Err(ErrorList::new(vec![
4110                Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}"),
4111            ])),
4112        },
4113        test_validate_strong_cycle_between_children_through_environment_runner => {
4114            input = {
4115                fdecl::Component {
4116                    environments: Some(vec![
4117                        fdecl::Environment {
4118                            name: Some("env".to_string()),
4119                            extends: Some(fdecl::EnvironmentExtends::Realm),
4120                            runners: Some(vec![
4121                                fdecl::RunnerRegistration {
4122                                    source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4123                                    source_name: Some("coff".to_string()),
4124                                    target_name: Some("coff".to_string()),
4125                                    ..Default::default()
4126                                }
4127                            ]),
4128                            ..Default::default()
4129                        },
4130                    ]),
4131                    offers: Some(vec![
4132                        fdecl::Offer::Service(fdecl::OfferService {
4133                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4134                            source_name: Some("a".to_string()),
4135                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4136                            target_name: Some("a".to_string()),
4137                            ..Default::default()
4138                        }),
4139                    ]),
4140                    children: Some(vec![
4141                        fdecl::Child {
4142                            name: Some("child1".to_string()),
4143                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4144                            startup: Some(fdecl::StartupMode::Lazy),
4145                            on_terminate: None,
4146                            ..Default::default()
4147                        },
4148                        fdecl::Child {
4149                            name: Some("child2".to_string()),
4150                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4151                            startup: Some(fdecl::StartupMode::Lazy),
4152                            environment: Some("env".to_string()),
4153                            on_terminate: None,
4154                            ..Default::default()
4155                        }
4156                    ]),
4157                    ..new_component_decl()
4158                }
4159            },
4160            result = Err(ErrorList::new(vec![
4161                Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}"),
4162            ])),
4163        },
4164        test_validate_strong_cycle_between_children_through_environment_resolver => {
4165            input = {
4166                fdecl::Component {
4167                    environments: Some(vec![
4168                        fdecl::Environment {
4169                            name: Some("env".to_string()),
4170                            extends: Some(fdecl::EnvironmentExtends::Realm),
4171                            resolvers: Some(vec![
4172                                fdecl::ResolverRegistration {
4173                                    resolver: Some("gopher".to_string()),
4174                                    source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4175                                    scheme: Some("gopher".to_string()),
4176                                    ..Default::default()
4177                                }
4178                            ]),
4179                            ..Default::default()
4180                        },
4181                    ]),
4182                    offers: Some(vec![
4183                        fdecl::Offer::Service(fdecl::OfferService {
4184                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4185                            source_name: Some("a".to_string()),
4186                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4187                            target_name: Some("a".to_string()),
4188                            ..Default::default()
4189                        }),
4190                    ]),
4191                    children: Some(vec![
4192                        fdecl::Child {
4193                            name: Some("child1".to_string()),
4194                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4195                            startup: Some(fdecl::StartupMode::Lazy),
4196                            on_terminate: None,
4197                            ..Default::default()
4198                        },
4199                        fdecl::Child {
4200                            name: Some("child2".to_string()),
4201                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4202                            startup: Some(fdecl::StartupMode::Lazy),
4203                            environment: Some("env".to_string()),
4204                            on_terminate: None,
4205                            ..Default::default()
4206                        }
4207                    ]),
4208                    ..new_component_decl()
4209                }
4210            },
4211            result = Err(ErrorList::new(vec![
4212                Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}"),
4213            ])),
4214        },
4215        test_validate_strong_cycle_between_self_and_two_children => {
4216            input = {
4217                fdecl::Component {
4218                    capabilities: Some(vec![
4219                        fdecl::Capability::Protocol(fdecl::Protocol {
4220                            name: Some("fuchsia.foo.Bar".to_string()),
4221                            source_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4222                            ..Default::default()
4223                        })
4224                    ]),
4225                    offers: Some(vec![
4226                        fdecl::Offer::Protocol(fdecl::OfferProtocol {
4227                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4228                            source_name: Some("fuchsia.foo.Bar".to_string()),
4229                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4230                            target_name: Some("fuchsia.foo.Bar".to_string()),
4231                            dependency_type: Some(fdecl::DependencyType::Strong),
4232                            ..Default::default()
4233                        }),
4234                        fdecl::Offer::Protocol(fdecl::OfferProtocol {
4235                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4236                            source_name: Some("fuchsia.bar.Baz".to_string()),
4237                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4238                            target_name: Some("fuchsia.bar.Baz".to_string()),
4239                            dependency_type: Some(fdecl::DependencyType::Strong),
4240                            ..Default::default()
4241                        }),
4242                    ]),
4243                    uses: Some(vec![
4244                        fdecl::Use::Protocol(fdecl::UseProtocol {
4245                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child2".to_string(), collection: None})),
4246                            source_name: Some("fuchsia.baz.Foo".to_string()),
4247                            target_path: Some("/svc/fuchsia.baz.Foo".to_string()),
4248                            dependency_type: Some(fdecl::DependencyType::Strong),
4249                            ..Default::default()
4250                        }),
4251                    ]),
4252                    children: Some(vec![
4253                        fdecl::Child {
4254                            name: Some("child1".to_string()),
4255                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4256                            startup: Some(fdecl::StartupMode::Lazy),
4257                            on_terminate: None,
4258                            ..Default::default()
4259                        },
4260                        fdecl::Child {
4261                            name: Some("child2".to_string()),
4262                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4263                            startup: Some(fdecl::StartupMode::Lazy),
4264                            on_terminate: None,
4265                            ..Default::default()
4266                        }
4267                    ]),
4268                    ..new_component_decl()
4269                }
4270            },
4271            result = Err(ErrorList::new(vec![
4272                Error::dependency_cycle("{{self -> child child1 -> child child2 -> self}}"),
4273            ])),
4274        },
4275        test_validate_strong_cycle_with_self_storage => {
4276            input = {
4277                fdecl::Component {
4278                    capabilities: Some(vec![
4279                        fdecl::Capability::Storage(fdecl::Storage {
4280                            name: Some("data".to_string()),
4281                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4282                            backing_dir: Some("minfs".to_string()),
4283                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4284                            ..Default::default()
4285                        }),
4286                        fdecl::Capability::Directory(fdecl::Directory {
4287                            name: Some("minfs".to_string()),
4288                            source_path: Some("/minfs".to_string()),
4289                            rights: Some(fio::RW_STAR_DIR),
4290                            ..Default::default()
4291                        }),
4292                    ]),
4293                    offers: Some(vec![
4294                        fdecl::Offer::Storage(fdecl::OfferStorage {
4295                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4296                            source_name: Some("data".to_string()),
4297                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4298                            target_name: Some("data".to_string()),
4299                            ..Default::default()
4300                        }),
4301                    ]),
4302                    uses: Some(vec![
4303                        fdecl::Use::Protocol(fdecl::UseProtocol {
4304                            dependency_type: Some(fdecl::DependencyType::Strong),
4305                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4306                            source_name: Some("fuchsia.foo.Bar".to_string()),
4307                            target_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4308                            ..Default::default()
4309                        }),
4310                    ]),
4311                    children: Some(vec![
4312                        fdecl::Child {
4313                            name: Some("child".to_string()),
4314                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4315                            startup: Some(fdecl::StartupMode::Lazy),
4316                            ..Default::default()
4317                        },
4318                    ]),
4319                    ..new_component_decl()
4320                }
4321            },
4322            result = Err(ErrorList::new(vec![
4323                Error::dependency_cycle("{{self -> capability data -> child child -> self}}"),
4324            ])),
4325        },
4326        test_validate_strong_cycle_with_self_storage_admin_protocol => {
4327            input = {
4328                fdecl::Component {
4329                    capabilities: Some(vec![
4330                        fdecl::Capability::Storage(fdecl::Storage {
4331                            name: Some("data".to_string()),
4332                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4333                            backing_dir: Some("minfs".to_string()),
4334                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4335                            ..Default::default()
4336                        }),
4337                        fdecl::Capability::Directory(fdecl::Directory {
4338                            name: Some("minfs".to_string()),
4339                            source_path: Some("/minfs".to_string()),
4340                            rights: Some(fio::RW_STAR_DIR),
4341                            ..Default::default()
4342                        }),
4343                    ]),
4344                    offers: Some(vec![
4345                        fdecl::Offer::Protocol(fdecl::OfferProtocol {
4346                            source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef { name: "data".to_string() })),
4347                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4348                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4349                            target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4350                            dependency_type: Some(fdecl::DependencyType::Strong),
4351                            ..Default::default()
4352                        }),
4353                    ]),
4354                    uses: Some(vec![
4355                        fdecl::Use::Protocol(fdecl::UseProtocol {
4356                            dependency_type: Some(fdecl::DependencyType::Strong),
4357                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4358                            source_name: Some("fuchsia.foo.Bar".to_string()),
4359                            target_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4360                            ..Default::default()
4361                        }),
4362                    ]),
4363                    children: Some(vec![
4364                        fdecl::Child {
4365                            name: Some("child".to_string()),
4366                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4367                            startup: Some(fdecl::StartupMode::Lazy),
4368                            ..Default::default()
4369                        },
4370                    ]),
4371                    ..new_component_decl()
4372                }
4373            },
4374            result = Err(ErrorList::new(vec![
4375                Error::dependency_cycle("{{self -> capability data -> child child -> self}}"),
4376            ])),
4377        },
4378        test_validate_strong_cycle_with_dictionary => {
4379            input = fdecl::Component {
4380                offers: Some(vec![
4381                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
4382                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4383                        source_name: Some("dict".into()),
4384                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4385                            name: "a".into(),
4386                            collection: None,
4387                        })),
4388                        target_name: Some("dict".into()),
4389                        dependency_type: Some(fdecl::DependencyType::Strong),
4390                        ..Default::default()
4391                    }),
4392                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4393                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4394                            name: "b".into(),
4395                            collection: None,
4396                        })),
4397                        source_name: Some("1".into()),
4398                        target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4399                            name: "dict".into(),
4400                        })),
4401                        target_name: Some("1".into()),
4402                        dependency_type: Some(fdecl::DependencyType::Strong),
4403                        ..Default::default()
4404                    }),
4405                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4406                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4407                            name: "a".into(),
4408                            collection: None,
4409                        })),
4410                        source_name: Some("2".into()),
4411                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4412                            name: "b".into(),
4413                            collection: None,
4414                        })),
4415                        target_name: Some("2".into()),
4416                        dependency_type: Some(fdecl::DependencyType::Strong),
4417                        ..Default::default()
4418                    }),
4419                ]),
4420                children: Some(vec![
4421                    fdecl::Child {
4422                        name: Some("a".into()),
4423                        url: Some("fuchsia-pkg://child".into()),
4424                        startup: Some(fdecl::StartupMode::Lazy),
4425                        ..Default::default()
4426                    },
4427                    fdecl::Child {
4428                        name: Some("b".into()),
4429                        url: Some("fuchsia-pkg://child".into()),
4430                        startup: Some(fdecl::StartupMode::Lazy),
4431                        ..Default::default()
4432                    },
4433                ]),
4434                capabilities: Some(vec![
4435                    fdecl::Capability::Dictionary(fdecl::Dictionary {
4436                        name: Some("dict".into()),
4437                        ..Default::default()
4438                    }),
4439                ]),
4440                ..Default::default()
4441            },
4442            result = Err(ErrorList::new(vec![
4443                Error::dependency_cycle("{{child a -> child b -> capability dict -> child a}}"),
4444            ])),
4445        },
4446        test_validate_strong_cycle_with_dictionary_indirect => {
4447            input = fdecl::Component {
4448                offers: Some(vec![
4449                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4450                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4451                        source_name: Some("3".into()),
4452                        source_dictionary: Some("dict".into()),
4453                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4454                            name: "a".into(),
4455                            collection: None,
4456                        })),
4457                        target_name: Some("3".into()),
4458                        dependency_type: Some(fdecl::DependencyType::Strong),
4459                        ..Default::default()
4460                    }),
4461                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4462                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4463                            name: "b".into(),
4464                            collection: None,
4465                        })),
4466                        source_name: Some("1".into()),
4467                        target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4468                            name: "dict".into(),
4469                        })),
4470                        target_name: Some("1".into()),
4471                        dependency_type: Some(fdecl::DependencyType::Strong),
4472                        ..Default::default()
4473                    }),
4474                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4475                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4476                            name: "a".into(),
4477                            collection: None,
4478                        })),
4479                        source_name: Some("2".into()),
4480                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4481                            name: "b".into(),
4482                            collection: None,
4483                        })),
4484                        target_name: Some("2".into()),
4485                        dependency_type: Some(fdecl::DependencyType::Strong),
4486                        ..Default::default()
4487                    }),
4488                ]),
4489                children: Some(vec![
4490                    fdecl::Child {
4491                        name: Some("a".into()),
4492                        url: Some("fuchsia-pkg://child".into()),
4493                        startup: Some(fdecl::StartupMode::Lazy),
4494                        ..Default::default()
4495                    },
4496                    fdecl::Child {
4497                        name: Some("b".into()),
4498                        url: Some("fuchsia-pkg://child".into()),
4499                        startup: Some(fdecl::StartupMode::Lazy),
4500                        ..Default::default()
4501                    },
4502                ]),
4503                capabilities: Some(vec![
4504                    fdecl::Capability::Dictionary(fdecl::Dictionary {
4505                        name: Some("dict".into()),
4506                        ..Default::default()
4507                    }),
4508                ]),
4509                ..Default::default()
4510            },
4511            result = Err(ErrorList::new(vec![
4512                Error::dependency_cycle("{{child a -> child b -> capability dict -> child a}}"),
4513            ])),
4514        },
4515        test_validate_use_dependency_cycle_with_dictionary => {
4516            input = fdecl::Component {
4517                offers: Some(vec![
4518                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
4519                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4520                        source_name: Some("dict".into()),
4521                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4522                            name: "a".into(),
4523                            collection: None,
4524                        })),
4525                        target_name: Some("dict".into()),
4526                        dependency_type: Some(fdecl::DependencyType::Strong),
4527                        ..Default::default()
4528                    }),
4529                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4530                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4531                        source_name: Some("1".into()),
4532                        target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4533                            name: "dict".into(),
4534                        })),
4535                        target_name: Some("1".into()),
4536                        dependency_type: Some(fdecl::DependencyType::Strong),
4537                        ..Default::default()
4538                    }),
4539                ]),
4540                children: Some(vec![
4541                    fdecl::Child {
4542                        name: Some("a".into()),
4543                        url: Some("fuchsia-pkg://child".into()),
4544                        startup: Some(fdecl::StartupMode::Lazy),
4545                        ..Default::default()
4546                    },
4547                ]),
4548                uses: Some(vec![
4549                        fdecl::Use::Protocol(fdecl::UseProtocol {
4550                            dependency_type: Some(fdecl::DependencyType::Strong),
4551                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "a".to_string(), collection: None})),
4552                            source_name: Some("2".to_string()),
4553                            target_path: Some("/svc/foo".into()),
4554                            ..Default::default()
4555                        }),
4556                ]),
4557                capabilities: Some(vec![
4558                    fdecl::Capability::Dictionary(fdecl::Dictionary {
4559                        name: Some("dict".into()),
4560                        ..Default::default()
4561                    }),
4562                    fdecl::Capability::Protocol(fdecl::Protocol {
4563                        name: Some("1".to_string()),
4564                        source_path: Some("/path".to_string()),
4565                        ..Default::default()
4566                    }),
4567                ]),
4568                ..Default::default()
4569            },
4570            result = Err(ErrorList::new(vec![
4571                Error::dependency_cycle("{{self -> capability dict -> child a -> self}}"),
4572            ])),
4573        },
4574        test_validate_use_from_child_offer_to_child_weak_cycle => {
4575            input = {
4576                fdecl::Component {
4577                    capabilities: Some(vec![
4578                        fdecl::Capability::Service(fdecl::Service {
4579                            name: Some("a".to_string()),
4580                            source_path: Some("/a".to_string()),
4581                            ..Default::default()
4582                        })]),
4583                    uses: Some(vec![
4584                        fdecl::Use::Protocol(fdecl::UseProtocol {
4585                            dependency_type: Some(fdecl::DependencyType::Weak),
4586                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4587                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4588                            target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
4589                            ..Default::default()
4590                        }),
4591                        fdecl::Use::Service(fdecl::UseService {
4592                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4593                            source_name: Some("service_name".to_string()),
4594                            target_path: Some("/svc/service_name".to_string()),
4595                            dependency_type: Some(fdecl::DependencyType::Weak),
4596                            ..Default::default()
4597                        }),
4598                        fdecl::Use::Directory(fdecl::UseDirectory {
4599                            dependency_type: Some(fdecl::DependencyType::Weak),
4600                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4601                            source_name: Some("DirectoryName".to_string()),
4602                            target_path: Some("/data/DirectoryName".to_string()),
4603                            rights: Some(fio::Operations::CONNECT),
4604                            subdir: None,
4605                            ..Default::default()
4606                        }),
4607                    ]),
4608                    offers: Some(vec![
4609                        fdecl::Offer::Service(fdecl::OfferService {
4610                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4611                            source_name: Some("a".to_string()),
4612                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4613                            target_name: Some("a".to_string()),
4614                            ..Default::default()
4615                        })
4616                    ]),
4617                    children: Some(vec![
4618                        fdecl::Child {
4619                            name: Some("child".to_string()),
4620                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4621                            startup: Some(fdecl::StartupMode::Lazy),
4622                            on_terminate: None,
4623                            ..Default::default()
4624                        }
4625                    ]),
4626                    ..new_component_decl()
4627                }
4628            },
4629            result = Ok(()),
4630        },
4631        test_validate_expose_from_self_to_framework_and_parent => {
4632            input = {
4633                fdecl::Component {
4634                    capabilities: Some(vec![
4635                        fdecl::Capability::Protocol(fdecl::Protocol {
4636                            name: Some("a".to_string()),
4637                            source_path: Some("/a".to_string()),
4638                            ..Default::default()
4639                        }),
4640                    ]),
4641                    exposes: Some(vec![
4642                        fdecl::Expose::Protocol(fdecl::ExposeProtocol {
4643                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4644                            source_name: Some("a".to_string()),
4645                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4646                            target_name: Some("a".to_string()),
4647                            ..Default::default()
4648                        }),
4649                        fdecl::Expose::Protocol(fdecl::ExposeProtocol {
4650                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4651                            source_name: Some("a".to_string()),
4652                            target: Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})),
4653                            target_name: Some("a".to_string()),
4654                            ..Default::default()
4655                        }),
4656                    ]),
4657                    ..new_component_decl()
4658                }
4659            },
4660            result = Ok(()),
4661        },
4662        test_validate_use_from_not_child_weak => {
4663            input = {
4664                fdecl::Component {
4665                    uses: Some(vec![
4666                        fdecl::Use::Protocol(fdecl::UseProtocol {
4667                            dependency_type: Some(fdecl::DependencyType::Weak),
4668                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4669                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4670                            target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
4671                            ..Default::default()
4672                        }),
4673                    ]),
4674                    ..new_component_decl()
4675                }
4676            },
4677            result = Err(ErrorList::new(vec![
4678                Error::invalid_field(DeclType::UseProtocol, "dependency_type"),
4679            ])),
4680        },
4681        test_validate_event_stream_offer_valid_decls => {
4682            input = {
4683                let mut decl = new_component_decl();
4684                decl.offers = Some(vec![
4685                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4686                        source_name: Some("stopped".to_string()),
4687                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4688                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4689                        target_name: Some("stopped".to_string()),
4690                        ..Default::default()
4691                    }),
4692                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4693                        source_name: Some("started".to_string()),
4694                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4695                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4696                        target_name: Some("started".to_string()),
4697                        ..Default::default()
4698                    }),
4699                ]);
4700                decl.children = Some(vec![fdecl::Child{
4701                    name: Some("test".to_string()),
4702                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4703                    startup: Some(fdecl::StartupMode::Lazy),
4704                    on_terminate: None,
4705                    environment: None,
4706                    ..Default::default()
4707                },
4708                fdecl::Child{
4709                    name: Some("test2".to_string()),
4710                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4711                    startup: Some(fdecl::StartupMode::Lazy),
4712                    on_terminate: None,
4713                    environment: None,
4714                    ..Default::default()
4715                }
4716                ]);
4717                decl
4718            },
4719            result = Ok(()),
4720        },
4721        test_validate_event_stream_offer_to_framework_invalid => {
4722            input = {
4723                let mut decl = new_component_decl();
4724                decl.offers = Some(vec![
4725                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4726                        source_name: Some("stopped".to_string()),
4727                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4728                        target: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
4729                        target_name: Some("stopped".to_string()),
4730                        ..Default::default()
4731                    }),
4732                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4733                        source_name: Some("started".to_string()),
4734                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4735                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4736                        target_name: Some("started".to_string()),
4737                        ..Default::default()
4738                    }),
4739                ]);
4740                decl.children = Some(vec![fdecl::Child{
4741                    name: Some("test".to_string()),
4742                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4743                    startup: Some(fdecl::StartupMode::Lazy),
4744                    on_terminate: None,
4745                    environment: None,
4746                    ..Default::default()
4747                },
4748                fdecl::Child{
4749                    name: Some("test2".to_string()),
4750                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4751                    startup: Some(fdecl::StartupMode::Lazy),
4752                    on_terminate: None,
4753                    environment: None,
4754                    ..Default::default()
4755                }
4756                ]);
4757                decl
4758            },
4759            result = Err(ErrorList::new(vec![
4760                Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "target".to_string() }),
4761                Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "target".to_string() }),
4762            ])),
4763        },
4764        test_validate_event_stream_offer_to_scope_zero_length_invalid => {
4765            input = {
4766                let mut decl = new_component_decl();
4767                decl.offers = Some(vec![
4768                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4769                        source_name: Some("started".to_string()),
4770                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4771                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4772                        scope: Some(vec![]),
4773                        target_name: Some("started".to_string()),
4774                        ..Default::default()
4775                    }),
4776                ]);
4777                decl.children = Some(vec![fdecl::Child{
4778                    name: Some("test".to_string()),
4779                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4780                    startup: Some(fdecl::StartupMode::Lazy),
4781                    on_terminate: None,
4782                    environment: None,
4783                    ..Default::default()
4784                },
4785                fdecl::Child{
4786                    name: Some("test2".to_string()),
4787                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4788                    startup: Some(fdecl::StartupMode::Lazy),
4789                    on_terminate: None,
4790                    environment: None,
4791                    ..Default::default()
4792                }
4793                ]);
4794                decl
4795            },
4796            result = Err(ErrorList::new(vec![
4797                Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "scope".to_string() }),
4798            ])),
4799        },
4800        test_validate_event_stream_offer_to_scope_framework_invalid => {
4801            input = {
4802                let mut decl = new_component_decl();
4803                decl.offers = Some(vec![
4804                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4805                        source_name: Some("started".to_string()),
4806                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4807                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4808                        scope: Some(vec![fdecl::Ref::Framework(fdecl::FrameworkRef{})]),
4809                        target_name: Some("started".to_string()),
4810                        ..Default::default()
4811                    }),
4812                ]);
4813                decl.children = Some(vec![fdecl::Child{
4814                    name: Some("test".to_string()),
4815                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4816                    startup: Some(fdecl::StartupMode::Lazy),
4817                    on_terminate: None,
4818                    environment: None,
4819                    ..Default::default()
4820                },
4821                fdecl::Child{
4822                    name: Some("test2".to_string()),
4823                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4824                    startup: Some(fdecl::StartupMode::Lazy),
4825                    on_terminate: None,
4826                    environment: None,
4827                    ..Default::default()
4828                }
4829                ]);
4830                decl
4831            },
4832            result = Err(ErrorList::new(vec![
4833                Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "scope".to_string() }),
4834            ])),
4835        },
4836        test_validate_event_stream_offer_to_scope_valid => {
4837            input = {
4838                let mut decl = new_component_decl();
4839                decl.offers = Some(vec![
4840                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4841                        source_name: Some("started".to_string()),
4842                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4843                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4844                        scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
4845                        target_name: Some("started".to_string()),
4846                        ..Default::default()
4847                    }),
4848                ]);
4849                decl.children = Some(vec![fdecl::Child{
4850                    name: Some("test".to_string()),
4851                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4852                    startup: Some(fdecl::StartupMode::Lazy),
4853                    on_terminate: None,
4854                    environment: None,
4855                    ..Default::default()
4856                },
4857                fdecl::Child{
4858                    name: Some("test2".to_string()),
4859                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4860                    startup: Some(fdecl::StartupMode::Lazy),
4861                    on_terminate: None,
4862                    environment: None,
4863                    ..Default::default()
4864                }
4865                ]);
4866                decl
4867            },
4868            result = Ok(()),
4869        },
4870        test_validate_event_stream_offer_to_scope_with_capability_requested => {
4871            input = {
4872                let mut decl = new_component_decl();
4873                decl.offers = Some(vec![
4874                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4875                        source_name: Some("capability_requested".to_string()),
4876                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4877                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4878                        scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
4879                        target_name: Some("started".to_string()),
4880                        ..Default::default()
4881                    }),
4882                ]);
4883                decl.children = Some(vec![fdecl::Child{
4884                    name: Some("test".to_string()),
4885                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4886                    startup: Some(fdecl::StartupMode::Lazy),
4887                    on_terminate: None,
4888                    environment: None,
4889                    ..Default::default()
4890                },
4891                fdecl::Child{
4892                    name: Some("test2".to_string()),
4893                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4894                    startup: Some(fdecl::StartupMode::Lazy),
4895                    on_terminate: None,
4896                    environment: None,
4897                    ..Default::default()
4898                }
4899                ]);
4900                decl
4901            },
4902            result = Ok(()),
4903        },
4904        test_validate_event_stream_offer_with_no_source_name_invalid => {
4905            input = {
4906                let mut decl = new_component_decl();
4907                decl.offers = Some(vec![
4908                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4909                        source_name: None,
4910                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4911                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4912                        scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
4913                        target_name: Some("started".to_string()),
4914                        ..Default::default()
4915                    }),
4916                ]);
4917                decl.children = Some(vec![fdecl::Child{
4918                    name: Some("test".to_string()),
4919                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4920                    startup: Some(fdecl::StartupMode::Lazy),
4921                    on_terminate: None,
4922                    environment: None,
4923                    ..Default::default()
4924                },
4925                fdecl::Child{
4926                    name: Some("test2".to_string()),
4927                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4928                    startup: Some(fdecl::StartupMode::Lazy),
4929                    on_terminate: None,
4930                    environment: None,
4931                    ..Default::default()
4932                }
4933                ]);
4934                decl
4935            },
4936            result = Err(ErrorList::new(vec![
4937                Error::MissingField(DeclField { decl: DeclType::OfferEventStream, field: "source_name".to_string() }),
4938            ])),
4939        },
4940        test_validate_event_stream_offer_invalid_source => {
4941            input = {
4942                let mut decl = new_component_decl();
4943                decl.offers = Some(vec![
4944                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4945                        source_name: Some("stopped".to_string()),
4946                        source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
4947                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4948                        target_name: Some("stopped".to_string()),
4949                        ..Default::default()
4950                    }),
4951                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4952                        source_name: Some("started".to_string()),
4953                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4954                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4955                        target_name: Some("started".to_string()),
4956                        ..Default::default()
4957                    }),
4958                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4959                        source_name: Some("capability_requested".to_string()),
4960                        source: Some(fdecl::Ref::Debug(fdecl::DebugRef{})),
4961                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4962                        target_name: Some("capability_requested".to_string()),
4963                        ..Default::default()
4964                    }),
4965                ]);
4966                decl.children = Some(vec![fdecl::Child{
4967                    name: Some("test".to_string()),
4968                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4969                    startup: Some(fdecl::StartupMode::Lazy),
4970                    on_terminate: None,
4971                    environment: None,
4972                    ..Default::default()
4973                },
4974                fdecl::Child{
4975                    name: Some("test2".to_string()),
4976                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4977                    startup: Some(fdecl::StartupMode::Lazy),
4978                    on_terminate: None,
4979                    environment: None,
4980                    ..Default::default()
4981                }
4982                ]);
4983                decl
4984            },
4985            result = Err(ErrorList::new(vec![
4986                Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "source".to_string() }),
4987            ])),
4988        },
4989
4990        test_validate_event_stream_offer_missing_source => {
4991            input = {
4992                let mut decl = new_component_decl();
4993                decl.offers = Some(vec![
4994                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4995                        source_name: Some("stopped".to_string()),
4996                        source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
4997                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4998                        target_name: Some("stopped".to_string()),
4999                        ..Default::default()
5000                    }),
5001                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
5002                        source_name: Some("started".to_string()),
5003                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5004                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5005                        target_name: Some("started".to_string()),
5006                        ..Default::default()
5007                    }),
5008                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
5009                        source_name: Some("capability_requested".to_string()),
5010                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5011                        target_name: Some("capability_requested".to_string()),
5012                        ..Default::default()
5013                    }),
5014                ]);
5015                decl.children = Some(vec![fdecl::Child{
5016                    name: Some("test".to_string()),
5017                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5018                    startup: Some(fdecl::StartupMode::Lazy),
5019                    on_terminate: None,
5020                    environment: None,
5021                    ..Default::default()
5022                },
5023                fdecl::Child{
5024                    name: Some("test2".to_string()),
5025                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
5026                    startup: Some(fdecl::StartupMode::Lazy),
5027                    on_terminate: None,
5028                    environment: None,
5029                    ..Default::default()
5030                }
5031                ]);
5032                decl
5033            },
5034            result = Err(ErrorList::new(vec![
5035                Error::MissingField(DeclField { decl: DeclType::OfferEventStream, field: "source".to_string() }),
5036            ])),
5037        },
5038        test_validate_event_stream_must_have_target_path => {
5039            input = {
5040                let mut decl = new_component_decl();
5041                decl.uses = Some(vec![
5042                    fdecl::Use::EventStream(fdecl::UseEventStream {
5043                        source_name: Some("bar".to_string()),
5044                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5045                        ..Default::default()
5046                    }),
5047                ]);
5048                decl
5049            },
5050            result = Err(ErrorList::new(vec![
5051                Error::MissingField(DeclField { decl: DeclType::UseEventStream, field: "target_path".to_string() })
5052            ])),
5053        },
5054        test_validate_event_stream_must_have_source_names => {
5055            input = {
5056                let mut decl = new_component_decl();
5057                decl.uses = Some(vec![
5058                    fdecl::Use::EventStream(fdecl::UseEventStream {
5059                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5060                        target_path: Some("/svc/something".to_string()),
5061                        ..Default::default()
5062                    }),
5063                ]);
5064                decl
5065            },
5066            result = Err(ErrorList::new(vec![
5067                Error::MissingField(DeclField { decl: DeclType::UseEventStream, field: "source_name".to_string() })
5068            ])),
5069        },
5070        test_validate_event_stream_scope_must_be_child_or_collection => {
5071            input = {
5072                let mut decl = new_component_decl();
5073                decl.uses = Some(vec![
5074                    fdecl::Use::EventStream(fdecl::UseEventStream {
5075                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5076                        target_path: Some("/svc/something".to_string()),
5077                        source_name: Some("some_source".to_string()),
5078                        scope: Some(vec![fdecl::Ref::Framework(fdecl::FrameworkRef{})]),
5079                        ..Default::default()
5080                    }),
5081                ]);
5082                decl
5083            },
5084            result = Err(ErrorList::new(vec![
5085                Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "scope".to_string() })
5086            ])),
5087        },
5088        test_validate_event_stream_source_must_be_parent_or_child => {
5089            input = {
5090                let mut decl = new_component_decl();
5091                decl.uses = Some(vec![
5092                    fdecl::Use::EventStream(fdecl::UseEventStream {
5093                        source: Some(fdecl::Ref::Debug(fdecl::DebugRef{})),
5094                        target_path: Some("/svc/something".to_string()),
5095                        source_name: Some("some_source".to_string()),
5096                        scope: Some(vec![]),
5097                        ..Default::default()
5098                    }),
5099                    fdecl::Use::EventStream(fdecl::UseEventStream {
5100                        source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
5101                        target_path: Some("/svc/something_else".to_string()),
5102                        source_name: Some("some_source".to_string()),
5103                        scope: Some(vec![]),
5104                        ..Default::default()
5105                    }),
5106                    fdecl::Use::EventStream(fdecl::UseEventStream {
5107                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5108                        target_path: Some("/svc/yet_something_else".to_string()),
5109                        source_name: Some("some_source".to_string()),
5110                        scope: Some(vec![]),
5111                        ..Default::default()
5112                    }),
5113                ]);
5114                decl
5115            },
5116            result = Err(ErrorList::new(vec![
5117                Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() }),
5118                Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() }),
5119                Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() })
5120            ])),
5121        },
5122        test_validate_no_runner => {
5123            input = {
5124                let mut decl = new_component_decl();
5125                decl.program = Some(fdecl::Program {
5126                    runner: None,
5127                    info: Some(fdata::Dictionary {
5128                        entries: None,
5129                        ..Default::default()
5130                    }),
5131                    ..Default::default()
5132                });
5133                decl
5134            },
5135            result = Err(ErrorList::new(vec![
5136                Error::MissingRunner,
5137            ])),
5138        },
5139        test_validate_uses_runner => {
5140            input = {
5141                let mut decl = new_component_decl();
5142                decl.program = Some(fdecl::Program {
5143                    runner: None,
5144                    info: Some(fdata::Dictionary {
5145                        entries: None,
5146                        ..Default::default()
5147                    }),
5148                    ..Default::default()
5149                });
5150                decl.uses = Some(vec![
5151                    fdecl::Use::Runner(fdecl::UseRunner {
5152                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5153                        source_name: Some("runner".to_string()),
5154                        ..Default::default()
5155                    }),
5156                ]);
5157                decl
5158            },
5159            result = Ok(()),
5160        },
5161        test_validate_program_and_uses_runner_match => {
5162            input = {
5163                let mut decl = new_component_decl();
5164                decl.program = Some(fdecl::Program {
5165                    runner: Some("runner".to_string()),
5166                    info: Some(fdata::Dictionary {
5167                        entries: None,
5168                        ..Default::default()
5169                    }),
5170                    ..Default::default()
5171                });
5172                decl.uses = Some(vec![
5173                    fdecl::Use::Runner(fdecl::UseRunner {
5174                        source: Some(fdecl::Ref::Environment(fdecl::EnvironmentRef {})),
5175                        source_name: Some("runner".to_string()),
5176                        ..Default::default()
5177                    }),
5178                ]);
5179                decl
5180            },
5181            result = Ok(()),
5182        },
5183        test_validate_runner_names_conflict => {
5184            input = {
5185                let mut decl = new_component_decl();
5186                decl.program = Some(fdecl::Program {
5187                    runner: Some("runner".to_string()),
5188                    info: Some(fdata::Dictionary {
5189                        entries: None,
5190                        ..Default::default()
5191                    }),
5192                    ..Default::default()
5193                });
5194                decl.uses = Some(vec![
5195                    fdecl::Use::Runner(fdecl::UseRunner {
5196                        source: Some(fdecl::Ref::Environment(fdecl::EnvironmentRef {})),
5197                        source_name: Some("other.runner".to_string()),
5198                        ..Default::default()
5199                    }),
5200                ]);
5201                decl
5202            },
5203            result = Err(ErrorList::new(vec![
5204                Error::ConflictingRunners,
5205            ])),
5206        },
5207        test_validate_uses_runner_not_environement => {
5208            input = {
5209                let mut decl = new_component_decl();
5210                decl.program = Some(fdecl::Program {
5211                    runner: Some("runner".to_string()),
5212                    info: Some(fdata::Dictionary {
5213                        entries: None,
5214                        ..Default::default()
5215                    }),
5216                    ..Default::default()
5217                });
5218                decl.uses = Some(vec![
5219                    fdecl::Use::Runner(fdecl::UseRunner {
5220                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5221                        source_name: Some("runner".to_string()),
5222                        ..Default::default()
5223                    }),
5224                ]);
5225                decl
5226            },
5227            result = Err(ErrorList::new(vec![
5228                Error::ConflictingRunners,
5229            ])),
5230        },
5231        test_validate_uses_long_identifiers => {
5232            input = {
5233                let mut decl = new_component_decl();
5234                decl.program = Some(fdecl::Program {
5235                    runner: Some("elf".to_string()),
5236                    info: Some(fdata::Dictionary {
5237                        entries: None,
5238                        ..Default::default()
5239                    }),
5240                    ..Default::default()
5241                });
5242                decl.uses = Some(vec![
5243                    fdecl::Use::Service(fdecl::UseService {
5244                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5245                        source_name: Some(format!("{}", "a".repeat(256))),
5246                        target_path: Some("/a".repeat(2048)),
5247                        dependency_type: Some(fdecl::DependencyType::Strong),
5248                        ..Default::default()
5249                    }),
5250                    fdecl::Use::Protocol(fdecl::UseProtocol {
5251                        dependency_type: Some(fdecl::DependencyType::Strong),
5252                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5253                        source_name: Some(format!("{}", "a".repeat(256))),
5254                        target_path: Some("/b".repeat(2048)),
5255                        ..Default::default()
5256                    }),
5257                    fdecl::Use::Directory(fdecl::UseDirectory {
5258                        dependency_type: Some(fdecl::DependencyType::Strong),
5259                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5260                        source_name: Some(format!("{}", "a".repeat(256))),
5261                        target_path: Some("/c".repeat(2048)),
5262                        rights: Some(fio::Operations::CONNECT),
5263                        subdir: None,
5264                        ..Default::default()
5265                    }),
5266                    fdecl::Use::Storage(fdecl::UseStorage {
5267                        source_name: Some("cache".to_string()),
5268                        target_path: Some("/d".repeat(2048)),
5269                        ..Default::default()
5270                    }),
5271                ]);
5272                decl
5273            },
5274            result = Err(ErrorList::new(vec![
5275                Error::field_too_long(DeclType::UseService, "source_name"),
5276                Error::field_too_long(DeclType::UseService, "target_path"),
5277                Error::field_too_long(DeclType::UseProtocol, "source_name"),
5278                Error::field_too_long(DeclType::UseProtocol, "target_path"),
5279                Error::field_too_long(DeclType::UseDirectory, "source_name"),
5280                Error::field_too_long(DeclType::UseDirectory, "target_path"),
5281                Error::field_too_long(DeclType::UseStorage, "target_path"),
5282            ])),
5283        },
5284        test_validate_conflicting_paths => {
5285            input = {
5286                let mut decl = new_component_decl();
5287                decl.uses = Some(vec![
5288                    fdecl::Use::Service(fdecl::UseService {
5289                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5290                        source_name: Some("foo".to_string()),
5291                        target_path: Some("/bar".to_string()),
5292                        dependency_type: Some(fdecl::DependencyType::Strong),
5293                        ..Default::default()
5294                    }),
5295                    fdecl::Use::Protocol(fdecl::UseProtocol {
5296                        dependency_type: Some(fdecl::DependencyType::Strong),
5297                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5298                        source_name: Some("space".to_string()),
5299                        target_path: Some("/bar".to_string()),
5300                        ..Default::default()
5301                    }),
5302                    fdecl::Use::Directory(fdecl::UseDirectory {
5303                        dependency_type: Some(fdecl::DependencyType::Strong),
5304                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5305                        source_name: Some("crow".to_string()),
5306                        target_path: Some("/bar".to_string()),
5307                        rights: Some(fio::Operations::CONNECT),
5308                        subdir: None,
5309                        ..Default::default()
5310                    }),
5311                ]);
5312                decl
5313            },
5314            result = Err(ErrorList::new(vec![
5315                Error::duplicate_field(DeclType::UseProtocol, "target_path", "/bar"),
5316                Error::duplicate_field(DeclType::UseDirectory, "target_path", "/bar"),
5317            ])),
5318        },
5319        // exposes
5320        test_validate_exposes_empty => {
5321            input = {
5322                let mut decl = new_component_decl();
5323                decl.exposes = Some(vec![
5324                    fdecl::Expose::Service(fdecl::ExposeService {
5325                        source: None,
5326                        source_name: None,
5327                        target_name: None,
5328                        target: None,
5329                        ..Default::default()
5330                    }),
5331                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5332                        source: None,
5333                        source_name: None,
5334                        target_name: None,
5335                        target: None,
5336                        ..Default::default()
5337                    }),
5338                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5339                        source: None,
5340                        source_name: None,
5341                        target_name: None,
5342                        target: None,
5343                        rights: None,
5344                        subdir: None,
5345                        ..Default::default()
5346                    }),
5347                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5348                        source: None,
5349                        source_name: None,
5350                        target: None,
5351                        target_name: None,
5352                        ..Default::default()
5353                    }),
5354                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5355                        source: None,
5356                        source_name: None,
5357                        target: None,
5358                        target_name: None,
5359                        ..Default::default()
5360                    }),
5361                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5362                        ..Default::default()
5363                    }),
5364                ]);
5365                decl
5366            },
5367            result = Err(ErrorList::new(vec![
5368                Error::missing_field(DeclType::ExposeService, "source"),
5369                Error::missing_field(DeclType::ExposeService, "target"),
5370                Error::missing_field(DeclType::ExposeService, "source_name"),
5371                Error::missing_field(DeclType::ExposeService, "target_name"),
5372                Error::missing_field(DeclType::ExposeProtocol, "source"),
5373                Error::missing_field(DeclType::ExposeProtocol, "target"),
5374                Error::missing_field(DeclType::ExposeProtocol, "source_name"),
5375                Error::missing_field(DeclType::ExposeProtocol, "target_name"),
5376                Error::missing_field(DeclType::ExposeDirectory, "source"),
5377                Error::missing_field(DeclType::ExposeDirectory, "target"),
5378                Error::missing_field(DeclType::ExposeDirectory, "source_name"),
5379                Error::missing_field(DeclType::ExposeDirectory, "target_name"),
5380                Error::missing_field(DeclType::ExposeRunner, "source"),
5381                Error::missing_field(DeclType::ExposeRunner, "target"),
5382                Error::missing_field(DeclType::ExposeRunner, "source_name"),
5383                Error::missing_field(DeclType::ExposeRunner, "target_name"),
5384                Error::missing_field(DeclType::ExposeResolver, "source"),
5385                Error::missing_field(DeclType::ExposeResolver, "target"),
5386                Error::missing_field(DeclType::ExposeResolver, "source_name"),
5387                Error::missing_field(DeclType::ExposeResolver, "target_name"),
5388                Error::missing_field(DeclType::ExposeDictionary, "source"),
5389                Error::missing_field(DeclType::ExposeDictionary, "target"),
5390                Error::missing_field(DeclType::ExposeDictionary, "source_name"),
5391                Error::missing_field(DeclType::ExposeDictionary, "target_name"),
5392            ])),
5393        },
5394        test_validate_exposes_extraneous => {
5395            input = {
5396                let mut decl = new_component_decl();
5397                decl.exposes = Some(vec![
5398                    fdecl::Expose::Service(fdecl::ExposeService {
5399                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5400                            name: "logger".to_string(),
5401                            collection: Some("modular".to_string()),
5402                        })),
5403                        source_name: Some("logger".to_string()),
5404                        target_name: Some("logger".to_string()),
5405                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5406                        ..Default::default()
5407                    }),
5408                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5409                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5410                            name: "logger".to_string(),
5411                            collection: Some("modular".to_string()),
5412                        })),
5413                        source_name: Some("legacy_logger".to_string()),
5414                        target_name: Some("legacy_logger".to_string()),
5415                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5416                        ..Default::default()
5417                    }),
5418                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5419                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5420                            name: "netstack".to_string(),
5421                            collection: Some("modular".to_string()),
5422                        })),
5423                        source_name: Some("data".to_string()),
5424                        target_name: Some("data".to_string()),
5425                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5426                        rights: Some(fio::Operations::CONNECT),
5427                        subdir: None,
5428                        ..Default::default()
5429                    }),
5430                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5431                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5432                            name: "netstack".to_string(),
5433                            collection: Some("modular".to_string()),
5434                        })),
5435                        source_name: Some("elf".to_string()),
5436                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5437                        target_name: Some("elf".to_string()),
5438                        ..Default::default()
5439                    }),
5440                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5441                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5442                            name: "netstack".to_string(),
5443                            collection: Some("modular".to_string()),
5444                        })),
5445                        source_name: Some("pkg".to_string()),
5446                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5447                        target_name: Some("pkg".to_string()),
5448                        ..Default::default()
5449                    }),
5450                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5451                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5452                            name: "netstack".to_string(),
5453                            collection: Some("modular".to_string()),
5454                        })),
5455                        source_name: Some("dict".to_string()),
5456                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5457                        target_name: Some("dict".to_string()),
5458                        ..Default::default()
5459                    }),
5460                ]);
5461                decl
5462            },
5463            result = Err(ErrorList::new(vec![
5464                Error::extraneous_field(DeclType::ExposeService, "source.child.collection"),
5465                Error::extraneous_field(DeclType::ExposeProtocol, "source.child.collection"),
5466                Error::extraneous_field(DeclType::ExposeDirectory, "source.child.collection"),
5467                Error::extraneous_field(DeclType::ExposeRunner, "source.child.collection"),
5468                Error::extraneous_field(DeclType::ExposeResolver, "source.child.collection"),
5469                Error::extraneous_field(DeclType::ExposeDictionary, "source.child.collection"),
5470            ])),
5471        },
5472        test_validate_exposes_invalid_identifiers => {
5473            input = {
5474                let mut decl = new_component_decl();
5475                decl.exposes = Some(vec![
5476                    fdecl::Expose::Service(fdecl::ExposeService {
5477                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5478                            name: "^bad".to_string(),
5479                            collection: None,
5480                        })),
5481                        source_name: Some("foo/".to_string()),
5482                        target_name: Some("/".to_string()),
5483                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5484                        ..Default::default()
5485                    }),
5486                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5487                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5488                            name: "^bad".to_string(),
5489                            collection: None,
5490                        })),
5491                        source_name: Some("foo/".to_string()),
5492                        target_name: Some("/".to_string()),
5493                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5494                        ..Default::default()
5495                    }),
5496                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5497                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5498                            name: "^bad".to_string(),
5499                            collection: None,
5500                        })),
5501                        source_name: Some("foo/".to_string()),
5502                        target_name: Some("/".to_string()),
5503                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5504                        rights: Some(fio::Operations::CONNECT),
5505                        subdir: Some("/foo".to_string()),
5506                        ..Default::default()
5507                    }),
5508                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5509                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5510                            name: "^bad".to_string(),
5511                            collection: None,
5512                        })),
5513                        source_name: Some("/path".to_string()),
5514                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5515                        target_name: Some("elf!".to_string()),
5516                        ..Default::default()
5517                    }),
5518                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5519                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5520                            name: "^bad".to_string(),
5521                            collection: None,
5522                        })),
5523                        source_name: Some("/path".to_string()),
5524                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5525                        target_name: Some("pkg!".to_string()),
5526                        ..Default::default()
5527                    }),
5528                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5529                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5530                            name: "^bad".to_string(),
5531                            collection: None,
5532                        })),
5533                        source_name: Some("/path".to_string()),
5534                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5535                        target_name: Some("pkg!".to_string()),
5536                        ..Default::default()
5537                    }),
5538                ]);
5539                decl
5540            },
5541            result = Err(ErrorList::new(vec![
5542                Error::invalid_field(DeclType::ExposeService, "source.child.name"),
5543                Error::invalid_field(DeclType::ExposeService, "source_name"),
5544                Error::invalid_field(DeclType::ExposeService, "target_name"),
5545                Error::invalid_field(DeclType::ExposeProtocol, "source.child.name"),
5546                Error::invalid_field(DeclType::ExposeProtocol, "source_name"),
5547                Error::invalid_field(DeclType::ExposeProtocol, "target_name"),
5548                Error::invalid_field(DeclType::ExposeDirectory, "source.child.name"),
5549                Error::invalid_field(DeclType::ExposeDirectory, "source_name"),
5550                Error::invalid_field(DeclType::ExposeDirectory, "target_name"),
5551                Error::invalid_field(DeclType::ExposeDirectory, "subdir"),
5552                Error::invalid_field(DeclType::ExposeRunner, "source.child.name"),
5553                Error::invalid_field(DeclType::ExposeRunner, "source_name"),
5554                Error::invalid_field(DeclType::ExposeRunner, "target_name"),
5555                Error::invalid_field(DeclType::ExposeResolver, "source.child.name"),
5556                Error::invalid_field(DeclType::ExposeResolver, "source_name"),
5557                Error::invalid_field(DeclType::ExposeResolver, "target_name"),
5558                Error::invalid_field(DeclType::ExposeDictionary, "source.child.name"),
5559                Error::invalid_field(DeclType::ExposeDictionary, "source_name"),
5560                Error::invalid_field(DeclType::ExposeDictionary, "target_name"),
5561            ])),
5562        },
5563        test_validate_exposes_invalid_source_target => {
5564            input = {
5565                let mut decl = new_component_decl();
5566                decl.children = Some(vec![fdecl::Child{
5567                    name: Some("logger".to_string()),
5568                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5569                    startup: Some(fdecl::StartupMode::Lazy),
5570                    on_terminate: None,
5571                    environment: None,
5572                    ..Default::default()
5573                }]);
5574                decl.exposes = Some(vec![
5575                    fdecl::Expose::Service(fdecl::ExposeService {
5576                        source: None,
5577                        source_name: Some("a".to_string()),
5578                        target_name: Some("b".to_string()),
5579                        target: None,
5580                        ..Default::default()
5581                    }),
5582                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5583                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5584                        source_name: Some("c".to_string()),
5585                        target_name: Some("d".to_string()),
5586                        target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5587                        ..Default::default()
5588                    }),
5589                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5590                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "z".to_string()})),
5591                        source_name: Some("e".to_string()),
5592                        target_name: Some("f".to_string()),
5593                        target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "z".to_string()})),
5594                        rights: Some(fio::Operations::CONNECT),
5595                        subdir: None,
5596                        ..Default::default()
5597                    }),
5598                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5599                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5600                        source_name: Some("g".to_string()),
5601                        target_name: Some("h".to_string()),
5602                        target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5603                        rights: Some(fio::Operations::CONNECT),
5604                        subdir: None,
5605                        ..Default::default()
5606                    }),
5607                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5608                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5609                        source_name: Some("i".to_string()),
5610                        target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5611                        target_name: Some("j".to_string()),
5612                        ..Default::default()
5613                    }),
5614                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5615                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5616                        source_name: Some("k".to_string()),
5617                        target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5618                        target_name: Some("l".to_string()),
5619                        ..Default::default()
5620                    }),
5621                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5622                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5623                            name: "logger".to_string(),
5624                            collection: None,
5625                        })),
5626                        source_name: Some("m".to_string()),
5627                        target_name: Some("n".to_string()),
5628                        target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5629                        ..Default::default()
5630                    }),
5631                ]);
5632                decl
5633            },
5634            result = Err(ErrorList::new(vec![
5635                Error::missing_field(DeclType::ExposeService, "source"),
5636                Error::missing_field(DeclType::ExposeService, "target"),
5637                Error::invalid_field(DeclType::ExposeProtocol, "source"),
5638                Error::invalid_field(DeclType::ExposeProtocol, "target"),
5639                Error::invalid_field(DeclType::ExposeDirectory, "source"),
5640                Error::invalid_field(DeclType::ExposeDirectory, "target"),
5641                Error::invalid_field(DeclType::ExposeDirectory, "source"),
5642                Error::invalid_field(DeclType::ExposeDirectory, "target"),
5643                Error::invalid_field(DeclType::ExposeRunner, "source"),
5644                Error::invalid_field(DeclType::ExposeRunner, "target"),
5645                Error::invalid_field(DeclType::ExposeResolver, "source"),
5646                Error::invalid_field(DeclType::ExposeResolver, "target"),
5647                Error::invalid_field(DeclType::ExposeDictionary, "target"),
5648            ])),
5649        },
5650        test_validate_exposes_invalid_source_collection => {
5651            input = {
5652                let mut decl = new_component_decl();
5653                decl.collections = Some(vec![fdecl::Collection{
5654                    name: Some("col".to_string()),
5655                    durability: Some(fdecl::Durability::Transient),
5656                    allowed_offers: None,
5657                    allow_long_names: None,
5658                    ..Default::default()
5659                }]);
5660                decl.exposes = Some(vec![
5661                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5662                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
5663                        source_name: Some("a".to_string()),
5664                        target_name: Some("a".to_string()),
5665                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5666                        ..Default::default()
5667                    }),
5668                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5669                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5670                        source_name: Some("b".to_string()),
5671                        target_name: Some("b".to_string()),
5672                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5673                        rights: Some(fio::Operations::CONNECT),
5674                        subdir: None,
5675                        ..Default::default()
5676                    }),
5677                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5678                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5679                        source_name: Some("c".to_string()),
5680                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5681                        target_name: Some("c".to_string()),
5682                        ..Default::default()
5683                    }),
5684                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5685                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5686                        source_name: Some("d".to_string()),
5687                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5688                        target_name: Some("d".to_string()),
5689                        ..Default::default()
5690                    }),
5691                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5692                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5693                        source_name: Some("e".to_string()),
5694                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5695                        target_name: Some("e".to_string()),
5696                        ..Default::default()
5697                    }),
5698                ]);
5699                decl
5700            },
5701            result = Err(ErrorList::new(vec![
5702                Error::invalid_field(DeclType::ExposeProtocol, "source"),
5703                Error::invalid_field(DeclType::ExposeDirectory, "source"),
5704                Error::invalid_field(DeclType::ExposeRunner, "source"),
5705                Error::invalid_field(DeclType::ExposeResolver, "source"),
5706                Error::invalid_field(DeclType::ExposeDictionary, "source"),
5707            ])),
5708        },
5709        test_validate_exposes_sources_collection => {
5710            input = {
5711                let mut decl = new_component_decl();
5712                decl.collections = Some(vec![
5713                    fdecl::Collection {
5714                        name: Some("col".to_string()),
5715                        durability: Some(fdecl::Durability::Transient),
5716                        allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
5717                        allow_long_names: None,
5718                        ..Default::default()
5719                    }
5720                ]);
5721                decl.exposes = Some(vec![
5722                    fdecl::Expose::Service(fdecl::ExposeService {
5723                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
5724                        source_name: Some("a".to_string()),
5725                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5726                        target_name: Some("a".to_string()),
5727                        ..Default::default()
5728                    })
5729                ]);
5730                decl
5731            },
5732            result = Ok(()),
5733        },
5734        test_validate_exposes_long_identifiers => {
5735            input = {
5736                let mut decl = new_component_decl();
5737                decl.exposes = Some(vec![
5738                    fdecl::Expose::Service(fdecl::ExposeService {
5739                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5740                            name: "b".repeat(256),
5741                            collection: None,
5742                        })),
5743                        source_name: Some(format!("{}", "a".repeat(1025))),
5744                        target_name: Some(format!("{}", "b".repeat(1025))),
5745                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5746                        ..Default::default()
5747                    }),
5748                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5749                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5750                            name: "b".repeat(256),
5751                            collection: None,
5752                        })),
5753                        source_name: Some(format!("{}", "a".repeat(256))),
5754                        target_name: Some(format!("{}", "b".repeat(256))),
5755                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5756                        ..Default::default()
5757                    }),
5758                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5759                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5760                            name: "b".repeat(256),
5761                            collection: None,
5762                        })),
5763                        source_name: Some(format!("{}", "a".repeat(256))),
5764                        target_name: Some(format!("{}", "b".repeat(256))),
5765                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5766                        rights: Some(fio::Operations::CONNECT),
5767                        subdir: None,
5768                        ..Default::default()
5769                    }),
5770                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5771                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5772                            name: "b".repeat(256),
5773                            collection: None,
5774                        })),
5775                        source_name: Some("a".repeat(256)),
5776                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5777                        target_name: Some("b".repeat(256)),
5778                        ..Default::default()
5779                    }),
5780                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5781                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5782                            name: "b".repeat(256),
5783                            collection: None,
5784                        })),
5785                        source_name: Some("a".repeat(256)),
5786                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5787                        target_name: Some("b".repeat(256)),
5788                        ..Default::default()
5789                    }),
5790                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5791                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5792                            name: "b".repeat(256),
5793                            collection: None,
5794                        })),
5795                        source_name: Some("a".repeat(256)),
5796                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5797                        target_name: Some("b".repeat(256)),
5798                        ..Default::default()
5799                    }),
5800                ]);
5801                decl
5802            },
5803            result = Err(ErrorList::new(vec![
5804                Error::field_too_long(DeclType::ExposeService, "source.child.name"),
5805                Error::field_too_long(DeclType::ExposeService, "source_name"),
5806                Error::field_too_long(DeclType::ExposeService, "target_name"),
5807                Error::field_too_long(DeclType::ExposeProtocol, "source.child.name"),
5808                Error::field_too_long(DeclType::ExposeProtocol, "source_name"),
5809                Error::field_too_long(DeclType::ExposeProtocol, "target_name"),
5810                Error::field_too_long(DeclType::ExposeDirectory, "source.child.name"),
5811                Error::field_too_long(DeclType::ExposeDirectory, "source_name"),
5812                Error::field_too_long(DeclType::ExposeDirectory, "target_name"),
5813                Error::field_too_long(DeclType::ExposeRunner, "source.child.name"),
5814                Error::field_too_long(DeclType::ExposeRunner, "source_name"),
5815                Error::field_too_long(DeclType::ExposeRunner, "target_name"),
5816                Error::field_too_long(DeclType::ExposeResolver, "source.child.name"),
5817                Error::field_too_long(DeclType::ExposeResolver, "source_name"),
5818                Error::field_too_long(DeclType::ExposeResolver, "target_name"),
5819                Error::field_too_long(DeclType::ExposeDictionary, "source.child.name"),
5820                Error::field_too_long(DeclType::ExposeDictionary, "source_name"),
5821                Error::field_too_long(DeclType::ExposeDictionary, "target_name"),
5822            ])),
5823        },
5824        test_validate_exposes_invalid_child => {
5825            input = {
5826                let mut decl = new_component_decl();
5827                decl.exposes = Some(vec![
5828                    fdecl::Expose::Service(fdecl::ExposeService {
5829                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5830                            name: "netstack".to_string(),
5831                            collection: None,
5832                        })),
5833                        source_name: Some("fuchsia.logger.Log".to_string()),
5834                        target_name: Some("fuchsia.logger.Log".to_string()),
5835                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5836                        ..Default::default()
5837                    }),
5838                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5839                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5840                            name: "netstack".to_string(),
5841                            collection: None,
5842                        })),
5843                        source_name: Some("fuchsia.logger.LegacyLog".to_string()),
5844                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
5845                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5846                        ..Default::default()
5847                    }),
5848                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5849                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5850                            name: "netstack".to_string(),
5851                            collection: None,
5852                        })),
5853                        source_name: Some("data".to_string()),
5854                        target_name: Some("data".to_string()),
5855                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5856                        rights: Some(fio::Operations::CONNECT),
5857                        subdir: None,
5858                        ..Default::default()
5859                    }),
5860                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5861                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5862                            name: "netstack".to_string(),
5863                            collection: None,
5864                        })),
5865                        source_name: Some("elf".to_string()),
5866                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5867                        target_name: Some("elf".to_string()),
5868                        ..Default::default()
5869                    }),
5870                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5871                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5872                            name: "netstack".to_string(),
5873                            collection: None,
5874                        })),
5875                        source_name: Some("pkg".to_string()),
5876                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5877                        target_name: Some("pkg".to_string()),
5878                        ..Default::default()
5879                    }),
5880                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5881                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5882                            name: "netstack".to_string(),
5883                            collection: None,
5884                        })),
5885                        source_name: Some("dict".to_string()),
5886                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5887                        target_name: Some("dict".to_string()),
5888                        ..Default::default()
5889                    }),
5890                ]);
5891                decl
5892            },
5893            result = Err(ErrorList::new(vec![
5894                Error::invalid_child(DeclType::ExposeService, "source", "netstack"),
5895                Error::invalid_child(DeclType::ExposeProtocol, "source", "netstack"),
5896                Error::invalid_child(DeclType::ExposeDirectory, "source", "netstack"),
5897                Error::invalid_child(DeclType::ExposeRunner, "source", "netstack"),
5898                Error::invalid_child(DeclType::ExposeResolver, "source", "netstack"),
5899                Error::invalid_child(DeclType::ExposeDictionary, "source", "netstack"),
5900            ])),
5901        },
5902        test_validate_exposes_invalid_source_capability => {
5903            input = {
5904                fdecl::Component {
5905                    exposes: Some(vec![
5906                        fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5907                            source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
5908                                name: "this-storage-doesnt-exist".to_string(),
5909                            })),
5910                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
5911                            target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
5912                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5913                            ..Default::default()
5914                        }),
5915                    ]),
5916                    ..new_component_decl()
5917                }
5918            },
5919            result = Err(ErrorList::new(vec![
5920                Error::invalid_capability(DeclType::ExposeProtocol, "source", "this-storage-doesnt-exist"),
5921            ])),
5922        },
5923        test_validate_exposes_duplicate_target => {
5924            input = {
5925                let mut decl = new_component_decl();
5926                decl.exposes = Some(vec![
5927                    fdecl::Expose::Service(fdecl::ExposeService {
5928                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
5929                            name: "coll".into(),
5930                        })),
5931                        source_name: Some("netstack".to_string()),
5932                        target_name: Some("fuchsia.net.Stack".to_string()),
5933                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5934                        ..Default::default()
5935                    }),
5936                    fdecl::Expose::Service(fdecl::ExposeService {
5937                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
5938                            name: "coll2".into(),
5939                        })),
5940                        source_name: Some("netstack2".to_string()),
5941                        target_name: Some("fuchsia.net.Stack".to_string()),
5942                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5943                        ..Default::default()
5944                    }),
5945                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5946                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5947                        source_name: Some("fonts".to_string()),
5948                        target_name: Some("fuchsia.fonts.Provider".to_string()),
5949                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5950                        ..Default::default()
5951                    }),
5952                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5953                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5954                        source_name: Some("fonts2".to_string()),
5955                        target_name: Some("fuchsia.fonts.Provider".to_string()),
5956                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5957                        ..Default::default()
5958                    }),
5959                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5960                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5961                        source_name: Some("assets".to_string()),
5962                        target_name: Some("stuff".to_string()),
5963                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5964                        rights: None,
5965                        subdir: None,
5966                        ..Default::default()
5967                    }),
5968                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5969                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5970                        source_name: Some("assets2".to_string()),
5971                        target_name: Some("stuff".to_string()),
5972                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5973                        rights: None,
5974                        subdir: None,
5975                        ..Default::default()
5976                    }),
5977                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5978                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5979                        source_name: Some("source_elf".to_string()),
5980                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5981                        target_name: Some("elf".to_string()),
5982                        ..Default::default()
5983                    }),
5984                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5985                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5986                        source_name: Some("source_elf".to_string()),
5987                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5988                        target_name: Some("elf".to_string()),
5989                        ..Default::default()
5990                    }),
5991                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5992                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5993                        source_name: Some("source_pkg".to_string()),
5994                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5995                        target_name: Some("pkg".to_string()),
5996                        ..Default::default()
5997                    }),
5998                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5999                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6000                        source_name: Some("source_pkg".to_string()),
6001                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6002                        target_name: Some("pkg".to_string()),
6003                        ..Default::default()
6004                    }),
6005                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
6006                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6007                        source_name: Some("source_dict".to_string()),
6008                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6009                        target_name: Some("dict".to_string()),
6010                        ..Default::default()
6011                    }),
6012                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
6013                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6014                        source_name: Some("source_dict".to_string()),
6015                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6016                        target_name: Some("dict".to_string()),
6017                        ..Default::default()
6018                    }),
6019                ]);
6020                decl.collections = Some(vec![
6021                    fdecl::Collection {
6022                        name: Some("coll".into()),
6023                        durability: Some(fdecl::Durability::Transient),
6024                        ..Default::default()
6025                    },
6026                    fdecl::Collection {
6027                        name: Some("coll2".into()),
6028                        durability: Some(fdecl::Durability::Transient),
6029                        ..Default::default()
6030                    },
6031                ]);
6032                decl.capabilities = Some(vec![
6033                    fdecl::Capability::Service(fdecl::Service {
6034                        name: Some("netstack".to_string()),
6035                        source_path: Some("/path".to_string()),
6036                        ..Default::default()
6037                    }),
6038                    fdecl::Capability::Service(fdecl::Service {
6039                        name: Some("netstack2".to_string()),
6040                        source_path: Some("/path".to_string()),
6041                        ..Default::default()
6042                    }),
6043                    fdecl::Capability::Protocol(fdecl::Protocol {
6044                        name: Some("fonts".to_string()),
6045                        source_path: Some("/path".to_string()),
6046                        ..Default::default()
6047                    }),
6048                    fdecl::Capability::Protocol(fdecl::Protocol {
6049                        name: Some("fonts2".to_string()),
6050                        source_path: Some("/path".to_string()),
6051                        ..Default::default()
6052                    }),
6053                    fdecl::Capability::Directory(fdecl::Directory {
6054                        name: Some("assets".to_string()),
6055                        source_path: Some("/path".to_string()),
6056                        rights: Some(fio::Operations::CONNECT),
6057                        ..Default::default()
6058                    }),
6059                    fdecl::Capability::Directory(fdecl::Directory {
6060                        name: Some("assets2".to_string()),
6061                        source_path: Some("/path".to_string()),
6062                        rights: Some(fio::Operations::CONNECT),
6063                        ..Default::default()
6064                    }),
6065                    fdecl::Capability::Runner(fdecl::Runner {
6066                        name: Some("source_elf".to_string()),
6067                        source_path: Some("/path".to_string()),
6068                        ..Default::default()
6069                    }),
6070                    fdecl::Capability::Resolver(fdecl::Resolver {
6071                        name: Some("source_pkg".to_string()),
6072                        source_path: Some("/path".to_string()),
6073                        ..Default::default()
6074                    }),
6075                    fdecl::Capability::Dictionary(fdecl::Dictionary {
6076                        name: Some("source_dict".to_string()),
6077                        ..Default::default()
6078                    }),
6079                ]);
6080                decl
6081            },
6082            result = Err(ErrorList::new(vec![
6083                // Duplicate services are allowed.
6084                Error::duplicate_field(DeclType::ExposeProtocol, "target_name",
6085                                    "fuchsia.fonts.Provider"),
6086                Error::duplicate_field(DeclType::ExposeDirectory, "target_name",
6087                                    "stuff"),
6088                Error::duplicate_field(DeclType::ExposeRunner, "target_name",
6089                                    "elf"),
6090                Error::duplicate_field(DeclType::ExposeResolver, "target_name", "pkg"),
6091                Error::duplicate_field(DeclType::ExposeDictionary, "target_name", "dict"),
6092            ])),
6093        },
6094        test_validate_exposes_invalid_capability_from_self => {
6095            input = {
6096                let mut decl = new_component_decl();
6097                decl.exposes = Some(vec![
6098                    fdecl::Expose::Service(fdecl::ExposeService {
6099                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6100                        source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
6101                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6102                        target_name: Some("foo".to_string()),
6103                        ..Default::default()
6104                    }),
6105                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6106                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6107                        source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
6108                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6109                        target_name: Some("bar".to_string()),
6110                        ..Default::default()
6111                    }),
6112                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
6113                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6114                        source_name: Some("dir".to_string()),
6115                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6116                        target_name: Some("assets".to_string()),
6117                        rights: None,
6118                        subdir: None,
6119                        ..Default::default()
6120                    }),
6121                    fdecl::Expose::Runner(fdecl::ExposeRunner {
6122                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6123                        source_name: Some("source_elf".to_string()),
6124                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6125                        target_name: Some("elf".to_string()),
6126                        ..Default::default()
6127                    }),
6128                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
6129                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6130                        source_name: Some("source_pkg".to_string()),
6131                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6132                        target_name: Some("pkg".to_string()),
6133                        ..Default::default()
6134                    }),
6135                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
6136                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6137                        source_name: Some("source_dict".to_string()),
6138                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6139                        target_name: Some("dict".to_string()),
6140                        ..Default::default()
6141                    }),
6142                    fdecl::Expose::Config(fdecl::ExposeConfiguration {
6143                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6144                        source_name: Some("source_config".to_string()),
6145                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6146                        target_name: Some("config".to_string()),
6147                        ..Default::default()
6148                    }),
6149                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6150                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6151                        source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
6152                        source_dictionary: Some("dict/inner".to_string()),
6153                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6154                        target_name: Some("baz".to_string()),
6155                        ..Default::default()
6156                    }),
6157                ]);
6158                decl
6159            },
6160            result = Err(ErrorList::new(vec![
6161                Error::invalid_capability(
6162                    DeclType::ExposeService,
6163                    "source",
6164                    "fuchsia.some.library.SomeProtocol"),
6165                Error::invalid_capability(
6166                    DeclType::ExposeProtocol,
6167                    "source",
6168                    "fuchsia.some.library.SomeProtocol"),
6169                Error::invalid_capability(DeclType::ExposeDirectory, "source", "dir"),
6170                Error::invalid_capability(DeclType::ExposeRunner, "source", "source_elf"),
6171                Error::invalid_capability(DeclType::ExposeResolver, "source", "source_pkg"),
6172                Error::invalid_capability(DeclType::ExposeDictionary, "source", "source_dict"),
6173                Error::invalid_capability(DeclType::ExposeConfig, "source", "source_config"),
6174                Error::invalid_capability(DeclType::ExposeProtocol, "source", "dict"),
6175            ])),
6176        },
6177
6178        test_validate_exposes_availability_service => {
6179            input = {
6180                let mut decl = generate_expose_different_source_and_availability_decl(
6181                    |source, availability, target_name|
6182                        fdecl::Expose::Service(fdecl::ExposeService {
6183                            source: Some(source),
6184                            source_name: Some("fuchsia.examples.Echo".to_string()),
6185                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6186                            target_name: Some(target_name.to_string()),
6187                            availability: Some(availability),
6188                            ..Default::default()
6189                        })
6190                );
6191                decl.capabilities = Some(vec![
6192                    fdecl::Capability::Service(fdecl::Service {
6193                        name: Some("fuchsia.examples.Echo".to_string()),
6194                        source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
6195                        ..Default::default()
6196                    }),
6197                ]);
6198                decl
6199            },
6200            result = {
6201                Err(ErrorList::new(vec![
6202                    Error::availability_must_be_optional(
6203                        DeclType::ExposeService,
6204                        "availability",
6205                        Some(&"fuchsia.examples.Echo".to_string()),
6206                    ),
6207                    Error::availability_must_be_optional(
6208                        DeclType::ExposeService,
6209                        "availability",
6210                        Some(&"fuchsia.examples.Echo".to_string()),
6211                    ),
6212                ]))
6213            },
6214        },
6215        test_validate_exposes_availability_protocol => {
6216            input = {
6217                let mut decl = generate_expose_different_source_and_availability_decl(
6218                    |source, availability, target_name|
6219                        fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6220                            source: Some(source),
6221                            source_name: Some("fuchsia.examples.Echo".to_string()),
6222                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6223                            target_name: Some(target_name.to_string()),
6224                            availability: Some(availability),
6225                            ..Default::default()
6226                        })
6227                );
6228                decl.capabilities = Some(vec![
6229                    fdecl::Capability::Protocol(fdecl::Protocol {
6230                        name: Some("fuchsia.examples.Echo".to_string()),
6231                        source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
6232                        ..Default::default()
6233                    }),
6234                ]);
6235                decl
6236            },
6237            result = {
6238                Err(ErrorList::new(vec![
6239                    Error::availability_must_be_optional(
6240                        DeclType::ExposeProtocol,
6241                        "availability",
6242                        Some(&"fuchsia.examples.Echo".to_string()),
6243                    ),
6244                    Error::availability_must_be_optional(
6245                        DeclType::ExposeProtocol,
6246                        "availability",
6247                        Some(&"fuchsia.examples.Echo".to_string()),
6248                    ),
6249                ]))
6250            },
6251        },
6252        test_validate_exposes_availability_directory => {
6253            input = {
6254                let mut decl = generate_expose_different_source_and_availability_decl(
6255                    |source, availability, target_name|
6256                        fdecl::Expose::Directory(fdecl::ExposeDirectory {
6257                            source: Some(source),
6258                            source_name: Some("fuchsia.examples.Echo".to_string()),
6259                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6260                            target_name: Some(target_name.to_string()),
6261                            availability: Some(availability),
6262                            ..Default::default()
6263                        })
6264                );
6265                decl.capabilities = Some(vec![
6266                    fdecl::Capability::Directory(fdecl::Directory {
6267                        name: Some("fuchsia.examples.Echo".to_string()),
6268                        source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
6269                        rights: Some(fio::Operations::READ_BYTES),
6270                        ..Default::default()
6271                    }),
6272                ]);
6273                decl
6274            },
6275            result = {
6276                Err(ErrorList::new(vec![
6277                    Error::availability_must_be_optional(
6278                        DeclType::ExposeDirectory,
6279                        "availability",
6280                        Some(&"fuchsia.examples.Echo".to_string()),
6281                    ),
6282                    Error::availability_must_be_optional(
6283                        DeclType::ExposeDirectory,
6284                        "availability",
6285                        Some(&"fuchsia.examples.Echo".to_string()),
6286                    ),
6287                ]))
6288            },
6289        },
6290
6291        // offers
6292        test_validate_offers_empty => {
6293            input = {
6294                let mut decl = new_component_decl();
6295                decl.offers = Some(vec![
6296                    fdecl::Offer::Service(fdecl::OfferService {
6297                        source: None,
6298                        source_name: None,
6299                        target: None,
6300                        target_name: None,
6301                        ..Default::default()
6302                    }),
6303                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6304                        source: None,
6305                        source_name: None,
6306                        target: None,
6307                        target_name: None,
6308                        dependency_type: None,
6309                        ..Default::default()
6310                    }),
6311                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6312                        source: None,
6313                        source_name: None,
6314                        target: None,
6315                        target_name: None,
6316                        rights: None,
6317                        subdir: None,
6318                        dependency_type: None,
6319                        ..Default::default()
6320                    }),
6321                    fdecl::Offer::Storage(fdecl::OfferStorage {
6322                        source_name: None,
6323                        source: None,
6324                        target: None,
6325                        target_name: None,
6326                        ..Default::default()
6327                    }),
6328                    fdecl::Offer::Runner(fdecl::OfferRunner {
6329                        source: None,
6330                        source_name: None,
6331                        target: None,
6332                        target_name: None,
6333                        ..Default::default()
6334                    }),
6335                    fdecl::Offer::Resolver(fdecl::OfferResolver {
6336                        ..Default::default()
6337                    }),
6338                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6339                        ..Default::default()
6340                    }),
6341                ]);
6342                decl
6343            },
6344            // TODO(dgonyeo): we need to handle the availability being unset until we've soft
6345            // migrated all manifests
6346            result = Err(ErrorList::new(vec![
6347                Error::missing_field(DeclType::OfferService, "source"),
6348                Error::missing_field(DeclType::OfferService, "source_name"),
6349                Error::missing_field(DeclType::OfferService, "target"),
6350                Error::missing_field(DeclType::OfferService, "target_name"),
6351                //Error::missing_field(DeclType::OfferService, "availability"),
6352                Error::missing_field(DeclType::OfferProtocol, "source"),
6353                Error::missing_field(DeclType::OfferProtocol, "source_name"),
6354                Error::missing_field(DeclType::OfferProtocol, "target"),
6355                Error::missing_field(DeclType::OfferProtocol, "target_name"),
6356                Error::missing_field(DeclType::OfferProtocol, "dependency_type"),
6357                //Error::missing_field(DeclType::OfferProtocol, "availability"),
6358                Error::missing_field(DeclType::OfferDirectory, "source"),
6359                Error::missing_field(DeclType::OfferDirectory, "source_name"),
6360                Error::missing_field(DeclType::OfferDirectory, "target"),
6361                Error::missing_field(DeclType::OfferDirectory, "target_name"),
6362                Error::missing_field(DeclType::OfferDirectory, "dependency_type"),
6363                //Error::missing_field(DeclType::OfferDirectory, "availability"),
6364                Error::missing_field(DeclType::OfferStorage, "source"),
6365                Error::missing_field(DeclType::OfferStorage, "source_name"),
6366                Error::missing_field(DeclType::OfferStorage, "target"),
6367                Error::missing_field(DeclType::OfferStorage, "target_name"),
6368                //Error::missing_field(DeclType::OfferStorage, "availability"),
6369                Error::missing_field(DeclType::OfferRunner, "source"),
6370                Error::missing_field(DeclType::OfferRunner, "source_name"),
6371                Error::missing_field(DeclType::OfferRunner, "target"),
6372                Error::missing_field(DeclType::OfferRunner, "target_name"),
6373                //Error::missing_field(DeclType::OfferRunner, "availability"),
6374                Error::missing_field(DeclType::OfferResolver, "source"),
6375                Error::missing_field(DeclType::OfferResolver, "source_name"),
6376                Error::missing_field(DeclType::OfferResolver, "target"),
6377                Error::missing_field(DeclType::OfferResolver, "target_name"),
6378                Error::missing_field(DeclType::OfferDictionary, "source"),
6379                Error::missing_field(DeclType::OfferDictionary, "source_name"),
6380                Error::missing_field(DeclType::OfferDictionary, "target"),
6381                Error::missing_field(DeclType::OfferDictionary, "target_name"),
6382                Error::missing_field(DeclType::OfferDictionary, "dependency_type"),
6383            ])),
6384        },
6385        test_validate_offers_long_identifiers => {
6386            input = {
6387                let mut decl = new_component_decl();
6388                decl.offers = Some(vec![
6389                    fdecl::Offer::Service(fdecl::OfferService {
6390                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6391                            name: "a".repeat(256),
6392                            collection: None,
6393                        })),
6394                        source_name: Some(format!("{}", "a".repeat(256))),
6395                        target: Some(fdecl::Ref::Child(
6396                        fdecl::ChildRef {
6397                            name: "b".repeat(256),
6398                            collection: None,
6399                        }
6400                        )),
6401                        target_name: Some(format!("{}", "b".repeat(256))),
6402                        ..Default::default()
6403                    }),
6404                    fdecl::Offer::Service(fdecl::OfferService {
6405                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6406                        source_name: Some("a".to_string()),
6407                        target: Some(fdecl::Ref::Collection(
6408                        fdecl::CollectionRef {
6409                            name: "b".repeat(256),
6410                        }
6411                        )),
6412                        target_name: Some(format!("{}", "b".repeat(256))),
6413                        ..Default::default()
6414                    }),
6415                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6416                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6417                            name: "a".repeat(256),
6418                            collection: None,
6419                        })),
6420                        source_name: Some(format!("{}", "a".repeat(256))),
6421                        target: Some(fdecl::Ref::Child(
6422                        fdecl::ChildRef {
6423                            name: "b".repeat(256),
6424                            collection: None,
6425                        }
6426                        )),
6427                        target_name: Some(format!("{}", "b".repeat(256))),
6428                        dependency_type: Some(fdecl::DependencyType::Strong),
6429                        ..Default::default()
6430                    }),
6431                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6432                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6433                        source_name: Some("a".to_string()),
6434                        target: Some(fdecl::Ref::Collection(
6435                        fdecl::CollectionRef {
6436                            name: "b".repeat(256),
6437                        }
6438                        )),
6439                        target_name: Some(format!("{}", "b".repeat(256))),
6440                        dependency_type: Some(fdecl::DependencyType::Weak),
6441                        ..Default::default()
6442                    }),
6443                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6444                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6445                            name: "a".repeat(256),
6446                            collection: None,
6447                        })),
6448                        source_name: Some(format!("{}", "a".repeat(256))),
6449                        target: Some(fdecl::Ref::Child(
6450                        fdecl::ChildRef {
6451                            name: "b".repeat(256),
6452                            collection: None,
6453                        }
6454                        )),
6455                        target_name: Some(format!("{}", "b".repeat(256))),
6456                        rights: Some(fio::Operations::CONNECT),
6457                        subdir: None,
6458                        dependency_type: Some(fdecl::DependencyType::Strong),
6459                        ..Default::default()
6460                    }),
6461                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6462                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6463                        source_name: Some("a".to_string()),
6464                        target: Some(fdecl::Ref::Collection(
6465                        fdecl::CollectionRef {
6466                            name: "b".repeat(256),
6467                        }
6468                        )),
6469                        target_name: Some(format!("{}", "b".repeat(256))),
6470                        rights: Some(fio::Operations::CONNECT),
6471                        subdir: None,
6472                        dependency_type: Some(fdecl::DependencyType::Weak),
6473                        ..Default::default()
6474                    }),
6475                    fdecl::Offer::Storage(fdecl::OfferStorage {
6476                        source_name: Some("data".to_string()),
6477                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6478                        target: Some(fdecl::Ref::Child(
6479                            fdecl::ChildRef {
6480                                name: "b".repeat(256),
6481                                collection: None,
6482                            }
6483                        )),
6484                        target_name: Some("data".to_string()),
6485                        ..Default::default()
6486                    }),
6487                    fdecl::Offer::Storage(fdecl::OfferStorage {
6488                        source_name: Some("data".to_string()),
6489                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6490                        target: Some(fdecl::Ref::Collection(
6491                            fdecl::CollectionRef { name: "b".repeat(256) }
6492                        )),
6493                        target_name: Some("data".to_string()),
6494                        ..Default::default()
6495                    }),
6496                    fdecl::Offer::Runner(fdecl::OfferRunner {
6497                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6498                            name: "a".repeat(256),
6499                            collection: None,
6500                        })),
6501                        source_name: Some("b".repeat(256)),
6502                        target: Some(fdecl::Ref::Collection(
6503                        fdecl::CollectionRef {
6504                            name: "c".repeat(256),
6505                        }
6506                        )),
6507                        target_name: Some("d".repeat(256)),
6508                        ..Default::default()
6509                    }),
6510                    fdecl::Offer::Resolver(fdecl::OfferResolver {
6511                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6512                            name: "a".repeat(256),
6513                            collection: None,
6514                        })),
6515                        source_name: Some("b".repeat(256)),
6516                        target: Some(fdecl::Ref::Collection(
6517                            fdecl::CollectionRef {
6518                                name: "c".repeat(256),
6519                            }
6520                        )),
6521                        target_name: Some("d".repeat(256)),
6522                        ..Default::default()
6523                    }),
6524                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6525                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6526                            name: "a".repeat(256),
6527                            collection: None,
6528                        })),
6529                        source_name: Some("b".repeat(256)),
6530                        target: Some(fdecl::Ref::Collection(
6531                            fdecl::CollectionRef {
6532                                name: "c".repeat(256),
6533                            }
6534                        )),
6535                        target_name: Some("d".repeat(256)),
6536                        dependency_type: Some(fdecl::DependencyType::Strong),
6537                        ..Default::default()
6538                    }),
6539                ]);
6540                decl
6541            },
6542            result = Err(ErrorList::new(vec![
6543                Error::field_too_long(DeclType::OfferService, "source.child.name"),
6544                Error::field_too_long(DeclType::OfferService, "source_name"),
6545                Error::field_too_long(DeclType::OfferService, "target.child.name"),
6546                Error::field_too_long(DeclType::OfferService, "target_name"),
6547                Error::field_too_long(DeclType::OfferService, "target.collection.name"),
6548                Error::field_too_long(DeclType::OfferService, "target_name"),
6549                Error::field_too_long(DeclType::OfferProtocol, "source.child.name"),
6550                Error::field_too_long(DeclType::OfferProtocol, "source_name"),
6551                Error::field_too_long(DeclType::OfferProtocol, "target.child.name"),
6552                Error::field_too_long(DeclType::OfferProtocol, "target_name"),
6553                Error::field_too_long(DeclType::OfferProtocol, "target.collection.name"),
6554                Error::field_too_long(DeclType::OfferProtocol, "target_name"),
6555                Error::field_too_long(DeclType::OfferDirectory, "source.child.name"),
6556                Error::field_too_long(DeclType::OfferDirectory, "source_name"),
6557                Error::field_too_long(DeclType::OfferDirectory, "target.child.name"),
6558                Error::field_too_long(DeclType::OfferDirectory, "target_name"),
6559                Error::field_too_long(DeclType::OfferDirectory, "target.collection.name"),
6560                Error::field_too_long(DeclType::OfferDirectory, "target_name"),
6561                Error::field_too_long(DeclType::OfferStorage, "target.child.name"),
6562                Error::field_too_long(DeclType::OfferStorage, "target.collection.name"),
6563                Error::field_too_long(DeclType::OfferRunner, "source.child.name"),
6564                Error::field_too_long(DeclType::OfferRunner, "source_name"),
6565                Error::field_too_long(DeclType::OfferRunner, "target.collection.name"),
6566                Error::field_too_long(DeclType::OfferRunner, "target_name"),
6567                Error::field_too_long(DeclType::OfferResolver, "source.child.name"),
6568                Error::field_too_long(DeclType::OfferResolver, "source_name"),
6569                Error::field_too_long(DeclType::OfferResolver, "target.collection.name"),
6570                Error::field_too_long(DeclType::OfferResolver, "target_name"),
6571                Error::field_too_long(DeclType::OfferDictionary, "source.child.name"),
6572                Error::field_too_long(DeclType::OfferDictionary, "source_name"),
6573                Error::field_too_long(DeclType::OfferDictionary, "target.collection.name"),
6574                Error::field_too_long(DeclType::OfferDictionary, "target_name"),
6575            ])),
6576        },
6577        test_validate_offers_extraneous => {
6578            input = {
6579                let mut decl = new_component_decl();
6580                decl.offers = Some(vec![
6581                    fdecl::Offer::Service(fdecl::OfferService {
6582                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6583                            name: "logger".to_string(),
6584                            collection: Some("modular".to_string()),
6585                        })),
6586                        source_name: Some("fuchsia.logger.Log".to_string()),
6587                        target: Some(fdecl::Ref::Child(
6588                            fdecl::ChildRef {
6589                                name: "netstack".to_string(),
6590                                collection: Some("modular".to_string()),
6591                            }
6592                        )),
6593                        target_name: Some("fuchsia.logger.Log".to_string()),
6594                        ..Default::default()
6595                    }),
6596                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6597                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6598                            name: "logger".to_string(),
6599                            collection: Some("modular".to_string()),
6600                        })),
6601                        source_name: Some("fuchsia.logger.Log".to_string()),
6602                        target: Some(fdecl::Ref::Child(
6603                            fdecl::ChildRef {
6604                                name: "netstack".to_string(),
6605                                collection: Some("modular".to_string()),
6606                            }
6607                        )),
6608                        target_name: Some("fuchsia.logger.Log".to_string()),
6609                        dependency_type: Some(fdecl::DependencyType::Strong),
6610                        ..Default::default()
6611                    }),
6612                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6613                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6614                            name: "logger".to_string(),
6615                            collection: Some("modular".to_string()),
6616                        })),
6617                        source_name: Some("assets".to_string()),
6618                        target: Some(fdecl::Ref::Child(
6619                            fdecl::ChildRef {
6620                                name: "netstack".to_string(),
6621                                collection: Some("modular".to_string()),
6622                            }
6623                        )),
6624                        target_name: Some("assets".to_string()),
6625                        rights: Some(fio::Operations::CONNECT),
6626                        subdir: None,
6627                        dependency_type: Some(fdecl::DependencyType::Weak),
6628                        ..Default::default()
6629                    }),
6630                    fdecl::Offer::Storage(fdecl::OfferStorage {
6631                        source_name: Some("data".to_string()),
6632                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{ })),
6633                        target: Some(fdecl::Ref::Child(
6634                            fdecl::ChildRef {
6635                                name: "netstack".to_string(),
6636                                collection: Some("modular".to_string()),
6637                            }
6638                        )),
6639                        target_name: Some("data".to_string()),
6640                        ..Default::default()
6641                    }),
6642                    fdecl::Offer::Runner(fdecl::OfferRunner {
6643                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6644                            name: "logger".to_string(),
6645                            collection: Some("modular".to_string()),
6646                        })),
6647                        source_name: Some("elf".to_string()),
6648                        target: Some(fdecl::Ref::Child(
6649                            fdecl::ChildRef {
6650                                name: "netstack".to_string(),
6651                                collection: Some("modular".to_string()),
6652                            }
6653                        )),
6654                        target_name: Some("elf".to_string()),
6655                        ..Default::default()
6656                    }),
6657                    fdecl::Offer::Resolver(fdecl::OfferResolver {
6658                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6659                            name: "logger".to_string(),
6660                            collection: Some("modular".to_string()),
6661                        })),
6662                        source_name: Some("pkg".to_string()),
6663                        target: Some(fdecl::Ref::Child(
6664                            fdecl::ChildRef {
6665                                name: "netstack".to_string(),
6666                                collection: Some("modular".to_string()),
6667                            }
6668                        )),
6669                        target_name: Some("pkg".to_string()),
6670                        ..Default::default()
6671                    }),
6672                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6673                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6674                            name: "logger".to_string(),
6675                            collection: Some("modular".to_string()),
6676                        })),
6677                        source_name: Some("dict".to_string()),
6678                        target: Some(fdecl::Ref::Child(
6679                            fdecl::ChildRef {
6680                                name: "netstack".to_string(),
6681                                collection: Some("modular".to_string()),
6682                            }
6683                        )),
6684                        target_name: Some("dict".to_string()),
6685                        dependency_type: Some(fdecl::DependencyType::Strong),
6686                        ..Default::default()
6687                    }),
6688                ]);
6689                decl.capabilities = Some(vec![
6690                    fdecl::Capability::Protocol(fdecl::Protocol {
6691                        name: Some("fuchsia.logger.Log".to_string()),
6692                        source_path: Some("/svc/logger".to_string()),
6693                        ..Default::default()
6694                    }),
6695                    fdecl::Capability::Directory(fdecl::Directory {
6696                        name: Some("assets".to_string()),
6697                        source_path: Some("/data/assets".to_string()),
6698                        rights: Some(fio::Operations::CONNECT),
6699                        ..Default::default()
6700                    }),
6701                ]);
6702                decl
6703            },
6704            result = Err(ErrorList::new(vec![
6705                Error::extraneous_field(DeclType::OfferService, "source.child.collection"),
6706                Error::extraneous_field(DeclType::OfferService, "target.child.collection"),
6707                Error::extraneous_field(DeclType::OfferProtocol, "source.child.collection"),
6708                Error::extraneous_field(DeclType::OfferProtocol, "target.child.collection"),
6709                Error::extraneous_field(DeclType::OfferDirectory, "source.child.collection"),
6710                Error::extraneous_field(DeclType::OfferDirectory, "target.child.collection"),
6711                Error::extraneous_field(DeclType::OfferStorage, "target.child.collection"),
6712                Error::extraneous_field(DeclType::OfferRunner, "source.child.collection"),
6713                Error::extraneous_field(DeclType::OfferRunner, "target.child.collection"),
6714                Error::extraneous_field(DeclType::OfferResolver, "source.child.collection"),
6715                Error::extraneous_field(DeclType::OfferResolver, "target.child.collection"),
6716                Error::extraneous_field(DeclType::OfferDictionary, "source.child.collection"),
6717                Error::extraneous_field(DeclType::OfferDictionary, "target.child.collection"),
6718            ])),
6719        },
6720        test_validate_offers_invalid_filtered_service_fields => {
6721            input = {
6722                let mut decl = new_component_decl();
6723                decl.offers = Some(vec![
6724                    fdecl::Offer::Service(fdecl::OfferService {
6725                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6726                        source_name: Some("fuchsia.logger.Log".to_string()),
6727                        target: Some(fdecl::Ref::Child(
6728                            fdecl::ChildRef {
6729                                name: "logger".to_string(),
6730                                collection: None,
6731                            }
6732                        )),
6733                        target_name: Some("fuchsia.logger.Log".to_string()),
6734                        source_instance_filter: Some(vec![]),
6735                        ..Default::default()
6736                    }),
6737                    fdecl::Offer::Service(fdecl::OfferService {
6738                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6739                        source_name: Some("fuchsia.logger.Log".to_string()),
6740                        target: Some(fdecl::Ref::Child(
6741                            fdecl::ChildRef {
6742                                name: "logger".to_string(),
6743                                collection: None,
6744                            }
6745                        )),
6746                        target_name: Some("fuchsia.logger.Log2".to_string()),
6747                        source_instance_filter: Some(vec!["^badname".to_string()]),
6748                        ..Default::default()
6749                    }),
6750                    fdecl::Offer::Service(fdecl::OfferService {
6751                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6752                        source_name: Some("fuchsia.logger.Log".to_string()),
6753                        target: Some(fdecl::Ref::Child(
6754                            fdecl::ChildRef {
6755                                name: "logger".to_string(),
6756                                collection: None,
6757                            }
6758                        )),
6759                        target_name: Some("fuchsia.logger.Log1".to_string()),
6760                        renamed_instances: Some(vec![fdecl::NameMapping{source_name: "a".to_string(), target_name: "b".to_string()}, fdecl::NameMapping{source_name: "c".to_string(), target_name: "b".to_string()}]),
6761                        ..Default::default()
6762                    }),
6763                    fdecl::Offer::Service(fdecl::OfferService {
6764                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6765                        source_name: Some("fuchsia.logger.Log".to_string()),
6766                        target: Some(fdecl::Ref::Child(
6767                            fdecl::ChildRef {
6768                                name: "logger".to_string(),
6769                                collection: None,
6770                            }
6771                        )),
6772                        target_name: Some("fuchsia.logger.Log3".to_string()),
6773                        renamed_instances: Some(vec![
6774                            fdecl::NameMapping {
6775                                source_name: "^badname".to_string(),
6776                                target_name: "^badname".to_string(),
6777                            }
6778                        ]),
6779                        ..Default::default()
6780                    })
6781                ]);
6782                decl.children = Some(vec![
6783                    fdecl::Child {
6784                        name: Some("logger".to_string()),
6785                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
6786                        startup: Some(fdecl::StartupMode::Lazy),
6787                        on_terminate: None,
6788                        environment: None,
6789                        ..Default::default()
6790                    },
6791                ]);
6792                decl
6793            },
6794            result = Err(ErrorList::new(vec![
6795                Error::invalid_field(DeclType::OfferService, "source_instance_filter"),
6796                Error::invalid_field(DeclType::OfferService, "source_instance_filter"),
6797                Error::invalid_field(DeclType::OfferService, "renamed_instances"),
6798                Error::invalid_field(DeclType::OfferService, "renamed_instances.source_name"),
6799                Error::invalid_field(DeclType::OfferService, "renamed_instances.target_name"),
6800            ])),
6801        },
6802        test_validate_offers_invalid_identifiers => {
6803            input = {
6804                let mut decl = new_component_decl();
6805                decl.offers = Some(vec![
6806                    fdecl::Offer::Service(fdecl::OfferService {
6807                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6808                            name: "^bad".to_string(),
6809                            collection: None,
6810                        })),
6811                        source_name: Some("foo/".to_string()),
6812                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6813                            name: "%bad".to_string(),
6814                            collection: None,
6815                        })),
6816                        target_name: Some("/".to_string()),
6817                        ..Default::default()
6818                    }),
6819                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6820                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6821                            name: "^bad".to_string(),
6822                            collection: None,
6823                        })),
6824                        source_name: Some("foo/".to_string()),
6825                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6826                            name: "%bad".to_string(),
6827                            collection: None,
6828                        })),
6829                        target_name: Some("/".to_string()),
6830                        dependency_type: Some(fdecl::DependencyType::Strong),
6831                        ..Default::default()
6832                    }),
6833                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6834                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6835                            name: "^bad".to_string(),
6836                            collection: None,
6837                        })),
6838                        source_name: Some("foo/".to_string()),
6839                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6840                            name: "%bad".to_string(),
6841                            collection: None,
6842                        })),
6843                        target_name: Some("/".to_string()),
6844                        rights: Some(fio::Operations::CONNECT),
6845                        subdir: Some("/foo".to_string()),
6846                        dependency_type: Some(fdecl::DependencyType::Strong),
6847                        ..Default::default()
6848                    }),
6849                    fdecl::Offer::Runner(fdecl::OfferRunner {
6850                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6851                            name: "^bad".to_string(),
6852                            collection: None,
6853                        })),
6854                        source_name: Some("/path".to_string()),
6855                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6856                            name: "%bad".to_string(),
6857                            collection: None,
6858                        })),
6859                        target_name: Some("elf!".to_string()),
6860                        ..Default::default()
6861                    }),
6862                    fdecl::Offer::Resolver(fdecl::OfferResolver {
6863                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6864                            name: "^bad".to_string(),
6865                            collection: None,
6866                        })),
6867                        source_name: Some("/path".to_string()),
6868                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6869                            name: "%bad".to_string(),
6870                            collection: None,
6871                        })),
6872                        target_name: Some("pkg!".to_string()),
6873                        ..Default::default()
6874                    }),
6875                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6876                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6877                            name: "^bad".to_string(),
6878                            collection: None,
6879                        })),
6880                        source_name: Some("/path".to_string()),
6881                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6882                            name: "%bad".to_string(),
6883                            collection: None,
6884                        })),
6885                        target_name: Some("pkg!".to_string()),
6886                        dependency_type: Some(fdecl::DependencyType::Strong),
6887                        ..Default::default()
6888                    }),
6889                ]);
6890                decl
6891            },
6892            result = Err(ErrorList::new(vec![
6893                Error::invalid_field(DeclType::OfferService, "source.child.name"),
6894                Error::invalid_field(DeclType::OfferService, "source_name"),
6895                Error::invalid_field(DeclType::OfferService, "target.child.name"),
6896                Error::invalid_field(DeclType::OfferService, "target_name"),
6897                Error::invalid_field(DeclType::OfferProtocol, "source.child.name"),
6898                Error::invalid_field(DeclType::OfferProtocol, "source_name"),
6899                Error::invalid_field(DeclType::OfferProtocol, "target.child.name"),
6900                Error::invalid_field(DeclType::OfferProtocol, "target_name"),
6901                Error::invalid_field(DeclType::OfferDirectory, "source.child.name"),
6902                Error::invalid_field(DeclType::OfferDirectory, "source_name"),
6903                Error::invalid_field(DeclType::OfferDirectory, "target.child.name"),
6904                Error::invalid_field(DeclType::OfferDirectory, "target_name"),
6905                Error::invalid_field(DeclType::OfferDirectory, "subdir"),
6906                Error::invalid_field(DeclType::OfferRunner, "source.child.name"),
6907                Error::invalid_field(DeclType::OfferRunner, "source_name"),
6908                Error::invalid_field(DeclType::OfferRunner, "target.child.name"),
6909                Error::invalid_field(DeclType::OfferRunner, "target_name"),
6910                Error::invalid_field(DeclType::OfferResolver, "source.child.name"),
6911                Error::invalid_field(DeclType::OfferResolver, "source_name"),
6912                Error::invalid_field(DeclType::OfferResolver, "target.child.name"),
6913                Error::invalid_field(DeclType::OfferResolver, "target_name"),
6914                Error::invalid_field(DeclType::OfferDictionary, "source.child.name"),
6915                Error::invalid_field(DeclType::OfferDictionary, "source_name"),
6916                Error::invalid_field(DeclType::OfferDictionary, "target.child.name"),
6917                Error::invalid_field(DeclType::OfferDictionary, "target_name"),
6918            ])),
6919        },
6920        test_validate_offers_target_equals_source => {
6921            input = {
6922                let mut decl = new_component_decl();
6923                decl.offers = Some(vec![
6924                    fdecl::Offer::Service(fdecl::OfferService {
6925                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6926                            name: "logger".to_string(),
6927                            collection: None,
6928                        })),
6929                        source_name: Some("logger".to_string()),
6930                        target: Some(fdecl::Ref::Child(
6931                        fdecl::ChildRef {
6932                            name: "logger".to_string(),
6933                            collection: None,
6934                        }
6935                        )),
6936                        target_name: Some("logger".to_string()),
6937                        ..Default::default()
6938                    }),
6939                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6940                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6941                            name: "logger".to_string(),
6942                            collection: None,
6943                        })),
6944                        source_name: Some("legacy_logger".to_string()),
6945                        target: Some(fdecl::Ref::Child(
6946                        fdecl::ChildRef {
6947                            name: "logger".to_string(),
6948                            collection: None,
6949                        }
6950                        )),
6951                        target_name: Some("weak_legacy_logger".to_string()),
6952                        dependency_type: Some(fdecl::DependencyType::Weak),
6953                        ..Default::default()
6954                    }),
6955                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6956                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6957                            name: "logger".to_string(),
6958                            collection: None,
6959                        })),
6960                        source_name: Some("legacy_logger".to_string()),
6961                        target: Some(fdecl::Ref::Child(
6962                        fdecl::ChildRef {
6963                            name: "logger".to_string(),
6964                            collection: None,
6965                        }
6966                        )),
6967                        target_name: Some("strong_legacy_logger".to_string()),
6968                        dependency_type: Some(fdecl::DependencyType::Strong),
6969                        ..Default::default()
6970                    }),
6971                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6972                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6973                            name: "logger".to_string(),
6974                            collection: None,
6975                        })),
6976                        source_name: Some("assets".to_string()),
6977                        target: Some(fdecl::Ref::Child(
6978                        fdecl::ChildRef {
6979                            name: "logger".to_string(),
6980                            collection: None,
6981                        }
6982                        )),
6983                        target_name: Some("assets".to_string()),
6984                        rights: Some(fio::Operations::CONNECT),
6985                        subdir: None,
6986                        dependency_type: Some(fdecl::DependencyType::Strong),
6987                        ..Default::default()
6988                    }),
6989                    fdecl::Offer::Runner(fdecl::OfferRunner {
6990                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6991                            name: "logger".to_string(),
6992                            collection: None,
6993                        })),
6994                        source_name: Some("web".to_string()),
6995                        target: Some(fdecl::Ref::Child(
6996                        fdecl::ChildRef {
6997                            name: "logger".to_string(),
6998                            collection: None,
6999                        }
7000                        )),
7001                        target_name: Some("web".to_string()),
7002                        ..Default::default()
7003                    }),
7004                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7005                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7006                            name: "logger".to_string(),
7007                            collection: None,
7008                        })),
7009                        source_name: Some("pkg".to_string()),
7010                        target: Some(fdecl::Ref::Child(
7011                        fdecl::ChildRef {
7012                            name: "logger".to_string(),
7013                            collection: None,
7014                        }
7015                        )),
7016                        target_name: Some("pkg".to_string()),
7017                        ..Default::default()
7018                    }),
7019                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7020                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7021                            name: "logger".to_string(),
7022                            collection: None,
7023                        })),
7024                        source_name: Some("dict".to_string()),
7025                        target: Some(fdecl::Ref::Child(
7026                        fdecl::ChildRef {
7027                            name: "logger".to_string(),
7028                            collection: None,
7029                        }
7030                        )),
7031                        target_name: Some("dict".to_string()),
7032                        dependency_type: Some(fdecl::DependencyType::Strong),
7033                        ..Default::default()
7034                    }),
7035                ]);
7036                decl.children = Some(vec![fdecl::Child{
7037                    name: Some("logger".to_string()),
7038                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
7039                    startup: Some(fdecl::StartupMode::Lazy),
7040                    on_terminate: None,
7041                    environment: None,
7042                    ..Default::default()
7043                }]);
7044                decl
7045            },
7046            result = Err(ErrorList::new(vec![
7047                Error::dependency_cycle("{{child logger -> child logger}}"),
7048            ])),
7049        },
7050        test_validate_offers_storage_target_equals_source => {
7051            input = fdecl::Component {
7052                offers: Some(vec![
7053                    fdecl::Offer::Storage(fdecl::OfferStorage {
7054                        source_name: Some("data".to_string()),
7055                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef { })),
7056                        target: Some(fdecl::Ref::Child(
7057                            fdecl::ChildRef {
7058                                name: "logger".to_string(),
7059                                collection: None,
7060                            }
7061                        )),
7062                        target_name: Some("data".to_string()),
7063                        ..Default::default()
7064                    })
7065                ]),
7066                capabilities: Some(vec![
7067                    fdecl::Capability::Storage(fdecl::Storage {
7068                        name: Some("data".to_string()),
7069                        backing_dir: Some("minfs".to_string()),
7070                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7071                            name: "logger".to_string(),
7072                            collection: None,
7073                        })),
7074                        subdir: None,
7075                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
7076                        ..Default::default()
7077                    }),
7078                ]),
7079                children: Some(vec![
7080                    fdecl::Child {
7081                        name: Some("logger".to_string()),
7082                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
7083                        startup: Some(fdecl::StartupMode::Lazy),
7084                        on_terminate: None,
7085                        environment: None,
7086                        ..Default::default()
7087                    },
7088                ]),
7089                ..new_component_decl()
7090            },
7091            result = Err(ErrorList::new(vec![
7092                Error::dependency_cycle("{{child logger -> capability data -> child logger}}"),
7093            ])),
7094        },
7095        test_validate_offers_invalid_child => {
7096            input = {
7097                let mut decl = new_component_decl();
7098                decl.offers = Some(vec![
7099                    fdecl::Offer::Service(fdecl::OfferService {
7100                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7101                            name: "logger".to_string(),
7102                            collection: None,
7103                        })),
7104                        source_name: Some("fuchsia.logger.Log".to_string()),
7105                        target: Some(fdecl::Ref::Child(
7106                        fdecl::ChildRef {
7107                            name: "netstack".to_string(),
7108                            collection: None,
7109                        }
7110                        )),
7111                        target_name: Some("fuchsia.logger.Log".to_string()),
7112                        ..Default::default()
7113                    }),
7114                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7115                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7116                            name: "logger".to_string(),
7117                            collection: None,
7118                        })),
7119                        source_name: Some("fuchsia.logger.LegacyLog".to_string()),
7120                        target: Some(fdecl::Ref::Child(
7121                        fdecl::ChildRef {
7122                            name: "netstack".to_string(),
7123                            collection: None,
7124                        }
7125                        )),
7126                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7127                        dependency_type: Some(fdecl::DependencyType::Strong),
7128                        ..Default::default()
7129                    }),
7130                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7131                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7132                            name: "logger".to_string(),
7133                            collection: None,
7134                        })),
7135                        source_name: Some("assets".to_string()),
7136                        target: Some(fdecl::Ref::Collection(
7137                        fdecl::CollectionRef { name: "modular".to_string() }
7138                        )),
7139                        target_name: Some("assets".to_string()),
7140                        rights: Some(fio::Operations::CONNECT),
7141                        subdir: None,
7142                        dependency_type: Some(fdecl::DependencyType::Weak),
7143                        ..Default::default()
7144                    }),
7145                ]);
7146                decl.capabilities = Some(vec![
7147                    fdecl::Capability::Storage(fdecl::Storage {
7148                        name: Some("memfs".to_string()),
7149                        backing_dir: Some("memfs".to_string()),
7150                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7151                            name: "logger".to_string(),
7152                            collection: None,
7153                        })),
7154                        subdir: None,
7155                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
7156                        ..Default::default()
7157                    }),
7158                ]);
7159                decl.children = Some(vec![
7160                    fdecl::Child {
7161                        name: Some("netstack".to_string()),
7162                        url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
7163                        startup: Some(fdecl::StartupMode::Lazy),
7164                        on_terminate: None,
7165                        environment: None,
7166                        ..Default::default()
7167                    },
7168                ]);
7169                decl.collections = Some(vec![
7170                    fdecl::Collection {
7171                        name: Some("modular".to_string()),
7172                        durability: Some(fdecl::Durability::Transient),
7173                        environment: None,
7174                        allowed_offers: Some(fdecl::AllowedOffers::StaticAndDynamic),
7175                        allow_long_names: None,
7176                        ..Default::default()
7177                    },
7178                ]);
7179                decl
7180            },
7181            result = Err(ErrorList::new(vec![
7182                Error::invalid_child(DeclType::Storage, "source", "logger"),
7183                Error::invalid_child(DeclType::OfferService, "source", "logger"),
7184                Error::invalid_child(DeclType::OfferProtocol, "source", "logger"),
7185                Error::invalid_child(DeclType::OfferDirectory, "source", "logger"),
7186            ])),
7187        },
7188        test_validate_offers_invalid_source_capability => {
7189            input = {
7190                fdecl::Component {
7191                    offers: Some(vec![
7192                        fdecl::Offer::Protocol(fdecl::OfferProtocol {
7193                            source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
7194                                name: "this-storage-doesnt-exist".to_string(),
7195                            })),
7196                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
7197                            target: Some(fdecl::Ref::Child(
7198                            fdecl::ChildRef {
7199                                name: "netstack".to_string(),
7200                                collection: None,
7201                            }
7202                            )),
7203                            target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
7204                            dependency_type: Some(fdecl::DependencyType::Strong),
7205                            ..Default::default()
7206                        }),
7207                    ]),
7208                    ..new_component_decl()
7209                }
7210            },
7211            result = Err(ErrorList::new(vec![
7212                Error::invalid_capability(DeclType::OfferProtocol, "source", "this-storage-doesnt-exist"),
7213                Error::invalid_child(DeclType::OfferProtocol, "target", "netstack"),
7214            ])),
7215        },
7216        test_validate_offers_target => {
7217            input = {
7218                let mut decl = new_component_decl();
7219                decl.offers = Some(vec![
7220                    fdecl::Offer::Service(fdecl::OfferService {
7221                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
7222                            name: "modular".into()
7223                        })),
7224                        source_name: Some("logger".to_string()),
7225                        target: Some(fdecl::Ref::Child(
7226                        fdecl::ChildRef {
7227                            name: "netstack".to_string(),
7228                            collection: None,
7229                        }
7230                        )),
7231                        target_name: Some("fuchsia.logger.Log".to_string()),
7232                        ..Default::default()
7233                    }),
7234                    fdecl::Offer::Service(fdecl::OfferService {
7235                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
7236                            name: "modular".into()
7237                        })),
7238                        source_name: Some("logger".to_string()),
7239                        target: Some(fdecl::Ref::Child(
7240                        fdecl::ChildRef {
7241                            name: "netstack".to_string(),
7242                            collection: None,
7243                        }
7244                        )),
7245                        target_name: Some("fuchsia.logger.Log".to_string()),
7246                        ..Default::default()
7247                    }),
7248                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7249                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7250                        source_name: Some("fuchsia.logger.LegacyLog".to_string()),
7251                        target: Some(fdecl::Ref::Child(
7252                        fdecl::ChildRef {
7253                            name: "netstack".to_string(),
7254                            collection: None,
7255                        }
7256                        )),
7257                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7258                        dependency_type: Some(fdecl::DependencyType::Strong),
7259                        ..Default::default()
7260                    }),
7261                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7262                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7263                        source_name: Some("fuchsia.logger.LegacyLog".to_string()),
7264                        target: Some(fdecl::Ref::Child(
7265                        fdecl::ChildRef {
7266                            name: "netstack".to_string(),
7267                            collection: None,
7268                        }
7269                        )),
7270                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7271                        dependency_type: Some(fdecl::DependencyType::Strong),
7272                        ..Default::default()
7273                    }),
7274                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7275                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7276                        source_name: Some("assets".to_string()),
7277                        target: Some(fdecl::Ref::Collection(
7278                        fdecl::CollectionRef { name: "modular".to_string() }
7279                        )),
7280                        target_name: Some("assets".to_string()),
7281                        rights: Some(fio::Operations::CONNECT),
7282                        subdir: None,
7283                        dependency_type: Some(fdecl::DependencyType::Strong),
7284                        ..Default::default()
7285                    }),
7286                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7287                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7288                        source_name: Some("assets".to_string()),
7289                        target: Some(fdecl::Ref::Collection(
7290                        fdecl::CollectionRef { name: "modular".to_string() }
7291                        )),
7292                        target_name: Some("assets".to_string()),
7293                        rights: Some(fio::Operations::CONNECT),
7294                        subdir: None,
7295                        dependency_type: Some(fdecl::DependencyType::Weak),
7296                        ..Default::default()
7297                    }),
7298                    fdecl::Offer::Storage(fdecl::OfferStorage {
7299                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7300                        source_name: Some("data".to_string()),
7301                        target: Some(fdecl::Ref::Collection(
7302                        fdecl::CollectionRef { name: "modular".to_string() }
7303                        )),
7304                        target_name: Some("data".to_string()),
7305                        ..Default::default()
7306                    }),
7307                    fdecl::Offer::Storage(fdecl::OfferStorage {
7308                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7309                        source_name: Some("data".to_string()),
7310                        target: Some(fdecl::Ref::Collection(
7311                        fdecl::CollectionRef { name: "modular".to_string() }
7312                        )),
7313                        target_name: Some("data".to_string()),
7314                        ..Default::default()
7315                    }),
7316                    fdecl::Offer::Runner(fdecl::OfferRunner {
7317                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7318                        source_name: Some("elf".to_string()),
7319                        target: Some(fdecl::Ref::Collection(
7320                        fdecl::CollectionRef { name: "modular".to_string() }
7321                        )),
7322                        target_name: Some("duplicated".to_string()),
7323                        ..Default::default()
7324                    }),
7325                    fdecl::Offer::Runner(fdecl::OfferRunner {
7326                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7327                        source_name: Some("elf".to_string()),
7328                        target: Some(fdecl::Ref::Collection(
7329                        fdecl::CollectionRef { name: "modular".to_string() }
7330                        )),
7331                        target_name: Some("duplicated".to_string()),
7332                        ..Default::default()
7333                    }),
7334                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7335                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7336                        source_name: Some("pkg".to_string()),
7337                        target: Some(fdecl::Ref::Collection(
7338                        fdecl::CollectionRef { name: "modular".to_string() }
7339                        )),
7340                        target_name: Some("duplicated".to_string()),
7341                        ..Default::default()
7342                    }),
7343                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
7344                        source_name: Some("started".to_string()),
7345                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7346                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "netstack".to_string(), collection: None})),
7347                        target_name: Some("started".to_string()),
7348                        ..Default::default()
7349                    }),
7350                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
7351                        source_name: Some("started".to_string()),
7352                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7353                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "netstack".to_string(), collection: None})),
7354                        target_name: Some("started".to_string()),
7355                        ..Default::default()
7356                    }),
7357                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7358                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7359                        source_name: Some("a".to_string()),
7360                        target: Some(fdecl::Ref::Collection(
7361                            fdecl::CollectionRef { name: "modular".to_string() }
7362                        )),
7363                        target_name: Some("dict".to_string()),
7364                        dependency_type: Some(fdecl::DependencyType::Strong),
7365                        ..Default::default()
7366                    }),
7367                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7368                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7369                        source_name: Some("b".to_string()),
7370                        target: Some(fdecl::Ref::Collection(
7371                            fdecl::CollectionRef { name: "modular".to_string() }
7372                        )),
7373                        target_name: Some("dict".to_string()),
7374                        dependency_type: Some(fdecl::DependencyType::Strong),
7375                        ..Default::default()
7376                    }),
7377                ]);
7378                decl.children = Some(vec![
7379                    fdecl::Child{
7380                        name: Some("netstack".to_string()),
7381                        url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
7382                        startup: Some(fdecl::StartupMode::Eager),
7383                        on_terminate: None,
7384                        environment: None,
7385                        ..Default::default()
7386                    },
7387                ]);
7388                decl.collections = Some(vec![
7389                    fdecl::Collection{
7390                        name: Some("modular".to_string()),
7391                        durability: Some(fdecl::Durability::Transient),
7392                        environment: None,
7393                        allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7394                        allow_long_names: None,
7395                        ..Default::default()
7396                    },
7397                ]);
7398                decl
7399            },
7400            result = Err(ErrorList::new(vec![
7401                // Duplicate services are allowed, for aggregation.
7402                Error::duplicate_field(DeclType::OfferProtocol, "target_name", "fuchsia.logger.LegacyLog"),
7403                Error::duplicate_field(DeclType::OfferDirectory, "target_name", "assets"),
7404                Error::duplicate_field(DeclType::OfferStorage, "target_name", "data"),
7405                Error::duplicate_field(DeclType::OfferRunner, "target_name", "duplicated"),
7406                Error::duplicate_field(DeclType::OfferResolver, "target_name", "duplicated"),
7407                Error::duplicate_field(DeclType::OfferEventStream, "target_name", "started"),
7408                Error::duplicate_field(DeclType::OfferDictionary, "target_name", "dict"),
7409            ])),
7410        },
7411        test_validate_offers_target_invalid => {
7412            input = {
7413                let mut decl = new_component_decl();
7414                decl.offers = Some(vec![
7415                    fdecl::Offer::Service(fdecl::OfferService {
7416                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7417                        source_name: Some("logger".to_string()),
7418                        target: Some(fdecl::Ref::Child(
7419                        fdecl::ChildRef {
7420                            name: "netstack".to_string(),
7421                            collection: None,
7422                        }
7423                        )),
7424                        target_name: Some("fuchsia.logger.Log".to_string()),
7425                        ..Default::default()
7426                    }),
7427                    fdecl::Offer::Service(fdecl::OfferService {
7428                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7429                        source_name: Some("logger".to_string()),
7430                        target: Some(fdecl::Ref::Collection(
7431                        fdecl::CollectionRef { name: "modular".to_string(), }
7432                        )),
7433                        target_name: Some("fuchsia.logger.Log".to_string()),
7434                        ..Default::default()
7435                    }),
7436                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7437                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7438                        source_name: Some("legacy_logger".to_string()),
7439                        target: Some(fdecl::Ref::Child(
7440                        fdecl::ChildRef {
7441                            name: "netstack".to_string(),
7442                            collection: None,
7443                        }
7444                        )),
7445                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7446                        dependency_type: Some(fdecl::DependencyType::Weak),
7447                        ..Default::default()
7448                    }),
7449                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7450                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7451                        source_name: Some("legacy_logger".to_string()),
7452                        target: Some(fdecl::Ref::Collection(
7453                        fdecl::CollectionRef { name: "modular".to_string(), }
7454                        )),
7455                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7456                        dependency_type: Some(fdecl::DependencyType::Strong),
7457                        ..Default::default()
7458                    }),
7459                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7460                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7461                        source_name: Some("assets".to_string()),
7462                        target: Some(fdecl::Ref::Child(
7463                        fdecl::ChildRef {
7464                            name: "netstack".to_string(),
7465                            collection: None,
7466                        }
7467                        )),
7468                        target_name: Some("data".to_string()),
7469                        rights: Some(fio::Operations::CONNECT),
7470                        subdir: None,
7471                        dependency_type: Some(fdecl::DependencyType::Strong),
7472                        ..Default::default()
7473                    }),
7474                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7475                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7476                        source_name: Some("assets".to_string()),
7477                        target: Some(fdecl::Ref::Collection(
7478                        fdecl::CollectionRef { name: "modular".to_string(), }
7479                        )),
7480                        target_name: Some("data".to_string()),
7481                        rights: Some(fio::Operations::CONNECT),
7482                        subdir: None,
7483                        dependency_type: Some(fdecl::DependencyType::Weak),
7484                        ..Default::default()
7485                    }),
7486                    fdecl::Offer::Storage(fdecl::OfferStorage {
7487                        source_name: Some("data".to_string()),
7488                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7489                        target: Some(fdecl::Ref::Child(
7490                            fdecl::ChildRef {
7491                                name: "netstack".to_string(),
7492                                collection: None,
7493                            }
7494                        )),
7495                        target_name: Some("data".to_string()),
7496                        ..Default::default()
7497                    }),
7498                    fdecl::Offer::Storage(fdecl::OfferStorage {
7499                        source_name: Some("data".to_string()),
7500                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7501                        target: Some(fdecl::Ref::Collection(
7502                            fdecl::CollectionRef { name: "modular".to_string(), }
7503                        )),
7504                        target_name: Some("data".to_string()),
7505                        ..Default::default()
7506                    }),
7507                    fdecl::Offer::Runner(fdecl::OfferRunner {
7508                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7509                        source_name: Some("elf".to_string()),
7510                        target: Some(fdecl::Ref::Child(
7511                            fdecl::ChildRef {
7512                                name: "netstack".to_string(),
7513                                collection: None,
7514                            }
7515                        )),
7516                        target_name: Some("elf".to_string()),
7517                        ..Default::default()
7518                    }),
7519                    fdecl::Offer::Runner(fdecl::OfferRunner {
7520                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7521                        source_name: Some("elf".to_string()),
7522                        target: Some(fdecl::Ref::Collection(
7523                        fdecl::CollectionRef { name: "modular".to_string(), }
7524                        )),
7525                        target_name: Some("elf".to_string()),
7526                        ..Default::default()
7527                    }),
7528                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7529                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7530                        source_name: Some("pkg".to_string()),
7531                        target: Some(fdecl::Ref::Child(
7532                            fdecl::ChildRef {
7533                                name: "netstack".to_string(),
7534                                collection: None,
7535                            }
7536                        )),
7537                        target_name: Some("pkg".to_string()),
7538                        ..Default::default()
7539                    }),
7540                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7541                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7542                        source_name: Some("pkg".to_string()),
7543                        target: Some(fdecl::Ref::Collection(
7544                        fdecl::CollectionRef { name: "modular".to_string(), }
7545                        )),
7546                        target_name: Some("pkg".to_string()),
7547                        ..Default::default()
7548                    }),
7549                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7550                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7551                        source_name: Some("pkg".to_string()),
7552                        target: Some(fdecl::Ref::Child(
7553                            fdecl::ChildRef {
7554                                name: "netstack".to_string(),
7555                                collection: None,
7556                            }
7557                        )),
7558                        target_name: Some("pkg".to_string()),
7559                        dependency_type: Some(fdecl::DependencyType::Strong),
7560                        ..Default::default()
7561                    }),
7562                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7563                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7564                        source_name: Some("pkg".to_string()),
7565                        target: Some(fdecl::Ref::Collection(
7566                        fdecl::CollectionRef { name: "modular".to_string(), }
7567                        )),
7568                        target_name: Some("pkg".to_string()),
7569                        dependency_type: Some(fdecl::DependencyType::Strong),
7570                        ..Default::default()
7571                    }),
7572                ]);
7573                decl
7574            },
7575            result = Err(ErrorList::new(vec![
7576                Error::invalid_child(DeclType::OfferService, "target", "netstack"),
7577                Error::invalid_collection(DeclType::OfferService, "target", "modular"),
7578                Error::invalid_child(DeclType::OfferProtocol, "target", "netstack"),
7579                Error::invalid_collection(DeclType::OfferProtocol, "target", "modular"),
7580                Error::invalid_child(DeclType::OfferDirectory, "target", "netstack"),
7581                Error::invalid_collection(DeclType::OfferDirectory, "target", "modular"),
7582                Error::invalid_child(DeclType::OfferStorage, "target", "netstack"),
7583                Error::invalid_collection(DeclType::OfferStorage, "target", "modular"),
7584                Error::invalid_child(DeclType::OfferRunner, "target", "netstack"),
7585                Error::invalid_collection(DeclType::OfferRunner, "target", "modular"),
7586                Error::invalid_child(DeclType::OfferResolver, "target", "netstack"),
7587                Error::invalid_collection(DeclType::OfferResolver, "target", "modular"),
7588                Error::invalid_child(DeclType::OfferDictionary, "target", "netstack"),
7589                Error::invalid_collection(DeclType::OfferDictionary, "target", "modular"),
7590            ])),
7591        },
7592        test_validate_offers_target_dictionary => {
7593            input = fdecl::Component {
7594                offers: Some(vec![
7595                    // Offer to static dictionary is ok
7596                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7597                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7598                        source_name: Some("p".to_string()),
7599                        target: Some(fdecl::Ref::Capability(
7600                            fdecl::CapabilityRef {
7601                                name: "dict".into(),
7602                            },
7603                        )),
7604                        target_name: Some("p".into()),
7605                        dependency_type: Some(fdecl::DependencyType::Strong),
7606                        ..Default::default()
7607                    }),
7608                    // Offer to dynamic dictionary is forbidden
7609                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7610                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7611                        source_name: Some("p".to_string()),
7612                        target: Some(fdecl::Ref::Capability(
7613                            fdecl::CapabilityRef {
7614                                name: "dynamic".into(),
7615                            },
7616                        )),
7617                        target_name: Some("p".into()),
7618                        dependency_type: Some(fdecl::DependencyType::Strong),
7619                        ..Default::default()
7620                    }),
7621                ]),
7622                capabilities: Some(vec![
7623                    fdecl::Capability::Dictionary(fdecl::Dictionary {
7624                        name: Some("dict".into()),
7625                        ..Default::default()
7626                    }),
7627                    fdecl::Capability::Dictionary(fdecl::Dictionary {
7628                        name: Some("dynamic".into()),
7629                        source_path: Some("/out/dir".into()),
7630                        ..Default::default()
7631                    }),
7632                ]),
7633                ..Default::default()
7634            },
7635            result = Err(ErrorList::new(vec![
7636                Error::invalid_field(DeclType::OfferProtocol, "target"),
7637            ])),
7638        },
7639        test_validate_offers_invalid_source_collection => {
7640            input = {
7641                let mut decl = new_component_decl();
7642                decl.collections = Some(vec![
7643                    fdecl::Collection {
7644                        name: Some("col".to_string()),
7645                        durability: Some(fdecl::Durability::Transient),
7646                        allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7647                        allow_long_names: None,
7648                        ..Default::default()
7649                    }
7650                ]);
7651                decl.children = Some(vec![
7652                    fdecl::Child {
7653                        name: Some("child".to_string()),
7654                        url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7655                        startup: Some(fdecl::StartupMode::Lazy),
7656                        on_terminate: None,
7657                        ..Default::default()
7658                    }
7659                ]);
7660                decl.offers = Some(vec![
7661                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7662                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7663                        source_name: Some("a".to_string()),
7664                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7665                        target_name: Some("a".to_string()),
7666                        dependency_type: Some(fdecl::DependencyType::Strong),
7667                        ..Default::default()
7668                    }),
7669                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7670                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7671                        source_name: Some("b".to_string()),
7672                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7673                        target_name: Some("b".to_string()),
7674                        rights: Some(fio::Operations::CONNECT),
7675                        subdir: None,
7676                        dependency_type: Some(fdecl::DependencyType::Strong),
7677                        ..Default::default()
7678                    }),
7679                    fdecl::Offer::Storage(fdecl::OfferStorage {
7680                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7681                        source_name: Some("c".to_string()),
7682                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7683                        target_name: Some("c".to_string()),
7684                        ..Default::default()
7685                    }),
7686                    fdecl::Offer::Runner(fdecl::OfferRunner {
7687                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7688                        source_name: Some("d".to_string()),
7689                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7690                        target_name: Some("d".to_string()),
7691                        ..Default::default()
7692                    }),
7693                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7694                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7695                        source_name: Some("e".to_string()),
7696                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7697                        target_name: Some("e".to_string()),
7698                        ..Default::default()
7699                    }),
7700                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7701                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7702                        source_name: Some("f".to_string()),
7703                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7704                        target_name: Some("f".to_string()),
7705                        dependency_type: Some(fdecl::DependencyType::Strong),
7706                        ..Default::default()
7707                    }),
7708                ]);
7709                decl
7710            },
7711            result = Err(ErrorList::new(vec![
7712                Error::invalid_field(DeclType::OfferProtocol, "source"),
7713                Error::invalid_field(DeclType::OfferDirectory, "source"),
7714                Error::invalid_field(DeclType::OfferStorage, "source"),
7715                Error::invalid_field(DeclType::OfferRunner, "source"),
7716                Error::invalid_field(DeclType::OfferResolver, "source"),
7717                Error::invalid_field(DeclType::OfferDictionary, "source"),
7718            ])),
7719        },
7720        test_validate_offers_source_collection => {
7721            input = {
7722                let mut decl = new_component_decl();
7723                decl.collections = Some(vec![
7724                    fdecl::Collection {
7725                        name: Some("col".to_string()),
7726                        durability: Some(fdecl::Durability::Transient),
7727                        allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7728                        allow_long_names: None,
7729                        ..Default::default()
7730                    }
7731                ]);
7732                decl.children = Some(vec![
7733                    fdecl::Child {
7734                        name: Some("child".to_string()),
7735                        url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7736                        startup: Some(fdecl::StartupMode::Lazy),
7737                        on_terminate: None,
7738                        ..Default::default()
7739                    }
7740                ]);
7741                decl.offers = Some(vec![
7742                    fdecl::Offer::Service(fdecl::OfferService {
7743                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7744                        source_name: Some("a".to_string()),
7745                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7746                        target_name: Some("a".to_string()),
7747                        ..Default::default()
7748                    })
7749                ]);
7750                decl
7751            },
7752            result = Ok(()),
7753        },
7754        test_validate_offers_invalid_capability_from_self => {
7755            input = {
7756                let mut decl = new_component_decl();
7757                decl.children = Some(vec![
7758                    fdecl::Child {
7759                        name: Some("child".to_string()),
7760                        url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7761                        startup: Some(fdecl::StartupMode::Lazy),
7762                        ..Default::default()
7763                    }
7764                ]);
7765                decl.offers = Some(vec![
7766                    fdecl::Offer::Service(fdecl::OfferService {
7767                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7768                        source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7769                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7770                            name: "child".into(),
7771                            collection: None
7772                        })),
7773                        target_name: Some("foo".into()),
7774                        ..Default::default()
7775                    }),
7776                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7777                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7778                        source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7779                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7780                            name: "child".into(),
7781                            collection: None
7782                        })),
7783                        target_name: Some("bar".into()),
7784                        dependency_type: Some(fdecl::DependencyType::Strong),
7785                        ..Default::default()
7786                    }),
7787                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7788                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7789                        source_name: Some("dir".into()),
7790                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7791                            name: "child".into(),
7792                            collection: None
7793                        })),
7794                        target_name: Some("assets".into()),
7795                        dependency_type: Some(fdecl::DependencyType::Strong),
7796                        ..Default::default()
7797                    }),
7798                    fdecl::Offer::Runner(fdecl::OfferRunner {
7799                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7800                        source_name: Some("source_elf".into()),
7801                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7802                            name: "child".into(),
7803                            collection: None
7804                        })),
7805                        target_name: Some("elf".into()),
7806                        ..Default::default()
7807                    }),
7808                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7809                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7810                        source_name: Some("source_pkg".into()),
7811                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7812                            name: "child".into(),
7813                            collection: None
7814                        })),
7815                        target_name: Some("pkg".into()),
7816                        ..Default::default()
7817                    }),
7818                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7819                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7820                        source_name: Some("source_dict".into()),
7821                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7822                            name: "child".into(),
7823                            collection: None
7824                        })),
7825                        target_name: Some("dict".into()),
7826                        dependency_type: Some(fdecl::DependencyType::Strong),
7827                        ..Default::default()
7828                    }),
7829                    fdecl::Offer::Storage(fdecl::OfferStorage {
7830                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7831                        source_name: Some("source_storage".into()),
7832                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7833                            name: "child".into(),
7834                            collection: None
7835                        })),
7836                        target_name: Some("storage".into()),
7837                        ..Default::default()
7838                    }),
7839                    fdecl::Offer::Config(fdecl::OfferConfiguration {
7840                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7841                        source_name: Some("source_config".into()),
7842                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7843                            name: "child".into(),
7844                            collection: None
7845                        })),
7846                        target_name: Some("config".into()),
7847                        ..Default::default()
7848                    }),
7849                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7850                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7851                        source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7852                        source_dictionary: Some("dict/inner".into()),
7853                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7854                            name: "child".into(),
7855                            collection: None
7856                        })),
7857                        target_name: Some("baz".into()),
7858                        dependency_type: Some(fdecl::DependencyType::Strong),
7859                        ..Default::default()
7860                    }),
7861                ]);
7862                decl
7863            },
7864            result = Err(ErrorList::new(vec![
7865                Error::invalid_capability(
7866                    DeclType::OfferService,
7867                    "source",
7868                    "fuchsia.some.library.SomeProtocol"),
7869                Error::invalid_capability(
7870                    DeclType::OfferProtocol,
7871                    "source",
7872                    "fuchsia.some.library.SomeProtocol"),
7873                Error::invalid_capability(DeclType::OfferDirectory, "source", "dir"),
7874                Error::invalid_capability(DeclType::OfferRunner, "source", "source_elf"),
7875                Error::invalid_capability(DeclType::OfferResolver, "source", "source_pkg"),
7876                Error::invalid_capability(DeclType::OfferDictionary, "source", "source_dict"),
7877                Error::invalid_capability(DeclType::OfferStorage, "source", "source_storage"),
7878                Error::invalid_capability(DeclType::OfferConfig, "source", "source_config"),
7879                Error::invalid_capability(DeclType::OfferProtocol, "source", "dict"),
7880            ])),
7881        },
7882        test_validate_offers_long_dependency_cycle => {
7883            input = {
7884                let mut decl = new_component_decl();
7885                let dependencies = vec![
7886                    ("d", "b"),
7887                    ("a", "b"),
7888                    ("b", "c"),
7889                    ("b", "d"),
7890                    ("c", "a"),
7891                ];
7892                let offers = dependencies.into_iter().map(|(from,to)|
7893                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7894                        source: Some(fdecl::Ref::Child(
7895                        fdecl::ChildRef { name: from.to_string(), collection: None },
7896                        )),
7897                        source_name: Some(format!("thing_{}", from)),
7898                        target: Some(fdecl::Ref::Child(
7899                        fdecl::ChildRef { name: to.to_string(), collection: None },
7900                        )),
7901                        target_name: Some(format!("thing_{}", from)),
7902                        dependency_type: Some(fdecl::DependencyType::Strong),
7903                        ..Default::default()
7904                    })).collect();
7905                let children = ["a", "b", "c", "d"].iter().map(|name| {
7906                    fdecl::Child {
7907                        name: Some(name.to_string()),
7908                        url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
7909                        startup: Some(fdecl::StartupMode::Lazy),
7910                        on_terminate: None,
7911                        environment: None,
7912                        ..Default::default()
7913                    }
7914                }).collect();
7915                decl.offers = Some(offers);
7916                decl.children = Some(children);
7917                decl
7918            },
7919            result = Err(ErrorList::new(vec![
7920                Error::dependency_cycle("{{child a -> child b -> child c -> child a}, {child b -> child d -> child b}}")
7921            ])),
7922        },
7923        test_validate_offers_not_required_invalid_source_service => {
7924            input = {
7925                let mut decl = generate_offer_different_source_and_availability_decl(
7926                    |source, availability, target_name|
7927                        fdecl::Offer::Service(fdecl::OfferService {
7928                            source: Some(source),
7929                            source_name: Some("fuchsia.examples.Echo".to_string()),
7930                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7931                                name: "sink".to_string(),
7932                                collection: None,
7933                            })),
7934                            target_name: Some(target_name.into()),
7935                            availability: Some(availability),
7936                            ..Default::default()
7937                        })
7938                );
7939                decl.capabilities = Some(vec![
7940                    fdecl::Capability::Service(fdecl::Service {
7941                        name: Some("fuchsia.examples.Echo".to_string()),
7942                        source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
7943                        ..Default::default()
7944                    }),
7945                ]);
7946                decl
7947            },
7948            result = {
7949                Err(ErrorList::new(vec![
7950                    Error::availability_must_be_optional(
7951                        DeclType::OfferService,
7952                        "availability",
7953                        Some(&"fuchsia.examples.Echo".to_string()),
7954                    ),
7955                    Error::availability_must_be_optional(
7956                        DeclType::OfferService,
7957                        "availability",
7958                        Some(&"fuchsia.examples.Echo".to_string()),
7959                    ),
7960                ]))
7961            },
7962        },
7963        test_validate_offers_not_required_invalid_source_protocol => {
7964            input = {
7965                let mut decl = generate_offer_different_source_and_availability_decl(
7966                    |source, availability, target_name|
7967                        fdecl::Offer::Protocol(fdecl::OfferProtocol {
7968                            source: Some(source),
7969                            source_name: Some("fuchsia.examples.Echo".to_string()),
7970                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7971                                name: "sink".to_string(),
7972                                collection: None,
7973                            })),
7974                            target_name: Some(target_name.into()),
7975                            dependency_type: Some(fdecl::DependencyType::Strong),
7976                            availability: Some(availability),
7977                            ..Default::default()
7978                        })
7979                );
7980                decl.capabilities = Some(vec![
7981                    fdecl::Capability::Protocol(fdecl::Protocol {
7982                        name: Some("fuchsia.examples.Echo".to_string()),
7983                        source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
7984                        ..Default::default()
7985                    }),
7986                ]);
7987                decl
7988            },
7989            result = {
7990                Err(ErrorList::new(vec![
7991                    Error::availability_must_be_optional(
7992                        DeclType::OfferProtocol,
7993                        "availability",
7994                        Some(&"fuchsia.examples.Echo".to_string()),
7995                    ),
7996                    Error::availability_must_be_optional(
7997                        DeclType::OfferProtocol,
7998                        "availability",
7999                        Some(&"fuchsia.examples.Echo".to_string()),
8000                    ),
8001                ]))
8002            },
8003        },
8004        test_validate_offers_not_required_invalid_source_directory => {
8005            input = {
8006                let mut decl = generate_offer_different_source_and_availability_decl(
8007                    |source, availability, target_name|
8008                        fdecl::Offer::Directory(fdecl::OfferDirectory {
8009                            source: Some(source),
8010                            source_name: Some("assets".to_string()),
8011                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8012                                name: "sink".to_string(),
8013                                collection: None,
8014                            })),
8015                            target_name: Some(target_name.into()),
8016                            rights: Some(fio::Operations::CONNECT),
8017                            subdir: None,
8018                            dependency_type: Some(fdecl::DependencyType::Weak),
8019                            availability: Some(availability),
8020                            ..Default::default()
8021                        })
8022                );
8023                decl.capabilities = Some(vec![
8024                    fdecl::Capability::Directory(fdecl::Directory {
8025                        name: Some("assets".to_string()),
8026                        source_path: Some("/assets".to_string()),
8027                        rights: Some(fio::Operations::CONNECT),
8028                        ..Default::default()
8029                    }),
8030                ]);
8031                decl
8032            },
8033            result = {
8034                Err(ErrorList::new(vec![
8035                    Error::availability_must_be_optional(
8036                        DeclType::OfferDirectory,
8037                        "availability",
8038                        Some(&"assets".to_string()),
8039                    ),
8040                    Error::availability_must_be_optional(
8041                        DeclType::OfferDirectory,
8042                        "availability",
8043                        Some(&"assets".to_string()),
8044                    ),
8045                ]))
8046            },
8047        },
8048        test_validate_offers_not_required_invalid_source_storage => {
8049            input = {
8050                let mut decl = new_component_decl();
8051                decl.children = Some(vec![
8052                    fdecl::Child {
8053                        name: Some("sink".to_string()),
8054                        url: Some("fuchsia-pkg://fuchsia.com/sink#meta/sink.cm".to_string()),
8055                        startup: Some(fdecl::StartupMode::Lazy),
8056                        on_terminate: None,
8057                        environment: None,
8058                        ..Default::default()
8059                    },
8060                ]);
8061                decl.capabilities = Some(vec![
8062                    fdecl::Capability::Storage(fdecl::Storage {
8063                        name: Some("data".to_string()),
8064                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8065                        backing_dir: Some("minfs".to_string()),
8066                        subdir: None,
8067                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
8068                        ..Default::default()
8069                    }),
8070                ]);
8071                let new_offer = |source: fdecl::Ref, availability: fdecl::Availability,
8072                                        target_name: &str|
8073                {
8074                    fdecl::Offer::Storage(fdecl::OfferStorage {
8075                        source: Some(source),
8076                        source_name: Some("data".to_string()),
8077                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8078                            name: "sink".to_string(),
8079                            collection: None,
8080                        })),
8081                        target_name: Some(target_name.into()),
8082                        availability: Some(availability),
8083                        ..Default::default()
8084                    })
8085                };
8086                decl.offers = Some(vec![
8087                    // These offers are fine, offers with a source of parent or void can be
8088                    // optional.
8089                    new_offer(
8090                        fdecl::Ref::Parent(fdecl::ParentRef {}),
8091                        fdecl::Availability::Required,
8092                        "data0",
8093                    ),
8094                    new_offer(
8095                        fdecl::Ref::Parent(fdecl::ParentRef {}),
8096                        fdecl::Availability::Optional,
8097                        "data1",
8098                    ),
8099                    new_offer(
8100                        fdecl::Ref::Parent(fdecl::ParentRef {}),
8101                        fdecl::Availability::SameAsTarget,
8102                        "data2",
8103                    ),
8104                    new_offer(
8105                        fdecl::Ref::VoidType(fdecl::VoidRef {}),
8106                        fdecl::Availability::Optional,
8107                        "data3",
8108                    ),
8109                    // These offers are not fine, offers with a source other than parent or void
8110                    // must be required.
8111                    new_offer(
8112                        fdecl::Ref::Self_(fdecl::SelfRef {}),
8113                        fdecl::Availability::Optional,
8114                        "data4",
8115                    ),
8116                    new_offer(
8117                        fdecl::Ref::Self_(fdecl::SelfRef {}),
8118                        fdecl::Availability::SameAsTarget,
8119                        "data5",
8120                    ),
8121                    // These offers are also not fine, offers with a source of void must be optional
8122                    new_offer(
8123                        fdecl::Ref::VoidType(fdecl::VoidRef {}),
8124                        fdecl::Availability::Required,
8125                        "data6",
8126                    ),
8127                    new_offer(
8128                        fdecl::Ref::VoidType(fdecl::VoidRef {}),
8129                        fdecl::Availability::SameAsTarget,
8130                        "data7",
8131                    ),
8132                ]);
8133                decl
8134            },
8135            result = {
8136                Err(ErrorList::new(vec![
8137                    Error::availability_must_be_optional(
8138                        DeclType::OfferStorage,
8139                        "availability",
8140                        Some(&"data".to_string()),
8141                    ),
8142                    Error::availability_must_be_optional(
8143                        DeclType::OfferStorage,
8144                        "availability",
8145                        Some(&"data".to_string()),
8146                    ),
8147                ]))
8148            },
8149        },
8150
8151        test_validate_offers_valid_service_aggregation => {
8152            input = {
8153                let mut decl = new_component_decl();
8154                decl.offers = Some(vec![
8155                    fdecl::Offer::Service(fdecl::OfferService {
8156                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
8157                            name: "coll_a".to_string()
8158                        })),
8159                        source_name: Some("fuchsia.logger.Log".to_string()),
8160                        target: Some(fdecl::Ref::Child(
8161                            fdecl::ChildRef {
8162                                name: "child_c".to_string(),
8163                                collection: None,
8164                            }
8165                        )),
8166                        target_name: Some("fuchsia.logger.Log".to_string()),
8167                        source_instance_filter: None,
8168                        ..Default::default()
8169                    }),
8170                    fdecl::Offer::Service(fdecl::OfferService {
8171                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
8172                            name: "coll_b".to_string()
8173                        })),
8174                        source_name: Some("fuchsia.logger.Log".to_string()),
8175                        target: Some(fdecl::Ref::Child(
8176                            fdecl::ChildRef {
8177                                name: "child_c".to_string(),
8178                                collection: None,
8179                            }
8180                        )),
8181                        target_name: Some("fuchsia.logger.Log".to_string()),
8182                        source_instance_filter: Some(vec!["a_different_default".to_string()]),
8183                        ..Default::default()
8184                    })
8185                ]);
8186                decl.children = Some(vec![
8187                    fdecl::Child {
8188                        name: Some("child_c".to_string()),
8189                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
8190                        startup: Some(fdecl::StartupMode::Lazy),
8191                        ..Default::default()
8192                    },
8193                ]);
8194                decl.collections = Some(vec![
8195                    fdecl::Collection {
8196                        name: Some("coll_a".into()),
8197                        durability: Some(fdecl::Durability::Transient),
8198                        ..Default::default()
8199                    },
8200                    fdecl::Collection {
8201                        name: Some("coll_b".into()),
8202                        durability: Some(fdecl::Durability::Transient),
8203                        ..Default::default()
8204                    },
8205                ]);
8206                decl
8207            },
8208            result = Ok(()),
8209        },
8210
8211        // dictionaries
8212        test_validate_source_dictionary => {
8213            input = fdecl::Component {
8214                program: Some(fdecl::Program {
8215                    runner: Some("elf".into()),
8216                    info: Some(fdata::Dictionary {
8217                        entries: None,
8218                        ..Default::default()
8219                    }),
8220                    ..Default::default()
8221                }),
8222                uses: Some(vec![
8223                    fdecl::Use::Protocol(fdecl::UseProtocol {
8224                        dependency_type: Some(fdecl::DependencyType::Strong),
8225                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8226                        source_dictionary: Some("bad//".into()),
8227                        source_name: Some("foo".into()),
8228                        target_path: Some("/svc/foo".into()),
8229                        ..Default::default()
8230                    }),
8231                ]),
8232                exposes: Some(vec![
8233                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
8234                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8235                            name: "missing".into(),
8236                            collection: None,
8237                        })),
8238                        source_dictionary: Some("in/dict".into()),
8239                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8240                        source_name: Some("foo".into()),
8241                        target_name: Some("bar".into()),
8242                        ..Default::default()
8243                    }),
8244                ]),
8245                offers: Some(vec![
8246                    fdecl::Offer::Service(fdecl::OfferService {
8247                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8248                        source_dictionary: Some("bad//".into()),
8249                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8250                            name: "child".into(),
8251                            collection: None,
8252                        })),
8253                        source_name: Some("foo".into()),
8254                        target_name: Some("bar".into()),
8255                        ..Default::default()
8256                    }),
8257                ]),
8258                children: Some(vec![
8259                    fdecl::Child {
8260                        name: Some("child".into()),
8261                        url: Some("fuchsia-pkg://child".into()),
8262                        startup: Some(fdecl::StartupMode::Lazy),
8263                        ..Default::default()
8264                    },
8265                ]),
8266                ..Default::default()
8267            },
8268            result = Err(ErrorList::new(vec![
8269                Error::invalid_field(DeclType::UseProtocol, "source_dictionary"),
8270                Error::invalid_child(DeclType::ExposeDirectory, "source", "missing"),
8271                Error::invalid_field(DeclType::OfferService, "source_dictionary"),
8272            ])),
8273        },
8274        test_validate_dictionary_too_long => {
8275            input = fdecl::Component {
8276                program: Some(fdecl::Program {
8277                    runner: Some("elf".into()),
8278                    info: Some(fdata::Dictionary {
8279                        entries: None,
8280                        ..Default::default()
8281                    }),
8282                    ..Default::default()
8283                }),
8284                uses: Some(vec![
8285                    fdecl::Use::Protocol(fdecl::UseProtocol {
8286                        dependency_type: Some(fdecl::DependencyType::Strong),
8287                        source: Some(fdecl::Ref::Parent( fdecl::ParentRef {} )),
8288                        source_dictionary: Some("a".repeat(4096)),
8289                        source_name: Some("foo".into()),
8290                        target_path: Some("/svc/foo".into()),
8291                        ..Default::default()
8292                    }),
8293                ]),
8294                ..Default::default()
8295            },
8296            result = Err(ErrorList::new(vec![
8297                Error::field_too_long(DeclType::UseProtocol, "source_dictionary"),
8298            ])),
8299        },
8300
8301        // environments
8302        test_validate_environment_empty => {
8303            input = {
8304                let mut decl = new_component_decl();
8305                decl.environments = Some(vec![fdecl::Environment {
8306                    name: None,
8307                    extends: None,
8308                    runners: None,
8309                    resolvers: None,
8310                    stop_timeout_ms: None,
8311                    debug_capabilities: None,
8312                    ..Default::default()
8313                }]);
8314                decl
8315            },
8316            result = Err(ErrorList::new(vec![
8317                Error::missing_field(DeclType::Environment, "name"),
8318                Error::missing_field(DeclType::Environment, "extends"),
8319            ])),
8320        },
8321
8322        test_validate_environment_no_stop_timeout => {
8323            input = {
8324                let mut decl = new_component_decl();
8325                decl.environments = Some(vec![fdecl::Environment {
8326                    name: Some("env".to_string()),
8327                    extends: Some(fdecl::EnvironmentExtends::None),
8328                    runners: None,
8329                    resolvers: None,
8330                    stop_timeout_ms: None,
8331                    ..Default::default()
8332                }]);
8333                decl
8334            },
8335            result = Err(ErrorList::new(vec![Error::missing_field(DeclType::Environment, "stop_timeout_ms")])),
8336        },
8337
8338        test_validate_environment_extends_stop_timeout => {
8339            input = {  let mut decl = new_component_decl();
8340                decl.environments = Some(vec![fdecl::Environment {
8341                    name: Some("env".to_string()),
8342                    extends: Some(fdecl::EnvironmentExtends::Realm),
8343                    runners: None,
8344                    resolvers: None,
8345                    stop_timeout_ms: None,
8346                    ..Default::default()
8347                }]);
8348                decl
8349            },
8350            result = Ok(()),
8351        },
8352        test_validate_environment_long_identifiers => {
8353            input = {
8354                let mut decl = new_component_decl();
8355                decl.environments = Some(vec![fdecl::Environment {
8356                    name: Some("a".repeat(256)),
8357                    extends: Some(fdecl::EnvironmentExtends::None),
8358                    runners: Some(vec![
8359                        fdecl::RunnerRegistration {
8360                            source_name: Some("a".repeat(256)),
8361                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8362                            target_name: Some("a".repeat(256)),
8363                            ..Default::default()
8364                        },
8365                    ]),
8366                    resolvers: Some(vec![
8367                        fdecl::ResolverRegistration {
8368                            resolver: Some("a".repeat(256)),
8369                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8370                            scheme: Some("a".repeat(256)),
8371                            ..Default::default()
8372                        },
8373                    ]),
8374                    stop_timeout_ms: Some(1234),
8375                    ..Default::default()
8376                }]);
8377                decl
8378            },
8379            result = Err(ErrorList::new(vec![
8380                Error::field_too_long(DeclType::Environment, "name"),
8381                Error::field_too_long(DeclType::RunnerRegistration, "source_name"),
8382                Error::field_too_long(DeclType::RunnerRegistration, "target_name"),
8383                Error::field_too_long(DeclType::ResolverRegistration, "resolver"),
8384                Error::field_too_long(DeclType::ResolverRegistration, "scheme"),
8385            ])),
8386        },
8387        test_validate_environment_empty_runner_resolver_fields => {
8388            input = {
8389                let mut decl = new_component_decl();
8390                decl.environments = Some(vec![fdecl::Environment {
8391                    name: Some("a".to_string()),
8392                    extends: Some(fdecl::EnvironmentExtends::None),
8393                    runners: Some(vec![
8394                        fdecl::RunnerRegistration {
8395                            source_name: None,
8396                            source: None,
8397                            target_name: None,
8398                            ..Default::default()
8399                        },
8400                    ]),
8401                    resolvers: Some(vec![
8402                        fdecl::ResolverRegistration {
8403                            resolver: None,
8404                            source: None,
8405                            scheme: None,
8406                            ..Default::default()
8407                        },
8408                    ]),
8409                    stop_timeout_ms: Some(1234),
8410                    ..Default::default()
8411                }]);
8412                decl
8413            },
8414            result = Err(ErrorList::new(vec![
8415                Error::missing_field(DeclType::RunnerRegistration, "source_name"),
8416                Error::missing_field(DeclType::RunnerRegistration, "source"),
8417                Error::missing_field(DeclType::RunnerRegistration, "target_name"),
8418                Error::missing_field(DeclType::ResolverRegistration, "resolver"),
8419                Error::missing_field(DeclType::ResolverRegistration, "source"),
8420                Error::missing_field(DeclType::ResolverRegistration, "scheme"),
8421            ])),
8422        },
8423        test_validate_environment_invalid_fields => {
8424            input = {
8425                let mut decl = new_component_decl();
8426                decl.environments = Some(vec![fdecl::Environment {
8427                    name: Some("a".to_string()),
8428                    extends: Some(fdecl::EnvironmentExtends::None),
8429                    runners: Some(vec![
8430                        fdecl::RunnerRegistration {
8431                            source_name: Some("^a".to_string()),
8432                            source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
8433                            target_name: Some("%a".to_string()),
8434                            ..Default::default()
8435                        },
8436                    ]),
8437                    resolvers: Some(vec![
8438                        fdecl::ResolverRegistration {
8439                            resolver: Some("^a".to_string()),
8440                            source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
8441                            scheme: Some("9scheme".to_string()),
8442                            ..Default::default()
8443                        },
8444                    ]),
8445                    stop_timeout_ms: Some(1234),
8446                    ..Default::default()
8447                }]);
8448                decl
8449            },
8450            result = Err(ErrorList::new(vec![
8451                Error::invalid_field(DeclType::RunnerRegistration, "source_name"),
8452                Error::invalid_field(DeclType::RunnerRegistration, "source"),
8453                Error::invalid_field(DeclType::RunnerRegistration, "target_name"),
8454                Error::invalid_field(DeclType::ResolverRegistration, "resolver"),
8455                Error::invalid_field(DeclType::ResolverRegistration, "source"),
8456                Error::invalid_field(DeclType::ResolverRegistration, "scheme"),
8457            ])),
8458        },
8459        test_validate_environment_missing_runner => {
8460            input = {
8461                let mut decl = new_component_decl();
8462                decl.environments = Some(vec![fdecl::Environment {
8463                    name: Some("a".to_string()),
8464                    extends: Some(fdecl::EnvironmentExtends::None),
8465                    runners: Some(vec![
8466                        fdecl::RunnerRegistration {
8467                            source_name: Some("dart".to_string()),
8468                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
8469                            target_name: Some("dart".to_string()),
8470                            ..Default::default()
8471                        },
8472                    ]),
8473                    resolvers: None,
8474                    stop_timeout_ms: Some(1234),
8475                    ..Default::default()
8476                }]);
8477                decl
8478            },
8479            result = Err(ErrorList::new(vec![
8480                Error::invalid_runner(DeclType::RunnerRegistration, "source_name", "dart"),
8481            ])),
8482        },
8483        test_validate_environment_duplicate_registrations => {
8484            input = {
8485                let mut decl = new_component_decl();
8486                decl.environments = Some(vec![fdecl::Environment {
8487                    name: Some("a".to_string()),
8488                    extends: Some(fdecl::EnvironmentExtends::None),
8489                    runners: Some(vec![
8490                        fdecl::RunnerRegistration {
8491                            source_name: Some("dart".to_string()),
8492                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8493                            target_name: Some("dart".to_string()),
8494                            ..Default::default()
8495                        },
8496                        fdecl::RunnerRegistration {
8497                            source_name: Some("other-dart".to_string()),
8498                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8499                            target_name: Some("dart".to_string()),
8500                            ..Default::default()
8501                        },
8502                    ]),
8503                    resolvers: Some(vec![
8504                        fdecl::ResolverRegistration {
8505                            resolver: Some("pkg_resolver".to_string()),
8506                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8507                            scheme: Some("fuchsia-pkg".to_string()),
8508                            ..Default::default()
8509                        },
8510                        fdecl::ResolverRegistration {
8511                            resolver: Some("base_resolver".to_string()),
8512                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8513                            scheme: Some("fuchsia-pkg".to_string()),
8514                            ..Default::default()
8515                        },
8516                    ]),
8517                    stop_timeout_ms: Some(1234),
8518                    ..Default::default()
8519                }]);
8520                decl
8521            },
8522            result = Err(ErrorList::new(vec![
8523                Error::duplicate_field(DeclType::RunnerRegistration, "target_name", "dart"),
8524                Error::duplicate_field(DeclType::ResolverRegistration, "scheme", "fuchsia-pkg"),
8525            ])),
8526        },
8527        test_validate_environment_from_missing_child => {
8528            input = {
8529                let mut decl = new_component_decl();
8530                decl.environments = Some(vec![fdecl::Environment {
8531                    name: Some("a".to_string()),
8532                    extends: Some(fdecl::EnvironmentExtends::None),
8533                    runners: Some(vec![
8534                        fdecl::RunnerRegistration {
8535                            source_name: Some("elf".to_string()),
8536                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8537                                name: "missing".to_string(),
8538                                collection: None,
8539                            })),
8540                            target_name: Some("elf".to_string()),
8541                            ..Default::default()
8542                        },
8543                    ]),
8544                    resolvers: Some(vec![
8545                        fdecl::ResolverRegistration {
8546                            resolver: Some("pkg_resolver".to_string()),
8547                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8548                                name: "missing".to_string(),
8549                                collection: None,
8550                            })),
8551                            scheme: Some("fuchsia-pkg".to_string()),
8552                            ..Default::default()
8553                        },
8554                    ]),
8555                    stop_timeout_ms: Some(1234),
8556                    ..Default::default()
8557                }]);
8558                decl
8559            },
8560            result = Err(ErrorList::new(vec![
8561                Error::invalid_child(DeclType::RunnerRegistration, "source", "missing"),
8562                Error::invalid_child(DeclType::ResolverRegistration, "source", "missing"),
8563            ])),
8564        },
8565        test_validate_environment_runner_child_cycle => {
8566            input = {
8567                let mut decl = new_component_decl();
8568                decl.environments = Some(vec![fdecl::Environment {
8569                    name: Some("env".to_string()),
8570                    extends: Some(fdecl::EnvironmentExtends::None),
8571                    runners: Some(vec![
8572                        fdecl::RunnerRegistration {
8573                            source_name: Some("elf".to_string()),
8574                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8575                                name: "child".to_string(),
8576                                collection: None,
8577                            })),
8578                            target_name: Some("elf".to_string()),
8579                            ..Default::default()
8580                        },
8581                    ]),
8582                    resolvers: None,
8583                    stop_timeout_ms: Some(1234),
8584                    ..Default::default()
8585                }]);
8586                decl.children = Some(vec![fdecl::Child {
8587                    name: Some("child".to_string()),
8588                    startup: Some(fdecl::StartupMode::Lazy),
8589                    on_terminate: None,
8590                    url: Some("fuchsia-pkg://child".to_string()),
8591                    environment: Some("env".to_string()),
8592                    ..Default::default()
8593                }]);
8594                decl
8595            },
8596            result = Err(ErrorList::new(vec![
8597                Error::dependency_cycle(
8598                    "{{child child -> environment env -> child child}}"
8599                ),
8600            ])),
8601        },
8602        test_validate_environment_resolver_child_cycle => {
8603            input = {
8604                let mut decl = new_component_decl();
8605                decl.environments = Some(vec![fdecl::Environment {
8606                    name: Some("env".to_string()),
8607                    extends: Some(fdecl::EnvironmentExtends::None),
8608                    runners: None,
8609                    resolvers: Some(vec![
8610                        fdecl::ResolverRegistration {
8611                            resolver: Some("pkg_resolver".to_string()),
8612                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8613                                name: "child".to_string(),
8614                                collection: None,
8615                            })),
8616                            scheme: Some("fuchsia-pkg".to_string()),
8617                            ..Default::default()
8618                        },
8619                    ]),
8620                    stop_timeout_ms: Some(1234),
8621                    ..Default::default()
8622                }]);
8623                decl.children = Some(vec![fdecl::Child {
8624                    name: Some("child".to_string()),
8625                    startup: Some(fdecl::StartupMode::Lazy),
8626                    on_terminate: None,
8627                    url: Some("fuchsia-pkg://child".to_string()),
8628                    environment: Some("env".to_string()),
8629                    ..Default::default()
8630                }]);
8631                decl
8632            },
8633            result = Err(ErrorList::new(vec![
8634                Error::dependency_cycle(
8635                    "{{child child -> environment env -> child child}}"
8636                ),
8637            ])),
8638        },
8639        test_validate_environment_resolver_multiple_children_cycle => {
8640            input = {
8641                let mut decl = new_component_decl();
8642                decl.environments = Some(vec![fdecl::Environment {
8643                    name: Some("env".to_string()),
8644                    extends: Some(fdecl::EnvironmentExtends::None),
8645                    runners: None,
8646                    resolvers: Some(vec![
8647                        fdecl::ResolverRegistration {
8648                            resolver: Some("pkg_resolver".to_string()),
8649                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8650                                name: "a".to_string(),
8651                                collection: None,
8652                            })),
8653                            scheme: Some("fuchsia-pkg".to_string()),
8654                            ..Default::default()
8655                        },
8656                    ]),
8657                    stop_timeout_ms: Some(1234),
8658                    ..Default::default()
8659                }]);
8660                decl.children = Some(vec![
8661                    fdecl::Child {
8662                        name: Some("a".to_string()),
8663                        startup: Some(fdecl::StartupMode::Lazy),
8664                        on_terminate: None,
8665                        url: Some("fuchsia-pkg://child-a".to_string()),
8666                        environment: None,
8667                        ..Default::default()
8668                    },
8669                    fdecl::Child {
8670                        name: Some("b".to_string()),
8671                        startup: Some(fdecl::StartupMode::Lazy),
8672                        on_terminate: None,
8673                        url: Some("fuchsia-pkg://child-b".to_string()),
8674                        environment: Some("env".to_string()),
8675                        ..Default::default()
8676                    },
8677                ]);
8678                decl.offers = Some(vec![fdecl::Offer::Service(fdecl::OfferService {
8679                    source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8680                        name: "b".to_string(),
8681                        collection: None,
8682                    })),
8683                    source_name: Some("thing".to_string()),
8684                    target: Some(fdecl::Ref::Child(
8685                    fdecl::ChildRef {
8686                        name: "a".to_string(),
8687                        collection: None,
8688                    }
8689                    )),
8690                    target_name: Some("thing".to_string()),
8691                    ..Default::default()
8692                })]);
8693                decl
8694            },
8695            result = Err(ErrorList::new(vec![
8696                Error::dependency_cycle(
8697                    "{{child a -> environment env -> child b -> child a}}"
8698                ),
8699            ])),
8700        },
8701        test_validate_environment_debug_empty => {
8702            input = {
8703                let mut decl = new_component_decl();
8704                decl.environments = Some(vec![
8705                    fdecl::Environment {
8706                        name: Some("a".to_string()),
8707                        extends: Some(fdecl::EnvironmentExtends::None),
8708                        stop_timeout_ms: Some(2),
8709                        debug_capabilities:Some(vec![
8710                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8711                                source: None,
8712                                source_name: None,
8713                                target_name: None,
8714                                ..Default::default()
8715                            }),
8716                    ]),
8717                    ..Default::default()
8718                }]);
8719                decl
8720            },
8721            result = Err(ErrorList::new(vec![
8722                Error::missing_field(DeclType::DebugProtocolRegistration, "source"),
8723                Error::missing_field(DeclType::DebugProtocolRegistration, "source_name"),
8724                Error::missing_field(DeclType::DebugProtocolRegistration, "target_name"),
8725            ])),
8726        },
8727        test_validate_environment_debug_log_identifier => {
8728            input = {
8729                let mut decl = new_component_decl();
8730                decl.environments = Some(vec![
8731                    fdecl::Environment {
8732                        name: Some("a".to_string()),
8733                        extends: Some(fdecl::EnvironmentExtends::None),
8734                        stop_timeout_ms: Some(2),
8735                        debug_capabilities:Some(vec![
8736                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8737                                source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8738                                    name: "a".repeat(256),
8739                                    collection: None,
8740                                })),
8741                                source_name: Some(format!("{}", "a".repeat(256))),
8742                                target_name: Some(format!("{}", "b".repeat(256))),
8743                                ..Default::default()
8744                            }),
8745                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8746                                source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8747                                source_name: Some("a".to_string()),
8748                                target_name: Some(format!("{}", "b".repeat(256))),
8749                                ..Default::default()
8750                            }),
8751                    ]),
8752                    ..Default::default()
8753                }]);
8754                decl
8755            },
8756            result = Err(ErrorList::new(vec![
8757                Error::field_too_long(DeclType::DebugProtocolRegistration, "source.child.name"),
8758                Error::field_too_long(DeclType::DebugProtocolRegistration, "source_name"),
8759                Error::field_too_long(DeclType::DebugProtocolRegistration, "target_name"),
8760                Error::field_too_long(DeclType::DebugProtocolRegistration, "target_name"),
8761            ])),
8762        },
8763        test_validate_environment_debug_log_extraneous => {
8764            input = {
8765                let mut decl = new_component_decl();
8766                decl.environments = Some(vec![
8767                    fdecl::Environment {
8768                        name: Some("a".to_string()),
8769                        extends: Some(fdecl::EnvironmentExtends::None),
8770                        stop_timeout_ms: Some(2),
8771                        debug_capabilities:Some(vec![
8772                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8773                                source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8774                                    name: "logger".to_string(),
8775                                    collection: Some("modular".to_string()),
8776                                })),
8777                                source_name: Some("fuchsia.logger.Log".to_string()),
8778                                target_name: Some("fuchsia.logger.Log".to_string()),
8779                                ..Default::default()
8780                            }),
8781                    ]),
8782                    ..Default::default()
8783                }]);
8784                decl
8785            },
8786            result = Err(ErrorList::new(vec![
8787                Error::extraneous_field(DeclType::DebugProtocolRegistration, "source.child.collection"),
8788            ])),
8789        },
8790        test_validate_environment_debug_log_invalid_identifiers => {
8791            input = {
8792                let mut decl = new_component_decl();
8793                decl.environments = Some(vec![
8794                    fdecl::Environment {
8795                        name: Some("a".to_string()),
8796                        extends: Some(fdecl::EnvironmentExtends::None),
8797                        stop_timeout_ms: Some(2),
8798                        debug_capabilities:Some(vec![
8799                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8800                                source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8801                                    name: "^bad".to_string(),
8802                                    collection: None,
8803                                })),
8804                                source_name: Some("foo/".to_string()),
8805                                target_name: Some("/".to_string()),
8806                                ..Default::default()
8807                            }),
8808                    ]),
8809                    ..Default::default()
8810                }]);
8811                decl
8812            },
8813            result = Err(ErrorList::new(vec![
8814                Error::invalid_field(DeclType::DebugProtocolRegistration, "source.child.name"),
8815                Error::invalid_field(DeclType::DebugProtocolRegistration, "source_name"),
8816                Error::invalid_field(DeclType::DebugProtocolRegistration, "target_name"),
8817            ])),
8818        },
8819        test_validate_environment_debug_log_invalid_child => {
8820            input = {
8821                let mut decl = new_component_decl();
8822                decl.environments = Some(vec![
8823                    fdecl::Environment {
8824                        name: Some("a".to_string()),
8825                        extends: Some(fdecl::EnvironmentExtends::None),
8826                        stop_timeout_ms: Some(2),
8827                        debug_capabilities:Some(vec![
8828                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8829                                source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8830                                    name: "logger".to_string(),
8831                                    collection: None,
8832                                })),
8833                                source_name: Some("fuchsia.logger.LegacyLog".to_string()),
8834                                target_name: Some("fuchsia.logger.LegacyLog".to_string()),
8835                                ..Default::default()
8836                            }),
8837                    ]),
8838                    ..Default::default()
8839                }]);
8840                decl.children = Some(vec![
8841                    fdecl::Child {
8842                        name: Some("netstack".to_string()),
8843                        url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
8844                        startup: Some(fdecl::StartupMode::Lazy),
8845                        on_terminate: None,
8846                        environment: None,
8847                        ..Default::default()
8848                    },
8849                ]);
8850                decl
8851            },
8852            result = Err(ErrorList::new(vec![
8853                Error::invalid_child(DeclType::DebugProtocolRegistration, "source", "logger"),
8854
8855            ])),
8856        },
8857        test_validate_environment_debug_source_capability => {
8858            input = {
8859                let mut decl = new_component_decl();
8860                decl.environments = Some(vec![
8861                    fdecl::Environment {
8862                        name: Some("a".to_string()),
8863                        extends: Some(fdecl::EnvironmentExtends::None),
8864                        stop_timeout_ms: Some(2),
8865                        debug_capabilities:Some(vec![
8866                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8867                                source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
8868                                    name: "storage".to_string(),
8869                                })),
8870                                source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
8871                                target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
8872                                ..Default::default()
8873                            }),
8874                    ]),
8875                    ..Default::default()
8876                }]);
8877                decl
8878            },
8879            result = Err(ErrorList::new(vec![
8880                Error::invalid_field(DeclType::DebugProtocolRegistration, "source"),
8881            ])),
8882        },
8883
8884        // children
8885        test_validate_children_empty => {
8886            input = {
8887                let mut decl = new_component_decl();
8888                decl.children = Some(vec![fdecl::Child{
8889                    name: None,
8890                    url: None,
8891                    startup: None,
8892                    on_terminate: None,
8893                    environment: None,
8894                    ..Default::default()
8895                }]);
8896                decl
8897            },
8898            result = Err(ErrorList::new(vec![
8899                Error::missing_field(DeclType::Child, "name"),
8900                Error::missing_field(DeclType::Child, "url"),
8901                Error::missing_field(DeclType::Child, "startup"),
8902                // `on_terminate` is allowed to be None
8903            ])),
8904        },
8905        test_validate_children_invalid_identifiers => {
8906            input = {
8907                let mut decl = new_component_decl();
8908                decl.children = Some(vec![fdecl::Child{
8909                    name: Some("^bad".to_string()),
8910                    url: Some("scheme://invalid-port:99999999/path#frag".to_string()),
8911                    startup: Some(fdecl::StartupMode::Lazy),
8912                    on_terminate: None,
8913                    environment: None,
8914                    ..Default::default()
8915                }]);
8916                decl
8917            },
8918            result = Err(ErrorList::new(vec![
8919                Error::invalid_field(DeclType::Child, "name"),
8920                Error::invalid_url(DeclType::Child, "url", "\"scheme://invalid-port:99999999/path#frag\": Malformed URL: InvalidPort."),
8921            ])),
8922        },
8923        test_validate_children_long_identifiers => {
8924            input = {
8925                let mut decl = new_component_decl();
8926                decl.children = Some(vec![fdecl::Child{
8927                    name: Some("a".repeat(1025)),
8928                    url: Some(format!("fuchsia-pkg://{}", "a".repeat(4083))),
8929                    startup: Some(fdecl::StartupMode::Lazy),
8930                    on_terminate: None,
8931                    environment: Some("a".repeat(1025)),
8932                    ..Default::default()
8933                }]);
8934                decl
8935            },
8936            result = Err(ErrorList::new(vec![
8937                Error::field_too_long(DeclType::Child, "name"),
8938                Error::field_too_long(DeclType::Child, "url"),
8939                Error::field_too_long(DeclType::Child, "environment"),
8940                Error::invalid_environment(DeclType::Child, "environment", "a".repeat(1025)),
8941            ])),
8942        },
8943        test_validate_child_references_unknown_env => {
8944            input = {
8945                let mut decl = new_component_decl();
8946                decl.children = Some(vec![fdecl::Child{
8947                    name: Some("foo".to_string()),
8948                    url: Some("fuchsia-pkg://foo".to_string()),
8949                    startup: Some(fdecl::StartupMode::Lazy),
8950                    on_terminate: None,
8951                    environment: Some("test_env".to_string()),
8952                    ..Default::default()
8953                }]);
8954                decl
8955            },
8956            result = Err(ErrorList::new(vec![
8957                Error::invalid_environment(DeclType::Child, "environment", "test_env"),
8958            ])),
8959        },
8960
8961        // collections
8962        test_validate_collections_empty => {
8963            input = {
8964                let mut decl = new_component_decl();
8965                decl.collections = Some(vec![fdecl::Collection{
8966                    name: None,
8967                    durability: None,
8968                    environment: None,
8969                    allowed_offers: None,
8970                    allow_long_names: None,
8971                    ..Default::default()
8972                }]);
8973                decl
8974            },
8975            result = Err(ErrorList::new(vec![
8976                Error::missing_field(DeclType::Collection, "name"),
8977                Error::missing_field(DeclType::Collection, "durability"),
8978            ])),
8979        },
8980        test_validate_collections_invalid_identifiers => {
8981            input = {
8982                let mut decl = new_component_decl();
8983                decl.collections = Some(vec![fdecl::Collection{
8984                    name: Some("^bad".to_string()),
8985                    durability: Some(fdecl::Durability::Transient),
8986                    environment: None,
8987                    allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
8988                    allow_long_names: None,
8989                    ..Default::default()
8990                }]);
8991                decl
8992            },
8993            result = Err(ErrorList::new(vec![
8994                Error::invalid_field(DeclType::Collection, "name"),
8995            ])),
8996        },
8997        test_validate_collections_long_identifiers => {
8998            input = {
8999                let mut decl = new_component_decl();
9000                decl.collections = Some(vec![fdecl::Collection{
9001                    name: Some("a".repeat(1025)),
9002                    durability: Some(fdecl::Durability::Transient),
9003                    environment: None,
9004                    allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
9005                    allow_long_names: None,
9006                    ..Default::default()
9007                }]);
9008                decl
9009            },
9010            result = Err(ErrorList::new(vec![
9011                Error::field_too_long(DeclType::Collection, "name"),
9012            ])),
9013        },
9014        test_validate_collection_references_unknown_env => {
9015            input = {
9016                let mut decl = new_component_decl();
9017                decl.collections = Some(vec![fdecl::Collection {
9018                    name: Some("foo".to_string()),
9019                    durability: Some(fdecl::Durability::Transient),
9020                    environment: Some("test_env".to_string()),
9021                    allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
9022                    allow_long_names: None,
9023                    ..Default::default()
9024                }]);
9025                decl
9026            },
9027            result = Err(ErrorList::new(vec![
9028                Error::invalid_environment(DeclType::Collection, "environment", "test_env"),
9029            ])),
9030        },
9031
9032        // capabilities
9033        test_validate_capabilities_empty => {
9034            input = {
9035                let mut decl = new_component_decl();
9036                decl.capabilities = Some(vec![
9037                    fdecl::Capability::Service(fdecl::Service {
9038                        name: None,
9039                        source_path: None,
9040                        ..Default::default()
9041                    }),
9042                    fdecl::Capability::Protocol(fdecl::Protocol {
9043                        name: None,
9044                        source_path: None,
9045                        ..Default::default()
9046                    }),
9047                    fdecl::Capability::Directory(fdecl::Directory {
9048                        name: None,
9049                        source_path: None,
9050                        rights: None,
9051                        ..Default::default()
9052                    }),
9053                    fdecl::Capability::Storage(fdecl::Storage {
9054                        name: None,
9055                        source: None,
9056                        backing_dir: None,
9057                        subdir: None,
9058                        storage_id: None,
9059                        ..Default::default()
9060                    }),
9061                    fdecl::Capability::Runner(fdecl::Runner {
9062                        name: None,
9063                        source_path: None,
9064                        ..Default::default()
9065                    }),
9066                    fdecl::Capability::Resolver(fdecl::Resolver {
9067                        name: None,
9068                        source_path: None,
9069                        ..Default::default()
9070                    }),
9071                    fdecl::Capability::Dictionary(fdecl::Dictionary {
9072                        ..Default::default()
9073                    }),
9074                ]);
9075                decl
9076            },
9077            result = Err(ErrorList::new(vec![
9078                Error::missing_field(DeclType::Dictionary, "name"),
9079                Error::missing_field(DeclType::Service, "name"),
9080                Error::missing_field(DeclType::Service, "source_path"),
9081                Error::missing_field(DeclType::Protocol, "name"),
9082                Error::missing_field(DeclType::Protocol, "source_path"),
9083                Error::missing_field(DeclType::Directory, "name"),
9084                Error::missing_field(DeclType::Directory, "source_path"),
9085                Error::missing_field(DeclType::Directory, "rights"),
9086                Error::missing_field(DeclType::Storage, "source"),
9087                Error::missing_field(DeclType::Storage, "name"),
9088                Error::missing_field(DeclType::Storage, "storage_id"),
9089                Error::missing_field(DeclType::Storage, "backing_dir"),
9090                Error::missing_field(DeclType::Runner, "name"),
9091                Error::missing_field(DeclType::Runner, "source_path"),
9092                Error::missing_field(DeclType::Resolver, "name"),
9093                Error::missing_field(DeclType::Resolver, "source_path"),
9094            ])),
9095        },
9096        test_validate_capabilities_invalid_identifiers => {
9097            input = {
9098                let mut decl = new_component_decl();
9099                decl.capabilities = Some(vec![
9100                    fdecl::Capability::Service(fdecl::Service {
9101                        name: Some("^bad".to_string()),
9102                        source_path: Some("&bad".to_string()),
9103                        ..Default::default()
9104                    }),
9105                    fdecl::Capability::Protocol(fdecl::Protocol {
9106                        name: Some("^bad".to_string()),
9107                        source_path: Some("&bad".to_string()),
9108                        ..Default::default()
9109                    }),
9110                    fdecl::Capability::Directory(fdecl::Directory {
9111                        name: Some("^bad".to_string()),
9112                        source_path: Some("&bad".to_string()),
9113                        rights: Some(fio::Operations::CONNECT),
9114                        ..Default::default()
9115                    }),
9116                    fdecl::Capability::Storage(fdecl::Storage {
9117                        name: Some("^bad".to_string()),
9118                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9119                            name: "/bad".to_string()
9120                        })),
9121                        backing_dir: Some("&bad".to_string()),
9122                        subdir: None,
9123                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9124                        ..Default::default()
9125                    }),
9126                    fdecl::Capability::Runner(fdecl::Runner {
9127                        name: Some("^bad".to_string()),
9128                        source_path: Some("&bad".to_string()),
9129                        ..Default::default()
9130                    }),
9131                    fdecl::Capability::Resolver(fdecl::Resolver {
9132                        name: Some("^bad".to_string()),
9133                        source_path: Some("&bad".to_string()),
9134                        ..Default::default()
9135                    }),
9136                    fdecl::Capability::Dictionary(fdecl::Dictionary {
9137                        name: Some("^bad".to_string()),
9138                        ..Default::default()
9139                    }),
9140                ]);
9141                decl
9142            },
9143            result = Err(ErrorList::new(vec![
9144                Error::invalid_field(DeclType::Dictionary, "name"),
9145                Error::invalid_field(DeclType::Service, "name"),
9146                Error::invalid_field(DeclType::Service, "source_path"),
9147                Error::invalid_field(DeclType::Protocol, "name"),
9148                Error::invalid_field(DeclType::Protocol, "source_path"),
9149                Error::invalid_field(DeclType::Directory, "name"),
9150                Error::invalid_field(DeclType::Directory, "source_path"),
9151                Error::invalid_field(DeclType::Storage, "source"),
9152                Error::invalid_field(DeclType::Storage, "name"),
9153                Error::invalid_field(DeclType::Storage, "backing_dir"),
9154                Error::invalid_field(DeclType::Runner, "name"),
9155                Error::invalid_field(DeclType::Runner, "source_path"),
9156                Error::invalid_field(DeclType::Resolver, "name"),
9157                Error::invalid_field(DeclType::Resolver, "source_path"),
9158            ])),
9159        },
9160        test_validate_capabilities_invalid_child => {
9161            input = {
9162                let mut decl = new_component_decl();
9163                decl.capabilities = Some(vec![
9164                    fdecl::Capability::Storage(fdecl::Storage {
9165                        name: Some("foo".to_string()),
9166                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9167                            name: "invalid".to_string(),
9168                        })),
9169                        backing_dir: Some("foo".to_string()),
9170                        subdir: None,
9171                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9172                        ..Default::default()
9173                    }),
9174                ]);
9175                decl
9176            },
9177            result = Err(ErrorList::new(vec![
9178                Error::invalid_field(DeclType::Storage, "source"),
9179            ])),
9180        },
9181        test_validate_capabilities_long_identifiers => {
9182            input = {
9183                let mut decl = new_component_decl();
9184                decl.capabilities = Some(vec![
9185                    fdecl::Capability::Service(fdecl::Service {
9186                        name: Some("a".repeat(256)),
9187                        source_path: Some("/c".repeat(2048)),
9188                        ..Default::default()
9189                    }),
9190                    fdecl::Capability::Protocol(fdecl::Protocol {
9191                        name: Some("a".repeat(256)),
9192                        source_path: Some("/c".repeat(2048)),
9193                        ..Default::default()
9194                    }),
9195                    fdecl::Capability::Directory(fdecl::Directory {
9196                        name: Some("a".repeat(256)),
9197                        source_path: Some("/c".repeat(2048)),
9198                        rights: Some(fio::Operations::CONNECT),
9199                        ..Default::default()
9200                    }),
9201                    fdecl::Capability::Storage(fdecl::Storage {
9202                        name: Some("a".repeat(256)),
9203                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
9204                            name: "b".repeat(256),
9205                            collection: None,
9206                        })),
9207                        backing_dir: Some(format!("{}", "c".repeat(256))),
9208                        subdir: None,
9209                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9210                        ..Default::default()
9211                    }),
9212                    fdecl::Capability::Runner(fdecl::Runner {
9213                        name: Some("a".repeat(256)),
9214                        source_path: Some("/c".repeat(2048)),
9215                        ..Default::default()
9216                    }),
9217                    fdecl::Capability::Resolver(fdecl::Resolver {
9218                        name: Some("a".repeat(256)),
9219                        source_path: Some("/c".repeat(2048)),
9220                        ..Default::default()
9221                    }),
9222                    fdecl::Capability::Dictionary(fdecl::Dictionary {
9223                        name: Some("a".repeat(256)),
9224                        ..Default::default()
9225                    }),
9226                ]);
9227                decl
9228            },
9229            result = Err(ErrorList::new(vec![
9230                Error::field_too_long(DeclType::Dictionary, "name"),
9231                Error::field_too_long(DeclType::Service, "name"),
9232                Error::field_too_long(DeclType::Service, "source_path"),
9233                Error::field_too_long(DeclType::Protocol, "name"),
9234                Error::field_too_long(DeclType::Protocol, "source_path"),
9235                Error::field_too_long(DeclType::Directory, "name"),
9236                Error::field_too_long(DeclType::Directory, "source_path"),
9237                Error::field_too_long(DeclType::Storage, "source.child.name"),
9238                Error::field_too_long(DeclType::Storage, "name"),
9239                Error::field_too_long(DeclType::Storage, "backing_dir"),
9240                Error::field_too_long(DeclType::Runner, "name"),
9241                Error::field_too_long(DeclType::Runner, "source_path"),
9242                Error::field_too_long(DeclType::Resolver, "name"),
9243                Error::field_too_long(DeclType::Resolver, "source_path"),
9244            ])),
9245        },
9246        test_validate_capabilities_duplicate_name => {
9247            input = {
9248                let mut decl = new_component_decl();
9249                decl.capabilities = Some(vec![
9250                    fdecl::Capability::Service(fdecl::Service {
9251                        name: Some("service".to_string()),
9252                        source_path: Some("/service".to_string()),
9253                        ..Default::default()
9254                    }),
9255                    fdecl::Capability::Service(fdecl::Service {
9256                        name: Some("service".to_string()),
9257                        source_path: Some("/service".to_string()),
9258                        ..Default::default()
9259                    }),
9260                    fdecl::Capability::Protocol(fdecl::Protocol {
9261                        name: Some("protocol".to_string()),
9262                        source_path: Some("/protocol".to_string()),
9263                        ..Default::default()
9264                    }),
9265                    fdecl::Capability::Protocol(fdecl::Protocol {
9266                        name: Some("protocol".to_string()),
9267                        source_path: Some("/protocol".to_string()),
9268                        ..Default::default()
9269                    }),
9270                    fdecl::Capability::Directory(fdecl::Directory {
9271                        name: Some("directory".to_string()),
9272                        source_path: Some("/directory".to_string()),
9273                        rights: Some(fio::Operations::CONNECT),
9274                        ..Default::default()
9275                    }),
9276                    fdecl::Capability::Directory(fdecl::Directory {
9277                        name: Some("directory".to_string()),
9278                        source_path: Some("/directory".to_string()),
9279                        rights: Some(fio::Operations::CONNECT),
9280                        ..Default::default()
9281                    }),
9282                    fdecl::Capability::Storage(fdecl::Storage {
9283                        name: Some("storage".to_string()),
9284                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
9285                        backing_dir: Some("directory".to_string()),
9286                        subdir: None,
9287                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9288                        ..Default::default()
9289                    }),
9290                    fdecl::Capability::Storage(fdecl::Storage {
9291                        name: Some("storage".to_string()),
9292                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
9293                        backing_dir: Some("directory".to_string()),
9294                        subdir: None,
9295                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9296                        ..Default::default()
9297                    }),
9298                    fdecl::Capability::Runner(fdecl::Runner {
9299                        name: Some("runner".to_string()),
9300                        source_path: Some("/runner".to_string()),
9301                        ..Default::default()
9302                    }),
9303                    fdecl::Capability::Runner(fdecl::Runner {
9304                        name: Some("runner".to_string()),
9305                        source_path: Some("/runner".to_string()),
9306                        ..Default::default()
9307                    }),
9308                    fdecl::Capability::Resolver(fdecl::Resolver {
9309                        name: Some("resolver".to_string()),
9310                        source_path: Some("/resolver".to_string()),
9311                        ..Default::default()
9312                    }),
9313                    fdecl::Capability::Resolver(fdecl::Resolver {
9314                        name: Some("resolver".to_string()),
9315                        source_path: Some("/resolver".to_string()),
9316                        ..Default::default()
9317                    }),
9318                    fdecl::Capability::Dictionary(fdecl::Dictionary {
9319                        name: Some("dictionary".to_string()),
9320                        ..Default::default()
9321                    }),
9322                    fdecl::Capability::Dictionary(fdecl::Dictionary {
9323                        name: Some("dictionary".to_string()),
9324                        ..Default::default()
9325                    }),
9326                ]);
9327                decl
9328            },
9329            result = Err(ErrorList::new(vec![
9330                Error::duplicate_field(DeclType::Dictionary, "name", "dictionary"),
9331                Error::duplicate_field(DeclType::Service, "name", "service"),
9332                Error::duplicate_field(DeclType::Protocol, "name", "protocol"),
9333                Error::duplicate_field(DeclType::Directory, "name", "directory"),
9334                Error::duplicate_field(DeclType::Storage, "name", "storage"),
9335                Error::duplicate_field(DeclType::Runner, "name", "runner"),
9336                Error::duplicate_field(DeclType::Resolver, "name", "resolver"),
9337            ])),
9338        },
9339        test_validate_invalid_service_aggregation_conflicting_filter => {
9340            input = {
9341                let mut decl = new_component_decl();
9342                decl.offers = Some(vec![
9343                    fdecl::Offer::Service(fdecl::OfferService {
9344                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9345                            name: "coll_a".to_string()
9346                        })),
9347                        source_name: Some("fuchsia.logger.Log".to_string()),
9348                        target: Some(fdecl::Ref::Child(
9349                            fdecl::ChildRef {
9350                                name: "child_c".to_string(),
9351                                collection: None,
9352                            }
9353                        )),
9354                        target_name: Some("fuchsia.logger.Log1".to_string()),
9355                        source_instance_filter: Some(vec!["default".to_string()]),
9356                        ..Default::default()
9357                    }),
9358                    fdecl::Offer::Service(fdecl::OfferService {
9359                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9360                            name: "coll_b".to_string()
9361                        })),
9362                        source_name: Some("fuchsia.logger.Log".to_string()),
9363                        target: Some(fdecl::Ref::Child(
9364                            fdecl::ChildRef {
9365                                name: "child_c".to_string(),
9366                                collection: None,
9367                            }
9368                        )),
9369                        target_name: Some("fuchsia.logger.Log1".to_string()),
9370                        source_instance_filter: Some(vec!["default".to_string()]),
9371                        ..Default::default()
9372                    }),
9373                ]);
9374                decl.collections = Some(vec![
9375                    fdecl::Collection {
9376                        name: Some("coll_a".to_string()),
9377                        durability: Some(fdecl::Durability::Transient),
9378                        ..Default::default()
9379                    },
9380                    fdecl::Collection {
9381                        name: Some("coll_b".to_string()),
9382                        durability: Some(fdecl::Durability::Transient),
9383                        ..Default::default()
9384                    },
9385                ]);
9386                decl.children = Some(vec![
9387                    fdecl::Child {
9388                        name: Some("child_c".to_string()),
9389                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9390                        startup: Some(fdecl::StartupMode::Lazy),
9391                        ..Default::default()
9392                    },
9393                ]);
9394                decl
9395            },
9396            result = Err(ErrorList::new(vec![
9397                Error::invalid_aggregate_offer("Conflicting source_instance_filter in aggregate \
9398                   service offer, instance_name 'default' seen in filter lists multiple times"),
9399            ])),
9400        },
9401
9402        test_validate_invalid_service_aggregation_conflicting_source_name => {
9403            input = {
9404                let mut decl = new_component_decl();
9405                decl.offers = Some(vec![
9406                    fdecl::Offer::Service(fdecl::OfferService {
9407                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9408                            name: "coll_a".into()
9409                        })),
9410                        source_name: Some("fuchsia.logger.Log".to_string()),
9411                        target: Some(fdecl::Ref::Child(
9412                            fdecl::ChildRef {
9413                                name: "child_c".to_string(),
9414                                collection: None,
9415                            }
9416                        )),
9417                        target_name: Some("fuchsia.logger.Log2".to_string()),
9418                        source_instance_filter: Some(vec!["default2".to_string()]),
9419                        ..Default::default()
9420                    }),
9421                    fdecl::Offer::Service(fdecl::OfferService {
9422                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9423                            name: "coll_b".into()
9424                        })),
9425                        source_name: Some("fuchsia.logger.LogAlt".to_string()),
9426                        target: Some(fdecl::Ref::Child(
9427                            fdecl::ChildRef {
9428                                name: "child_c".to_string(),
9429                                collection: None,
9430                            }
9431                        )),
9432                        target_name: Some("fuchsia.logger.Log2".to_string()),
9433                        source_instance_filter: Some(vec!["default".to_string()]),
9434                        ..Default::default()
9435                    })
9436                ]);
9437                decl.collections = Some(vec![
9438                    fdecl::Collection {
9439                        name: Some("coll_a".to_string()),
9440                        durability: Some(fdecl::Durability::Transient),
9441                        ..Default::default()
9442                    },
9443                    fdecl::Collection {
9444                        name: Some("coll_b".to_string()),
9445                        durability: Some(fdecl::Durability::Transient),
9446                        ..Default::default()
9447                    },
9448                ]);
9449                decl.children = Some(vec![
9450                    fdecl::Child {
9451                        name: Some("child_c".to_string()),
9452                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9453                        startup: Some(fdecl::StartupMode::Lazy),
9454                        on_terminate: None,
9455                        environment: None,
9456                        ..Default::default()
9457                    },
9458                ]);
9459                decl
9460            },
9461            result = Err(ErrorList::new(vec![
9462                Error::invalid_aggregate_offer("All aggregate service offers must have the same source_name, saw fuchsia.logger.Log, fuchsia.logger.LogAlt. Use renamed_instances to rename instance names to avoid conflict."),
9463            ])),
9464        },
9465
9466        test_validate_resolvers_missing_from_offer => {
9467            input = {
9468                let mut decl = new_component_decl();
9469                decl.offers = Some(vec![fdecl::Offer::Resolver(fdecl::OfferResolver {
9470                    source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
9471                    source_name: Some("a".to_string()),
9472                    target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
9473                    target_name: Some("a".to_string()),
9474                    ..Default::default()
9475                })]);
9476                decl.children = Some(vec![fdecl::Child {
9477                    name: Some("child".to_string()),
9478                    url: Some("test:///child".to_string()),
9479                    startup: Some(fdecl::StartupMode::Eager),
9480                    on_terminate: None,
9481                    environment: None,
9482                    ..Default::default()
9483                }]);
9484                decl
9485            },
9486            result = Err(ErrorList::new(vec![
9487                Error::invalid_capability(DeclType::OfferResolver, "source", "a"),
9488            ])),
9489        },
9490        test_validate_resolvers_missing_from_expose => {
9491            input = {
9492                let mut decl = new_component_decl();
9493                decl.exposes = Some(vec![fdecl::Expose::Resolver(fdecl::ExposeResolver {
9494                    source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
9495                    source_name: Some("a".to_string()),
9496                    target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
9497                    target_name: Some("a".to_string()),
9498                    ..Default::default()
9499                })]);
9500                decl
9501            },
9502            result = Err(ErrorList::new(vec![
9503                Error::invalid_capability(DeclType::ExposeResolver, "source", "a"),
9504            ])),
9505        },
9506
9507        test_validate_config_missing_config => {
9508            input = {
9509                let mut decl = new_component_decl();
9510                decl.config = Some(fdecl::ConfigSchema{
9511                    fields: None,
9512                    checksum: None,
9513                    value_source: None,
9514                    ..Default::default()
9515                });
9516                decl
9517            },
9518            result = Err(ErrorList::new(vec![
9519                Error::missing_field(DeclType::ConfigSchema, "fields"),
9520                Error::missing_field(DeclType::ConfigSchema, "checksum"),
9521                Error::missing_field(DeclType::ConfigSchema, "value_source"),
9522            ])),
9523        },
9524
9525        test_validate_config_missing_config_field => {
9526            input = {
9527                let mut decl = new_component_decl();
9528                decl.config = Some(fdecl::ConfigSchema{
9529                    fields: Some(vec![
9530                        fdecl::ConfigField {
9531                            key: None,
9532                            type_: None,
9533                            ..Default::default()
9534                        }
9535                    ]),
9536                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9537                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9538                    ..Default::default()
9539                });
9540                decl
9541            },
9542            result = Err(ErrorList::new(vec![
9543                Error::missing_field(DeclType::ConfigField, "key"),
9544                Error::missing_field(DeclType::ConfigField, "value_type"),
9545            ])),
9546        },
9547
9548        test_validate_config_bool => {
9549            input = {
9550                let mut decl = new_component_decl();
9551                decl.config = Some(fdecl::ConfigSchema{
9552                    fields: Some(vec![
9553                        fdecl::ConfigField {
9554                            key: Some("test".to_string()),
9555                            type_: Some(fdecl::ConfigType {
9556                                layout: fdecl::ConfigTypeLayout::Bool,
9557                                parameters: Some(vec![]),
9558                                constraints: vec![]
9559                            }),
9560                            ..Default::default()
9561                        }
9562                    ]),
9563                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9564                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9565                    ..Default::default()
9566                });
9567                decl
9568            },
9569            result = Ok(()),
9570        },
9571
9572        test_validate_config_bool_extra_constraint => {
9573            input = {
9574                let mut decl = new_component_decl();
9575                decl.config = Some(fdecl::ConfigSchema{
9576                    fields: Some(vec![
9577                        fdecl::ConfigField {
9578                            key: Some("test".to_string()),
9579                            type_: Some(fdecl::ConfigType {
9580                                layout: fdecl::ConfigTypeLayout::Bool,
9581                                parameters: Some(vec![]),
9582                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9583                            }),
9584                            ..Default::default()
9585                        }
9586                    ]),
9587                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9588                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9589                    ..Default::default()
9590                });
9591                decl
9592            },
9593            result = Err(ErrorList::new(vec![
9594                Error::extraneous_field(DeclType::ConfigType, "constraints")
9595            ])),
9596        },
9597
9598        test_validate_config_bool_missing_parameters => {
9599            input = {
9600                let mut decl = new_component_decl();
9601                decl.config = Some(fdecl::ConfigSchema{
9602                    fields: Some(vec![
9603                        fdecl::ConfigField {
9604                            key: Some("test".to_string()),
9605                            type_: Some(fdecl::ConfigType {
9606                                layout: fdecl::ConfigTypeLayout::Bool,
9607                                parameters: None,
9608                                constraints: vec![]
9609                            }),
9610                            ..Default::default()
9611                        }
9612                    ]),
9613                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9614                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9615                    ..Default::default()
9616                });
9617                decl
9618            },
9619            result = Err(ErrorList::new(vec![
9620                Error::missing_field(DeclType::ConfigType, "parameters")
9621            ])),
9622        },
9623
9624        test_validate_config_string => {
9625            input = {
9626                let mut decl = new_component_decl();
9627                decl.config = Some(fdecl::ConfigSchema{
9628                    fields: Some(vec![
9629                        fdecl::ConfigField {
9630                            key: Some("test".to_string()),
9631                            type_: Some(fdecl::ConfigType {
9632                                layout: fdecl::ConfigTypeLayout::String,
9633                                parameters: Some(vec![]),
9634                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9635                            }),
9636                            ..Default::default()
9637                        }
9638                    ]),
9639                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9640                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9641                    ..Default::default()
9642                });
9643                decl
9644            },
9645            result = Ok(()),
9646        },
9647
9648        test_validate_config_string_missing_parameter => {
9649            input = {
9650                let mut decl = new_component_decl();
9651                decl.config = Some(fdecl::ConfigSchema{
9652                    fields: Some(vec![
9653                        fdecl::ConfigField {
9654                            key: Some("test".to_string()),
9655                            type_: Some(fdecl::ConfigType {
9656                                layout: fdecl::ConfigTypeLayout::String,
9657                                parameters: None,
9658                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9659                            }),
9660                            ..Default::default()
9661                        }
9662                    ]),
9663                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9664                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9665                    ..Default::default()
9666                });
9667                decl
9668            },
9669            result = Err(ErrorList::new(vec![
9670                Error::missing_field(DeclType::ConfigType, "parameters")
9671            ])),
9672        },
9673
9674        test_validate_config_string_missing_constraint => {
9675            input = {
9676                let mut decl = new_component_decl();
9677                decl.config = Some(fdecl::ConfigSchema{
9678                    fields: Some(vec![
9679                        fdecl::ConfigField {
9680                            key: Some("test".to_string()),
9681                            type_: Some(fdecl::ConfigType {
9682                                layout: fdecl::ConfigTypeLayout::String,
9683                                parameters: Some(vec![]),
9684                                constraints: vec![]
9685                            }),
9686                            ..Default::default()
9687                        }
9688                    ]),
9689                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9690                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9691                    ..Default::default()
9692                });
9693                decl
9694            },
9695            result = Err(ErrorList::new(vec![
9696                Error::missing_field(DeclType::ConfigType, "constraints")
9697            ])),
9698        },
9699
9700        test_validate_config_string_extra_constraint => {
9701            input = {
9702                let mut decl = new_component_decl();
9703                decl.config = Some(fdecl::ConfigSchema{
9704                    fields: Some(vec![
9705                        fdecl::ConfigField {
9706                            key: Some("test".to_string()),
9707                            type_: Some(fdecl::ConfigType {
9708                                layout: fdecl::ConfigTypeLayout::String,
9709                                parameters: Some(vec![]),
9710                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10), fdecl::LayoutConstraint::MaxSize(10)]
9711                            }),
9712                            ..Default::default()
9713                        }
9714                    ]),
9715                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9716                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9717                    ..Default::default()
9718                });
9719                decl
9720            },
9721            result = Err(ErrorList::new(vec![
9722                Error::extraneous_field(DeclType::ConfigType, "constraints")
9723            ])),
9724        },
9725
9726        test_validate_config_vector_bool => {
9727            input = {
9728                let mut decl = new_component_decl();
9729                decl.config = Some(fdecl::ConfigSchema{
9730                    fields: Some(vec![
9731                        fdecl::ConfigField {
9732                            key: Some("test".to_string()),
9733                            type_: Some(fdecl::ConfigType {
9734                                layout: fdecl::ConfigTypeLayout::Vector,
9735                                parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9736                                    layout: fdecl::ConfigTypeLayout::Bool,
9737                                    parameters: Some(vec![]),
9738                                    constraints: vec![],
9739                                })]),
9740                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9741                            }),
9742                            ..Default::default()
9743                        }
9744                    ]),
9745                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9746                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9747                    ..Default::default()
9748                });
9749                decl
9750            },
9751            result = Ok(()),
9752        },
9753
9754        test_validate_config_vector_extra_parameter => {
9755            input = {
9756                let mut decl = new_component_decl();
9757                decl.config = Some(fdecl::ConfigSchema{
9758                    fields: Some(vec![
9759                        fdecl::ConfigField {
9760                            key: Some("test".to_string()),
9761                            type_: Some(fdecl::ConfigType {
9762                                layout: fdecl::ConfigTypeLayout::Vector,
9763                                parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9764                                    layout: fdecl::ConfigTypeLayout::Bool,
9765                                    parameters: Some(vec![]),
9766                                    constraints: vec![],
9767                                }), fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9768                                    layout: fdecl::ConfigTypeLayout::Uint8,
9769                                    parameters: Some(vec![]),
9770                                    constraints: vec![],
9771                                })]),
9772                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9773                            }),
9774                            ..Default::default()
9775                        }
9776                    ]),
9777                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9778                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9779                    ..Default::default()
9780                });
9781                decl
9782            },
9783            result = Err(ErrorList::new(vec![
9784                Error::extraneous_field(DeclType::ConfigType, "parameters")
9785            ])),
9786        },
9787
9788        test_validate_config_vector_missing_parameter => {
9789            input = {
9790                let mut decl = new_component_decl();
9791                decl.config = Some(fdecl::ConfigSchema{
9792                    fields: Some(vec![
9793                        fdecl::ConfigField {
9794                            key: Some("test".to_string()),
9795                            type_: Some(fdecl::ConfigType {
9796                                layout: fdecl::ConfigTypeLayout::Vector,
9797                                parameters: Some(vec![]),
9798                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9799                            }),
9800                            ..Default::default()
9801                        }
9802                    ]),
9803                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9804                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9805                    ..Default::default()
9806                });
9807                decl
9808            },
9809            result = Err(ErrorList::new(vec![
9810                Error::missing_field(DeclType::ConfigType, "parameters")
9811            ])),
9812        },
9813
9814        test_validate_config_vector_string => {
9815            input = {
9816                let mut decl = new_component_decl();
9817                decl.config = Some(fdecl::ConfigSchema{
9818                    fields: Some(vec![
9819                        fdecl::ConfigField {
9820                            key: Some("test".to_string()),
9821                            type_: Some(fdecl::ConfigType {
9822                                layout: fdecl::ConfigTypeLayout::Vector,
9823                                parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9824                                    layout: fdecl::ConfigTypeLayout::String,
9825                                    parameters: Some(vec![]),
9826                                    constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9827                                })]),
9828                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9829                            }),
9830                            ..Default::default()
9831                        }
9832                    ]),
9833                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9834                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9835                    ..Default::default()
9836                });
9837                decl
9838            },
9839            result = Ok(()),
9840        },
9841
9842        test_validate_config_vector_vector => {
9843            input = {
9844                let mut decl = new_component_decl();
9845                decl.config = Some(fdecl::ConfigSchema{
9846                    fields: Some(vec![
9847                        fdecl::ConfigField {
9848                            key: Some("test".to_string()),
9849                            type_: Some(fdecl::ConfigType {
9850                                layout: fdecl::ConfigTypeLayout::Vector,
9851                                parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9852                                    layout: fdecl::ConfigTypeLayout::Vector,
9853                                    parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9854                                        layout: fdecl::ConfigTypeLayout::String,
9855                                        parameters: Some(vec![]),
9856                                        constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9857                                    })]),
9858                                    constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9859                                })]),
9860                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9861                            }),
9862                            ..Default::default()
9863                        }
9864                    ]),
9865                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9866                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9867                    ..Default::default()
9868                });
9869                decl
9870            },
9871            result = Err(ErrorList::new(vec![
9872                Error::nested_vector()
9873            ])),
9874        },
9875
9876        test_validate_exposes_invalid_aggregation_different_availability => {
9877            input = {
9878                let mut decl = new_component_decl();
9879                decl.exposes = Some(vec![
9880                    fdecl::Expose::Service(fdecl::ExposeService {
9881                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9882                            name: "coll_a".into()
9883                        })),
9884                        source_name: Some("fuchsia.logger.Log".to_string()),
9885                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
9886                        target_name: Some("fuchsia.logger.Log".to_string()),
9887                        availability: Some(fdecl::Availability::Required),
9888                        ..Default::default()
9889                    }),
9890                    fdecl::Expose::Service(fdecl::ExposeService {
9891                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9892                            name: "coll_b".into()
9893                        })),
9894                        source_name: Some("fuchsia.logger.Log".to_string()),
9895                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
9896                        target_name: Some("fuchsia.logger.Log".to_string()),
9897                        availability: Some(fdecl::Availability::Optional),
9898                        ..Default::default()
9899                    })
9900                ]);
9901                decl.collections = Some(vec![
9902                    fdecl::Collection {
9903                        name: Some("coll_a".to_string()),
9904                        durability: Some(fdecl::Durability::Transient),
9905                        ..Default::default()
9906                    },
9907                    fdecl::Collection {
9908                        name: Some("coll_b".to_string()),
9909                        durability: Some(fdecl::Durability::Transient),
9910                        ..Default::default()
9911                    },
9912                ]);
9913                decl
9914            },
9915            result = Err(ErrorList::new(vec![
9916                Error::DifferentAvailabilityInAggregation(AvailabilityList(vec![
9917                    fdecl::Availability::Required,
9918                    fdecl::Availability::Optional,
9919                ]))
9920            ])),
9921        },
9922
9923        test_validate_offers_invalid_aggregation_different_availability => {
9924            input = {
9925                let mut decl = new_component_decl();
9926                decl.offers = Some(vec![
9927                    fdecl::Offer::Service(fdecl::OfferService {
9928                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9929                            name: "coll_a".into()
9930                        })),
9931                        source_name: Some("fuchsia.logger.Log".to_string()),
9932                        target: Some(fdecl::Ref::Child(
9933                            fdecl::ChildRef {
9934                                name: "child_c".to_string(),
9935                                collection: None,
9936                            }
9937                        )),
9938                        target_name: Some("fuchsia.logger.Log".to_string()),
9939                        source_instance_filter: Some(vec!["default".to_string()]),
9940                        availability: Some(fdecl::Availability::Required),
9941                        ..Default::default()
9942                    }),
9943                    fdecl::Offer::Service(fdecl::OfferService {
9944                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9945                            name: "coll_b".into()
9946                        })),
9947                        source_name: Some("fuchsia.logger.Log".to_string()),
9948                        target: Some(fdecl::Ref::Child(
9949                            fdecl::ChildRef {
9950                                name: "child_c".to_string(),
9951                                collection: None,
9952                            }
9953                        )),
9954                        target_name: Some("fuchsia.logger.Log".to_string()),
9955                        source_instance_filter: Some(vec!["a_different_default".to_string()]),
9956                        availability: Some(fdecl::Availability::Optional),
9957                        ..Default::default()
9958                    })
9959                ]);
9960                decl.collections = Some(vec![
9961                    fdecl::Collection {
9962                        name: Some("coll_a".to_string()),
9963                        durability: Some(fdecl::Durability::Transient),
9964                        ..Default::default()
9965                    },
9966                    fdecl::Collection {
9967                        name: Some("coll_b".to_string()),
9968                        durability: Some(fdecl::Durability::Transient),
9969                        ..Default::default()
9970                    },
9971                ]);
9972                decl.children = Some(vec![
9973                    fdecl::Child {
9974                        name: Some("child_c".to_string()),
9975                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9976                        startup: Some(fdecl::StartupMode::Lazy),
9977                        on_terminate: None,
9978                        environment: None,
9979                        ..Default::default()
9980                    },
9981                ]);
9982                decl
9983            },
9984            result = Err(ErrorList::new(vec![
9985                Error::DifferentAvailabilityInAggregation(AvailabilityList(vec![
9986                    fdecl::Availability::Required,
9987                    fdecl::Availability::Optional,
9988                ]))
9989            ])),
9990        },
9991    }
9992
9993    test_validate_capabilities! {
9994        test_validate_capabilities_individually_ok => {
9995            input = vec![
9996                fdecl::Capability::Protocol(fdecl::Protocol {
9997                    name: Some("foo_svc".into()),
9998                    source_path: Some("/svc/foo".into()),
9999                    ..Default::default()
10000                }),
10001                fdecl::Capability::Directory(fdecl::Directory {
10002                    name: Some("foo_dir".into()),
10003                    source_path: Some("/foo".into()),
10004                    rights: Some(fio::Operations::CONNECT),
10005                    ..Default::default()
10006                }),
10007            ],
10008            as_builtin = false,
10009            result = Ok(()),
10010        },
10011        test_validate_capabilities_individually_err => {
10012            input = vec![
10013                fdecl::Capability::Protocol(fdecl::Protocol {
10014                    name: None,
10015                    source_path: None,
10016                    ..Default::default()
10017                }),
10018                fdecl::Capability::Directory(fdecl::Directory {
10019                    name: None,
10020                    source_path: None,
10021                    rights: None,
10022                    ..Default::default()
10023                }),
10024            ],
10025            as_builtin = false,
10026            result = Err(ErrorList::new(vec![
10027                Error::missing_field(DeclType::Protocol, "name"),
10028                Error::missing_field(DeclType::Protocol, "source_path"),
10029                Error::missing_field(DeclType::Directory, "name"),
10030                Error::missing_field(DeclType::Directory, "source_path"),
10031                Error::missing_field(DeclType::Directory, "rights"),
10032            ])),
10033        },
10034        test_validate_builtin_capabilities_individually_ok => {
10035            input = vec![
10036                fdecl::Capability::Protocol(fdecl::Protocol {
10037                    name: Some("foo_protocol".into()),
10038                    source_path: None,
10039                    ..Default::default()
10040                }),
10041                fdecl::Capability::Directory(fdecl::Directory {
10042                    name: Some("foo_dir".into()),
10043                    source_path: None,
10044                    rights: Some(fio::Operations::CONNECT),
10045                    ..Default::default()
10046                }),
10047                fdecl::Capability::Service(fdecl::Service {
10048                    name: Some("foo_svc".into()),
10049                    source_path: None,
10050                    ..Default::default()
10051                }),
10052                fdecl::Capability::Runner(fdecl::Runner {
10053                    name: Some("foo_runner".into()),
10054                    source_path: None,
10055                    ..Default::default()
10056                }),
10057                fdecl::Capability::Resolver(fdecl::Resolver {
10058                    name: Some("foo_resolver".into()),
10059                    source_path: None,
10060                    ..Default::default()
10061                }),
10062            ],
10063            as_builtin = true,
10064            result = Ok(()),
10065        },
10066        test_validate_builtin_capabilities_individually_err => {
10067            input = vec![
10068                fdecl::Capability::Protocol(fdecl::Protocol {
10069                    name: None,
10070                    source_path: Some("/svc/foo".into()),
10071                    ..Default::default()
10072                }),
10073                fdecl::Capability::Directory(fdecl::Directory {
10074                    name: None,
10075                    source_path: Some("/foo".into()),
10076                    rights: None,
10077                    ..Default::default()
10078                }),
10079                fdecl::Capability::Service(fdecl::Service {
10080                    name: None,
10081                    source_path: Some("/svc/foo".into()),
10082                    ..Default::default()
10083                }),
10084                fdecl::Capability::Runner(fdecl::Runner {
10085                    name: None,
10086                    source_path:  Some("/foo".into()),
10087                    ..Default::default()
10088                }),
10089                fdecl::Capability::Resolver(fdecl::Resolver {
10090                    name: None,
10091                    source_path:  Some("/foo".into()),
10092                    ..Default::default()
10093                }),
10094                fdecl::Capability::Storage(fdecl::Storage {
10095                    name: None,
10096                    ..Default::default()
10097                }),
10098            ],
10099            as_builtin = true,
10100            result = Err(ErrorList::new(vec![
10101                Error::missing_field(DeclType::Protocol, "name"),
10102                Error::extraneous_source_path(DeclType::Protocol, "/svc/foo"),
10103                Error::missing_field(DeclType::Directory, "name"),
10104                Error::extraneous_source_path(DeclType::Directory, "/foo"),
10105                Error::missing_field(DeclType::Directory, "rights"),
10106                Error::missing_field(DeclType::Service, "name"),
10107                Error::extraneous_source_path(DeclType::Service, "/svc/foo"),
10108                Error::missing_field(DeclType::Runner, "name"),
10109                Error::extraneous_source_path(DeclType::Runner, "/foo"),
10110                Error::missing_field(DeclType::Resolver, "name"),
10111                Error::extraneous_source_path(DeclType::Resolver, "/foo"),
10112                Error::CapabilityCannotBeBuiltin(DeclType::Storage),
10113            ])),
10114        },
10115        test_validate_delivery_type_ok => {
10116            input = vec![
10117                fdecl::Capability::Protocol(fdecl::Protocol {
10118                    name: Some("foo_svc1".into()),
10119                    source_path: Some("/svc/foo1".into()),
10120                    ..Default::default()
10121                }),
10122                fdecl::Capability::Protocol(fdecl::Protocol {
10123                    name: Some("foo_svc2".into()),
10124                    source_path: Some("/svc/foo2".into()),
10125                    delivery: Some(fdecl::DeliveryType::Immediate),
10126                    ..Default::default()
10127                }),
10128                fdecl::Capability::Protocol(fdecl::Protocol {
10129                    name: Some("foo_svc3".into()),
10130                    source_path: Some("/svc/foo3".into()),
10131                    delivery: Some(fdecl::DeliveryType::OnReadable),
10132                    ..Default::default()
10133                }),
10134            ],
10135            as_builtin = false,
10136            result = Ok(()),
10137        },
10138        test_validate_delivery_type_err => {
10139            input = vec![
10140                fdecl::Capability::Protocol(fdecl::Protocol {
10141                    name: Some("foo_svc".into()),
10142                    source_path: Some("/svc/foo".into()),
10143                    delivery: Some(fdecl::DeliveryType::unknown()),
10144                    ..Default::default()
10145                }),
10146            ],
10147            as_builtin = false,
10148            result = Err(ErrorList::new(vec![
10149                Error::invalid_field(DeclType::Protocol, "delivery"),
10150            ])),
10151        },
10152    }
10153
10154    /// Passes different source and availability options to `new_expose` to
10155    /// generate a component declaration.
10156    fn generate_expose_different_source_and_availability_decl(
10157        new_expose: impl Fn(fdecl::Ref, fdecl::Availability, &str) -> fdecl::Expose,
10158    ) -> fdecl::Component {
10159        let mut decl = new_component_decl();
10160        let child = "child";
10161        decl.children = Some(vec![fdecl::Child {
10162            name: Some(child.to_string()),
10163            url: Some("fuchsia-pkg://fuchsia.com/source#meta/source.cm".to_string()),
10164            startup: Some(fdecl::StartupMode::Lazy),
10165            ..Default::default()
10166        }]);
10167        decl.exposes = Some(vec![
10168            // Optional expose from self is okay.
10169            new_expose(
10170                fdecl::Ref::Self_(fdecl::SelfRef {}),
10171                fdecl::Availability::Optional,
10172                "fuchsia.examples.Echo1",
10173            ),
10174            // Optional expose from child is okay.
10175            new_expose(
10176                fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
10177                fdecl::Availability::Optional,
10178                "fuchsia.examples.Echo2",
10179            ),
10180            // Optional expose from void is okay.
10181            new_expose(
10182                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10183                fdecl::Availability::Optional,
10184                "fuchsia.examples.Echo3",
10185            ),
10186            // Optional expose from framework is okay.
10187            new_expose(
10188                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10189                fdecl::Availability::Optional,
10190                "fuchsia.examples.Echo4",
10191            ),
10192            // Transitional expose from self is okay.
10193            new_expose(
10194                fdecl::Ref::Self_(fdecl::SelfRef {}),
10195                fdecl::Availability::Transitional,
10196                "fuchsia.examples.Echo5",
10197            ),
10198            // Transitional expose from child is okay.
10199            new_expose(
10200                fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
10201                fdecl::Availability::Transitional,
10202                "fuchsia.examples.Echo6",
10203            ),
10204            // Transitional expose from void is okay.
10205            new_expose(
10206                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10207                fdecl::Availability::Transitional,
10208                "fuchsia.examples.Echo7",
10209            ),
10210            // Transitional expose from framework is okay.
10211            new_expose(
10212                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10213                fdecl::Availability::Transitional,
10214                "fuchsia.examples.Echo8",
10215            ),
10216            // Same-as-target expose from self is okay.
10217            new_expose(
10218                fdecl::Ref::Self_(fdecl::SelfRef {}),
10219                fdecl::Availability::SameAsTarget,
10220                "fuchsia.examples.Echo9",
10221            ),
10222            // Same-as-target expose from child is okay.
10223            new_expose(
10224                fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
10225                fdecl::Availability::SameAsTarget,
10226                "fuchsia.examples.Echo10",
10227            ),
10228            // Same-as-target expose from framework is okay.
10229            new_expose(
10230                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10231                fdecl::Availability::SameAsTarget,
10232                "fuchsia.examples.Echo11",
10233            ),
10234            // Required expose from void is an error.
10235            new_expose(
10236                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10237                fdecl::Availability::Required,
10238                "fuchsia.examples.Echo12",
10239            ),
10240            // Same-as-target expose from void is an error.
10241            new_expose(
10242                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10243                fdecl::Availability::SameAsTarget,
10244                "fuchsia.examples.Echo13",
10245            ),
10246        ]);
10247        decl
10248    }
10249
10250    /// Passes different source and availability options to `new_offer` to
10251    /// generate a component declaration.
10252    fn generate_offer_different_source_and_availability_decl(
10253        new_offer: impl Fn(fdecl::Ref, fdecl::Availability, &str) -> fdecl::Offer,
10254    ) -> fdecl::Component {
10255        let mut decl = new_component_decl();
10256        decl.children = Some(vec![
10257            fdecl::Child {
10258                name: Some("source".to_string()),
10259                url: Some("fuchsia-pkg://fuchsia.com/source#meta/source.cm".to_string()),
10260                startup: Some(fdecl::StartupMode::Lazy),
10261                on_terminate: None,
10262                environment: None,
10263                ..Default::default()
10264            },
10265            fdecl::Child {
10266                name: Some("sink".to_string()),
10267                url: Some("fuchsia-pkg://fuchsia.com/sink#meta/sink.cm".to_string()),
10268                startup: Some(fdecl::StartupMode::Lazy),
10269                on_terminate: None,
10270                environment: None,
10271                ..Default::default()
10272            },
10273        ]);
10274        decl.offers = Some(vec![
10275            // These offers are fine, offers with a source of parent or void can be
10276            // optional.
10277            new_offer(
10278                fdecl::Ref::Parent(fdecl::ParentRef {}),
10279                fdecl::Availability::Required,
10280                "fuchsia.examples.Echo0",
10281            ),
10282            new_offer(
10283                fdecl::Ref::Parent(fdecl::ParentRef {}),
10284                fdecl::Availability::Optional,
10285                "fuchsia.examples.Echo1",
10286            ),
10287            new_offer(
10288                fdecl::Ref::Parent(fdecl::ParentRef {}),
10289                fdecl::Availability::SameAsTarget,
10290                "fuchsia.examples.Echo2",
10291            ),
10292            new_offer(
10293                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10294                fdecl::Availability::Optional,
10295                "fuchsia.examples.Echo3",
10296            ),
10297            // These offers are fine, offers with a source other than parent or void
10298            // can also be optional.
10299            new_offer(
10300                fdecl::Ref::Self_(fdecl::SelfRef {}),
10301                fdecl::Availability::Optional,
10302                "fuchsia.examples.Echo4",
10303            ),
10304            new_offer(
10305                fdecl::Ref::Self_(fdecl::SelfRef {}),
10306                fdecl::Availability::SameAsTarget,
10307                "fuchsia.examples.Echo5",
10308            ),
10309            new_offer(
10310                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10311                fdecl::Availability::Optional,
10312                "fuchsia.examples.Echo6",
10313            ),
10314            new_offer(
10315                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10316                fdecl::Availability::SameAsTarget,
10317                "fuchsia.examples.Echo7",
10318            ),
10319            new_offer(
10320                fdecl::Ref::Child(fdecl::ChildRef { name: "source".to_string(), collection: None }),
10321                fdecl::Availability::Optional,
10322                "fuchsia.examples.Echo8",
10323            ),
10324            new_offer(
10325                fdecl::Ref::Child(fdecl::ChildRef { name: "source".to_string(), collection: None }),
10326                fdecl::Availability::SameAsTarget,
10327                "fuchsia.examples.Echo9",
10328            ),
10329            // These offers are also not fine, offers with a source of void must be optional
10330            new_offer(
10331                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10332                fdecl::Availability::Required,
10333                "fuchsia.examples.Echo10",
10334            ),
10335            new_offer(
10336                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10337                fdecl::Availability::SameAsTarget,
10338                "fuchsia.examples.Echo11",
10339            ),
10340        ]);
10341        decl
10342    }
10343
10344    #[test]
10345    fn test_validate_dynamic_offers_empty() {
10346        assert_eq!(
10347            validate_dynamic_offers(
10348                vec![],
10349                &mut DirectedGraph::new(),
10350                &vec![],
10351                &fdecl::Component::default()
10352            ),
10353            Ok(())
10354        );
10355    }
10356
10357    #[test]
10358    fn test_validate_dynamic_offers_okay() {
10359        assert_eq!(
10360            validate_dynamic_offers(
10361                vec![],
10362                &mut DirectedGraph::new(),
10363                &vec![
10364                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
10365                        dependency_type: Some(fdecl::DependencyType::Strong),
10366                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef)),
10367                        source_name: Some("thing".to_string()),
10368                        target_name: Some("thing".repeat(26)),
10369                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10370                            name: "foo".to_string(),
10371                            collection: Some("foo".to_string()),
10372                        })),
10373                        ..Default::default()
10374                    }),
10375                    fdecl::Offer::Service(fdecl::OfferService {
10376                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10377                        source_name: Some("thang".repeat(26)),
10378                        target_name: Some("thang".repeat(26)),
10379                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10380                            name: "foo".to_string(),
10381                            collection: Some("foo".to_string()),
10382                        })),
10383                        ..Default::default()
10384                    }),
10385                    fdecl::Offer::Directory(fdecl::OfferDirectory {
10386                        dependency_type: Some(fdecl::DependencyType::Strong),
10387                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10388                        source_name: Some("thung1".repeat(26)),
10389                        target_name: Some("thung1".repeat(26)),
10390                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10391                            name: "foo".to_string(),
10392                            collection: Some("foo".to_string()),
10393                        })),
10394                        ..Default::default()
10395                    }),
10396                    fdecl::Offer::Storage(fdecl::OfferStorage {
10397                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10398                        source_name: Some("thung2".repeat(26)),
10399                        target_name: Some("thung2".repeat(26)),
10400                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10401                            name: "foo".to_string(),
10402                            collection: Some("foo".to_string()),
10403                        })),
10404                        ..Default::default()
10405                    }),
10406                    fdecl::Offer::Runner(fdecl::OfferRunner {
10407                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10408                        source_name: Some("thung3".repeat(26)),
10409                        target_name: Some("thung3".repeat(26)),
10410                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10411                            name: "foo".to_string(),
10412                            collection: Some("foo".to_string()),
10413                        })),
10414                        ..Default::default()
10415                    }),
10416                    fdecl::Offer::Resolver(fdecl::OfferResolver {
10417                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10418                        source_name: Some("thung4".repeat(26)),
10419                        target_name: Some("thung4".repeat(26)),
10420                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10421                            name: "foo".to_string(),
10422                            collection: Some("foo".to_string()),
10423                        })),
10424                        ..Default::default()
10425                    }),
10426                ],
10427                &fdecl::Component {
10428                    capabilities: Some(vec![fdecl::Capability::Protocol(fdecl::Protocol {
10429                        name: Some("thing".to_string()),
10430                        source_path: Some("/svc/foo".into()),
10431                        ..Default::default()
10432                    }),]),
10433                    ..Default::default()
10434                }
10435            ),
10436            Ok(())
10437        );
10438    }
10439
10440    #[test]
10441    fn test_validate_dynamic_offers_valid_service_aggregation() {
10442        assert_eq!(
10443            validate_dynamic_offers(
10444                vec![],
10445                &mut DirectedGraph::new(),
10446                &vec![
10447                    fdecl::Offer::Service(fdecl::OfferService {
10448                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10449                            name: "child_a".to_string(),
10450                            collection: None
10451                        })),
10452                        source_name: Some("fuchsia.logger.Log".to_string()),
10453                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10454                            name: "child_c".to_string(),
10455                            collection: None,
10456                        })),
10457                        target_name: Some("fuchsia.logger.Log".to_string()),
10458                        source_instance_filter: Some(vec!["default".to_string()]),
10459                        ..Default::default()
10460                    }),
10461                    fdecl::Offer::Service(fdecl::OfferService {
10462                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10463                            name: "child_b".to_string(),
10464                            collection: None
10465                        })),
10466                        source_name: Some("fuchsia.logger.Log".to_string()),
10467                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10468                            name: "child_c".to_string(),
10469                            collection: None,
10470                        })),
10471                        target_name: Some("fuchsia.logger.Log".to_string()),
10472                        source_instance_filter: Some(vec!["a_different_default".to_string()]),
10473                        ..Default::default()
10474                    })
10475                ],
10476                &fdecl::Component {
10477                    children: Some(vec![
10478                        fdecl::Child {
10479                            name: Some("child_a".to_string()),
10480                            url: Some(
10481                                "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10482                                    .to_string()
10483                            ),
10484                            startup: Some(fdecl::StartupMode::Lazy),
10485                            on_terminate: None,
10486                            environment: None,
10487                            ..Default::default()
10488                        },
10489                        fdecl::Child {
10490                            name: Some("child_b".to_string()),
10491                            url: Some(
10492                                "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10493                                    .to_string()
10494                            ),
10495                            startup: Some(fdecl::StartupMode::Lazy),
10496                            on_terminate: None,
10497                            environment: None,
10498                            ..Default::default()
10499                        },
10500                        fdecl::Child {
10501                            name: Some("child_c".to_string()),
10502                            url: Some(
10503                                "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10504                                    .to_string()
10505                            ),
10506                            startup: Some(fdecl::StartupMode::Lazy),
10507                            on_terminate: None,
10508                            environment: None,
10509                            ..Default::default()
10510                        },
10511                    ]),
10512                    ..Default::default()
10513                }
10514            ),
10515            Ok(())
10516        );
10517    }
10518
10519    #[test]
10520    fn test_validate_dynamic_service_aggregation_missing_filter() {
10521        assert_eq!(
10522            validate_dynamic_offers(
10523                vec![],
10524                &mut DirectedGraph::new(),
10525                &vec![
10526                    fdecl::Offer::Service(fdecl::OfferService {
10527                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10528                            name: "child_a".to_string(),
10529                            collection: None
10530                        })),
10531                        source_name: Some("fuchsia.logger.Log".to_string()),
10532                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10533                            name: "child_c".to_string(),
10534                            collection: None,
10535                        })),
10536                        target_name: Some("fuchsia.logger.Log".to_string()),
10537                        source_instance_filter: Some(vec!["default".to_string()]),
10538                        ..Default::default()
10539                    }),
10540                    fdecl::Offer::Service(fdecl::OfferService {
10541                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10542                            name: "child_b".to_string(),
10543                            collection: None
10544                        })),
10545                        source_name: Some("fuchsia.logger.Log".to_string()),
10546                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10547                            name: "child_c".to_string(),
10548                            collection: None,
10549                        })),
10550                        target_name: Some("fuchsia.logger.Log".to_string()),
10551                        source_instance_filter: None,
10552                        ..Default::default()
10553                    }),
10554                ],
10555                &fdecl::Component {
10556                    children: Some(vec![
10557                        fdecl::Child {
10558                            name: Some("child_a".to_string()),
10559                            url: Some(
10560                                "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10561                                    .to_string()
10562                            ),
10563                            startup: Some(fdecl::StartupMode::Lazy),
10564                            ..Default::default()
10565                        },
10566                        fdecl::Child {
10567                            name: Some("child_b".to_string()),
10568                            url: Some(
10569                                "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10570                                    .to_string()
10571                            ),
10572                            startup: Some(fdecl::StartupMode::Lazy),
10573                            ..Default::default()
10574                        },
10575                        fdecl::Child {
10576                            name: Some("child_c".to_string()),
10577                            url: Some(
10578                                "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10579                                    .to_string()
10580                            ),
10581                            startup: Some(fdecl::StartupMode::Lazy),
10582                            ..Default::default()
10583                        },
10584                    ]),
10585                    ..Default::default()
10586                },
10587            ),
10588            Err(ErrorList::new(vec![Error::invalid_aggregate_offer(
10589                "source_instance_filter must be set for dynamic aggregate service offers"
10590            ),]))
10591        );
10592    }
10593
10594    #[test]
10595    fn test_validate_dynamic_offers_omit_target() {
10596        assert_eq!(
10597            validate_dynamic_offers(
10598                vec![],
10599                &mut DirectedGraph::new(),
10600                &vec![
10601                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
10602                        dependency_type: Some(fdecl::DependencyType::Strong),
10603                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef)),
10604                        source_name: Some("thing".to_string()),
10605                        target_name: Some("thing".to_string()),
10606                        ..Default::default()
10607                    }),
10608                    fdecl::Offer::Service(fdecl::OfferService {
10609                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10610                        source_name: Some("thang".to_string()),
10611                        target_name: Some("thang".to_string()),
10612                        ..Default::default()
10613                    }),
10614                    fdecl::Offer::Directory(fdecl::OfferDirectory {
10615                        dependency_type: Some(fdecl::DependencyType::Strong),
10616                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10617                        source_name: Some("thung1".to_string()),
10618                        target_name: Some("thung1".to_string()),
10619                        ..Default::default()
10620                    }),
10621                    fdecl::Offer::Storage(fdecl::OfferStorage {
10622                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10623                        source_name: Some("thung2".to_string()),
10624                        target_name: Some("thung2".to_string()),
10625                        ..Default::default()
10626                    }),
10627                    fdecl::Offer::Runner(fdecl::OfferRunner {
10628                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10629                        source_name: Some("thung3".to_string()),
10630                        target_name: Some("thung3".to_string()),
10631                        ..Default::default()
10632                    }),
10633                    fdecl::Offer::Resolver(fdecl::OfferResolver {
10634                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10635                        source_name: Some("thung4".to_string()),
10636                        target_name: Some("thung4".to_string()),
10637                        ..Default::default()
10638                    }),
10639                ],
10640                &fdecl::Component {
10641                    capabilities: Some(vec![fdecl::Capability::Protocol(fdecl::Protocol {
10642                        name: Some("thing".to_string()),
10643                        source_path: Some("/svc/foo".into()),
10644                        ..Default::default()
10645                    }),]),
10646                    ..Default::default()
10647                }
10648            ),
10649            Err(ErrorList::new(vec![
10650                Error::missing_field(DeclType::OfferProtocol, "target"),
10651                Error::missing_field(DeclType::OfferService, "target"),
10652                Error::missing_field(DeclType::OfferDirectory, "target"),
10653                Error::missing_field(DeclType::OfferStorage, "target"),
10654                Error::missing_field(DeclType::OfferRunner, "target"),
10655                Error::missing_field(DeclType::OfferResolver, "target"),
10656            ]))
10657        );
10658    }
10659
10660    #[test]
10661    fn test_validate_dynamic_offers_collection_collision() {
10662        assert_eq!(
10663            validate_dynamic_offers(
10664                vec![],
10665                &mut DirectedGraph::new(),
10666                &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10667                    dependency_type: Some(fdecl::DependencyType::Strong),
10668                    source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10669                    source_name: Some("thing".to_string()),
10670                    target_name: Some("thing".to_string()),
10671                    target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10672                        name: "child".to_string(),
10673                        collection: Some("coll".to_string()),
10674                    })),
10675                    ..Default::default()
10676                }),],
10677                &fdecl::Component {
10678                    offers: Some(vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10679                        dependency_type: Some(fdecl::DependencyType::Strong),
10680                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10681                        source_name: Some("thing".to_string()),
10682                        target_name: Some("thing".to_string()),
10683                        target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10684                            name: "coll".into()
10685                        })),
10686                        ..Default::default()
10687                    }),]),
10688                    collections: Some(vec![fdecl::Collection {
10689                        name: Some("coll".to_string()),
10690                        durability: Some(fdecl::Durability::Transient),
10691                        ..Default::default()
10692                    },]),
10693                    ..Default::default()
10694                }
10695            ),
10696            Err(ErrorList::new(vec![Error::duplicate_field(
10697                DeclType::OfferProtocol,
10698                "target_name",
10699                "thing"
10700            ),]))
10701        );
10702    }
10703
10704    #[test]
10705    fn test_validate_dynamic_offers_cycle_collection_to_static_child() {
10706        assert_eq!(
10707            validate_dynamic_offers(
10708                vec![("dyn", "coll")],
10709                &mut DirectedGraph::new(),
10710                &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10711                    source_name: Some("bar".to_string()),
10712                    target_name: Some("bar".to_string()),
10713                    source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10714                        name: "static_child".into(),
10715                        collection: None,
10716                    })),
10717                    target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10718                        name: "dyn".to_string(),
10719                        collection: Some("coll".to_string()),
10720                    })),
10721                    dependency_type: Some(fdecl::DependencyType::Strong),
10722                    ..Default::default()
10723                }),],
10724                &fdecl::Component {
10725                    offers: Some(vec![fdecl::Offer::Service(fdecl::OfferService {
10726                        source_name: Some("foo".to_string()),
10727                        target_name: Some("foo".to_string()),
10728                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10729                            name: "coll".into(),
10730                        })),
10731                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10732                            name: "static_child".into(),
10733                            collection: None,
10734                        })),
10735                        ..Default::default()
10736                    })]),
10737                    children: Some(vec![fdecl::Child {
10738                        name: Some("static_child".into()),
10739                        url: Some("url#child.cm".into()),
10740                        startup: Some(fdecl::StartupMode::Lazy),
10741                        ..Default::default()
10742                    }]),
10743                    collections: Some(vec![fdecl::Collection {
10744                        name: Some("coll".into()),
10745                        durability: Some(fdecl::Durability::Transient),
10746                        ..Default::default()
10747                    }]),
10748                    ..Default::default()
10749                }
10750            ),
10751            Err(ErrorList::new(vec![Error::dependency_cycle(
10752                "{{child coll:dyn -> collection coll -> child static_child -> child coll:dyn}}"
10753            )]))
10754        );
10755    }
10756
10757    #[test]
10758    fn test_validate_dynamic_offers_cycle_collection_to_dynamic_child() {
10759        assert_eq!(
10760            validate_dynamic_offers(
10761                vec![("dyn", "coll1"), ("dyn", "coll2")],
10762                &mut DirectedGraph::new(),
10763                &vec![
10764                    fdecl::Offer::Service(fdecl::OfferService {
10765                        source_name: Some("foo".to_string()),
10766                        target_name: Some("foo".to_string()),
10767                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10768                            name: "coll2".into(),
10769                        })),
10770                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10771                            name: "dyn".into(),
10772                            collection: Some("coll1".into()),
10773                        })),
10774                        ..Default::default()
10775                    }),
10776                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
10777                        source_name: Some("bar".to_string()),
10778                        target_name: Some("bar".to_string()),
10779                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10780                            name: "dyn".into(),
10781                            collection: Some("coll1".into()),
10782                        })),
10783                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10784                            name: "dyn".to_string(),
10785                            collection: Some("coll2".to_string()),
10786                        })),
10787                        dependency_type: Some(fdecl::DependencyType::Strong),
10788                        ..Default::default()
10789                    }),
10790                ],
10791                &fdecl::Component {
10792                    collections: Some(vec![
10793                        fdecl::Collection {
10794                            name: Some("coll1".into()),
10795                            durability: Some(fdecl::Durability::Transient),
10796                            ..Default::default()
10797                        },
10798                        fdecl::Collection {
10799                            name: Some("coll2".into()),
10800                            durability: Some(fdecl::Durability::Transient),
10801                            ..Default::default()
10802                        },
10803                    ]),
10804                    ..Default::default()
10805                }
10806            ),
10807            Err(ErrorList::new(vec![Error::dependency_cycle(
10808                "{{child coll1:dyn -> child coll2:dyn -> collection coll2 -> child coll1:dyn}}"
10809            )]))
10810        );
10811    }
10812
10813    #[test]
10814    fn test_validate_dynamic_offers_cycle_collection_to_dynamic_child_between_new_and_existing() {
10815        assert_eq!(
10816            validate_dynamic_offers(
10817                vec![("dyn", "coll1"), ("dyn", "coll2")],
10818                &mut DirectedGraph::from([(
10819                    DependencyNode::Child("dyn".into(), Some("coll1".into())),
10820                    DependencyNode::Child("dyn".into(), Some("coll2".into())),
10821                )]),
10822                &vec![fdecl::Offer::Service(fdecl::OfferService {
10823                    source_name: Some("foo".to_string()),
10824                    target_name: Some("foo".to_string()),
10825                    source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10826                        name: "coll2".into(),
10827                    })),
10828                    target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10829                        name: "dyn".into(),
10830                        collection: Some("coll1".into()),
10831                    })),
10832                    ..Default::default()
10833                }),],
10834                &fdecl::Component {
10835                    collections: Some(vec![
10836                        fdecl::Collection {
10837                            name: Some("coll1".into()),
10838                            durability: Some(fdecl::Durability::Transient),
10839                            ..Default::default()
10840                        },
10841                        fdecl::Collection {
10842                            name: Some("coll2".into()),
10843                            durability: Some(fdecl::Durability::Transient),
10844                            ..Default::default()
10845                        },
10846                    ]),
10847                    ..Default::default()
10848                }
10849            ),
10850            Err(ErrorList::new(vec![Error::dependency_cycle(
10851                "{{child coll1:dyn -> child coll2:dyn -> collection coll2 -> child coll1:dyn}}"
10852            )]))
10853        );
10854    }
10855
10856    #[test]
10857    fn test_validate_dynamic_offers_cycle_collection_to_collection() {
10858        assert_eq!(
10859            validate_dynamic_offers(
10860                vec![("dyn", "coll1"), ("dyn", "coll2")],
10861                &mut DirectedGraph::new(),
10862                &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10863                    source_name: Some("bar".to_string()),
10864                    target_name: Some("bar".to_string()),
10865                    source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10866                        name: "dyn".into(),
10867                        collection: Some("coll2".parse().unwrap()),
10868                    })),
10869                    target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10870                        name: "dyn".into(),
10871                        collection: Some("coll1".parse().unwrap()),
10872                    })),
10873                    dependency_type: Some(fdecl::DependencyType::Strong),
10874                    ..Default::default()
10875                }),],
10876                &fdecl::Component {
10877                    offers: Some(vec![fdecl::Offer::Service(fdecl::OfferService {
10878                        source_name: Some("foo".to_string()),
10879                        target_name: Some("foo".to_string()),
10880                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10881                            name: "coll1".into(),
10882                        })),
10883                        target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10884                            name: "coll2".into(),
10885                        })),
10886                        ..Default::default()
10887                    })]),
10888                    collections: Some(vec![
10889                        fdecl::Collection {
10890                            name: Some("coll1".into()),
10891                            durability: Some(fdecl::Durability::Transient),
10892                            ..Default::default()
10893                        },
10894                        fdecl::Collection {
10895                            name: Some("coll2".into()),
10896                            durability: Some(fdecl::Durability::Transient),
10897                            ..Default::default()
10898                        },
10899                    ]),
10900                    ..Default::default()
10901                }
10902            ),
10903            Err(ErrorList::new(vec![Error::dependency_cycle(
10904                "{{child coll1:dyn -> collection coll1 -> child coll2:dyn -> child coll1:dyn}}",
10905            )]))
10906        );
10907    }
10908
10909    #[test]
10910    fn test_validate_dynamic_child() {
10911        assert_eq!(
10912            Ok(()),
10913            validate_dynamic_child(&fdecl::Child {
10914                name: Some("a".repeat(MAX_LONG_NAME_LENGTH).to_string()),
10915                url: Some("test:///child".to_string()),
10916                startup: Some(fdecl::StartupMode::Lazy),
10917                on_terminate: None,
10918                environment: None,
10919                ..Default::default()
10920            })
10921        );
10922    }
10923
10924    #[test]
10925    fn test_validate_dynamic_child_environment_is_invalid() {
10926        assert_eq!(
10927            validate_dynamic_child(&fdecl::Child {
10928                name: Some("a".repeat(MAX_LONG_NAME_LENGTH).to_string()),
10929                url: Some("test:///child".to_string()),
10930                startup: Some(fdecl::StartupMode::Lazy),
10931                on_terminate: None,
10932                environment: Some("env".to_string()),
10933                ..Default::default()
10934            }),
10935            Err(ErrorList::new(vec![Error::DynamicChildWithEnvironment]))
10936        );
10937    }
10938
10939    #[test]
10940    fn test_validate_dynamic_offers_missing_stuff() {
10941        assert_eq!(
10942            validate_dynamic_offers(
10943                vec![],
10944                &mut DirectedGraph::new(),
10945                &vec![
10946                    fdecl::Offer::Protocol(fdecl::OfferProtocol::default()),
10947                    fdecl::Offer::Service(fdecl::OfferService::default()),
10948                    fdecl::Offer::Directory(fdecl::OfferDirectory::default()),
10949                    fdecl::Offer::Storage(fdecl::OfferStorage::default()),
10950                    fdecl::Offer::Runner(fdecl::OfferRunner::default()),
10951                    fdecl::Offer::Resolver(fdecl::OfferResolver::default()),
10952                ],
10953                &fdecl::Component::default()
10954            ),
10955            Err(ErrorList::new(vec![
10956                Error::missing_field(DeclType::OfferProtocol, "source"),
10957                Error::missing_field(DeclType::OfferProtocol, "source_name"),
10958                Error::missing_field(DeclType::OfferProtocol, "target"),
10959                Error::missing_field(DeclType::OfferProtocol, "target_name"),
10960                Error::missing_field(DeclType::OfferProtocol, "dependency_type"),
10961                Error::missing_field(DeclType::OfferService, "source"),
10962                Error::missing_field(DeclType::OfferService, "source_name"),
10963                Error::missing_field(DeclType::OfferService, "target"),
10964                Error::missing_field(DeclType::OfferService, "target_name"),
10965                Error::missing_field(DeclType::OfferDirectory, "source"),
10966                Error::missing_field(DeclType::OfferDirectory, "source_name"),
10967                Error::missing_field(DeclType::OfferDirectory, "target"),
10968                Error::missing_field(DeclType::OfferDirectory, "target_name"),
10969                Error::missing_field(DeclType::OfferDirectory, "dependency_type"),
10970                Error::missing_field(DeclType::OfferStorage, "source"),
10971                Error::missing_field(DeclType::OfferStorage, "source_name"),
10972                Error::missing_field(DeclType::OfferStorage, "target"),
10973                Error::missing_field(DeclType::OfferStorage, "target_name"),
10974                Error::missing_field(DeclType::OfferRunner, "source"),
10975                Error::missing_field(DeclType::OfferRunner, "source_name"),
10976                Error::missing_field(DeclType::OfferRunner, "target"),
10977                Error::missing_field(DeclType::OfferRunner, "target_name"),
10978                Error::missing_field(DeclType::OfferResolver, "source"),
10979                Error::missing_field(DeclType::OfferResolver, "source_name"),
10980                Error::missing_field(DeclType::OfferResolver, "target"),
10981                Error::missing_field(DeclType::OfferResolver, "target_name"),
10982            ]))
10983        );
10984    }
10985
10986    test_dependency! {
10987        (test_validate_offers_protocol_dependency_cycle) => {
10988            ty = fdecl::Offer::Protocol,
10989            offer_decl = fdecl::OfferProtocol {
10990                source: None,  // Filled by macro
10991                target: None,  // Filled by macro
10992                source_name: Some(format!("thing")),
10993                target_name: Some(format!("thing")),
10994                dependency_type: Some(fdecl::DependencyType::Strong),
10995                ..Default::default()
10996            },
10997        },
10998        (test_validate_offers_directory_dependency_cycle) => {
10999            ty = fdecl::Offer::Directory,
11000            offer_decl = fdecl::OfferDirectory {
11001                source: None,  // Filled by macro
11002                target: None,  // Filled by macro
11003                source_name: Some(format!("thing")),
11004                target_name: Some(format!("thing")),
11005                rights: Some(fio::Operations::CONNECT),
11006                subdir: None,
11007                dependency_type: Some(fdecl::DependencyType::Strong),
11008                ..Default::default()
11009            },
11010        },
11011        (test_validate_offers_service_dependency_cycle) => {
11012            ty = fdecl::Offer::Service,
11013            offer_decl = fdecl::OfferService {
11014                source: None,  // Filled by macro
11015                target: None,  // Filled by macro
11016                source_name: Some(format!("thing")),
11017                target_name: Some(format!("thing")),
11018                ..Default::default()
11019            },
11020        },
11021        (test_validate_offers_runner_dependency_cycle) => {
11022            ty = fdecl::Offer::Runner,
11023            offer_decl = fdecl::OfferRunner {
11024                source: None,  // Filled by macro
11025                target: None,  // Filled by macro
11026                source_name: Some(format!("thing")),
11027                target_name: Some(format!("thing")),
11028                ..Default::default()
11029            },
11030        },
11031        (test_validate_offers_resolver_dependency_cycle) => {
11032            ty = fdecl::Offer::Resolver,
11033            offer_decl = fdecl::OfferResolver {
11034                source: None,  // Filled by macro
11035                target: None,  // Filled by macro
11036                source_name: Some(format!("thing")),
11037                target_name: Some(format!("thing")),
11038                ..Default::default()
11039            },
11040        },
11041    }
11042    test_weak_dependency! {
11043        (test_validate_offers_protocol_weak_dependency_cycle) => {
11044            ty = fdecl::Offer::Protocol,
11045            offer_decl = fdecl::OfferProtocol {
11046                source: None,  // Filled by macro
11047                target: None,  // Filled by macro
11048                source_name: Some(format!("thing")),
11049                target_name: Some(format!("thing")),
11050                dependency_type: None, // Filled by macro
11051                ..Default::default()
11052            },
11053        },
11054        (test_validate_offers_directory_weak_dependency_cycle) => {
11055            ty = fdecl::Offer::Directory,
11056            offer_decl = fdecl::OfferDirectory {
11057                source: None,  // Filled by macro
11058                target: None,  // Filled by macro
11059                source_name: Some(format!("thing")),
11060                target_name: Some(format!("thing")),
11061                rights: Some(fio::Operations::CONNECT),
11062                subdir: None,
11063                dependency_type: None,  // Filled by macro
11064                ..Default::default()
11065            },
11066        },
11067        (test_validate_offers_service_weak_dependency_cycle) => {
11068            ty = fdecl::Offer::Service,
11069            offer_decl = fdecl::OfferService {
11070                source: None,  // Filled by macro
11071                target: None,  // Filled by macro
11072                source_name: Some(format!("thing")),
11073                target_name: Some(format!("thing")),
11074                dependency_type: None, // Filled by macro
11075                ..Default::default()
11076            },
11077        },
11078    }
11079}