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 directed_graph::DirectedGraph;
14use fidl_fuchsia_component_decl as fdecl;
15use itertools::Itertools;
16use std::collections::{BTreeSet, HashMap, HashSet};
17use std::path::Path;
18
19trait HasAvailability {
20    fn availability(&self) -> fdecl::Availability;
21}
22
23impl HasAvailability for fdecl::ExposeService {
24    fn availability(&self) -> fdecl::Availability {
25        return self.availability.unwrap_or(fdecl::Availability::Required);
26    }
27}
28
29impl HasAvailability for fdecl::OfferService {
30    fn availability(&self) -> fdecl::Availability {
31        return self.availability.unwrap_or(fdecl::Availability::Required);
32    }
33}
34
35#[cfg(fuchsia_api_level_at_least = "25")]
36macro_rules! get_source_dictionary {
37    ($decl:ident) => {
38        $decl.source_dictionary.as_ref()
39    };
40}
41#[cfg(fuchsia_api_level_less_than = "25")]
42macro_rules! get_source_dictionary {
43    ($decl:ident) => {
44        None
45    };
46}
47
48/// Validates Configuration Value Spec.
49///
50/// For now, this simply verifies that all semantically required fields are present.
51pub fn validate_value_spec(spec: &fdecl::ConfigValueSpec) -> Result<(), ErrorList> {
52    let mut errors = vec![];
53    if let Some(value) = &spec.value {
54        match value {
55            fdecl::ConfigValue::Single(s) => match s {
56                fdecl::ConfigSingleValue::Bool(_)
57                | fdecl::ConfigSingleValue::Uint8(_)
58                | fdecl::ConfigSingleValue::Uint16(_)
59                | fdecl::ConfigSingleValue::Uint32(_)
60                | fdecl::ConfigSingleValue::Uint64(_)
61                | fdecl::ConfigSingleValue::Int8(_)
62                | fdecl::ConfigSingleValue::Int16(_)
63                | fdecl::ConfigSingleValue::Int32(_)
64                | fdecl::ConfigSingleValue::Int64(_)
65                | fdecl::ConfigSingleValue::String(_) => {}
66                fdecl::ConfigSingleValueUnknown!() => {
67                    errors.push(Error::invalid_field(DeclType::ConfigValueSpec, "value"));
68                }
69            },
70            fdecl::ConfigValue::Vector(l) => match l {
71                fdecl::ConfigVectorValue::BoolVector(_)
72                | fdecl::ConfigVectorValue::Uint8Vector(_)
73                | fdecl::ConfigVectorValue::Uint16Vector(_)
74                | fdecl::ConfigVectorValue::Uint32Vector(_)
75                | fdecl::ConfigVectorValue::Uint64Vector(_)
76                | fdecl::ConfigVectorValue::Int8Vector(_)
77                | fdecl::ConfigVectorValue::Int16Vector(_)
78                | fdecl::ConfigVectorValue::Int32Vector(_)
79                | fdecl::ConfigVectorValue::Int64Vector(_)
80                | fdecl::ConfigVectorValue::StringVector(_) => {}
81                fdecl::ConfigVectorValueUnknown!() => {
82                    errors.push(Error::invalid_field(DeclType::ConfigValueSpec, "value"));
83                }
84            },
85            fdecl::ConfigValueUnknown!() => {
86                errors.push(Error::invalid_field(DeclType::ConfigValueSpec, "value"));
87            }
88        }
89    } else {
90        errors.push(Error::missing_field(DeclType::ConfigValueSpec, "value"));
91    }
92
93    if errors.is_empty() {
94        Ok(())
95    } else {
96        Err(ErrorList::new(errors))
97    }
98}
99
100/// Validates Configuration Values Data.
101///
102/// The Value Data may ultimately originate from a CVF file, or be directly constructed by the
103/// caller. Either way, Value Data should always be validated before it's used. For now, this
104/// simply verifies that all semantically required fields are present.
105///
106/// This method does not validate value data against a configuration schema.
107pub fn validate_values_data(data: &fdecl::ConfigValuesData) -> Result<(), ErrorList> {
108    let mut errors = vec![];
109    if let Some(values) = &data.values {
110        for spec in values {
111            if let Err(mut e) = validate_value_spec(spec) {
112                errors.append(&mut e.errs);
113            }
114        }
115    } else {
116        errors.push(Error::missing_field(DeclType::ConfigValuesData, "values"));
117    }
118
119    if let Some(checksum) = &data.checksum {
120        match checksum {
121            fdecl::ConfigChecksum::Sha256(_) => {}
122            fdecl::ConfigChecksumUnknown!() => {
123                errors.push(Error::invalid_field(DeclType::ConfigValuesData, "checksum"));
124            }
125        }
126    } else {
127        errors.push(Error::missing_field(DeclType::ConfigValuesData, "checksum"));
128    }
129
130    if errors.is_empty() {
131        Ok(())
132    } else {
133        Err(ErrorList::new(errors))
134    }
135}
136
137// `fdecl::Ref` is not hashable, so define this equivalent type for use in maps
138#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
139enum RefKey<'a> {
140    Parent,
141    Self_,
142    Child(&'a str),
143    Collection(&'a str),
144    Framework,
145    Capability,
146    Debug,
147}
148
149/// Validates a Component.
150///
151/// The Component may ultimately originate from a CM file, or be directly constructed by the
152/// caller. Either way, a Component should always be validated before it's used. Examples
153/// of what is validated (which may evolve in the future):
154///
155/// - That all semantically required fields are present
156/// - That a child_name referenced in a source actually exists in the list of children
157/// - That there are no duplicate target paths.
158/// - That only weak-dependency capabilities may be offered back to the
159///   component that exposed them.
160///
161/// All checks are local to this Component.
162pub fn validate(decl: &fdecl::Component) -> Result<(), ErrorList> {
163    let ctx = ValidationContext::default();
164    ctx.validate(decl, &vec![]).map_err(|errs| ErrorList::new(errs))
165}
166
167/// Validates a list of namespace or builtin Capabilities.
168fn validate_capabilities(
169    capabilities: &[fdecl::Capability],
170    as_builtin: bool,
171) -> Result<(), ErrorList> {
172    let mut ctx = ValidationContext::default();
173
174    #[cfg(fuchsia_api_level_at_least = "25")]
175    ctx.load_dictionary_names(capabilities.iter().filter_map(|capability| match capability {
176        fdecl::Capability::Dictionary(dictionary_decl) => Some(dictionary_decl),
177        _ => None,
178    }));
179
180    ctx.validate_capability_decls(capabilities, as_builtin);
181    if ctx.errors.is_empty() {
182        Ok(())
183    } else {
184        Err(ErrorList::new(ctx.errors))
185    }
186}
187
188// Validate builtin capabilities.
189pub fn validate_builtin_capabilities(
190    capabilities: &Vec<fdecl::Capability>,
191) -> Result<(), ErrorList> {
192    validate_capabilities(capabilities, true)
193}
194
195// Validate namespace capabilities.
196pub fn validate_namespace_capabilities(
197    capabilities: &Vec<fdecl::Capability>,
198) -> Result<(), ErrorList> {
199    validate_capabilities(capabilities, false)
200}
201
202/// An interface to call into either `check_dynamic_name()` or `check_name()`, depending on the context
203/// of the caller.
204type CheckChildNameFn = fn(Option<&String>, DeclType, &str, &mut Vec<Error>) -> bool;
205
206pub fn validate_dynamic_child(child: &fdecl::Child) -> Result<(), ErrorList> {
207    let mut errors = vec![];
208
209    if let Err(mut error_list) = validate_child(child, check_dynamic_name) {
210        errors.append(&mut error_list.errs);
211    }
212
213    if child.environment.is_some() {
214        errors.push(Error::DynamicChildWithEnvironment);
215    }
216
217    if errors.is_empty() {
218        Ok(())
219    } else {
220        Err(ErrorList { errs: errors })
221    }
222}
223
224/// Validates an independent Child. Performs the same validation on it as `validate`. A
225/// `check_name_fn` is passed into specify the function used to validate the child name.
226fn validate_child(
227    child: &fdecl::Child,
228    check_child_name: CheckChildNameFn,
229) -> Result<(), ErrorList> {
230    let mut errors = vec![];
231    check_child_name(child.name.as_ref(), DeclType::Child, "name", &mut errors);
232    check_url(child.url.as_ref(), DeclType::Child, "url", &mut errors);
233    if child.startup.is_none() {
234        errors.push(Error::missing_field(DeclType::Child, "startup"));
235    }
236    // Allow `on_terminate` to be unset since the default is almost always desired.
237    if child.environment.is_some() {
238        check_name(child.environment.as_ref(), DeclType::Child, "environment", &mut errors);
239    }
240    if errors.is_empty() {
241        Ok(())
242    } else {
243        Err(ErrorList { errs: errors })
244    }
245}
246
247/// Validates a collection of dynamic offers. Dynamic offers differ from static
248/// offers, in that
249///
250/// 1. a dynamic offer's `target` field must be omitted;
251/// 2. a dynamic offer's `source` _may_ be a dynamic child;
252/// 3. since this crate isn't really designed to handle dynamic children, we
253///    disable the checks that ensure that the source/target exist, and that the
254///    offers don't introduce any cycles.
255pub fn validate_dynamic_offers<'a>(
256    dynamic_children: Vec<(&'a str, &'a str)>,
257    offers: &'a Vec<fdecl::Offer>,
258    decl: &'a fdecl::Component,
259) -> Result<(), ErrorList> {
260    let mut ctx = ValidationContext::default();
261    ctx.dynamic_children = dynamic_children;
262    ctx.validate(decl, offers).map_err(|errs| ErrorList::new(errs))
263}
264
265fn check_offer_name(
266    prop: Option<&String>,
267    decl: DeclType,
268    keyword: &str,
269    offer_type: OfferType,
270    errors: &mut Vec<Error>,
271) -> bool {
272    if offer_type == OfferType::Dynamic {
273        check_dynamic_name(prop, decl, keyword, errors)
274    } else {
275        check_name(prop, decl, keyword, errors)
276    }
277}
278
279#[derive(Default)]
280struct ValidationContext<'a> {
281    all_children: HashMap<&'a str, &'a fdecl::Child>,
282    all_collections: HashSet<&'a str>,
283    all_capability_ids: HashSet<&'a str>,
284    all_storages: HashMap<&'a str, Option<&'a fdecl::Ref>>,
285    all_services: HashSet<&'a str>,
286    all_protocols: HashSet<&'a str>,
287    all_directories: HashSet<&'a str>,
288    all_runners: HashSet<&'a str>,
289    all_resolvers: HashSet<&'a str>,
290    #[cfg(fuchsia_api_level_at_least = "25")]
291    all_dictionaries: HashMap<&'a str, &'a fdecl::Dictionary>,
292
293    #[cfg(fuchsia_api_level_at_least = "HEAD")]
294    all_configs: HashSet<&'a str>,
295
296    all_environment_names: HashSet<&'a str>,
297    dynamic_children: Vec<(&'a str, &'a str)>,
298    strong_dependencies: DirectedGraph<cm_graph::DependencyNode<'a>>,
299    target_ids: IdMap<'a>,
300    errors: Vec<Error>,
301}
302
303/// [Container] provides a capability type agnostic trait to check for the existence of a
304/// capability definition of a particular type. This is useful for writing common validation
305/// functions.
306trait Container {
307    fn contains(&self, key: &str) -> bool;
308}
309
310impl<'a> Container for HashSet<&'a str> {
311    fn contains(&self, key: &str) -> bool {
312        self.contains(key)
313    }
314}
315
316impl<'a, T> Container for HashMap<&'a str, T> {
317    fn contains(&self, key: &str) -> bool {
318        self.contains_key(key)
319    }
320}
321
322impl<'a> ValidationContext<'a> {
323    fn validate(
324        mut self,
325        decl: &'a fdecl::Component,
326        dynamic_offers: &'a Vec<fdecl::Offer>,
327    ) -> Result<(), Vec<Error>> {
328        // Collect all environment names first, so that references to them can be checked.
329        if let Some(envs) = &decl.environments {
330            self.collect_environment_names(&envs);
331        }
332
333        // Validate "children" and build the set of all children.
334        if let Some(children) = decl.children.as_ref() {
335            for child in children {
336                self.validate_child_decl(&child);
337            }
338        }
339
340        // Validate "collections" and build the set of all collections.
341        if let Some(collections) = decl.collections.as_ref() {
342            for collection in collections {
343                self.validate_collection_decl(&collection);
344            }
345        }
346
347        // Validate "capabilities" and build the set of all capabilities.
348        if let Some(capabilities) = decl.capabilities.as_ref() {
349            #[cfg(fuchsia_api_level_at_least = "25")]
350            self.load_dictionary_names(capabilities.iter().filter_map(
351                |capability| match capability {
352                    fdecl::Capability::Dictionary(dictionary_decl) => Some(dictionary_decl),
353                    _ => None,
354                },
355            ));
356            self.validate_capability_decls(capabilities, false);
357        }
358
359        // Validate "uses".
360        let mut use_runner_name = None;
361        let mut use_runner_source = None;
362        if let Some(uses) = decl.uses.as_ref() {
363            (use_runner_name, use_runner_source) = self.validate_use_decls(uses);
364        }
365
366        // Validate "program".
367        if let Some(program) = decl.program.as_ref() {
368            self.validate_program(program, use_runner_name, use_runner_source);
369        }
370
371        // Validate "exposes".
372        if let Some(exposes) = decl.exposes.as_ref() {
373            let mut expose_to_parent_ids = HashMap::new();
374            let mut expose_to_framework_ids = HashMap::new();
375            for expose in exposes.iter() {
376                self.validate_expose_decl(
377                    &expose,
378                    &mut expose_to_parent_ids,
379                    &mut expose_to_framework_ids,
380                );
381            }
382            self.validate_expose_group(&exposes);
383        }
384
385        // Validate "offers".
386        if let Some(offers) = decl.offers.as_ref() {
387            for offer in offers.iter() {
388                self.validate_offer_decl(&offer, OfferType::Static);
389            }
390            self.validate_offer_group(&offers, OfferType::Static);
391        }
392
393        for dynamic_offer in dynamic_offers.iter() {
394            self.validate_offer_decl(&dynamic_offer, OfferType::Dynamic);
395        }
396        self.validate_offer_group(&dynamic_offers, OfferType::Dynamic);
397
398        // Validate "environments" after all other declarations are processed.
399        if let Some(environment) = decl.environments.as_ref() {
400            for environment in environment {
401                self.validate_environment_decl(&environment);
402            }
403        }
404
405        // Validate "config"
406        #[cfg(fuchsia_api_level_at_least = "20")]
407        self.validate_config(decl.config.as_ref(), decl.uses.as_ref());
408
409        // Check that there are no strong cyclical dependencies
410        cm_graph::generate_dependency_graph(
411            &mut self.strong_dependencies,
412            &decl,
413            &self.dynamic_children,
414            dynamic_offers,
415        );
416        if let Err(e) = self.strong_dependencies.topological_sort() {
417            self.errors.push(Error::dependency_cycle(e.format_cycle()));
418        }
419
420        if self.errors.is_empty() {
421            Ok(())
422        } else {
423            Err(self.errors)
424        }
425    }
426
427    /// 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    #[cfg(fuchsia_api_level_at_least = "20")]
441    fn validate_config(
442        &mut self,
443        config: Option<&fdecl::ConfigSchema>,
444        uses: Option<&Vec<fdecl::Use>>,
445    ) {
446        use std::collections::BTreeMap;
447
448        // Get all of the `use` configs that are optional without a default.
449        let optional_use_keys: BTreeMap<String, fdecl::ConfigType> =
450            uses.map_or(BTreeMap::new(), |u| {
451                u.iter()
452                    .map(|u| {
453                        let fdecl::Use::Config(config) = u else {
454                            return None;
455                        };
456                        if config.availability == Some(fdecl::Availability::Required)
457                            || config.availability == None
458                        {
459                            return None;
460                        }
461                        if let Some(_) = config.default.as_ref() {
462                            return None;
463                        }
464                        let Some(key) = config.target_name.clone() else {
465                            return None;
466                        };
467                        let Some(value) = config.type_.clone() else {
468                            return None;
469                        };
470                        Some((key, value))
471                    })
472                    .flatten()
473                    .collect()
474            });
475
476        // Validate default values in use configs.
477        for u in uses.iter().flat_map(|x| x.iter()) {
478            let fdecl::Use::Config(config) = u else { continue };
479            let Some(default) = config.default.as_ref() else { continue };
480            validate_value_spec(&fdecl::ConfigValueSpec {
481                value: Some(default.clone()),
482                ..Default::default()
483            })
484            .map_err(|mut e| self.errors.append(&mut e.errs))
485            .ok();
486        }
487
488        let Some(config) = config else {
489            if !optional_use_keys.is_empty() {
490                self.errors.push(Error::missing_field(DeclType::ConfigField, "config"))
491            }
492            return;
493        };
494
495        if let Some(fields) = &config.fields {
496            for field in fields {
497                if field.key.is_none() {
498                    self.errors.push(Error::missing_field(DeclType::ConfigField, "key"));
499                }
500                if let Some(type_) = &field.type_ {
501                    self.validate_config_type(type_, true);
502                } else {
503                    self.errors.push(Error::missing_field(DeclType::ConfigField, "value_type"));
504                }
505            }
506        } else {
507            self.errors.push(Error::missing_field(DeclType::ConfigSchema, "fields"));
508        }
509
510        if let Some(checksum) = &config.checksum {
511            match checksum {
512                fdecl::ConfigChecksum::Sha256(_) => {}
513                fdecl::ConfigChecksumUnknown!() => {
514                    self.errors.push(Error::invalid_field(DeclType::ConfigSchema, "checksum"));
515                }
516            }
517        } else {
518            self.errors.push(Error::missing_field(DeclType::ConfigSchema, "checksum"));
519        }
520
521        'outer: for (key, value) in optional_use_keys.iter() {
522            for field in config.fields.iter().flatten() {
523                if field.key.as_ref() == Some(key) {
524                    if field.type_.as_ref() != Some(value) {
525                        self.errors.push(Error::invalid_field(DeclType::ConfigField, key.clone()));
526                    }
527                    continue 'outer;
528                }
529            }
530            self.errors.push(Error::missing_field(DeclType::ConfigField, key.clone()));
531        }
532
533        match config.value_source {
534            None => self.errors.push(Error::missing_field(DeclType::ConfigSchema, "value_source")),
535            #[cfg(fuchsia_api_level_at_least = "HEAD")]
536            Some(fdecl::ConfigValueSource::Capabilities(_)) => {
537                if !optional_use_keys.is_empty() {
538                    self.errors
539                        .push(Error::invalid_field(DeclType::ConfigValueSource, "ValueSource"))
540                }
541            }
542            Some(fdecl::ConfigValueSource::__SourceBreaking { .. }) => {
543                self.errors.push(Error::invalid_field(DeclType::ConfigValueSource, "ValueSource"))
544            }
545            _ => (),
546        };
547    }
548
549    #[cfg(fuchsia_api_level_at_least = "20")]
550    fn validate_config_type(&mut self, type_: &fdecl::ConfigType, accept_vectors: bool) {
551        match &type_.layout {
552            fdecl::ConfigTypeLayout::Bool
553            | fdecl::ConfigTypeLayout::Uint8
554            | fdecl::ConfigTypeLayout::Uint16
555            | fdecl::ConfigTypeLayout::Uint32
556            | fdecl::ConfigTypeLayout::Uint64
557            | fdecl::ConfigTypeLayout::Int8
558            | fdecl::ConfigTypeLayout::Int16
559            | fdecl::ConfigTypeLayout::Int32
560            | fdecl::ConfigTypeLayout::Int64 => {
561                // These layouts have no parameters or constraints
562                if let Some(parameters) = &type_.parameters {
563                    if !parameters.is_empty() {
564                        self.errors
565                            .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
566                    }
567                } else {
568                    self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"));
569                }
570
571                if !type_.constraints.is_empty() {
572                    self.errors.push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
573                }
574            }
575            fdecl::ConfigTypeLayout::String => {
576                // String has exactly one constraint and no parameter
577                if let Some(parameters) = &type_.parameters {
578                    if !parameters.is_empty() {
579                        self.errors
580                            .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
581                    }
582                } else {
583                    self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"));
584                }
585
586                if type_.constraints.is_empty() {
587                    self.errors.push(Error::missing_field(DeclType::ConfigType, "constraints"));
588                } else if type_.constraints.len() > 1 {
589                    self.errors.push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
590                } else if let fdecl::LayoutConstraint::MaxSize(_) = &type_.constraints[0] {
591                } else {
592                    self.errors.push(Error::invalid_field(DeclType::ConfigType, "constraints"));
593                }
594            }
595            fdecl::ConfigTypeLayout::Vector => {
596                if accept_vectors {
597                    // Vector has exactly one constraint and one parameter
598                    if let Some(parameters) = &type_.parameters {
599                        if parameters.is_empty() {
600                            self.errors
601                                .push(Error::missing_field(DeclType::ConfigType, "parameters"));
602                        } else if parameters.len() > 1 {
603                            self.errors
604                                .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
605                        } else if let fdecl::LayoutParameter::NestedType(nested_type) =
606                            &parameters[0]
607                        {
608                            self.validate_config_type(nested_type, false);
609                        } else {
610                            self.errors
611                                .push(Error::invalid_field(DeclType::ConfigType, "parameters"));
612                        }
613                    } else {
614                        self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"))
615                    }
616
617                    if type_.constraints.is_empty() {
618                        self.errors.push(Error::missing_field(DeclType::ConfigType, "constraints"));
619                    } else if type_.constraints.len() > 1 {
620                        self.errors
621                            .push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
622                    } else if let fdecl::LayoutConstraint::MaxSize(_) = &type_.constraints[0] {
623                    } else {
624                        self.errors.push(Error::invalid_field(DeclType::ConfigType, "constraints"));
625                    }
626                } else {
627                    self.errors.push(Error::nested_vector());
628                }
629            }
630            _ => self.errors.push(Error::invalid_field(DeclType::ConfigType, "layout")),
631        }
632    }
633
634    fn validate_capability_decls(
635        &mut self,
636        capabilities: &'a [fdecl::Capability],
637        as_builtin: bool,
638    ) {
639        for capability in capabilities {
640            self.validate_capability_decl(capability, as_builtin);
641        }
642    }
643
644    /// Validates an individual capability declaration as either a built-in capability or (if
645    /// `as_builtin = false`) as a component or namespace capability.
646    // Storage capabilities are not currently allowed as built-ins, but there's no deep reason for this.
647    // Update this method to allow built-in storage capabilities as needed.
648    fn validate_capability_decl(&mut self, capability: &'a fdecl::Capability, as_builtin: bool) {
649        match capability {
650            fdecl::Capability::Service(service) => self.validate_service_decl(&service, as_builtin),
651            fdecl::Capability::Protocol(protocol) => {
652                self.validate_protocol_decl(&protocol, as_builtin)
653            }
654            fdecl::Capability::Directory(directory) => {
655                self.validate_directory_decl(&directory, as_builtin)
656            }
657            fdecl::Capability::Storage(storage) => {
658                if as_builtin {
659                    self.errors.push(Error::CapabilityCannotBeBuiltin(DeclType::Storage))
660                } else {
661                    self.validate_storage_decl(&storage)
662                }
663            }
664            fdecl::Capability::Runner(runner) => self.validate_runner_decl(&runner, as_builtin),
665            fdecl::Capability::Resolver(resolver) => {
666                self.validate_resolver_decl(&resolver, as_builtin)
667            }
668            fdecl::Capability::EventStream(event) => {
669                if as_builtin {
670                    self.validate_event_stream_decl(&event)
671                } else {
672                    self.errors.push(Error::CapabilityMustBeBuiltin(DeclType::EventStream))
673                }
674            }
675            #[cfg(fuchsia_api_level_at_least = "25")]
676            fdecl::Capability::Dictionary(dictionary) => {
677                self.validate_dictionary_decl(&dictionary);
678            }
679            #[cfg(fuchsia_api_level_at_least = "HEAD")]
680            fdecl::Capability::Config(config) => {
681                self.validate_configuration_decl(&config);
682            }
683            fdecl::CapabilityUnknown!() => self.errors.push(Error::UnknownCapability),
684        }
685    }
686
687    /// Returns the `source_name` and `source` of the runner in `uses`, if present.
688    fn validate_use_decls(
689        &mut self,
690        uses: &'a [fdecl::Use],
691    ) -> (Option<&'a String>, Option<&'a fdecl::Ref>) {
692        // Validate individual fields.
693        for use_ in uses.iter() {
694            self.validate_use_decl(&use_);
695        }
696        self.validate_use_paths(&uses);
697
698        #[cfg(fuchsia_api_level_at_least = "HEAD")]
699        {
700            let mut use_runner_name = None;
701            let mut use_runner_source = None;
702            for use_ in uses.iter() {
703                if let fdecl::Use::Runner(use_runner) = use_ {
704                    if use_runner_name.is_some() {
705                        self.errors.push(Error::MultipleRunnersUsed);
706                    }
707
708                    use_runner_name = use_runner.source_name.as_ref();
709                    use_runner_source = use_runner.source.as_ref();
710                }
711            }
712            return (use_runner_name, use_runner_source);
713        }
714        #[cfg(fuchsia_api_level_less_than = "HEAD")]
715        return (None, None);
716    }
717
718    fn validate_use_decl(&mut self, use_: &'a fdecl::Use) {
719        match use_ {
720            fdecl::Use::Service(u) => {
721                let decl = DeclType::UseService;
722                self.validate_use_fields(
723                    decl,
724                    Self::service_checker,
725                    u.source.as_ref(),
726                    u.source_name.as_ref(),
727                    get_source_dictionary!(u),
728                    u.target_path.as_ref(),
729                    u.dependency_type.as_ref(),
730                    u.availability.as_ref(),
731                );
732                if u.dependency_type.is_none() {
733                    self.errors.push(Error::missing_field(decl, "dependency_type"));
734                }
735            }
736            fdecl::Use::Protocol(u) => {
737                let decl = DeclType::UseProtocol;
738                self.validate_use_fields(
739                    decl,
740                    Self::protocol_checker,
741                    u.source.as_ref(),
742                    u.source_name.as_ref(),
743                    get_source_dictionary!(u),
744                    u.target_path.as_ref(),
745                    u.dependency_type.as_ref(),
746                    u.availability.as_ref(),
747                );
748                if u.dependency_type.is_none() {
749                    self.errors.push(Error::missing_field(decl, "dependency_type"));
750                }
751            }
752            fdecl::Use::Directory(u) => {
753                let decl = DeclType::UseDirectory;
754                self.validate_use_fields(
755                    decl,
756                    Self::directory_checker,
757                    u.source.as_ref(),
758                    u.source_name.as_ref(),
759                    get_source_dictionary!(u),
760                    u.target_path.as_ref(),
761                    u.dependency_type.as_ref(),
762                    u.availability.as_ref(),
763                );
764                if u.dependency_type.is_none() {
765                    self.errors.push(Error::missing_field(decl, "dependency_type"));
766                }
767                if u.rights.is_none() {
768                    self.errors.push(Error::missing_field(DeclType::UseDirectory, "rights"));
769                }
770                if let Some(subdir) = u.subdir.as_ref() {
771                    check_relative_path(
772                        Some(subdir),
773                        DeclType::UseDirectory,
774                        "subdir",
775                        &mut self.errors,
776                    );
777                }
778            }
779            fdecl::Use::Storage(u) => {
780                const SOURCE: Option<fdecl::Ref> = Some(fdecl::Ref::Parent(fdecl::ParentRef {}));
781                const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
782                    Some(fdecl::DependencyType::Strong);
783                self.validate_use_fields(
784                    DeclType::UseStorage,
785                    Self::storage_checker,
786                    SOURCE.as_ref(),
787                    u.source_name.as_ref(),
788                    None,
789                    u.target_path.as_ref(),
790                    DEPENDENCY_TYPE.as_ref(),
791                    u.availability.as_ref(),
792                );
793            }
794            fdecl::Use::EventStream(u) => {
795                const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
796                    Some(fdecl::DependencyType::Strong);
797                let decl = DeclType::UseEventStream;
798                self.validate_use_fields(
799                    decl,
800                    Self::event_stream_checker,
801                    u.source.as_ref(),
802                    u.source_name.as_ref(),
803                    None,
804                    u.target_path.as_ref(),
805                    DEPENDENCY_TYPE.as_ref(),
806                    u.availability.as_ref(),
807                );
808                // Additional validation.
809                match u.source {
810                    Some(fdecl::Ref::Child(_)) | Some(fdecl::Ref::Parent(_)) => {
811                        // Allowed.
812                    }
813                    Some(fdecl::Ref::Framework(_))
814                    | Some(fdecl::Ref::Self_(_))
815                    | Some(fdecl::Ref::Debug(_)) => {
816                        // Allowed in general but not for event streams, add an error.
817                        self.errors.push(Error::invalid_field(decl, "source"));
818                    }
819                    Some(fdecl::Ref::Collection(_)) | Some(fdecl::RefUnknown!()) | None => {
820                        // Already handled by validate_use_fields.
821                    }
822                }
823                if let Some(scope) = &u.scope {
824                    for reference in scope {
825                        if !matches!(reference, fdecl::Ref::Child(_) | fdecl::Ref::Collection(_)) {
826                            self.errors.push(Error::invalid_field(decl, "scope"));
827                        }
828                    }
829                }
830            }
831            #[cfg(fuchsia_api_level_at_least = "HEAD")]
832            fdecl::Use::Runner(u) => {
833                const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
834                    Some(fdecl::DependencyType::Strong);
835                const AVAILABILITY: Option<fdecl::Availability> =
836                    Some(fdecl::Availability::Required);
837                let decl = DeclType::UseRunner;
838                self.validate_use_fields(
839                    decl,
840                    Self::runner_checker,
841                    u.source.as_ref(),
842                    u.source_name.as_ref(),
843                    get_source_dictionary!(u),
844                    None,
845                    DEPENDENCY_TYPE.as_ref(),
846                    AVAILABILITY.as_ref(),
847                );
848            }
849            #[cfg(fuchsia_api_level_at_least = "HEAD")]
850            fdecl::Use::Config(u) => {
851                const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
852                    Some(fdecl::DependencyType::Strong);
853                let decl = DeclType::UseConfiguration;
854                self.validate_use_fields(
855                    decl,
856                    Self::config_checker,
857                    u.source.as_ref(),
858                    u.source_name.as_ref(),
859                    None,
860                    None,
861                    DEPENDENCY_TYPE.as_ref(),
862                    u.availability.as_ref(),
863                );
864            }
865            fdecl::UseUnknown!() => {
866                self.errors.push(Error::invalid_field(DeclType::Component, "use"));
867            }
868        }
869    }
870
871    /// Validates the "program" declaration. This does not check runner-specific properties
872    /// since those are checked by the runner.
873    fn validate_program(
874        &mut self,
875        program: &fdecl::Program,
876        use_runner_name: Option<&String>,
877        _use_runner_source: Option<&fdecl::Ref>,
878    ) {
879        match &program.runner {
880            Some(_) =>
881            {
882                #[cfg(fuchsia_api_level_at_least = "HEAD")]
883                if use_runner_name.is_some() {
884                    if use_runner_name != program.runner.as_ref()
885                        || _use_runner_source
886                            != Some(&fdecl::Ref::Environment(fdecl::EnvironmentRef))
887                    {
888                        self.errors.push(Error::ConflictingRunners);
889                    }
890                }
891            }
892            None => {
893                if use_runner_name.is_none() {
894                    self.errors.push(Error::MissingRunner);
895                }
896            }
897        }
898
899        if program.info.is_none() {
900            self.errors.push(Error::missing_field(DeclType::Program, "info"));
901        }
902    }
903
904    /// Validates that paths-based capabilities (service, directory, protocol)
905    /// are different, are not prefixes of each other, and do not collide "/pkg".
906    fn validate_use_paths(&mut self, uses: &[fdecl::Use]) {
907        #[derive(Debug, PartialEq, Clone, Copy)]
908        struct PathCapability<'a> {
909            decl: DeclType,
910            dir: &'a Path,
911            use_: &'a fdecl::Use,
912        }
913        let mut used_paths = HashMap::new();
914        for use_ in uses.iter() {
915            match use_ {
916                fdecl::Use::Service(fdecl::UseService { target_path: Some(path), .. })
917                | fdecl::Use::Protocol(fdecl::UseProtocol { target_path: Some(path), .. })
918                | fdecl::Use::Directory(fdecl::UseDirectory { target_path: Some(path), .. })
919                | fdecl::Use::Storage(fdecl::UseStorage { target_path: Some(path), .. }) => {
920                    let capability = match use_ {
921                        fdecl::Use::Service(_) => {
922                            let dir = match Path::new(path).parent() {
923                                Some(p) => p,
924                                None => continue, // Invalid path, validated elsewhere
925                            };
926                            PathCapability { decl: DeclType::UseService, dir, use_ }
927                        }
928                        fdecl::Use::Protocol(_) => {
929                            let dir = match Path::new(path).parent() {
930                                Some(p) => p,
931                                None => continue, // Invalid path, validated elsewhere
932                            };
933                            PathCapability { decl: DeclType::UseProtocol, dir, use_ }
934                        }
935                        fdecl::Use::Directory(_) => PathCapability {
936                            decl: DeclType::UseDirectory,
937                            dir: Path::new(path),
938                            use_,
939                        },
940                        fdecl::Use::Storage(_) => PathCapability {
941                            decl: DeclType::UseStorage,
942                            dir: Path::new(path),
943                            use_,
944                        },
945                        _ => unreachable!(),
946                    };
947                    if used_paths.insert(path, capability).is_some() {
948                        // Disallow multiple capabilities for the same path.
949                        self.errors.push(Error::duplicate_field(
950                            capability.decl,
951                            "target_path",
952                            path,
953                        ));
954                    }
955                }
956                _ => {}
957            }
958        }
959        for ((&path_a, capability_a), (&path_b, capability_b)) in
960            used_paths.iter().tuple_combinations()
961        {
962            if match (capability_a.use_, capability_b.use_) {
963                // Directories and storage can't be the same or partially overlap.
964                (fdecl::Use::Directory(_), fdecl::Use::Directory(_))
965                | (fdecl::Use::Storage(_), fdecl::Use::Directory(_))
966                | (fdecl::Use::Directory(_), fdecl::Use::Storage(_))
967                | (fdecl::Use::Storage(_), fdecl::Use::Storage(_)) => {
968                    capability_b.dir == capability_a.dir
969                        || capability_b.dir.starts_with(capability_a.dir)
970                        || capability_a.dir.starts_with(capability_b.dir)
971                }
972
973                // Protocols and Services can't overlap with Directories.
974                (_, fdecl::Use::Directory(_)) | (fdecl::Use::Directory(_), _) => {
975                    capability_b.dir == capability_a.dir
976                        || capability_b.dir.starts_with(capability_a.dir)
977                        || capability_a.dir.starts_with(capability_b.dir)
978                }
979
980                // Protocols and Services containing directories may be same, but
981                // partial overlap is disallowed.
982                (_, _) => {
983                    capability_b.dir != capability_a.dir
984                        && (capability_b.dir.starts_with(capability_a.dir)
985                            || capability_a.dir.starts_with(capability_b.dir))
986                }
987            } {
988                self.errors.push(Error::invalid_path_overlap(
989                    capability_a.decl,
990                    path_a,
991                    capability_b.decl,
992                    path_b,
993                ));
994            }
995        }
996        for (used_path, capability) in used_paths.iter() {
997            if used_path.as_str() == "/pkg" || used_path.starts_with("/pkg/") {
998                self.errors.push(Error::pkg_path_overlap(capability.decl, *used_path));
999            }
1000        }
1001    }
1002
1003    fn validate_use_fields(
1004        &mut self,
1005        decl: DeclType,
1006        // This takes a callback that returns a [Container], instead of the &[Container] directly,
1007        // to avoid a borrow checker error that would occur from a simultaneous borrow on
1008        // &mut self.
1009        capability_checker: impl Fn(&Self) -> &dyn Container,
1010        source: Option<&'a fdecl::Ref>,
1011        source_name: Option<&'a String>,
1012        source_dictionary: Option<&'a String>,
1013        target_path: Option<&'a String>,
1014        dependency_type: Option<&fdecl::DependencyType>,
1015        availability: Option<&'a fdecl::Availability>,
1016    ) {
1017        self.validate_use_source(decl, source, source_dictionary);
1018
1019        check_name(source_name, decl, "source_name", &mut self.errors);
1020        if source_dictionary.is_some() {
1021            check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
1022        }
1023        if decl != DeclType::UseRunner && decl != DeclType::UseConfiguration {
1024            check_path(target_path, decl, "target_path", &mut self.errors);
1025        }
1026        check_use_availability(decl, availability, &mut self.errors);
1027
1028        // Only allow `weak` dependency with `use from child`.
1029        let is_use_from_child = match source {
1030            Some(fdecl::Ref::Child(_)) => true,
1031            _ => false,
1032        };
1033        match (is_use_from_child, dependency_type) {
1034            (false, Some(fdecl::DependencyType::Weak)) => {
1035                self.errors.push(Error::invalid_field(decl, "dependency_type"));
1036            }
1037            _ => {}
1038        }
1039
1040        self.validate_route_from_self(
1041            decl,
1042            source,
1043            source_name,
1044            source_dictionary,
1045            capability_checker,
1046        );
1047    }
1048
1049    fn validate_use_source(
1050        &mut self,
1051        decl: DeclType,
1052        source: Option<&'a fdecl::Ref>,
1053        source_dictionary: Option<&'a String>,
1054    ) {
1055        match (source, source_dictionary) {
1056            // These sources support source_dictionary.
1057            (Some(fdecl::Ref::Parent(_)), _) => {}
1058            (Some(fdecl::Ref::Self_(_)), _) => {}
1059            (Some(fdecl::Ref::Child(child)), _) => {
1060                self.validate_child_ref(decl, "source", &child, OfferType::Static);
1061                return;
1062            }
1063            // These sources don't.
1064            (Some(fdecl::Ref::Framework(_)), None) => {}
1065            (Some(fdecl::Ref::Debug(_)), None) => {}
1066            (Some(fdecl::Ref::Capability(c)), None) => {
1067                self.validate_source_capability(&c, decl, "source");
1068                return;
1069            }
1070            #[cfg(fuchsia_api_level_at_least = "HEAD")]
1071            (Some(fdecl::Ref::Environment(_)), None) => {}
1072            (Some(fdecl::Ref::Collection(collection)), None) if decl == DeclType::UseService => {
1073                self.validate_collection_ref(decl, "source", &collection);
1074                return;
1075            }
1076            // `source` is required.
1077            (None, _) => self.errors.push(Error::missing_field(decl, "source")),
1078            // Any combination that was not recognized above must be invalid.
1079            (_, _) => self.errors.push(Error::invalid_field(decl, "source")),
1080        }
1081    }
1082
1083    fn validate_child_decl(&mut self, child: &'a fdecl::Child) {
1084        if let Err(mut e) = validate_child(child, check_name) {
1085            self.errors.append(&mut e.errs);
1086        }
1087        if let Some(name) = child.name.as_ref() {
1088            let name: &str = name;
1089            if self.all_children.insert(name, child).is_some() {
1090                self.errors.push(Error::duplicate_field(DeclType::Child, "name", name));
1091            }
1092        }
1093        if let Some(environment) = child.environment.as_ref() {
1094            if !self.all_environment_names.contains(environment.as_str()) {
1095                self.errors.push(Error::invalid_environment(
1096                    DeclType::Child,
1097                    "environment",
1098                    environment,
1099                ));
1100            }
1101        }
1102    }
1103
1104    fn validate_collection_decl(&mut self, collection: &'a fdecl::Collection) {
1105        let name = collection.name.as_ref();
1106        if check_name(name, DeclType::Collection, "name", &mut self.errors) {
1107            let name: &str = name.unwrap();
1108            if !self.all_collections.insert(name) {
1109                self.errors.push(Error::duplicate_field(DeclType::Collection, "name", name));
1110            }
1111        }
1112        if collection.durability.is_none() {
1113            self.errors.push(Error::missing_field(DeclType::Collection, "durability"));
1114        }
1115        if let Some(environment) = collection.environment.as_ref() {
1116            if !self.all_environment_names.contains(environment.as_str()) {
1117                self.errors.push(Error::invalid_environment(
1118                    DeclType::Collection,
1119                    "environment",
1120                    environment,
1121                ));
1122            }
1123        }
1124        // Allow `allowed_offers` & `allow_long_names` to be unset/unvalidated, for backwards compatibility.
1125    }
1126
1127    fn validate_environment_decl(&mut self, environment: &'a fdecl::Environment) {
1128        let name = environment.name.as_ref();
1129        check_name(name, DeclType::Environment, "name", &mut self.errors);
1130        if environment.extends.is_none() {
1131            self.errors.push(Error::missing_field(DeclType::Environment, "extends"));
1132        }
1133        if let Some(runners) = environment.runners.as_ref() {
1134            let mut registered_runners = HashSet::new();
1135            for runner in runners {
1136                self.validate_runner_registration(runner, &mut registered_runners);
1137            }
1138        }
1139        if let Some(resolvers) = environment.resolvers.as_ref() {
1140            let mut registered_schemes = HashSet::new();
1141            for resolver in resolvers {
1142                self.validate_resolver_registration(resolver, &mut registered_schemes);
1143            }
1144        }
1145
1146        match environment.extends.as_ref() {
1147            Some(fdecl::EnvironmentExtends::None) => {
1148                if environment.stop_timeout_ms.is_none() {
1149                    self.errors
1150                        .push(Error::missing_field(DeclType::Environment, "stop_timeout_ms"));
1151                }
1152            }
1153            None | Some(fdecl::EnvironmentExtends::Realm) => {}
1154        }
1155
1156        if let Some(debugs) = environment.debug_capabilities.as_ref() {
1157            for debug in debugs {
1158                self.validate_environment_debug_registration(debug);
1159            }
1160        }
1161    }
1162
1163    fn validate_runner_registration(
1164        &mut self,
1165        runner_registration: &'a fdecl::RunnerRegistration,
1166        runner_names: &mut HashSet<&'a str>,
1167    ) {
1168        check_name(
1169            runner_registration.source_name.as_ref(),
1170            DeclType::RunnerRegistration,
1171            "source_name",
1172            &mut self.errors,
1173        );
1174        self.validate_registration_source(
1175            runner_registration.source.as_ref(),
1176            DeclType::RunnerRegistration,
1177        );
1178        // If the source is `self`, ensure we have a corresponding Runner.
1179        if let (Some(fdecl::Ref::Self_(_)), Some(ref name)) =
1180            (&runner_registration.source, &runner_registration.source_name)
1181        {
1182            if !self.all_runners.contains(name as &str) {
1183                self.errors.push(Error::invalid_runner(
1184                    DeclType::RunnerRegistration,
1185                    "source_name",
1186                    name,
1187                ));
1188            }
1189        }
1190
1191        check_name(
1192            runner_registration.target_name.as_ref(),
1193            DeclType::RunnerRegistration,
1194            "target_name",
1195            &mut self.errors,
1196        );
1197        if let Some(name) = runner_registration.target_name.as_ref() {
1198            if !runner_names.insert(name.as_str()) {
1199                self.errors.push(Error::duplicate_field(
1200                    DeclType::RunnerRegistration,
1201                    "target_name",
1202                    name,
1203                ));
1204            }
1205        }
1206    }
1207
1208    fn validate_resolver_registration(
1209        &mut self,
1210        resolver_registration: &'a fdecl::ResolverRegistration,
1211        schemes: &mut HashSet<&'a str>,
1212    ) {
1213        check_name(
1214            resolver_registration.resolver.as_ref(),
1215            DeclType::ResolverRegistration,
1216            "resolver",
1217            &mut self.errors,
1218        );
1219        self.validate_registration_source(
1220            resolver_registration.source.as_ref(),
1221            DeclType::ResolverRegistration,
1222        );
1223        check_url_scheme(
1224            resolver_registration.scheme.as_ref(),
1225            DeclType::ResolverRegistration,
1226            "scheme",
1227            &mut self.errors,
1228        );
1229        if let Some(scheme) = resolver_registration.scheme.as_ref() {
1230            if !schemes.insert(scheme.as_str()) {
1231                self.errors.push(Error::duplicate_field(
1232                    DeclType::ResolverRegistration,
1233                    "scheme",
1234                    scheme,
1235                ));
1236            }
1237        }
1238    }
1239
1240    fn validate_registration_source(&mut self, source: Option<&'a fdecl::Ref>, ty: DeclType) {
1241        match source {
1242            Some(fdecl::Ref::Parent(_)) => {}
1243            Some(fdecl::Ref::Self_(_)) => {}
1244            Some(fdecl::Ref::Child(child_ref)) => {
1245                // Make sure the child is valid.
1246                self.validate_child_ref(ty, "source", &child_ref, OfferType::Static);
1247            }
1248            Some(_) => {
1249                self.errors.push(Error::invalid_field(ty, "source"));
1250            }
1251            None => {
1252                self.errors.push(Error::missing_field(ty, "source"));
1253            }
1254        }
1255    }
1256
1257    fn validate_service_decl(&mut self, service: &'a fdecl::Service, as_builtin: bool) {
1258        if check_name(service.name.as_ref(), DeclType::Service, "name", &mut self.errors) {
1259            let name = service.name.as_ref().unwrap();
1260            if !self.all_capability_ids.insert(name) {
1261                self.errors.push(Error::duplicate_field(DeclType::Service, "name", name.as_str()));
1262            }
1263            self.all_services.insert(name);
1264        }
1265        match as_builtin {
1266            true => {
1267                if let Some(path) = service.source_path.as_ref() {
1268                    self.errors.push(Error::extraneous_source_path(DeclType::Service, path))
1269                }
1270            }
1271            false => {
1272                check_path(
1273                    service.source_path.as_ref(),
1274                    DeclType::Service,
1275                    "source_path",
1276                    &mut self.errors,
1277                );
1278            }
1279        }
1280    }
1281
1282    fn validate_protocol_decl(&mut self, protocol: &'a fdecl::Protocol, as_builtin: bool) {
1283        if check_name(protocol.name.as_ref(), DeclType::Protocol, "name", &mut self.errors) {
1284            let name = protocol.name.as_ref().unwrap();
1285            if !self.all_capability_ids.insert(name) {
1286                self.errors.push(Error::duplicate_field(DeclType::Protocol, "name", name.as_str()));
1287            }
1288            self.all_protocols.insert(name);
1289        }
1290        match as_builtin {
1291            true => {
1292                if let Some(path) = protocol.source_path.as_ref() {
1293                    self.errors.push(Error::extraneous_source_path(DeclType::Protocol, path))
1294                }
1295            }
1296            false => {
1297                check_path(
1298                    protocol.source_path.as_ref(),
1299                    DeclType::Protocol,
1300                    "source_path",
1301                    &mut self.errors,
1302                );
1303            }
1304        }
1305
1306        #[cfg(fuchsia_api_level_at_least = "HEAD")]
1307        match protocol.delivery {
1308            Some(delivery) => match cm_types::DeliveryType::try_from(delivery) {
1309                Ok(_) => {}
1310                Err(_) => self.errors.push(Error::invalid_field(DeclType::Protocol, "delivery")),
1311            },
1312            None => {}
1313        }
1314    }
1315
1316    fn validate_directory_decl(&mut self, directory: &'a fdecl::Directory, as_builtin: bool) {
1317        if check_name(directory.name.as_ref(), DeclType::Directory, "name", &mut self.errors) {
1318            let name = directory.name.as_ref().unwrap();
1319            if !self.all_capability_ids.insert(name) {
1320                self.errors.push(Error::duplicate_field(
1321                    DeclType::Directory,
1322                    "name",
1323                    name.as_str(),
1324                ));
1325            }
1326            self.all_directories.insert(name);
1327        }
1328        match as_builtin {
1329            true => {
1330                if let Some(path) = directory.source_path.as_ref() {
1331                    self.errors.push(Error::extraneous_source_path(DeclType::Directory, path))
1332                }
1333            }
1334            false => {
1335                check_path(
1336                    directory.source_path.as_ref(),
1337                    DeclType::Directory,
1338                    "source_path",
1339                    &mut self.errors,
1340                );
1341            }
1342        }
1343        if directory.rights.is_none() {
1344            self.errors.push(Error::missing_field(DeclType::Directory, "rights"));
1345        }
1346    }
1347
1348    fn validate_storage_decl(&mut self, storage: &'a fdecl::Storage) {
1349        match storage.source.as_ref() {
1350            Some(fdecl::Ref::Parent(_)) => {}
1351            Some(fdecl::Ref::Self_(_)) => {}
1352            Some(fdecl::Ref::Child(child)) => {
1353                let _ =
1354                    self.validate_child_ref(DeclType::Storage, "source", &child, OfferType::Static);
1355            }
1356            Some(_) => {
1357                self.errors.push(Error::invalid_field(DeclType::Storage, "source"));
1358            }
1359            None => {
1360                self.errors.push(Error::missing_field(DeclType::Storage, "source"));
1361            }
1362        };
1363        if check_name(storage.name.as_ref(), DeclType::Storage, "name", &mut self.errors) {
1364            let name = storage.name.as_ref().unwrap();
1365            if !self.all_capability_ids.insert(name) {
1366                self.errors.push(Error::duplicate_field(DeclType::Storage, "name", name.as_str()));
1367            }
1368            self.all_storages.insert(name, storage.source.as_ref());
1369        }
1370        if storage.storage_id.is_none() {
1371            self.errors.push(Error::missing_field(DeclType::Storage, "storage_id"));
1372        }
1373        check_name(
1374            storage.backing_dir.as_ref(),
1375            DeclType::Storage,
1376            "backing_dir",
1377            &mut self.errors,
1378        );
1379    }
1380
1381    fn validate_runner_decl(&mut self, runner: &'a fdecl::Runner, as_builtin: bool) {
1382        if check_name(runner.name.as_ref(), DeclType::Runner, "name", &mut self.errors) {
1383            let name = runner.name.as_ref().unwrap();
1384            if !self.all_capability_ids.insert(name) {
1385                self.errors.push(Error::duplicate_field(DeclType::Runner, "name", name.as_str()));
1386            }
1387            self.all_runners.insert(name);
1388        }
1389        match as_builtin {
1390            true => {
1391                if let Some(path) = runner.source_path.as_ref() {
1392                    self.errors.push(Error::extraneous_source_path(DeclType::Runner, path))
1393                }
1394            }
1395            false => {
1396                check_path(
1397                    runner.source_path.as_ref(),
1398                    DeclType::Runner,
1399                    "source_path",
1400                    &mut self.errors,
1401                );
1402            }
1403        }
1404    }
1405
1406    fn validate_resolver_decl(&mut self, resolver: &'a fdecl::Resolver, as_builtin: bool) {
1407        if check_name(resolver.name.as_ref(), DeclType::Resolver, "name", &mut self.errors) {
1408            let name = resolver.name.as_ref().unwrap();
1409            if !self.all_capability_ids.insert(name) {
1410                self.errors.push(Error::duplicate_field(DeclType::Resolver, "name", name.as_str()));
1411            }
1412            self.all_resolvers.insert(name);
1413        }
1414        match as_builtin {
1415            true => {
1416                if let Some(path) = resolver.source_path.as_ref() {
1417                    self.errors.push(Error::extraneous_source_path(DeclType::Resolver, path))
1418                }
1419            }
1420            false => {
1421                check_path(
1422                    resolver.source_path.as_ref(),
1423                    DeclType::Resolver,
1424                    "source_path",
1425                    &mut self.errors,
1426                );
1427            }
1428        }
1429    }
1430
1431    // Dictionaries can reference other dictionaries in the same manifest, so before processing any
1432    // dictionary declarations this function should be called to do a first pass to pre-populate
1433    // the dictionary map.
1434    #[cfg(fuchsia_api_level_at_least = "25")]
1435    fn load_dictionary_names(&mut self, dictionaries: impl Iterator<Item = &'a fdecl::Dictionary>) {
1436        for dictionary in dictionaries {
1437            let decl = DeclType::Dictionary;
1438            if check_name(dictionary.name.as_ref(), decl, "name", &mut self.errors) {
1439                let name = dictionary.name.as_ref().unwrap();
1440                if !self.all_capability_ids.insert(name) {
1441                    self.errors.push(Error::duplicate_field(decl, "name", name.as_str()));
1442                }
1443                self.all_dictionaries.insert(name, &dictionary);
1444            }
1445        }
1446    }
1447
1448    #[cfg(fuchsia_api_level_at_least = "25")]
1449    fn validate_dictionary_decl(&mut self, dictionary: &'a fdecl::Dictionary) {
1450        let decl = DeclType::Dictionary;
1451        if let Some(path) = dictionary.source_path.as_ref() {
1452            if dictionary.source.is_some() {
1453                self.errors.push(Error::extraneous_field(decl, "source"));
1454            }
1455            check_path(Some(path), DeclType::Dictionary, "source_path", &mut self.errors);
1456        }
1457    }
1458
1459    #[cfg(fuchsia_api_level_at_least = "HEAD")]
1460    fn validate_configuration_decl(&mut self, config: &'a fdecl::Configuration) {
1461        let decl = DeclType::Configuration;
1462        if check_name(config.name.as_ref(), decl, "name", &mut self.errors) {
1463            let name = config.name.as_ref().unwrap();
1464            if !self.all_capability_ids.insert(name) {
1465                self.errors.push(Error::duplicate_field(decl, "name", name.as_str()));
1466            }
1467            self.all_configs.insert(name);
1468        }
1469    }
1470
1471    fn validate_environment_debug_registration(&mut self, debug: &'a fdecl::DebugRegistration) {
1472        match debug {
1473            fdecl::DebugRegistration::Protocol(o) => {
1474                let decl = DeclType::DebugProtocolRegistration;
1475                self.validate_environment_debug_fields(
1476                    decl,
1477                    o.source.as_ref(),
1478                    o.source_name.as_ref(),
1479                    o.target_name.as_ref(),
1480                );
1481
1482                if let (Some(fdecl::Ref::Self_(_)), Some(ref name)) = (&o.source, &o.source_name) {
1483                    if !self.all_protocols.contains(&name as &str) {
1484                        self.errors.push(Error::invalid_field(decl, "source"));
1485                    }
1486                }
1487            }
1488            _ => {
1489                self.errors.push(Error::invalid_field(DeclType::Environment, "debug"));
1490            }
1491        }
1492    }
1493
1494    fn validate_environment_debug_fields(
1495        &mut self,
1496        decl: DeclType,
1497        source: Option<&fdecl::Ref>,
1498        source_name: Option<&String>,
1499        target_name: Option<&'a String>,
1500    ) {
1501        // We don't support "source" from "capability" for now.
1502        match source {
1503            Some(fdecl::Ref::Parent(_)) => {}
1504            Some(fdecl::Ref::Self_(_)) => {}
1505            Some(fdecl::Ref::Framework(_)) => {}
1506            Some(fdecl::Ref::Child(child)) => {
1507                let _ = self.validate_child_ref(decl, "source", &child, OfferType::Static);
1508            }
1509            Some(_) => self.errors.push(Error::invalid_field(decl, "source")),
1510            None => self.errors.push(Error::missing_field(decl, "source")),
1511        }
1512        check_name(source_name, decl, "source_name", &mut self.errors);
1513        check_name(target_name, decl, "target_name", &mut self.errors);
1514    }
1515
1516    fn validate_event_stream_decl(&mut self, event: &'a fdecl::EventStream) {
1517        if check_name(event.name.as_ref(), DeclType::EventStream, "name", &mut self.errors) {
1518            let name = event.name.as_ref().unwrap();
1519            if !self.all_capability_ids.insert(name) {
1520                self.errors.push(Error::duplicate_field(
1521                    DeclType::EventStream,
1522                    "name",
1523                    name.as_str(),
1524                ));
1525            }
1526        }
1527    }
1528
1529    fn validate_source_collection(
1530        &mut self,
1531        collection: &fdecl::CollectionRef,
1532        decl_type: DeclType,
1533    ) -> bool {
1534        let num_errors = self.errors.len();
1535        if check_name(Some(&collection.name), decl_type, "source.collection.name", &mut self.errors)
1536            && !self.all_collections.contains(&collection.name as &str)
1537        {
1538            self.errors.push(Error::invalid_collection(
1539                decl_type,
1540                "source",
1541                &collection.name as &str,
1542            ));
1543        }
1544        num_errors == self.errors.len()
1545    }
1546
1547    fn validate_filtered_service_fields(
1548        &mut self,
1549        decl_type: DeclType,
1550        source_instance_filter: Option<&Vec<String>>,
1551        renamed_instances: Option<&Vec<fdecl::NameMapping>>,
1552    ) {
1553        if let Some(source_instance_filter) = source_instance_filter {
1554            if source_instance_filter.is_empty() {
1555                // if the  source_instance_filter is empty the offered service will have 0 instances,
1556                // which means the offer shouldn't have been created at all.
1557                self.errors.push(Error::invalid_field(decl_type, "source_instance_filter"));
1558            }
1559            for name in source_instance_filter {
1560                check_name(Some(name), decl_type, "source_instance_filter", &mut self.errors);
1561            }
1562        }
1563        if let Some(renamed_instances) = renamed_instances {
1564            // Multiple sources shouldn't map to the same target name
1565            let mut seen_target_names = HashSet::<String>::new();
1566            for mapping in renamed_instances {
1567                check_name(
1568                    Some(&mapping.source_name),
1569                    decl_type,
1570                    "renamed_instances.source_name",
1571                    &mut self.errors,
1572                );
1573                check_name(
1574                    Some(&mapping.target_name),
1575                    decl_type,
1576                    "renamed_instances.target_name",
1577                    &mut self.errors,
1578                );
1579                if !seen_target_names.insert(mapping.target_name.clone()) {
1580                    self.errors.push(Error::invalid_field(decl_type, "renamed_instances"));
1581                    break;
1582                }
1583            }
1584        }
1585    }
1586
1587    fn validate_source_capability(
1588        &mut self,
1589        capability: &fdecl::CapabilityRef,
1590        decl_type: DeclType,
1591        field: &str,
1592    ) -> bool {
1593        let num_errors = self.errors.len();
1594        if check_name(Some(&capability.name), decl_type, "source.capability.name", &mut self.errors)
1595            && !self.all_capability_ids.contains(capability.name.as_str())
1596        {
1597            self.errors.push(Error::invalid_capability(decl_type, field, &capability.name));
1598        }
1599        num_errors == self.errors.len()
1600    }
1601
1602    /// Return a key that can be used in `HashMap` to group aggregate declarations.
1603    ///
1604    /// Returns `None` if the input resembles an invalid declaration.
1605    fn make_group_key(
1606        target_name: Option<&'a String>,
1607        target: Option<&'a fdecl::Ref>,
1608    ) -> Option<(&'a str, RefKey<'a>)> {
1609        if target_name.is_none() {
1610            return None;
1611        }
1612        let target_name = target_name.unwrap().as_str();
1613        if target.is_none() {
1614            return None;
1615        }
1616        let target = match target.unwrap() {
1617            fdecl::Ref::Parent(_) => RefKey::Parent,
1618            fdecl::Ref::Self_(_) => RefKey::Self_,
1619            fdecl::Ref::Child(r) => RefKey::Child(r.name.as_str()),
1620            fdecl::Ref::Collection(r) => RefKey::Collection(r.name.as_str()),
1621            fdecl::Ref::Framework(_) => RefKey::Framework,
1622            fdecl::Ref::Capability(_) => RefKey::Capability,
1623            fdecl::Ref::Debug(_) => RefKey::Debug,
1624            fdecl::RefUnknown!() => {
1625                return None;
1626            }
1627        };
1628        Some((target_name, target))
1629    }
1630
1631    fn validate_aggregation_has_same_availability(
1632        &mut self,
1633        route_group: &Vec<impl HasAvailability>,
1634    ) {
1635        // Use `BtreeSet` for stable ordering of items in error message.
1636        let availability_of_sources: BTreeSet<_> =
1637            route_group.iter().map(|r| r.availability()).collect();
1638
1639        // All sources that feed into an aggregation operation should have the same availability.
1640        if availability_of_sources.len() > 1 {
1641            self.errors.push(Error::different_availability_in_aggregation(
1642                availability_of_sources.into_iter().collect(),
1643            ));
1644        }
1645    }
1646
1647    // Checks a group of expose decls to confirm that any duplicate exposes are
1648    // valid aggregate expose declarations.
1649    fn validate_expose_group(&mut self, exposes: &'a Vec<fdecl::Expose>) {
1650        let mut expose_groups: HashMap<_, Vec<fdecl::ExposeService>> = HashMap::new();
1651        let service_exposes = exposes.into_iter().filter_map(|o| {
1652            if let fdecl::Expose::Service(s) = o {
1653                Some(s)
1654            } else {
1655                None
1656            }
1657        });
1658        for expose in service_exposes {
1659            let key = Self::make_group_key(expose.target_name.as_ref(), expose.target.as_ref());
1660            if let Some(key) = key {
1661                expose_groups.entry(key).or_insert_with(|| vec![]).push(expose.clone());
1662            }
1663        }
1664        for expose_group in expose_groups.into_values() {
1665            if expose_group.len() == 1 {
1666                // If there are not multiple exposes for a (target_name, target) pair then there are
1667                // no aggregation conditions to check.
1668                continue;
1669            }
1670
1671            self.validate_aggregation_has_same_availability(&expose_group);
1672        }
1673    }
1674
1675    fn validate_expose_decl(
1676        &mut self,
1677        expose: &'a fdecl::Expose,
1678        expose_to_parent_ids: &mut HashMap<&'a str, AllowableIds>,
1679        expose_to_framework_ids: &mut HashMap<&'a str, AllowableIds>,
1680    ) {
1681        match expose {
1682            fdecl::Expose::Service(e) => {
1683                let decl = DeclType::ExposeService;
1684                self.validate_expose_fields(
1685                    decl,
1686                    AllowableIds::Many,
1687                    CollectionSource::Allow,
1688                    Self::service_checker,
1689                    e.source.as_ref(),
1690                    e.source_name.as_ref(),
1691                    get_source_dictionary!(e),
1692                    e.target.as_ref(),
1693                    e.target_name.as_ref(),
1694                    e.availability.as_ref(),
1695                    expose_to_parent_ids,
1696                    expose_to_framework_ids,
1697                );
1698            }
1699            fdecl::Expose::Protocol(e) => {
1700                let decl = DeclType::ExposeProtocol;
1701                self.validate_expose_fields(
1702                    decl,
1703                    AllowableIds::One,
1704                    CollectionSource::Deny,
1705                    Self::protocol_checker,
1706                    e.source.as_ref(),
1707                    e.source_name.as_ref(),
1708                    get_source_dictionary!(e),
1709                    e.target.as_ref(),
1710                    e.target_name.as_ref(),
1711                    e.availability.as_ref(),
1712                    expose_to_parent_ids,
1713                    expose_to_framework_ids,
1714                );
1715            }
1716            fdecl::Expose::Directory(e) => {
1717                let decl = DeclType::ExposeDirectory;
1718                self.validate_expose_fields(
1719                    decl,
1720                    AllowableIds::One,
1721                    CollectionSource::Deny,
1722                    Self::directory_checker,
1723                    e.source.as_ref(),
1724                    e.source_name.as_ref(),
1725                    get_source_dictionary!(e),
1726                    e.target.as_ref(),
1727                    e.target_name.as_ref(),
1728                    e.availability.as_ref(),
1729                    expose_to_parent_ids,
1730                    expose_to_framework_ids,
1731                );
1732
1733                // Subdir makes sense when routing, but when exposing to framework the subdirectory
1734                // can be exposed directly.
1735                match e.target.as_ref() {
1736                    Some(fdecl::Ref::Framework(_)) => {
1737                        if e.subdir.is_some() {
1738                            self.errors.push(Error::invalid_field(decl, "subdir"));
1739                        }
1740                    }
1741                    _ => {}
1742                }
1743
1744                if let Some(subdir) = e.subdir.as_ref() {
1745                    check_relative_path(Some(subdir), decl, "subdir", &mut self.errors);
1746                }
1747            }
1748            fdecl::Expose::Runner(e) => {
1749                let decl = DeclType::ExposeRunner;
1750                self.validate_expose_fields(
1751                    decl,
1752                    AllowableIds::One,
1753                    CollectionSource::Deny,
1754                    Self::runner_checker,
1755                    e.source.as_ref(),
1756                    e.source_name.as_ref(),
1757                    get_source_dictionary!(e),
1758                    e.target.as_ref(),
1759                    e.target_name.as_ref(),
1760                    Some(&fdecl::Availability::Required),
1761                    expose_to_parent_ids,
1762                    expose_to_framework_ids,
1763                );
1764            }
1765            fdecl::Expose::Resolver(e) => {
1766                let decl = DeclType::ExposeResolver;
1767                self.validate_expose_fields(
1768                    decl,
1769                    AllowableIds::One,
1770                    CollectionSource::Deny,
1771                    Self::resolver_checker,
1772                    e.source.as_ref(),
1773                    e.source_name.as_ref(),
1774                    get_source_dictionary!(e),
1775                    e.target.as_ref(),
1776                    e.target_name.as_ref(),
1777                    Some(&fdecl::Availability::Required),
1778                    expose_to_parent_ids,
1779                    expose_to_framework_ids,
1780                );
1781            }
1782            #[cfg(fuchsia_api_level_at_least = "25")]
1783            fdecl::Expose::Dictionary(e) => {
1784                let decl = DeclType::ExposeDictionary;
1785                self.validate_expose_fields(
1786                    decl,
1787                    AllowableIds::One,
1788                    CollectionSource::Deny,
1789                    Self::dictionary_checker,
1790                    e.source.as_ref(),
1791                    e.source_name.as_ref(),
1792                    get_source_dictionary!(e),
1793                    e.target.as_ref(),
1794                    e.target_name.as_ref(),
1795                    Some(&fdecl::Availability::Required),
1796                    expose_to_parent_ids,
1797                    expose_to_framework_ids,
1798                );
1799            }
1800            #[cfg(fuchsia_api_level_at_least = "HEAD")]
1801            fdecl::Expose::Config(e) => {
1802                let decl = DeclType::ExposeConfig;
1803                self.validate_expose_fields(
1804                    decl,
1805                    AllowableIds::One,
1806                    CollectionSource::Deny,
1807                    Self::config_checker,
1808                    e.source.as_ref(),
1809                    e.source_name.as_ref(),
1810                    None,
1811                    e.target.as_ref(),
1812                    e.target_name.as_ref(),
1813                    e.availability.as_ref(),
1814                    expose_to_parent_ids,
1815                    expose_to_framework_ids,
1816                );
1817            }
1818            _ => {
1819                self.errors.push(Error::invalid_field(DeclType::Component, "expose"));
1820            }
1821        }
1822    }
1823
1824    fn validate_expose_fields(
1825        &mut self,
1826        decl: DeclType,
1827        allowable_ids: AllowableIds,
1828        collection_source: CollectionSource,
1829        // This takes a callback that returns a [Container], instead of the &[Container] directly,
1830        // to avoid a borrow checker error that would occur from a simultaneous borrow on
1831        // &mut self.
1832        capability_checker: impl Fn(&Self) -> &dyn Container,
1833        source: Option<&fdecl::Ref>,
1834        source_name: Option<&String>,
1835        source_dictionary: Option<&String>,
1836        target: Option<&fdecl::Ref>,
1837        target_name: Option<&'a String>,
1838        availability: Option<&fdecl::Availability>,
1839        expose_to_parent_ids: &mut HashMap<&'a str, AllowableIds>,
1840        expose_to_framework_ids: &mut HashMap<&'a str, AllowableIds>,
1841    ) {
1842        self.validate_expose_source(decl, collection_source, source, source_dictionary);
1843        check_route_availability(decl, availability, source, source_name, &mut self.errors);
1844        match target {
1845            Some(r) => match r {
1846                fdecl::Ref::Parent(_) => {}
1847                fdecl::Ref::Framework(_) => {}
1848                _ => {
1849                    self.errors.push(Error::invalid_field(decl, "target"));
1850                }
1851            },
1852            None => {
1853                self.errors.push(Error::missing_field(decl, "target"));
1854            }
1855        }
1856        check_name(source_name, decl, "source_name", &mut self.errors);
1857        if source_dictionary.is_some() {
1858            check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
1859        }
1860        if check_name(target_name, decl, "target_name", &mut self.errors) {
1861            let maybe_ids_set = match target {
1862                Some(fdecl::Ref::Parent(_)) => Some(expose_to_parent_ids),
1863                Some(fdecl::Ref::Framework(_)) => Some(expose_to_framework_ids),
1864                _ => None,
1865            };
1866            if let Some(ids_set) = maybe_ids_set {
1867                let target_name = target_name.unwrap();
1868                if let Some(prev_state) = ids_set.insert(target_name, allowable_ids) {
1869                    if prev_state == AllowableIds::One || prev_state != allowable_ids {
1870                        self.errors.push(Error::duplicate_field(decl, "target_name", target_name));
1871                    }
1872                }
1873            }
1874        }
1875
1876        self.validate_route_from_self(
1877            decl,
1878            source,
1879            source_name,
1880            source_dictionary,
1881            capability_checker,
1882        );
1883    }
1884
1885    fn validate_expose_source(
1886        &mut self,
1887        decl: DeclType,
1888        collection_source: CollectionSource,
1889        source: Option<&fdecl::Ref>,
1890        source_dictionary: Option<&String>,
1891    ) {
1892        match (source, source_dictionary) {
1893            // These sources support source_dictionary.
1894            (Some(fdecl::Ref::Self_(_)), _) => {}
1895            (Some(fdecl::Ref::Child(child)), _) => {
1896                let _ = self.validate_child_ref(decl, "source", &child, OfferType::Static);
1897            }
1898            // These sources don't.
1899            (Some(fdecl::Ref::VoidType(_)), None) => {}
1900            (Some(fdecl::Ref::Framework(_)), None) => {}
1901            (Some(fdecl::Ref::Capability(c)), None) => {
1902                self.validate_source_capability(c, decl, "source");
1903            }
1904            (Some(fdecl::Ref::Collection(c)), None)
1905                if collection_source == CollectionSource::Allow =>
1906            {
1907                self.validate_source_collection(c, decl);
1908            }
1909            // `source` is required.
1910            (None, _) => {
1911                self.errors.push(Error::missing_field(decl, "source"));
1912            }
1913            // Any combination that was not recognized above must be invalid.
1914            (_, _) => {
1915                self.errors.push(Error::invalid_field(decl, "source"));
1916            }
1917        }
1918    }
1919
1920    // Checks a group of offer decls to confirm that any duplicate offers are
1921    // valid aggregate offer declarations.
1922    fn validate_offer_group(&mut self, offers: &'a Vec<fdecl::Offer>, offer_type: OfferType) {
1923        let mut offer_groups: HashMap<_, Vec<fdecl::OfferService>> = HashMap::new();
1924        let service_offers = offers.into_iter().filter_map(|o| {
1925            if let fdecl::Offer::Service(s) = o {
1926                Some(s)
1927            } else {
1928                None
1929            }
1930        });
1931        for offer in service_offers {
1932            let key = Self::make_group_key(offer.target_name.as_ref(), offer.target.as_ref());
1933            if let Some(key) = key {
1934                offer_groups.entry(key).or_insert_with(|| vec![]).push(offer.clone());
1935            }
1936        }
1937        for offer_group in offer_groups.into_values() {
1938            if offer_group.len() == 1 {
1939                // If there are not multiple offers for a (target_name, target) pair then there are
1940                // no aggregation conditions to check.
1941                continue;
1942            }
1943
1944            self.validate_aggregation_has_same_availability(&offer_group);
1945
1946            let mut source_instance_filter_entries: HashSet<String> = HashSet::new();
1947            let mut service_source_names: HashSet<String> = HashSet::new();
1948            for o in offer_group {
1949                // Currently only service capabilities can be aggregated
1950                match (o.source_instance_filter, offer_type) {
1951                    (Some(source_instance_filter), _) => {
1952                        for instance_name in source_instance_filter {
1953                            if !source_instance_filter_entries.insert(instance_name.clone()) {
1954                                // If the source instance in the filter has been seen before this
1955                                // means there is a conflicting aggregate service offer.
1956                                self.errors.push(Error::invalid_aggregate_offer(format!(
1957                                    "Conflicting source_instance_filter in aggregate service \
1958                                    offer, instance_name '{}' seen in filter lists multiple times",
1959                                    instance_name,
1960                                )));
1961                            }
1962                        }
1963                    }
1964                    (None, OfferType::Static) => {}
1965                    (None, OfferType::Dynamic) => {
1966                        // Dynamic offers must include a filter.
1967                        self.errors.push(Error::invalid_aggregate_offer(
1968                            "source_instance_filter must be set for dynamic aggregate service \
1969                            offers",
1970                        ));
1971                    }
1972                }
1973                service_source_names.insert(
1974                    o.source_name
1975                        .expect("Offer Service declarations must always contain source_name"),
1976                );
1977            }
1978
1979            if service_source_names.len() > 1 {
1980                self.errors.push(Error::invalid_aggregate_offer(format!(
1981                    "All aggregate service offers must have the same source_name, saw {}. Use \
1982                    renamed_instances to rename instance names to avoid conflict.",
1983                    service_source_names.into_iter().sorted().collect::<Vec<String>>().join(", ")
1984                )));
1985            }
1986        }
1987    }
1988
1989    fn validate_offer_decl(&mut self, offer: &'a fdecl::Offer, offer_type: OfferType) {
1990        match offer {
1991            fdecl::Offer::Service(o) => {
1992                let decl = DeclType::OfferService;
1993                self.validate_offer_fields(
1994                    decl,
1995                    AllowableIds::Many,
1996                    CollectionSource::Allow,
1997                    Self::service_checker,
1998                    o.source.as_ref(),
1999                    o.source_name.as_ref(),
2000                    get_source_dictionary!(o),
2001                    o.target.as_ref(),
2002                    o.target_name.as_ref(),
2003                    #[cfg(fuchsia_api_level_at_least = "HEAD")]
2004                    Some(o.dependency_type.as_ref().unwrap_or(&fdecl::DependencyType::Strong)),
2005                    #[cfg(fuchsia_api_level_less_than = "HEAD")]
2006                    Some(&fdecl::DependencyType::Strong),
2007                    o.availability.as_ref(),
2008                    offer_type,
2009                );
2010                self.validate_filtered_service_fields(
2011                    decl,
2012                    o.source_instance_filter.as_ref(),
2013                    o.renamed_instances.as_ref(),
2014                );
2015            }
2016            fdecl::Offer::Protocol(o) => {
2017                let decl = DeclType::OfferProtocol;
2018                self.validate_offer_fields(
2019                    decl,
2020                    AllowableIds::One,
2021                    CollectionSource::Deny,
2022                    Self::protocol_checker,
2023                    o.source.as_ref(),
2024                    o.source_name.as_ref(),
2025                    get_source_dictionary!(o),
2026                    o.target.as_ref(),
2027                    o.target_name.as_ref(),
2028                    o.dependency_type.as_ref(),
2029                    o.availability.as_ref(),
2030                    offer_type,
2031                );
2032            }
2033            fdecl::Offer::Directory(o) => {
2034                let decl = DeclType::OfferDirectory;
2035                self.validate_offer_fields(
2036                    decl,
2037                    AllowableIds::One,
2038                    CollectionSource::Deny,
2039                    Self::directory_checker,
2040                    o.source.as_ref(),
2041                    o.source_name.as_ref(),
2042                    get_source_dictionary!(o),
2043                    o.target.as_ref(),
2044                    o.target_name.as_ref(),
2045                    o.dependency_type.as_ref(),
2046                    o.availability.as_ref(),
2047                    offer_type,
2048                );
2049                if let Some(subdir) = o.subdir.as_ref() {
2050                    check_relative_path(
2051                        Some(subdir),
2052                        DeclType::OfferDirectory,
2053                        "subdir",
2054                        &mut self.errors,
2055                    );
2056                }
2057            }
2058            fdecl::Offer::Storage(o) => {
2059                let decl = DeclType::OfferStorage;
2060                self.validate_storage_offer_fields(
2061                    decl,
2062                    Self::storage_checker,
2063                    o.source.as_ref(),
2064                    o.source_name.as_ref(),
2065                    o.target.as_ref(),
2066                    o.target_name.as_ref(),
2067                    o.availability.as_ref(),
2068                    offer_type,
2069                );
2070            }
2071            fdecl::Offer::Runner(o) => {
2072                let decl = DeclType::OfferRunner;
2073                self.validate_offer_fields(
2074                    decl,
2075                    AllowableIds::One,
2076                    CollectionSource::Deny,
2077                    Self::runner_checker,
2078                    o.source.as_ref(),
2079                    o.source_name.as_ref(),
2080                    get_source_dictionary!(o),
2081                    o.target.as_ref(),
2082                    o.target_name.as_ref(),
2083                    Some(&fdecl::DependencyType::Strong),
2084                    Some(&fdecl::Availability::Required),
2085                    offer_type,
2086                );
2087            }
2088            fdecl::Offer::Resolver(o) => {
2089                let decl = DeclType::OfferResolver;
2090                self.validate_offer_fields(
2091                    decl,
2092                    AllowableIds::One,
2093                    CollectionSource::Deny,
2094                    Self::resolver_checker,
2095                    o.source.as_ref(),
2096                    o.source_name.as_ref(),
2097                    get_source_dictionary!(o),
2098                    o.target.as_ref(),
2099                    o.target_name.as_ref(),
2100                    Some(&fdecl::DependencyType::Strong),
2101                    Some(&fdecl::Availability::Required),
2102                    offer_type,
2103                );
2104            }
2105            fdecl::Offer::EventStream(e) => {
2106                self.validate_event_stream_offer_fields(e, offer_type);
2107            }
2108            #[cfg(fuchsia_api_level_at_least = "25")]
2109            fdecl::Offer::Dictionary(o) => {
2110                let decl = DeclType::OfferDictionary;
2111                self.validate_offer_fields(
2112                    decl,
2113                    AllowableIds::One,
2114                    CollectionSource::Deny,
2115                    Self::dictionary_checker,
2116                    o.source.as_ref(),
2117                    o.source_name.as_ref(),
2118                    get_source_dictionary!(o),
2119                    o.target.as_ref(),
2120                    o.target_name.as_ref(),
2121                    o.dependency_type.as_ref(),
2122                    o.availability.as_ref(),
2123                    offer_type,
2124                );
2125            }
2126            #[cfg(fuchsia_api_level_at_least = "HEAD")]
2127            fdecl::Offer::Config(o) => {
2128                let decl = DeclType::OfferConfig;
2129                self.validate_offer_fields(
2130                    decl,
2131                    AllowableIds::One,
2132                    CollectionSource::Deny,
2133                    Self::config_checker,
2134                    o.source.as_ref(),
2135                    o.source_name.as_ref(),
2136                    None,
2137                    o.target.as_ref(),
2138                    o.target_name.as_ref(),
2139                    Some(&fdecl::DependencyType::Strong),
2140                    o.availability.as_ref(),
2141                    offer_type,
2142                );
2143            }
2144            fdecl::OfferUnknown!() => {
2145                self.errors.push(Error::invalid_field(DeclType::Component, "offer"));
2146            }
2147        }
2148    }
2149
2150    fn validate_offer_fields(
2151        &mut self,
2152        decl: DeclType,
2153        allowable_names: AllowableIds,
2154        collection_source: CollectionSource,
2155        capability_checker: impl Fn(&Self) -> &dyn Container,
2156        source: Option<&'a fdecl::Ref>,
2157        source_name: Option<&'a String>,
2158        source_dictionary: Option<&'a String>,
2159        target: Option<&'a fdecl::Ref>,
2160        target_name: Option<&'a String>,
2161        dependency_type: Option<&'a fdecl::DependencyType>,
2162        availability: Option<&'a fdecl::Availability>,
2163        offer_type: OfferType,
2164    ) {
2165        self.validate_offer_source(decl, collection_source, source, source_dictionary, offer_type);
2166        check_route_availability(decl, availability, source, source_name, &mut self.errors);
2167        check_offer_name(source_name, decl, "source_name", offer_type, &mut self.errors);
2168        if source_dictionary.is_some() {
2169            check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
2170        }
2171        self.validate_offer_target(decl, allowable_names, target, target_name, offer_type);
2172        check_offer_name(target_name, decl, "target_name", offer_type, &mut self.errors);
2173
2174        if dependency_type.is_none() {
2175            self.errors.push(Error::missing_field(decl, "dependency_type"));
2176        }
2177
2178        self.validate_route_from_self(
2179            decl,
2180            source,
2181            source_name,
2182            source_dictionary,
2183            capability_checker,
2184        );
2185    }
2186
2187    fn validate_offer_source(
2188        &mut self,
2189        decl: DeclType,
2190        collection_source: CollectionSource,
2191        source: Option<&'a fdecl::Ref>,
2192        source_dictionary: Option<&'a String>,
2193        offer_type: OfferType,
2194    ) {
2195        match (source, source_dictionary) {
2196            // These sources support source_dictionary.
2197            (Some(fdecl::Ref::Parent(_)), _) => {}
2198            (Some(fdecl::Ref::Self_(_)), _) => {}
2199            (Some(fdecl::Ref::Child(child)), _) => {
2200                self.validate_child_ref(decl, "source", &child, offer_type);
2201            }
2202            // These sources don't.
2203            (Some(fdecl::Ref::VoidType(_)), None) => {}
2204            (Some(fdecl::Ref::Framework(_)), None) => {}
2205            (Some(fdecl::Ref::Capability(c)), None) => {
2206                self.validate_source_capability(c, decl, "source");
2207            }
2208            (Some(fdecl::Ref::Collection(c)), None)
2209                if collection_source == CollectionSource::Allow =>
2210            {
2211                self.validate_source_collection(c, decl);
2212            }
2213            // `source` is required.
2214            (None, _) => self.errors.push(Error::missing_field(decl, "source")),
2215            // Any combination that was not recognized above must be invalid.
2216            (_, _) => self.errors.push(Error::invalid_field(decl, "source")),
2217        }
2218    }
2219
2220    fn validate_storage_offer_fields(
2221        &mut self,
2222        decl: DeclType,
2223        // This takes a callback that returns a [Container], instead of the &[Container] directly,
2224        // to avoid a borrow checker error that would occur from a simultaneous borrow on
2225        // &mut self.
2226        capability_checker: impl Fn(&Self) -> &dyn Container,
2227        source: Option<&'a fdecl::Ref>,
2228        source_name: Option<&'a String>,
2229        target: Option<&'a fdecl::Ref>,
2230        target_name: Option<&'a String>,
2231        availability: Option<&fdecl::Availability>,
2232        offer_type: OfferType,
2233    ) {
2234        match source {
2235            Some(fdecl::Ref::Parent(_) | fdecl::Ref::VoidType(_) | fdecl::Ref::Self_(_)) => {}
2236            Some(_) => {
2237                self.errors.push(Error::invalid_field(decl, "source"));
2238            }
2239            None => {
2240                self.errors.push(Error::missing_field(decl, "source"));
2241            }
2242        }
2243        check_route_availability(decl, availability, source, source_name, &mut self.errors);
2244        check_offer_name(source_name, decl, "source_name", offer_type, &mut self.errors);
2245        self.validate_offer_target(decl, AllowableIds::One, target, target_name, offer_type);
2246        check_offer_name(target_name, decl, "target_name", offer_type, &mut self.errors);
2247
2248        if let (Some(fdecl::Ref::Self_(_)), Some(name)) = (source, source_name) {
2249            if !(capability_checker)(self).contains(name) {
2250                self.errors.push(Error::invalid_capability(decl, "source", name));
2251            }
2252        }
2253    }
2254
2255    fn validate_event_stream_offer_fields(
2256        &mut self,
2257        event_stream: &'a fdecl::OfferEventStream,
2258        offer_type: OfferType,
2259    ) {
2260        let decl = DeclType::OfferEventStream;
2261        check_name(event_stream.source_name.as_ref(), decl, "source_name", &mut self.errors);
2262        if event_stream.target == Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})) {
2263            // Expose to framework from framework is never valid.
2264            self.errors.push(Error::invalid_field(DeclType::OfferEventStream, "target"));
2265        }
2266        if let Some(scope) = &event_stream.scope {
2267            if scope.is_empty() {
2268                self.errors.push(Error::invalid_field(decl, "scope"));
2269            }
2270            for value in scope {
2271                match value {
2272                    fdecl::Ref::Child(child) => {
2273                        self.validate_child_ref(
2274                            DeclType::OfferEventStream,
2275                            "scope",
2276                            &child,
2277                            offer_type,
2278                        );
2279                    }
2280                    fdecl::Ref::Collection(collection) => {
2281                        self.validate_collection_ref(
2282                            DeclType::OfferEventStream,
2283                            "scope",
2284                            &collection,
2285                        );
2286                    }
2287                    _ => {
2288                        self.errors.push(Error::invalid_field(DeclType::OfferEventStream, "scope"));
2289                    }
2290                }
2291            }
2292        }
2293        // Only parent, framework, child, and void are valid.
2294        match event_stream.source {
2295            Some(
2296                fdecl::Ref::Parent(_)
2297                | fdecl::Ref::Framework(_)
2298                | fdecl::Ref::Child(_)
2299                | fdecl::Ref::VoidType(_),
2300            ) => {}
2301            Some(_) => {
2302                self.errors.push(Error::invalid_field(decl, "source"));
2303            }
2304            None => {
2305                self.errors.push(Error::missing_field(decl, "source"));
2306            }
2307        };
2308
2309        check_route_availability(
2310            decl,
2311            event_stream.availability.as_ref(),
2312            event_stream.source.as_ref(),
2313            event_stream.source_name.as_ref(),
2314            &mut self.errors,
2315        );
2316
2317        self.validate_offer_target(
2318            decl,
2319            AllowableIds::One,
2320            event_stream.target.as_ref(),
2321            event_stream.target_name.as_ref(),
2322            offer_type,
2323        );
2324        check_name(event_stream.target_name.as_ref(), decl, "target_name", &mut self.errors);
2325    }
2326
2327    /// Check a `ChildRef` contains a valid child that exists.
2328    fn validate_child_ref(
2329        &mut self,
2330        decl: DeclType,
2331        field_name: &str,
2332        child: &fdecl::ChildRef,
2333        offer_type: OfferType,
2334    ) -> bool {
2335        if offer_type == OfferType::Dynamic && child.collection.is_some() {
2336            return self.validate_dynamic_child_ref(decl, field_name, child);
2337        }
2338        // Ensure the name is valid, and the reference refers to a static child.
2339        //
2340        // We attempt to list all errors if possible.
2341        let mut valid = true;
2342        if !check_name(
2343            Some(&child.name),
2344            decl,
2345            &format!("{}.child.name", field_name),
2346            &mut self.errors,
2347        ) {
2348            valid = false;
2349        }
2350        if child.collection.is_some() {
2351            self.errors
2352                .push(Error::extraneous_field(decl, format!("{}.child.collection", field_name)));
2353            valid = false;
2354        }
2355        if !valid {
2356            return false;
2357        }
2358
2359        // Ensure the child exists.
2360        let name: &str = &child.name;
2361        if !self.all_children.contains_key(name) {
2362            self.errors.push(Error::invalid_child(decl, field_name, name));
2363            return false;
2364        }
2365
2366        true
2367    }
2368
2369    /// Check a `ChildRef` contains a valid dynamic child.
2370    ///
2371    /// The manifest we're validating doesn't contain dynamic children so we can't check if the dynamic
2372    /// child actually exists, but we can confirm things like the name is valid.
2373    fn validate_dynamic_child_ref(
2374        &mut self,
2375        decl: DeclType,
2376        field_name: &str,
2377        child: &fdecl::ChildRef,
2378    ) -> bool {
2379        // Ensure the name is valid.
2380        //
2381        // We attempt to list all errors if possible.
2382        let mut valid = true;
2383        if !check_dynamic_name(
2384            Some(&child.name),
2385            decl,
2386            &format!("{}.child.name", field_name),
2387            &mut self.errors,
2388        ) {
2389            valid = false;
2390        }
2391        if !check_name(
2392            child.collection.as_ref(),
2393            decl,
2394            &format!("{}.child.collection", field_name),
2395            &mut self.errors,
2396        ) {
2397            valid = false;
2398        }
2399        valid
2400    }
2401
2402    /// Check a `CollectionRef` is valid and refers to an existing collection.
2403    fn validate_collection_ref(
2404        &mut self,
2405        decl: DeclType,
2406        field_name: &str,
2407        collection: &fdecl::CollectionRef,
2408    ) -> bool {
2409        // Ensure the name is valid.
2410        if !check_name(
2411            Some(&collection.name),
2412            decl,
2413            &format!("{}.collection.name", field_name),
2414            &mut self.errors,
2415        ) {
2416            return false;
2417        }
2418
2419        // Ensure the collection exists.
2420        if !self.all_collections.contains(&collection.name as &str) {
2421            self.errors.push(Error::invalid_collection(decl, field_name, &collection.name as &str));
2422            return false;
2423        }
2424
2425        true
2426    }
2427
2428    fn validate_offer_target(
2429        &mut self,
2430        decl: DeclType,
2431        allowable_names: AllowableIds,
2432        target: Option<&'a fdecl::Ref>,
2433        target_name: Option<&'a String>,
2434        offer_type: OfferType,
2435    ) {
2436        match target {
2437            Some(fdecl::Ref::Child(c)) => {
2438                self.validate_target_child(decl, allowable_names, c, target_name, offer_type);
2439            }
2440            Some(fdecl::Ref::Collection(c)) => {
2441                self.validate_target_collection(decl, allowable_names, c, target_name);
2442            }
2443            Some(fdecl::Ref::Capability(c)) => {
2444                // Only offers to dictionary capabilities are valid.
2445                #[cfg(fuchsia_api_level_at_least = "25")]
2446                if let Some(d) = self.all_dictionaries.get(&c.name.as_str()) {
2447                    if d.source_path.is_some() {
2448                        // If `source_path` is present that means this is an offer into a
2449                        // dynamic dictionary, which is not allowed.
2450                        self.errors.push(Error::invalid_field(decl, "target"));
2451                    }
2452                } else {
2453                    self.errors.push(Error::invalid_field(decl, "target"));
2454                }
2455                #[cfg(not(fuchsia_api_level_at_least = "25"))]
2456                {
2457                    let _ = c;
2458                    self.errors.push(Error::invalid_field(decl, "target"));
2459                }
2460            }
2461            Some(_) => {
2462                self.errors.push(Error::invalid_field(decl, "target"));
2463            }
2464            None => {
2465                self.errors.push(Error::missing_field(decl, "target"));
2466            }
2467        }
2468    }
2469
2470    fn validate_target_child(
2471        &mut self,
2472        decl: DeclType,
2473        allowable_names: AllowableIds,
2474        child: &'a fdecl::ChildRef,
2475        target_name: Option<&'a String>,
2476        offer_type: OfferType,
2477    ) {
2478        if !self.validate_child_ref(decl, "target", child, offer_type) {
2479            return;
2480        }
2481        if let Some(target_name) = target_name {
2482            let names_for_target = self
2483                .target_ids
2484                .entry(TargetId::Component(
2485                    &child.name,
2486                    child.collection.as_ref().map(|s| s.as_str()),
2487                ))
2488                .or_insert(HashMap::new());
2489            if let Some(prev_state) = names_for_target.insert(target_name, allowable_names) {
2490                if prev_state == AllowableIds::One || prev_state != allowable_names {
2491                    self.errors.push(Error::duplicate_field(
2492                        decl,
2493                        "target_name",
2494                        target_name as &str,
2495                    ));
2496                }
2497            }
2498            if let Some(collection) = child.collection.as_ref() {
2499                if let Some(names_for_target) =
2500                    self.target_ids.get(&TargetId::Collection(&collection))
2501                {
2502                    if names_for_target.contains_key(&target_name.as_str()) {
2503                        // This dynamic offer conflicts with a static offer to the same collection.
2504                        self.errors.push(Error::duplicate_field(
2505                            decl,
2506                            "target_name",
2507                            target_name as &str,
2508                        ));
2509                    }
2510                }
2511            }
2512        }
2513    }
2514
2515    fn validate_target_collection(
2516        &mut self,
2517        decl: DeclType,
2518        allowable_names: AllowableIds,
2519        collection: &'a fdecl::CollectionRef,
2520        target_name: Option<&'a String>,
2521    ) {
2522        if !self.validate_collection_ref(decl, "target", &collection) {
2523            return;
2524        }
2525        if let Some(target_name) = target_name {
2526            let names_for_target = self
2527                .target_ids
2528                .entry(TargetId::Collection(&collection.name))
2529                .or_insert(HashMap::new());
2530            if let Some(prev_state) = names_for_target.insert(target_name, allowable_names) {
2531                if prev_state == AllowableIds::One || prev_state != allowable_names {
2532                    self.errors.push(Error::duplicate_field(
2533                        decl,
2534                        "target_name",
2535                        target_name as &str,
2536                    ));
2537                }
2538            }
2539        }
2540    }
2541
2542    fn validate_route_from_self(
2543        &mut self,
2544        decl: DeclType,
2545        source: Option<&fdecl::Ref>,
2546        source_name: Option<&String>,
2547        source_dictionary: Option<&String>,
2548        capability_checker: impl Fn(&Self) -> &dyn Container,
2549    ) {
2550        let (Some(fdecl::Ref::Self_(_)), Some(name)) = (source, source_name) else {
2551            return;
2552        };
2553        match source_dictionary {
2554            Some(source_dictionary) => {
2555                #[cfg(fuchsia_api_level_at_least = "25")]
2556                {
2557                    use cm_types::IterablePath;
2558                    if let Ok(path) = cm_types::RelativePath::new(source_dictionary) {
2559                        if let Some(first_segment) = path.iter_segments().next().map(|s| s.as_str())
2560                        {
2561                            if !self.all_dictionaries.contains_key(first_segment) {
2562                                self.errors.push(Error::invalid_capability(
2563                                    decl,
2564                                    "source",
2565                                    first_segment,
2566                                ));
2567                            }
2568                        }
2569                    }
2570                }
2571                #[cfg(not(fuchsia_api_level_at_least = "25"))]
2572                let _ = source_dictionary;
2573            }
2574            None => {
2575                if !(capability_checker)(self).contains(name) {
2576                    self.errors.push(Error::invalid_capability(decl, "source", name));
2577                }
2578            }
2579        }
2580    }
2581
2582    // The following functions can be used to convert a type-specific collection of capabilities
2583    // into [Container].
2584    fn service_checker(&self) -> &dyn Container {
2585        &self.all_services
2586    }
2587    fn protocol_checker(&self) -> &dyn Container {
2588        &self.all_protocols
2589    }
2590    fn directory_checker(&self) -> &dyn Container {
2591        &self.all_directories
2592    }
2593    fn runner_checker(&self) -> &dyn Container {
2594        &self.all_runners
2595    }
2596    fn resolver_checker(&self) -> &dyn Container {
2597        &self.all_resolvers
2598    }
2599
2600    #[cfg(fuchsia_api_level_at_least = "25")]
2601    fn dictionary_checker(&self) -> &dyn Container {
2602        &self.all_dictionaries
2603    }
2604
2605    #[cfg(fuchsia_api_level_at_least = "HEAD")]
2606    fn config_checker(&self) -> &dyn Container {
2607        &self.all_configs
2608    }
2609    fn storage_checker(&self) -> &dyn Container {
2610        &self.all_storages
2611    }
2612    fn event_stream_checker(&self) -> &dyn Container {
2613        // Components can't define their own event streams. If someone tries to route an event
2614        // stream from Self it should generate some other error. So just return `true` to bypass
2615        // the logic.
2616        struct AlwaysTrueContainer {}
2617        impl Container for AlwaysTrueContainer {
2618            fn contains(&self, _key: &str) -> bool {
2619                true
2620            }
2621        }
2622        static CONTAINER: AlwaysTrueContainer = AlwaysTrueContainer {};
2623        &CONTAINER
2624    }
2625}
2626
2627#[cfg(test)]
2628mod tests {
2629    use super::*;
2630    use cm_types::MAX_LONG_NAME_LENGTH;
2631    use test_case::test_case;
2632    use {
2633        fidl_fuchsia_component_decl as fdecl, fidl_fuchsia_data as fdata, fidl_fuchsia_io as fio,
2634    };
2635
2636    macro_rules! test_validate {
2637        (
2638            $(
2639                $test_name:ident => {
2640                    input = $input:expr,
2641                    result = $result:expr,
2642                },
2643            )+
2644        ) => {
2645            $(
2646                #[test]
2647                fn $test_name() {
2648                    validate_test($input, $result);
2649                }
2650            )+
2651        }
2652    }
2653
2654    macro_rules! test_validate_any_result {
2655        (
2656            $(
2657                $test_name:ident => {
2658                    input = $input:expr,
2659                    results = $results:expr,
2660                },
2661            )+
2662        ) => {
2663            $(
2664                #[test]
2665                fn $test_name() {
2666                    validate_test_any_result($input, $results);
2667                }
2668            )+
2669        }
2670    }
2671
2672    macro_rules! test_validate_values_data {
2673        (
2674            $(
2675                $test_name:ident => {
2676                    input = $input:expr,
2677                    result = $result:expr,
2678                },
2679            )+
2680        ) => {
2681            $(
2682                #[test]
2683                fn $test_name() {
2684                    validate_values_data_test($input, $result);
2685                }
2686            )+
2687        }
2688    }
2689
2690    macro_rules! test_validate_capabilities {
2691        (
2692            $(
2693                $test_name:ident => {
2694                    input = $input:expr,
2695                    as_builtin = $as_builtin:expr,
2696                    result = $result:expr,
2697                },
2698            )+
2699        ) => {
2700            $(
2701                #[test]
2702                fn $test_name() {
2703                    validate_capabilities_test($input, $as_builtin, $result);
2704                }
2705            )+
2706        }
2707    }
2708
2709    macro_rules! test_dependency {
2710        (
2711            $(
2712                ($test_name:ident) => {
2713                    ty = $ty:expr,
2714                    offer_decl = $offer_decl:expr,
2715                },
2716            )+
2717        ) => {
2718            $(
2719                #[test]
2720                fn $test_name() {
2721                    let mut decl = new_component_decl();
2722                    let dependencies = vec![
2723                        ("a", "b"),
2724                        ("b", "a"),
2725                    ];
2726                    let offers = dependencies.into_iter().map(|(from,to)| {
2727                        let mut offer_decl = $offer_decl;
2728                        offer_decl.source = Some(fdecl::Ref::Child(
2729                           fdecl::ChildRef { name: from.to_string(), collection: None },
2730                        ));
2731                        offer_decl.target = Some(fdecl::Ref::Child(
2732                           fdecl::ChildRef { name: to.to_string(), collection: None },
2733                        ));
2734                        $ty(offer_decl)
2735                    }).collect();
2736                    let children = ["a", "b"].iter().map(|name| {
2737                        fdecl::Child {
2738                            name: Some(name.to_string()),
2739                            url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
2740                            startup: Some(fdecl::StartupMode::Lazy),
2741                            on_terminate: None,
2742                            environment: None,
2743                            ..Default::default()
2744                        }
2745                    }).collect();
2746                    decl.offers = Some(offers);
2747                    decl.children = Some(children);
2748                    let result = Err(ErrorList::new(vec![
2749                        Error::dependency_cycle(
2750                            directed_graph::Error::CyclesDetected([vec!["child a", "child b", "child a"]].iter().cloned().collect()).format_cycle()),
2751                    ]));
2752                    validate_test(decl, result);
2753                }
2754            )+
2755        }
2756    }
2757
2758    macro_rules! test_weak_dependency {
2759        (
2760            $(
2761                ($test_name:ident) => {
2762                    ty = $ty:expr,
2763                    offer_decl = $offer_decl:expr,
2764                },
2765            )+
2766        ) => {
2767            $(
2768                #[test_case(fdecl::DependencyType::Weak)]
2769                fn $test_name(weak_dep: fdecl::DependencyType) {
2770                    let mut decl = new_component_decl();
2771                    let offers = vec![
2772                        {
2773                            let mut offer_decl = $offer_decl;
2774                            offer_decl.source = Some(fdecl::Ref::Child(
2775                               fdecl::ChildRef { name: "a".to_string(), collection: None },
2776                            ));
2777                            offer_decl.target = Some(fdecl::Ref::Child(
2778                               fdecl::ChildRef { name: "b".to_string(), collection: None },
2779                            ));
2780                            offer_decl.dependency_type = Some(fdecl::DependencyType::Strong);
2781                            $ty(offer_decl)
2782                        },
2783                        {
2784                            let mut offer_decl = $offer_decl;
2785                            offer_decl.source = Some(fdecl::Ref::Child(
2786                               fdecl::ChildRef { name: "b".to_string(), collection: None },
2787                            ));
2788                            offer_decl.target = Some(fdecl::Ref::Child(
2789                               fdecl::ChildRef { name: "a".to_string(), collection: None },
2790                            ));
2791                            offer_decl.dependency_type = Some(weak_dep);
2792                            $ty(offer_decl)
2793                        },
2794                    ];
2795                    let children = ["a", "b"].iter().map(|name| {
2796                        fdecl::Child {
2797                            name: Some(name.to_string()),
2798                            url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
2799                            startup: Some(fdecl::StartupMode::Lazy),
2800                            on_terminate: None,
2801                            environment: None,
2802                            ..Default::default()
2803                        }
2804                    }).collect();
2805                    decl.offers = Some(offers);
2806                    decl.children = Some(children);
2807                    let result = Ok(());
2808                    validate_test(decl, result);
2809                }
2810            )+
2811        }
2812    }
2813
2814    #[track_caller]
2815    fn validate_test(input: fdecl::Component, expected_res: Result<(), ErrorList>) {
2816        let res = validate(&input);
2817        assert_eq!(res, expected_res);
2818    }
2819
2820    #[track_caller]
2821    fn validate_test_any_result(input: fdecl::Component, expected_res: Vec<Result<(), ErrorList>>) {
2822        let res = format!("{:?}", validate(&input));
2823        let expected_res_debug = format!("{:?}", expected_res);
2824
2825        let matched_exp =
2826            expected_res.into_iter().find(|expected| res == format!("{:?}", expected));
2827
2828        assert!(
2829            matched_exp.is_some(),
2830            "assertion failed: Expected one of:\n{:?}\nActual:\n{:?}",
2831            expected_res_debug,
2832            res
2833        );
2834    }
2835
2836    #[track_caller]
2837    fn validate_values_data_test(
2838        input: fdecl::ConfigValuesData,
2839        expected_res: Result<(), ErrorList>,
2840    ) {
2841        let res = validate_values_data(&input);
2842        assert_eq!(res, expected_res);
2843    }
2844
2845    #[track_caller]
2846    fn validate_capabilities_test(
2847        input: Vec<fdecl::Capability>,
2848        as_builtin: bool,
2849        expected_res: Result<(), ErrorList>,
2850    ) {
2851        let res = validate_capabilities(&input, as_builtin);
2852        assert_eq!(res, expected_res);
2853    }
2854
2855    fn new_component_decl() -> fdecl::Component {
2856        fdecl::Component {
2857            program: None,
2858            uses: None,
2859            exposes: None,
2860            offers: None,
2861            facets: None,
2862            capabilities: None,
2863            children: None,
2864            collections: None,
2865            environments: None,
2866            ..Default::default()
2867        }
2868    }
2869
2870    test_validate_any_result! {
2871        test_validate_use_disallows_nested_dirs => {
2872            input = {
2873                let mut decl = new_component_decl();
2874                decl.uses = Some(vec![
2875                    fdecl::Use::Directory(fdecl::UseDirectory {
2876                        dependency_type: Some(fdecl::DependencyType::Strong),
2877                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2878                        source_name: Some("abc".to_string()),
2879                        target_path: Some("/foo/bar".to_string()),
2880                        rights: Some(fio::Operations::CONNECT),
2881                        subdir: None,
2882                        ..Default::default()
2883                    }),
2884                    fdecl::Use::Directory(fdecl::UseDirectory {
2885                        dependency_type: Some(fdecl::DependencyType::Strong),
2886                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2887                        source_name: Some("abc".to_string()),
2888                        target_path: Some("/foo/bar/baz".to_string()),
2889                        rights: Some(fio::Operations::CONNECT),
2890                        subdir: None,
2891                        ..Default::default()
2892                    }),
2893                ]);
2894                decl
2895            },
2896            results = vec![
2897                Err(ErrorList::new(vec![
2898                    Error::invalid_path_overlap(
2899                        DeclType::UseDirectory, "/foo/bar/baz", DeclType::UseDirectory, "/foo/bar"),
2900                ])),
2901                Err(ErrorList::new(vec![
2902                    Error::invalid_path_overlap(
2903                        DeclType::UseDirectory, "/foo/bar", DeclType::UseDirectory, "/foo/bar/baz"),
2904                ])),
2905            ],
2906        },
2907        test_validate_use_disallows_nested_dirs_storage => {
2908            input = {
2909                let mut decl = new_component_decl();
2910                decl.uses = Some(vec![
2911                    fdecl::Use::Storage(fdecl::UseStorage {
2912                        source_name: Some("abc".to_string()),
2913                        target_path: Some("/foo/bar".to_string()),
2914                        ..Default::default()
2915                    }),
2916                    fdecl::Use::Storage(fdecl::UseStorage {
2917                        source_name: Some("abc".to_string()),
2918                        target_path: Some("/foo/bar/baz".to_string()),
2919                        ..Default::default()
2920                    }),
2921                ]);
2922                decl
2923            },
2924            results = vec![
2925                Err(ErrorList::new(vec![
2926                    Error::invalid_path_overlap(
2927                        DeclType::UseStorage, "/foo/bar/baz", DeclType::UseStorage, "/foo/bar"),
2928                ])),
2929                Err(ErrorList::new(vec![
2930                    Error::invalid_path_overlap(
2931                        DeclType::UseStorage, "/foo/bar", DeclType::UseStorage, "/foo/bar/baz"),
2932                ])),
2933            ],
2934        },
2935        test_validate_use_disallows_nested_dirs_directory_and_storage => {
2936            input = {
2937                let mut decl = new_component_decl();
2938                decl.uses = Some(vec![
2939                    fdecl::Use::Directory(fdecl::UseDirectory {
2940                        dependency_type: Some(fdecl::DependencyType::Strong),
2941                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2942                        source_name: Some("abc".to_string()),
2943                        target_path: Some("/foo/bar".to_string()),
2944                        rights: Some(fio::Operations::CONNECT),
2945                        subdir: None,
2946                        ..Default::default()
2947                    }),
2948                    fdecl::Use::Storage(fdecl::UseStorage {
2949                        source_name: Some("abc".to_string()),
2950                        target_path: Some("/foo/bar/baz".to_string()),
2951                        ..Default::default()
2952                    }),
2953                ]);
2954                decl
2955            },
2956            results = vec![
2957                Err(ErrorList::new(vec![
2958                    Error::invalid_path_overlap(
2959                        DeclType::UseStorage, "/foo/bar/baz", DeclType::UseDirectory, "/foo/bar"),
2960                ])),
2961                Err(ErrorList::new(vec![
2962                    Error::invalid_path_overlap(
2963                        DeclType::UseDirectory, "/foo/bar", DeclType::UseStorage, "/foo/bar/baz"),
2964                ])),
2965            ],
2966        },
2967        test_validate_use_disallows_common_prefixes_protocol => {
2968            input = {
2969                let mut decl = new_component_decl();
2970                decl.uses = Some(vec![
2971                    fdecl::Use::Directory(fdecl::UseDirectory {
2972                        dependency_type: Some(fdecl::DependencyType::Strong),
2973                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2974                        source_name: Some("abc".to_string()),
2975                        target_path: Some("/foo/bar".to_string()),
2976                        rights: Some(fio::Operations::CONNECT),
2977                        subdir: None,
2978                        ..Default::default()
2979                    }),
2980                    fdecl::Use::Protocol(fdecl::UseProtocol {
2981                        dependency_type: Some(fdecl::DependencyType::Strong),
2982                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2983                        source_name: Some("crow".to_string()),
2984                        target_path: Some("/foo/bar/fuchsia.2".to_string()),
2985                        ..Default::default()
2986                    }),
2987                ]);
2988                decl
2989            },
2990            results = vec![
2991                Err(ErrorList::new(vec![
2992                    Error::invalid_path_overlap(
2993                        DeclType::UseProtocol, "/foo/bar/fuchsia.2", DeclType::UseDirectory, "/foo/bar"),
2994                ])),
2995                Err(ErrorList::new(vec![
2996                    Error::invalid_path_overlap(
2997                        DeclType::UseDirectory, "/foo/bar", DeclType::UseProtocol, "/foo/bar/fuchsia.2"),
2998                ])),
2999            ],
3000        },
3001        test_validate_use_disallows_common_prefixes_service => {
3002            input = {
3003                let mut decl = new_component_decl();
3004                decl.uses = Some(vec![
3005                    fdecl::Use::Directory(fdecl::UseDirectory {
3006                        dependency_type: Some(fdecl::DependencyType::Strong),
3007                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3008                        source_name: Some("abc".to_string()),
3009                        target_path: Some("/foo/bar".to_string()),
3010                        rights: Some(fio::Operations::CONNECT),
3011                        subdir: None,
3012                        ..Default::default()
3013                    }),
3014                    fdecl::Use::Service(fdecl::UseService {
3015                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3016                        source_name: Some("space".to_string()),
3017                        target_path: Some("/foo/bar/baz/fuchsia.logger.Log".to_string()),
3018                        dependency_type: Some(fdecl::DependencyType::Strong),
3019                        ..Default::default()
3020                    }),
3021                ]);
3022                decl
3023            },
3024            results = vec![
3025                Err(ErrorList::new(vec![
3026                    Error::invalid_path_overlap(
3027                        DeclType::UseService, "/foo/bar/baz/fuchsia.logger.Log", DeclType::UseDirectory, "/foo/bar"),
3028                ])),
3029                Err(ErrorList::new(vec![
3030                    Error::invalid_path_overlap(
3031                        DeclType::UseDirectory, "/foo/bar", DeclType::UseService, "/foo/bar/baz/fuchsia.logger.Log"),
3032                ])),
3033            ],
3034        },
3035        test_validate_use_disallows_pkg => {
3036            input = {
3037                let mut decl = new_component_decl();
3038                decl.uses = Some(vec![
3039                    fdecl::Use::Directory(fdecl::UseDirectory {
3040                        dependency_type: Some(fdecl::DependencyType::Strong),
3041                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3042                        source_name: Some("abc".to_string()),
3043                        target_path: Some("/pkg".to_string()),
3044                        rights: Some(fio::Operations::CONNECT),
3045                        subdir: None,
3046                        ..Default::default()
3047                    }),
3048                ]);
3049                decl
3050            },
3051            results = vec![
3052                Err(ErrorList::new(vec![
3053                    Error::pkg_path_overlap(DeclType::UseDirectory, "/pkg"),
3054                ])),
3055            ],
3056        },
3057        test_validate_use_disallows_pkg_overlap => {
3058            input = {
3059                let mut decl = new_component_decl();
3060                decl.uses = Some(vec![
3061                    fdecl::Use::Directory(fdecl::UseDirectory {
3062                        dependency_type: Some(fdecl::DependencyType::Strong),
3063                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3064                        source_name: Some("abc".to_string()),
3065                        target_path: Some("/pkg/foo".to_string()),
3066                        rights: Some(fio::Operations::CONNECT),
3067                        subdir: None,
3068                        ..Default::default()
3069                    }),
3070                ]);
3071                decl
3072            },
3073            results = vec![
3074                Err(ErrorList::new(vec![
3075                    Error::pkg_path_overlap(DeclType::UseDirectory, "/pkg/foo"),
3076                ])),
3077            ],
3078        },
3079        test_validate_use_optional_config_correct => {
3080            input = {
3081                let mut decl = new_component_decl();
3082                decl.uses = Some(vec![
3083                    fdecl::Use::Config(fdecl::UseConfiguration {
3084                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3085                        source_name: Some("abc".to_string()),
3086                        target_name: Some("foo".to_string()),
3087                        availability: Some(fdecl::Availability::Optional),
3088                        type_: Some(fdecl::ConfigType {
3089                            layout: fdecl::ConfigTypeLayout::Bool,
3090                            parameters: Some(Vec::new()),
3091                            constraints: Vec::new(),
3092                        }),
3093                        ..Default::default()
3094                    }),
3095                ]);
3096                decl.config = Some(fdecl::ConfigSchema {
3097                    fields: Some(vec![fdecl::ConfigField {
3098                        key: Some("foo".into()),
3099                        type_: Some(fdecl::ConfigType {
3100                            layout: fdecl::ConfigTypeLayout::Bool,
3101                            parameters: Some(Vec::new()),
3102                            constraints: Vec::new(),
3103                        }),
3104                        mutability: None,
3105                        ..Default::default()}]),
3106                    checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3107                    value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3108                    ..Default::default()
3109                     });
3110                decl
3111            },
3112            results = vec![Ok(())],
3113        },
3114        test_validate_use_optional_config_no_config_schema => {
3115            input = {
3116                let mut decl = new_component_decl();
3117                decl.uses = Some(vec![
3118                    fdecl::Use::Config(fdecl::UseConfiguration {
3119                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3120                        source_name: Some("abc".to_string()),
3121                        target_name: Some("foo".to_string()),
3122                        availability: Some(fdecl::Availability::Optional),
3123                        type_: Some(fdecl::ConfigType {
3124                            layout: fdecl::ConfigTypeLayout::Bool,
3125                            parameters: None,
3126                            constraints: Vec::new(),
3127                        }),
3128                        ..Default::default()
3129                    }),
3130                ]);
3131                decl
3132            },
3133            results = vec![
3134                Err(ErrorList::new(vec![
3135                    Error::missing_field(DeclType::ConfigField, "config"),
3136                ])),
3137            ],
3138        },
3139        test_validate_use_optional_config_no_config_field => {
3140            input = {
3141                let mut decl = new_component_decl();
3142                decl.uses = Some(vec![
3143                    fdecl::Use::Config(fdecl::UseConfiguration {
3144                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3145                        source_name: Some("abc".to_string()),
3146                        target_name: Some("foo".to_string()),
3147                        availability: Some(fdecl::Availability::Optional),
3148                        type_: Some(fdecl::ConfigType {
3149                            layout: fdecl::ConfigTypeLayout::Bool,
3150                            parameters: None,
3151                            constraints: Vec::new(),
3152                        }),
3153                        ..Default::default()
3154                    }),
3155                ]);
3156                decl.config = Some(fdecl::ConfigSchema {
3157                    fields: Some(vec![]),
3158                    checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3159                    value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3160                    ..Default::default()
3161                     });
3162                decl
3163            },
3164            results = vec![
3165                Err(ErrorList::new(vec![
3166                    Error::missing_field(DeclType::ConfigField, "foo"),
3167                ])),
3168            ],
3169        },
3170        test_validate_use_optional_config_bad_type => {
3171            input = {
3172                let mut decl = new_component_decl();
3173                decl.uses = Some(vec![
3174                    fdecl::Use::Config(fdecl::UseConfiguration {
3175                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3176                        source_name: Some("abc".to_string()),
3177                        target_name: Some("foo".to_string()),
3178                        availability: Some(fdecl::Availability::Optional),
3179                        type_: Some(fdecl::ConfigType {
3180                            layout: fdecl::ConfigTypeLayout::Bool,
3181                            parameters: None,
3182                            constraints: Vec::new(),
3183                        }),
3184                        ..Default::default()
3185                    }),
3186                ]);
3187                decl.config = Some(fdecl::ConfigSchema {
3188                    fields: Some(vec![fdecl::ConfigField {
3189                        key: Some("foo".into()),
3190                        type_: Some(fdecl::ConfigType {
3191                            layout: fdecl::ConfigTypeLayout::Int16,
3192                            parameters: Some(Vec::new()),
3193                            constraints: Vec::new(),
3194                        }),
3195                        mutability: None,
3196                        ..Default::default()}]),
3197                    checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3198                    value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3199                    ..Default::default()
3200                     });
3201                decl
3202            },
3203            results = vec![
3204                Err(ErrorList::new(vec![
3205                    Error::invalid_field(DeclType::ConfigField, "foo"),
3206                ])),
3207            ],
3208        },
3209    }
3210
3211    test_validate_values_data! {
3212        test_values_data_ok => {
3213            input = fdecl::ConfigValuesData {
3214                values: Some(vec![
3215                    fdecl::ConfigValueSpec {
3216                        value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(true))),
3217                        ..Default::default()
3218                    }
3219                ]),
3220                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3221                ..Default::default()
3222            },
3223            result = Ok(()),
3224        },
3225        test_values_data_no_checksum => {
3226            input = fdecl::ConfigValuesData {
3227                values: Some(vec![]),
3228                checksum: None,
3229                ..Default::default()
3230            },
3231            result = Err(ErrorList::new(vec![
3232                Error::missing_field(DeclType::ConfigValuesData, "checksum")
3233            ])),
3234        },
3235        test_values_data_unknown_checksum => {
3236            input = fdecl::ConfigValuesData {
3237                values: Some(vec![]),
3238                checksum: Some(fdecl::ConfigChecksum::unknown_variant_for_testing()),
3239                ..Default::default()
3240            },
3241            result = Err(ErrorList::new(vec![
3242                Error::invalid_field(DeclType::ConfigValuesData, "checksum")
3243            ])),
3244        },
3245        test_values_data_no_values => {
3246            input = fdecl::ConfigValuesData {
3247                values: None,
3248                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3249                ..Default::default()
3250            },
3251            result = Err(ErrorList::new(vec![
3252                Error::missing_field(DeclType::ConfigValuesData, "values")
3253            ])),
3254        },
3255        test_values_data_no_inner_value => {
3256            input = fdecl::ConfigValuesData {
3257                values: Some(vec![
3258                    fdecl::ConfigValueSpec::default()
3259                ]),
3260                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3261                ..Default::default()
3262            },
3263            result = Err(ErrorList::new(vec![
3264                Error::missing_field(DeclType::ConfigValueSpec, "value")
3265            ])),
3266        },
3267        test_values_data_unknown_inner_value => {
3268            input = fdecl::ConfigValuesData {
3269                values: Some(vec![
3270                    fdecl::ConfigValueSpec {
3271                        value: Some(fdecl::ConfigValue::unknown_variant_for_testing()),
3272                        ..Default::default()
3273                    }
3274                ]),
3275                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3276                ..Default::default()
3277            },
3278            result = Err(ErrorList::new(vec![
3279                Error::invalid_field(DeclType::ConfigValueSpec, "value")
3280            ])),
3281        },
3282        test_values_data_unknown_single_value => {
3283            input = fdecl::ConfigValuesData {
3284                values: Some(vec![
3285                    fdecl::ConfigValueSpec {
3286                        value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::unknown_variant_for_testing())),
3287                        ..Default::default()
3288                    }
3289                ]),
3290                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3291                ..Default::default()
3292            },
3293            result = Err(ErrorList::new(vec![
3294                Error::invalid_field(DeclType::ConfigValueSpec, "value")
3295            ])),
3296        },
3297        test_values_data_unknown_list_value => {
3298            input = fdecl::ConfigValuesData {
3299                values: Some(vec![
3300                    fdecl::ConfigValueSpec {
3301                        value: Some(fdecl::ConfigValue::Vector(fdecl::ConfigVectorValue::unknown_variant_for_testing())),
3302                        ..Default::default()
3303                    }
3304                ]),
3305                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3306                ..Default::default()
3307            },
3308            result = Err(ErrorList::new(vec![
3309                Error::invalid_field(DeclType::ConfigValueSpec, "value")
3310            ])),
3311        },
3312    }
3313
3314    test_validate! {
3315        // uses
3316        test_validate_uses_empty => {
3317            input = {
3318                let mut decl = new_component_decl();
3319                decl.program = Some(fdecl::Program {
3320                    runner: Some("elf".to_string()),
3321                    info: Some(fdata::Dictionary {
3322                        entries: None,
3323                        ..Default::default()
3324                    }),
3325                    ..Default::default()
3326                });
3327                decl.uses = Some(vec![
3328                    fdecl::Use::Service(fdecl::UseService {
3329                        source: None,
3330                        source_name: None,
3331                        target_path: None,
3332                        dependency_type: None,
3333                        ..Default::default()
3334                    }),
3335                    fdecl::Use::Protocol(fdecl::UseProtocol {
3336                        dependency_type: None,
3337                        source: None,
3338                        source_name: None,
3339                        target_path: None,
3340                        ..Default::default()
3341                    }),
3342                    fdecl::Use::Directory(fdecl::UseDirectory {
3343                        dependency_type: None,
3344                        source: None,
3345                        source_name: None,
3346                        target_path: None,
3347                        rights: None,
3348                        subdir: None,
3349                        ..Default::default()
3350                    }),
3351                    fdecl::Use::Storage(fdecl::UseStorage {
3352                        source_name: None,
3353                        target_path: None,
3354                        ..Default::default()
3355                    }),
3356                    fdecl::Use::EventStream(fdecl::UseEventStream {
3357                        source_name: None,
3358                        source: None,
3359                        target_path: None,
3360                        ..Default::default()
3361                    }),
3362                    fdecl::Use::Runner(fdecl::UseRunner {
3363                        source_name: None,
3364                        source: None,
3365                        ..Default::default()
3366                    }),
3367                ]);
3368                decl
3369            },
3370            result = Err(ErrorList::new(vec![
3371                Error::missing_field(DeclType::UseService, "source"),
3372                Error::missing_field(DeclType::UseService, "source_name"),
3373                Error::missing_field(DeclType::UseService, "target_path"),
3374                Error::missing_field(DeclType::UseService, "dependency_type"),
3375                Error::missing_field(DeclType::UseProtocol, "source"),
3376                Error::missing_field(DeclType::UseProtocol, "source_name"),
3377                Error::missing_field(DeclType::UseProtocol, "target_path"),
3378                Error::missing_field(DeclType::UseProtocol, "dependency_type"),
3379                Error::missing_field(DeclType::UseDirectory, "source"),
3380                Error::missing_field(DeclType::UseDirectory, "source_name"),
3381                Error::missing_field(DeclType::UseDirectory, "target_path"),
3382                Error::missing_field(DeclType::UseDirectory, "dependency_type"),
3383                Error::missing_field(DeclType::UseDirectory, "rights"),
3384                Error::missing_field(DeclType::UseStorage, "source_name"),
3385                Error::missing_field(DeclType::UseStorage, "target_path"),
3386                Error::missing_field(DeclType::UseEventStream, "source"),
3387                Error::missing_field(DeclType::UseEventStream, "source_name"),
3388                Error::missing_field(DeclType::UseEventStream, "target_path"),
3389                Error::missing_field(DeclType::UseRunner, "source"),
3390                Error::missing_field(DeclType::UseRunner, "source_name"),
3391            ])),
3392        },
3393        test_validate_missing_program_info => {
3394            input = {
3395                let mut decl = new_component_decl();
3396                decl.program = Some(fdecl::Program {
3397                    runner: Some("runner".to_string()),
3398                    info: None,
3399                    ..Default::default()
3400                });
3401                decl
3402            },
3403            result = Err(ErrorList::new(vec![
3404                Error::missing_field(DeclType::Program, "info")
3405            ])),
3406        },
3407        test_validate_uses_invalid_identifiers => {
3408            input = {
3409                let mut decl = new_component_decl();
3410                decl.uses = Some(vec![
3411                    fdecl::Use::Service(fdecl::UseService {
3412                        source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
3413                            name: "^bad".to_string(),
3414                        })),
3415                        source_name: Some("foo/".to_string()),
3416                        target_path: Some("a/foo".to_string()),
3417                        dependency_type: Some(fdecl::DependencyType::Strong),
3418                        ..Default::default()
3419                    }),
3420                    fdecl::Use::Protocol(fdecl::UseProtocol {
3421                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3422                            name: "^bad".to_string(),
3423                            collection: None,
3424                        })),
3425                        source_name: Some("foo/".to_string()),
3426                        target_path: Some("b/foo".to_string()),
3427                        dependency_type: Some(fdecl::DependencyType::Strong),
3428                        ..Default::default()
3429                    }),
3430                    fdecl::Use::Directory(fdecl::UseDirectory {
3431                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3432                            name: "^bad".to_string(),
3433                            collection: None,
3434                        })),
3435                        source_name: Some("foo/".to_string()),
3436                        target_path: Some("c".to_string()),
3437                        rights: Some(fio::Operations::CONNECT),
3438                        subdir: Some("/foo".to_string()),
3439                        dependency_type: Some(fdecl::DependencyType::Strong),
3440                        ..Default::default()
3441                    }),
3442                    fdecl::Use::Storage(fdecl::UseStorage {
3443                        source_name: Some("foo/".to_string()),
3444                        target_path: Some("d".to_string()),
3445                        ..Default::default()
3446                    }),
3447                    fdecl::Use::EventStream(fdecl::UseEventStream {
3448                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3449                            name: "^bad".to_string(),
3450                            collection: None,
3451                        })),
3452                        source_name: Some("foo/".to_string()),
3453                        target_path: Some("e".to_string()),
3454                        ..Default::default()
3455                    }),
3456                    fdecl::Use::Runner(fdecl::UseRunner {
3457                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3458                            name: "^bad".to_string(),
3459                            collection: None,
3460                        })),
3461                        source_name: Some("foo/".to_string()),
3462                        ..Default::default()
3463                    }),
3464                ]);
3465                decl
3466            },
3467            result = Err(ErrorList::new(vec![
3468                Error::invalid_field(DeclType::UseService, "source.capability.name"),
3469                Error::invalid_field(DeclType::UseService, "source_name"),
3470                Error::invalid_field(DeclType::UseService, "target_path"),
3471                Error::invalid_field(DeclType::UseProtocol, "source.child.name"),
3472                Error::invalid_field(DeclType::UseProtocol, "source_name"),
3473                Error::invalid_field(DeclType::UseProtocol, "target_path"),
3474                Error::invalid_field(DeclType::UseDirectory, "source.child.name"),
3475                Error::invalid_field(DeclType::UseDirectory, "source_name"),
3476                Error::invalid_field(DeclType::UseDirectory, "target_path"),
3477                Error::invalid_field(DeclType::UseDirectory, "subdir"),
3478                Error::invalid_field(DeclType::UseStorage, "source_name"),
3479                Error::invalid_field(DeclType::UseStorage, "target_path"),
3480                Error::invalid_field(DeclType::UseEventStream, "source.child.name"),
3481                Error::invalid_field(DeclType::UseEventStream, "source_name"),
3482                Error::invalid_field(DeclType::UseEventStream, "target_path"),
3483                Error::invalid_field(DeclType::UseRunner, "source.child.name"),
3484                Error::invalid_field(DeclType::UseRunner, "source_name"),
3485            ])),
3486        },
3487        test_validate_uses_missing_source => {
3488            input = {
3489                fdecl::Component {
3490                    uses: Some(vec![
3491                        fdecl::Use::Protocol(fdecl::UseProtocol {
3492                            dependency_type: Some(fdecl::DependencyType::Strong),
3493                            source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
3494                                name: "this-storage-doesnt-exist".to_string(),
3495                            })),
3496                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3497                            target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3498                            ..Default::default()
3499                        })
3500                    ]),
3501                    ..new_component_decl()
3502                }
3503            },
3504            result = Err(ErrorList::new(vec![
3505                Error::invalid_capability(DeclType::UseProtocol, "source", "this-storage-doesnt-exist"),
3506            ])),
3507        },
3508        test_validate_uses_invalid_child => {
3509            input = {
3510                fdecl::Component {
3511                    uses: Some(vec![
3512                        fdecl::Use::Protocol(fdecl::UseProtocol {
3513                            dependency_type: Some(fdecl::DependencyType::Strong),
3514                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3515                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3516                            target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3517                            ..Default::default()
3518                        }),
3519                        fdecl::Use::Service(fdecl::UseService {
3520                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3521                            source_name: Some("service_name".to_string()),
3522                            target_path: Some("/svc/service_name".to_string()),
3523                            dependency_type: Some(fdecl::DependencyType::Strong),
3524                            ..Default::default()
3525                        }),
3526                        fdecl::Use::Directory(fdecl::UseDirectory {
3527                            dependency_type: Some(fdecl::DependencyType::Strong),
3528                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3529                            source_name: Some("DirectoryName".to_string()),
3530                            target_path: Some("/data/DirectoryName".to_string()),
3531                            rights: Some(fio::Operations::CONNECT),
3532                            subdir: None,
3533                            ..Default::default()
3534                        }),
3535                        fdecl::Use::Runner(fdecl::UseRunner {
3536                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3537                            source_name: Some("RunnerName".to_string()),
3538                            ..Default::default()
3539                        }),
3540                    ]),
3541                    ..new_component_decl()
3542                }
3543            },
3544            result = Err(ErrorList::new(vec![
3545                Error::invalid_child(DeclType::UseProtocol, "source", "no-such-child"),
3546                Error::invalid_child(DeclType::UseService, "source", "no-such-child"),
3547                Error::invalid_child(DeclType::UseDirectory, "source", "no-such-child"),
3548                Error::invalid_child(DeclType::UseRunner, "source", "no-such-child"),
3549            ])),
3550        },
3551        test_validate_uses_invalid_capability_from_self => {
3552            input = {
3553                let mut decl = new_component_decl();
3554                decl.uses = Some(vec![
3555                    fdecl::Use::Service(fdecl::UseService {
3556                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3557                        source_name: Some("fuchsia.some.library.SomeService".into()),
3558                        target_path: Some("/svc/foo".into()),
3559                        dependency_type: Some(fdecl::DependencyType::Strong),
3560                        ..Default::default()
3561                    }),
3562                    fdecl::Use::Protocol(fdecl::UseProtocol {
3563                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3564                        source_name: Some("fuchsia.some.library.SomeProtocol".into()),
3565                        target_path: Some("/svc/bar".into()),
3566                        dependency_type: Some(fdecl::DependencyType::Strong),
3567                        ..Default::default()
3568                    }),
3569                    fdecl::Use::Directory(fdecl::UseDirectory {
3570                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3571                        source_name: Some("dir".into()),
3572                        target_path: Some("/assets".into()),
3573                        dependency_type: Some(fdecl::DependencyType::Strong),
3574                        rights: Some(fio::Operations::CONNECT),
3575                        ..Default::default()
3576                    }),
3577                    fdecl::Use::Runner(fdecl::UseRunner {
3578                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3579                        source_name: Some("source_elf".into()),
3580                        ..Default::default()
3581                    }),
3582                    fdecl::Use::Config(fdecl::UseConfiguration {
3583                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3584                        source_name: Some("source_config".into()),
3585                        target_name: Some("config".into()),
3586                        type_: Some(fdecl::ConfigType {
3587                            layout: fdecl::ConfigTypeLayout::Bool,
3588                            parameters: Some(Vec::new()),
3589                            constraints: Vec::new(),
3590                        }),
3591                        ..Default::default()
3592                    }),
3593                    fdecl::Use::Protocol(fdecl::UseProtocol {
3594                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3595                        source_name: Some("fuchsia.some.library.SomeProtocol".into()),
3596                        source_dictionary: Some("dict/inner".into()),
3597                        target_path: Some("/svc/baz".into()),
3598                        dependency_type: Some(fdecl::DependencyType::Strong),
3599                        ..Default::default()
3600                    }),
3601                ]);
3602                decl
3603            },
3604            result = Err(ErrorList::new(vec![
3605                Error::invalid_capability(
3606                    DeclType::UseService,
3607                    "source",
3608                    "fuchsia.some.library.SomeService"),
3609                Error::invalid_capability(
3610                    DeclType::UseProtocol,
3611                    "source",
3612                    "fuchsia.some.library.SomeProtocol"),
3613                Error::invalid_capability(DeclType::UseDirectory, "source", "dir"),
3614                Error::invalid_capability(DeclType::UseRunner, "source", "source_elf"),
3615                Error::invalid_capability(DeclType::UseConfiguration, "source", "source_config"),
3616                Error::invalid_capability(DeclType::UseProtocol, "source", "dict"),
3617            ])),
3618        },
3619        test_validate_use_from_child_offer_to_child_strong_cycle => {
3620            input = {
3621                fdecl::Component {
3622                    capabilities: Some(vec![
3623                        fdecl::Capability::Service(fdecl::Service {
3624                            name: Some("a".to_string()),
3625                            source_path: Some("/a".to_string()),
3626                            ..Default::default()
3627                        })]),
3628                    uses: Some(vec![
3629                        fdecl::Use::Protocol(fdecl::UseProtocol {
3630                            dependency_type: Some(fdecl::DependencyType::Strong),
3631                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3632                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3633                            target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3634                            ..Default::default()
3635                        }),
3636                        fdecl::Use::Service(fdecl::UseService {
3637                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3638                            source_name: Some("service_name".to_string()),
3639                            target_path: Some("/svc/service_name".to_string()),
3640                            dependency_type: Some(fdecl::DependencyType::Strong),
3641                            ..Default::default()
3642                        }),
3643                        fdecl::Use::Directory(fdecl::UseDirectory {
3644                            dependency_type: Some(fdecl::DependencyType::Strong),
3645                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3646                            source_name: Some("DirectoryName".to_string()),
3647                            target_path: Some("/data/DirectoryName".to_string()),
3648                            rights: Some(fio::Operations::CONNECT),
3649                            subdir: None,
3650                            ..Default::default()
3651                        }),
3652                    ]),
3653                    offers: Some(vec![
3654                        fdecl::Offer::Service(fdecl::OfferService {
3655                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3656                            source_name: Some("a".to_string()),
3657                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
3658                            target_name: Some("a".to_string()),
3659                            ..Default::default()
3660                        })
3661                    ]),
3662                    children: Some(vec![
3663                        fdecl::Child {
3664                            name: Some("child".to_string()),
3665                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3666                            startup: Some(fdecl::StartupMode::Lazy),
3667                            on_terminate: None,
3668                            ..Default::default()
3669                        }
3670                    ]),
3671                    ..new_component_decl()
3672                }
3673            },
3674            result = Err(ErrorList::new(vec![
3675                Error::dependency_cycle("{{self -> child child -> self}}".to_string()),
3676            ])),
3677        },
3678        test_validate_use_from_child_storage_no_cycle => {
3679            input = {
3680                fdecl::Component {
3681                    capabilities: Some(vec![
3682                        fdecl::Capability::Storage(fdecl::Storage {
3683                            name: Some("cdata".to_string()),
3684                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None } )),
3685                            backing_dir: Some("minfs".to_string()),
3686                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
3687                            ..Default::default()
3688                        }),
3689                        fdecl::Capability::Storage(fdecl::Storage {
3690                            name: Some("pdata".to_string()),
3691                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
3692                            backing_dir: Some("minfs".to_string()),
3693                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
3694                            ..Default::default()
3695                        }),
3696                    ]),
3697                    uses: Some(vec![
3698                        fdecl::Use::Protocol(fdecl::UseProtocol {
3699                            dependency_type: Some(fdecl::DependencyType::Strong),
3700                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child1".to_string(), collection: None})),
3701                            source_name: Some("a".to_string()),
3702                            target_path: Some("/svc/a".to_string()),
3703                            ..Default::default()
3704                        }),
3705                    ]),
3706                    offers: Some(vec![
3707                        fdecl::Offer::Storage(fdecl::OfferStorage {
3708                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3709                            source_name: Some("cdata".to_string()),
3710                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3711                            target_name: Some("cdata".to_string()),
3712                            ..Default::default()
3713                        }),
3714                        fdecl::Offer::Storage(fdecl::OfferStorage {
3715                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3716                            source_name: Some("pdata".to_string()),
3717                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3718                            target_name: Some("pdata".to_string()),
3719                            ..Default::default()
3720                        }),
3721                    ]),
3722                    children: Some(vec![
3723                        fdecl::Child {
3724                            name: Some("child1".to_string()),
3725                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3726                            startup: Some(fdecl::StartupMode::Lazy),
3727                            on_terminate: None,
3728                            ..Default::default()
3729                        },
3730                        fdecl::Child {
3731                            name: Some("child2".to_string()),
3732                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
3733                            startup: Some(fdecl::StartupMode::Lazy),
3734                            on_terminate: None,
3735                            ..Default::default()
3736                        }
3737                    ]),
3738                    ..new_component_decl()
3739                }
3740            },
3741            result = Ok(()),
3742        },
3743        test_validate_use_from_child_storage_cycle => {
3744            input = {
3745                fdecl::Component {
3746                    capabilities: Some(vec![
3747                        fdecl::Capability::Storage(fdecl::Storage {
3748                            name: Some("data".to_string()),
3749                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
3750                            backing_dir: Some("minfs".to_string()),
3751                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
3752                            ..Default::default()
3753                        }),
3754                    ]),
3755                    uses: Some(vec![
3756                        fdecl::Use::Protocol(fdecl::UseProtocol {
3757                            dependency_type: Some(fdecl::DependencyType::Strong),
3758                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3759                            source_name: Some("a".to_string()),
3760                            target_path: Some("/svc/a".to_string()),
3761                            ..Default::default()
3762                        }),
3763                    ]),
3764                    offers: Some(vec![
3765                        fdecl::Offer::Storage(fdecl::OfferStorage {
3766                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3767                            source_name: Some("data".to_string()),
3768                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
3769                            target_name: Some("data".to_string()),
3770                            ..Default::default()
3771                        }),
3772                    ]),
3773                    children: Some(vec![
3774                        fdecl::Child {
3775                            name: Some("child".to_string()),
3776                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3777                            startup: Some(fdecl::StartupMode::Lazy),
3778                            on_terminate: None,
3779                            ..Default::default()
3780                        },
3781                    ]),
3782                    ..new_component_decl()
3783                }
3784            },
3785            result = Err(ErrorList::new(vec![
3786                Error::dependency_cycle("{{self -> capability data -> child child -> self}}".to_string()),
3787            ])),
3788        },
3789        test_validate_storage_strong_cycle_between_children => {
3790            input = {
3791                fdecl::Component {
3792                    capabilities: Some(vec![
3793                        fdecl::Capability::Storage(fdecl::Storage {
3794                            name: Some("data".to_string()),
3795                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None } )),
3796                            backing_dir: Some("minfs".to_string()),
3797                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
3798                            ..Default::default()
3799                        })
3800                    ]),
3801                    offers: Some(vec![
3802                        fdecl::Offer::Storage(fdecl::OfferStorage {
3803                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3804                            source_name: Some("data".to_string()),
3805                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
3806                            target_name: Some("data".to_string()),
3807                            ..Default::default()
3808                        }),
3809                        fdecl::Offer::Service(fdecl::OfferService {
3810                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
3811                            source_name: Some("a".to_string()),
3812                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3813                            target_name: Some("a".to_string()),
3814                            ..Default::default()
3815                        }),
3816                    ]),
3817                    children: Some(vec![
3818                        fdecl::Child {
3819                            name: Some("child1".to_string()),
3820                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3821                            startup: Some(fdecl::StartupMode::Lazy),
3822                            on_terminate: None,
3823                            ..Default::default()
3824                        },
3825                        fdecl::Child {
3826                            name: Some("child2".to_string()),
3827                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
3828                            startup: Some(fdecl::StartupMode::Lazy),
3829                            on_terminate: None,
3830                            ..Default::default()
3831                        }
3832                    ]),
3833                    ..new_component_decl()
3834                }
3835            },
3836            result = Err(ErrorList::new(vec![
3837                Error::dependency_cycle("{{child child1 -> capability data -> child child2 -> child child1}}".to_string()),
3838            ])),
3839        },
3840        test_validate_strong_cycle_between_children_through_environment_debug => {
3841            input = {
3842                fdecl::Component {
3843                    environments: Some(vec![
3844                        fdecl::Environment {
3845                            name: Some("env".to_string()),
3846                            extends: Some(fdecl::EnvironmentExtends::Realm),
3847                            debug_capabilities: Some(vec![
3848                                fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
3849                                    source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3850                                    source_name: Some("fuchsia.foo.Bar".to_string()),
3851                                    target_name: Some("fuchsia.foo.Bar".to_string()),
3852                                    ..Default::default()
3853                                }),
3854                            ]),
3855                            ..Default::default()
3856                        },
3857                    ]),
3858                    offers: Some(vec![
3859                        fdecl::Offer::Service(fdecl::OfferService {
3860                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
3861                            source_name: Some("a".to_string()),
3862                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3863                            target_name: Some("a".to_string()),
3864                            ..Default::default()
3865                        }),
3866                    ]),
3867                    children: Some(vec![
3868                        fdecl::Child {
3869                            name: Some("child1".to_string()),
3870                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3871                            startup: Some(fdecl::StartupMode::Lazy),
3872                            on_terminate: None,
3873                            ..Default::default()
3874                        },
3875                        fdecl::Child {
3876                            name: Some("child2".to_string()),
3877                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
3878                            startup: Some(fdecl::StartupMode::Lazy),
3879                            environment: Some("env".to_string()),
3880                            on_terminate: None,
3881                            ..Default::default()
3882                        }
3883                    ]),
3884                    ..new_component_decl()
3885                }
3886            },
3887            result = Err(ErrorList::new(vec![
3888                Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}".to_string()),
3889            ])),
3890        },
3891        test_validate_strong_cycle_between_children_through_environment_runner => {
3892            input = {
3893                fdecl::Component {
3894                    environments: Some(vec![
3895                        fdecl::Environment {
3896                            name: Some("env".to_string()),
3897                            extends: Some(fdecl::EnvironmentExtends::Realm),
3898                            runners: Some(vec![
3899                                fdecl::RunnerRegistration {
3900                                    source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3901                                    source_name: Some("coff".to_string()),
3902                                    target_name: Some("coff".to_string()),
3903                                    ..Default::default()
3904                                }
3905                            ]),
3906                            ..Default::default()
3907                        },
3908                    ]),
3909                    offers: Some(vec![
3910                        fdecl::Offer::Service(fdecl::OfferService {
3911                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
3912                            source_name: Some("a".to_string()),
3913                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3914                            target_name: Some("a".to_string()),
3915                            ..Default::default()
3916                        }),
3917                    ]),
3918                    children: Some(vec![
3919                        fdecl::Child {
3920                            name: Some("child1".to_string()),
3921                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3922                            startup: Some(fdecl::StartupMode::Lazy),
3923                            on_terminate: None,
3924                            ..Default::default()
3925                        },
3926                        fdecl::Child {
3927                            name: Some("child2".to_string()),
3928                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
3929                            startup: Some(fdecl::StartupMode::Lazy),
3930                            environment: Some("env".to_string()),
3931                            on_terminate: None,
3932                            ..Default::default()
3933                        }
3934                    ]),
3935                    ..new_component_decl()
3936                }
3937            },
3938            result = Err(ErrorList::new(vec![
3939                Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}".to_string()),
3940            ])),
3941        },
3942        test_validate_strong_cycle_between_children_through_environment_resolver => {
3943            input = {
3944                fdecl::Component {
3945                    environments: Some(vec![
3946                        fdecl::Environment {
3947                            name: Some("env".to_string()),
3948                            extends: Some(fdecl::EnvironmentExtends::Realm),
3949                            resolvers: Some(vec![
3950                                fdecl::ResolverRegistration {
3951                                    resolver: Some("gopher".to_string()),
3952                                    source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3953                                    scheme: Some("gopher".to_string()),
3954                                    ..Default::default()
3955                                }
3956                            ]),
3957                            ..Default::default()
3958                        },
3959                    ]),
3960                    offers: Some(vec![
3961                        fdecl::Offer::Service(fdecl::OfferService {
3962                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
3963                            source_name: Some("a".to_string()),
3964                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3965                            target_name: Some("a".to_string()),
3966                            ..Default::default()
3967                        }),
3968                    ]),
3969                    children: Some(vec![
3970                        fdecl::Child {
3971                            name: Some("child1".to_string()),
3972                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3973                            startup: Some(fdecl::StartupMode::Lazy),
3974                            on_terminate: None,
3975                            ..Default::default()
3976                        },
3977                        fdecl::Child {
3978                            name: Some("child2".to_string()),
3979                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
3980                            startup: Some(fdecl::StartupMode::Lazy),
3981                            environment: Some("env".to_string()),
3982                            on_terminate: None,
3983                            ..Default::default()
3984                        }
3985                    ]),
3986                    ..new_component_decl()
3987                }
3988            },
3989            result = Err(ErrorList::new(vec![
3990                Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}".to_string()),
3991            ])),
3992        },
3993        test_validate_strong_cycle_between_self_and_two_children => {
3994            input = {
3995                fdecl::Component {
3996                    capabilities: Some(vec![
3997                        fdecl::Capability::Protocol(fdecl::Protocol {
3998                            name: Some("fuchsia.foo.Bar".to_string()),
3999                            source_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4000                            ..Default::default()
4001                        })
4002                    ]),
4003                    offers: Some(vec![
4004                        fdecl::Offer::Protocol(fdecl::OfferProtocol {
4005                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4006                            source_name: Some("fuchsia.foo.Bar".to_string()),
4007                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4008                            target_name: Some("fuchsia.foo.Bar".to_string()),
4009                            dependency_type: Some(fdecl::DependencyType::Strong),
4010                            ..Default::default()
4011                        }),
4012                        fdecl::Offer::Protocol(fdecl::OfferProtocol {
4013                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4014                            source_name: Some("fuchsia.bar.Baz".to_string()),
4015                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4016                            target_name: Some("fuchsia.bar.Baz".to_string()),
4017                            dependency_type: Some(fdecl::DependencyType::Strong),
4018                            ..Default::default()
4019                        }),
4020                    ]),
4021                    uses: Some(vec![
4022                        fdecl::Use::Protocol(fdecl::UseProtocol {
4023                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child2".to_string(), collection: None})),
4024                            source_name: Some("fuchsia.baz.Foo".to_string()),
4025                            target_path: Some("/svc/fuchsia.baz.Foo".to_string()),
4026                            dependency_type: Some(fdecl::DependencyType::Strong),
4027                            ..Default::default()
4028                        }),
4029                    ]),
4030                    children: Some(vec![
4031                        fdecl::Child {
4032                            name: Some("child1".to_string()),
4033                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4034                            startup: Some(fdecl::StartupMode::Lazy),
4035                            on_terminate: None,
4036                            ..Default::default()
4037                        },
4038                        fdecl::Child {
4039                            name: Some("child2".to_string()),
4040                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4041                            startup: Some(fdecl::StartupMode::Lazy),
4042                            on_terminate: None,
4043                            ..Default::default()
4044                        }
4045                    ]),
4046                    ..new_component_decl()
4047                }
4048            },
4049            result = Err(ErrorList::new(vec![
4050                Error::dependency_cycle("{{self -> child child1 -> child child2 -> self}}".to_string()),
4051            ])),
4052        },
4053        test_validate_strong_cycle_with_self_storage => {
4054            input = {
4055                fdecl::Component {
4056                    capabilities: Some(vec![
4057                        fdecl::Capability::Storage(fdecl::Storage {
4058                            name: Some("data".to_string()),
4059                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4060                            backing_dir: Some("minfs".to_string()),
4061                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4062                            ..Default::default()
4063                        }),
4064                        fdecl::Capability::Directory(fdecl::Directory {
4065                            name: Some("minfs".to_string()),
4066                            source_path: Some("/minfs".to_string()),
4067                            rights: Some(fio::RW_STAR_DIR),
4068                            ..Default::default()
4069                        }),
4070                    ]),
4071                    offers: Some(vec![
4072                        fdecl::Offer::Storage(fdecl::OfferStorage {
4073                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4074                            source_name: Some("data".to_string()),
4075                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4076                            target_name: Some("data".to_string()),
4077                            ..Default::default()
4078                        }),
4079                    ]),
4080                    uses: Some(vec![
4081                        fdecl::Use::Protocol(fdecl::UseProtocol {
4082                            dependency_type: Some(fdecl::DependencyType::Strong),
4083                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4084                            source_name: Some("fuchsia.foo.Bar".to_string()),
4085                            target_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4086                            ..Default::default()
4087                        }),
4088                    ]),
4089                    children: Some(vec![
4090                        fdecl::Child {
4091                            name: Some("child".to_string()),
4092                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4093                            startup: Some(fdecl::StartupMode::Lazy),
4094                            ..Default::default()
4095                        },
4096                    ]),
4097                    ..new_component_decl()
4098                }
4099            },
4100            result = Err(ErrorList::new(vec![
4101                Error::dependency_cycle("{{self -> capability data -> child child -> self}}".to_string()),
4102            ])),
4103        },
4104        test_validate_strong_cycle_with_self_storage_admin_protocol => {
4105            input = {
4106                fdecl::Component {
4107                    capabilities: Some(vec![
4108                        fdecl::Capability::Storage(fdecl::Storage {
4109                            name: Some("data".to_string()),
4110                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4111                            backing_dir: Some("minfs".to_string()),
4112                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4113                            ..Default::default()
4114                        }),
4115                        fdecl::Capability::Directory(fdecl::Directory {
4116                            name: Some("minfs".to_string()),
4117                            source_path: Some("/minfs".to_string()),
4118                            rights: Some(fio::RW_STAR_DIR),
4119                            ..Default::default()
4120                        }),
4121                    ]),
4122                    offers: Some(vec![
4123                        fdecl::Offer::Protocol(fdecl::OfferProtocol {
4124                            source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef { name: "data".to_string() })),
4125                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4126                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4127                            target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4128                            dependency_type: Some(fdecl::DependencyType::Strong),
4129                            ..Default::default()
4130                        }),
4131                    ]),
4132                    uses: Some(vec![
4133                        fdecl::Use::Protocol(fdecl::UseProtocol {
4134                            dependency_type: Some(fdecl::DependencyType::Strong),
4135                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4136                            source_name: Some("fuchsia.foo.Bar".to_string()),
4137                            target_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4138                            ..Default::default()
4139                        }),
4140                    ]),
4141                    children: Some(vec![
4142                        fdecl::Child {
4143                            name: Some("child".to_string()),
4144                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4145                            startup: Some(fdecl::StartupMode::Lazy),
4146                            ..Default::default()
4147                        },
4148                    ]),
4149                    ..new_component_decl()
4150                }
4151            },
4152            result = Err(ErrorList::new(vec![
4153                Error::dependency_cycle("{{self -> capability data -> child child -> self}}".to_string()),
4154            ])),
4155        },
4156        test_validate_strong_cycle_with_dictionary => {
4157            input = fdecl::Component {
4158                offers: Some(vec![
4159                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
4160                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4161                        source_name: Some("dict".into()),
4162                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4163                            name: "a".into(),
4164                            collection: None,
4165                        })),
4166                        target_name: Some("dict".into()),
4167                        dependency_type: Some(fdecl::DependencyType::Strong),
4168                        ..Default::default()
4169                    }),
4170                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4171                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4172                            name: "b".into(),
4173                            collection: None,
4174                        })),
4175                        source_name: Some("1".into()),
4176                        target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4177                            name: "dict".into(),
4178                        })),
4179                        target_name: Some("1".into()),
4180                        dependency_type: Some(fdecl::DependencyType::Strong),
4181                        ..Default::default()
4182                    }),
4183                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4184                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4185                            name: "a".into(),
4186                            collection: None,
4187                        })),
4188                        source_name: Some("2".into()),
4189                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4190                            name: "b".into(),
4191                            collection: None,
4192                        })),
4193                        target_name: Some("2".into()),
4194                        dependency_type: Some(fdecl::DependencyType::Strong),
4195                        ..Default::default()
4196                    }),
4197                ]),
4198                children: Some(vec![
4199                    fdecl::Child {
4200                        name: Some("a".into()),
4201                        url: Some("fuchsia-pkg://child".into()),
4202                        startup: Some(fdecl::StartupMode::Lazy),
4203                        ..Default::default()
4204                    },
4205                    fdecl::Child {
4206                        name: Some("b".into()),
4207                        url: Some("fuchsia-pkg://child".into()),
4208                        startup: Some(fdecl::StartupMode::Lazy),
4209                        ..Default::default()
4210                    },
4211                ]),
4212                capabilities: Some(vec![
4213                    fdecl::Capability::Dictionary(fdecl::Dictionary {
4214                        name: Some("dict".into()),
4215                        ..Default::default()
4216                    }),
4217                ]),
4218                ..Default::default()
4219            },
4220            result = Err(ErrorList::new(vec![
4221                Error::dependency_cycle("{{child a -> child b -> capability dict -> child a}}".to_string()),
4222            ])),
4223        },
4224        test_validate_strong_cycle_with_dictionary_indirect => {
4225            input = fdecl::Component {
4226                offers: Some(vec![
4227                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4228                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4229                        source_name: Some("3".into()),
4230                        source_dictionary: Some("dict".into()),
4231                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4232                            name: "a".into(),
4233                            collection: None,
4234                        })),
4235                        target_name: Some("3".into()),
4236                        dependency_type: Some(fdecl::DependencyType::Strong),
4237                        ..Default::default()
4238                    }),
4239                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4240                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4241                            name: "b".into(),
4242                            collection: None,
4243                        })),
4244                        source_name: Some("1".into()),
4245                        target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4246                            name: "dict".into(),
4247                        })),
4248                        target_name: Some("1".into()),
4249                        dependency_type: Some(fdecl::DependencyType::Strong),
4250                        ..Default::default()
4251                    }),
4252                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4253                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4254                            name: "a".into(),
4255                            collection: None,
4256                        })),
4257                        source_name: Some("2".into()),
4258                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4259                            name: "b".into(),
4260                            collection: None,
4261                        })),
4262                        target_name: Some("2".into()),
4263                        dependency_type: Some(fdecl::DependencyType::Strong),
4264                        ..Default::default()
4265                    }),
4266                ]),
4267                children: Some(vec![
4268                    fdecl::Child {
4269                        name: Some("a".into()),
4270                        url: Some("fuchsia-pkg://child".into()),
4271                        startup: Some(fdecl::StartupMode::Lazy),
4272                        ..Default::default()
4273                    },
4274                    fdecl::Child {
4275                        name: Some("b".into()),
4276                        url: Some("fuchsia-pkg://child".into()),
4277                        startup: Some(fdecl::StartupMode::Lazy),
4278                        ..Default::default()
4279                    },
4280                ]),
4281                capabilities: Some(vec![
4282                    fdecl::Capability::Dictionary(fdecl::Dictionary {
4283                        name: Some("dict".into()),
4284                        ..Default::default()
4285                    }),
4286                ]),
4287                ..Default::default()
4288            },
4289            result = Err(ErrorList::new(vec![
4290                Error::dependency_cycle("{{child a -> child b -> capability dict -> child a}}".to_string()),
4291            ])),
4292        },
4293        test_validate_use_from_child_offer_to_child_weak_cycle => {
4294            input = {
4295                fdecl::Component {
4296                    capabilities: Some(vec![
4297                        fdecl::Capability::Service(fdecl::Service {
4298                            name: Some("a".to_string()),
4299                            source_path: Some("/a".to_string()),
4300                            ..Default::default()
4301                        })]),
4302                    uses: Some(vec![
4303                        fdecl::Use::Protocol(fdecl::UseProtocol {
4304                            dependency_type: Some(fdecl::DependencyType::Weak),
4305                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4306                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4307                            target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
4308                            ..Default::default()
4309                        }),
4310                        fdecl::Use::Service(fdecl::UseService {
4311                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4312                            source_name: Some("service_name".to_string()),
4313                            target_path: Some("/svc/service_name".to_string()),
4314                            dependency_type: Some(fdecl::DependencyType::Weak),
4315                            ..Default::default()
4316                        }),
4317                        fdecl::Use::Directory(fdecl::UseDirectory {
4318                            dependency_type: Some(fdecl::DependencyType::Weak),
4319                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4320                            source_name: Some("DirectoryName".to_string()),
4321                            target_path: Some("/data/DirectoryName".to_string()),
4322                            rights: Some(fio::Operations::CONNECT),
4323                            subdir: None,
4324                            ..Default::default()
4325                        }),
4326                    ]),
4327                    offers: Some(vec![
4328                        fdecl::Offer::Service(fdecl::OfferService {
4329                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4330                            source_name: Some("a".to_string()),
4331                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4332                            target_name: Some("a".to_string()),
4333                            ..Default::default()
4334                        })
4335                    ]),
4336                    children: Some(vec![
4337                        fdecl::Child {
4338                            name: Some("child".to_string()),
4339                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4340                            startup: Some(fdecl::StartupMode::Lazy),
4341                            on_terminate: None,
4342                            ..Default::default()
4343                        }
4344                    ]),
4345                    ..new_component_decl()
4346                }
4347            },
4348            result = Ok(()),
4349        },
4350        test_validate_expose_from_self_to_framework_and_parent => {
4351            input = {
4352                fdecl::Component {
4353                    capabilities: Some(vec![
4354                        fdecl::Capability::Protocol(fdecl::Protocol {
4355                            name: Some("a".to_string()),
4356                            source_path: Some("/a".to_string()),
4357                            ..Default::default()
4358                        }),
4359                    ]),
4360                    exposes: Some(vec![
4361                        fdecl::Expose::Protocol(fdecl::ExposeProtocol {
4362                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4363                            source_name: Some("a".to_string()),
4364                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4365                            target_name: Some("a".to_string()),
4366                            ..Default::default()
4367                        }),
4368                        fdecl::Expose::Protocol(fdecl::ExposeProtocol {
4369                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4370                            source_name: Some("a".to_string()),
4371                            target: Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})),
4372                            target_name: Some("a".to_string()),
4373                            ..Default::default()
4374                        }),
4375                    ]),
4376                    ..new_component_decl()
4377                }
4378            },
4379            result = Ok(()),
4380        },
4381        test_validate_use_from_not_child_weak => {
4382            input = {
4383                fdecl::Component {
4384                    uses: Some(vec![
4385                        fdecl::Use::Protocol(fdecl::UseProtocol {
4386                            dependency_type: Some(fdecl::DependencyType::Weak),
4387                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4388                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4389                            target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
4390                            ..Default::default()
4391                        }),
4392                    ]),
4393                    ..new_component_decl()
4394                }
4395            },
4396            result = Err(ErrorList::new(vec![
4397                Error::invalid_field(DeclType::UseProtocol, "dependency_type"),
4398            ])),
4399        },
4400        test_validate_event_stream_offer_valid_decls => {
4401            input = {
4402                let mut decl = new_component_decl();
4403                decl.offers = Some(vec![
4404                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4405                        source_name: Some("stopped".to_string()),
4406                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4407                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4408                        target_name: Some("stopped".to_string()),
4409                        ..Default::default()
4410                    }),
4411                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4412                        source_name: Some("started".to_string()),
4413                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4414                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4415                        target_name: Some("started".to_string()),
4416                        ..Default::default()
4417                    }),
4418                ]);
4419                decl.children = Some(vec![fdecl::Child{
4420                    name: Some("test".to_string()),
4421                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4422                    startup: Some(fdecl::StartupMode::Lazy),
4423                    on_terminate: None,
4424                    environment: None,
4425                    ..Default::default()
4426                },
4427                fdecl::Child{
4428                    name: Some("test2".to_string()),
4429                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4430                    startup: Some(fdecl::StartupMode::Lazy),
4431                    on_terminate: None,
4432                    environment: None,
4433                    ..Default::default()
4434                }
4435                ]);
4436                decl
4437            },
4438            result = Ok(()),
4439        },
4440        test_validate_event_stream_offer_to_framework_invalid => {
4441            input = {
4442                let mut decl = new_component_decl();
4443                decl.offers = Some(vec![
4444                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4445                        source_name: Some("stopped".to_string()),
4446                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4447                        target: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
4448                        target_name: Some("stopped".to_string()),
4449                        ..Default::default()
4450                    }),
4451                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4452                        source_name: Some("started".to_string()),
4453                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4454                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4455                        target_name: Some("started".to_string()),
4456                        ..Default::default()
4457                    }),
4458                ]);
4459                decl.children = Some(vec![fdecl::Child{
4460                    name: Some("test".to_string()),
4461                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4462                    startup: Some(fdecl::StartupMode::Lazy),
4463                    on_terminate: None,
4464                    environment: None,
4465                    ..Default::default()
4466                },
4467                fdecl::Child{
4468                    name: Some("test2".to_string()),
4469                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4470                    startup: Some(fdecl::StartupMode::Lazy),
4471                    on_terminate: None,
4472                    environment: None,
4473                    ..Default::default()
4474                }
4475                ]);
4476                decl
4477            },
4478            result = Err(ErrorList::new(vec![
4479                Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "target".to_string() }),
4480                Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "target".to_string() }),
4481            ])),
4482        },
4483        test_validate_event_stream_offer_to_scope_zero_length_invalid => {
4484            input = {
4485                let mut decl = new_component_decl();
4486                decl.offers = Some(vec![
4487                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4488                        source_name: Some("started".to_string()),
4489                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4490                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4491                        scope: Some(vec![]),
4492                        target_name: Some("started".to_string()),
4493                        ..Default::default()
4494                    }),
4495                ]);
4496                decl.children = Some(vec![fdecl::Child{
4497                    name: Some("test".to_string()),
4498                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4499                    startup: Some(fdecl::StartupMode::Lazy),
4500                    on_terminate: None,
4501                    environment: None,
4502                    ..Default::default()
4503                },
4504                fdecl::Child{
4505                    name: Some("test2".to_string()),
4506                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4507                    startup: Some(fdecl::StartupMode::Lazy),
4508                    on_terminate: None,
4509                    environment: None,
4510                    ..Default::default()
4511                }
4512                ]);
4513                decl
4514            },
4515            result = Err(ErrorList::new(vec![
4516                Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "scope".to_string() }),
4517            ])),
4518        },
4519        test_validate_event_stream_offer_to_scope_framework_invalid => {
4520            input = {
4521                let mut decl = new_component_decl();
4522                decl.offers = Some(vec![
4523                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4524                        source_name: Some("started".to_string()),
4525                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4526                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4527                        scope: Some(vec![fdecl::Ref::Framework(fdecl::FrameworkRef{})]),
4528                        target_name: Some("started".to_string()),
4529                        ..Default::default()
4530                    }),
4531                ]);
4532                decl.children = Some(vec![fdecl::Child{
4533                    name: Some("test".to_string()),
4534                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4535                    startup: Some(fdecl::StartupMode::Lazy),
4536                    on_terminate: None,
4537                    environment: None,
4538                    ..Default::default()
4539                },
4540                fdecl::Child{
4541                    name: Some("test2".to_string()),
4542                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4543                    startup: Some(fdecl::StartupMode::Lazy),
4544                    on_terminate: None,
4545                    environment: None,
4546                    ..Default::default()
4547                }
4548                ]);
4549                decl
4550            },
4551            result = Err(ErrorList::new(vec![
4552                Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "scope".to_string() }),
4553            ])),
4554        },
4555        test_validate_event_stream_offer_to_scope_valid => {
4556            input = {
4557                let mut decl = new_component_decl();
4558                decl.offers = Some(vec![
4559                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4560                        source_name: Some("started".to_string()),
4561                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4562                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4563                        scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
4564                        target_name: Some("started".to_string()),
4565                        ..Default::default()
4566                    }),
4567                ]);
4568                decl.children = Some(vec![fdecl::Child{
4569                    name: Some("test".to_string()),
4570                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4571                    startup: Some(fdecl::StartupMode::Lazy),
4572                    on_terminate: None,
4573                    environment: None,
4574                    ..Default::default()
4575                },
4576                fdecl::Child{
4577                    name: Some("test2".to_string()),
4578                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4579                    startup: Some(fdecl::StartupMode::Lazy),
4580                    on_terminate: None,
4581                    environment: None,
4582                    ..Default::default()
4583                }
4584                ]);
4585                decl
4586            },
4587            result = Ok(()),
4588        },
4589        test_validate_event_stream_offer_to_scope_with_capability_requested => {
4590            input = {
4591                let mut decl = new_component_decl();
4592                decl.offers = Some(vec![
4593                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4594                        source_name: Some("capability_requested".to_string()),
4595                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4596                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4597                        scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
4598                        target_name: Some("started".to_string()),
4599                        ..Default::default()
4600                    }),
4601                ]);
4602                decl.children = Some(vec![fdecl::Child{
4603                    name: Some("test".to_string()),
4604                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4605                    startup: Some(fdecl::StartupMode::Lazy),
4606                    on_terminate: None,
4607                    environment: None,
4608                    ..Default::default()
4609                },
4610                fdecl::Child{
4611                    name: Some("test2".to_string()),
4612                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4613                    startup: Some(fdecl::StartupMode::Lazy),
4614                    on_terminate: None,
4615                    environment: None,
4616                    ..Default::default()
4617                }
4618                ]);
4619                decl
4620            },
4621            result = Ok(()),
4622        },
4623        test_validate_event_stream_offer_with_no_source_name_invalid => {
4624            input = {
4625                let mut decl = new_component_decl();
4626                decl.offers = Some(vec![
4627                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4628                        source_name: None,
4629                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4630                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4631                        scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
4632                        target_name: Some("started".to_string()),
4633                        ..Default::default()
4634                    }),
4635                ]);
4636                decl.children = Some(vec![fdecl::Child{
4637                    name: Some("test".to_string()),
4638                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4639                    startup: Some(fdecl::StartupMode::Lazy),
4640                    on_terminate: None,
4641                    environment: None,
4642                    ..Default::default()
4643                },
4644                fdecl::Child{
4645                    name: Some("test2".to_string()),
4646                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4647                    startup: Some(fdecl::StartupMode::Lazy),
4648                    on_terminate: None,
4649                    environment: None,
4650                    ..Default::default()
4651                }
4652                ]);
4653                decl
4654            },
4655            result = Err(ErrorList::new(vec![
4656                Error::MissingField(DeclField { decl: DeclType::OfferEventStream, field: "source_name".to_string() }),
4657            ])),
4658        },
4659        test_validate_event_stream_offer_invalid_source => {
4660            input = {
4661                let mut decl = new_component_decl();
4662                decl.offers = Some(vec![
4663                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4664                        source_name: Some("stopped".to_string()),
4665                        source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
4666                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4667                        target_name: Some("stopped".to_string()),
4668                        ..Default::default()
4669                    }),
4670                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4671                        source_name: Some("started".to_string()),
4672                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4673                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4674                        target_name: Some("started".to_string()),
4675                        ..Default::default()
4676                    }),
4677                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4678                        source_name: Some("capability_requested".to_string()),
4679                        source: Some(fdecl::Ref::Debug(fdecl::DebugRef{})),
4680                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4681                        target_name: Some("capability_requested".to_string()),
4682                        ..Default::default()
4683                    }),
4684                ]);
4685                decl.children = Some(vec![fdecl::Child{
4686                    name: Some("test".to_string()),
4687                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4688                    startup: Some(fdecl::StartupMode::Lazy),
4689                    on_terminate: None,
4690                    environment: None,
4691                    ..Default::default()
4692                },
4693                fdecl::Child{
4694                    name: Some("test2".to_string()),
4695                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4696                    startup: Some(fdecl::StartupMode::Lazy),
4697                    on_terminate: None,
4698                    environment: None,
4699                    ..Default::default()
4700                }
4701                ]);
4702                decl
4703            },
4704            result = Err(ErrorList::new(vec![
4705                Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "source".to_string() }),
4706            ])),
4707        },
4708
4709        test_validate_event_stream_offer_missing_source => {
4710            input = {
4711                let mut decl = new_component_decl();
4712                decl.offers = Some(vec![
4713                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4714                        source_name: Some("stopped".to_string()),
4715                        source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
4716                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4717                        target_name: Some("stopped".to_string()),
4718                        ..Default::default()
4719                    }),
4720                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4721                        source_name: Some("started".to_string()),
4722                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4723                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4724                        target_name: Some("started".to_string()),
4725                        ..Default::default()
4726                    }),
4727                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4728                        source_name: Some("capability_requested".to_string()),
4729                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4730                        target_name: Some("capability_requested".to_string()),
4731                        ..Default::default()
4732                    }),
4733                ]);
4734                decl.children = Some(vec![fdecl::Child{
4735                    name: Some("test".to_string()),
4736                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4737                    startup: Some(fdecl::StartupMode::Lazy),
4738                    on_terminate: None,
4739                    environment: None,
4740                    ..Default::default()
4741                },
4742                fdecl::Child{
4743                    name: Some("test2".to_string()),
4744                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4745                    startup: Some(fdecl::StartupMode::Lazy),
4746                    on_terminate: None,
4747                    environment: None,
4748                    ..Default::default()
4749                }
4750                ]);
4751                decl
4752            },
4753            result = Err(ErrorList::new(vec![
4754                Error::MissingField(DeclField { decl: DeclType::OfferEventStream, field: "source".to_string() }),
4755            ])),
4756        },
4757        test_validate_event_stream_must_have_target_path => {
4758            input = {
4759                let mut decl = new_component_decl();
4760                decl.uses = Some(vec![
4761                    fdecl::Use::EventStream(fdecl::UseEventStream {
4762                        source_name: Some("bar".to_string()),
4763                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4764                        ..Default::default()
4765                    }),
4766                ]);
4767                decl
4768            },
4769            result = Err(ErrorList::new(vec![
4770                Error::MissingField(DeclField { decl: DeclType::UseEventStream, field: "target_path".to_string() })
4771            ])),
4772        },
4773        test_validate_event_stream_must_have_source_names => {
4774            input = {
4775                let mut decl = new_component_decl();
4776                decl.uses = Some(vec![
4777                    fdecl::Use::EventStream(fdecl::UseEventStream {
4778                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4779                        target_path: Some("/svc/something".to_string()),
4780                        ..Default::default()
4781                    }),
4782                ]);
4783                decl
4784            },
4785            result = Err(ErrorList::new(vec![
4786                Error::MissingField(DeclField { decl: DeclType::UseEventStream, field: "source_name".to_string() })
4787            ])),
4788        },
4789        test_validate_event_stream_scope_must_be_child_or_collection => {
4790            input = {
4791                let mut decl = new_component_decl();
4792                decl.uses = Some(vec![
4793                    fdecl::Use::EventStream(fdecl::UseEventStream {
4794                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4795                        target_path: Some("/svc/something".to_string()),
4796                        source_name: Some("some_source".to_string()),
4797                        scope: Some(vec![fdecl::Ref::Framework(fdecl::FrameworkRef{})]),
4798                        ..Default::default()
4799                    }),
4800                ]);
4801                decl
4802            },
4803            result = Err(ErrorList::new(vec![
4804                Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "scope".to_string() })
4805            ])),
4806        },
4807        test_validate_event_stream_source_must_be_parent_or_child => {
4808            input = {
4809                let mut decl = new_component_decl();
4810                decl.uses = Some(vec![
4811                    fdecl::Use::EventStream(fdecl::UseEventStream {
4812                        source: Some(fdecl::Ref::Debug(fdecl::DebugRef{})),
4813                        target_path: Some("/svc/something".to_string()),
4814                        source_name: Some("some_source".to_string()),
4815                        scope: Some(vec![]),
4816                        ..Default::default()
4817                    }),
4818                    fdecl::Use::EventStream(fdecl::UseEventStream {
4819                        source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
4820                        target_path: Some("/svc/something_else".to_string()),
4821                        source_name: Some("some_source".to_string()),
4822                        scope: Some(vec![]),
4823                        ..Default::default()
4824                    }),
4825                    fdecl::Use::EventStream(fdecl::UseEventStream {
4826                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4827                        target_path: Some("/svc/yet_something_else".to_string()),
4828                        source_name: Some("some_source".to_string()),
4829                        scope: Some(vec![]),
4830                        ..Default::default()
4831                    }),
4832                ]);
4833                decl
4834            },
4835            result = Err(ErrorList::new(vec![
4836                Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() }),
4837                Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() }),
4838                Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() })
4839            ])),
4840        },
4841        test_validate_no_runner => {
4842            input = {
4843                let mut decl = new_component_decl();
4844                decl.program = Some(fdecl::Program {
4845                    runner: None,
4846                    info: Some(fdata::Dictionary {
4847                        entries: None,
4848                        ..Default::default()
4849                    }),
4850                    ..Default::default()
4851                });
4852                decl
4853            },
4854            result = Err(ErrorList::new(vec![
4855                Error::MissingRunner,
4856            ])),
4857        },
4858        test_validate_uses_runner => {
4859            input = {
4860                let mut decl = new_component_decl();
4861                decl.program = Some(fdecl::Program {
4862                    runner: None,
4863                    info: Some(fdata::Dictionary {
4864                        entries: None,
4865                        ..Default::default()
4866                    }),
4867                    ..Default::default()
4868                });
4869                decl.uses = Some(vec![
4870                    fdecl::Use::Runner(fdecl::UseRunner {
4871                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4872                        source_name: Some("runner".to_string()),
4873                        ..Default::default()
4874                    }),
4875                ]);
4876                decl
4877            },
4878            result = Ok(()),
4879        },
4880        test_validate_program_and_uses_runner_match => {
4881            input = {
4882                let mut decl = new_component_decl();
4883                decl.program = Some(fdecl::Program {
4884                    runner: Some("runner".to_string()),
4885                    info: Some(fdata::Dictionary {
4886                        entries: None,
4887                        ..Default::default()
4888                    }),
4889                    ..Default::default()
4890                });
4891                decl.uses = Some(vec![
4892                    fdecl::Use::Runner(fdecl::UseRunner {
4893                        source: Some(fdecl::Ref::Environment(fdecl::EnvironmentRef {})),
4894                        source_name: Some("runner".to_string()),
4895                        ..Default::default()
4896                    }),
4897                ]);
4898                decl
4899            },
4900            result = Ok(()),
4901        },
4902        test_validate_runner_names_conflict => {
4903            input = {
4904                let mut decl = new_component_decl();
4905                decl.program = Some(fdecl::Program {
4906                    runner: Some("runner".to_string()),
4907                    info: Some(fdata::Dictionary {
4908                        entries: None,
4909                        ..Default::default()
4910                    }),
4911                    ..Default::default()
4912                });
4913                decl.uses = Some(vec![
4914                    fdecl::Use::Runner(fdecl::UseRunner {
4915                        source: Some(fdecl::Ref::Environment(fdecl::EnvironmentRef {})),
4916                        source_name: Some("other.runner".to_string()),
4917                        ..Default::default()
4918                    }),
4919                ]);
4920                decl
4921            },
4922            result = Err(ErrorList::new(vec![
4923                Error::ConflictingRunners,
4924            ])),
4925        },
4926        test_validate_uses_runner_not_environement => {
4927            input = {
4928                let mut decl = new_component_decl();
4929                decl.program = Some(fdecl::Program {
4930                    runner: Some("runner".to_string()),
4931                    info: Some(fdata::Dictionary {
4932                        entries: None,
4933                        ..Default::default()
4934                    }),
4935                    ..Default::default()
4936                });
4937                decl.uses = Some(vec![
4938                    fdecl::Use::Runner(fdecl::UseRunner {
4939                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4940                        source_name: Some("runner".to_string()),
4941                        ..Default::default()
4942                    }),
4943                ]);
4944                decl
4945            },
4946            result = Err(ErrorList::new(vec![
4947                Error::ConflictingRunners,
4948            ])),
4949        },
4950        test_validate_uses_long_identifiers => {
4951            input = {
4952                let mut decl = new_component_decl();
4953                decl.program = Some(fdecl::Program {
4954                    runner: Some("elf".to_string()),
4955                    info: Some(fdata::Dictionary {
4956                        entries: None,
4957                        ..Default::default()
4958                    }),
4959                    ..Default::default()
4960                });
4961                decl.uses = Some(vec![
4962                    fdecl::Use::Service(fdecl::UseService {
4963                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4964                        source_name: Some(format!("{}", "a".repeat(256))),
4965                        target_path: Some("/a".repeat(2048)),
4966                        dependency_type: Some(fdecl::DependencyType::Strong),
4967                        ..Default::default()
4968                    }),
4969                    fdecl::Use::Protocol(fdecl::UseProtocol {
4970                        dependency_type: Some(fdecl::DependencyType::Strong),
4971                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4972                        source_name: Some(format!("{}", "a".repeat(256))),
4973                        target_path: Some("/b".repeat(2048)),
4974                        ..Default::default()
4975                    }),
4976                    fdecl::Use::Directory(fdecl::UseDirectory {
4977                        dependency_type: Some(fdecl::DependencyType::Strong),
4978                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4979                        source_name: Some(format!("{}", "a".repeat(256))),
4980                        target_path: Some("/c".repeat(2048)),
4981                        rights: Some(fio::Operations::CONNECT),
4982                        subdir: None,
4983                        ..Default::default()
4984                    }),
4985                    fdecl::Use::Storage(fdecl::UseStorage {
4986                        source_name: Some("cache".to_string()),
4987                        target_path: Some("/d".repeat(2048)),
4988                        ..Default::default()
4989                    }),
4990                ]);
4991                decl
4992            },
4993            result = Err(ErrorList::new(vec![
4994                Error::field_too_long(DeclType::UseService, "source_name"),
4995                Error::field_too_long(DeclType::UseService, "target_path"),
4996                Error::field_too_long(DeclType::UseProtocol, "source_name"),
4997                Error::field_too_long(DeclType::UseProtocol, "target_path"),
4998                Error::field_too_long(DeclType::UseDirectory, "source_name"),
4999                Error::field_too_long(DeclType::UseDirectory, "target_path"),
5000                Error::field_too_long(DeclType::UseStorage, "target_path"),
5001            ])),
5002        },
5003        test_validate_conflicting_paths => {
5004            input = {
5005                let mut decl = new_component_decl();
5006                decl.uses = Some(vec![
5007                    fdecl::Use::Service(fdecl::UseService {
5008                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5009                        source_name: Some("foo".to_string()),
5010                        target_path: Some("/bar".to_string()),
5011                        dependency_type: Some(fdecl::DependencyType::Strong),
5012                        ..Default::default()
5013                    }),
5014                    fdecl::Use::Protocol(fdecl::UseProtocol {
5015                        dependency_type: Some(fdecl::DependencyType::Strong),
5016                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5017                        source_name: Some("space".to_string()),
5018                        target_path: Some("/bar".to_string()),
5019                        ..Default::default()
5020                    }),
5021                    fdecl::Use::Directory(fdecl::UseDirectory {
5022                        dependency_type: Some(fdecl::DependencyType::Strong),
5023                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5024                        source_name: Some("crow".to_string()),
5025                        target_path: Some("/bar".to_string()),
5026                        rights: Some(fio::Operations::CONNECT),
5027                        subdir: None,
5028                        ..Default::default()
5029                    }),
5030                ]);
5031                decl
5032            },
5033            result = Err(ErrorList::new(vec![
5034                Error::duplicate_field(DeclType::UseProtocol, "target_path", "/bar"),
5035                Error::duplicate_field(DeclType::UseDirectory, "target_path", "/bar"),
5036            ])),
5037        },
5038        // exposes
5039        test_validate_exposes_empty => {
5040            input = {
5041                let mut decl = new_component_decl();
5042                decl.exposes = Some(vec![
5043                    fdecl::Expose::Service(fdecl::ExposeService {
5044                        source: None,
5045                        source_name: None,
5046                        target_name: None,
5047                        target: None,
5048                        ..Default::default()
5049                    }),
5050                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5051                        source: None,
5052                        source_name: None,
5053                        target_name: None,
5054                        target: None,
5055                        ..Default::default()
5056                    }),
5057                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5058                        source: None,
5059                        source_name: None,
5060                        target_name: None,
5061                        target: None,
5062                        rights: None,
5063                        subdir: None,
5064                        ..Default::default()
5065                    }),
5066                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5067                        source: None,
5068                        source_name: None,
5069                        target: None,
5070                        target_name: None,
5071                        ..Default::default()
5072                    }),
5073                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5074                        source: None,
5075                        source_name: None,
5076                        target: None,
5077                        target_name: None,
5078                        ..Default::default()
5079                    }),
5080                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5081                        ..Default::default()
5082                    }),
5083                ]);
5084                decl
5085            },
5086            result = Err(ErrorList::new(vec![
5087                Error::missing_field(DeclType::ExposeService, "source"),
5088                Error::missing_field(DeclType::ExposeService, "target"),
5089                Error::missing_field(DeclType::ExposeService, "source_name"),
5090                Error::missing_field(DeclType::ExposeService, "target_name"),
5091                Error::missing_field(DeclType::ExposeProtocol, "source"),
5092                Error::missing_field(DeclType::ExposeProtocol, "target"),
5093                Error::missing_field(DeclType::ExposeProtocol, "source_name"),
5094                Error::missing_field(DeclType::ExposeProtocol, "target_name"),
5095                Error::missing_field(DeclType::ExposeDirectory, "source"),
5096                Error::missing_field(DeclType::ExposeDirectory, "target"),
5097                Error::missing_field(DeclType::ExposeDirectory, "source_name"),
5098                Error::missing_field(DeclType::ExposeDirectory, "target_name"),
5099                Error::missing_field(DeclType::ExposeRunner, "source"),
5100                Error::missing_field(DeclType::ExposeRunner, "target"),
5101                Error::missing_field(DeclType::ExposeRunner, "source_name"),
5102                Error::missing_field(DeclType::ExposeRunner, "target_name"),
5103                Error::missing_field(DeclType::ExposeResolver, "source"),
5104                Error::missing_field(DeclType::ExposeResolver, "target"),
5105                Error::missing_field(DeclType::ExposeResolver, "source_name"),
5106                Error::missing_field(DeclType::ExposeResolver, "target_name"),
5107                Error::missing_field(DeclType::ExposeDictionary, "source"),
5108                Error::missing_field(DeclType::ExposeDictionary, "target"),
5109                Error::missing_field(DeclType::ExposeDictionary, "source_name"),
5110                Error::missing_field(DeclType::ExposeDictionary, "target_name"),
5111            ])),
5112        },
5113        test_validate_exposes_extraneous => {
5114            input = {
5115                let mut decl = new_component_decl();
5116                decl.exposes = Some(vec![
5117                    fdecl::Expose::Service(fdecl::ExposeService {
5118                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5119                            name: "logger".to_string(),
5120                            collection: Some("modular".to_string()),
5121                        })),
5122                        source_name: Some("logger".to_string()),
5123                        target_name: Some("logger".to_string()),
5124                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5125                        ..Default::default()
5126                    }),
5127                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5128                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5129                            name: "logger".to_string(),
5130                            collection: Some("modular".to_string()),
5131                        })),
5132                        source_name: Some("legacy_logger".to_string()),
5133                        target_name: Some("legacy_logger".to_string()),
5134                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5135                        ..Default::default()
5136                    }),
5137                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5138                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5139                            name: "netstack".to_string(),
5140                            collection: Some("modular".to_string()),
5141                        })),
5142                        source_name: Some("data".to_string()),
5143                        target_name: Some("data".to_string()),
5144                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5145                        rights: Some(fio::Operations::CONNECT),
5146                        subdir: None,
5147                        ..Default::default()
5148                    }),
5149                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5150                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5151                            name: "netstack".to_string(),
5152                            collection: Some("modular".to_string()),
5153                        })),
5154                        source_name: Some("elf".to_string()),
5155                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5156                        target_name: Some("elf".to_string()),
5157                        ..Default::default()
5158                    }),
5159                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5160                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5161                            name: "netstack".to_string(),
5162                            collection: Some("modular".to_string()),
5163                        })),
5164                        source_name: Some("pkg".to_string()),
5165                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5166                        target_name: Some("pkg".to_string()),
5167                        ..Default::default()
5168                    }),
5169                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5170                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5171                            name: "netstack".to_string(),
5172                            collection: Some("modular".to_string()),
5173                        })),
5174                        source_name: Some("dict".to_string()),
5175                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5176                        target_name: Some("dict".to_string()),
5177                        ..Default::default()
5178                    }),
5179                ]);
5180                decl
5181            },
5182            result = Err(ErrorList::new(vec![
5183                Error::extraneous_field(DeclType::ExposeService, "source.child.collection"),
5184                Error::extraneous_field(DeclType::ExposeProtocol, "source.child.collection"),
5185                Error::extraneous_field(DeclType::ExposeDirectory, "source.child.collection"),
5186                Error::extraneous_field(DeclType::ExposeRunner, "source.child.collection"),
5187                Error::extraneous_field(DeclType::ExposeResolver, "source.child.collection"),
5188                Error::extraneous_field(DeclType::ExposeDictionary, "source.child.collection"),
5189            ])),
5190        },
5191        test_validate_exposes_invalid_identifiers => {
5192            input = {
5193                let mut decl = new_component_decl();
5194                decl.exposes = Some(vec![
5195                    fdecl::Expose::Service(fdecl::ExposeService {
5196                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5197                            name: "^bad".to_string(),
5198                            collection: None,
5199                        })),
5200                        source_name: Some("foo/".to_string()),
5201                        target_name: Some("/".to_string()),
5202                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5203                        ..Default::default()
5204                    }),
5205                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5206                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5207                            name: "^bad".to_string(),
5208                            collection: None,
5209                        })),
5210                        source_name: Some("foo/".to_string()),
5211                        target_name: Some("/".to_string()),
5212                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5213                        ..Default::default()
5214                    }),
5215                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5216                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5217                            name: "^bad".to_string(),
5218                            collection: None,
5219                        })),
5220                        source_name: Some("foo/".to_string()),
5221                        target_name: Some("/".to_string()),
5222                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5223                        rights: Some(fio::Operations::CONNECT),
5224                        subdir: Some("/foo".to_string()),
5225                        ..Default::default()
5226                    }),
5227                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5228                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5229                            name: "^bad".to_string(),
5230                            collection: None,
5231                        })),
5232                        source_name: Some("/path".to_string()),
5233                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5234                        target_name: Some("elf!".to_string()),
5235                        ..Default::default()
5236                    }),
5237                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5238                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5239                            name: "^bad".to_string(),
5240                            collection: None,
5241                        })),
5242                        source_name: Some("/path".to_string()),
5243                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5244                        target_name: Some("pkg!".to_string()),
5245                        ..Default::default()
5246                    }),
5247                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5248                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5249                            name: "^bad".to_string(),
5250                            collection: None,
5251                        })),
5252                        source_name: Some("/path".to_string()),
5253                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5254                        target_name: Some("pkg!".to_string()),
5255                        ..Default::default()
5256                    }),
5257                ]);
5258                decl
5259            },
5260            result = Err(ErrorList::new(vec![
5261                Error::invalid_field(DeclType::ExposeService, "source.child.name"),
5262                Error::invalid_field(DeclType::ExposeService, "source_name"),
5263                Error::invalid_field(DeclType::ExposeService, "target_name"),
5264                Error::invalid_field(DeclType::ExposeProtocol, "source.child.name"),
5265                Error::invalid_field(DeclType::ExposeProtocol, "source_name"),
5266                Error::invalid_field(DeclType::ExposeProtocol, "target_name"),
5267                Error::invalid_field(DeclType::ExposeDirectory, "source.child.name"),
5268                Error::invalid_field(DeclType::ExposeDirectory, "source_name"),
5269                Error::invalid_field(DeclType::ExposeDirectory, "target_name"),
5270                Error::invalid_field(DeclType::ExposeDirectory, "subdir"),
5271                Error::invalid_field(DeclType::ExposeRunner, "source.child.name"),
5272                Error::invalid_field(DeclType::ExposeRunner, "source_name"),
5273                Error::invalid_field(DeclType::ExposeRunner, "target_name"),
5274                Error::invalid_field(DeclType::ExposeResolver, "source.child.name"),
5275                Error::invalid_field(DeclType::ExposeResolver, "source_name"),
5276                Error::invalid_field(DeclType::ExposeResolver, "target_name"),
5277                Error::invalid_field(DeclType::ExposeDictionary, "source.child.name"),
5278                Error::invalid_field(DeclType::ExposeDictionary, "source_name"),
5279                Error::invalid_field(DeclType::ExposeDictionary, "target_name"),
5280            ])),
5281        },
5282        test_validate_exposes_invalid_source_target => {
5283            input = {
5284                let mut decl = new_component_decl();
5285                decl.children = Some(vec![fdecl::Child{
5286                    name: Some("logger".to_string()),
5287                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5288                    startup: Some(fdecl::StartupMode::Lazy),
5289                    on_terminate: None,
5290                    environment: None,
5291                    ..Default::default()
5292                }]);
5293                decl.exposes = Some(vec![
5294                    fdecl::Expose::Service(fdecl::ExposeService {
5295                        source: None,
5296                        source_name: Some("a".to_string()),
5297                        target_name: Some("b".to_string()),
5298                        target: None,
5299                        ..Default::default()
5300                    }),
5301                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5302                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5303                        source_name: Some("c".to_string()),
5304                        target_name: Some("d".to_string()),
5305                        target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5306                        ..Default::default()
5307                    }),
5308                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5309                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "z".to_string()})),
5310                        source_name: Some("e".to_string()),
5311                        target_name: Some("f".to_string()),
5312                        target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "z".to_string()})),
5313                        rights: Some(fio::Operations::CONNECT),
5314                        subdir: None,
5315                        ..Default::default()
5316                    }),
5317                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5318                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5319                        source_name: Some("g".to_string()),
5320                        target_name: Some("h".to_string()),
5321                        target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5322                        rights: Some(fio::Operations::CONNECT),
5323                        subdir: None,
5324                        ..Default::default()
5325                    }),
5326                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5327                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5328                        source_name: Some("i".to_string()),
5329                        target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5330                        target_name: Some("j".to_string()),
5331                        ..Default::default()
5332                    }),
5333                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5334                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5335                        source_name: Some("k".to_string()),
5336                        target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5337                        target_name: Some("l".to_string()),
5338                        ..Default::default()
5339                    }),
5340                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5341                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5342                            name: "logger".to_string(),
5343                            collection: None,
5344                        })),
5345                        source_name: Some("m".to_string()),
5346                        target_name: Some("n".to_string()),
5347                        target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5348                        ..Default::default()
5349                    }),
5350                ]);
5351                decl
5352            },
5353            result = Err(ErrorList::new(vec![
5354                Error::missing_field(DeclType::ExposeService, "source"),
5355                Error::missing_field(DeclType::ExposeService, "target"),
5356                Error::invalid_field(DeclType::ExposeProtocol, "source"),
5357                Error::invalid_field(DeclType::ExposeProtocol, "target"),
5358                Error::invalid_field(DeclType::ExposeDirectory, "source"),
5359                Error::invalid_field(DeclType::ExposeDirectory, "target"),
5360                Error::invalid_field(DeclType::ExposeDirectory, "source"),
5361                Error::invalid_field(DeclType::ExposeDirectory, "target"),
5362                Error::invalid_field(DeclType::ExposeRunner, "source"),
5363                Error::invalid_field(DeclType::ExposeRunner, "target"),
5364                Error::invalid_field(DeclType::ExposeResolver, "source"),
5365                Error::invalid_field(DeclType::ExposeResolver, "target"),
5366                Error::invalid_field(DeclType::ExposeDictionary, "target"),
5367            ])),
5368        },
5369        test_validate_exposes_invalid_source_collection => {
5370            input = {
5371                let mut decl = new_component_decl();
5372                decl.collections = Some(vec![fdecl::Collection{
5373                    name: Some("col".to_string()),
5374                    durability: Some(fdecl::Durability::Transient),
5375                    allowed_offers: None,
5376                    allow_long_names: None,
5377                    ..Default::default()
5378                }]);
5379                decl.exposes = Some(vec![
5380                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5381                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
5382                        source_name: Some("a".to_string()),
5383                        target_name: Some("a".to_string()),
5384                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5385                        ..Default::default()
5386                    }),
5387                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5388                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5389                        source_name: Some("b".to_string()),
5390                        target_name: Some("b".to_string()),
5391                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5392                        rights: Some(fio::Operations::CONNECT),
5393                        subdir: None,
5394                        ..Default::default()
5395                    }),
5396                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5397                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5398                        source_name: Some("c".to_string()),
5399                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5400                        target_name: Some("c".to_string()),
5401                        ..Default::default()
5402                    }),
5403                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5404                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5405                        source_name: Some("d".to_string()),
5406                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5407                        target_name: Some("d".to_string()),
5408                        ..Default::default()
5409                    }),
5410                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5411                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5412                        source_name: Some("e".to_string()),
5413                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5414                        target_name: Some("e".to_string()),
5415                        ..Default::default()
5416                    }),
5417                ]);
5418                decl
5419            },
5420            result = Err(ErrorList::new(vec![
5421                Error::invalid_field(DeclType::ExposeProtocol, "source"),
5422                Error::invalid_field(DeclType::ExposeDirectory, "source"),
5423                Error::invalid_field(DeclType::ExposeRunner, "source"),
5424                Error::invalid_field(DeclType::ExposeResolver, "source"),
5425                Error::invalid_field(DeclType::ExposeDictionary, "source"),
5426            ])),
5427        },
5428        test_validate_exposes_sources_collection => {
5429            input = {
5430                let mut decl = new_component_decl();
5431                decl.collections = Some(vec![
5432                    fdecl::Collection {
5433                        name: Some("col".to_string()),
5434                        durability: Some(fdecl::Durability::Transient),
5435                        allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
5436                        allow_long_names: None,
5437                        ..Default::default()
5438                    }
5439                ]);
5440                decl.exposes = Some(vec![
5441                    fdecl::Expose::Service(fdecl::ExposeService {
5442                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
5443                        source_name: Some("a".to_string()),
5444                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5445                        target_name: Some("a".to_string()),
5446                        ..Default::default()
5447                    })
5448                ]);
5449                decl
5450            },
5451            result = Ok(()),
5452        },
5453        test_validate_exposes_long_identifiers => {
5454            input = {
5455                let mut decl = new_component_decl();
5456                decl.exposes = Some(vec![
5457                    fdecl::Expose::Service(fdecl::ExposeService {
5458                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5459                            name: "b".repeat(256),
5460                            collection: None,
5461                        })),
5462                        source_name: Some(format!("{}", "a".repeat(1025))),
5463                        target_name: Some(format!("{}", "b".repeat(1025))),
5464                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5465                        ..Default::default()
5466                    }),
5467                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5468                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5469                            name: "b".repeat(256),
5470                            collection: None,
5471                        })),
5472                        source_name: Some(format!("{}", "a".repeat(256))),
5473                        target_name: Some(format!("{}", "b".repeat(256))),
5474                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5475                        ..Default::default()
5476                    }),
5477                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5478                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5479                            name: "b".repeat(256),
5480                            collection: None,
5481                        })),
5482                        source_name: Some(format!("{}", "a".repeat(256))),
5483                        target_name: Some(format!("{}", "b".repeat(256))),
5484                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5485                        rights: Some(fio::Operations::CONNECT),
5486                        subdir: None,
5487                        ..Default::default()
5488                    }),
5489                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5490                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5491                            name: "b".repeat(256),
5492                            collection: None,
5493                        })),
5494                        source_name: Some("a".repeat(256)),
5495                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5496                        target_name: Some("b".repeat(256)),
5497                        ..Default::default()
5498                    }),
5499                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5500                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5501                            name: "b".repeat(256),
5502                            collection: None,
5503                        })),
5504                        source_name: Some("a".repeat(256)),
5505                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5506                        target_name: Some("b".repeat(256)),
5507                        ..Default::default()
5508                    }),
5509                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5510                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5511                            name: "b".repeat(256),
5512                            collection: None,
5513                        })),
5514                        source_name: Some("a".repeat(256)),
5515                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5516                        target_name: Some("b".repeat(256)),
5517                        ..Default::default()
5518                    }),
5519                ]);
5520                decl
5521            },
5522            result = Err(ErrorList::new(vec![
5523                Error::field_too_long(DeclType::ExposeService, "source.child.name"),
5524                Error::field_too_long(DeclType::ExposeService, "source_name"),
5525                Error::field_too_long(DeclType::ExposeService, "target_name"),
5526                Error::field_too_long(DeclType::ExposeProtocol, "source.child.name"),
5527                Error::field_too_long(DeclType::ExposeProtocol, "source_name"),
5528                Error::field_too_long(DeclType::ExposeProtocol, "target_name"),
5529                Error::field_too_long(DeclType::ExposeDirectory, "source.child.name"),
5530                Error::field_too_long(DeclType::ExposeDirectory, "source_name"),
5531                Error::field_too_long(DeclType::ExposeDirectory, "target_name"),
5532                Error::field_too_long(DeclType::ExposeRunner, "source.child.name"),
5533                Error::field_too_long(DeclType::ExposeRunner, "source_name"),
5534                Error::field_too_long(DeclType::ExposeRunner, "target_name"),
5535                Error::field_too_long(DeclType::ExposeResolver, "source.child.name"),
5536                Error::field_too_long(DeclType::ExposeResolver, "source_name"),
5537                Error::field_too_long(DeclType::ExposeResolver, "target_name"),
5538                Error::field_too_long(DeclType::ExposeDictionary, "source.child.name"),
5539                Error::field_too_long(DeclType::ExposeDictionary, "source_name"),
5540                Error::field_too_long(DeclType::ExposeDictionary, "target_name"),
5541            ])),
5542        },
5543        test_validate_exposes_invalid_child => {
5544            input = {
5545                let mut decl = new_component_decl();
5546                decl.exposes = Some(vec![
5547                    fdecl::Expose::Service(fdecl::ExposeService {
5548                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5549                            name: "netstack".to_string(),
5550                            collection: None,
5551                        })),
5552                        source_name: Some("fuchsia.logger.Log".to_string()),
5553                        target_name: Some("fuchsia.logger.Log".to_string()),
5554                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5555                        ..Default::default()
5556                    }),
5557                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5558                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5559                            name: "netstack".to_string(),
5560                            collection: None,
5561                        })),
5562                        source_name: Some("fuchsia.logger.LegacyLog".to_string()),
5563                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
5564                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5565                        ..Default::default()
5566                    }),
5567                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5568                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5569                            name: "netstack".to_string(),
5570                            collection: None,
5571                        })),
5572                        source_name: Some("data".to_string()),
5573                        target_name: Some("data".to_string()),
5574                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5575                        rights: Some(fio::Operations::CONNECT),
5576                        subdir: None,
5577                        ..Default::default()
5578                    }),
5579                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5580                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5581                            name: "netstack".to_string(),
5582                            collection: None,
5583                        })),
5584                        source_name: Some("elf".to_string()),
5585                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5586                        target_name: Some("elf".to_string()),
5587                        ..Default::default()
5588                    }),
5589                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5590                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5591                            name: "netstack".to_string(),
5592                            collection: None,
5593                        })),
5594                        source_name: Some("pkg".to_string()),
5595                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5596                        target_name: Some("pkg".to_string()),
5597                        ..Default::default()
5598                    }),
5599                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5600                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5601                            name: "netstack".to_string(),
5602                            collection: None,
5603                        })),
5604                        source_name: Some("dict".to_string()),
5605                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5606                        target_name: Some("dict".to_string()),
5607                        ..Default::default()
5608                    }),
5609                ]);
5610                decl
5611            },
5612            result = Err(ErrorList::new(vec![
5613                Error::invalid_child(DeclType::ExposeService, "source", "netstack"),
5614                Error::invalid_child(DeclType::ExposeProtocol, "source", "netstack"),
5615                Error::invalid_child(DeclType::ExposeDirectory, "source", "netstack"),
5616                Error::invalid_child(DeclType::ExposeRunner, "source", "netstack"),
5617                Error::invalid_child(DeclType::ExposeResolver, "source", "netstack"),
5618                Error::invalid_child(DeclType::ExposeDictionary, "source", "netstack"),
5619            ])),
5620        },
5621        test_validate_exposes_invalid_source_capability => {
5622            input = {
5623                fdecl::Component {
5624                    exposes: Some(vec![
5625                        fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5626                            source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
5627                                name: "this-storage-doesnt-exist".to_string(),
5628                            })),
5629                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
5630                            target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
5631                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5632                            ..Default::default()
5633                        }),
5634                    ]),
5635                    ..new_component_decl()
5636                }
5637            },
5638            result = Err(ErrorList::new(vec![
5639                Error::invalid_capability(DeclType::ExposeProtocol, "source", "this-storage-doesnt-exist"),
5640            ])),
5641        },
5642        test_validate_exposes_duplicate_target => {
5643            input = {
5644                let mut decl = new_component_decl();
5645                decl.exposes = Some(vec![
5646                    fdecl::Expose::Service(fdecl::ExposeService {
5647                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
5648                            name: "coll".into(),
5649                        })),
5650                        source_name: Some("netstack".to_string()),
5651                        target_name: Some("fuchsia.net.Stack".to_string()),
5652                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5653                        ..Default::default()
5654                    }),
5655                    fdecl::Expose::Service(fdecl::ExposeService {
5656                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
5657                            name: "coll2".into(),
5658                        })),
5659                        source_name: Some("netstack2".to_string()),
5660                        target_name: Some("fuchsia.net.Stack".to_string()),
5661                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5662                        ..Default::default()
5663                    }),
5664                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5665                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5666                        source_name: Some("fonts".to_string()),
5667                        target_name: Some("fuchsia.fonts.Provider".to_string()),
5668                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5669                        ..Default::default()
5670                    }),
5671                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5672                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5673                        source_name: Some("fonts2".to_string()),
5674                        target_name: Some("fuchsia.fonts.Provider".to_string()),
5675                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5676                        ..Default::default()
5677                    }),
5678                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5679                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5680                        source_name: Some("assets".to_string()),
5681                        target_name: Some("stuff".to_string()),
5682                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5683                        rights: None,
5684                        subdir: None,
5685                        ..Default::default()
5686                    }),
5687                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5688                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5689                        source_name: Some("assets2".to_string()),
5690                        target_name: Some("stuff".to_string()),
5691                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5692                        rights: None,
5693                        subdir: None,
5694                        ..Default::default()
5695                    }),
5696                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5697                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5698                        source_name: Some("source_elf".to_string()),
5699                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5700                        target_name: Some("elf".to_string()),
5701                        ..Default::default()
5702                    }),
5703                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5704                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5705                        source_name: Some("source_elf".to_string()),
5706                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5707                        target_name: Some("elf".to_string()),
5708                        ..Default::default()
5709                    }),
5710                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5711                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5712                        source_name: Some("source_pkg".to_string()),
5713                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5714                        target_name: Some("pkg".to_string()),
5715                        ..Default::default()
5716                    }),
5717                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5718                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5719                        source_name: Some("source_pkg".to_string()),
5720                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5721                        target_name: Some("pkg".to_string()),
5722                        ..Default::default()
5723                    }),
5724                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5725                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5726                        source_name: Some("source_dict".to_string()),
5727                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5728                        target_name: Some("dict".to_string()),
5729                        ..Default::default()
5730                    }),
5731                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5732                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5733                        source_name: Some("source_dict".to_string()),
5734                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5735                        target_name: Some("dict".to_string()),
5736                        ..Default::default()
5737                    }),
5738                ]);
5739                decl.collections = Some(vec![
5740                    fdecl::Collection {
5741                        name: Some("coll".into()),
5742                        durability: Some(fdecl::Durability::Transient),
5743                        ..Default::default()
5744                    },
5745                    fdecl::Collection {
5746                        name: Some("coll2".into()),
5747                        durability: Some(fdecl::Durability::Transient),
5748                        ..Default::default()
5749                    },
5750                ]);
5751                decl.capabilities = Some(vec![
5752                    fdecl::Capability::Service(fdecl::Service {
5753                        name: Some("netstack".to_string()),
5754                        source_path: Some("/path".to_string()),
5755                        ..Default::default()
5756                    }),
5757                    fdecl::Capability::Service(fdecl::Service {
5758                        name: Some("netstack2".to_string()),
5759                        source_path: Some("/path".to_string()),
5760                        ..Default::default()
5761                    }),
5762                    fdecl::Capability::Protocol(fdecl::Protocol {
5763                        name: Some("fonts".to_string()),
5764                        source_path: Some("/path".to_string()),
5765                        ..Default::default()
5766                    }),
5767                    fdecl::Capability::Protocol(fdecl::Protocol {
5768                        name: Some("fonts2".to_string()),
5769                        source_path: Some("/path".to_string()),
5770                        ..Default::default()
5771                    }),
5772                    fdecl::Capability::Directory(fdecl::Directory {
5773                        name: Some("assets".to_string()),
5774                        source_path: Some("/path".to_string()),
5775                        rights: Some(fio::Operations::CONNECT),
5776                        ..Default::default()
5777                    }),
5778                    fdecl::Capability::Directory(fdecl::Directory {
5779                        name: Some("assets2".to_string()),
5780                        source_path: Some("/path".to_string()),
5781                        rights: Some(fio::Operations::CONNECT),
5782                        ..Default::default()
5783                    }),
5784                    fdecl::Capability::Runner(fdecl::Runner {
5785                        name: Some("source_elf".to_string()),
5786                        source_path: Some("/path".to_string()),
5787                        ..Default::default()
5788                    }),
5789                    fdecl::Capability::Resolver(fdecl::Resolver {
5790                        name: Some("source_pkg".to_string()),
5791                        source_path: Some("/path".to_string()),
5792                        ..Default::default()
5793                    }),
5794                    fdecl::Capability::Dictionary(fdecl::Dictionary {
5795                        name: Some("source_dict".to_string()),
5796                        ..Default::default()
5797                    }),
5798                ]);
5799                decl
5800            },
5801            result = Err(ErrorList::new(vec![
5802                // Duplicate services are allowed.
5803                Error::duplicate_field(DeclType::ExposeProtocol, "target_name",
5804                                    "fuchsia.fonts.Provider"),
5805                Error::duplicate_field(DeclType::ExposeDirectory, "target_name",
5806                                    "stuff"),
5807                Error::duplicate_field(DeclType::ExposeRunner, "target_name",
5808                                    "elf"),
5809                Error::duplicate_field(DeclType::ExposeResolver, "target_name", "pkg"),
5810                Error::duplicate_field(DeclType::ExposeDictionary, "target_name", "dict"),
5811            ])),
5812        },
5813        test_validate_exposes_invalid_capability_from_self => {
5814            input = {
5815                let mut decl = new_component_decl();
5816                decl.exposes = Some(vec![
5817                    fdecl::Expose::Service(fdecl::ExposeService {
5818                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5819                        source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
5820                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5821                        target_name: Some("foo".to_string()),
5822                        ..Default::default()
5823                    }),
5824                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5825                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5826                        source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
5827                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5828                        target_name: Some("bar".to_string()),
5829                        ..Default::default()
5830                    }),
5831                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5832                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5833                        source_name: Some("dir".to_string()),
5834                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5835                        target_name: Some("assets".to_string()),
5836                        rights: None,
5837                        subdir: None,
5838                        ..Default::default()
5839                    }),
5840                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5841                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5842                        source_name: Some("source_elf".to_string()),
5843                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5844                        target_name: Some("elf".to_string()),
5845                        ..Default::default()
5846                    }),
5847                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5848                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5849                        source_name: Some("source_pkg".to_string()),
5850                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5851                        target_name: Some("pkg".to_string()),
5852                        ..Default::default()
5853                    }),
5854                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5855                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5856                        source_name: Some("source_dict".to_string()),
5857                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5858                        target_name: Some("dict".to_string()),
5859                        ..Default::default()
5860                    }),
5861                    fdecl::Expose::Config(fdecl::ExposeConfiguration {
5862                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5863                        source_name: Some("source_config".to_string()),
5864                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5865                        target_name: Some("config".to_string()),
5866                        ..Default::default()
5867                    }),
5868                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5869                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5870                        source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
5871                        source_dictionary: Some("dict/inner".to_string()),
5872                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5873                        target_name: Some("baz".to_string()),
5874                        ..Default::default()
5875                    }),
5876                ]);
5877                decl
5878            },
5879            result = Err(ErrorList::new(vec![
5880                Error::invalid_capability(
5881                    DeclType::ExposeService,
5882                    "source",
5883                    "fuchsia.some.library.SomeProtocol"),
5884                Error::invalid_capability(
5885                    DeclType::ExposeProtocol,
5886                    "source",
5887                    "fuchsia.some.library.SomeProtocol"),
5888                Error::invalid_capability(DeclType::ExposeDirectory, "source", "dir"),
5889                Error::invalid_capability(DeclType::ExposeRunner, "source", "source_elf"),
5890                Error::invalid_capability(DeclType::ExposeResolver, "source", "source_pkg"),
5891                Error::invalid_capability(DeclType::ExposeDictionary, "source", "source_dict"),
5892                Error::invalid_capability(DeclType::ExposeConfig, "source", "source_config"),
5893                Error::invalid_capability(DeclType::ExposeProtocol, "source", "dict"),
5894            ])),
5895        },
5896
5897        test_validate_exposes_availability_service => {
5898            input = {
5899                let mut decl = generate_expose_different_source_and_availability_decl(
5900                    |source, availability, target_name|
5901                        fdecl::Expose::Service(fdecl::ExposeService {
5902                            source: Some(source),
5903                            source_name: Some("fuchsia.examples.Echo".to_string()),
5904                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5905                            target_name: Some(target_name.to_string()),
5906                            availability: Some(availability),
5907                            ..Default::default()
5908                        })
5909                );
5910                decl.capabilities = Some(vec![
5911                    fdecl::Capability::Service(fdecl::Service {
5912                        name: Some("fuchsia.examples.Echo".to_string()),
5913                        source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
5914                        ..Default::default()
5915                    }),
5916                ]);
5917                decl
5918            },
5919            result = {
5920                Err(ErrorList::new(vec![
5921                    Error::availability_must_be_optional(
5922                        DeclType::ExposeService,
5923                        "availability",
5924                        Some(&"fuchsia.examples.Echo".to_string()),
5925                    ),
5926                    Error::availability_must_be_optional(
5927                        DeclType::ExposeService,
5928                        "availability",
5929                        Some(&"fuchsia.examples.Echo".to_string()),
5930                    ),
5931                ]))
5932            },
5933        },
5934        test_validate_exposes_availability_protocol => {
5935            input = {
5936                let mut decl = generate_expose_different_source_and_availability_decl(
5937                    |source, availability, target_name|
5938                        fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5939                            source: Some(source),
5940                            source_name: Some("fuchsia.examples.Echo".to_string()),
5941                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5942                            target_name: Some(target_name.to_string()),
5943                            availability: Some(availability),
5944                            ..Default::default()
5945                        })
5946                );
5947                decl.capabilities = Some(vec![
5948                    fdecl::Capability::Protocol(fdecl::Protocol {
5949                        name: Some("fuchsia.examples.Echo".to_string()),
5950                        source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
5951                        ..Default::default()
5952                    }),
5953                ]);
5954                decl
5955            },
5956            result = {
5957                Err(ErrorList::new(vec![
5958                    Error::availability_must_be_optional(
5959                        DeclType::ExposeProtocol,
5960                        "availability",
5961                        Some(&"fuchsia.examples.Echo".to_string()),
5962                    ),
5963                    Error::availability_must_be_optional(
5964                        DeclType::ExposeProtocol,
5965                        "availability",
5966                        Some(&"fuchsia.examples.Echo".to_string()),
5967                    ),
5968                ]))
5969            },
5970        },
5971        test_validate_exposes_availability_directory => {
5972            input = {
5973                let mut decl = generate_expose_different_source_and_availability_decl(
5974                    |source, availability, target_name|
5975                        fdecl::Expose::Directory(fdecl::ExposeDirectory {
5976                            source: Some(source),
5977                            source_name: Some("fuchsia.examples.Echo".to_string()),
5978                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5979                            target_name: Some(target_name.to_string()),
5980                            availability: Some(availability),
5981                            ..Default::default()
5982                        })
5983                );
5984                decl.capabilities = Some(vec![
5985                    fdecl::Capability::Directory(fdecl::Directory {
5986                        name: Some("fuchsia.examples.Echo".to_string()),
5987                        source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
5988                        rights: Some(fio::Operations::READ_BYTES),
5989                        ..Default::default()
5990                    }),
5991                ]);
5992                decl
5993            },
5994            result = {
5995                Err(ErrorList::new(vec![
5996                    Error::availability_must_be_optional(
5997                        DeclType::ExposeDirectory,
5998                        "availability",
5999                        Some(&"fuchsia.examples.Echo".to_string()),
6000                    ),
6001                    Error::availability_must_be_optional(
6002                        DeclType::ExposeDirectory,
6003                        "availability",
6004                        Some(&"fuchsia.examples.Echo".to_string()),
6005                    ),
6006                ]))
6007            },
6008        },
6009
6010        // offers
6011        test_validate_offers_empty => {
6012            input = {
6013                let mut decl = new_component_decl();
6014                decl.offers = Some(vec![
6015                    fdecl::Offer::Service(fdecl::OfferService {
6016                        source: None,
6017                        source_name: None,
6018                        target: None,
6019                        target_name: None,
6020                        ..Default::default()
6021                    }),
6022                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6023                        source: None,
6024                        source_name: None,
6025                        target: None,
6026                        target_name: None,
6027                        dependency_type: None,
6028                        ..Default::default()
6029                    }),
6030                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6031                        source: None,
6032                        source_name: None,
6033                        target: None,
6034                        target_name: None,
6035                        rights: None,
6036                        subdir: None,
6037                        dependency_type: None,
6038                        ..Default::default()
6039                    }),
6040                    fdecl::Offer::Storage(fdecl::OfferStorage {
6041                        source_name: None,
6042                        source: None,
6043                        target: None,
6044                        target_name: None,
6045                        ..Default::default()
6046                    }),
6047                    fdecl::Offer::Runner(fdecl::OfferRunner {
6048                        source: None,
6049                        source_name: None,
6050                        target: None,
6051                        target_name: None,
6052                        ..Default::default()
6053                    }),
6054                    fdecl::Offer::Resolver(fdecl::OfferResolver {
6055                        ..Default::default()
6056                    }),
6057                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6058                        ..Default::default()
6059                    }),
6060                ]);
6061                decl
6062            },
6063            // TODO(dgonyeo): we need to handle the availability being unset until we've soft
6064            // migrated all manifests
6065            result = Err(ErrorList::new(vec![
6066                Error::missing_field(DeclType::OfferService, "source"),
6067                Error::missing_field(DeclType::OfferService, "source_name"),
6068                Error::missing_field(DeclType::OfferService, "target"),
6069                Error::missing_field(DeclType::OfferService, "target_name"),
6070                //Error::missing_field(DeclType::OfferService, "availability"),
6071                Error::missing_field(DeclType::OfferProtocol, "source"),
6072                Error::missing_field(DeclType::OfferProtocol, "source_name"),
6073                Error::missing_field(DeclType::OfferProtocol, "target"),
6074                Error::missing_field(DeclType::OfferProtocol, "target_name"),
6075                Error::missing_field(DeclType::OfferProtocol, "dependency_type"),
6076                //Error::missing_field(DeclType::OfferProtocol, "availability"),
6077                Error::missing_field(DeclType::OfferDirectory, "source"),
6078                Error::missing_field(DeclType::OfferDirectory, "source_name"),
6079                Error::missing_field(DeclType::OfferDirectory, "target"),
6080                Error::missing_field(DeclType::OfferDirectory, "target_name"),
6081                Error::missing_field(DeclType::OfferDirectory, "dependency_type"),
6082                //Error::missing_field(DeclType::OfferDirectory, "availability"),
6083                Error::missing_field(DeclType::OfferStorage, "source"),
6084                Error::missing_field(DeclType::OfferStorage, "source_name"),
6085                Error::missing_field(DeclType::OfferStorage, "target"),
6086                Error::missing_field(DeclType::OfferStorage, "target_name"),
6087                //Error::missing_field(DeclType::OfferStorage, "availability"),
6088                Error::missing_field(DeclType::OfferRunner, "source"),
6089                Error::missing_field(DeclType::OfferRunner, "source_name"),
6090                Error::missing_field(DeclType::OfferRunner, "target"),
6091                Error::missing_field(DeclType::OfferRunner, "target_name"),
6092                //Error::missing_field(DeclType::OfferRunner, "availability"),
6093                Error::missing_field(DeclType::OfferResolver, "source"),
6094                Error::missing_field(DeclType::OfferResolver, "source_name"),
6095                Error::missing_field(DeclType::OfferResolver, "target"),
6096                Error::missing_field(DeclType::OfferResolver, "target_name"),
6097                Error::missing_field(DeclType::OfferDictionary, "source"),
6098                Error::missing_field(DeclType::OfferDictionary, "source_name"),
6099                Error::missing_field(DeclType::OfferDictionary, "target"),
6100                Error::missing_field(DeclType::OfferDictionary, "target_name"),
6101                Error::missing_field(DeclType::OfferDictionary, "dependency_type"),
6102            ])),
6103        },
6104        test_validate_offers_long_identifiers => {
6105            input = {
6106                let mut decl = new_component_decl();
6107                decl.offers = Some(vec![
6108                    fdecl::Offer::Service(fdecl::OfferService {
6109                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6110                            name: "a".repeat(256),
6111                            collection: None,
6112                        })),
6113                        source_name: Some(format!("{}", "a".repeat(256))),
6114                        target: Some(fdecl::Ref::Child(
6115                        fdecl::ChildRef {
6116                            name: "b".repeat(256),
6117                            collection: None,
6118                        }
6119                        )),
6120                        target_name: Some(format!("{}", "b".repeat(256))),
6121                        ..Default::default()
6122                    }),
6123                    fdecl::Offer::Service(fdecl::OfferService {
6124                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6125                        source_name: Some("a".to_string()),
6126                        target: Some(fdecl::Ref::Collection(
6127                        fdecl::CollectionRef {
6128                            name: "b".repeat(256),
6129                        }
6130                        )),
6131                        target_name: Some(format!("{}", "b".repeat(256))),
6132                        ..Default::default()
6133                    }),
6134                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6135                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6136                            name: "a".repeat(256),
6137                            collection: None,
6138                        })),
6139                        source_name: Some(format!("{}", "a".repeat(256))),
6140                        target: Some(fdecl::Ref::Child(
6141                        fdecl::ChildRef {
6142                            name: "b".repeat(256),
6143                            collection: None,
6144                        }
6145                        )),
6146                        target_name: Some(format!("{}", "b".repeat(256))),
6147                        dependency_type: Some(fdecl::DependencyType::Strong),
6148                        ..Default::default()
6149                    }),
6150                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6151                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6152                        source_name: Some("a".to_string()),
6153                        target: Some(fdecl::Ref::Collection(
6154                        fdecl::CollectionRef {
6155                            name: "b".repeat(256),
6156                        }
6157                        )),
6158                        target_name: Some(format!("{}", "b".repeat(256))),
6159                        dependency_type: Some(fdecl::DependencyType::Weak),
6160                        ..Default::default()
6161                    }),
6162                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6163                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6164                            name: "a".repeat(256),
6165                            collection: None,
6166                        })),
6167                        source_name: Some(format!("{}", "a".repeat(256))),
6168                        target: Some(fdecl::Ref::Child(
6169                        fdecl::ChildRef {
6170                            name: "b".repeat(256),
6171                            collection: None,
6172                        }
6173                        )),
6174                        target_name: Some(format!("{}", "b".repeat(256))),
6175                        rights: Some(fio::Operations::CONNECT),
6176                        subdir: None,
6177                        dependency_type: Some(fdecl::DependencyType::Strong),
6178                        ..Default::default()
6179                    }),
6180                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6181                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6182                        source_name: Some("a".to_string()),
6183                        target: Some(fdecl::Ref::Collection(
6184                        fdecl::CollectionRef {
6185                            name: "b".repeat(256),
6186                        }
6187                        )),
6188                        target_name: Some(format!("{}", "b".repeat(256))),
6189                        rights: Some(fio::Operations::CONNECT),
6190                        subdir: None,
6191                        dependency_type: Some(fdecl::DependencyType::Weak),
6192                        ..Default::default()
6193                    }),
6194                    fdecl::Offer::Storage(fdecl::OfferStorage {
6195                        source_name: Some("data".to_string()),
6196                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6197                        target: Some(fdecl::Ref::Child(
6198                            fdecl::ChildRef {
6199                                name: "b".repeat(256),
6200                                collection: None,
6201                            }
6202                        )),
6203                        target_name: Some("data".to_string()),
6204                        ..Default::default()
6205                    }),
6206                    fdecl::Offer::Storage(fdecl::OfferStorage {
6207                        source_name: Some("data".to_string()),
6208                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6209                        target: Some(fdecl::Ref::Collection(
6210                            fdecl::CollectionRef { name: "b".repeat(256) }
6211                        )),
6212                        target_name: Some("data".to_string()),
6213                        ..Default::default()
6214                    }),
6215                    fdecl::Offer::Runner(fdecl::OfferRunner {
6216                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6217                            name: "a".repeat(256),
6218                            collection: None,
6219                        })),
6220                        source_name: Some("b".repeat(256)),
6221                        target: Some(fdecl::Ref::Collection(
6222                        fdecl::CollectionRef {
6223                            name: "c".repeat(256),
6224                        }
6225                        )),
6226                        target_name: Some("d".repeat(256)),
6227                        ..Default::default()
6228                    }),
6229                    fdecl::Offer::Resolver(fdecl::OfferResolver {
6230                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6231                            name: "a".repeat(256),
6232                            collection: None,
6233                        })),
6234                        source_name: Some("b".repeat(256)),
6235                        target: Some(fdecl::Ref::Collection(
6236                            fdecl::CollectionRef {
6237                                name: "c".repeat(256),
6238                            }
6239                        )),
6240                        target_name: Some("d".repeat(256)),
6241                        ..Default::default()
6242                    }),
6243                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6244                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6245                            name: "a".repeat(256),
6246                            collection: None,
6247                        })),
6248                        source_name: Some("b".repeat(256)),
6249                        target: Some(fdecl::Ref::Collection(
6250                            fdecl::CollectionRef {
6251                                name: "c".repeat(256),
6252                            }
6253                        )),
6254                        target_name: Some("d".repeat(256)),
6255                        dependency_type: Some(fdecl::DependencyType::Strong),
6256                        ..Default::default()
6257                    }),
6258                ]);
6259                decl
6260            },
6261            result = Err(ErrorList::new(vec![
6262                Error::field_too_long(DeclType::OfferService, "source.child.name"),
6263                Error::field_too_long(DeclType::OfferService, "source_name"),
6264                Error::field_too_long(DeclType::OfferService, "target.child.name"),
6265                Error::field_too_long(DeclType::OfferService, "target_name"),
6266                Error::field_too_long(DeclType::OfferService, "target.collection.name"),
6267                Error::field_too_long(DeclType::OfferService, "target_name"),
6268                Error::field_too_long(DeclType::OfferProtocol, "source.child.name"),
6269                Error::field_too_long(DeclType::OfferProtocol, "source_name"),
6270                Error::field_too_long(DeclType::OfferProtocol, "target.child.name"),
6271                Error::field_too_long(DeclType::OfferProtocol, "target_name"),
6272                Error::field_too_long(DeclType::OfferProtocol, "target.collection.name"),
6273                Error::field_too_long(DeclType::OfferProtocol, "target_name"),
6274                Error::field_too_long(DeclType::OfferDirectory, "source.child.name"),
6275                Error::field_too_long(DeclType::OfferDirectory, "source_name"),
6276                Error::field_too_long(DeclType::OfferDirectory, "target.child.name"),
6277                Error::field_too_long(DeclType::OfferDirectory, "target_name"),
6278                Error::field_too_long(DeclType::OfferDirectory, "target.collection.name"),
6279                Error::field_too_long(DeclType::OfferDirectory, "target_name"),
6280                Error::field_too_long(DeclType::OfferStorage, "target.child.name"),
6281                Error::field_too_long(DeclType::OfferStorage, "target.collection.name"),
6282                Error::field_too_long(DeclType::OfferRunner, "source.child.name"),
6283                Error::field_too_long(DeclType::OfferRunner, "source_name"),
6284                Error::field_too_long(DeclType::OfferRunner, "target.collection.name"),
6285                Error::field_too_long(DeclType::OfferRunner, "target_name"),
6286                Error::field_too_long(DeclType::OfferResolver, "source.child.name"),
6287                Error::field_too_long(DeclType::OfferResolver, "source_name"),
6288                Error::field_too_long(DeclType::OfferResolver, "target.collection.name"),
6289                Error::field_too_long(DeclType::OfferResolver, "target_name"),
6290                Error::field_too_long(DeclType::OfferDictionary, "source.child.name"),
6291                Error::field_too_long(DeclType::OfferDictionary, "source_name"),
6292                Error::field_too_long(DeclType::OfferDictionary, "target.collection.name"),
6293                Error::field_too_long(DeclType::OfferDictionary, "target_name"),
6294            ])),
6295        },
6296        test_validate_offers_extraneous => {
6297            input = {
6298                let mut decl = new_component_decl();
6299                decl.offers = Some(vec![
6300                    fdecl::Offer::Service(fdecl::OfferService {
6301                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6302                            name: "logger".to_string(),
6303                            collection: Some("modular".to_string()),
6304                        })),
6305                        source_name: Some("fuchsia.logger.Log".to_string()),
6306                        target: Some(fdecl::Ref::Child(
6307                            fdecl::ChildRef {
6308                                name: "netstack".to_string(),
6309                                collection: Some("modular".to_string()),
6310                            }
6311                        )),
6312                        target_name: Some("fuchsia.logger.Log".to_string()),
6313                        ..Default::default()
6314                    }),
6315                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6316                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6317                            name: "logger".to_string(),
6318                            collection: Some("modular".to_string()),
6319                        })),
6320                        source_name: Some("fuchsia.logger.Log".to_string()),
6321                        target: Some(fdecl::Ref::Child(
6322                            fdecl::ChildRef {
6323                                name: "netstack".to_string(),
6324                                collection: Some("modular".to_string()),
6325                            }
6326                        )),
6327                        target_name: Some("fuchsia.logger.Log".to_string()),
6328                        dependency_type: Some(fdecl::DependencyType::Strong),
6329                        ..Default::default()
6330                    }),
6331                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6332                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6333                            name: "logger".to_string(),
6334                            collection: Some("modular".to_string()),
6335                        })),
6336                        source_name: Some("assets".to_string()),
6337                        target: Some(fdecl::Ref::Child(
6338                            fdecl::ChildRef {
6339                                name: "netstack".to_string(),
6340                                collection: Some("modular".to_string()),
6341                            }
6342                        )),
6343                        target_name: Some("assets".to_string()),
6344                        rights: Some(fio::Operations::CONNECT),
6345                        subdir: None,
6346                        dependency_type: Some(fdecl::DependencyType::Weak),
6347                        ..Default::default()
6348                    }),
6349                    fdecl::Offer::Storage(fdecl::OfferStorage {
6350                        source_name: Some("data".to_string()),
6351                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{ })),
6352                        target: Some(fdecl::Ref::Child(
6353                            fdecl::ChildRef {
6354                                name: "netstack".to_string(),
6355                                collection: Some("modular".to_string()),
6356                            }
6357                        )),
6358                        target_name: Some("data".to_string()),
6359                        ..Default::default()
6360                    }),
6361                    fdecl::Offer::Runner(fdecl::OfferRunner {
6362                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6363                            name: "logger".to_string(),
6364                            collection: Some("modular".to_string()),
6365                        })),
6366                        source_name: Some("elf".to_string()),
6367                        target: Some(fdecl::Ref::Child(
6368                            fdecl::ChildRef {
6369                                name: "netstack".to_string(),
6370                                collection: Some("modular".to_string()),
6371                            }
6372                        )),
6373                        target_name: Some("elf".to_string()),
6374                        ..Default::default()
6375                    }),
6376                    fdecl::Offer::Resolver(fdecl::OfferResolver {
6377                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6378                            name: "logger".to_string(),
6379                            collection: Some("modular".to_string()),
6380                        })),
6381                        source_name: Some("pkg".to_string()),
6382                        target: Some(fdecl::Ref::Child(
6383                            fdecl::ChildRef {
6384                                name: "netstack".to_string(),
6385                                collection: Some("modular".to_string()),
6386                            }
6387                        )),
6388                        target_name: Some("pkg".to_string()),
6389                        ..Default::default()
6390                    }),
6391                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6392                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6393                            name: "logger".to_string(),
6394                            collection: Some("modular".to_string()),
6395                        })),
6396                        source_name: Some("dict".to_string()),
6397                        target: Some(fdecl::Ref::Child(
6398                            fdecl::ChildRef {
6399                                name: "netstack".to_string(),
6400                                collection: Some("modular".to_string()),
6401                            }
6402                        )),
6403                        target_name: Some("dict".to_string()),
6404                        dependency_type: Some(fdecl::DependencyType::Strong),
6405                        ..Default::default()
6406                    }),
6407                ]);
6408                decl.capabilities = Some(vec![
6409                    fdecl::Capability::Protocol(fdecl::Protocol {
6410                        name: Some("fuchsia.logger.Log".to_string()),
6411                        source_path: Some("/svc/logger".to_string()),
6412                        ..Default::default()
6413                    }),
6414                    fdecl::Capability::Directory(fdecl::Directory {
6415                        name: Some("assets".to_string()),
6416                        source_path: Some("/data/assets".to_string()),
6417                        rights: Some(fio::Operations::CONNECT),
6418                        ..Default::default()
6419                    }),
6420                ]);
6421                decl
6422            },
6423            result = Err(ErrorList::new(vec![
6424                Error::extraneous_field(DeclType::OfferService, "source.child.collection"),
6425                Error::extraneous_field(DeclType::OfferService, "target.child.collection"),
6426                Error::extraneous_field(DeclType::OfferProtocol, "source.child.collection"),
6427                Error::extraneous_field(DeclType::OfferProtocol, "target.child.collection"),
6428                Error::extraneous_field(DeclType::OfferDirectory, "source.child.collection"),
6429                Error::extraneous_field(DeclType::OfferDirectory, "target.child.collection"),
6430                Error::extraneous_field(DeclType::OfferStorage, "target.child.collection"),
6431                Error::extraneous_field(DeclType::OfferRunner, "source.child.collection"),
6432                Error::extraneous_field(DeclType::OfferRunner, "target.child.collection"),
6433                Error::extraneous_field(DeclType::OfferResolver, "source.child.collection"),
6434                Error::extraneous_field(DeclType::OfferResolver, "target.child.collection"),
6435                Error::extraneous_field(DeclType::OfferDictionary, "source.child.collection"),
6436                Error::extraneous_field(DeclType::OfferDictionary, "target.child.collection"),
6437            ])),
6438        },
6439        test_validate_offers_invalid_filtered_service_fields => {
6440            input = {
6441                let mut decl = new_component_decl();
6442                decl.offers = Some(vec![
6443                    fdecl::Offer::Service(fdecl::OfferService {
6444                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6445                        source_name: Some("fuchsia.logger.Log".to_string()),
6446                        target: Some(fdecl::Ref::Child(
6447                            fdecl::ChildRef {
6448                                name: "logger".to_string(),
6449                                collection: None,
6450                            }
6451                        )),
6452                        target_name: Some("fuchsia.logger.Log".to_string()),
6453                        source_instance_filter: Some(vec![]),
6454                        ..Default::default()
6455                    }),
6456                    fdecl::Offer::Service(fdecl::OfferService {
6457                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6458                        source_name: Some("fuchsia.logger.Log".to_string()),
6459                        target: Some(fdecl::Ref::Child(
6460                            fdecl::ChildRef {
6461                                name: "logger".to_string(),
6462                                collection: None,
6463                            }
6464                        )),
6465                        target_name: Some("fuchsia.logger.Log2".to_string()),
6466                        source_instance_filter: Some(vec!["^badname".to_string()]),
6467                        ..Default::default()
6468                    }),
6469                    fdecl::Offer::Service(fdecl::OfferService {
6470                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6471                        source_name: Some("fuchsia.logger.Log".to_string()),
6472                        target: Some(fdecl::Ref::Child(
6473                            fdecl::ChildRef {
6474                                name: "logger".to_string(),
6475                                collection: None,
6476                            }
6477                        )),
6478                        target_name: Some("fuchsia.logger.Log1".to_string()),
6479                        renamed_instances: Some(vec![fdecl::NameMapping{source_name: "a".to_string(), target_name: "b".to_string()}, fdecl::NameMapping{source_name: "c".to_string(), target_name: "b".to_string()}]),
6480                        ..Default::default()
6481                    }),
6482                    fdecl::Offer::Service(fdecl::OfferService {
6483                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6484                        source_name: Some("fuchsia.logger.Log".to_string()),
6485                        target: Some(fdecl::Ref::Child(
6486                            fdecl::ChildRef {
6487                                name: "logger".to_string(),
6488                                collection: None,
6489                            }
6490                        )),
6491                        target_name: Some("fuchsia.logger.Log3".to_string()),
6492                        renamed_instances: Some(vec![
6493                            fdecl::NameMapping {
6494                                source_name: "^badname".to_string(),
6495                                target_name: "^badname".to_string(),
6496                            }
6497                        ]),
6498                        ..Default::default()
6499                    })
6500                ]);
6501                decl.children = Some(vec![
6502                    fdecl::Child {
6503                        name: Some("logger".to_string()),
6504                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
6505                        startup: Some(fdecl::StartupMode::Lazy),
6506                        on_terminate: None,
6507                        environment: None,
6508                        ..Default::default()
6509                    },
6510                ]);
6511                decl
6512            },
6513            result = Err(ErrorList::new(vec![
6514                Error::invalid_field(DeclType::OfferService, "source_instance_filter"),
6515                Error::invalid_field(DeclType::OfferService, "source_instance_filter"),
6516                Error::invalid_field(DeclType::OfferService, "renamed_instances"),
6517                Error::invalid_field(DeclType::OfferService, "renamed_instances.source_name"),
6518                Error::invalid_field(DeclType::OfferService, "renamed_instances.target_name"),
6519            ])),
6520        },
6521        test_validate_offers_invalid_identifiers => {
6522            input = {
6523                let mut decl = new_component_decl();
6524                decl.offers = Some(vec![
6525                    fdecl::Offer::Service(fdecl::OfferService {
6526                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6527                            name: "^bad".to_string(),
6528                            collection: None,
6529                        })),
6530                        source_name: Some("foo/".to_string()),
6531                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6532                            name: "%bad".to_string(),
6533                            collection: None,
6534                        })),
6535                        target_name: Some("/".to_string()),
6536                        ..Default::default()
6537                    }),
6538                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6539                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6540                            name: "^bad".to_string(),
6541                            collection: None,
6542                        })),
6543                        source_name: Some("foo/".to_string()),
6544                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6545                            name: "%bad".to_string(),
6546                            collection: None,
6547                        })),
6548                        target_name: Some("/".to_string()),
6549                        dependency_type: Some(fdecl::DependencyType::Strong),
6550                        ..Default::default()
6551                    }),
6552                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6553                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6554                            name: "^bad".to_string(),
6555                            collection: None,
6556                        })),
6557                        source_name: Some("foo/".to_string()),
6558                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6559                            name: "%bad".to_string(),
6560                            collection: None,
6561                        })),
6562                        target_name: Some("/".to_string()),
6563                        rights: Some(fio::Operations::CONNECT),
6564                        subdir: Some("/foo".to_string()),
6565                        dependency_type: Some(fdecl::DependencyType::Strong),
6566                        ..Default::default()
6567                    }),
6568                    fdecl::Offer::Runner(fdecl::OfferRunner {
6569                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6570                            name: "^bad".to_string(),
6571                            collection: None,
6572                        })),
6573                        source_name: Some("/path".to_string()),
6574                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6575                            name: "%bad".to_string(),
6576                            collection: None,
6577                        })),
6578                        target_name: Some("elf!".to_string()),
6579                        ..Default::default()
6580                    }),
6581                    fdecl::Offer::Resolver(fdecl::OfferResolver {
6582                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6583                            name: "^bad".to_string(),
6584                            collection: None,
6585                        })),
6586                        source_name: Some("/path".to_string()),
6587                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6588                            name: "%bad".to_string(),
6589                            collection: None,
6590                        })),
6591                        target_name: Some("pkg!".to_string()),
6592                        ..Default::default()
6593                    }),
6594                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6595                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6596                            name: "^bad".to_string(),
6597                            collection: None,
6598                        })),
6599                        source_name: Some("/path".to_string()),
6600                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6601                            name: "%bad".to_string(),
6602                            collection: None,
6603                        })),
6604                        target_name: Some("pkg!".to_string()),
6605                        dependency_type: Some(fdecl::DependencyType::Strong),
6606                        ..Default::default()
6607                    }),
6608                ]);
6609                decl
6610            },
6611            result = Err(ErrorList::new(vec![
6612                Error::invalid_field(DeclType::OfferService, "source.child.name"),
6613                Error::invalid_field(DeclType::OfferService, "source_name"),
6614                Error::invalid_field(DeclType::OfferService, "target.child.name"),
6615                Error::invalid_field(DeclType::OfferService, "target_name"),
6616                Error::invalid_field(DeclType::OfferProtocol, "source.child.name"),
6617                Error::invalid_field(DeclType::OfferProtocol, "source_name"),
6618                Error::invalid_field(DeclType::OfferProtocol, "target.child.name"),
6619                Error::invalid_field(DeclType::OfferProtocol, "target_name"),
6620                Error::invalid_field(DeclType::OfferDirectory, "source.child.name"),
6621                Error::invalid_field(DeclType::OfferDirectory, "source_name"),
6622                Error::invalid_field(DeclType::OfferDirectory, "target.child.name"),
6623                Error::invalid_field(DeclType::OfferDirectory, "target_name"),
6624                Error::invalid_field(DeclType::OfferDirectory, "subdir"),
6625                Error::invalid_field(DeclType::OfferRunner, "source.child.name"),
6626                Error::invalid_field(DeclType::OfferRunner, "source_name"),
6627                Error::invalid_field(DeclType::OfferRunner, "target.child.name"),
6628                Error::invalid_field(DeclType::OfferRunner, "target_name"),
6629                Error::invalid_field(DeclType::OfferResolver, "source.child.name"),
6630                Error::invalid_field(DeclType::OfferResolver, "source_name"),
6631                Error::invalid_field(DeclType::OfferResolver, "target.child.name"),
6632                Error::invalid_field(DeclType::OfferResolver, "target_name"),
6633                Error::invalid_field(DeclType::OfferDictionary, "source.child.name"),
6634                Error::invalid_field(DeclType::OfferDictionary, "source_name"),
6635                Error::invalid_field(DeclType::OfferDictionary, "target.child.name"),
6636                Error::invalid_field(DeclType::OfferDictionary, "target_name"),
6637            ])),
6638        },
6639        test_validate_offers_target_equals_source => {
6640            input = {
6641                let mut decl = new_component_decl();
6642                decl.offers = Some(vec![
6643                    fdecl::Offer::Service(fdecl::OfferService {
6644                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6645                            name: "logger".to_string(),
6646                            collection: None,
6647                        })),
6648                        source_name: Some("logger".to_string()),
6649                        target: Some(fdecl::Ref::Child(
6650                        fdecl::ChildRef {
6651                            name: "logger".to_string(),
6652                            collection: None,
6653                        }
6654                        )),
6655                        target_name: Some("logger".to_string()),
6656                        ..Default::default()
6657                    }),
6658                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6659                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6660                            name: "logger".to_string(),
6661                            collection: None,
6662                        })),
6663                        source_name: Some("legacy_logger".to_string()),
6664                        target: Some(fdecl::Ref::Child(
6665                        fdecl::ChildRef {
6666                            name: "logger".to_string(),
6667                            collection: None,
6668                        }
6669                        )),
6670                        target_name: Some("weak_legacy_logger".to_string()),
6671                        dependency_type: Some(fdecl::DependencyType::Weak),
6672                        ..Default::default()
6673                    }),
6674                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6675                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6676                            name: "logger".to_string(),
6677                            collection: None,
6678                        })),
6679                        source_name: Some("legacy_logger".to_string()),
6680                        target: Some(fdecl::Ref::Child(
6681                        fdecl::ChildRef {
6682                            name: "logger".to_string(),
6683                            collection: None,
6684                        }
6685                        )),
6686                        target_name: Some("strong_legacy_logger".to_string()),
6687                        dependency_type: Some(fdecl::DependencyType::Strong),
6688                        ..Default::default()
6689                    }),
6690                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6691                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6692                            name: "logger".to_string(),
6693                            collection: None,
6694                        })),
6695                        source_name: Some("assets".to_string()),
6696                        target: Some(fdecl::Ref::Child(
6697                        fdecl::ChildRef {
6698                            name: "logger".to_string(),
6699                            collection: None,
6700                        }
6701                        )),
6702                        target_name: Some("assets".to_string()),
6703                        rights: Some(fio::Operations::CONNECT),
6704                        subdir: None,
6705                        dependency_type: Some(fdecl::DependencyType::Strong),
6706                        ..Default::default()
6707                    }),
6708                    fdecl::Offer::Runner(fdecl::OfferRunner {
6709                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6710                            name: "logger".to_string(),
6711                            collection: None,
6712                        })),
6713                        source_name: Some("web".to_string()),
6714                        target: Some(fdecl::Ref::Child(
6715                        fdecl::ChildRef {
6716                            name: "logger".to_string(),
6717                            collection: None,
6718                        }
6719                        )),
6720                        target_name: Some("web".to_string()),
6721                        ..Default::default()
6722                    }),
6723                    fdecl::Offer::Resolver(fdecl::OfferResolver {
6724                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6725                            name: "logger".to_string(),
6726                            collection: None,
6727                        })),
6728                        source_name: Some("pkg".to_string()),
6729                        target: Some(fdecl::Ref::Child(
6730                        fdecl::ChildRef {
6731                            name: "logger".to_string(),
6732                            collection: None,
6733                        }
6734                        )),
6735                        target_name: Some("pkg".to_string()),
6736                        ..Default::default()
6737                    }),
6738                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6739                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6740                            name: "logger".to_string(),
6741                            collection: None,
6742                        })),
6743                        source_name: Some("dict".to_string()),
6744                        target: Some(fdecl::Ref::Child(
6745                        fdecl::ChildRef {
6746                            name: "logger".to_string(),
6747                            collection: None,
6748                        }
6749                        )),
6750                        target_name: Some("dict".to_string()),
6751                        dependency_type: Some(fdecl::DependencyType::Strong),
6752                        ..Default::default()
6753                    }),
6754                ]);
6755                decl.children = Some(vec![fdecl::Child{
6756                    name: Some("logger".to_string()),
6757                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
6758                    startup: Some(fdecl::StartupMode::Lazy),
6759                    on_terminate: None,
6760                    environment: None,
6761                    ..Default::default()
6762                }]);
6763                decl
6764            },
6765            result = Err(ErrorList::new(vec![
6766                Error::dependency_cycle("{{child logger -> child logger}}".to_string()),
6767            ])),
6768        },
6769        test_validate_offers_storage_target_equals_source => {
6770            input = fdecl::Component {
6771                offers: Some(vec![
6772                    fdecl::Offer::Storage(fdecl::OfferStorage {
6773                        source_name: Some("data".to_string()),
6774                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef { })),
6775                        target: Some(fdecl::Ref::Child(
6776                            fdecl::ChildRef {
6777                                name: "logger".to_string(),
6778                                collection: None,
6779                            }
6780                        )),
6781                        target_name: Some("data".to_string()),
6782                        ..Default::default()
6783                    })
6784                ]),
6785                capabilities: Some(vec![
6786                    fdecl::Capability::Storage(fdecl::Storage {
6787                        name: Some("data".to_string()),
6788                        backing_dir: Some("minfs".to_string()),
6789                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6790                            name: "logger".to_string(),
6791                            collection: None,
6792                        })),
6793                        subdir: None,
6794                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
6795                        ..Default::default()
6796                    }),
6797                ]),
6798                children: Some(vec![
6799                    fdecl::Child {
6800                        name: Some("logger".to_string()),
6801                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
6802                        startup: Some(fdecl::StartupMode::Lazy),
6803                        on_terminate: None,
6804                        environment: None,
6805                        ..Default::default()
6806                    },
6807                ]),
6808                ..new_component_decl()
6809            },
6810            result = Err(ErrorList::new(vec![
6811                Error::dependency_cycle("{{child logger -> capability data -> child logger}}".to_string()),
6812            ])),
6813        },
6814        test_validate_offers_invalid_child => {
6815            input = {
6816                let mut decl = new_component_decl();
6817                decl.offers = Some(vec![
6818                    fdecl::Offer::Service(fdecl::OfferService {
6819                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6820                            name: "logger".to_string(),
6821                            collection: None,
6822                        })),
6823                        source_name: Some("fuchsia.logger.Log".to_string()),
6824                        target: Some(fdecl::Ref::Child(
6825                        fdecl::ChildRef {
6826                            name: "netstack".to_string(),
6827                            collection: None,
6828                        }
6829                        )),
6830                        target_name: Some("fuchsia.logger.Log".to_string()),
6831                        ..Default::default()
6832                    }),
6833                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6834                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6835                            name: "logger".to_string(),
6836                            collection: None,
6837                        })),
6838                        source_name: Some("fuchsia.logger.LegacyLog".to_string()),
6839                        target: Some(fdecl::Ref::Child(
6840                        fdecl::ChildRef {
6841                            name: "netstack".to_string(),
6842                            collection: None,
6843                        }
6844                        )),
6845                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
6846                        dependency_type: Some(fdecl::DependencyType::Strong),
6847                        ..Default::default()
6848                    }),
6849                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6850                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6851                            name: "logger".to_string(),
6852                            collection: None,
6853                        })),
6854                        source_name: Some("assets".to_string()),
6855                        target: Some(fdecl::Ref::Collection(
6856                        fdecl::CollectionRef { name: "modular".to_string() }
6857                        )),
6858                        target_name: Some("assets".to_string()),
6859                        rights: Some(fio::Operations::CONNECT),
6860                        subdir: None,
6861                        dependency_type: Some(fdecl::DependencyType::Weak),
6862                        ..Default::default()
6863                    }),
6864                ]);
6865                decl.capabilities = Some(vec![
6866                    fdecl::Capability::Storage(fdecl::Storage {
6867                        name: Some("memfs".to_string()),
6868                        backing_dir: Some("memfs".to_string()),
6869                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6870                            name: "logger".to_string(),
6871                            collection: None,
6872                        })),
6873                        subdir: None,
6874                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
6875                        ..Default::default()
6876                    }),
6877                ]);
6878                decl.children = Some(vec![
6879                    fdecl::Child {
6880                        name: Some("netstack".to_string()),
6881                        url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
6882                        startup: Some(fdecl::StartupMode::Lazy),
6883                        on_terminate: None,
6884                        environment: None,
6885                        ..Default::default()
6886                    },
6887                ]);
6888                decl.collections = Some(vec![
6889                    fdecl::Collection {
6890                        name: Some("modular".to_string()),
6891                        durability: Some(fdecl::Durability::Transient),
6892                        environment: None,
6893                        allowed_offers: Some(fdecl::AllowedOffers::StaticAndDynamic),
6894                        allow_long_names: None,
6895                        ..Default::default()
6896                    },
6897                ]);
6898                decl
6899            },
6900            result = Err(ErrorList::new(vec![
6901                Error::invalid_child(DeclType::Storage, "source", "logger"),
6902                Error::invalid_child(DeclType::OfferService, "source", "logger"),
6903                Error::invalid_child(DeclType::OfferProtocol, "source", "logger"),
6904                Error::invalid_child(DeclType::OfferDirectory, "source", "logger"),
6905            ])),
6906        },
6907        test_validate_offers_invalid_source_capability => {
6908            input = {
6909                fdecl::Component {
6910                    offers: Some(vec![
6911                        fdecl::Offer::Protocol(fdecl::OfferProtocol {
6912                            source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
6913                                name: "this-storage-doesnt-exist".to_string(),
6914                            })),
6915                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
6916                            target: Some(fdecl::Ref::Child(
6917                            fdecl::ChildRef {
6918                                name: "netstack".to_string(),
6919                                collection: None,
6920                            }
6921                            )),
6922                            target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
6923                            dependency_type: Some(fdecl::DependencyType::Strong),
6924                            ..Default::default()
6925                        }),
6926                    ]),
6927                    ..new_component_decl()
6928                }
6929            },
6930            result = Err(ErrorList::new(vec![
6931                Error::invalid_capability(DeclType::OfferProtocol, "source", "this-storage-doesnt-exist"),
6932                Error::invalid_child(DeclType::OfferProtocol, "target", "netstack"),
6933            ])),
6934        },
6935        test_validate_offers_target => {
6936            input = {
6937                let mut decl = new_component_decl();
6938                decl.offers = Some(vec![
6939                    fdecl::Offer::Service(fdecl::OfferService {
6940                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6941                            name: "modular".into()
6942                        })),
6943                        source_name: Some("logger".to_string()),
6944                        target: Some(fdecl::Ref::Child(
6945                        fdecl::ChildRef {
6946                            name: "netstack".to_string(),
6947                            collection: None,
6948                        }
6949                        )),
6950                        target_name: Some("fuchsia.logger.Log".to_string()),
6951                        ..Default::default()
6952                    }),
6953                    fdecl::Offer::Service(fdecl::OfferService {
6954                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6955                            name: "modular".into()
6956                        })),
6957                        source_name: Some("logger".to_string()),
6958                        target: Some(fdecl::Ref::Child(
6959                        fdecl::ChildRef {
6960                            name: "netstack".to_string(),
6961                            collection: None,
6962                        }
6963                        )),
6964                        target_name: Some("fuchsia.logger.Log".to_string()),
6965                        ..Default::default()
6966                    }),
6967                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6968                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6969                        source_name: Some("fuchsia.logger.LegacyLog".to_string()),
6970                        target: Some(fdecl::Ref::Child(
6971                        fdecl::ChildRef {
6972                            name: "netstack".to_string(),
6973                            collection: None,
6974                        }
6975                        )),
6976                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
6977                        dependency_type: Some(fdecl::DependencyType::Strong),
6978                        ..Default::default()
6979                    }),
6980                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6981                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6982                        source_name: Some("fuchsia.logger.LegacyLog".to_string()),
6983                        target: Some(fdecl::Ref::Child(
6984                        fdecl::ChildRef {
6985                            name: "netstack".to_string(),
6986                            collection: None,
6987                        }
6988                        )),
6989                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
6990                        dependency_type: Some(fdecl::DependencyType::Strong),
6991                        ..Default::default()
6992                    }),
6993                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6994                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6995                        source_name: Some("assets".to_string()),
6996                        target: Some(fdecl::Ref::Collection(
6997                        fdecl::CollectionRef { name: "modular".to_string() }
6998                        )),
6999                        target_name: Some("assets".to_string()),
7000                        rights: Some(fio::Operations::CONNECT),
7001                        subdir: None,
7002                        dependency_type: Some(fdecl::DependencyType::Strong),
7003                        ..Default::default()
7004                    }),
7005                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7006                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7007                        source_name: Some("assets".to_string()),
7008                        target: Some(fdecl::Ref::Collection(
7009                        fdecl::CollectionRef { name: "modular".to_string() }
7010                        )),
7011                        target_name: Some("assets".to_string()),
7012                        rights: Some(fio::Operations::CONNECT),
7013                        subdir: None,
7014                        dependency_type: Some(fdecl::DependencyType::Weak),
7015                        ..Default::default()
7016                    }),
7017                    fdecl::Offer::Storage(fdecl::OfferStorage {
7018                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7019                        source_name: Some("data".to_string()),
7020                        target: Some(fdecl::Ref::Collection(
7021                        fdecl::CollectionRef { name: "modular".to_string() }
7022                        )),
7023                        target_name: Some("data".to_string()),
7024                        ..Default::default()
7025                    }),
7026                    fdecl::Offer::Storage(fdecl::OfferStorage {
7027                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7028                        source_name: Some("data".to_string()),
7029                        target: Some(fdecl::Ref::Collection(
7030                        fdecl::CollectionRef { name: "modular".to_string() }
7031                        )),
7032                        target_name: Some("data".to_string()),
7033                        ..Default::default()
7034                    }),
7035                    fdecl::Offer::Runner(fdecl::OfferRunner {
7036                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7037                        source_name: Some("elf".to_string()),
7038                        target: Some(fdecl::Ref::Collection(
7039                        fdecl::CollectionRef { name: "modular".to_string() }
7040                        )),
7041                        target_name: Some("duplicated".to_string()),
7042                        ..Default::default()
7043                    }),
7044                    fdecl::Offer::Runner(fdecl::OfferRunner {
7045                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7046                        source_name: Some("elf".to_string()),
7047                        target: Some(fdecl::Ref::Collection(
7048                        fdecl::CollectionRef { name: "modular".to_string() }
7049                        )),
7050                        target_name: Some("duplicated".to_string()),
7051                        ..Default::default()
7052                    }),
7053                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7054                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7055                        source_name: Some("pkg".to_string()),
7056                        target: Some(fdecl::Ref::Collection(
7057                        fdecl::CollectionRef { name: "modular".to_string() }
7058                        )),
7059                        target_name: Some("duplicated".to_string()),
7060                        ..Default::default()
7061                    }),
7062                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
7063                        source_name: Some("started".to_string()),
7064                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7065                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "netstack".to_string(), collection: None})),
7066                        target_name: Some("started".to_string()),
7067                        ..Default::default()
7068                    }),
7069                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
7070                        source_name: Some("started".to_string()),
7071                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7072                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "netstack".to_string(), collection: None})),
7073                        target_name: Some("started".to_string()),
7074                        ..Default::default()
7075                    }),
7076                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7077                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7078                        source_name: Some("a".to_string()),
7079                        target: Some(fdecl::Ref::Collection(
7080                            fdecl::CollectionRef { name: "modular".to_string() }
7081                        )),
7082                        target_name: Some("dict".to_string()),
7083                        dependency_type: Some(fdecl::DependencyType::Strong),
7084                        ..Default::default()
7085                    }),
7086                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7087                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7088                        source_name: Some("b".to_string()),
7089                        target: Some(fdecl::Ref::Collection(
7090                            fdecl::CollectionRef { name: "modular".to_string() }
7091                        )),
7092                        target_name: Some("dict".to_string()),
7093                        dependency_type: Some(fdecl::DependencyType::Strong),
7094                        ..Default::default()
7095                    }),
7096                ]);
7097                decl.children = Some(vec![
7098                    fdecl::Child{
7099                        name: Some("netstack".to_string()),
7100                        url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
7101                        startup: Some(fdecl::StartupMode::Eager),
7102                        on_terminate: None,
7103                        environment: None,
7104                        ..Default::default()
7105                    },
7106                ]);
7107                decl.collections = Some(vec![
7108                    fdecl::Collection{
7109                        name: Some("modular".to_string()),
7110                        durability: Some(fdecl::Durability::Transient),
7111                        environment: None,
7112                        allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7113                        allow_long_names: None,
7114                        ..Default::default()
7115                    },
7116                ]);
7117                decl
7118            },
7119            result = Err(ErrorList::new(vec![
7120                // Duplicate services are allowed, for aggregation.
7121                Error::duplicate_field(DeclType::OfferProtocol, "target_name", "fuchsia.logger.LegacyLog"),
7122                Error::duplicate_field(DeclType::OfferDirectory, "target_name", "assets"),
7123                Error::duplicate_field(DeclType::OfferStorage, "target_name", "data"),
7124                Error::duplicate_field(DeclType::OfferRunner, "target_name", "duplicated"),
7125                Error::duplicate_field(DeclType::OfferResolver, "target_name", "duplicated"),
7126                Error::duplicate_field(DeclType::OfferEventStream, "target_name", "started"),
7127                Error::duplicate_field(DeclType::OfferDictionary, "target_name", "dict"),
7128            ])),
7129        },
7130        test_validate_offers_target_invalid => {
7131            input = {
7132                let mut decl = new_component_decl();
7133                decl.offers = Some(vec![
7134                    fdecl::Offer::Service(fdecl::OfferService {
7135                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7136                        source_name: Some("logger".to_string()),
7137                        target: Some(fdecl::Ref::Child(
7138                        fdecl::ChildRef {
7139                            name: "netstack".to_string(),
7140                            collection: None,
7141                        }
7142                        )),
7143                        target_name: Some("fuchsia.logger.Log".to_string()),
7144                        ..Default::default()
7145                    }),
7146                    fdecl::Offer::Service(fdecl::OfferService {
7147                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7148                        source_name: Some("logger".to_string()),
7149                        target: Some(fdecl::Ref::Collection(
7150                        fdecl::CollectionRef { name: "modular".to_string(), }
7151                        )),
7152                        target_name: Some("fuchsia.logger.Log".to_string()),
7153                        ..Default::default()
7154                    }),
7155                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7156                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7157                        source_name: Some("legacy_logger".to_string()),
7158                        target: Some(fdecl::Ref::Child(
7159                        fdecl::ChildRef {
7160                            name: "netstack".to_string(),
7161                            collection: None,
7162                        }
7163                        )),
7164                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7165                        dependency_type: Some(fdecl::DependencyType::Weak),
7166                        ..Default::default()
7167                    }),
7168                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7169                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7170                        source_name: Some("legacy_logger".to_string()),
7171                        target: Some(fdecl::Ref::Collection(
7172                        fdecl::CollectionRef { name: "modular".to_string(), }
7173                        )),
7174                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7175                        dependency_type: Some(fdecl::DependencyType::Strong),
7176                        ..Default::default()
7177                    }),
7178                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7179                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7180                        source_name: Some("assets".to_string()),
7181                        target: Some(fdecl::Ref::Child(
7182                        fdecl::ChildRef {
7183                            name: "netstack".to_string(),
7184                            collection: None,
7185                        }
7186                        )),
7187                        target_name: Some("data".to_string()),
7188                        rights: Some(fio::Operations::CONNECT),
7189                        subdir: None,
7190                        dependency_type: Some(fdecl::DependencyType::Strong),
7191                        ..Default::default()
7192                    }),
7193                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7194                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7195                        source_name: Some("assets".to_string()),
7196                        target: Some(fdecl::Ref::Collection(
7197                        fdecl::CollectionRef { name: "modular".to_string(), }
7198                        )),
7199                        target_name: Some("data".to_string()),
7200                        rights: Some(fio::Operations::CONNECT),
7201                        subdir: None,
7202                        dependency_type: Some(fdecl::DependencyType::Weak),
7203                        ..Default::default()
7204                    }),
7205                    fdecl::Offer::Storage(fdecl::OfferStorage {
7206                        source_name: Some("data".to_string()),
7207                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7208                        target: Some(fdecl::Ref::Child(
7209                            fdecl::ChildRef {
7210                                name: "netstack".to_string(),
7211                                collection: None,
7212                            }
7213                        )),
7214                        target_name: Some("data".to_string()),
7215                        ..Default::default()
7216                    }),
7217                    fdecl::Offer::Storage(fdecl::OfferStorage {
7218                        source_name: Some("data".to_string()),
7219                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7220                        target: Some(fdecl::Ref::Collection(
7221                            fdecl::CollectionRef { name: "modular".to_string(), }
7222                        )),
7223                        target_name: Some("data".to_string()),
7224                        ..Default::default()
7225                    }),
7226                    fdecl::Offer::Runner(fdecl::OfferRunner {
7227                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7228                        source_name: Some("elf".to_string()),
7229                        target: Some(fdecl::Ref::Child(
7230                            fdecl::ChildRef {
7231                                name: "netstack".to_string(),
7232                                collection: None,
7233                            }
7234                        )),
7235                        target_name: Some("elf".to_string()),
7236                        ..Default::default()
7237                    }),
7238                    fdecl::Offer::Runner(fdecl::OfferRunner {
7239                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7240                        source_name: Some("elf".to_string()),
7241                        target: Some(fdecl::Ref::Collection(
7242                        fdecl::CollectionRef { name: "modular".to_string(), }
7243                        )),
7244                        target_name: Some("elf".to_string()),
7245                        ..Default::default()
7246                    }),
7247                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7248                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7249                        source_name: Some("pkg".to_string()),
7250                        target: Some(fdecl::Ref::Child(
7251                            fdecl::ChildRef {
7252                                name: "netstack".to_string(),
7253                                collection: None,
7254                            }
7255                        )),
7256                        target_name: Some("pkg".to_string()),
7257                        ..Default::default()
7258                    }),
7259                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7260                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7261                        source_name: Some("pkg".to_string()),
7262                        target: Some(fdecl::Ref::Collection(
7263                        fdecl::CollectionRef { name: "modular".to_string(), }
7264                        )),
7265                        target_name: Some("pkg".to_string()),
7266                        ..Default::default()
7267                    }),
7268                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7269                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7270                        source_name: Some("pkg".to_string()),
7271                        target: Some(fdecl::Ref::Child(
7272                            fdecl::ChildRef {
7273                                name: "netstack".to_string(),
7274                                collection: None,
7275                            }
7276                        )),
7277                        target_name: Some("pkg".to_string()),
7278                        dependency_type: Some(fdecl::DependencyType::Strong),
7279                        ..Default::default()
7280                    }),
7281                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7282                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7283                        source_name: Some("pkg".to_string()),
7284                        target: Some(fdecl::Ref::Collection(
7285                        fdecl::CollectionRef { name: "modular".to_string(), }
7286                        )),
7287                        target_name: Some("pkg".to_string()),
7288                        dependency_type: Some(fdecl::DependencyType::Strong),
7289                        ..Default::default()
7290                    }),
7291                ]);
7292                decl
7293            },
7294            result = Err(ErrorList::new(vec![
7295                Error::invalid_child(DeclType::OfferService, "target", "netstack"),
7296                Error::invalid_collection(DeclType::OfferService, "target", "modular"),
7297                Error::invalid_child(DeclType::OfferProtocol, "target", "netstack"),
7298                Error::invalid_collection(DeclType::OfferProtocol, "target", "modular"),
7299                Error::invalid_child(DeclType::OfferDirectory, "target", "netstack"),
7300                Error::invalid_collection(DeclType::OfferDirectory, "target", "modular"),
7301                Error::invalid_child(DeclType::OfferStorage, "target", "netstack"),
7302                Error::invalid_collection(DeclType::OfferStorage, "target", "modular"),
7303                Error::invalid_child(DeclType::OfferRunner, "target", "netstack"),
7304                Error::invalid_collection(DeclType::OfferRunner, "target", "modular"),
7305                Error::invalid_child(DeclType::OfferResolver, "target", "netstack"),
7306                Error::invalid_collection(DeclType::OfferResolver, "target", "modular"),
7307                Error::invalid_child(DeclType::OfferDictionary, "target", "netstack"),
7308                Error::invalid_collection(DeclType::OfferDictionary, "target", "modular"),
7309            ])),
7310        },
7311        test_validate_offers_target_dictionary => {
7312            input = fdecl::Component {
7313                offers: Some(vec![
7314                    // Offer to static dictionary is ok
7315                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7316                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7317                        source_name: Some("p".to_string()),
7318                        target: Some(fdecl::Ref::Capability(
7319                            fdecl::CapabilityRef {
7320                                name: "dict".into(),
7321                            },
7322                        )),
7323                        target_name: Some("p".into()),
7324                        dependency_type: Some(fdecl::DependencyType::Strong),
7325                        ..Default::default()
7326                    }),
7327                    // Offer to dynamic dictionary is forbidden
7328                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7329                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7330                        source_name: Some("p".to_string()),
7331                        target: Some(fdecl::Ref::Capability(
7332                            fdecl::CapabilityRef {
7333                                name: "dynamic".into(),
7334                            },
7335                        )),
7336                        target_name: Some("p".into()),
7337                        dependency_type: Some(fdecl::DependencyType::Strong),
7338                        ..Default::default()
7339                    }),
7340                ]),
7341                capabilities: Some(vec![
7342                    fdecl::Capability::Dictionary(fdecl::Dictionary {
7343                        name: Some("dict".into()),
7344                        ..Default::default()
7345                    }),
7346                    fdecl::Capability::Dictionary(fdecl::Dictionary {
7347                        name: Some("dynamic".into()),
7348                        source_path: Some("/out/dir".into()),
7349                        ..Default::default()
7350                    }),
7351                ]),
7352                ..Default::default()
7353            },
7354            result = Err(ErrorList::new(vec![
7355                Error::invalid_field(DeclType::OfferProtocol, "target"),
7356            ])),
7357        },
7358        test_validate_offers_invalid_source_collection => {
7359            input = {
7360                let mut decl = new_component_decl();
7361                decl.collections = Some(vec![
7362                    fdecl::Collection {
7363                        name: Some("col".to_string()),
7364                        durability: Some(fdecl::Durability::Transient),
7365                        allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7366                        allow_long_names: None,
7367                        ..Default::default()
7368                    }
7369                ]);
7370                decl.children = Some(vec![
7371                    fdecl::Child {
7372                        name: Some("child".to_string()),
7373                        url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7374                        startup: Some(fdecl::StartupMode::Lazy),
7375                        on_terminate: None,
7376                        ..Default::default()
7377                    }
7378                ]);
7379                decl.offers = Some(vec![
7380                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7381                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7382                        source_name: Some("a".to_string()),
7383                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7384                        target_name: Some("a".to_string()),
7385                        dependency_type: Some(fdecl::DependencyType::Strong),
7386                        ..Default::default()
7387                    }),
7388                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7389                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7390                        source_name: Some("b".to_string()),
7391                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7392                        target_name: Some("b".to_string()),
7393                        rights: Some(fio::Operations::CONNECT),
7394                        subdir: None,
7395                        dependency_type: Some(fdecl::DependencyType::Strong),
7396                        ..Default::default()
7397                    }),
7398                    fdecl::Offer::Storage(fdecl::OfferStorage {
7399                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7400                        source_name: Some("c".to_string()),
7401                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7402                        target_name: Some("c".to_string()),
7403                        ..Default::default()
7404                    }),
7405                    fdecl::Offer::Runner(fdecl::OfferRunner {
7406                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7407                        source_name: Some("d".to_string()),
7408                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7409                        target_name: Some("d".to_string()),
7410                        ..Default::default()
7411                    }),
7412                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7413                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7414                        source_name: Some("e".to_string()),
7415                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7416                        target_name: Some("e".to_string()),
7417                        ..Default::default()
7418                    }),
7419                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7420                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7421                        source_name: Some("f".to_string()),
7422                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7423                        target_name: Some("f".to_string()),
7424                        dependency_type: Some(fdecl::DependencyType::Strong),
7425                        ..Default::default()
7426                    }),
7427                ]);
7428                decl
7429            },
7430            result = Err(ErrorList::new(vec![
7431                Error::invalid_field(DeclType::OfferProtocol, "source"),
7432                Error::invalid_field(DeclType::OfferDirectory, "source"),
7433                Error::invalid_field(DeclType::OfferStorage, "source"),
7434                Error::invalid_field(DeclType::OfferRunner, "source"),
7435                Error::invalid_field(DeclType::OfferResolver, "source"),
7436                Error::invalid_field(DeclType::OfferDictionary, "source"),
7437            ])),
7438        },
7439        test_validate_offers_source_collection => {
7440            input = {
7441                let mut decl = new_component_decl();
7442                decl.collections = Some(vec![
7443                    fdecl::Collection {
7444                        name: Some("col".to_string()),
7445                        durability: Some(fdecl::Durability::Transient),
7446                        allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7447                        allow_long_names: None,
7448                        ..Default::default()
7449                    }
7450                ]);
7451                decl.children = Some(vec![
7452                    fdecl::Child {
7453                        name: Some("child".to_string()),
7454                        url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7455                        startup: Some(fdecl::StartupMode::Lazy),
7456                        on_terminate: None,
7457                        ..Default::default()
7458                    }
7459                ]);
7460                decl.offers = Some(vec![
7461                    fdecl::Offer::Service(fdecl::OfferService {
7462                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7463                        source_name: Some("a".to_string()),
7464                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7465                        target_name: Some("a".to_string()),
7466                        ..Default::default()
7467                    })
7468                ]);
7469                decl
7470            },
7471            result = Ok(()),
7472        },
7473        test_validate_offers_invalid_capability_from_self => {
7474            input = {
7475                let mut decl = new_component_decl();
7476                decl.children = Some(vec![
7477                    fdecl::Child {
7478                        name: Some("child".to_string()),
7479                        url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7480                        startup: Some(fdecl::StartupMode::Lazy),
7481                        ..Default::default()
7482                    }
7483                ]);
7484                decl.offers = Some(vec![
7485                    fdecl::Offer::Service(fdecl::OfferService {
7486                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7487                        source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7488                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7489                            name: "child".into(),
7490                            collection: None
7491                        })),
7492                        target_name: Some("foo".into()),
7493                        ..Default::default()
7494                    }),
7495                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7496                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7497                        source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7498                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7499                            name: "child".into(),
7500                            collection: None
7501                        })),
7502                        target_name: Some("bar".into()),
7503                        dependency_type: Some(fdecl::DependencyType::Strong),
7504                        ..Default::default()
7505                    }),
7506                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7507                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7508                        source_name: Some("dir".into()),
7509                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7510                            name: "child".into(),
7511                            collection: None
7512                        })),
7513                        target_name: Some("assets".into()),
7514                        dependency_type: Some(fdecl::DependencyType::Strong),
7515                        ..Default::default()
7516                    }),
7517                    fdecl::Offer::Runner(fdecl::OfferRunner {
7518                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7519                        source_name: Some("source_elf".into()),
7520                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7521                            name: "child".into(),
7522                            collection: None
7523                        })),
7524                        target_name: Some("elf".into()),
7525                        ..Default::default()
7526                    }),
7527                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7528                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7529                        source_name: Some("source_pkg".into()),
7530                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7531                            name: "child".into(),
7532                            collection: None
7533                        })),
7534                        target_name: Some("pkg".into()),
7535                        ..Default::default()
7536                    }),
7537                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7538                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7539                        source_name: Some("source_dict".into()),
7540                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7541                            name: "child".into(),
7542                            collection: None
7543                        })),
7544                        target_name: Some("dict".into()),
7545                        dependency_type: Some(fdecl::DependencyType::Strong),
7546                        ..Default::default()
7547                    }),
7548                    fdecl::Offer::Storage(fdecl::OfferStorage {
7549                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7550                        source_name: Some("source_storage".into()),
7551                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7552                            name: "child".into(),
7553                            collection: None
7554                        })),
7555                        target_name: Some("storage".into()),
7556                        ..Default::default()
7557                    }),
7558                    fdecl::Offer::Config(fdecl::OfferConfiguration {
7559                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7560                        source_name: Some("source_config".into()),
7561                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7562                            name: "child".into(),
7563                            collection: None
7564                        })),
7565                        target_name: Some("config".into()),
7566                        ..Default::default()
7567                    }),
7568                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7569                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7570                        source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7571                        source_dictionary: Some("dict/inner".into()),
7572                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7573                            name: "child".into(),
7574                            collection: None
7575                        })),
7576                        target_name: Some("baz".into()),
7577                        dependency_type: Some(fdecl::DependencyType::Strong),
7578                        ..Default::default()
7579                    }),
7580                ]);
7581                decl
7582            },
7583            result = Err(ErrorList::new(vec![
7584                Error::invalid_capability(
7585                    DeclType::OfferService,
7586                    "source",
7587                    "fuchsia.some.library.SomeProtocol"),
7588                Error::invalid_capability(
7589                    DeclType::OfferProtocol,
7590                    "source",
7591                    "fuchsia.some.library.SomeProtocol"),
7592                Error::invalid_capability(DeclType::OfferDirectory, "source", "dir"),
7593                Error::invalid_capability(DeclType::OfferRunner, "source", "source_elf"),
7594                Error::invalid_capability(DeclType::OfferResolver, "source", "source_pkg"),
7595                Error::invalid_capability(DeclType::OfferDictionary, "source", "source_dict"),
7596                Error::invalid_capability(DeclType::OfferStorage, "source", "source_storage"),
7597                Error::invalid_capability(DeclType::OfferConfig, "source", "source_config"),
7598                Error::invalid_capability(DeclType::OfferProtocol, "source", "dict"),
7599            ])),
7600        },
7601        test_validate_offers_long_dependency_cycle => {
7602            input = {
7603                let mut decl = new_component_decl();
7604                let dependencies = vec![
7605                    ("d", "b"),
7606                    ("a", "b"),
7607                    ("b", "c"),
7608                    ("b", "d"),
7609                    ("c", "a"),
7610                ];
7611                let offers = dependencies.into_iter().map(|(from,to)|
7612                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7613                        source: Some(fdecl::Ref::Child(
7614                        fdecl::ChildRef { name: from.to_string(), collection: None },
7615                        )),
7616                        source_name: Some(format!("thing_{}", from)),
7617                        target: Some(fdecl::Ref::Child(
7618                        fdecl::ChildRef { name: to.to_string(), collection: None },
7619                        )),
7620                        target_name: Some(format!("thing_{}", from)),
7621                        dependency_type: Some(fdecl::DependencyType::Strong),
7622                        ..Default::default()
7623                    })).collect();
7624                let children = ["a", "b", "c", "d"].iter().map(|name| {
7625                    fdecl::Child {
7626                        name: Some(name.to_string()),
7627                        url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
7628                        startup: Some(fdecl::StartupMode::Lazy),
7629                        on_terminate: None,
7630                        environment: None,
7631                        ..Default::default()
7632                    }
7633                }).collect();
7634                decl.offers = Some(offers);
7635                decl.children = Some(children);
7636                decl
7637            },
7638            result = Err(ErrorList::new(vec![
7639                Error::dependency_cycle(directed_graph::Error::CyclesDetected([vec!["child a", "child b", "child c", "child a"], vec!["child b", "child d", "child b"]].iter().cloned().collect()).format_cycle()),
7640            ])),
7641        },
7642        test_validate_offers_not_required_invalid_source_service => {
7643            input = {
7644                let mut decl = generate_offer_different_source_and_availability_decl(
7645                    |source, availability, target_name|
7646                        fdecl::Offer::Service(fdecl::OfferService {
7647                            source: Some(source),
7648                            source_name: Some("fuchsia.examples.Echo".to_string()),
7649                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7650                                name: "sink".to_string(),
7651                                collection: None,
7652                            })),
7653                            target_name: Some(target_name.into()),
7654                            availability: Some(availability),
7655                            ..Default::default()
7656                        })
7657                );
7658                decl.capabilities = Some(vec![
7659                    fdecl::Capability::Service(fdecl::Service {
7660                        name: Some("fuchsia.examples.Echo".to_string()),
7661                        source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
7662                        ..Default::default()
7663                    }),
7664                ]);
7665                decl
7666            },
7667            result = {
7668                Err(ErrorList::new(vec![
7669                    Error::availability_must_be_optional(
7670                        DeclType::OfferService,
7671                        "availability",
7672                        Some(&"fuchsia.examples.Echo".to_string()),
7673                    ),
7674                    Error::availability_must_be_optional(
7675                        DeclType::OfferService,
7676                        "availability",
7677                        Some(&"fuchsia.examples.Echo".to_string()),
7678                    ),
7679                ]))
7680            },
7681        },
7682        test_validate_offers_not_required_invalid_source_protocol => {
7683            input = {
7684                let mut decl = generate_offer_different_source_and_availability_decl(
7685                    |source, availability, target_name|
7686                        fdecl::Offer::Protocol(fdecl::OfferProtocol {
7687                            source: Some(source),
7688                            source_name: Some("fuchsia.examples.Echo".to_string()),
7689                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7690                                name: "sink".to_string(),
7691                                collection: None,
7692                            })),
7693                            target_name: Some(target_name.into()),
7694                            dependency_type: Some(fdecl::DependencyType::Strong),
7695                            availability: Some(availability),
7696                            ..Default::default()
7697                        })
7698                );
7699                decl.capabilities = Some(vec![
7700                    fdecl::Capability::Protocol(fdecl::Protocol {
7701                        name: Some("fuchsia.examples.Echo".to_string()),
7702                        source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
7703                        ..Default::default()
7704                    }),
7705                ]);
7706                decl
7707            },
7708            result = {
7709                Err(ErrorList::new(vec![
7710                    Error::availability_must_be_optional(
7711                        DeclType::OfferProtocol,
7712                        "availability",
7713                        Some(&"fuchsia.examples.Echo".to_string()),
7714                    ),
7715                    Error::availability_must_be_optional(
7716                        DeclType::OfferProtocol,
7717                        "availability",
7718                        Some(&"fuchsia.examples.Echo".to_string()),
7719                    ),
7720                ]))
7721            },
7722        },
7723        test_validate_offers_not_required_invalid_source_directory => {
7724            input = {
7725                let mut decl = generate_offer_different_source_and_availability_decl(
7726                    |source, availability, target_name|
7727                        fdecl::Offer::Directory(fdecl::OfferDirectory {
7728                            source: Some(source),
7729                            source_name: Some("assets".to_string()),
7730                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7731                                name: "sink".to_string(),
7732                                collection: None,
7733                            })),
7734                            target_name: Some(target_name.into()),
7735                            rights: Some(fio::Operations::CONNECT),
7736                            subdir: None,
7737                            dependency_type: Some(fdecl::DependencyType::Weak),
7738                            availability: Some(availability),
7739                            ..Default::default()
7740                        })
7741                );
7742                decl.capabilities = Some(vec![
7743                    fdecl::Capability::Directory(fdecl::Directory {
7744                        name: Some("assets".to_string()),
7745                        source_path: Some("/assets".to_string()),
7746                        rights: Some(fio::Operations::CONNECT),
7747                        ..Default::default()
7748                    }),
7749                ]);
7750                decl
7751            },
7752            result = {
7753                Err(ErrorList::new(vec![
7754                    Error::availability_must_be_optional(
7755                        DeclType::OfferDirectory,
7756                        "availability",
7757                        Some(&"assets".to_string()),
7758                    ),
7759                    Error::availability_must_be_optional(
7760                        DeclType::OfferDirectory,
7761                        "availability",
7762                        Some(&"assets".to_string()),
7763                    ),
7764                ]))
7765            },
7766        },
7767        test_validate_offers_not_required_invalid_source_storage => {
7768            input = {
7769                let mut decl = new_component_decl();
7770                decl.children = Some(vec![
7771                    fdecl::Child {
7772                        name: Some("sink".to_string()),
7773                        url: Some("fuchsia-pkg://fuchsia.com/sink#meta/sink.cm".to_string()),
7774                        startup: Some(fdecl::StartupMode::Lazy),
7775                        on_terminate: None,
7776                        environment: None,
7777                        ..Default::default()
7778                    },
7779                ]);
7780                decl.capabilities = Some(vec![
7781                    fdecl::Capability::Storage(fdecl::Storage {
7782                        name: Some("data".to_string()),
7783                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
7784                        backing_dir: Some("minfs".to_string()),
7785                        subdir: None,
7786                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
7787                        ..Default::default()
7788                    }),
7789                ]);
7790                let new_offer = |source: fdecl::Ref, availability: fdecl::Availability,
7791                                        target_name: &str|
7792                {
7793                    fdecl::Offer::Storage(fdecl::OfferStorage {
7794                        source: Some(source),
7795                        source_name: Some("data".to_string()),
7796                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7797                            name: "sink".to_string(),
7798                            collection: None,
7799                        })),
7800                        target_name: Some(target_name.into()),
7801                        availability: Some(availability),
7802                        ..Default::default()
7803                    })
7804                };
7805                decl.offers = Some(vec![
7806                    // These offers are fine, offers with a source of parent or void can be
7807                    // optional.
7808                    new_offer(
7809                        fdecl::Ref::Parent(fdecl::ParentRef {}),
7810                        fdecl::Availability::Required,
7811                        "data0",
7812                    ),
7813                    new_offer(
7814                        fdecl::Ref::Parent(fdecl::ParentRef {}),
7815                        fdecl::Availability::Optional,
7816                        "data1",
7817                    ),
7818                    new_offer(
7819                        fdecl::Ref::Parent(fdecl::ParentRef {}),
7820                        fdecl::Availability::SameAsTarget,
7821                        "data2",
7822                    ),
7823                    new_offer(
7824                        fdecl::Ref::VoidType(fdecl::VoidRef {}),
7825                        fdecl::Availability::Optional,
7826                        "data3",
7827                    ),
7828                    // These offers are not fine, offers with a source other than parent or void
7829                    // must be required.
7830                    new_offer(
7831                        fdecl::Ref::Self_(fdecl::SelfRef {}),
7832                        fdecl::Availability::Optional,
7833                        "data4",
7834                    ),
7835                    new_offer(
7836                        fdecl::Ref::Self_(fdecl::SelfRef {}),
7837                        fdecl::Availability::SameAsTarget,
7838                        "data5",
7839                    ),
7840                    // These offers are also not fine, offers with a source of void must be optional
7841                    new_offer(
7842                        fdecl::Ref::VoidType(fdecl::VoidRef {}),
7843                        fdecl::Availability::Required,
7844                        "data6",
7845                    ),
7846                    new_offer(
7847                        fdecl::Ref::VoidType(fdecl::VoidRef {}),
7848                        fdecl::Availability::SameAsTarget,
7849                        "data7",
7850                    ),
7851                ]);
7852                decl
7853            },
7854            result = {
7855                Err(ErrorList::new(vec![
7856                    Error::availability_must_be_optional(
7857                        DeclType::OfferStorage,
7858                        "availability",
7859                        Some(&"data".to_string()),
7860                    ),
7861                    Error::availability_must_be_optional(
7862                        DeclType::OfferStorage,
7863                        "availability",
7864                        Some(&"data".to_string()),
7865                    ),
7866                ]))
7867            },
7868        },
7869
7870        test_validate_offers_valid_service_aggregation => {
7871            input = {
7872                let mut decl = new_component_decl();
7873                decl.offers = Some(vec![
7874                    fdecl::Offer::Service(fdecl::OfferService {
7875                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
7876                            name: "coll_a".to_string()
7877                        })),
7878                        source_name: Some("fuchsia.logger.Log".to_string()),
7879                        target: Some(fdecl::Ref::Child(
7880                            fdecl::ChildRef {
7881                                name: "child_c".to_string(),
7882                                collection: None,
7883                            }
7884                        )),
7885                        target_name: Some("fuchsia.logger.Log".to_string()),
7886                        source_instance_filter: None,
7887                        ..Default::default()
7888                    }),
7889                    fdecl::Offer::Service(fdecl::OfferService {
7890                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
7891                            name: "coll_b".to_string()
7892                        })),
7893                        source_name: Some("fuchsia.logger.Log".to_string()),
7894                        target: Some(fdecl::Ref::Child(
7895                            fdecl::ChildRef {
7896                                name: "child_c".to_string(),
7897                                collection: None,
7898                            }
7899                        )),
7900                        target_name: Some("fuchsia.logger.Log".to_string()),
7901                        source_instance_filter: Some(vec!["a_different_default".to_string()]),
7902                        ..Default::default()
7903                    })
7904                ]);
7905                decl.children = Some(vec![
7906                    fdecl::Child {
7907                        name: Some("child_c".to_string()),
7908                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
7909                        startup: Some(fdecl::StartupMode::Lazy),
7910                        ..Default::default()
7911                    },
7912                ]);
7913                decl.collections = Some(vec![
7914                    fdecl::Collection {
7915                        name: Some("coll_a".into()),
7916                        durability: Some(fdecl::Durability::Transient),
7917                        ..Default::default()
7918                    },
7919                    fdecl::Collection {
7920                        name: Some("coll_b".into()),
7921                        durability: Some(fdecl::Durability::Transient),
7922                        ..Default::default()
7923                    },
7924                ]);
7925                decl
7926            },
7927            result = Ok(()),
7928        },
7929
7930        // dictionaries
7931        test_validate_source_dictionary => {
7932            input = fdecl::Component {
7933                program: Some(fdecl::Program {
7934                    runner: Some("elf".into()),
7935                    info: Some(fdata::Dictionary {
7936                        entries: None,
7937                        ..Default::default()
7938                    }),
7939                    ..Default::default()
7940                }),
7941                uses: Some(vec![
7942                    fdecl::Use::Protocol(fdecl::UseProtocol {
7943                        dependency_type: Some(fdecl::DependencyType::Strong),
7944                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
7945                        source_dictionary: Some("bad//".into()),
7946                        source_name: Some("foo".into()),
7947                        target_path: Some("/svc/foo".into()),
7948                        ..Default::default()
7949                    }),
7950                ]),
7951                exposes: Some(vec![
7952                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
7953                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7954                            name: "missing".into(),
7955                            collection: None,
7956                        })),
7957                        source_dictionary: Some("in/dict".into()),
7958                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
7959                        source_name: Some("foo".into()),
7960                        target_name: Some("bar".into()),
7961                        ..Default::default()
7962                    }),
7963                ]),
7964                offers: Some(vec![
7965                    fdecl::Offer::Service(fdecl::OfferService {
7966                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
7967                        source_dictionary: Some("bad//".into()),
7968                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7969                            name: "child".into(),
7970                            collection: None,
7971                        })),
7972                        source_name: Some("foo".into()),
7973                        target_name: Some("bar".into()),
7974                        ..Default::default()
7975                    }),
7976                ]),
7977                children: Some(vec![
7978                    fdecl::Child {
7979                        name: Some("child".into()),
7980                        url: Some("fuchsia-pkg://child".into()),
7981                        startup: Some(fdecl::StartupMode::Lazy),
7982                        ..Default::default()
7983                    },
7984                ]),
7985                ..Default::default()
7986            },
7987            result = Err(ErrorList::new(vec![
7988                Error::invalid_field(DeclType::UseProtocol, "source_dictionary"),
7989                Error::invalid_child(DeclType::ExposeDirectory, "source", "missing"),
7990                Error::invalid_field(DeclType::OfferService, "source_dictionary"),
7991            ])),
7992        },
7993        test_validate_dictionary_too_long => {
7994            input = fdecl::Component {
7995                program: Some(fdecl::Program {
7996                    runner: Some("elf".into()),
7997                    info: Some(fdata::Dictionary {
7998                        entries: None,
7999                        ..Default::default()
8000                    }),
8001                    ..Default::default()
8002                }),
8003                uses: Some(vec![
8004                    fdecl::Use::Protocol(fdecl::UseProtocol {
8005                        dependency_type: Some(fdecl::DependencyType::Strong),
8006                        source: Some(fdecl::Ref::Parent( fdecl::ParentRef {} )),
8007                        source_dictionary: Some("a".repeat(4096)),
8008                        source_name: Some("foo".into()),
8009                        target_path: Some("/svc/foo".into()),
8010                        ..Default::default()
8011                    }),
8012                ]),
8013                ..Default::default()
8014            },
8015            result = Err(ErrorList::new(vec![
8016                Error::field_too_long(DeclType::UseProtocol, "source_dictionary"),
8017            ])),
8018        },
8019
8020        // environments
8021        test_validate_environment_empty => {
8022            input = {
8023                let mut decl = new_component_decl();
8024                decl.environments = Some(vec![fdecl::Environment {
8025                    name: None,
8026                    extends: None,
8027                    runners: None,
8028                    resolvers: None,
8029                    stop_timeout_ms: None,
8030                    debug_capabilities: None,
8031                    ..Default::default()
8032                }]);
8033                decl
8034            },
8035            result = Err(ErrorList::new(vec![
8036                Error::missing_field(DeclType::Environment, "name"),
8037                Error::missing_field(DeclType::Environment, "extends"),
8038            ])),
8039        },
8040
8041        test_validate_environment_no_stop_timeout => {
8042            input = {
8043                let mut decl = new_component_decl();
8044                decl.environments = Some(vec![fdecl::Environment {
8045                    name: Some("env".to_string()),
8046                    extends: Some(fdecl::EnvironmentExtends::None),
8047                    runners: None,
8048                    resolvers: None,
8049                    stop_timeout_ms: None,
8050                    ..Default::default()
8051                }]);
8052                decl
8053            },
8054            result = Err(ErrorList::new(vec![Error::missing_field(DeclType::Environment, "stop_timeout_ms")])),
8055        },
8056
8057        test_validate_environment_extends_stop_timeout => {
8058            input = {  let mut decl = new_component_decl();
8059                decl.environments = Some(vec![fdecl::Environment {
8060                    name: Some("env".to_string()),
8061                    extends: Some(fdecl::EnvironmentExtends::Realm),
8062                    runners: None,
8063                    resolvers: None,
8064                    stop_timeout_ms: None,
8065                    ..Default::default()
8066                }]);
8067                decl
8068            },
8069            result = Ok(()),
8070        },
8071        test_validate_environment_long_identifiers => {
8072            input = {
8073                let mut decl = new_component_decl();
8074                decl.environments = Some(vec![fdecl::Environment {
8075                    name: Some("a".repeat(256)),
8076                    extends: Some(fdecl::EnvironmentExtends::None),
8077                    runners: Some(vec![
8078                        fdecl::RunnerRegistration {
8079                            source_name: Some("a".repeat(256)),
8080                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8081                            target_name: Some("a".repeat(256)),
8082                            ..Default::default()
8083                        },
8084                    ]),
8085                    resolvers: Some(vec![
8086                        fdecl::ResolverRegistration {
8087                            resolver: Some("a".repeat(256)),
8088                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8089                            scheme: Some("a".repeat(256)),
8090                            ..Default::default()
8091                        },
8092                    ]),
8093                    stop_timeout_ms: Some(1234),
8094                    ..Default::default()
8095                }]);
8096                decl
8097            },
8098            result = Err(ErrorList::new(vec![
8099                Error::field_too_long(DeclType::Environment, "name"),
8100                Error::field_too_long(DeclType::RunnerRegistration, "source_name"),
8101                Error::field_too_long(DeclType::RunnerRegistration, "target_name"),
8102                Error::field_too_long(DeclType::ResolverRegistration, "resolver"),
8103                Error::field_too_long(DeclType::ResolverRegistration, "scheme"),
8104            ])),
8105        },
8106        test_validate_environment_empty_runner_resolver_fields => {
8107            input = {
8108                let mut decl = new_component_decl();
8109                decl.environments = Some(vec![fdecl::Environment {
8110                    name: Some("a".to_string()),
8111                    extends: Some(fdecl::EnvironmentExtends::None),
8112                    runners: Some(vec![
8113                        fdecl::RunnerRegistration {
8114                            source_name: None,
8115                            source: None,
8116                            target_name: None,
8117                            ..Default::default()
8118                        },
8119                    ]),
8120                    resolvers: Some(vec![
8121                        fdecl::ResolverRegistration {
8122                            resolver: None,
8123                            source: None,
8124                            scheme: None,
8125                            ..Default::default()
8126                        },
8127                    ]),
8128                    stop_timeout_ms: Some(1234),
8129                    ..Default::default()
8130                }]);
8131                decl
8132            },
8133            result = Err(ErrorList::new(vec![
8134                Error::missing_field(DeclType::RunnerRegistration, "source_name"),
8135                Error::missing_field(DeclType::RunnerRegistration, "source"),
8136                Error::missing_field(DeclType::RunnerRegistration, "target_name"),
8137                Error::missing_field(DeclType::ResolverRegistration, "resolver"),
8138                Error::missing_field(DeclType::ResolverRegistration, "source"),
8139                Error::missing_field(DeclType::ResolverRegistration, "scheme"),
8140            ])),
8141        },
8142        test_validate_environment_invalid_fields => {
8143            input = {
8144                let mut decl = new_component_decl();
8145                decl.environments = Some(vec![fdecl::Environment {
8146                    name: Some("a".to_string()),
8147                    extends: Some(fdecl::EnvironmentExtends::None),
8148                    runners: Some(vec![
8149                        fdecl::RunnerRegistration {
8150                            source_name: Some("^a".to_string()),
8151                            source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
8152                            target_name: Some("%a".to_string()),
8153                            ..Default::default()
8154                        },
8155                    ]),
8156                    resolvers: Some(vec![
8157                        fdecl::ResolverRegistration {
8158                            resolver: Some("^a".to_string()),
8159                            source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
8160                            scheme: Some("9scheme".to_string()),
8161                            ..Default::default()
8162                        },
8163                    ]),
8164                    stop_timeout_ms: Some(1234),
8165                    ..Default::default()
8166                }]);
8167                decl
8168            },
8169            result = Err(ErrorList::new(vec![
8170                Error::invalid_field(DeclType::RunnerRegistration, "source_name"),
8171                Error::invalid_field(DeclType::RunnerRegistration, "source"),
8172                Error::invalid_field(DeclType::RunnerRegistration, "target_name"),
8173                Error::invalid_field(DeclType::ResolverRegistration, "resolver"),
8174                Error::invalid_field(DeclType::ResolverRegistration, "source"),
8175                Error::invalid_field(DeclType::ResolverRegistration, "scheme"),
8176            ])),
8177        },
8178        test_validate_environment_missing_runner => {
8179            input = {
8180                let mut decl = new_component_decl();
8181                decl.environments = Some(vec![fdecl::Environment {
8182                    name: Some("a".to_string()),
8183                    extends: Some(fdecl::EnvironmentExtends::None),
8184                    runners: Some(vec![
8185                        fdecl::RunnerRegistration {
8186                            source_name: Some("dart".to_string()),
8187                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
8188                            target_name: Some("dart".to_string()),
8189                            ..Default::default()
8190                        },
8191                    ]),
8192                    resolvers: None,
8193                    stop_timeout_ms: Some(1234),
8194                    ..Default::default()
8195                }]);
8196                decl
8197            },
8198            result = Err(ErrorList::new(vec![
8199                Error::invalid_runner(DeclType::RunnerRegistration, "source_name", "dart"),
8200            ])),
8201        },
8202        test_validate_environment_duplicate_registrations => {
8203            input = {
8204                let mut decl = new_component_decl();
8205                decl.environments = Some(vec![fdecl::Environment {
8206                    name: Some("a".to_string()),
8207                    extends: Some(fdecl::EnvironmentExtends::None),
8208                    runners: Some(vec![
8209                        fdecl::RunnerRegistration {
8210                            source_name: Some("dart".to_string()),
8211                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8212                            target_name: Some("dart".to_string()),
8213                            ..Default::default()
8214                        },
8215                        fdecl::RunnerRegistration {
8216                            source_name: Some("other-dart".to_string()),
8217                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8218                            target_name: Some("dart".to_string()),
8219                            ..Default::default()
8220                        },
8221                    ]),
8222                    resolvers: Some(vec![
8223                        fdecl::ResolverRegistration {
8224                            resolver: Some("pkg_resolver".to_string()),
8225                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8226                            scheme: Some("fuchsia-pkg".to_string()),
8227                            ..Default::default()
8228                        },
8229                        fdecl::ResolverRegistration {
8230                            resolver: Some("base_resolver".to_string()),
8231                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8232                            scheme: Some("fuchsia-pkg".to_string()),
8233                            ..Default::default()
8234                        },
8235                    ]),
8236                    stop_timeout_ms: Some(1234),
8237                    ..Default::default()
8238                }]);
8239                decl
8240            },
8241            result = Err(ErrorList::new(vec![
8242                Error::duplicate_field(DeclType::RunnerRegistration, "target_name", "dart"),
8243                Error::duplicate_field(DeclType::ResolverRegistration, "scheme", "fuchsia-pkg"),
8244            ])),
8245        },
8246        test_validate_environment_from_missing_child => {
8247            input = {
8248                let mut decl = new_component_decl();
8249                decl.environments = Some(vec![fdecl::Environment {
8250                    name: Some("a".to_string()),
8251                    extends: Some(fdecl::EnvironmentExtends::None),
8252                    runners: Some(vec![
8253                        fdecl::RunnerRegistration {
8254                            source_name: Some("elf".to_string()),
8255                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8256                                name: "missing".to_string(),
8257                                collection: None,
8258                            })),
8259                            target_name: Some("elf".to_string()),
8260                            ..Default::default()
8261                        },
8262                    ]),
8263                    resolvers: Some(vec![
8264                        fdecl::ResolverRegistration {
8265                            resolver: Some("pkg_resolver".to_string()),
8266                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8267                                name: "missing".to_string(),
8268                                collection: None,
8269                            })),
8270                            scheme: Some("fuchsia-pkg".to_string()),
8271                            ..Default::default()
8272                        },
8273                    ]),
8274                    stop_timeout_ms: Some(1234),
8275                    ..Default::default()
8276                }]);
8277                decl
8278            },
8279            result = Err(ErrorList::new(vec![
8280                Error::invalid_child(DeclType::RunnerRegistration, "source", "missing"),
8281                Error::invalid_child(DeclType::ResolverRegistration, "source", "missing"),
8282            ])),
8283        },
8284        test_validate_environment_runner_child_cycle => {
8285            input = {
8286                let mut decl = new_component_decl();
8287                decl.environments = Some(vec![fdecl::Environment {
8288                    name: Some("env".to_string()),
8289                    extends: Some(fdecl::EnvironmentExtends::None),
8290                    runners: Some(vec![
8291                        fdecl::RunnerRegistration {
8292                            source_name: Some("elf".to_string()),
8293                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8294                                name: "child".to_string(),
8295                                collection: None,
8296                            })),
8297                            target_name: Some("elf".to_string()),
8298                            ..Default::default()
8299                        },
8300                    ]),
8301                    resolvers: None,
8302                    stop_timeout_ms: Some(1234),
8303                    ..Default::default()
8304                }]);
8305                decl.children = Some(vec![fdecl::Child {
8306                    name: Some("child".to_string()),
8307                    startup: Some(fdecl::StartupMode::Lazy),
8308                    on_terminate: None,
8309                    url: Some("fuchsia-pkg://child".to_string()),
8310                    environment: Some("env".to_string()),
8311                    ..Default::default()
8312                }]);
8313                decl
8314            },
8315            result = Err(ErrorList::new(vec![
8316                Error::dependency_cycle(
8317                    directed_graph::Error::CyclesDetected([vec!["child child", "environment env", "child child"]].iter().cloned().collect()).format_cycle()
8318                ),
8319            ])),
8320        },
8321        test_validate_environment_resolver_child_cycle => {
8322            input = {
8323                let mut decl = new_component_decl();
8324                decl.environments = Some(vec![fdecl::Environment {
8325                    name: Some("env".to_string()),
8326                    extends: Some(fdecl::EnvironmentExtends::None),
8327                    runners: None,
8328                    resolvers: Some(vec![
8329                        fdecl::ResolverRegistration {
8330                            resolver: Some("pkg_resolver".to_string()),
8331                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8332                                name: "child".to_string(),
8333                                collection: None,
8334                            })),
8335                            scheme: Some("fuchsia-pkg".to_string()),
8336                            ..Default::default()
8337                        },
8338                    ]),
8339                    stop_timeout_ms: Some(1234),
8340                    ..Default::default()
8341                }]);
8342                decl.children = Some(vec![fdecl::Child {
8343                    name: Some("child".to_string()),
8344                    startup: Some(fdecl::StartupMode::Lazy),
8345                    on_terminate: None,
8346                    url: Some("fuchsia-pkg://child".to_string()),
8347                    environment: Some("env".to_string()),
8348                    ..Default::default()
8349                }]);
8350                decl
8351            },
8352            result = Err(ErrorList::new(vec![
8353                Error::dependency_cycle(
8354                    directed_graph::Error::CyclesDetected([vec!["child child", "environment env", "child child"]].iter().cloned().collect()).format_cycle()
8355                ),
8356            ])),
8357        },
8358        test_validate_environment_resolver_multiple_children_cycle => {
8359            input = {
8360                let mut decl = new_component_decl();
8361                decl.environments = Some(vec![fdecl::Environment {
8362                    name: Some("env".to_string()),
8363                    extends: Some(fdecl::EnvironmentExtends::None),
8364                    runners: None,
8365                    resolvers: Some(vec![
8366                        fdecl::ResolverRegistration {
8367                            resolver: Some("pkg_resolver".to_string()),
8368                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8369                                name: "a".to_string(),
8370                                collection: None,
8371                            })),
8372                            scheme: Some("fuchsia-pkg".to_string()),
8373                            ..Default::default()
8374                        },
8375                    ]),
8376                    stop_timeout_ms: Some(1234),
8377                    ..Default::default()
8378                }]);
8379                decl.children = Some(vec![
8380                    fdecl::Child {
8381                        name: Some("a".to_string()),
8382                        startup: Some(fdecl::StartupMode::Lazy),
8383                        on_terminate: None,
8384                        url: Some("fuchsia-pkg://child-a".to_string()),
8385                        environment: None,
8386                        ..Default::default()
8387                    },
8388                    fdecl::Child {
8389                        name: Some("b".to_string()),
8390                        startup: Some(fdecl::StartupMode::Lazy),
8391                        on_terminate: None,
8392                        url: Some("fuchsia-pkg://child-b".to_string()),
8393                        environment: Some("env".to_string()),
8394                        ..Default::default()
8395                    },
8396                ]);
8397                decl.offers = Some(vec![fdecl::Offer::Service(fdecl::OfferService {
8398                    source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8399                        name: "b".to_string(),
8400                        collection: None,
8401                    })),
8402                    source_name: Some("thing".to_string()),
8403                    target: Some(fdecl::Ref::Child(
8404                    fdecl::ChildRef {
8405                        name: "a".to_string(),
8406                        collection: None,
8407                    }
8408                    )),
8409                    target_name: Some("thing".to_string()),
8410                    ..Default::default()
8411                })]);
8412                decl
8413            },
8414            result = Err(ErrorList::new(vec![
8415                Error::dependency_cycle(
8416                    directed_graph::Error::CyclesDetected([vec!["child a", "environment env", "child b", "child a"]].iter().cloned().collect()).format_cycle()
8417                ),
8418            ])),
8419        },
8420        test_validate_environment_debug_empty => {
8421            input = {
8422                let mut decl = new_component_decl();
8423                decl.environments = Some(vec![
8424                    fdecl::Environment {
8425                        name: Some("a".to_string()),
8426                        extends: Some(fdecl::EnvironmentExtends::None),
8427                        stop_timeout_ms: Some(2),
8428                        debug_capabilities:Some(vec![
8429                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8430                                source: None,
8431                                source_name: None,
8432                                target_name: None,
8433                                ..Default::default()
8434                            }),
8435                    ]),
8436                    ..Default::default()
8437                }]);
8438                decl
8439            },
8440            result = Err(ErrorList::new(vec![
8441                Error::missing_field(DeclType::DebugProtocolRegistration, "source"),
8442                Error::missing_field(DeclType::DebugProtocolRegistration, "source_name"),
8443                Error::missing_field(DeclType::DebugProtocolRegistration, "target_name"),
8444            ])),
8445        },
8446        test_validate_environment_debug_log_identifier => {
8447            input = {
8448                let mut decl = new_component_decl();
8449                decl.environments = Some(vec![
8450                    fdecl::Environment {
8451                        name: Some("a".to_string()),
8452                        extends: Some(fdecl::EnvironmentExtends::None),
8453                        stop_timeout_ms: Some(2),
8454                        debug_capabilities:Some(vec![
8455                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8456                                source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8457                                    name: "a".repeat(256),
8458                                    collection: None,
8459                                })),
8460                                source_name: Some(format!("{}", "a".repeat(256))),
8461                                target_name: Some(format!("{}", "b".repeat(256))),
8462                                ..Default::default()
8463                            }),
8464                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8465                                source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8466                                source_name: Some("a".to_string()),
8467                                target_name: Some(format!("{}", "b".repeat(256))),
8468                                ..Default::default()
8469                            }),
8470                    ]),
8471                    ..Default::default()
8472                }]);
8473                decl
8474            },
8475            result = Err(ErrorList::new(vec![
8476                Error::field_too_long(DeclType::DebugProtocolRegistration, "source.child.name"),
8477                Error::field_too_long(DeclType::DebugProtocolRegistration, "source_name"),
8478                Error::field_too_long(DeclType::DebugProtocolRegistration, "target_name"),
8479                Error::field_too_long(DeclType::DebugProtocolRegistration, "target_name"),
8480            ])),
8481        },
8482        test_validate_environment_debug_log_extraneous => {
8483            input = {
8484                let mut decl = new_component_decl();
8485                decl.environments = Some(vec![
8486                    fdecl::Environment {
8487                        name: Some("a".to_string()),
8488                        extends: Some(fdecl::EnvironmentExtends::None),
8489                        stop_timeout_ms: Some(2),
8490                        debug_capabilities:Some(vec![
8491                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8492                                source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8493                                    name: "logger".to_string(),
8494                                    collection: Some("modular".to_string()),
8495                                })),
8496                                source_name: Some("fuchsia.logger.Log".to_string()),
8497                                target_name: Some("fuchsia.logger.Log".to_string()),
8498                                ..Default::default()
8499                            }),
8500                    ]),
8501                    ..Default::default()
8502                }]);
8503                decl
8504            },
8505            result = Err(ErrorList::new(vec![
8506                Error::extraneous_field(DeclType::DebugProtocolRegistration, "source.child.collection"),
8507            ])),
8508        },
8509        test_validate_environment_debug_log_invalid_identifiers => {
8510            input = {
8511                let mut decl = new_component_decl();
8512                decl.environments = Some(vec![
8513                    fdecl::Environment {
8514                        name: Some("a".to_string()),
8515                        extends: Some(fdecl::EnvironmentExtends::None),
8516                        stop_timeout_ms: Some(2),
8517                        debug_capabilities:Some(vec![
8518                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8519                                source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8520                                    name: "^bad".to_string(),
8521                                    collection: None,
8522                                })),
8523                                source_name: Some("foo/".to_string()),
8524                                target_name: Some("/".to_string()),
8525                                ..Default::default()
8526                            }),
8527                    ]),
8528                    ..Default::default()
8529                }]);
8530                decl
8531            },
8532            result = Err(ErrorList::new(vec![
8533                Error::invalid_field(DeclType::DebugProtocolRegistration, "source.child.name"),
8534                Error::invalid_field(DeclType::DebugProtocolRegistration, "source_name"),
8535                Error::invalid_field(DeclType::DebugProtocolRegistration, "target_name"),
8536            ])),
8537        },
8538        test_validate_environment_debug_log_invalid_child => {
8539            input = {
8540                let mut decl = new_component_decl();
8541                decl.environments = Some(vec![
8542                    fdecl::Environment {
8543                        name: Some("a".to_string()),
8544                        extends: Some(fdecl::EnvironmentExtends::None),
8545                        stop_timeout_ms: Some(2),
8546                        debug_capabilities:Some(vec![
8547                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8548                                source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8549                                    name: "logger".to_string(),
8550                                    collection: None,
8551                                })),
8552                                source_name: Some("fuchsia.logger.LegacyLog".to_string()),
8553                                target_name: Some("fuchsia.logger.LegacyLog".to_string()),
8554                                ..Default::default()
8555                            }),
8556                    ]),
8557                    ..Default::default()
8558                }]);
8559                decl.children = Some(vec![
8560                    fdecl::Child {
8561                        name: Some("netstack".to_string()),
8562                        url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
8563                        startup: Some(fdecl::StartupMode::Lazy),
8564                        on_terminate: None,
8565                        environment: None,
8566                        ..Default::default()
8567                    },
8568                ]);
8569                decl
8570            },
8571            result = Err(ErrorList::new(vec![
8572                Error::invalid_child(DeclType::DebugProtocolRegistration, "source", "logger"),
8573
8574            ])),
8575        },
8576        test_validate_environment_debug_source_capability => {
8577            input = {
8578                let mut decl = new_component_decl();
8579                decl.environments = Some(vec![
8580                    fdecl::Environment {
8581                        name: Some("a".to_string()),
8582                        extends: Some(fdecl::EnvironmentExtends::None),
8583                        stop_timeout_ms: Some(2),
8584                        debug_capabilities:Some(vec![
8585                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8586                                source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
8587                                    name: "storage".to_string(),
8588                                })),
8589                                source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
8590                                target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
8591                                ..Default::default()
8592                            }),
8593                    ]),
8594                    ..Default::default()
8595                }]);
8596                decl
8597            },
8598            result = Err(ErrorList::new(vec![
8599                Error::invalid_field(DeclType::DebugProtocolRegistration, "source"),
8600            ])),
8601        },
8602
8603        // children
8604        test_validate_children_empty => {
8605            input = {
8606                let mut decl = new_component_decl();
8607                decl.children = Some(vec![fdecl::Child{
8608                    name: None,
8609                    url: None,
8610                    startup: None,
8611                    on_terminate: None,
8612                    environment: None,
8613                    ..Default::default()
8614                }]);
8615                decl
8616            },
8617            result = Err(ErrorList::new(vec![
8618                Error::missing_field(DeclType::Child, "name"),
8619                Error::missing_field(DeclType::Child, "url"),
8620                Error::missing_field(DeclType::Child, "startup"),
8621                // `on_terminate` is allowed to be None
8622            ])),
8623        },
8624        test_validate_children_invalid_identifiers => {
8625            input = {
8626                let mut decl = new_component_decl();
8627                decl.children = Some(vec![fdecl::Child{
8628                    name: Some("^bad".to_string()),
8629                    url: Some("scheme://invalid-port:99999999/path#frag".to_string()),
8630                    startup: Some(fdecl::StartupMode::Lazy),
8631                    on_terminate: None,
8632                    environment: None,
8633                    ..Default::default()
8634                }]);
8635                decl
8636            },
8637            result = Err(ErrorList::new(vec![
8638                Error::invalid_field(DeclType::Child, "name"),
8639                Error::invalid_url(DeclType::Child, "url", "\"scheme://invalid-port:99999999/path#frag\": Malformed URL: InvalidPort."),
8640            ])),
8641        },
8642        test_validate_children_long_identifiers => {
8643            input = {
8644                let mut decl = new_component_decl();
8645                decl.children = Some(vec![fdecl::Child{
8646                    name: Some("a".repeat(1025)),
8647                    url: Some(format!("fuchsia-pkg://{}", "a".repeat(4083))),
8648                    startup: Some(fdecl::StartupMode::Lazy),
8649                    on_terminate: None,
8650                    environment: Some("a".repeat(1025)),
8651                    ..Default::default()
8652                }]);
8653                decl
8654            },
8655            result = Err(ErrorList::new(vec![
8656                Error::field_too_long(DeclType::Child, "name"),
8657                Error::field_too_long(DeclType::Child, "url"),
8658                Error::field_too_long(DeclType::Child, "environment"),
8659                Error::invalid_environment(DeclType::Child, "environment", "a".repeat(1025)),
8660            ])),
8661        },
8662        test_validate_child_references_unknown_env => {
8663            input = {
8664                let mut decl = new_component_decl();
8665                decl.children = Some(vec![fdecl::Child{
8666                    name: Some("foo".to_string()),
8667                    url: Some("fuchsia-pkg://foo".to_string()),
8668                    startup: Some(fdecl::StartupMode::Lazy),
8669                    on_terminate: None,
8670                    environment: Some("test_env".to_string()),
8671                    ..Default::default()
8672                }]);
8673                decl
8674            },
8675            result = Err(ErrorList::new(vec![
8676                Error::invalid_environment(DeclType::Child, "environment", "test_env"),
8677            ])),
8678        },
8679
8680        // collections
8681        test_validate_collections_empty => {
8682            input = {
8683                let mut decl = new_component_decl();
8684                decl.collections = Some(vec![fdecl::Collection{
8685                    name: None,
8686                    durability: None,
8687                    environment: None,
8688                    allowed_offers: None,
8689                    allow_long_names: None,
8690                    ..Default::default()
8691                }]);
8692                decl
8693            },
8694            result = Err(ErrorList::new(vec![
8695                Error::missing_field(DeclType::Collection, "name"),
8696                Error::missing_field(DeclType::Collection, "durability"),
8697            ])),
8698        },
8699        test_validate_collections_invalid_identifiers => {
8700            input = {
8701                let mut decl = new_component_decl();
8702                decl.collections = Some(vec![fdecl::Collection{
8703                    name: Some("^bad".to_string()),
8704                    durability: Some(fdecl::Durability::Transient),
8705                    environment: None,
8706                    allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
8707                    allow_long_names: None,
8708                    ..Default::default()
8709                }]);
8710                decl
8711            },
8712            result = Err(ErrorList::new(vec![
8713                Error::invalid_field(DeclType::Collection, "name"),
8714            ])),
8715        },
8716        test_validate_collections_long_identifiers => {
8717            input = {
8718                let mut decl = new_component_decl();
8719                decl.collections = Some(vec![fdecl::Collection{
8720                    name: Some("a".repeat(1025)),
8721                    durability: Some(fdecl::Durability::Transient),
8722                    environment: None,
8723                    allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
8724                    allow_long_names: None,
8725                    ..Default::default()
8726                }]);
8727                decl
8728            },
8729            result = Err(ErrorList::new(vec![
8730                Error::field_too_long(DeclType::Collection, "name"),
8731            ])),
8732        },
8733        test_validate_collection_references_unknown_env => {
8734            input = {
8735                let mut decl = new_component_decl();
8736                decl.collections = Some(vec![fdecl::Collection {
8737                    name: Some("foo".to_string()),
8738                    durability: Some(fdecl::Durability::Transient),
8739                    environment: Some("test_env".to_string()),
8740                    allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
8741                    allow_long_names: None,
8742                    ..Default::default()
8743                }]);
8744                decl
8745            },
8746            result = Err(ErrorList::new(vec![
8747                Error::invalid_environment(DeclType::Collection, "environment", "test_env"),
8748            ])),
8749        },
8750
8751        // capabilities
8752        test_validate_capabilities_empty => {
8753            input = {
8754                let mut decl = new_component_decl();
8755                decl.capabilities = Some(vec![
8756                    fdecl::Capability::Service(fdecl::Service {
8757                        name: None,
8758                        source_path: None,
8759                        ..Default::default()
8760                    }),
8761                    fdecl::Capability::Protocol(fdecl::Protocol {
8762                        name: None,
8763                        source_path: None,
8764                        ..Default::default()
8765                    }),
8766                    fdecl::Capability::Directory(fdecl::Directory {
8767                        name: None,
8768                        source_path: None,
8769                        rights: None,
8770                        ..Default::default()
8771                    }),
8772                    fdecl::Capability::Storage(fdecl::Storage {
8773                        name: None,
8774                        source: None,
8775                        backing_dir: None,
8776                        subdir: None,
8777                        storage_id: None,
8778                        ..Default::default()
8779                    }),
8780                    fdecl::Capability::Runner(fdecl::Runner {
8781                        name: None,
8782                        source_path: None,
8783                        ..Default::default()
8784                    }),
8785                    fdecl::Capability::Resolver(fdecl::Resolver {
8786                        name: None,
8787                        source_path: None,
8788                        ..Default::default()
8789                    }),
8790                    fdecl::Capability::Dictionary(fdecl::Dictionary {
8791                        ..Default::default()
8792                    }),
8793                ]);
8794                decl
8795            },
8796            result = Err(ErrorList::new(vec![
8797                Error::missing_field(DeclType::Dictionary, "name"),
8798                Error::missing_field(DeclType::Service, "name"),
8799                Error::missing_field(DeclType::Service, "source_path"),
8800                Error::missing_field(DeclType::Protocol, "name"),
8801                Error::missing_field(DeclType::Protocol, "source_path"),
8802                Error::missing_field(DeclType::Directory, "name"),
8803                Error::missing_field(DeclType::Directory, "source_path"),
8804                Error::missing_field(DeclType::Directory, "rights"),
8805                Error::missing_field(DeclType::Storage, "source"),
8806                Error::missing_field(DeclType::Storage, "name"),
8807                Error::missing_field(DeclType::Storage, "storage_id"),
8808                Error::missing_field(DeclType::Storage, "backing_dir"),
8809                Error::missing_field(DeclType::Runner, "name"),
8810                Error::missing_field(DeclType::Runner, "source_path"),
8811                Error::missing_field(DeclType::Resolver, "name"),
8812                Error::missing_field(DeclType::Resolver, "source_path"),
8813            ])),
8814        },
8815        test_validate_capabilities_invalid_identifiers => {
8816            input = {
8817                let mut decl = new_component_decl();
8818                decl.capabilities = Some(vec![
8819                    fdecl::Capability::Service(fdecl::Service {
8820                        name: Some("^bad".to_string()),
8821                        source_path: Some("&bad".to_string()),
8822                        ..Default::default()
8823                    }),
8824                    fdecl::Capability::Protocol(fdecl::Protocol {
8825                        name: Some("^bad".to_string()),
8826                        source_path: Some("&bad".to_string()),
8827                        ..Default::default()
8828                    }),
8829                    fdecl::Capability::Directory(fdecl::Directory {
8830                        name: Some("^bad".to_string()),
8831                        source_path: Some("&bad".to_string()),
8832                        rights: Some(fio::Operations::CONNECT),
8833                        ..Default::default()
8834                    }),
8835                    fdecl::Capability::Storage(fdecl::Storage {
8836                        name: Some("^bad".to_string()),
8837                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
8838                            name: "/bad".to_string()
8839                        })),
8840                        backing_dir: Some("&bad".to_string()),
8841                        subdir: None,
8842                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
8843                        ..Default::default()
8844                    }),
8845                    fdecl::Capability::Runner(fdecl::Runner {
8846                        name: Some("^bad".to_string()),
8847                        source_path: Some("&bad".to_string()),
8848                        ..Default::default()
8849                    }),
8850                    fdecl::Capability::Resolver(fdecl::Resolver {
8851                        name: Some("^bad".to_string()),
8852                        source_path: Some("&bad".to_string()),
8853                        ..Default::default()
8854                    }),
8855                    fdecl::Capability::Dictionary(fdecl::Dictionary {
8856                        name: Some("^bad".to_string()),
8857                        ..Default::default()
8858                    }),
8859                ]);
8860                decl
8861            },
8862            result = Err(ErrorList::new(vec![
8863                Error::invalid_field(DeclType::Dictionary, "name"),
8864                Error::invalid_field(DeclType::Service, "name"),
8865                Error::invalid_field(DeclType::Service, "source_path"),
8866                Error::invalid_field(DeclType::Protocol, "name"),
8867                Error::invalid_field(DeclType::Protocol, "source_path"),
8868                Error::invalid_field(DeclType::Directory, "name"),
8869                Error::invalid_field(DeclType::Directory, "source_path"),
8870                Error::invalid_field(DeclType::Storage, "source"),
8871                Error::invalid_field(DeclType::Storage, "name"),
8872                Error::invalid_field(DeclType::Storage, "backing_dir"),
8873                Error::invalid_field(DeclType::Runner, "name"),
8874                Error::invalid_field(DeclType::Runner, "source_path"),
8875                Error::invalid_field(DeclType::Resolver, "name"),
8876                Error::invalid_field(DeclType::Resolver, "source_path"),
8877            ])),
8878        },
8879        test_validate_capabilities_invalid_child => {
8880            input = {
8881                let mut decl = new_component_decl();
8882                decl.capabilities = Some(vec![
8883                    fdecl::Capability::Storage(fdecl::Storage {
8884                        name: Some("foo".to_string()),
8885                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
8886                            name: "invalid".to_string(),
8887                        })),
8888                        backing_dir: Some("foo".to_string()),
8889                        subdir: None,
8890                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
8891                        ..Default::default()
8892                    }),
8893                ]);
8894                decl
8895            },
8896            result = Err(ErrorList::new(vec![
8897                Error::invalid_field(DeclType::Storage, "source"),
8898            ])),
8899        },
8900        test_validate_capabilities_long_identifiers => {
8901            input = {
8902                let mut decl = new_component_decl();
8903                decl.capabilities = Some(vec![
8904                    fdecl::Capability::Service(fdecl::Service {
8905                        name: Some("a".repeat(256)),
8906                        source_path: Some("/c".repeat(2048)),
8907                        ..Default::default()
8908                    }),
8909                    fdecl::Capability::Protocol(fdecl::Protocol {
8910                        name: Some("a".repeat(256)),
8911                        source_path: Some("/c".repeat(2048)),
8912                        ..Default::default()
8913                    }),
8914                    fdecl::Capability::Directory(fdecl::Directory {
8915                        name: Some("a".repeat(256)),
8916                        source_path: Some("/c".repeat(2048)),
8917                        rights: Some(fio::Operations::CONNECT),
8918                        ..Default::default()
8919                    }),
8920                    fdecl::Capability::Storage(fdecl::Storage {
8921                        name: Some("a".repeat(256)),
8922                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8923                            name: "b".repeat(256),
8924                            collection: None,
8925                        })),
8926                        backing_dir: Some(format!("{}", "c".repeat(256))),
8927                        subdir: None,
8928                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
8929                        ..Default::default()
8930                    }),
8931                    fdecl::Capability::Runner(fdecl::Runner {
8932                        name: Some("a".repeat(256)),
8933                        source_path: Some("/c".repeat(2048)),
8934                        ..Default::default()
8935                    }),
8936                    fdecl::Capability::Resolver(fdecl::Resolver {
8937                        name: Some("a".repeat(256)),
8938                        source_path: Some("/c".repeat(2048)),
8939                        ..Default::default()
8940                    }),
8941                    fdecl::Capability::Dictionary(fdecl::Dictionary {
8942                        name: Some("a".repeat(256)),
8943                        ..Default::default()
8944                    }),
8945                ]);
8946                decl
8947            },
8948            result = Err(ErrorList::new(vec![
8949                Error::field_too_long(DeclType::Dictionary, "name"),
8950                Error::field_too_long(DeclType::Service, "name"),
8951                Error::field_too_long(DeclType::Service, "source_path"),
8952                Error::field_too_long(DeclType::Protocol, "name"),
8953                Error::field_too_long(DeclType::Protocol, "source_path"),
8954                Error::field_too_long(DeclType::Directory, "name"),
8955                Error::field_too_long(DeclType::Directory, "source_path"),
8956                Error::field_too_long(DeclType::Storage, "source.child.name"),
8957                Error::field_too_long(DeclType::Storage, "name"),
8958                Error::field_too_long(DeclType::Storage, "backing_dir"),
8959                Error::field_too_long(DeclType::Runner, "name"),
8960                Error::field_too_long(DeclType::Runner, "source_path"),
8961                Error::field_too_long(DeclType::Resolver, "name"),
8962                Error::field_too_long(DeclType::Resolver, "source_path"),
8963            ])),
8964        },
8965        test_validate_capabilities_duplicate_name => {
8966            input = {
8967                let mut decl = new_component_decl();
8968                decl.capabilities = Some(vec![
8969                    fdecl::Capability::Service(fdecl::Service {
8970                        name: Some("service".to_string()),
8971                        source_path: Some("/service".to_string()),
8972                        ..Default::default()
8973                    }),
8974                    fdecl::Capability::Service(fdecl::Service {
8975                        name: Some("service".to_string()),
8976                        source_path: Some("/service".to_string()),
8977                        ..Default::default()
8978                    }),
8979                    fdecl::Capability::Protocol(fdecl::Protocol {
8980                        name: Some("protocol".to_string()),
8981                        source_path: Some("/protocol".to_string()),
8982                        ..Default::default()
8983                    }),
8984                    fdecl::Capability::Protocol(fdecl::Protocol {
8985                        name: Some("protocol".to_string()),
8986                        source_path: Some("/protocol".to_string()),
8987                        ..Default::default()
8988                    }),
8989                    fdecl::Capability::Directory(fdecl::Directory {
8990                        name: Some("directory".to_string()),
8991                        source_path: Some("/directory".to_string()),
8992                        rights: Some(fio::Operations::CONNECT),
8993                        ..Default::default()
8994                    }),
8995                    fdecl::Capability::Directory(fdecl::Directory {
8996                        name: Some("directory".to_string()),
8997                        source_path: Some("/directory".to_string()),
8998                        rights: Some(fio::Operations::CONNECT),
8999                        ..Default::default()
9000                    }),
9001                    fdecl::Capability::Storage(fdecl::Storage {
9002                        name: Some("storage".to_string()),
9003                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
9004                        backing_dir: Some("directory".to_string()),
9005                        subdir: None,
9006                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9007                        ..Default::default()
9008                    }),
9009                    fdecl::Capability::Storage(fdecl::Storage {
9010                        name: Some("storage".to_string()),
9011                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
9012                        backing_dir: Some("directory".to_string()),
9013                        subdir: None,
9014                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9015                        ..Default::default()
9016                    }),
9017                    fdecl::Capability::Runner(fdecl::Runner {
9018                        name: Some("runner".to_string()),
9019                        source_path: Some("/runner".to_string()),
9020                        ..Default::default()
9021                    }),
9022                    fdecl::Capability::Runner(fdecl::Runner {
9023                        name: Some("runner".to_string()),
9024                        source_path: Some("/runner".to_string()),
9025                        ..Default::default()
9026                    }),
9027                    fdecl::Capability::Resolver(fdecl::Resolver {
9028                        name: Some("resolver".to_string()),
9029                        source_path: Some("/resolver".to_string()),
9030                        ..Default::default()
9031                    }),
9032                    fdecl::Capability::Resolver(fdecl::Resolver {
9033                        name: Some("resolver".to_string()),
9034                        source_path: Some("/resolver".to_string()),
9035                        ..Default::default()
9036                    }),
9037                    fdecl::Capability::Dictionary(fdecl::Dictionary {
9038                        name: Some("dictionary".to_string()),
9039                        ..Default::default()
9040                    }),
9041                    fdecl::Capability::Dictionary(fdecl::Dictionary {
9042                        name: Some("dictionary".to_string()),
9043                        ..Default::default()
9044                    }),
9045                ]);
9046                decl
9047            },
9048            result = Err(ErrorList::new(vec![
9049                Error::duplicate_field(DeclType::Dictionary, "name", "dictionary"),
9050                Error::duplicate_field(DeclType::Service, "name", "service"),
9051                Error::duplicate_field(DeclType::Protocol, "name", "protocol"),
9052                Error::duplicate_field(DeclType::Directory, "name", "directory"),
9053                Error::duplicate_field(DeclType::Storage, "name", "storage"),
9054                Error::duplicate_field(DeclType::Runner, "name", "runner"),
9055                Error::duplicate_field(DeclType::Resolver, "name", "resolver"),
9056            ])),
9057        },
9058        test_validate_invalid_service_aggregation_conflicting_filter => {
9059            input = {
9060                let mut decl = new_component_decl();
9061                decl.offers = Some(vec![
9062                    fdecl::Offer::Service(fdecl::OfferService {
9063                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9064                            name: "coll_a".to_string()
9065                        })),
9066                        source_name: Some("fuchsia.logger.Log".to_string()),
9067                        target: Some(fdecl::Ref::Child(
9068                            fdecl::ChildRef {
9069                                name: "child_c".to_string(),
9070                                collection: None,
9071                            }
9072                        )),
9073                        target_name: Some("fuchsia.logger.Log1".to_string()),
9074                        source_instance_filter: Some(vec!["default".to_string()]),
9075                        ..Default::default()
9076                    }),
9077                    fdecl::Offer::Service(fdecl::OfferService {
9078                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9079                            name: "coll_b".to_string()
9080                        })),
9081                        source_name: Some("fuchsia.logger.Log".to_string()),
9082                        target: Some(fdecl::Ref::Child(
9083                            fdecl::ChildRef {
9084                                name: "child_c".to_string(),
9085                                collection: None,
9086                            }
9087                        )),
9088                        target_name: Some("fuchsia.logger.Log1".to_string()),
9089                        source_instance_filter: Some(vec!["default".to_string()]),
9090                        ..Default::default()
9091                    }),
9092                ]);
9093                decl.collections = Some(vec![
9094                    fdecl::Collection {
9095                        name: Some("coll_a".to_string()),
9096                        durability: Some(fdecl::Durability::Transient),
9097                        ..Default::default()
9098                    },
9099                    fdecl::Collection {
9100                        name: Some("coll_b".to_string()),
9101                        durability: Some(fdecl::Durability::Transient),
9102                        ..Default::default()
9103                    },
9104                ]);
9105                decl.children = Some(vec![
9106                    fdecl::Child {
9107                        name: Some("child_c".to_string()),
9108                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9109                        startup: Some(fdecl::StartupMode::Lazy),
9110                        ..Default::default()
9111                    },
9112                ]);
9113                decl
9114            },
9115            result = Err(ErrorList::new(vec![
9116                Error::invalid_aggregate_offer("Conflicting source_instance_filter in aggregate \
9117                   service offer, instance_name 'default' seen in filter lists multiple times"),
9118            ])),
9119        },
9120
9121        test_validate_invalid_service_aggregation_conflicting_source_name => {
9122            input = {
9123                let mut decl = new_component_decl();
9124                decl.offers = Some(vec![
9125                    fdecl::Offer::Service(fdecl::OfferService {
9126                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9127                            name: "coll_a".into()
9128                        })),
9129                        source_name: Some("fuchsia.logger.Log".to_string()),
9130                        target: Some(fdecl::Ref::Child(
9131                            fdecl::ChildRef {
9132                                name: "child_c".to_string(),
9133                                collection: None,
9134                            }
9135                        )),
9136                        target_name: Some("fuchsia.logger.Log2".to_string()),
9137                        source_instance_filter: Some(vec!["default2".to_string()]),
9138                        ..Default::default()
9139                    }),
9140                    fdecl::Offer::Service(fdecl::OfferService {
9141                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9142                            name: "coll_b".into()
9143                        })),
9144                        source_name: Some("fuchsia.logger.LogAlt".to_string()),
9145                        target: Some(fdecl::Ref::Child(
9146                            fdecl::ChildRef {
9147                                name: "child_c".to_string(),
9148                                collection: None,
9149                            }
9150                        )),
9151                        target_name: Some("fuchsia.logger.Log2".to_string()),
9152                        source_instance_filter: Some(vec!["default".to_string()]),
9153                        ..Default::default()
9154                    })
9155                ]);
9156                decl.collections = Some(vec![
9157                    fdecl::Collection {
9158                        name: Some("coll_a".to_string()),
9159                        durability: Some(fdecl::Durability::Transient),
9160                        ..Default::default()
9161                    },
9162                    fdecl::Collection {
9163                        name: Some("coll_b".to_string()),
9164                        durability: Some(fdecl::Durability::Transient),
9165                        ..Default::default()
9166                    },
9167                ]);
9168                decl.children = Some(vec![
9169                    fdecl::Child {
9170                        name: Some("child_c".to_string()),
9171                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9172                        startup: Some(fdecl::StartupMode::Lazy),
9173                        on_terminate: None,
9174                        environment: None,
9175                        ..Default::default()
9176                    },
9177                ]);
9178                decl
9179            },
9180            result = Err(ErrorList::new(vec![
9181                Error::invalid_aggregate_offer("All aggregate service offers must have the same source_name, saw fuchsia.logger.Log, fuchsia.logger.LogAlt. Use renamed_instances to rename instance names to avoid conflict."),
9182            ])),
9183        },
9184
9185        test_validate_resolvers_missing_from_offer => {
9186            input = {
9187                let mut decl = new_component_decl();
9188                decl.offers = Some(vec![fdecl::Offer::Resolver(fdecl::OfferResolver {
9189                    source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
9190                    source_name: Some("a".to_string()),
9191                    target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
9192                    target_name: Some("a".to_string()),
9193                    ..Default::default()
9194                })]);
9195                decl.children = Some(vec![fdecl::Child {
9196                    name: Some("child".to_string()),
9197                    url: Some("test:///child".to_string()),
9198                    startup: Some(fdecl::StartupMode::Eager),
9199                    on_terminate: None,
9200                    environment: None,
9201                    ..Default::default()
9202                }]);
9203                decl
9204            },
9205            result = Err(ErrorList::new(vec![
9206                Error::invalid_capability(DeclType::OfferResolver, "source", "a"),
9207            ])),
9208        },
9209        test_validate_resolvers_missing_from_expose => {
9210            input = {
9211                let mut decl = new_component_decl();
9212                decl.exposes = Some(vec![fdecl::Expose::Resolver(fdecl::ExposeResolver {
9213                    source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
9214                    source_name: Some("a".to_string()),
9215                    target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
9216                    target_name: Some("a".to_string()),
9217                    ..Default::default()
9218                })]);
9219                decl
9220            },
9221            result = Err(ErrorList::new(vec![
9222                Error::invalid_capability(DeclType::ExposeResolver, "source", "a"),
9223            ])),
9224        },
9225
9226        test_validate_config_missing_config => {
9227            input = {
9228                let mut decl = new_component_decl();
9229                decl.config = Some(fdecl::ConfigSchema{
9230                    fields: None,
9231                    checksum: None,
9232                    value_source: None,
9233                    ..Default::default()
9234                });
9235                decl
9236            },
9237            result = Err(ErrorList::new(vec![
9238                Error::missing_field(DeclType::ConfigSchema, "fields"),
9239                Error::missing_field(DeclType::ConfigSchema, "checksum"),
9240                Error::missing_field(DeclType::ConfigSchema, "value_source"),
9241            ])),
9242        },
9243
9244        test_validate_config_missing_config_field => {
9245            input = {
9246                let mut decl = new_component_decl();
9247                decl.config = Some(fdecl::ConfigSchema{
9248                    fields: Some(vec![
9249                        fdecl::ConfigField {
9250                            key: None,
9251                            type_: None,
9252                            ..Default::default()
9253                        }
9254                    ]),
9255                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9256                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9257                    ..Default::default()
9258                });
9259                decl
9260            },
9261            result = Err(ErrorList::new(vec![
9262                Error::missing_field(DeclType::ConfigField, "key"),
9263                Error::missing_field(DeclType::ConfigField, "value_type"),
9264            ])),
9265        },
9266
9267        test_validate_config_bool => {
9268            input = {
9269                let mut decl = new_component_decl();
9270                decl.config = Some(fdecl::ConfigSchema{
9271                    fields: Some(vec![
9272                        fdecl::ConfigField {
9273                            key: Some("test".to_string()),
9274                            type_: Some(fdecl::ConfigType {
9275                                layout: fdecl::ConfigTypeLayout::Bool,
9276                                parameters: Some(vec![]),
9277                                constraints: vec![]
9278                            }),
9279                            ..Default::default()
9280                        }
9281                    ]),
9282                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9283                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9284                    ..Default::default()
9285                });
9286                decl
9287            },
9288            result = Ok(()),
9289        },
9290
9291        test_validate_config_bool_extra_constraint => {
9292            input = {
9293                let mut decl = new_component_decl();
9294                decl.config = Some(fdecl::ConfigSchema{
9295                    fields: Some(vec![
9296                        fdecl::ConfigField {
9297                            key: Some("test".to_string()),
9298                            type_: Some(fdecl::ConfigType {
9299                                layout: fdecl::ConfigTypeLayout::Bool,
9300                                parameters: Some(vec![]),
9301                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9302                            }),
9303                            ..Default::default()
9304                        }
9305                    ]),
9306                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9307                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9308                    ..Default::default()
9309                });
9310                decl
9311            },
9312            result = Err(ErrorList::new(vec![
9313                Error::extraneous_field(DeclType::ConfigType, "constraints")
9314            ])),
9315        },
9316
9317        test_validate_config_bool_missing_parameters => {
9318            input = {
9319                let mut decl = new_component_decl();
9320                decl.config = Some(fdecl::ConfigSchema{
9321                    fields: Some(vec![
9322                        fdecl::ConfigField {
9323                            key: Some("test".to_string()),
9324                            type_: Some(fdecl::ConfigType {
9325                                layout: fdecl::ConfigTypeLayout::Bool,
9326                                parameters: None,
9327                                constraints: vec![]
9328                            }),
9329                            ..Default::default()
9330                        }
9331                    ]),
9332                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9333                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9334                    ..Default::default()
9335                });
9336                decl
9337            },
9338            result = Err(ErrorList::new(vec![
9339                Error::missing_field(DeclType::ConfigType, "parameters")
9340            ])),
9341        },
9342
9343        test_validate_config_string => {
9344            input = {
9345                let mut decl = new_component_decl();
9346                decl.config = Some(fdecl::ConfigSchema{
9347                    fields: Some(vec![
9348                        fdecl::ConfigField {
9349                            key: Some("test".to_string()),
9350                            type_: Some(fdecl::ConfigType {
9351                                layout: fdecl::ConfigTypeLayout::String,
9352                                parameters: Some(vec![]),
9353                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9354                            }),
9355                            ..Default::default()
9356                        }
9357                    ]),
9358                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9359                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9360                    ..Default::default()
9361                });
9362                decl
9363            },
9364            result = Ok(()),
9365        },
9366
9367        test_validate_config_string_missing_parameter => {
9368            input = {
9369                let mut decl = new_component_decl();
9370                decl.config = Some(fdecl::ConfigSchema{
9371                    fields: Some(vec![
9372                        fdecl::ConfigField {
9373                            key: Some("test".to_string()),
9374                            type_: Some(fdecl::ConfigType {
9375                                layout: fdecl::ConfigTypeLayout::String,
9376                                parameters: None,
9377                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9378                            }),
9379                            ..Default::default()
9380                        }
9381                    ]),
9382                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9383                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9384                    ..Default::default()
9385                });
9386                decl
9387            },
9388            result = Err(ErrorList::new(vec![
9389                Error::missing_field(DeclType::ConfigType, "parameters")
9390            ])),
9391        },
9392
9393        test_validate_config_string_missing_constraint => {
9394            input = {
9395                let mut decl = new_component_decl();
9396                decl.config = Some(fdecl::ConfigSchema{
9397                    fields: Some(vec![
9398                        fdecl::ConfigField {
9399                            key: Some("test".to_string()),
9400                            type_: Some(fdecl::ConfigType {
9401                                layout: fdecl::ConfigTypeLayout::String,
9402                                parameters: Some(vec![]),
9403                                constraints: vec![]
9404                            }),
9405                            ..Default::default()
9406                        }
9407                    ]),
9408                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9409                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9410                    ..Default::default()
9411                });
9412                decl
9413            },
9414            result = Err(ErrorList::new(vec![
9415                Error::missing_field(DeclType::ConfigType, "constraints")
9416            ])),
9417        },
9418
9419        test_validate_config_string_extra_constraint => {
9420            input = {
9421                let mut decl = new_component_decl();
9422                decl.config = Some(fdecl::ConfigSchema{
9423                    fields: Some(vec![
9424                        fdecl::ConfigField {
9425                            key: Some("test".to_string()),
9426                            type_: Some(fdecl::ConfigType {
9427                                layout: fdecl::ConfigTypeLayout::String,
9428                                parameters: Some(vec![]),
9429                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10), fdecl::LayoutConstraint::MaxSize(10)]
9430                            }),
9431                            ..Default::default()
9432                        }
9433                    ]),
9434                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9435                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9436                    ..Default::default()
9437                });
9438                decl
9439            },
9440            result = Err(ErrorList::new(vec![
9441                Error::extraneous_field(DeclType::ConfigType, "constraints")
9442            ])),
9443        },
9444
9445        test_validate_config_vector_bool => {
9446            input = {
9447                let mut decl = new_component_decl();
9448                decl.config = Some(fdecl::ConfigSchema{
9449                    fields: Some(vec![
9450                        fdecl::ConfigField {
9451                            key: Some("test".to_string()),
9452                            type_: Some(fdecl::ConfigType {
9453                                layout: fdecl::ConfigTypeLayout::Vector,
9454                                parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9455                                    layout: fdecl::ConfigTypeLayout::Bool,
9456                                    parameters: Some(vec![]),
9457                                    constraints: vec![],
9458                                })]),
9459                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9460                            }),
9461                            ..Default::default()
9462                        }
9463                    ]),
9464                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9465                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9466                    ..Default::default()
9467                });
9468                decl
9469            },
9470            result = Ok(()),
9471        },
9472
9473        test_validate_config_vector_extra_parameter => {
9474            input = {
9475                let mut decl = new_component_decl();
9476                decl.config = Some(fdecl::ConfigSchema{
9477                    fields: Some(vec![
9478                        fdecl::ConfigField {
9479                            key: Some("test".to_string()),
9480                            type_: Some(fdecl::ConfigType {
9481                                layout: fdecl::ConfigTypeLayout::Vector,
9482                                parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9483                                    layout: fdecl::ConfigTypeLayout::Bool,
9484                                    parameters: Some(vec![]),
9485                                    constraints: vec![],
9486                                }), fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9487                                    layout: fdecl::ConfigTypeLayout::Uint8,
9488                                    parameters: Some(vec![]),
9489                                    constraints: vec![],
9490                                })]),
9491                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9492                            }),
9493                            ..Default::default()
9494                        }
9495                    ]),
9496                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9497                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9498                    ..Default::default()
9499                });
9500                decl
9501            },
9502            result = Err(ErrorList::new(vec![
9503                Error::extraneous_field(DeclType::ConfigType, "parameters")
9504            ])),
9505        },
9506
9507        test_validate_config_vector_missing_parameter => {
9508            input = {
9509                let mut decl = new_component_decl();
9510                decl.config = Some(fdecl::ConfigSchema{
9511                    fields: Some(vec![
9512                        fdecl::ConfigField {
9513                            key: Some("test".to_string()),
9514                            type_: Some(fdecl::ConfigType {
9515                                layout: fdecl::ConfigTypeLayout::Vector,
9516                                parameters: Some(vec![]),
9517                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9518                            }),
9519                            ..Default::default()
9520                        }
9521                    ]),
9522                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9523                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9524                    ..Default::default()
9525                });
9526                decl
9527            },
9528            result = Err(ErrorList::new(vec![
9529                Error::missing_field(DeclType::ConfigType, "parameters")
9530            ])),
9531        },
9532
9533        test_validate_config_vector_string => {
9534            input = {
9535                let mut decl = new_component_decl();
9536                decl.config = Some(fdecl::ConfigSchema{
9537                    fields: Some(vec![
9538                        fdecl::ConfigField {
9539                            key: Some("test".to_string()),
9540                            type_: Some(fdecl::ConfigType {
9541                                layout: fdecl::ConfigTypeLayout::Vector,
9542                                parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9543                                    layout: fdecl::ConfigTypeLayout::String,
9544                                    parameters: Some(vec![]),
9545                                    constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9546                                })]),
9547                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9548                            }),
9549                            ..Default::default()
9550                        }
9551                    ]),
9552                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9553                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9554                    ..Default::default()
9555                });
9556                decl
9557            },
9558            result = Ok(()),
9559        },
9560
9561        test_validate_config_vector_vector => {
9562            input = {
9563                let mut decl = new_component_decl();
9564                decl.config = Some(fdecl::ConfigSchema{
9565                    fields: Some(vec![
9566                        fdecl::ConfigField {
9567                            key: Some("test".to_string()),
9568                            type_: Some(fdecl::ConfigType {
9569                                layout: fdecl::ConfigTypeLayout::Vector,
9570                                parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9571                                    layout: fdecl::ConfigTypeLayout::Vector,
9572                                    parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9573                                        layout: fdecl::ConfigTypeLayout::String,
9574                                        parameters: Some(vec![]),
9575                                        constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9576                                    })]),
9577                                    constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9578                                })]),
9579                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9580                            }),
9581                            ..Default::default()
9582                        }
9583                    ]),
9584                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9585                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9586                    ..Default::default()
9587                });
9588                decl
9589            },
9590            result = Err(ErrorList::new(vec![
9591                Error::nested_vector()
9592            ])),
9593        },
9594
9595        test_validate_exposes_invalid_aggregation_different_availability => {
9596            input = {
9597                let mut decl = new_component_decl();
9598                decl.exposes = Some(vec![
9599                    fdecl::Expose::Service(fdecl::ExposeService {
9600                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9601                            name: "coll_a".into()
9602                        })),
9603                        source_name: Some("fuchsia.logger.Log".to_string()),
9604                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
9605                        target_name: Some("fuchsia.logger.Log".to_string()),
9606                        availability: Some(fdecl::Availability::Required),
9607                        ..Default::default()
9608                    }),
9609                    fdecl::Expose::Service(fdecl::ExposeService {
9610                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9611                            name: "coll_b".into()
9612                        })),
9613                        source_name: Some("fuchsia.logger.Log".to_string()),
9614                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
9615                        target_name: Some("fuchsia.logger.Log".to_string()),
9616                        availability: Some(fdecl::Availability::Optional),
9617                        ..Default::default()
9618                    })
9619                ]);
9620                decl.collections = Some(vec![
9621                    fdecl::Collection {
9622                        name: Some("coll_a".to_string()),
9623                        durability: Some(fdecl::Durability::Transient),
9624                        ..Default::default()
9625                    },
9626                    fdecl::Collection {
9627                        name: Some("coll_b".to_string()),
9628                        durability: Some(fdecl::Durability::Transient),
9629                        ..Default::default()
9630                    },
9631                ]);
9632                decl
9633            },
9634            result = Err(ErrorList::new(vec![
9635                Error::DifferentAvailabilityInAggregation(AvailabilityList(vec![
9636                    fdecl::Availability::Required,
9637                    fdecl::Availability::Optional,
9638                ]))
9639            ])),
9640        },
9641
9642        test_validate_offers_invalid_aggregation_different_availability => {
9643            input = {
9644                let mut decl = new_component_decl();
9645                decl.offers = Some(vec![
9646                    fdecl::Offer::Service(fdecl::OfferService {
9647                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9648                            name: "coll_a".into()
9649                        })),
9650                        source_name: Some("fuchsia.logger.Log".to_string()),
9651                        target: Some(fdecl::Ref::Child(
9652                            fdecl::ChildRef {
9653                                name: "child_c".to_string(),
9654                                collection: None,
9655                            }
9656                        )),
9657                        target_name: Some("fuchsia.logger.Log".to_string()),
9658                        source_instance_filter: Some(vec!["default".to_string()]),
9659                        availability: Some(fdecl::Availability::Required),
9660                        ..Default::default()
9661                    }),
9662                    fdecl::Offer::Service(fdecl::OfferService {
9663                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9664                            name: "coll_b".into()
9665                        })),
9666                        source_name: Some("fuchsia.logger.Log".to_string()),
9667                        target: Some(fdecl::Ref::Child(
9668                            fdecl::ChildRef {
9669                                name: "child_c".to_string(),
9670                                collection: None,
9671                            }
9672                        )),
9673                        target_name: Some("fuchsia.logger.Log".to_string()),
9674                        source_instance_filter: Some(vec!["a_different_default".to_string()]),
9675                        availability: Some(fdecl::Availability::Optional),
9676                        ..Default::default()
9677                    })
9678                ]);
9679                decl.collections = Some(vec![
9680                    fdecl::Collection {
9681                        name: Some("coll_a".to_string()),
9682                        durability: Some(fdecl::Durability::Transient),
9683                        ..Default::default()
9684                    },
9685                    fdecl::Collection {
9686                        name: Some("coll_b".to_string()),
9687                        durability: Some(fdecl::Durability::Transient),
9688                        ..Default::default()
9689                    },
9690                ]);
9691                decl.children = Some(vec![
9692                    fdecl::Child {
9693                        name: Some("child_c".to_string()),
9694                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9695                        startup: Some(fdecl::StartupMode::Lazy),
9696                        on_terminate: None,
9697                        environment: None,
9698                        ..Default::default()
9699                    },
9700                ]);
9701                decl
9702            },
9703            result = Err(ErrorList::new(vec![
9704                Error::DifferentAvailabilityInAggregation(AvailabilityList(vec![
9705                    fdecl::Availability::Required,
9706                    fdecl::Availability::Optional,
9707                ]))
9708            ])),
9709        },
9710    }
9711
9712    test_validate_capabilities! {
9713        test_validate_capabilities_individually_ok => {
9714            input = vec![
9715                fdecl::Capability::Protocol(fdecl::Protocol {
9716                    name: Some("foo_svc".into()),
9717                    source_path: Some("/svc/foo".into()),
9718                    ..Default::default()
9719                }),
9720                fdecl::Capability::Directory(fdecl::Directory {
9721                    name: Some("foo_dir".into()),
9722                    source_path: Some("/foo".into()),
9723                    rights: Some(fio::Operations::CONNECT),
9724                    ..Default::default()
9725                }),
9726            ],
9727            as_builtin = false,
9728            result = Ok(()),
9729        },
9730        test_validate_capabilities_individually_err => {
9731            input = vec![
9732                fdecl::Capability::Protocol(fdecl::Protocol {
9733                    name: None,
9734                    source_path: None,
9735                    ..Default::default()
9736                }),
9737                fdecl::Capability::Directory(fdecl::Directory {
9738                    name: None,
9739                    source_path: None,
9740                    rights: None,
9741                    ..Default::default()
9742                }),
9743            ],
9744            as_builtin = false,
9745            result = Err(ErrorList::new(vec![
9746                Error::missing_field(DeclType::Protocol, "name"),
9747                Error::missing_field(DeclType::Protocol, "source_path"),
9748                Error::missing_field(DeclType::Directory, "name"),
9749                Error::missing_field(DeclType::Directory, "source_path"),
9750                Error::missing_field(DeclType::Directory, "rights"),
9751            ])),
9752        },
9753        test_validate_builtin_capabilities_individually_ok => {
9754            input = vec![
9755                fdecl::Capability::Protocol(fdecl::Protocol {
9756                    name: Some("foo_protocol".into()),
9757                    source_path: None,
9758                    ..Default::default()
9759                }),
9760                fdecl::Capability::Directory(fdecl::Directory {
9761                    name: Some("foo_dir".into()),
9762                    source_path: None,
9763                    rights: Some(fio::Operations::CONNECT),
9764                    ..Default::default()
9765                }),
9766                fdecl::Capability::Service(fdecl::Service {
9767                    name: Some("foo_svc".into()),
9768                    source_path: None,
9769                    ..Default::default()
9770                }),
9771                fdecl::Capability::Runner(fdecl::Runner {
9772                    name: Some("foo_runner".into()),
9773                    source_path: None,
9774                    ..Default::default()
9775                }),
9776                fdecl::Capability::Resolver(fdecl::Resolver {
9777                    name: Some("foo_resolver".into()),
9778                    source_path: None,
9779                    ..Default::default()
9780                }),
9781            ],
9782            as_builtin = true,
9783            result = Ok(()),
9784        },
9785        test_validate_builtin_capabilities_individually_err => {
9786            input = vec![
9787                fdecl::Capability::Protocol(fdecl::Protocol {
9788                    name: None,
9789                    source_path: Some("/svc/foo".into()),
9790                    ..Default::default()
9791                }),
9792                fdecl::Capability::Directory(fdecl::Directory {
9793                    name: None,
9794                    source_path: Some("/foo".into()),
9795                    rights: None,
9796                    ..Default::default()
9797                }),
9798                fdecl::Capability::Service(fdecl::Service {
9799                    name: None,
9800                    source_path: Some("/svc/foo".into()),
9801                    ..Default::default()
9802                }),
9803                fdecl::Capability::Runner(fdecl::Runner {
9804                    name: None,
9805                    source_path:  Some("/foo".into()),
9806                    ..Default::default()
9807                }),
9808                fdecl::Capability::Resolver(fdecl::Resolver {
9809                    name: None,
9810                    source_path:  Some("/foo".into()),
9811                    ..Default::default()
9812                }),
9813                fdecl::Capability::Storage(fdecl::Storage {
9814                    name: None,
9815                    ..Default::default()
9816                }),
9817            ],
9818            as_builtin = true,
9819            result = Err(ErrorList::new(vec![
9820                Error::missing_field(DeclType::Protocol, "name"),
9821                Error::extraneous_source_path(DeclType::Protocol, "/svc/foo"),
9822                Error::missing_field(DeclType::Directory, "name"),
9823                Error::extraneous_source_path(DeclType::Directory, "/foo"),
9824                Error::missing_field(DeclType::Directory, "rights"),
9825                Error::missing_field(DeclType::Service, "name"),
9826                Error::extraneous_source_path(DeclType::Service, "/svc/foo"),
9827                Error::missing_field(DeclType::Runner, "name"),
9828                Error::extraneous_source_path(DeclType::Runner, "/foo"),
9829                Error::missing_field(DeclType::Resolver, "name"),
9830                Error::extraneous_source_path(DeclType::Resolver, "/foo"),
9831                Error::CapabilityCannotBeBuiltin(DeclType::Storage),
9832            ])),
9833        },
9834        test_validate_delivery_type_ok => {
9835            input = vec![
9836                fdecl::Capability::Protocol(fdecl::Protocol {
9837                    name: Some("foo_svc1".into()),
9838                    source_path: Some("/svc/foo1".into()),
9839                    ..Default::default()
9840                }),
9841                fdecl::Capability::Protocol(fdecl::Protocol {
9842                    name: Some("foo_svc2".into()),
9843                    source_path: Some("/svc/foo2".into()),
9844                    delivery: Some(fdecl::DeliveryType::Immediate),
9845                    ..Default::default()
9846                }),
9847                fdecl::Capability::Protocol(fdecl::Protocol {
9848                    name: Some("foo_svc3".into()),
9849                    source_path: Some("/svc/foo3".into()),
9850                    delivery: Some(fdecl::DeliveryType::OnReadable),
9851                    ..Default::default()
9852                }),
9853            ],
9854            as_builtin = false,
9855            result = Ok(()),
9856        },
9857        test_validate_delivery_type_err => {
9858            input = vec![
9859                fdecl::Capability::Protocol(fdecl::Protocol {
9860                    name: Some("foo_svc".into()),
9861                    source_path: Some("/svc/foo".into()),
9862                    delivery: Some(fdecl::DeliveryType::unknown()),
9863                    ..Default::default()
9864                }),
9865            ],
9866            as_builtin = false,
9867            result = Err(ErrorList::new(vec![
9868                Error::invalid_field(DeclType::Protocol, "delivery"),
9869            ])),
9870        },
9871    }
9872
9873    /// Passes different source and availability options to `new_expose` to
9874    /// generate a component declaration.
9875    fn generate_expose_different_source_and_availability_decl(
9876        new_expose: impl Fn(fdecl::Ref, fdecl::Availability, &str) -> fdecl::Expose,
9877    ) -> fdecl::Component {
9878        let mut decl = new_component_decl();
9879        let child = "child";
9880        decl.children = Some(vec![fdecl::Child {
9881            name: Some(child.to_string()),
9882            url: Some("fuchsia-pkg://fuchsia.com/source#meta/source.cm".to_string()),
9883            startup: Some(fdecl::StartupMode::Lazy),
9884            ..Default::default()
9885        }]);
9886        decl.exposes = Some(vec![
9887            // Optional expose from self is okay.
9888            new_expose(
9889                fdecl::Ref::Self_(fdecl::SelfRef {}),
9890                fdecl::Availability::Optional,
9891                "fuchsia.examples.Echo1",
9892            ),
9893            // Optional expose from child is okay.
9894            new_expose(
9895                fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
9896                fdecl::Availability::Optional,
9897                "fuchsia.examples.Echo2",
9898            ),
9899            // Optional expose from void is okay.
9900            new_expose(
9901                fdecl::Ref::VoidType(fdecl::VoidRef {}),
9902                fdecl::Availability::Optional,
9903                "fuchsia.examples.Echo3",
9904            ),
9905            // Optional expose from framework is okay.
9906            new_expose(
9907                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
9908                fdecl::Availability::Optional,
9909                "fuchsia.examples.Echo4",
9910            ),
9911            // Transitional expose from self is okay.
9912            new_expose(
9913                fdecl::Ref::Self_(fdecl::SelfRef {}),
9914                fdecl::Availability::Transitional,
9915                "fuchsia.examples.Echo5",
9916            ),
9917            // Transitional expose from child is okay.
9918            new_expose(
9919                fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
9920                fdecl::Availability::Transitional,
9921                "fuchsia.examples.Echo6",
9922            ),
9923            // Transitional expose from void is okay.
9924            new_expose(
9925                fdecl::Ref::VoidType(fdecl::VoidRef {}),
9926                fdecl::Availability::Transitional,
9927                "fuchsia.examples.Echo7",
9928            ),
9929            // Transitional expose from framework is okay.
9930            new_expose(
9931                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
9932                fdecl::Availability::Transitional,
9933                "fuchsia.examples.Echo8",
9934            ),
9935            // Same-as-target expose from self is okay.
9936            new_expose(
9937                fdecl::Ref::Self_(fdecl::SelfRef {}),
9938                fdecl::Availability::SameAsTarget,
9939                "fuchsia.examples.Echo9",
9940            ),
9941            // Same-as-target expose from child is okay.
9942            new_expose(
9943                fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
9944                fdecl::Availability::SameAsTarget,
9945                "fuchsia.examples.Echo10",
9946            ),
9947            // Same-as-target expose from framework is okay.
9948            new_expose(
9949                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
9950                fdecl::Availability::SameAsTarget,
9951                "fuchsia.examples.Echo11",
9952            ),
9953            // Required expose from void is an error.
9954            new_expose(
9955                fdecl::Ref::VoidType(fdecl::VoidRef {}),
9956                fdecl::Availability::Required,
9957                "fuchsia.examples.Echo12",
9958            ),
9959            // Same-as-target expose from void is an error.
9960            new_expose(
9961                fdecl::Ref::VoidType(fdecl::VoidRef {}),
9962                fdecl::Availability::SameAsTarget,
9963                "fuchsia.examples.Echo13",
9964            ),
9965        ]);
9966        decl
9967    }
9968
9969    /// Passes different source and availability options to `new_offer` to
9970    /// generate a component declaration.
9971    fn generate_offer_different_source_and_availability_decl(
9972        new_offer: impl Fn(fdecl::Ref, fdecl::Availability, &str) -> fdecl::Offer,
9973    ) -> fdecl::Component {
9974        let mut decl = new_component_decl();
9975        decl.children = Some(vec![
9976            fdecl::Child {
9977                name: Some("source".to_string()),
9978                url: Some("fuchsia-pkg://fuchsia.com/source#meta/source.cm".to_string()),
9979                startup: Some(fdecl::StartupMode::Lazy),
9980                on_terminate: None,
9981                environment: None,
9982                ..Default::default()
9983            },
9984            fdecl::Child {
9985                name: Some("sink".to_string()),
9986                url: Some("fuchsia-pkg://fuchsia.com/sink#meta/sink.cm".to_string()),
9987                startup: Some(fdecl::StartupMode::Lazy),
9988                on_terminate: None,
9989                environment: None,
9990                ..Default::default()
9991            },
9992        ]);
9993        decl.offers = Some(vec![
9994            // These offers are fine, offers with a source of parent or void can be
9995            // optional.
9996            new_offer(
9997                fdecl::Ref::Parent(fdecl::ParentRef {}),
9998                fdecl::Availability::Required,
9999                "fuchsia.examples.Echo0",
10000            ),
10001            new_offer(
10002                fdecl::Ref::Parent(fdecl::ParentRef {}),
10003                fdecl::Availability::Optional,
10004                "fuchsia.examples.Echo1",
10005            ),
10006            new_offer(
10007                fdecl::Ref::Parent(fdecl::ParentRef {}),
10008                fdecl::Availability::SameAsTarget,
10009                "fuchsia.examples.Echo2",
10010            ),
10011            new_offer(
10012                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10013                fdecl::Availability::Optional,
10014                "fuchsia.examples.Echo3",
10015            ),
10016            // These offers are fine, offers with a source other than parent or void
10017            // can also be optional.
10018            new_offer(
10019                fdecl::Ref::Self_(fdecl::SelfRef {}),
10020                fdecl::Availability::Optional,
10021                "fuchsia.examples.Echo4",
10022            ),
10023            new_offer(
10024                fdecl::Ref::Self_(fdecl::SelfRef {}),
10025                fdecl::Availability::SameAsTarget,
10026                "fuchsia.examples.Echo5",
10027            ),
10028            new_offer(
10029                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10030                fdecl::Availability::Optional,
10031                "fuchsia.examples.Echo6",
10032            ),
10033            new_offer(
10034                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10035                fdecl::Availability::SameAsTarget,
10036                "fuchsia.examples.Echo7",
10037            ),
10038            new_offer(
10039                fdecl::Ref::Child(fdecl::ChildRef { name: "source".to_string(), collection: None }),
10040                fdecl::Availability::Optional,
10041                "fuchsia.examples.Echo8",
10042            ),
10043            new_offer(
10044                fdecl::Ref::Child(fdecl::ChildRef { name: "source".to_string(), collection: None }),
10045                fdecl::Availability::SameAsTarget,
10046                "fuchsia.examples.Echo9",
10047            ),
10048            // These offers are also not fine, offers with a source of void must be optional
10049            new_offer(
10050                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10051                fdecl::Availability::Required,
10052                "fuchsia.examples.Echo10",
10053            ),
10054            new_offer(
10055                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10056                fdecl::Availability::SameAsTarget,
10057                "fuchsia.examples.Echo11",
10058            ),
10059        ]);
10060        decl
10061    }
10062
10063    #[test]
10064    fn test_validate_dynamic_offers_empty() {
10065        assert_eq!(validate_dynamic_offers(vec![], &vec![], &fdecl::Component::default()), Ok(()));
10066    }
10067
10068    #[test]
10069    fn test_validate_dynamic_offers_okay() {
10070        assert_eq!(
10071            validate_dynamic_offers(
10072                vec![],
10073                &vec![
10074                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
10075                        dependency_type: Some(fdecl::DependencyType::Strong),
10076                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef)),
10077                        source_name: Some("thing".to_string()),
10078                        target_name: Some("thing".repeat(26)),
10079                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10080                            name: "foo".to_string(),
10081                            collection: Some("foo".to_string()),
10082                        })),
10083                        ..Default::default()
10084                    }),
10085                    fdecl::Offer::Service(fdecl::OfferService {
10086                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10087                        source_name: Some("thang".repeat(26)),
10088                        target_name: Some("thang".repeat(26)),
10089                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10090                            name: "foo".to_string(),
10091                            collection: Some("foo".to_string()),
10092                        })),
10093                        ..Default::default()
10094                    }),
10095                    fdecl::Offer::Directory(fdecl::OfferDirectory {
10096                        dependency_type: Some(fdecl::DependencyType::Strong),
10097                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10098                        source_name: Some("thung1".repeat(26)),
10099                        target_name: Some("thung1".repeat(26)),
10100                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10101                            name: "foo".to_string(),
10102                            collection: Some("foo".to_string()),
10103                        })),
10104                        ..Default::default()
10105                    }),
10106                    fdecl::Offer::Storage(fdecl::OfferStorage {
10107                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10108                        source_name: Some("thung2".repeat(26)),
10109                        target_name: Some("thung2".repeat(26)),
10110                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10111                            name: "foo".to_string(),
10112                            collection: Some("foo".to_string()),
10113                        })),
10114                        ..Default::default()
10115                    }),
10116                    fdecl::Offer::Runner(fdecl::OfferRunner {
10117                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10118                        source_name: Some("thung3".repeat(26)),
10119                        target_name: Some("thung3".repeat(26)),
10120                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10121                            name: "foo".to_string(),
10122                            collection: Some("foo".to_string()),
10123                        })),
10124                        ..Default::default()
10125                    }),
10126                    fdecl::Offer::Resolver(fdecl::OfferResolver {
10127                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10128                        source_name: Some("thung4".repeat(26)),
10129                        target_name: Some("thung4".repeat(26)),
10130                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10131                            name: "foo".to_string(),
10132                            collection: Some("foo".to_string()),
10133                        })),
10134                        ..Default::default()
10135                    }),
10136                ],
10137                &fdecl::Component {
10138                    capabilities: Some(vec![fdecl::Capability::Protocol(fdecl::Protocol {
10139                        name: Some("thing".to_string()),
10140                        source_path: Some("/svc/foo".into()),
10141                        ..Default::default()
10142                    }),]),
10143                    ..Default::default()
10144                }
10145            ),
10146            Ok(())
10147        );
10148    }
10149
10150    #[test]
10151    fn test_validate_dynamic_offers_valid_service_aggregation() {
10152        assert_eq!(
10153            validate_dynamic_offers(
10154                vec![],
10155                &vec![
10156                    fdecl::Offer::Service(fdecl::OfferService {
10157                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10158                            name: "child_a".to_string(),
10159                            collection: None
10160                        })),
10161                        source_name: Some("fuchsia.logger.Log".to_string()),
10162                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10163                            name: "child_c".to_string(),
10164                            collection: None,
10165                        })),
10166                        target_name: Some("fuchsia.logger.Log".to_string()),
10167                        source_instance_filter: Some(vec!["default".to_string()]),
10168                        ..Default::default()
10169                    }),
10170                    fdecl::Offer::Service(fdecl::OfferService {
10171                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10172                            name: "child_b".to_string(),
10173                            collection: None
10174                        })),
10175                        source_name: Some("fuchsia.logger.Log".to_string()),
10176                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10177                            name: "child_c".to_string(),
10178                            collection: None,
10179                        })),
10180                        target_name: Some("fuchsia.logger.Log".to_string()),
10181                        source_instance_filter: Some(vec!["a_different_default".to_string()]),
10182                        ..Default::default()
10183                    })
10184                ],
10185                &fdecl::Component {
10186                    children: Some(vec![
10187                        fdecl::Child {
10188                            name: Some("child_a".to_string()),
10189                            url: Some(
10190                                "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10191                                    .to_string()
10192                            ),
10193                            startup: Some(fdecl::StartupMode::Lazy),
10194                            on_terminate: None,
10195                            environment: None,
10196                            ..Default::default()
10197                        },
10198                        fdecl::Child {
10199                            name: Some("child_b".to_string()),
10200                            url: Some(
10201                                "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10202                                    .to_string()
10203                            ),
10204                            startup: Some(fdecl::StartupMode::Lazy),
10205                            on_terminate: None,
10206                            environment: None,
10207                            ..Default::default()
10208                        },
10209                        fdecl::Child {
10210                            name: Some("child_c".to_string()),
10211                            url: Some(
10212                                "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10213                                    .to_string()
10214                            ),
10215                            startup: Some(fdecl::StartupMode::Lazy),
10216                            on_terminate: None,
10217                            environment: None,
10218                            ..Default::default()
10219                        },
10220                    ]),
10221                    ..Default::default()
10222                }
10223            ),
10224            Ok(())
10225        );
10226    }
10227
10228    #[test]
10229    fn test_validate_dynamic_service_aggregation_missing_filter() {
10230        assert_eq!(
10231            validate_dynamic_offers(
10232                vec![],
10233                &vec![
10234                    fdecl::Offer::Service(fdecl::OfferService {
10235                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10236                            name: "child_a".to_string(),
10237                            collection: None
10238                        })),
10239                        source_name: Some("fuchsia.logger.Log".to_string()),
10240                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10241                            name: "child_c".to_string(),
10242                            collection: None,
10243                        })),
10244                        target_name: Some("fuchsia.logger.Log".to_string()),
10245                        source_instance_filter: Some(vec!["default".to_string()]),
10246                        ..Default::default()
10247                    }),
10248                    fdecl::Offer::Service(fdecl::OfferService {
10249                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10250                            name: "child_b".to_string(),
10251                            collection: None
10252                        })),
10253                        source_name: Some("fuchsia.logger.Log".to_string()),
10254                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10255                            name: "child_c".to_string(),
10256                            collection: None,
10257                        })),
10258                        target_name: Some("fuchsia.logger.Log".to_string()),
10259                        source_instance_filter: None,
10260                        ..Default::default()
10261                    }),
10262                ],
10263                &fdecl::Component {
10264                    children: Some(vec![
10265                        fdecl::Child {
10266                            name: Some("child_a".to_string()),
10267                            url: Some(
10268                                "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10269                                    .to_string()
10270                            ),
10271                            startup: Some(fdecl::StartupMode::Lazy),
10272                            ..Default::default()
10273                        },
10274                        fdecl::Child {
10275                            name: Some("child_b".to_string()),
10276                            url: Some(
10277                                "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10278                                    .to_string()
10279                            ),
10280                            startup: Some(fdecl::StartupMode::Lazy),
10281                            ..Default::default()
10282                        },
10283                        fdecl::Child {
10284                            name: Some("child_c".to_string()),
10285                            url: Some(
10286                                "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10287                                    .to_string()
10288                            ),
10289                            startup: Some(fdecl::StartupMode::Lazy),
10290                            ..Default::default()
10291                        },
10292                    ]),
10293                    ..Default::default()
10294                },
10295            ),
10296            Err(ErrorList::new(vec![Error::invalid_aggregate_offer(
10297                "source_instance_filter must be set for dynamic aggregate service offers"
10298            ),]))
10299        );
10300    }
10301
10302    #[test]
10303    fn test_validate_dynamic_offers_omit_target() {
10304        assert_eq!(
10305            validate_dynamic_offers(
10306                vec![],
10307                &vec![
10308                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
10309                        dependency_type: Some(fdecl::DependencyType::Strong),
10310                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef)),
10311                        source_name: Some("thing".to_string()),
10312                        target_name: Some("thing".to_string()),
10313                        ..Default::default()
10314                    }),
10315                    fdecl::Offer::Service(fdecl::OfferService {
10316                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10317                        source_name: Some("thang".to_string()),
10318                        target_name: Some("thang".to_string()),
10319                        ..Default::default()
10320                    }),
10321                    fdecl::Offer::Directory(fdecl::OfferDirectory {
10322                        dependency_type: Some(fdecl::DependencyType::Strong),
10323                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10324                        source_name: Some("thung1".to_string()),
10325                        target_name: Some("thung1".to_string()),
10326                        ..Default::default()
10327                    }),
10328                    fdecl::Offer::Storage(fdecl::OfferStorage {
10329                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10330                        source_name: Some("thung2".to_string()),
10331                        target_name: Some("thung2".to_string()),
10332                        ..Default::default()
10333                    }),
10334                    fdecl::Offer::Runner(fdecl::OfferRunner {
10335                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10336                        source_name: Some("thung3".to_string()),
10337                        target_name: Some("thung3".to_string()),
10338                        ..Default::default()
10339                    }),
10340                    fdecl::Offer::Resolver(fdecl::OfferResolver {
10341                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10342                        source_name: Some("thung4".to_string()),
10343                        target_name: Some("thung4".to_string()),
10344                        ..Default::default()
10345                    }),
10346                ],
10347                &fdecl::Component {
10348                    capabilities: Some(vec![fdecl::Capability::Protocol(fdecl::Protocol {
10349                        name: Some("thing".to_string()),
10350                        source_path: Some("/svc/foo".into()),
10351                        ..Default::default()
10352                    }),]),
10353                    ..Default::default()
10354                }
10355            ),
10356            Err(ErrorList::new(vec![
10357                Error::missing_field(DeclType::OfferProtocol, "target"),
10358                Error::missing_field(DeclType::OfferService, "target"),
10359                Error::missing_field(DeclType::OfferDirectory, "target"),
10360                Error::missing_field(DeclType::OfferStorage, "target"),
10361                Error::missing_field(DeclType::OfferRunner, "target"),
10362                Error::missing_field(DeclType::OfferResolver, "target"),
10363            ]))
10364        );
10365    }
10366
10367    #[test]
10368    fn test_validate_dynamic_offers_collection_collision() {
10369        assert_eq!(
10370            validate_dynamic_offers(
10371                vec![],
10372                &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10373                    dependency_type: Some(fdecl::DependencyType::Strong),
10374                    source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10375                    source_name: Some("thing".to_string()),
10376                    target_name: Some("thing".to_string()),
10377                    target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10378                        name: "child".to_string(),
10379                        collection: Some("coll".to_string()),
10380                    })),
10381                    ..Default::default()
10382                }),],
10383                &fdecl::Component {
10384                    offers: Some(vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10385                        dependency_type: Some(fdecl::DependencyType::Strong),
10386                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10387                        source_name: Some("thing".to_string()),
10388                        target_name: Some("thing".to_string()),
10389                        target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10390                            name: "coll".into()
10391                        })),
10392                        ..Default::default()
10393                    }),]),
10394                    collections: Some(vec![fdecl::Collection {
10395                        name: Some("coll".to_string()),
10396                        durability: Some(fdecl::Durability::Transient),
10397                        ..Default::default()
10398                    },]),
10399                    ..Default::default()
10400                }
10401            ),
10402            Err(ErrorList::new(vec![Error::duplicate_field(
10403                DeclType::OfferProtocol,
10404                "target_name",
10405                "thing"
10406            ),]))
10407        );
10408    }
10409
10410    #[test]
10411    fn test_validate_dynamic_offers_cycle_collection_to_static_child() {
10412        assert_eq!(
10413            validate_dynamic_offers(
10414                vec![("dyn", "coll")],
10415                &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10416                    source_name: Some("bar".to_string()),
10417                    target_name: Some("bar".to_string()),
10418                    source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10419                        name: "static_child".into(),
10420                        collection: None,
10421                    })),
10422                    target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10423                        name: "dyn".to_string(),
10424                        collection: Some("coll".to_string()),
10425                    })),
10426                    dependency_type: Some(fdecl::DependencyType::Strong),
10427                    ..Default::default()
10428                }),],
10429                &fdecl::Component {
10430                    offers: Some(vec![fdecl::Offer::Service(fdecl::OfferService {
10431                        source_name: Some("foo".to_string()),
10432                        target_name: Some("foo".to_string()),
10433                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10434                            name: "coll".into(),
10435                        })),
10436                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10437                            name: "static_child".into(),
10438                            collection: None,
10439                        })),
10440                        ..Default::default()
10441                    })]),
10442                    children: Some(vec![fdecl::Child {
10443                        name: Some("static_child".into()),
10444                        url: Some("url#child.cm".into()),
10445                        startup: Some(fdecl::StartupMode::Lazy),
10446                        ..Default::default()
10447                    }]),
10448                    collections: Some(vec![fdecl::Collection {
10449                        name: Some("coll".into()),
10450                        durability: Some(fdecl::Durability::Transient),
10451                        ..Default::default()
10452                    }]),
10453                    ..Default::default()
10454                }
10455            ),
10456            Err(ErrorList::new(vec![Error::dependency_cycle(
10457                directed_graph::Error::CyclesDetected(
10458                    [vec![
10459                        "child coll:dyn",
10460                        "collection coll",
10461                        "child static_child",
10462                        "child coll:dyn",
10463                    ]]
10464                    .iter()
10465                    .cloned()
10466                    .collect()
10467                )
10468                .format_cycle()
10469            )]))
10470        );
10471    }
10472
10473    #[test]
10474    fn test_validate_dynamic_offers_cycle_collection_to_dynamic_child() {
10475        assert_eq!(
10476            validate_dynamic_offers(
10477                vec![("dyn", "coll1"), ("dyn", "coll2")],
10478                &vec![
10479                    fdecl::Offer::Service(fdecl::OfferService {
10480                        source_name: Some("foo".to_string()),
10481                        target_name: Some("foo".to_string()),
10482                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10483                            name: "coll2".into(),
10484                        })),
10485                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10486                            name: "dyn".into(),
10487                            collection: Some("coll1".into()),
10488                        })),
10489                        ..Default::default()
10490                    }),
10491                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
10492                        source_name: Some("bar".to_string()),
10493                        target_name: Some("bar".to_string()),
10494                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10495                            name: "dyn".into(),
10496                            collection: Some("coll1".into()),
10497                        })),
10498                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10499                            name: "dyn".to_string(),
10500                            collection: Some("coll2".to_string()),
10501                        })),
10502                        dependency_type: Some(fdecl::DependencyType::Strong),
10503                        ..Default::default()
10504                    }),
10505                ],
10506                &fdecl::Component {
10507                    collections: Some(vec![
10508                        fdecl::Collection {
10509                            name: Some("coll1".into()),
10510                            durability: Some(fdecl::Durability::Transient),
10511                            ..Default::default()
10512                        },
10513                        fdecl::Collection {
10514                            name: Some("coll2".into()),
10515                            durability: Some(fdecl::Durability::Transient),
10516                            ..Default::default()
10517                        },
10518                    ]),
10519                    ..Default::default()
10520                }
10521            ),
10522            Err(ErrorList::new(vec![Error::dependency_cycle(
10523                directed_graph::Error::CyclesDetected(
10524                    [vec![
10525                        "child coll1:dyn",
10526                        "child coll2:dyn",
10527                        "collection coll2",
10528                        "child coll1:dyn",
10529                    ]]
10530                    .iter()
10531                    .cloned()
10532                    .collect()
10533                )
10534                .format_cycle()
10535            )]))
10536        );
10537    }
10538
10539    #[test]
10540    fn test_validate_dynamic_offers_cycle_collection_to_collection() {
10541        assert_eq!(
10542            validate_dynamic_offers(
10543                vec![("dyn", "coll1"), ("dyn", "coll2")],
10544                &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10545                    source_name: Some("bar".to_string()),
10546                    target_name: Some("bar".to_string()),
10547                    source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10548                        name: "dyn".into(),
10549                        collection: Some("coll2".parse().unwrap()),
10550                    })),
10551                    target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10552                        name: "dyn".into(),
10553                        collection: Some("coll1".parse().unwrap()),
10554                    })),
10555                    dependency_type: Some(fdecl::DependencyType::Strong),
10556                    ..Default::default()
10557                }),],
10558                &fdecl::Component {
10559                    offers: Some(vec![fdecl::Offer::Service(fdecl::OfferService {
10560                        source_name: Some("foo".to_string()),
10561                        target_name: Some("foo".to_string()),
10562                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10563                            name: "coll1".into(),
10564                        })),
10565                        target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10566                            name: "coll2".into(),
10567                        })),
10568                        ..Default::default()
10569                    })]),
10570                    collections: Some(vec![
10571                        fdecl::Collection {
10572                            name: Some("coll1".into()),
10573                            durability: Some(fdecl::Durability::Transient),
10574                            ..Default::default()
10575                        },
10576                        fdecl::Collection {
10577                            name: Some("coll2".into()),
10578                            durability: Some(fdecl::Durability::Transient),
10579                            ..Default::default()
10580                        },
10581                    ]),
10582                    ..Default::default()
10583                }
10584            ),
10585            Err(ErrorList::new(vec![Error::dependency_cycle(
10586                directed_graph::Error::CyclesDetected(
10587                    [vec![
10588                        "child coll1:dyn",
10589                        "collection coll1",
10590                        "child coll2:dyn",
10591                        "child coll1:dyn",
10592                    ]]
10593                    .iter()
10594                    .cloned()
10595                    .collect()
10596                )
10597                .format_cycle()
10598            )]))
10599        );
10600    }
10601
10602    #[test]
10603    fn test_validate_dynamic_child() {
10604        assert_eq!(
10605            Ok(()),
10606            validate_dynamic_child(&fdecl::Child {
10607                name: Some("a".repeat(MAX_LONG_NAME_LENGTH).to_string()),
10608                url: Some("test:///child".to_string()),
10609                startup: Some(fdecl::StartupMode::Lazy),
10610                on_terminate: None,
10611                environment: None,
10612                ..Default::default()
10613            })
10614        );
10615    }
10616
10617    #[test]
10618    fn test_validate_dynamic_child_environment_is_invalid() {
10619        assert_eq!(
10620            validate_dynamic_child(&fdecl::Child {
10621                name: Some("a".repeat(MAX_LONG_NAME_LENGTH).to_string()),
10622                url: Some("test:///child".to_string()),
10623                startup: Some(fdecl::StartupMode::Lazy),
10624                on_terminate: None,
10625                environment: Some("env".to_string()),
10626                ..Default::default()
10627            }),
10628            Err(ErrorList::new(vec![Error::DynamicChildWithEnvironment]))
10629        );
10630    }
10631
10632    #[test]
10633    fn test_validate_dynamic_offers_missing_stuff() {
10634        assert_eq!(
10635            validate_dynamic_offers(
10636                vec![],
10637                &vec![
10638                    fdecl::Offer::Protocol(fdecl::OfferProtocol::default()),
10639                    fdecl::Offer::Service(fdecl::OfferService::default()),
10640                    fdecl::Offer::Directory(fdecl::OfferDirectory::default()),
10641                    fdecl::Offer::Storage(fdecl::OfferStorage::default()),
10642                    fdecl::Offer::Runner(fdecl::OfferRunner::default()),
10643                    fdecl::Offer::Resolver(fdecl::OfferResolver::default()),
10644                ],
10645                &fdecl::Component::default()
10646            ),
10647            Err(ErrorList::new(vec![
10648                Error::missing_field(DeclType::OfferProtocol, "source"),
10649                Error::missing_field(DeclType::OfferProtocol, "source_name"),
10650                Error::missing_field(DeclType::OfferProtocol, "target"),
10651                Error::missing_field(DeclType::OfferProtocol, "target_name"),
10652                Error::missing_field(DeclType::OfferProtocol, "dependency_type"),
10653                Error::missing_field(DeclType::OfferService, "source"),
10654                Error::missing_field(DeclType::OfferService, "source_name"),
10655                Error::missing_field(DeclType::OfferService, "target"),
10656                Error::missing_field(DeclType::OfferService, "target_name"),
10657                Error::missing_field(DeclType::OfferDirectory, "source"),
10658                Error::missing_field(DeclType::OfferDirectory, "source_name"),
10659                Error::missing_field(DeclType::OfferDirectory, "target"),
10660                Error::missing_field(DeclType::OfferDirectory, "target_name"),
10661                Error::missing_field(DeclType::OfferDirectory, "dependency_type"),
10662                Error::missing_field(DeclType::OfferStorage, "source"),
10663                Error::missing_field(DeclType::OfferStorage, "source_name"),
10664                Error::missing_field(DeclType::OfferStorage, "target"),
10665                Error::missing_field(DeclType::OfferStorage, "target_name"),
10666                Error::missing_field(DeclType::OfferRunner, "source"),
10667                Error::missing_field(DeclType::OfferRunner, "source_name"),
10668                Error::missing_field(DeclType::OfferRunner, "target"),
10669                Error::missing_field(DeclType::OfferRunner, "target_name"),
10670                Error::missing_field(DeclType::OfferResolver, "source"),
10671                Error::missing_field(DeclType::OfferResolver, "source_name"),
10672                Error::missing_field(DeclType::OfferResolver, "target"),
10673                Error::missing_field(DeclType::OfferResolver, "target_name"),
10674            ]))
10675        );
10676    }
10677
10678    test_dependency! {
10679        (test_validate_offers_protocol_dependency_cycle) => {
10680            ty = fdecl::Offer::Protocol,
10681            offer_decl = fdecl::OfferProtocol {
10682                source: None,  // Filled by macro
10683                target: None,  // Filled by macro
10684                source_name: Some(format!("thing")),
10685                target_name: Some(format!("thing")),
10686                dependency_type: Some(fdecl::DependencyType::Strong),
10687                ..Default::default()
10688            },
10689        },
10690        (test_validate_offers_directory_dependency_cycle) => {
10691            ty = fdecl::Offer::Directory,
10692            offer_decl = fdecl::OfferDirectory {
10693                source: None,  // Filled by macro
10694                target: None,  // Filled by macro
10695                source_name: Some(format!("thing")),
10696                target_name: Some(format!("thing")),
10697                rights: Some(fio::Operations::CONNECT),
10698                subdir: None,
10699                dependency_type: Some(fdecl::DependencyType::Strong),
10700                ..Default::default()
10701            },
10702        },
10703        (test_validate_offers_service_dependency_cycle) => {
10704            ty = fdecl::Offer::Service,
10705            offer_decl = fdecl::OfferService {
10706                source: None,  // Filled by macro
10707                target: None,  // Filled by macro
10708                source_name: Some(format!("thing")),
10709                target_name: Some(format!("thing")),
10710                ..Default::default()
10711            },
10712        },
10713        (test_validate_offers_runner_dependency_cycle) => {
10714            ty = fdecl::Offer::Runner,
10715            offer_decl = fdecl::OfferRunner {
10716                source: None,  // Filled by macro
10717                target: None,  // Filled by macro
10718                source_name: Some(format!("thing")),
10719                target_name: Some(format!("thing")),
10720                ..Default::default()
10721            },
10722        },
10723        (test_validate_offers_resolver_dependency_cycle) => {
10724            ty = fdecl::Offer::Resolver,
10725            offer_decl = fdecl::OfferResolver {
10726                source: None,  // Filled by macro
10727                target: None,  // Filled by macro
10728                source_name: Some(format!("thing")),
10729                target_name: Some(format!("thing")),
10730                ..Default::default()
10731            },
10732        },
10733    }
10734    test_weak_dependency! {
10735        (test_validate_offers_protocol_weak_dependency_cycle) => {
10736            ty = fdecl::Offer::Protocol,
10737            offer_decl = fdecl::OfferProtocol {
10738                source: None,  // Filled by macro
10739                target: None,  // Filled by macro
10740                source_name: Some(format!("thing")),
10741                target_name: Some(format!("thing")),
10742                dependency_type: None, // Filled by macro
10743                ..Default::default()
10744            },
10745        },
10746        (test_validate_offers_directory_weak_dependency_cycle) => {
10747            ty = fdecl::Offer::Directory,
10748            offer_decl = fdecl::OfferDirectory {
10749                source: None,  // Filled by macro
10750                target: None,  // Filled by macro
10751                source_name: Some(format!("thing")),
10752                target_name: Some(format!("thing")),
10753                rights: Some(fio::Operations::CONNECT),
10754                subdir: None,
10755                dependency_type: None,  // Filled by macro
10756                ..Default::default()
10757            },
10758        },
10759        (test_validate_offers_service_weak_dependency_cycle) => {
10760            ty = fdecl::Offer::Service,
10761            offer_decl = fdecl::OfferService {
10762                source: None,  // Filled by macro
10763                target: None,  // Filled by macro
10764                source_name: Some(format!("thing")),
10765                target_name: Some(format!("thing")),
10766                dependency_type: None, // Filled by macro
10767                ..Default::default()
10768            },
10769        },
10770    }
10771}