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::fmt;
18use std::path::Path;
19
20trait HasAvailability {
21    fn availability(&self) -> fdecl::Availability;
22}
23
24impl HasAvailability for fdecl::ExposeService {
25    fn availability(&self) -> fdecl::Availability {
26        return self.availability.unwrap_or(fdecl::Availability::Required);
27    }
28}
29
30impl HasAvailability for fdecl::OfferService {
31    fn availability(&self) -> fdecl::Availability {
32        return self.availability.unwrap_or(fdecl::Availability::Required);
33    }
34}
35
36#[cfg(fuchsia_api_level_at_least = "25")]
37macro_rules! get_source_dictionary {
38    ($decl:ident) => {
39        $decl.source_dictionary.as_ref()
40    };
41}
42#[cfg(fuchsia_api_level_less_than = "25")]
43macro_rules! get_source_dictionary {
44    ($decl:ident) => {
45        None
46    };
47}
48
49/// Validates Configuration Value Spec.
50///
51/// For now, this simply verifies that all semantically required fields are present.
52pub fn validate_value_spec(spec: &fdecl::ConfigValueSpec) -> Result<(), ErrorList> {
53    let mut errors = vec![];
54    if let Some(value) = &spec.value {
55        match value {
56            fdecl::ConfigValue::Single(s) => match s {
57                fdecl::ConfigSingleValue::Bool(_)
58                | fdecl::ConfigSingleValue::Uint8(_)
59                | fdecl::ConfigSingleValue::Uint16(_)
60                | fdecl::ConfigSingleValue::Uint32(_)
61                | fdecl::ConfigSingleValue::Uint64(_)
62                | fdecl::ConfigSingleValue::Int8(_)
63                | fdecl::ConfigSingleValue::Int16(_)
64                | fdecl::ConfigSingleValue::Int32(_)
65                | fdecl::ConfigSingleValue::Int64(_)
66                | fdecl::ConfigSingleValue::String(_) => {}
67                fdecl::ConfigSingleValueUnknown!() => {
68                    errors.push(Error::invalid_field(DeclType::ConfigValueSpec, "value"));
69                }
70            },
71            fdecl::ConfigValue::Vector(l) => match l {
72                fdecl::ConfigVectorValue::BoolVector(_)
73                | fdecl::ConfigVectorValue::Uint8Vector(_)
74                | fdecl::ConfigVectorValue::Uint16Vector(_)
75                | fdecl::ConfigVectorValue::Uint32Vector(_)
76                | fdecl::ConfigVectorValue::Uint64Vector(_)
77                | fdecl::ConfigVectorValue::Int8Vector(_)
78                | fdecl::ConfigVectorValue::Int16Vector(_)
79                | fdecl::ConfigVectorValue::Int32Vector(_)
80                | fdecl::ConfigVectorValue::Int64Vector(_)
81                | fdecl::ConfigVectorValue::StringVector(_) => {}
82                fdecl::ConfigVectorValueUnknown!() => {
83                    errors.push(Error::invalid_field(DeclType::ConfigValueSpec, "value"));
84                }
85            },
86            fdecl::ConfigValueUnknown!() => {
87                errors.push(Error::invalid_field(DeclType::ConfigValueSpec, "value"));
88            }
89        }
90    } else {
91        errors.push(Error::missing_field(DeclType::ConfigValueSpec, "value"));
92    }
93
94    if errors.is_empty() {
95        Ok(())
96    } else {
97        Err(ErrorList::new(errors))
98    }
99}
100
101/// Validates Configuration Values Data.
102///
103/// The Value Data may ultimately originate from a CVF file, or be directly constructed by the
104/// caller. Either way, Value Data should always be validated before it's used. For now, this
105/// simply verifies that all semantically required fields are present.
106///
107/// This method does not validate value data against a configuration schema.
108pub fn validate_values_data(data: &fdecl::ConfigValuesData) -> Result<(), ErrorList> {
109    let mut errors = vec![];
110    if let Some(values) = &data.values {
111        for spec in values {
112            if let Err(mut e) = validate_value_spec(spec) {
113                errors.append(&mut e.errs);
114            }
115        }
116    } else {
117        errors.push(Error::missing_field(DeclType::ConfigValuesData, "values"));
118    }
119
120    if let Some(checksum) = &data.checksum {
121        match checksum {
122            fdecl::ConfigChecksum::Sha256(_) => {}
123            fdecl::ConfigChecksumUnknown!() => {
124                errors.push(Error::invalid_field(DeclType::ConfigValuesData, "checksum"));
125            }
126        }
127    } else {
128        errors.push(Error::missing_field(DeclType::ConfigValuesData, "checksum"));
129    }
130
131    if errors.is_empty() {
132        Ok(())
133    } else {
134        Err(ErrorList::new(errors))
135    }
136}
137
138// `fdecl::Ref` is not hashable, so define this equivalent type for use in maps
139#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
140enum RefKey<'a> {
141    Parent,
142    Self_,
143    Child(&'a str),
144    Collection(&'a str),
145    Framework,
146    Capability,
147    Debug,
148}
149
150/// Validates a Component.
151///
152/// The Component may ultimately originate from a CM file, or be directly constructed by the
153/// caller. Either way, a Component should always be validated before it's used. Examples
154/// of what is validated (which may evolve in the future):
155///
156/// - That all semantically required fields are present
157/// - That a child_name referenced in a source actually exists in the list of children
158/// - That there are no duplicate target paths.
159/// - That only weak-dependency capabilities may be offered back to the
160///   component that exposed them.
161///
162/// All checks are local to this Component.
163pub fn validate(decl: &fdecl::Component) -> Result<(), ErrorList> {
164    let ctx = ValidationContext::default();
165    ctx.validate(decl, None).map_err(|errs| ErrorList::new(errs))
166}
167
168/// Validates a list of namespace or builtin Capabilities.
169fn validate_capabilities(
170    capabilities: &[fdecl::Capability],
171    as_builtin: bool,
172) -> Result<(), ErrorList> {
173    let mut ctx = ValidationContext::default();
174
175    #[cfg(fuchsia_api_level_at_least = "25")]
176    ctx.load_dictionary_names(capabilities.iter().filter_map(|capability| match capability {
177        fdecl::Capability::Dictionary(dictionary_decl) => Some(dictionary_decl),
178        _ => None,
179    }));
180
181    ctx.validate_capability_decls(capabilities, as_builtin);
182    if ctx.errors.is_empty() {
183        Ok(())
184    } else {
185        Err(ErrorList::new(ctx.errors))
186    }
187}
188
189// Validate builtin capabilities.
190pub fn validate_builtin_capabilities(
191    capabilities: &Vec<fdecl::Capability>,
192) -> Result<(), ErrorList> {
193    validate_capabilities(capabilities, true)
194}
195
196// Validate namespace capabilities.
197pub fn validate_namespace_capabilities(
198    capabilities: &Vec<fdecl::Capability>,
199) -> Result<(), ErrorList> {
200    validate_capabilities(capabilities, false)
201}
202
203/// An interface to call into either `check_dynamic_name()` or `check_name()`, depending on the context
204/// of the caller.
205type CheckChildNameFn = fn(Option<&String>, DeclType, &str, &mut Vec<Error>) -> bool;
206
207pub fn validate_dynamic_child(child: &fdecl::Child) -> Result<(), ErrorList> {
208    let mut errors = vec![];
209
210    if let Err(mut error_list) = validate_child(child, check_dynamic_name) {
211        errors.append(&mut error_list.errs);
212    }
213
214    if child.environment.is_some() {
215        errors.push(Error::DynamicChildWithEnvironment);
216    }
217
218    if errors.is_empty() {
219        Ok(())
220    } else {
221        Err(ErrorList { errs: errors })
222    }
223}
224
225/// Validates an independent Child. Performs the same validation on it as `validate`. A
226/// `check_name_fn` is passed into specify the function used to validate the child name.
227fn validate_child(
228    child: &fdecl::Child,
229    check_child_name: CheckChildNameFn,
230) -> Result<(), ErrorList> {
231    let mut errors = vec![];
232    check_child_name(child.name.as_ref(), DeclType::Child, "name", &mut errors);
233    check_url(child.url.as_ref(), DeclType::Child, "url", &mut errors);
234    if child.startup.is_none() {
235        errors.push(Error::missing_field(DeclType::Child, "startup"));
236    }
237    // Allow `on_terminate` to be unset since the default is almost always desired.
238    if child.environment.is_some() {
239        check_name(child.environment.as_ref(), DeclType::Child, "environment", &mut errors);
240    }
241    if errors.is_empty() {
242        Ok(())
243    } else {
244        Err(ErrorList { errs: errors })
245    }
246}
247
248/// Validates a collection of dynamic offers. Dynamic offers differ from static
249/// offers, in that
250///
251/// 1. a dynamic offer's `target` field must be omitted;
252/// 2. a dynamic offer's `source` _may_ be a dynamic child;
253/// 3. since this crate isn't really designed to handle dynamic children, we
254///    disable the checks that ensure that the source/target exist, and that the
255///    offers don't introduce any cycles.
256pub fn validate_dynamic_offers<'a>(
257    dynamic_children: Vec<(&'a str, &'a str)>,
258    offers: &'a Vec<fdecl::Offer>,
259    decl: &'a fdecl::Component,
260) -> Result<(), ErrorList> {
261    let mut ctx = ValidationContext::default();
262    ctx.dynamic_children = dynamic_children;
263    ctx.validate(decl, Some(offers)).map_err(|errs| ErrorList::new(errs))
264}
265
266fn check_offer_name(
267    prop: Option<&String>,
268    decl: DeclType,
269    keyword: &str,
270    offer_type: OfferType,
271    errors: &mut Vec<Error>,
272) -> bool {
273    if offer_type == OfferType::Dynamic {
274        check_dynamic_name(prop, decl, keyword, errors)
275    } else {
276        check_name(prop, decl, keyword, errors)
277    }
278}
279
280#[derive(Default)]
281struct ValidationContext<'a> {
282    all_children: HashMap<&'a str, &'a fdecl::Child>,
283    all_collections: HashSet<&'a str>,
284    all_capability_ids: HashSet<&'a str>,
285    all_storages: HashMap<&'a str, Option<&'a fdecl::Ref>>,
286    all_services: HashSet<&'a str>,
287    all_protocols: HashSet<&'a str>,
288    all_directories: HashSet<&'a str>,
289    all_runners: HashSet<&'a str>,
290    all_resolvers: HashSet<&'a str>,
291    #[cfg(fuchsia_api_level_at_least = "25")]
292    all_dictionaries: HashMap<&'a str, &'a fdecl::Dictionary>,
293
294    #[cfg(fuchsia_api_level_at_least = "HEAD")]
295    all_configs: HashSet<&'a str>,
296
297    all_environment_names: HashSet<&'a str>,
298    dynamic_children: Vec<(&'a str, &'a str)>,
299    strong_dependencies: DirectedGraph<DependencyNode<'a>>,
300    target_ids: IdMap<'a>,
301    errors: Vec<Error>,
302}
303
304/// [Container] provides a capability type agnostic trait to check for the existence of a
305/// capability definition of a particular type. This is useful for writing common validation
306/// functions.
307trait Container {
308    fn contains(&self, key: &str) -> bool;
309}
310
311impl<'a> Container for HashSet<&'a str> {
312    fn contains(&self, key: &str) -> bool {
313        self.contains(key)
314    }
315}
316
317impl<'a, T> Container for HashMap<&'a str, T> {
318    fn contains(&self, key: &str) -> bool {
319        self.contains_key(key)
320    }
321}
322
323/// A node in the DependencyGraph. The first string describes the type of node and the second
324/// string is the name of the node.
325#[derive(Copy, Clone, Hash, Ord, Debug, PartialOrd, PartialEq, Eq)]
326enum DependencyNode<'a> {
327    Self_,
328    Child(&'a str, Option<&'a str>),
329    Collection(&'a str),
330    Environment(&'a str),
331    Capability(&'a str),
332}
333
334impl<'a> fmt::Display for DependencyNode<'a> {
335    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
336        match self {
337            DependencyNode::Self_ => write!(f, "self"),
338            DependencyNode::Child(name, None) => write!(f, "child {}", name),
339            DependencyNode::Child(name, Some(collection)) => {
340                write!(f, "child {}:{}", collection, name)
341            }
342            DependencyNode::Collection(name) => write!(f, "collection {}", name),
343            DependencyNode::Environment(name) => write!(f, "environment {}", name),
344            DependencyNode::Capability(name) => write!(f, "capability {}", name),
345        }
346    }
347}
348
349fn ref_to_dependency_node<'a>(ref_: Option<&'a fdecl::Ref>) -> Option<DependencyNode<'a>> {
350    match ref_? {
351        fdecl::Ref::Self_(_) => Some(DependencyNode::Self_),
352        fdecl::Ref::Child(fdecl::ChildRef { name, collection }) => {
353            Some(DependencyNode::Child(name, collection.as_ref().map(|s| s.as_str())))
354        }
355        fdecl::Ref::Collection(fdecl::CollectionRef { name }) => {
356            Some(DependencyNode::Collection(name))
357        }
358        fdecl::Ref::Capability(fdecl::CapabilityRef { name }) => {
359            Some(DependencyNode::Capability(name))
360        }
361        fdecl::Ref::Framework(_)
362        | fdecl::Ref::Parent(_)
363        | fdecl::Ref::Debug(_)
364        | fdecl::Ref::VoidType(_) => None,
365        #[cfg(fuchsia_api_level_at_least = "HEAD")]
366        fdecl::Ref::Environment(_) => None,
367        _ => None,
368    }
369}
370
371fn generate_dependency_graph<'a>(
372    strong_dependencies: &mut DirectedGraph<DependencyNode<'a>>,
373    decl: &'a fdecl::Component,
374) {
375    if let Some(uses) = decl.uses.as_ref() {
376        for use_ in uses.iter() {
377            let (dependency_type, source) = match use_ {
378                fdecl::Use::Service(u) => (u.dependency_type, &u.source),
379                fdecl::Use::Protocol(u) => (u.dependency_type, &u.source),
380                fdecl::Use::Directory(u) => (u.dependency_type, &u.source),
381                fdecl::Use::EventStream(u) => (Some(fdecl::DependencyType::Strong), &u.source),
382                #[cfg(fuchsia_api_level_at_least = "HEAD")]
383                fdecl::Use::Runner(u) => (Some(fdecl::DependencyType::Strong), &u.source),
384                #[cfg(fuchsia_api_level_at_least = "HEAD")]
385                fdecl::Use::Config(u) => (Some(fdecl::DependencyType::Strong), &u.source),
386                // Storage can only be used from parent, which we don't track.
387                fdecl::Use::Storage(_) => continue,
388                _ => continue,
389            };
390            if dependency_type != Some(fdecl::DependencyType::Strong) {
391                continue;
392            }
393            if let Some(fdecl::Ref::Self_(_)) = &source {
394                continue;
395            }
396            if let Some(source_node) = ref_to_dependency_node(source.as_ref()) {
397                strong_dependencies.add_edge(source_node, DependencyNode::Self_);
398            }
399        }
400    }
401}
402
403impl<'a> ValidationContext<'a> {
404    fn validate(
405        mut self,
406        decl: &'a fdecl::Component,
407        dynamic_offers: Option<&'a Vec<fdecl::Offer>>,
408    ) -> Result<(), Vec<Error>> {
409        // Collect all environment names first, so that references to them can be checked.
410        if let Some(envs) = &decl.environments {
411            self.collect_environment_names(&envs);
412        }
413
414        // Validate "children" and build the set of all children.
415        if let Some(children) = decl.children.as_ref() {
416            for child in children {
417                self.validate_child_decl(&child);
418            }
419        }
420
421        // Validate "collections" and build the set of all collections.
422        if let Some(collections) = decl.collections.as_ref() {
423            for collection in collections {
424                self.validate_collection_decl(&collection);
425            }
426        }
427
428        // Validate "capabilities" and build the set of all capabilities.
429        if let Some(capabilities) = decl.capabilities.as_ref() {
430            #[cfg(fuchsia_api_level_at_least = "25")]
431            self.load_dictionary_names(capabilities.iter().filter_map(
432                |capability| match capability {
433                    fdecl::Capability::Dictionary(dictionary_decl) => Some(dictionary_decl),
434                    _ => None,
435                },
436            ));
437            self.validate_capability_decls(capabilities, false);
438        }
439
440        // Validate "uses".
441        let mut use_runner_name = None;
442        let mut use_runner_source = None;
443        if let Some(uses) = decl.uses.as_ref() {
444            (use_runner_name, use_runner_source) = self.validate_use_decls(uses);
445        }
446
447        // Validate "program".
448        if let Some(program) = decl.program.as_ref() {
449            self.validate_program(program, use_runner_name, use_runner_source);
450        }
451
452        // Validate "exposes".
453        if let Some(exposes) = decl.exposes.as_ref() {
454            let mut expose_to_parent_ids = HashMap::new();
455            let mut expose_to_framework_ids = HashMap::new();
456            for expose in exposes.iter() {
457                self.validate_expose_decl(
458                    &expose,
459                    &mut expose_to_parent_ids,
460                    &mut expose_to_framework_ids,
461                );
462            }
463            self.validate_expose_group(&exposes);
464        }
465
466        // Validate "offers".
467        if let Some(offers) = decl.offers.as_ref() {
468            for offer in offers.iter() {
469                self.validate_offer_decl(&offer, OfferType::Static);
470            }
471            self.validate_offer_group(&offers, OfferType::Static);
472        }
473
474        if let Some(dynamic_offers) = dynamic_offers.as_ref() {
475            for dynamic_offer in dynamic_offers.iter() {
476                self.validate_offer_decl(&dynamic_offer, OfferType::Dynamic);
477            }
478            self.validate_offer_group(&dynamic_offers, OfferType::Dynamic);
479        }
480
481        // Validate "environments" after all other declarations are processed.
482        if let Some(environment) = decl.environments.as_ref() {
483            for environment in environment {
484                self.validate_environment_decl(&environment);
485            }
486        }
487
488        // Validate "config"
489        #[cfg(fuchsia_api_level_at_least = "20")]
490        self.validate_config(decl.config.as_ref(), decl.uses.as_ref());
491
492        // Check that there are no strong cyclical dependencies
493        generate_dependency_graph(&mut self.strong_dependencies, &decl);
494        if let Err(e) = self.strong_dependencies.topological_sort() {
495            self.errors.push(Error::dependency_cycle(e.format_cycle()));
496        }
497
498        if self.errors.is_empty() {
499            Ok(())
500        } else {
501            Err(self.errors)
502        }
503    }
504
505    /// Collects all the environment names, watching for duplicates.
506    fn collect_environment_names(&mut self, envs: &'a [fdecl::Environment]) {
507        for env in envs {
508            if let Some(name) = env.name.as_ref() {
509                if !self.all_environment_names.insert(name) {
510                    self.errors.push(Error::duplicate_field(DeclType::Environment, "name", name));
511                }
512            }
513        }
514    }
515
516    // Validates a config schema. Checks that each field's layout matches the expected constraints
517    // and properties.
518    #[cfg(fuchsia_api_level_at_least = "20")]
519    fn validate_config(
520        &mut self,
521        config: Option<&fdecl::ConfigSchema>,
522        uses: Option<&Vec<fdecl::Use>>,
523    ) {
524        use std::collections::BTreeMap;
525
526        // Get all of the `use` configs that are optional without a default.
527        let optional_use_keys: BTreeMap<String, fdecl::ConfigType> =
528            uses.map_or(BTreeMap::new(), |u| {
529                u.iter()
530                    .map(|u| {
531                        let fdecl::Use::Config(config) = u else {
532                            return None;
533                        };
534                        if config.availability == Some(fdecl::Availability::Required)
535                            || config.availability == None
536                        {
537                            return None;
538                        }
539                        if let Some(_) = config.default.as_ref() {
540                            return None;
541                        }
542                        let Some(key) = config.target_name.clone() else {
543                            return None;
544                        };
545                        let Some(value) = config.type_.clone() else {
546                            return None;
547                        };
548                        Some((key, value))
549                    })
550                    .flatten()
551                    .collect()
552            });
553
554        // Validate default values in use configs.
555        for u in uses.iter().flat_map(|x| x.iter()) {
556            let fdecl::Use::Config(config) = u else { continue };
557            let Some(default) = config.default.as_ref() else { continue };
558            validate_value_spec(&fdecl::ConfigValueSpec {
559                value: Some(default.clone()),
560                ..Default::default()
561            })
562            .map_err(|mut e| self.errors.append(&mut e.errs))
563            .ok();
564        }
565
566        let Some(config) = config else {
567            if !optional_use_keys.is_empty() {
568                self.errors.push(Error::missing_field(DeclType::ConfigField, "config"))
569            }
570            return;
571        };
572
573        if let Some(fields) = &config.fields {
574            for field in fields {
575                if field.key.is_none() {
576                    self.errors.push(Error::missing_field(DeclType::ConfigField, "key"));
577                }
578                if let Some(type_) = &field.type_ {
579                    self.validate_config_type(type_, true);
580                } else {
581                    self.errors.push(Error::missing_field(DeclType::ConfigField, "value_type"));
582                }
583            }
584        } else {
585            self.errors.push(Error::missing_field(DeclType::ConfigSchema, "fields"));
586        }
587
588        if let Some(checksum) = &config.checksum {
589            match checksum {
590                fdecl::ConfigChecksum::Sha256(_) => {}
591                fdecl::ConfigChecksumUnknown!() => {
592                    self.errors.push(Error::invalid_field(DeclType::ConfigSchema, "checksum"));
593                }
594            }
595        } else {
596            self.errors.push(Error::missing_field(DeclType::ConfigSchema, "checksum"));
597        }
598
599        'outer: for (key, value) in optional_use_keys.iter() {
600            for field in config.fields.iter().flatten() {
601                if field.key.as_ref() == Some(key) {
602                    if field.type_.as_ref() != Some(value) {
603                        self.errors.push(Error::invalid_field(DeclType::ConfigField, key.clone()));
604                    }
605                    continue 'outer;
606                }
607            }
608            self.errors.push(Error::missing_field(DeclType::ConfigField, key.clone()));
609        }
610
611        match config.value_source {
612            None => self.errors.push(Error::missing_field(DeclType::ConfigSchema, "value_source")),
613            #[cfg(fuchsia_api_level_at_least = "HEAD")]
614            Some(fdecl::ConfigValueSource::Capabilities(_)) => {
615                if !optional_use_keys.is_empty() {
616                    self.errors
617                        .push(Error::invalid_field(DeclType::ConfigValueSource, "ValueSource"))
618                }
619            }
620            Some(fdecl::ConfigValueSource::__SourceBreaking { .. }) => {
621                self.errors.push(Error::invalid_field(DeclType::ConfigValueSource, "ValueSource"))
622            }
623            _ => (),
624        };
625    }
626
627    #[cfg(fuchsia_api_level_at_least = "20")]
628    fn validate_config_type(&mut self, type_: &fdecl::ConfigType, accept_vectors: bool) {
629        match &type_.layout {
630            fdecl::ConfigTypeLayout::Bool
631            | fdecl::ConfigTypeLayout::Uint8
632            | fdecl::ConfigTypeLayout::Uint16
633            | fdecl::ConfigTypeLayout::Uint32
634            | fdecl::ConfigTypeLayout::Uint64
635            | fdecl::ConfigTypeLayout::Int8
636            | fdecl::ConfigTypeLayout::Int16
637            | fdecl::ConfigTypeLayout::Int32
638            | fdecl::ConfigTypeLayout::Int64 => {
639                // These layouts have no parameters or constraints
640                if let Some(parameters) = &type_.parameters {
641                    if !parameters.is_empty() {
642                        self.errors
643                            .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
644                    }
645                } else {
646                    self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"));
647                }
648
649                if !type_.constraints.is_empty() {
650                    self.errors.push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
651                }
652            }
653            fdecl::ConfigTypeLayout::String => {
654                // String has exactly one constraint and no parameter
655                if let Some(parameters) = &type_.parameters {
656                    if !parameters.is_empty() {
657                        self.errors
658                            .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
659                    }
660                } else {
661                    self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"));
662                }
663
664                if type_.constraints.is_empty() {
665                    self.errors.push(Error::missing_field(DeclType::ConfigType, "constraints"));
666                } else if type_.constraints.len() > 1 {
667                    self.errors.push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
668                } else if let fdecl::LayoutConstraint::MaxSize(_) = &type_.constraints[0] {
669                } else {
670                    self.errors.push(Error::invalid_field(DeclType::ConfigType, "constraints"));
671                }
672            }
673            fdecl::ConfigTypeLayout::Vector => {
674                if accept_vectors {
675                    // Vector has exactly one constraint and one parameter
676                    if let Some(parameters) = &type_.parameters {
677                        if parameters.is_empty() {
678                            self.errors
679                                .push(Error::missing_field(DeclType::ConfigType, "parameters"));
680                        } else if parameters.len() > 1 {
681                            self.errors
682                                .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
683                        } else if let fdecl::LayoutParameter::NestedType(nested_type) =
684                            &parameters[0]
685                        {
686                            self.validate_config_type(nested_type, false);
687                        } else {
688                            self.errors
689                                .push(Error::invalid_field(DeclType::ConfigType, "parameters"));
690                        }
691                    } else {
692                        self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"))
693                    }
694
695                    if type_.constraints.is_empty() {
696                        self.errors.push(Error::missing_field(DeclType::ConfigType, "constraints"));
697                    } else if type_.constraints.len() > 1 {
698                        self.errors
699                            .push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
700                    } else if let fdecl::LayoutConstraint::MaxSize(_) = &type_.constraints[0] {
701                    } else {
702                        self.errors.push(Error::invalid_field(DeclType::ConfigType, "constraints"));
703                    }
704                } else {
705                    self.errors.push(Error::nested_vector());
706                }
707            }
708            _ => self.errors.push(Error::invalid_field(DeclType::ConfigType, "layout")),
709        }
710    }
711
712    fn validate_capability_decls(
713        &mut self,
714        capabilities: &'a [fdecl::Capability],
715        as_builtin: bool,
716    ) {
717        for capability in capabilities {
718            self.validate_capability_decl(capability, as_builtin);
719        }
720    }
721
722    /// Validates an individual capability declaration as either a built-in capability or (if
723    /// `as_builtin = false`) as a component or namespace capability.
724    // Storage capabilities are not currently allowed as built-ins, but there's no deep reason for this.
725    // Update this method to allow built-in storage capabilities as needed.
726    fn validate_capability_decl(&mut self, capability: &'a fdecl::Capability, as_builtin: bool) {
727        match capability {
728            fdecl::Capability::Service(service) => self.validate_service_decl(&service, as_builtin),
729            fdecl::Capability::Protocol(protocol) => {
730                self.validate_protocol_decl(&protocol, as_builtin)
731            }
732            fdecl::Capability::Directory(directory) => {
733                self.validate_directory_decl(&directory, as_builtin)
734            }
735            fdecl::Capability::Storage(storage) => {
736                if as_builtin {
737                    self.errors.push(Error::CapabilityCannotBeBuiltin(DeclType::Storage))
738                } else {
739                    self.validate_storage_decl(&storage)
740                }
741            }
742            fdecl::Capability::Runner(runner) => self.validate_runner_decl(&runner, as_builtin),
743            fdecl::Capability::Resolver(resolver) => {
744                self.validate_resolver_decl(&resolver, as_builtin)
745            }
746            fdecl::Capability::EventStream(event) => {
747                if as_builtin {
748                    self.validate_event_stream_decl(&event)
749                } else {
750                    self.errors.push(Error::CapabilityMustBeBuiltin(DeclType::EventStream))
751                }
752            }
753            #[cfg(fuchsia_api_level_at_least = "25")]
754            fdecl::Capability::Dictionary(dictionary) => {
755                self.validate_dictionary_decl(&dictionary);
756            }
757            #[cfg(fuchsia_api_level_at_least = "HEAD")]
758            fdecl::Capability::Config(config) => {
759                self.validate_configuration_decl(&config);
760            }
761            fdecl::CapabilityUnknown!() => self.errors.push(Error::UnknownCapability),
762        }
763    }
764
765    /// Returns the `source_name` and `source` of the runner in `uses`, if present.
766    fn validate_use_decls(
767        &mut self,
768        uses: &'a [fdecl::Use],
769    ) -> (Option<&'a String>, Option<&'a fdecl::Ref>) {
770        // Validate individual fields.
771        for use_ in uses.iter() {
772            self.validate_use_decl(&use_);
773        }
774        self.validate_use_paths(&uses);
775
776        #[cfg(fuchsia_api_level_at_least = "HEAD")]
777        {
778            let mut use_runner_name = None;
779            let mut use_runner_source = None;
780            for use_ in uses.iter() {
781                if let fdecl::Use::Runner(use_runner) = use_ {
782                    if use_runner_name.is_some() {
783                        self.errors.push(Error::MultipleRunnersUsed);
784                    }
785
786                    use_runner_name = use_runner.source_name.as_ref();
787                    use_runner_source = use_runner.source.as_ref();
788                }
789            }
790            return (use_runner_name, use_runner_source);
791        }
792        #[cfg(fuchsia_api_level_less_than = "HEAD")]
793        return (None, None);
794    }
795
796    fn validate_use_decl(&mut self, use_: &'a fdecl::Use) {
797        match use_ {
798            fdecl::Use::Service(u) => {
799                let decl = DeclType::UseService;
800                self.validate_use_fields(
801                    decl,
802                    Self::service_checker,
803                    u.source.as_ref(),
804                    u.source_name.as_ref(),
805                    get_source_dictionary!(u),
806                    u.target_path.as_ref(),
807                    u.dependency_type.as_ref(),
808                    u.availability.as_ref(),
809                );
810                if u.dependency_type.is_none() {
811                    self.errors.push(Error::missing_field(decl, "dependency_type"));
812                }
813            }
814            fdecl::Use::Protocol(u) => {
815                let decl = DeclType::UseProtocol;
816                self.validate_use_fields(
817                    decl,
818                    Self::protocol_checker,
819                    u.source.as_ref(),
820                    u.source_name.as_ref(),
821                    get_source_dictionary!(u),
822                    u.target_path.as_ref(),
823                    u.dependency_type.as_ref(),
824                    u.availability.as_ref(),
825                );
826                if u.dependency_type.is_none() {
827                    self.errors.push(Error::missing_field(decl, "dependency_type"));
828                }
829            }
830            fdecl::Use::Directory(u) => {
831                let decl = DeclType::UseDirectory;
832                self.validate_use_fields(
833                    decl,
834                    Self::directory_checker,
835                    u.source.as_ref(),
836                    u.source_name.as_ref(),
837                    get_source_dictionary!(u),
838                    u.target_path.as_ref(),
839                    u.dependency_type.as_ref(),
840                    u.availability.as_ref(),
841                );
842                if u.dependency_type.is_none() {
843                    self.errors.push(Error::missing_field(decl, "dependency_type"));
844                }
845                if u.rights.is_none() {
846                    self.errors.push(Error::missing_field(DeclType::UseDirectory, "rights"));
847                }
848                if let Some(subdir) = u.subdir.as_ref() {
849                    check_relative_path(
850                        Some(subdir),
851                        DeclType::UseDirectory,
852                        "subdir",
853                        &mut self.errors,
854                    );
855                }
856            }
857            fdecl::Use::Storage(u) => {
858                const SOURCE: Option<fdecl::Ref> = Some(fdecl::Ref::Parent(fdecl::ParentRef {}));
859                const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
860                    Some(fdecl::DependencyType::Strong);
861                self.validate_use_fields(
862                    DeclType::UseStorage,
863                    Self::storage_checker,
864                    SOURCE.as_ref(),
865                    u.source_name.as_ref(),
866                    None,
867                    u.target_path.as_ref(),
868                    DEPENDENCY_TYPE.as_ref(),
869                    u.availability.as_ref(),
870                );
871            }
872            fdecl::Use::EventStream(u) => {
873                const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
874                    Some(fdecl::DependencyType::Strong);
875                let decl = DeclType::UseEventStream;
876                self.validate_use_fields(
877                    decl,
878                    Self::event_stream_checker,
879                    u.source.as_ref(),
880                    u.source_name.as_ref(),
881                    None,
882                    u.target_path.as_ref(),
883                    DEPENDENCY_TYPE.as_ref(),
884                    u.availability.as_ref(),
885                );
886                // Additional validation.
887                match u.source {
888                    Some(fdecl::Ref::Child(_)) | Some(fdecl::Ref::Parent(_)) => {
889                        // Allowed.
890                    }
891                    Some(fdecl::Ref::Framework(_))
892                    | Some(fdecl::Ref::Self_(_))
893                    | Some(fdecl::Ref::Debug(_)) => {
894                        // Allowed in general but not for event streams, add an error.
895                        self.errors.push(Error::invalid_field(decl, "source"));
896                    }
897                    Some(fdecl::Ref::Collection(_)) | Some(fdecl::RefUnknown!()) | None => {
898                        // Already handled by validate_use_fields.
899                    }
900                }
901                if let Some(scope) = &u.scope {
902                    for reference in scope {
903                        if !matches!(reference, fdecl::Ref::Child(_) | fdecl::Ref::Collection(_)) {
904                            self.errors.push(Error::invalid_field(decl, "scope"));
905                        }
906                    }
907                }
908            }
909            #[cfg(fuchsia_api_level_at_least = "HEAD")]
910            fdecl::Use::Runner(u) => {
911                const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
912                    Some(fdecl::DependencyType::Strong);
913                const AVAILABILITY: Option<fdecl::Availability> =
914                    Some(fdecl::Availability::Required);
915                let decl = DeclType::UseRunner;
916                self.validate_use_fields(
917                    decl,
918                    Self::runner_checker,
919                    u.source.as_ref(),
920                    u.source_name.as_ref(),
921                    get_source_dictionary!(u),
922                    None,
923                    DEPENDENCY_TYPE.as_ref(),
924                    AVAILABILITY.as_ref(),
925                );
926            }
927            #[cfg(fuchsia_api_level_at_least = "HEAD")]
928            fdecl::Use::Config(u) => {
929                const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
930                    Some(fdecl::DependencyType::Strong);
931                let decl = DeclType::UseConfiguration;
932                self.validate_use_fields(
933                    decl,
934                    Self::config_checker,
935                    u.source.as_ref(),
936                    u.source_name.as_ref(),
937                    None,
938                    None,
939                    DEPENDENCY_TYPE.as_ref(),
940                    u.availability.as_ref(),
941                );
942            }
943            fdecl::UseUnknown!() => {
944                self.errors.push(Error::invalid_field(DeclType::Component, "use"));
945            }
946        }
947    }
948
949    /// Validates the "program" declaration. This does not check runner-specific properties
950    /// since those are checked by the runner.
951    fn validate_program(
952        &mut self,
953        program: &fdecl::Program,
954        use_runner_name: Option<&String>,
955        _use_runner_source: Option<&fdecl::Ref>,
956    ) {
957        match &program.runner {
958            Some(_) =>
959            {
960                #[cfg(fuchsia_api_level_at_least = "HEAD")]
961                if use_runner_name.is_some() {
962                    if use_runner_name != program.runner.as_ref()
963                        || _use_runner_source
964                            != Some(&fdecl::Ref::Environment(fdecl::EnvironmentRef))
965                    {
966                        self.errors.push(Error::ConflictingRunners);
967                    }
968                }
969            }
970            None => {
971                if use_runner_name.is_none() {
972                    self.errors.push(Error::MissingRunner);
973                }
974            }
975        }
976
977        if program.info.is_none() {
978            self.errors.push(Error::missing_field(DeclType::Program, "info"));
979        }
980    }
981
982    /// Validates that paths-based capabilities (service, directory, protocol)
983    /// are different, are not prefixes of each other, and do not collide "/pkg".
984    fn validate_use_paths(&mut self, uses: &[fdecl::Use]) {
985        #[derive(Debug, PartialEq, Clone, Copy)]
986        struct PathCapability<'a> {
987            decl: DeclType,
988            dir: &'a Path,
989            use_: &'a fdecl::Use,
990        }
991        let mut used_paths = HashMap::new();
992        for use_ in uses.iter() {
993            match use_ {
994                fdecl::Use::Service(fdecl::UseService { target_path: Some(path), .. })
995                | fdecl::Use::Protocol(fdecl::UseProtocol { target_path: Some(path), .. })
996                | fdecl::Use::Directory(fdecl::UseDirectory { target_path: Some(path), .. })
997                | fdecl::Use::Storage(fdecl::UseStorage { target_path: Some(path), .. }) => {
998                    let capability = match use_ {
999                        fdecl::Use::Service(_) => {
1000                            let dir = match Path::new(path).parent() {
1001                                Some(p) => p,
1002                                None => continue, // Invalid path, validated elsewhere
1003                            };
1004                            PathCapability { decl: DeclType::UseService, dir, use_ }
1005                        }
1006                        fdecl::Use::Protocol(_) => {
1007                            let dir = match Path::new(path).parent() {
1008                                Some(p) => p,
1009                                None => continue, // Invalid path, validated elsewhere
1010                            };
1011                            PathCapability { decl: DeclType::UseProtocol, dir, use_ }
1012                        }
1013                        fdecl::Use::Directory(_) => PathCapability {
1014                            decl: DeclType::UseDirectory,
1015                            dir: Path::new(path),
1016                            use_,
1017                        },
1018                        fdecl::Use::Storage(_) => PathCapability {
1019                            decl: DeclType::UseStorage,
1020                            dir: Path::new(path),
1021                            use_,
1022                        },
1023                        _ => unreachable!(),
1024                    };
1025                    if used_paths.insert(path, capability).is_some() {
1026                        // Disallow multiple capabilities for the same path.
1027                        self.errors.push(Error::duplicate_field(
1028                            capability.decl,
1029                            "target_path",
1030                            path,
1031                        ));
1032                    }
1033                }
1034                _ => {}
1035            }
1036        }
1037        for ((&path_a, capability_a), (&path_b, capability_b)) in
1038            used_paths.iter().tuple_combinations()
1039        {
1040            if match (capability_a.use_, capability_b.use_) {
1041                // Directories and storage can't be the same or partially overlap.
1042                (fdecl::Use::Directory(_), fdecl::Use::Directory(_))
1043                | (fdecl::Use::Storage(_), fdecl::Use::Directory(_))
1044                | (fdecl::Use::Directory(_), fdecl::Use::Storage(_))
1045                | (fdecl::Use::Storage(_), fdecl::Use::Storage(_)) => {
1046                    capability_b.dir == capability_a.dir
1047                        || capability_b.dir.starts_with(capability_a.dir)
1048                        || capability_a.dir.starts_with(capability_b.dir)
1049                }
1050
1051                // Protocols and Services can't overlap with Directories.
1052                (_, fdecl::Use::Directory(_)) | (fdecl::Use::Directory(_), _) => {
1053                    capability_b.dir == capability_a.dir
1054                        || capability_b.dir.starts_with(capability_a.dir)
1055                        || capability_a.dir.starts_with(capability_b.dir)
1056                }
1057
1058                // Protocols and Services containing directories may be same, but
1059                // partial overlap is disallowed.
1060                (_, _) => {
1061                    capability_b.dir != capability_a.dir
1062                        && (capability_b.dir.starts_with(capability_a.dir)
1063                            || capability_a.dir.starts_with(capability_b.dir))
1064                }
1065            } {
1066                self.errors.push(Error::invalid_path_overlap(
1067                    capability_a.decl,
1068                    path_a,
1069                    capability_b.decl,
1070                    path_b,
1071                ));
1072            }
1073        }
1074        for (used_path, capability) in used_paths.iter() {
1075            if used_path.as_str() == "/pkg" || used_path.starts_with("/pkg/") {
1076                self.errors.push(Error::pkg_path_overlap(capability.decl, *used_path));
1077            }
1078        }
1079    }
1080
1081    fn validate_use_fields(
1082        &mut self,
1083        decl: DeclType,
1084        // This takes a callback that returns a [Container], instead of the &[Container] directly,
1085        // to avoid a borrow checker error that would occur from a simultaneous borrow on
1086        // &mut self.
1087        capability_checker: impl Fn(&Self) -> &dyn Container,
1088        source: Option<&'a fdecl::Ref>,
1089        source_name: Option<&'a String>,
1090        source_dictionary: Option<&'a String>,
1091        target_path: Option<&'a String>,
1092        dependency_type: Option<&fdecl::DependencyType>,
1093        availability: Option<&'a fdecl::Availability>,
1094    ) {
1095        self.validate_use_source(decl, source, source_dictionary);
1096
1097        check_name(source_name, decl, "source_name", &mut self.errors);
1098        if source_dictionary.is_some() {
1099            check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
1100        }
1101        if decl != DeclType::UseRunner && decl != DeclType::UseConfiguration {
1102            check_path(target_path, decl, "target_path", &mut self.errors);
1103        }
1104        check_use_availability(decl, availability, &mut self.errors);
1105
1106        // Only allow `weak` dependency with `use from child`.
1107        let is_use_from_child = match source {
1108            Some(fdecl::Ref::Child(_)) => true,
1109            _ => false,
1110        };
1111        match (is_use_from_child, dependency_type) {
1112            (false, Some(fdecl::DependencyType::Weak)) => {
1113                self.errors.push(Error::invalid_field(decl, "dependency_type"));
1114            }
1115            _ => {}
1116        }
1117
1118        self.validate_route_from_self(
1119            decl,
1120            source,
1121            source_name,
1122            source_dictionary,
1123            capability_checker,
1124        );
1125    }
1126
1127    fn validate_use_source(
1128        &mut self,
1129        decl: DeclType,
1130        source: Option<&'a fdecl::Ref>,
1131        source_dictionary: Option<&'a String>,
1132    ) {
1133        match (source, source_dictionary) {
1134            // These sources support source_dictionary.
1135            (Some(fdecl::Ref::Parent(_)), _) => {}
1136            (Some(fdecl::Ref::Self_(_)), _) => {}
1137            (Some(fdecl::Ref::Child(child)), _) => {
1138                self.validate_child_ref(decl, "source", &child, OfferType::Static);
1139                return;
1140            }
1141            // These sources don't.
1142            (Some(fdecl::Ref::Framework(_)), None) => {}
1143            (Some(fdecl::Ref::Debug(_)), None) => {}
1144            (Some(fdecl::Ref::Capability(c)), None) => {
1145                self.validate_source_capability(&c, decl, "source");
1146                return;
1147            }
1148            #[cfg(fuchsia_api_level_at_least = "HEAD")]
1149            (Some(fdecl::Ref::Environment(_)), None) => {}
1150            (Some(fdecl::Ref::Collection(collection)), None) if decl == DeclType::UseService => {
1151                self.validate_collection_ref(decl, "source", &collection);
1152                return;
1153            }
1154            // `source` is required.
1155            (None, _) => self.errors.push(Error::missing_field(decl, "source")),
1156            // Any combination that was not recognized above must be invalid.
1157            (_, _) => self.errors.push(Error::invalid_field(decl, "source")),
1158        }
1159    }
1160
1161    fn validate_child_decl(&mut self, child: &'a fdecl::Child) {
1162        if let Err(mut e) = validate_child(child, check_name) {
1163            self.errors.append(&mut e.errs);
1164        }
1165        if let Some(name) = child.name.as_ref() {
1166            let name: &str = name;
1167            if self.all_children.insert(name, child).is_some() {
1168                self.errors.push(Error::duplicate_field(DeclType::Child, "name", name));
1169            }
1170            if let Some(env) = child.environment.as_ref() {
1171                let source = DependencyNode::Environment(env.as_str());
1172                let target = DependencyNode::Child(name, None);
1173                self.add_strong_dep(Some(source), Some(target));
1174            }
1175        }
1176        if let Some(environment) = child.environment.as_ref() {
1177            if !self.all_environment_names.contains(environment.as_str()) {
1178                self.errors.push(Error::invalid_environment(
1179                    DeclType::Child,
1180                    "environment",
1181                    environment,
1182                ));
1183            }
1184        }
1185    }
1186
1187    fn validate_collection_decl(&mut self, collection: &'a fdecl::Collection) {
1188        let name = collection.name.as_ref();
1189        if check_name(name, DeclType::Collection, "name", &mut self.errors) {
1190            let name: &str = name.unwrap();
1191            if !self.all_collections.insert(name) {
1192                self.errors.push(Error::duplicate_field(DeclType::Collection, "name", name));
1193            }
1194        }
1195        if collection.durability.is_none() {
1196            self.errors.push(Error::missing_field(DeclType::Collection, "durability"));
1197        }
1198        if let Some(environment) = collection.environment.as_ref() {
1199            if !self.all_environment_names.contains(environment.as_str()) {
1200                self.errors.push(Error::invalid_environment(
1201                    DeclType::Collection,
1202                    "environment",
1203                    environment,
1204                ));
1205            }
1206            if let Some(name) = collection.name.as_ref() {
1207                let source = DependencyNode::Environment(environment.as_str());
1208                let target = DependencyNode::Collection(name.as_str());
1209                self.add_strong_dep(Some(source), Some(target));
1210            }
1211        }
1212        // Allow `allowed_offers` & `allow_long_names` to be unset/unvalidated, for backwards compatibility.
1213    }
1214
1215    fn validate_environment_decl(&mut self, environment: &'a fdecl::Environment) {
1216        let name = environment.name.as_ref();
1217        check_name(name, DeclType::Environment, "name", &mut self.errors);
1218        if environment.extends.is_none() {
1219            self.errors.push(Error::missing_field(DeclType::Environment, "extends"));
1220        }
1221        if let Some(runners) = environment.runners.as_ref() {
1222            let mut registered_runners = HashSet::new();
1223            for runner in runners {
1224                self.validate_runner_registration(runner, name.clone(), &mut registered_runners);
1225            }
1226        }
1227        if let Some(resolvers) = environment.resolvers.as_ref() {
1228            let mut registered_schemes = HashSet::new();
1229            for resolver in resolvers {
1230                self.validate_resolver_registration(
1231                    resolver,
1232                    name.clone(),
1233                    &mut registered_schemes,
1234                );
1235            }
1236        }
1237
1238        match environment.extends.as_ref() {
1239            Some(fdecl::EnvironmentExtends::None) => {
1240                if environment.stop_timeout_ms.is_none() {
1241                    self.errors
1242                        .push(Error::missing_field(DeclType::Environment, "stop_timeout_ms"));
1243                }
1244            }
1245            None | Some(fdecl::EnvironmentExtends::Realm) => {}
1246        }
1247
1248        if let Some(debugs) = environment.debug_capabilities.as_ref() {
1249            for debug in debugs {
1250                self.validate_environment_debug_registration(debug, name.clone());
1251            }
1252        }
1253    }
1254
1255    fn validate_runner_registration(
1256        &mut self,
1257        runner_registration: &'a fdecl::RunnerRegistration,
1258        environment_name: Option<&'a String>,
1259        runner_names: &mut HashSet<&'a str>,
1260    ) {
1261        check_name(
1262            runner_registration.source_name.as_ref(),
1263            DeclType::RunnerRegistration,
1264            "source_name",
1265            &mut self.errors,
1266        );
1267        self.validate_registration_source(
1268            environment_name,
1269            runner_registration.source.as_ref(),
1270            DeclType::RunnerRegistration,
1271        );
1272        // If the source is `self`, ensure we have a corresponding Runner.
1273        if let (Some(fdecl::Ref::Self_(_)), Some(ref name)) =
1274            (&runner_registration.source, &runner_registration.source_name)
1275        {
1276            if !self.all_runners.contains(name as &str) {
1277                self.errors.push(Error::invalid_runner(
1278                    DeclType::RunnerRegistration,
1279                    "source_name",
1280                    name,
1281                ));
1282            }
1283        }
1284
1285        check_name(
1286            runner_registration.target_name.as_ref(),
1287            DeclType::RunnerRegistration,
1288            "target_name",
1289            &mut self.errors,
1290        );
1291        if let Some(name) = runner_registration.target_name.as_ref() {
1292            if !runner_names.insert(name.as_str()) {
1293                self.errors.push(Error::duplicate_field(
1294                    DeclType::RunnerRegistration,
1295                    "target_name",
1296                    name,
1297                ));
1298            }
1299        }
1300    }
1301
1302    fn validate_resolver_registration(
1303        &mut self,
1304        resolver_registration: &'a fdecl::ResolverRegistration,
1305        environment_name: Option<&'a String>,
1306        schemes: &mut HashSet<&'a str>,
1307    ) {
1308        check_name(
1309            resolver_registration.resolver.as_ref(),
1310            DeclType::ResolverRegistration,
1311            "resolver",
1312            &mut self.errors,
1313        );
1314        self.validate_registration_source(
1315            environment_name,
1316            resolver_registration.source.as_ref(),
1317            DeclType::ResolverRegistration,
1318        );
1319        check_url_scheme(
1320            resolver_registration.scheme.as_ref(),
1321            DeclType::ResolverRegistration,
1322            "scheme",
1323            &mut self.errors,
1324        );
1325        if let Some(scheme) = resolver_registration.scheme.as_ref() {
1326            if !schemes.insert(scheme.as_str()) {
1327                self.errors.push(Error::duplicate_field(
1328                    DeclType::ResolverRegistration,
1329                    "scheme",
1330                    scheme,
1331                ));
1332            }
1333        }
1334    }
1335
1336    fn validate_registration_source(
1337        &mut self,
1338        environment_name: Option<&'a String>,
1339        source: Option<&'a fdecl::Ref>,
1340        ty: DeclType,
1341    ) {
1342        match source {
1343            Some(fdecl::Ref::Parent(_)) => {}
1344            Some(fdecl::Ref::Self_(_)) => {}
1345            Some(fdecl::Ref::Child(child_ref)) => {
1346                // Make sure the child is valid.
1347                self.validate_child_ref(ty, "source", &child_ref, OfferType::Static);
1348            }
1349            Some(_) => {
1350                self.errors.push(Error::invalid_field(ty, "source"));
1351            }
1352            None => {
1353                self.errors.push(Error::missing_field(ty, "source"));
1354            }
1355        }
1356
1357        let source = self.source_dependency_from_ref(None, None, source);
1358        if let Some(source) = source {
1359            if let Some(env_name) = &environment_name {
1360                let target = DependencyNode::Environment(env_name);
1361                self.strong_dependencies.add_edge(source, target);
1362            }
1363        }
1364    }
1365
1366    fn validate_service_decl(&mut self, service: &'a fdecl::Service, as_builtin: bool) {
1367        if check_name(service.name.as_ref(), DeclType::Service, "name", &mut self.errors) {
1368            let name = service.name.as_ref().unwrap();
1369            if !self.all_capability_ids.insert(name) {
1370                self.errors.push(Error::duplicate_field(DeclType::Service, "name", name.as_str()));
1371            }
1372            self.all_services.insert(name);
1373        }
1374        match as_builtin {
1375            true => {
1376                if let Some(path) = service.source_path.as_ref() {
1377                    self.errors.push(Error::extraneous_source_path(DeclType::Service, path))
1378                }
1379            }
1380            false => {
1381                check_path(
1382                    service.source_path.as_ref(),
1383                    DeclType::Service,
1384                    "source_path",
1385                    &mut self.errors,
1386                );
1387            }
1388        }
1389    }
1390
1391    fn validate_protocol_decl(&mut self, protocol: &'a fdecl::Protocol, as_builtin: bool) {
1392        if check_name(protocol.name.as_ref(), DeclType::Protocol, "name", &mut self.errors) {
1393            let name = protocol.name.as_ref().unwrap();
1394            if !self.all_capability_ids.insert(name) {
1395                self.errors.push(Error::duplicate_field(DeclType::Protocol, "name", name.as_str()));
1396            }
1397            self.all_protocols.insert(name);
1398        }
1399        match as_builtin {
1400            true => {
1401                if let Some(path) = protocol.source_path.as_ref() {
1402                    self.errors.push(Error::extraneous_source_path(DeclType::Protocol, path))
1403                }
1404            }
1405            false => {
1406                check_path(
1407                    protocol.source_path.as_ref(),
1408                    DeclType::Protocol,
1409                    "source_path",
1410                    &mut self.errors,
1411                );
1412            }
1413        }
1414
1415        #[cfg(fuchsia_api_level_at_least = "HEAD")]
1416        match protocol.delivery {
1417            Some(delivery) => match cm_types::DeliveryType::try_from(delivery) {
1418                Ok(_) => {}
1419                Err(_) => self.errors.push(Error::invalid_field(DeclType::Protocol, "delivery")),
1420            },
1421            None => {}
1422        }
1423    }
1424
1425    fn validate_directory_decl(&mut self, directory: &'a fdecl::Directory, as_builtin: bool) {
1426        if check_name(directory.name.as_ref(), DeclType::Directory, "name", &mut self.errors) {
1427            let name = directory.name.as_ref().unwrap();
1428            if !self.all_capability_ids.insert(name) {
1429                self.errors.push(Error::duplicate_field(
1430                    DeclType::Directory,
1431                    "name",
1432                    name.as_str(),
1433                ));
1434            }
1435            self.all_directories.insert(name);
1436        }
1437        match as_builtin {
1438            true => {
1439                if let Some(path) = directory.source_path.as_ref() {
1440                    self.errors.push(Error::extraneous_source_path(DeclType::Directory, path))
1441                }
1442            }
1443            false => {
1444                check_path(
1445                    directory.source_path.as_ref(),
1446                    DeclType::Directory,
1447                    "source_path",
1448                    &mut self.errors,
1449                );
1450            }
1451        }
1452        if directory.rights.is_none() {
1453            self.errors.push(Error::missing_field(DeclType::Directory, "rights"));
1454        }
1455    }
1456
1457    fn validate_storage_decl(&mut self, storage: &'a fdecl::Storage) {
1458        match storage.source.as_ref() {
1459            Some(fdecl::Ref::Parent(_)) => {}
1460            Some(fdecl::Ref::Self_(_)) => {}
1461            Some(fdecl::Ref::Child(child)) => {
1462                let _ =
1463                    self.validate_child_ref(DeclType::Storage, "source", &child, OfferType::Static);
1464            }
1465            Some(_) => {
1466                self.errors.push(Error::invalid_field(DeclType::Storage, "source"));
1467            }
1468            None => {
1469                self.errors.push(Error::missing_field(DeclType::Storage, "source"));
1470            }
1471        };
1472        if check_name(storage.name.as_ref(), DeclType::Storage, "name", &mut self.errors) {
1473            let name = storage.name.as_ref().unwrap();
1474            if !self.all_capability_ids.insert(name) {
1475                self.errors.push(Error::duplicate_field(DeclType::Storage, "name", name.as_str()));
1476            }
1477            self.all_storages.insert(name, storage.source.as_ref());
1478        }
1479        if storage.storage_id.is_none() {
1480            self.errors.push(Error::missing_field(DeclType::Storage, "storage_id"));
1481        }
1482        check_name(
1483            storage.backing_dir.as_ref(),
1484            DeclType::Storage,
1485            "backing_dir",
1486            &mut self.errors,
1487        );
1488
1489        // The storage capability depends on its backing dir.
1490        if let (Some(name), Some(backing_dir), Some(source)) =
1491            (storage.name.as_ref(), storage.backing_dir.as_ref(), storage.source.as_ref())
1492        {
1493            let source = self.source_dependency_from_ref(Some(backing_dir), None, Some(source));
1494            let target = Some(DependencyNode::Capability(name));
1495            self.add_strong_dep(source, target);
1496        }
1497    }
1498
1499    fn validate_runner_decl(&mut self, runner: &'a fdecl::Runner, as_builtin: bool) {
1500        if check_name(runner.name.as_ref(), DeclType::Runner, "name", &mut self.errors) {
1501            let name = runner.name.as_ref().unwrap();
1502            if !self.all_capability_ids.insert(name) {
1503                self.errors.push(Error::duplicate_field(DeclType::Runner, "name", name.as_str()));
1504            }
1505            self.all_runners.insert(name);
1506        }
1507        match as_builtin {
1508            true => {
1509                if let Some(path) = runner.source_path.as_ref() {
1510                    self.errors.push(Error::extraneous_source_path(DeclType::Runner, path))
1511                }
1512            }
1513            false => {
1514                check_path(
1515                    runner.source_path.as_ref(),
1516                    DeclType::Runner,
1517                    "source_path",
1518                    &mut self.errors,
1519                );
1520            }
1521        }
1522    }
1523
1524    fn validate_resolver_decl(&mut self, resolver: &'a fdecl::Resolver, as_builtin: bool) {
1525        if check_name(resolver.name.as_ref(), DeclType::Resolver, "name", &mut self.errors) {
1526            let name = resolver.name.as_ref().unwrap();
1527            if !self.all_capability_ids.insert(name) {
1528                self.errors.push(Error::duplicate_field(DeclType::Resolver, "name", name.as_str()));
1529            }
1530            self.all_resolvers.insert(name);
1531        }
1532        match as_builtin {
1533            true => {
1534                if let Some(path) = resolver.source_path.as_ref() {
1535                    self.errors.push(Error::extraneous_source_path(DeclType::Resolver, path))
1536                }
1537            }
1538            false => {
1539                check_path(
1540                    resolver.source_path.as_ref(),
1541                    DeclType::Resolver,
1542                    "source_path",
1543                    &mut self.errors,
1544                );
1545            }
1546        }
1547    }
1548
1549    // Dictionaries can reference other dictionaries in the same manifest, so before processing any
1550    // dictionary declarations this function should be called to do a first pass to pre-populate
1551    // the dictionary map.
1552    #[cfg(fuchsia_api_level_at_least = "25")]
1553    fn load_dictionary_names(&mut self, dictionaries: impl Iterator<Item = &'a fdecl::Dictionary>) {
1554        for dictionary in dictionaries {
1555            let decl = DeclType::Dictionary;
1556            if check_name(dictionary.name.as_ref(), decl, "name", &mut self.errors) {
1557                let name = dictionary.name.as_ref().unwrap();
1558                if !self.all_capability_ids.insert(name) {
1559                    self.errors.push(Error::duplicate_field(decl, "name", name.as_str()));
1560                }
1561                self.all_dictionaries.insert(name, &dictionary);
1562            }
1563        }
1564    }
1565
1566    #[cfg(fuchsia_api_level_at_least = "25")]
1567    fn validate_dictionary_decl(&mut self, dictionary: &'a fdecl::Dictionary) {
1568        let decl = DeclType::Dictionary;
1569        if let Some(path) = dictionary.source_path.as_ref() {
1570            if dictionary.source.is_some() {
1571                self.errors.push(Error::extraneous_field(decl, "source"));
1572            }
1573            check_path(Some(path), DeclType::Dictionary, "source_path", &mut self.errors);
1574            // If `source_path` is set that means the dictionary is provided by the program,
1575            // which implies a dependency from `self` to the dictionary declaration.
1576            if let Some(name) = dictionary.name.as_ref() {
1577                self.add_strong_dep(
1578                    Some(DependencyNode::Self_),
1579                    Some(DependencyNode::Capability(name)),
1580                );
1581            }
1582        }
1583    }
1584
1585    #[cfg(fuchsia_api_level_at_least = "HEAD")]
1586    fn validate_configuration_decl(&mut self, config: &'a fdecl::Configuration) {
1587        let decl = DeclType::Configuration;
1588        if check_name(config.name.as_ref(), decl, "name", &mut self.errors) {
1589            let name = config.name.as_ref().unwrap();
1590            if !self.all_capability_ids.insert(name) {
1591                self.errors.push(Error::duplicate_field(decl, "name", name.as_str()));
1592            }
1593            self.all_configs.insert(name);
1594        }
1595    }
1596
1597    fn validate_environment_debug_registration(
1598        &mut self,
1599        debug: &'a fdecl::DebugRegistration,
1600        environment_name: Option<&'a String>,
1601    ) {
1602        match debug {
1603            fdecl::DebugRegistration::Protocol(o) => {
1604                let decl = DeclType::DebugProtocolRegistration;
1605                self.validate_environment_debug_fields(
1606                    decl,
1607                    o.source.as_ref(),
1608                    o.source_name.as_ref(),
1609                    o.target_name.as_ref(),
1610                );
1611
1612                if let (Some(fdecl::Ref::Self_(_)), Some(ref name)) = (&o.source, &o.source_name) {
1613                    if !self.all_protocols.contains(&name as &str) {
1614                        self.errors.push(Error::invalid_field(decl, "source"));
1615                    }
1616                }
1617
1618                if let Some(env_name) = &environment_name {
1619                    let source = self.source_dependency_from_ref(
1620                        o.source_name.as_ref(),
1621                        None,
1622                        o.source.as_ref(),
1623                    );
1624                    let target = Some(DependencyNode::Environment(env_name));
1625                    self.add_strong_dep(source, target);
1626                }
1627            }
1628            _ => {
1629                self.errors.push(Error::invalid_field(DeclType::Environment, "debug"));
1630            }
1631        }
1632    }
1633
1634    fn validate_environment_debug_fields(
1635        &mut self,
1636        decl: DeclType,
1637        source: Option<&fdecl::Ref>,
1638        source_name: Option<&String>,
1639        target_name: Option<&'a String>,
1640    ) {
1641        // We don't support "source" from "capability" for now.
1642        match source {
1643            Some(fdecl::Ref::Parent(_)) => {}
1644            Some(fdecl::Ref::Self_(_)) => {}
1645            Some(fdecl::Ref::Framework(_)) => {}
1646            Some(fdecl::Ref::Child(child)) => {
1647                let _ = self.validate_child_ref(decl, "source", &child, OfferType::Static);
1648            }
1649            Some(_) => self.errors.push(Error::invalid_field(decl, "source")),
1650            None => self.errors.push(Error::missing_field(decl, "source")),
1651        }
1652        check_name(source_name, decl, "source_name", &mut self.errors);
1653        check_name(target_name, decl, "target_name", &mut self.errors);
1654    }
1655
1656    fn validate_event_stream_decl(&mut self, event: &'a fdecl::EventStream) {
1657        if check_name(event.name.as_ref(), DeclType::EventStream, "name", &mut self.errors) {
1658            let name = event.name.as_ref().unwrap();
1659            if !self.all_capability_ids.insert(name) {
1660                self.errors.push(Error::duplicate_field(
1661                    DeclType::EventStream,
1662                    "name",
1663                    name.as_str(),
1664                ));
1665            }
1666        }
1667    }
1668
1669    fn validate_source_collection(
1670        &mut self,
1671        collection: &fdecl::CollectionRef,
1672        decl_type: DeclType,
1673    ) -> bool {
1674        let num_errors = self.errors.len();
1675        if check_name(Some(&collection.name), decl_type, "source.collection.name", &mut self.errors)
1676            && !self.all_collections.contains(&collection.name as &str)
1677        {
1678            self.errors.push(Error::invalid_collection(
1679                decl_type,
1680                "source",
1681                &collection.name as &str,
1682            ));
1683        }
1684        num_errors == self.errors.len()
1685    }
1686
1687    fn validate_filtered_service_fields(
1688        &mut self,
1689        decl_type: DeclType,
1690        source_instance_filter: Option<&Vec<String>>,
1691        renamed_instances: Option<&Vec<fdecl::NameMapping>>,
1692    ) {
1693        if let Some(source_instance_filter) = source_instance_filter {
1694            if source_instance_filter.is_empty() {
1695                // if the  source_instance_filter is empty the offered service will have 0 instances,
1696                // which means the offer shouldn't have been created at all.
1697                self.errors.push(Error::invalid_field(decl_type, "source_instance_filter"));
1698            }
1699            for name in source_instance_filter {
1700                check_name(Some(name), decl_type, "source_instance_filter", &mut self.errors);
1701            }
1702        }
1703        if let Some(renamed_instances) = renamed_instances {
1704            // Multiple sources shouldn't map to the same target name
1705            let mut seen_target_names = HashSet::<String>::new();
1706            for mapping in renamed_instances {
1707                check_name(
1708                    Some(&mapping.source_name),
1709                    decl_type,
1710                    "renamed_instances.source_name",
1711                    &mut self.errors,
1712                );
1713                check_name(
1714                    Some(&mapping.target_name),
1715                    decl_type,
1716                    "renamed_instances.target_name",
1717                    &mut self.errors,
1718                );
1719                if !seen_target_names.insert(mapping.target_name.clone()) {
1720                    self.errors.push(Error::invalid_field(decl_type, "renamed_instances"));
1721                    break;
1722                }
1723            }
1724        }
1725    }
1726
1727    fn validate_source_capability(
1728        &mut self,
1729        capability: &fdecl::CapabilityRef,
1730        decl_type: DeclType,
1731        field: &str,
1732    ) -> bool {
1733        let num_errors = self.errors.len();
1734        if check_name(Some(&capability.name), decl_type, "source.capability.name", &mut self.errors)
1735            && !self.all_capability_ids.contains(capability.name.as_str())
1736        {
1737            self.errors.push(Error::invalid_capability(decl_type, field, &capability.name));
1738        }
1739        num_errors == self.errors.len()
1740    }
1741
1742    /// Return a key that can be used in `HashMap` to group aggregate declarations.
1743    ///
1744    /// Returns `None` if the input resembles an invalid declaration.
1745    fn make_group_key(
1746        target_name: Option<&'a String>,
1747        target: Option<&'a fdecl::Ref>,
1748    ) -> Option<(&'a str, RefKey<'a>)> {
1749        if target_name.is_none() {
1750            return None;
1751        }
1752        let target_name = target_name.unwrap().as_str();
1753        if target.is_none() {
1754            return None;
1755        }
1756        let target = match target.unwrap() {
1757            fdecl::Ref::Parent(_) => RefKey::Parent,
1758            fdecl::Ref::Self_(_) => RefKey::Self_,
1759            fdecl::Ref::Child(r) => RefKey::Child(r.name.as_str()),
1760            fdecl::Ref::Collection(r) => RefKey::Collection(r.name.as_str()),
1761            fdecl::Ref::Framework(_) => RefKey::Framework,
1762            fdecl::Ref::Capability(_) => RefKey::Capability,
1763            fdecl::Ref::Debug(_) => RefKey::Debug,
1764            fdecl::RefUnknown!() => {
1765                return None;
1766            }
1767        };
1768        Some((target_name, target))
1769    }
1770
1771    fn validate_aggregation_has_same_availability(
1772        &mut self,
1773        route_group: &Vec<impl HasAvailability>,
1774    ) {
1775        // Use `BtreeSet` for stable ordering of items in error message.
1776        let availability_of_sources: BTreeSet<_> =
1777            route_group.iter().map(|r| r.availability()).collect();
1778
1779        // All sources that feed into an aggregation operation should have the same availability.
1780        if availability_of_sources.len() > 1 {
1781            self.errors.push(Error::different_availability_in_aggregation(
1782                availability_of_sources.into_iter().collect(),
1783            ));
1784        }
1785    }
1786
1787    // Checks a group of expose decls to confirm that any duplicate exposes are
1788    // valid aggregate expose declarations.
1789    fn validate_expose_group(&mut self, exposes: &'a Vec<fdecl::Expose>) {
1790        let mut expose_groups: HashMap<_, Vec<fdecl::ExposeService>> = HashMap::new();
1791        let service_exposes = exposes.into_iter().filter_map(|o| {
1792            if let fdecl::Expose::Service(s) = o {
1793                Some(s)
1794            } else {
1795                None
1796            }
1797        });
1798        for expose in service_exposes {
1799            let key = Self::make_group_key(expose.target_name.as_ref(), expose.target.as_ref());
1800            if let Some(key) = key {
1801                expose_groups.entry(key).or_insert_with(|| vec![]).push(expose.clone());
1802            }
1803        }
1804        for expose_group in expose_groups.into_values() {
1805            if expose_group.len() == 1 {
1806                // If there are not multiple exposes for a (target_name, target) pair then there are
1807                // no aggregation conditions to check.
1808                continue;
1809            }
1810
1811            self.validate_aggregation_has_same_availability(&expose_group);
1812        }
1813    }
1814
1815    fn validate_expose_decl(
1816        &mut self,
1817        expose: &'a fdecl::Expose,
1818        expose_to_parent_ids: &mut HashMap<&'a str, AllowableIds>,
1819        expose_to_framework_ids: &mut HashMap<&'a str, AllowableIds>,
1820    ) {
1821        match expose {
1822            fdecl::Expose::Service(e) => {
1823                let decl = DeclType::ExposeService;
1824                self.validate_expose_fields(
1825                    decl,
1826                    AllowableIds::Many,
1827                    CollectionSource::Allow,
1828                    Self::service_checker,
1829                    e.source.as_ref(),
1830                    e.source_name.as_ref(),
1831                    get_source_dictionary!(e),
1832                    e.target.as_ref(),
1833                    e.target_name.as_ref(),
1834                    e.availability.as_ref(),
1835                    expose_to_parent_ids,
1836                    expose_to_framework_ids,
1837                );
1838            }
1839            fdecl::Expose::Protocol(e) => {
1840                let decl = DeclType::ExposeProtocol;
1841                self.validate_expose_fields(
1842                    decl,
1843                    AllowableIds::One,
1844                    CollectionSource::Deny,
1845                    Self::protocol_checker,
1846                    e.source.as_ref(),
1847                    e.source_name.as_ref(),
1848                    get_source_dictionary!(e),
1849                    e.target.as_ref(),
1850                    e.target_name.as_ref(),
1851                    e.availability.as_ref(),
1852                    expose_to_parent_ids,
1853                    expose_to_framework_ids,
1854                );
1855            }
1856            fdecl::Expose::Directory(e) => {
1857                let decl = DeclType::ExposeDirectory;
1858                self.validate_expose_fields(
1859                    decl,
1860                    AllowableIds::One,
1861                    CollectionSource::Deny,
1862                    Self::directory_checker,
1863                    e.source.as_ref(),
1864                    e.source_name.as_ref(),
1865                    get_source_dictionary!(e),
1866                    e.target.as_ref(),
1867                    e.target_name.as_ref(),
1868                    e.availability.as_ref(),
1869                    expose_to_parent_ids,
1870                    expose_to_framework_ids,
1871                );
1872
1873                // Subdir makes sense when routing, but when exposing to framework the subdirectory
1874                // can be exposed directly.
1875                match e.target.as_ref() {
1876                    Some(fdecl::Ref::Framework(_)) => {
1877                        if e.subdir.is_some() {
1878                            self.errors.push(Error::invalid_field(decl, "subdir"));
1879                        }
1880                    }
1881                    _ => {}
1882                }
1883
1884                if let Some(subdir) = e.subdir.as_ref() {
1885                    check_relative_path(Some(subdir), decl, "subdir", &mut self.errors);
1886                }
1887            }
1888            fdecl::Expose::Runner(e) => {
1889                let decl = DeclType::ExposeRunner;
1890                self.validate_expose_fields(
1891                    decl,
1892                    AllowableIds::One,
1893                    CollectionSource::Deny,
1894                    Self::runner_checker,
1895                    e.source.as_ref(),
1896                    e.source_name.as_ref(),
1897                    get_source_dictionary!(e),
1898                    e.target.as_ref(),
1899                    e.target_name.as_ref(),
1900                    Some(&fdecl::Availability::Required),
1901                    expose_to_parent_ids,
1902                    expose_to_framework_ids,
1903                );
1904            }
1905            fdecl::Expose::Resolver(e) => {
1906                let decl = DeclType::ExposeResolver;
1907                self.validate_expose_fields(
1908                    decl,
1909                    AllowableIds::One,
1910                    CollectionSource::Deny,
1911                    Self::resolver_checker,
1912                    e.source.as_ref(),
1913                    e.source_name.as_ref(),
1914                    get_source_dictionary!(e),
1915                    e.target.as_ref(),
1916                    e.target_name.as_ref(),
1917                    Some(&fdecl::Availability::Required),
1918                    expose_to_parent_ids,
1919                    expose_to_framework_ids,
1920                );
1921            }
1922            #[cfg(fuchsia_api_level_at_least = "25")]
1923            fdecl::Expose::Dictionary(e) => {
1924                let decl = DeclType::ExposeDictionary;
1925                self.validate_expose_fields(
1926                    decl,
1927                    AllowableIds::One,
1928                    CollectionSource::Deny,
1929                    Self::dictionary_checker,
1930                    e.source.as_ref(),
1931                    e.source_name.as_ref(),
1932                    get_source_dictionary!(e),
1933                    e.target.as_ref(),
1934                    e.target_name.as_ref(),
1935                    Some(&fdecl::Availability::Required),
1936                    expose_to_parent_ids,
1937                    expose_to_framework_ids,
1938                );
1939            }
1940            #[cfg(fuchsia_api_level_at_least = "HEAD")]
1941            fdecl::Expose::Config(e) => {
1942                let decl = DeclType::ExposeConfig;
1943                self.validate_expose_fields(
1944                    decl,
1945                    AllowableIds::One,
1946                    CollectionSource::Deny,
1947                    Self::config_checker,
1948                    e.source.as_ref(),
1949                    e.source_name.as_ref(),
1950                    None,
1951                    e.target.as_ref(),
1952                    e.target_name.as_ref(),
1953                    e.availability.as_ref(),
1954                    expose_to_parent_ids,
1955                    expose_to_framework_ids,
1956                );
1957            }
1958            _ => {
1959                self.errors.push(Error::invalid_field(DeclType::Component, "expose"));
1960            }
1961        }
1962    }
1963
1964    fn validate_expose_fields(
1965        &mut self,
1966        decl: DeclType,
1967        allowable_ids: AllowableIds,
1968        collection_source: CollectionSource,
1969        // This takes a callback that returns a [Container], instead of the &[Container] directly,
1970        // to avoid a borrow checker error that would occur from a simultaneous borrow on
1971        // &mut self.
1972        capability_checker: impl Fn(&Self) -> &dyn Container,
1973        source: Option<&fdecl::Ref>,
1974        source_name: Option<&String>,
1975        source_dictionary: Option<&String>,
1976        target: Option<&fdecl::Ref>,
1977        target_name: Option<&'a String>,
1978        availability: Option<&fdecl::Availability>,
1979        expose_to_parent_ids: &mut HashMap<&'a str, AllowableIds>,
1980        expose_to_framework_ids: &mut HashMap<&'a str, AllowableIds>,
1981    ) {
1982        self.validate_expose_source(decl, collection_source, source, source_dictionary);
1983        check_route_availability(decl, availability, source, source_name, &mut self.errors);
1984        match target {
1985            Some(r) => match r {
1986                fdecl::Ref::Parent(_) => {}
1987                fdecl::Ref::Framework(_) => {}
1988                _ => {
1989                    self.errors.push(Error::invalid_field(decl, "target"));
1990                }
1991            },
1992            None => {
1993                self.errors.push(Error::missing_field(decl, "target"));
1994            }
1995        }
1996        check_name(source_name, decl, "source_name", &mut self.errors);
1997        if source_dictionary.is_some() {
1998            check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
1999        }
2000        if check_name(target_name, decl, "target_name", &mut self.errors) {
2001            let maybe_ids_set = match target {
2002                Some(fdecl::Ref::Parent(_)) => Some(expose_to_parent_ids),
2003                Some(fdecl::Ref::Framework(_)) => Some(expose_to_framework_ids),
2004                _ => None,
2005            };
2006            if let Some(ids_set) = maybe_ids_set {
2007                let target_name = target_name.unwrap();
2008                if let Some(prev_state) = ids_set.insert(target_name, allowable_ids) {
2009                    if prev_state == AllowableIds::One || prev_state != allowable_ids {
2010                        self.errors.push(Error::duplicate_field(decl, "target_name", target_name));
2011                    }
2012                }
2013            }
2014        }
2015
2016        self.validate_route_from_self(
2017            decl,
2018            source,
2019            source_name,
2020            source_dictionary,
2021            capability_checker,
2022        );
2023    }
2024
2025    fn validate_expose_source(
2026        &mut self,
2027        decl: DeclType,
2028        collection_source: CollectionSource,
2029        source: Option<&fdecl::Ref>,
2030        source_dictionary: Option<&String>,
2031    ) {
2032        match (source, source_dictionary) {
2033            // These sources support source_dictionary.
2034            (Some(fdecl::Ref::Self_(_)), _) => {}
2035            (Some(fdecl::Ref::Child(child)), _) => {
2036                let _ = self.validate_child_ref(decl, "source", &child, OfferType::Static);
2037            }
2038            // These sources don't.
2039            (Some(fdecl::Ref::VoidType(_)), None) => {}
2040            (Some(fdecl::Ref::Framework(_)), None) => {}
2041            (Some(fdecl::Ref::Capability(c)), None) => {
2042                self.validate_source_capability(c, decl, "source");
2043            }
2044            (Some(fdecl::Ref::Collection(c)), None)
2045                if collection_source == CollectionSource::Allow =>
2046            {
2047                self.validate_source_collection(c, decl);
2048            }
2049            // `source` is required.
2050            (None, _) => {
2051                self.errors.push(Error::missing_field(decl, "source"));
2052            }
2053            // Any combination that was not recognized above must be invalid.
2054            (_, _) => {
2055                self.errors.push(Error::invalid_field(decl, "source"));
2056            }
2057        }
2058    }
2059
2060    /// Adds a strong dependency between two nodes in the dependency graph between `source` and
2061    /// `target`.
2062    ///
2063    /// `source_name` is the name of the capability being routed (if applicable). The function is
2064    /// a no-op if `source` or `target` is `None`; this behavior is a convenience so that the
2065    /// caller can directly pass the result of `DependencyNode::try_from_ref`.
2066    fn add_strong_dep(
2067        &mut self,
2068        source: Option<DependencyNode<'a>>,
2069        target: Option<DependencyNode<'a>>,
2070    ) {
2071        if source.is_none() || target.is_none() {
2072            return;
2073        }
2074        let source = source.unwrap();
2075        let target = target.unwrap();
2076        match (source, target) {
2077            (DependencyNode::Self_, DependencyNode::Self_) => {
2078                // `self` dependencies (e.g. `use from self`) are allowed.
2079            }
2080            (source, target) => {
2081                self.strong_dependencies.add_edge(source, target);
2082            }
2083        }
2084    }
2085
2086    // Checks a group of offer decls to confirm that any duplicate offers are
2087    // valid aggregate offer declarations.
2088    fn validate_offer_group(&mut self, offers: &'a Vec<fdecl::Offer>, offer_type: OfferType) {
2089        let mut offer_groups: HashMap<_, Vec<fdecl::OfferService>> = HashMap::new();
2090        let service_offers = offers.into_iter().filter_map(|o| {
2091            if let fdecl::Offer::Service(s) = o {
2092                Some(s)
2093            } else {
2094                None
2095            }
2096        });
2097        for offer in service_offers {
2098            let key = Self::make_group_key(offer.target_name.as_ref(), offer.target.as_ref());
2099            if let Some(key) = key {
2100                offer_groups.entry(key).or_insert_with(|| vec![]).push(offer.clone());
2101            }
2102        }
2103        for offer_group in offer_groups.into_values() {
2104            if offer_group.len() == 1 {
2105                // If there are not multiple offers for a (target_name, target) pair then there are
2106                // no aggregation conditions to check.
2107                continue;
2108            }
2109
2110            self.validate_aggregation_has_same_availability(&offer_group);
2111
2112            let mut source_instance_filter_entries: HashSet<String> = HashSet::new();
2113            let mut service_source_names: HashSet<String> = HashSet::new();
2114            for o in offer_group {
2115                // Currently only service capabilities can be aggregated
2116                match (o.source_instance_filter, offer_type) {
2117                    (Some(source_instance_filter), _) => {
2118                        for instance_name in source_instance_filter {
2119                            if !source_instance_filter_entries.insert(instance_name.clone()) {
2120                                // If the source instance in the filter has been seen before this
2121                                // means there is a conflicting aggregate service offer.
2122                                self.errors.push(Error::invalid_aggregate_offer(format!(
2123                                    "Conflicting source_instance_filter in aggregate service \
2124                                    offer, instance_name '{}' seen in filter lists multiple times",
2125                                    instance_name,
2126                                )));
2127                            }
2128                        }
2129                    }
2130                    (None, OfferType::Static) => {}
2131                    (None, OfferType::Dynamic) => {
2132                        // Dynamic offers must include a filter.
2133                        self.errors.push(Error::invalid_aggregate_offer(
2134                            "source_instance_filter must be set for dynamic aggregate service \
2135                            offers",
2136                        ));
2137                    }
2138                }
2139                service_source_names.insert(
2140                    o.source_name
2141                        .expect("Offer Service declarations must always contain source_name"),
2142                );
2143            }
2144
2145            if service_source_names.len() > 1 {
2146                self.errors.push(Error::invalid_aggregate_offer(format!(
2147                    "All aggregate service offers must have the same source_name, saw {}. Use \
2148                    renamed_instances to rename instance names to avoid conflict.",
2149                    service_source_names.into_iter().sorted().collect::<Vec<String>>().join(", ")
2150                )));
2151            }
2152        }
2153    }
2154
2155    fn validate_offer_decl(&mut self, offer: &'a fdecl::Offer, offer_type: OfferType) {
2156        match offer {
2157            fdecl::Offer::Service(o) => {
2158                let decl = DeclType::OfferService;
2159                self.validate_offer_fields(
2160                    decl,
2161                    AllowableIds::Many,
2162                    CollectionSource::Allow,
2163                    Self::service_checker,
2164                    o.source.as_ref(),
2165                    o.source_name.as_ref(),
2166                    get_source_dictionary!(o),
2167                    o.target.as_ref(),
2168                    o.target_name.as_ref(),
2169                    #[cfg(fuchsia_api_level_at_least = "HEAD")]
2170                    Some(o.dependency_type.as_ref().unwrap_or(&fdecl::DependencyType::Strong)),
2171                    #[cfg(fuchsia_api_level_less_than = "HEAD")]
2172                    Some(&fdecl::DependencyType::Strong),
2173                    o.availability.as_ref(),
2174                    offer_type,
2175                );
2176                self.validate_filtered_service_fields(
2177                    decl,
2178                    o.source_instance_filter.as_ref(),
2179                    o.renamed_instances.as_ref(),
2180                );
2181            }
2182            fdecl::Offer::Protocol(o) => {
2183                let decl = DeclType::OfferProtocol;
2184                self.validate_offer_fields(
2185                    decl,
2186                    AllowableIds::One,
2187                    CollectionSource::Deny,
2188                    Self::protocol_checker,
2189                    o.source.as_ref(),
2190                    o.source_name.as_ref(),
2191                    get_source_dictionary!(o),
2192                    o.target.as_ref(),
2193                    o.target_name.as_ref(),
2194                    o.dependency_type.as_ref(),
2195                    o.availability.as_ref(),
2196                    offer_type,
2197                );
2198            }
2199            fdecl::Offer::Directory(o) => {
2200                let decl = DeclType::OfferDirectory;
2201                self.validate_offer_fields(
2202                    decl,
2203                    AllowableIds::One,
2204                    CollectionSource::Deny,
2205                    Self::directory_checker,
2206                    o.source.as_ref(),
2207                    o.source_name.as_ref(),
2208                    get_source_dictionary!(o),
2209                    o.target.as_ref(),
2210                    o.target_name.as_ref(),
2211                    o.dependency_type.as_ref(),
2212                    o.availability.as_ref(),
2213                    offer_type,
2214                );
2215                if let Some(subdir) = o.subdir.as_ref() {
2216                    check_relative_path(
2217                        Some(subdir),
2218                        DeclType::OfferDirectory,
2219                        "subdir",
2220                        &mut self.errors,
2221                    );
2222                }
2223            }
2224            fdecl::Offer::Storage(o) => {
2225                let decl = DeclType::OfferStorage;
2226                self.validate_storage_offer_fields(
2227                    decl,
2228                    Self::storage_checker,
2229                    o.source.as_ref(),
2230                    o.source_name.as_ref(),
2231                    o.target.as_ref(),
2232                    o.target_name.as_ref(),
2233                    o.availability.as_ref(),
2234                    offer_type,
2235                );
2236                self.add_strong_dep(
2237                    self.source_dependency_from_ref(
2238                        o.source_name.as_ref(),
2239                        None,
2240                        o.source.as_ref(),
2241                    ),
2242                    self.target_dependency_from_ref(o.target.as_ref()),
2243                );
2244            }
2245            fdecl::Offer::Runner(o) => {
2246                let decl = DeclType::OfferRunner;
2247                self.validate_offer_fields(
2248                    decl,
2249                    AllowableIds::One,
2250                    CollectionSource::Deny,
2251                    Self::runner_checker,
2252                    o.source.as_ref(),
2253                    o.source_name.as_ref(),
2254                    get_source_dictionary!(o),
2255                    o.target.as_ref(),
2256                    o.target_name.as_ref(),
2257                    Some(&fdecl::DependencyType::Strong),
2258                    Some(&fdecl::Availability::Required),
2259                    offer_type,
2260                );
2261            }
2262            fdecl::Offer::Resolver(o) => {
2263                let decl = DeclType::OfferResolver;
2264                self.validate_offer_fields(
2265                    decl,
2266                    AllowableIds::One,
2267                    CollectionSource::Deny,
2268                    Self::resolver_checker,
2269                    o.source.as_ref(),
2270                    o.source_name.as_ref(),
2271                    get_source_dictionary!(o),
2272                    o.target.as_ref(),
2273                    o.target_name.as_ref(),
2274                    Some(&fdecl::DependencyType::Strong),
2275                    Some(&fdecl::Availability::Required),
2276                    offer_type,
2277                );
2278            }
2279            fdecl::Offer::EventStream(e) => {
2280                self.validate_event_stream_offer_fields(e, offer_type);
2281            }
2282            #[cfg(fuchsia_api_level_at_least = "25")]
2283            fdecl::Offer::Dictionary(o) => {
2284                let decl = DeclType::OfferDictionary;
2285                self.validate_offer_fields(
2286                    decl,
2287                    AllowableIds::One,
2288                    CollectionSource::Deny,
2289                    Self::dictionary_checker,
2290                    o.source.as_ref(),
2291                    o.source_name.as_ref(),
2292                    get_source_dictionary!(o),
2293                    o.target.as_ref(),
2294                    o.target_name.as_ref(),
2295                    o.dependency_type.as_ref(),
2296                    o.availability.as_ref(),
2297                    offer_type,
2298                );
2299            }
2300            #[cfg(fuchsia_api_level_at_least = "HEAD")]
2301            fdecl::Offer::Config(o) => {
2302                let decl = DeclType::OfferConfig;
2303                self.validate_offer_fields(
2304                    decl,
2305                    AllowableIds::One,
2306                    CollectionSource::Deny,
2307                    Self::config_checker,
2308                    o.source.as_ref(),
2309                    o.source_name.as_ref(),
2310                    None,
2311                    o.target.as_ref(),
2312                    o.target_name.as_ref(),
2313                    Some(&fdecl::DependencyType::Strong),
2314                    o.availability.as_ref(),
2315                    offer_type,
2316                );
2317            }
2318            fdecl::OfferUnknown!() => {
2319                self.errors.push(Error::invalid_field(DeclType::Component, "offer"));
2320            }
2321        }
2322    }
2323
2324    fn validate_offer_fields(
2325        &mut self,
2326        decl: DeclType,
2327        allowable_names: AllowableIds,
2328        collection_source: CollectionSource,
2329        // This takes a callback that returns a [Container], instead of the &[Container] directly,
2330        // to avoid a borrow checker error that would occur from a simultaneous borrow on
2331        // &mut self.
2332        capability_checker: impl Fn(&Self) -> &dyn Container,
2333        source: Option<&'a fdecl::Ref>,
2334        source_name: Option<&'a String>,
2335        source_dictionary: Option<&'a String>,
2336        target: Option<&'a fdecl::Ref>,
2337        target_name: Option<&'a String>,
2338        dependency_type: Option<&'a fdecl::DependencyType>,
2339        availability: Option<&'a fdecl::Availability>,
2340        offer_type: OfferType,
2341    ) {
2342        self.validate_offer_source(decl, collection_source, source, source_dictionary, offer_type);
2343        check_route_availability(decl, availability, source, source_name, &mut self.errors);
2344        check_offer_name(source_name, decl, "source_name", offer_type, &mut self.errors);
2345        if source_dictionary.is_some() {
2346            check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
2347        }
2348        self.validate_offer_target(decl, allowable_names, target, target_name, offer_type);
2349        check_offer_name(target_name, decl, "target_name", offer_type, &mut self.errors);
2350
2351        if dependency_type.is_none() {
2352            self.errors.push(Error::missing_field(decl, "dependency_type"));
2353        } else if dependency_type == Some(&fdecl::DependencyType::Strong) {
2354            self.add_strong_dep(
2355                self.source_dependency_from_ref(source_name, source_dictionary, source),
2356                self.target_dependency_from_ref(target),
2357            );
2358
2359            // If `target` is a collection, we should add a dependency edge to all dynamic
2360            // instances in this collection.
2361            if let Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: collection })) =
2362                target.as_ref()
2363            {
2364                for name in self.dynamic_children_in_collection(&collection) {
2365                    self.add_strong_dep(
2366                        self.source_dependency_from_ref(source_name, source_dictionary, source),
2367                        Some(DependencyNode::Child(name, Some(&collection))),
2368                    );
2369                }
2370            }
2371        }
2372
2373        // If `source` is a collection, add dependency edges from all its dynamic children
2374        // to the collection, since the collection forms an aggregate.
2375        if let Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: collection })) = source {
2376            for name in self.dynamic_children_in_collection(&collection) {
2377                self.add_strong_dep(
2378                    Some(DependencyNode::Child(&name, Some(&collection))),
2379                    Some(DependencyNode::Collection(collection)),
2380                );
2381            }
2382        }
2383
2384        self.validate_route_from_self(
2385            decl,
2386            source,
2387            source_name,
2388            source_dictionary,
2389            capability_checker,
2390        );
2391    }
2392
2393    fn validate_offer_source(
2394        &mut self,
2395        decl: DeclType,
2396        collection_source: CollectionSource,
2397        source: Option<&'a fdecl::Ref>,
2398        source_dictionary: Option<&'a String>,
2399        offer_type: OfferType,
2400    ) {
2401        match (source, source_dictionary) {
2402            // These sources support source_dictionary.
2403            (Some(fdecl::Ref::Parent(_)), _) => {}
2404            (Some(fdecl::Ref::Self_(_)), _) => {}
2405            (Some(fdecl::Ref::Child(child)), _) => {
2406                self.validate_child_ref(decl, "source", &child, offer_type);
2407            }
2408            // These sources don't.
2409            (Some(fdecl::Ref::VoidType(_)), None) => {}
2410            (Some(fdecl::Ref::Framework(_)), None) => {}
2411            (Some(fdecl::Ref::Capability(c)), None) => {
2412                self.validate_source_capability(c, decl, "source");
2413            }
2414            (Some(fdecl::Ref::Collection(c)), None)
2415                if collection_source == CollectionSource::Allow =>
2416            {
2417                self.validate_source_collection(c, decl);
2418            }
2419            // `source` is required.
2420            (None, _) => self.errors.push(Error::missing_field(decl, "source")),
2421            // Any combination that was not recognized above must be invalid.
2422            (_, _) => self.errors.push(Error::invalid_field(decl, "source")),
2423        }
2424    }
2425
2426    fn validate_storage_offer_fields(
2427        &mut self,
2428        decl: DeclType,
2429        // This takes a callback that returns a [Container], instead of the &[Container] directly,
2430        // to avoid a borrow checker error that would occur from a simultaneous borrow on
2431        // &mut self.
2432        capability_checker: impl Fn(&Self) -> &dyn Container,
2433        source: Option<&'a fdecl::Ref>,
2434        source_name: Option<&'a String>,
2435        target: Option<&'a fdecl::Ref>,
2436        target_name: Option<&'a String>,
2437        availability: Option<&fdecl::Availability>,
2438        offer_type: OfferType,
2439    ) {
2440        match source {
2441            Some(fdecl::Ref::Parent(_) | fdecl::Ref::VoidType(_) | fdecl::Ref::Self_(_)) => {}
2442            Some(_) => {
2443                self.errors.push(Error::invalid_field(decl, "source"));
2444            }
2445            None => {
2446                self.errors.push(Error::missing_field(decl, "source"));
2447            }
2448        }
2449        check_route_availability(decl, availability, source, source_name, &mut self.errors);
2450        check_offer_name(source_name, decl, "source_name", offer_type, &mut self.errors);
2451        self.validate_offer_target(decl, AllowableIds::One, target, target_name, offer_type);
2452        check_offer_name(target_name, decl, "target_name", offer_type, &mut self.errors);
2453
2454        if let (Some(fdecl::Ref::Self_(_)), Some(name)) = (source, source_name) {
2455            if !(capability_checker)(self).contains(name) {
2456                self.errors.push(Error::invalid_capability(decl, "source", name));
2457            }
2458        }
2459    }
2460
2461    fn validate_event_stream_offer_fields(
2462        &mut self,
2463        event_stream: &'a fdecl::OfferEventStream,
2464        offer_type: OfferType,
2465    ) {
2466        let decl = DeclType::OfferEventStream;
2467        check_name(event_stream.source_name.as_ref(), decl, "source_name", &mut self.errors);
2468        if event_stream.target == Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})) {
2469            // Expose to framework from framework is never valid.
2470            self.errors.push(Error::invalid_field(DeclType::OfferEventStream, "target"));
2471        }
2472        if let Some(scope) = &event_stream.scope {
2473            if scope.is_empty() {
2474                self.errors.push(Error::invalid_field(decl, "scope"));
2475            }
2476            for value in scope {
2477                match value {
2478                    fdecl::Ref::Child(child) => {
2479                        self.validate_child_ref(
2480                            DeclType::OfferEventStream,
2481                            "scope",
2482                            &child,
2483                            offer_type,
2484                        );
2485                    }
2486                    fdecl::Ref::Collection(collection) => {
2487                        self.validate_collection_ref(
2488                            DeclType::OfferEventStream,
2489                            "scope",
2490                            &collection,
2491                        );
2492                    }
2493                    _ => {
2494                        self.errors.push(Error::invalid_field(DeclType::OfferEventStream, "scope"));
2495                    }
2496                }
2497            }
2498        }
2499        // Only parent, framework, child, and void are valid.
2500        match event_stream.source {
2501            Some(
2502                fdecl::Ref::Parent(_)
2503                | fdecl::Ref::Framework(_)
2504                | fdecl::Ref::Child(_)
2505                | fdecl::Ref::VoidType(_),
2506            ) => {}
2507            Some(_) => {
2508                self.errors.push(Error::invalid_field(decl, "source"));
2509            }
2510            None => {
2511                self.errors.push(Error::missing_field(decl, "source"));
2512            }
2513        };
2514
2515        check_route_availability(
2516            decl,
2517            event_stream.availability.as_ref(),
2518            event_stream.source.as_ref(),
2519            event_stream.source_name.as_ref(),
2520            &mut self.errors,
2521        );
2522
2523        self.validate_offer_target(
2524            decl,
2525            AllowableIds::One,
2526            event_stream.target.as_ref(),
2527            event_stream.target_name.as_ref(),
2528            offer_type,
2529        );
2530        check_name(event_stream.target_name.as_ref(), decl, "target_name", &mut self.errors);
2531    }
2532
2533    /// Check a `ChildRef` contains a valid child that exists.
2534    fn validate_child_ref(
2535        &mut self,
2536        decl: DeclType,
2537        field_name: &str,
2538        child: &fdecl::ChildRef,
2539        offer_type: OfferType,
2540    ) -> bool {
2541        if offer_type == OfferType::Dynamic && child.collection.is_some() {
2542            return self.validate_dynamic_child_ref(decl, field_name, child);
2543        }
2544        // Ensure the name is valid, and the reference refers to a static child.
2545        //
2546        // We attempt to list all errors if possible.
2547        let mut valid = true;
2548        if !check_name(
2549            Some(&child.name),
2550            decl,
2551            &format!("{}.child.name", field_name),
2552            &mut self.errors,
2553        ) {
2554            valid = false;
2555        }
2556        if child.collection.is_some() {
2557            self.errors
2558                .push(Error::extraneous_field(decl, format!("{}.child.collection", field_name)));
2559            valid = false;
2560        }
2561        if !valid {
2562            return false;
2563        }
2564
2565        // Ensure the child exists.
2566        let name: &str = &child.name;
2567        if !self.all_children.contains_key(name) {
2568            self.errors.push(Error::invalid_child(decl, field_name, name));
2569            return false;
2570        }
2571
2572        true
2573    }
2574
2575    /// Check a `ChildRef` contains a valid dynamic child.
2576    ///
2577    /// The manifest we're validating doesn't contain dynamic children so we can't check if the dynamic
2578    /// child actually exists, but we can confirm things like the name is valid.
2579    fn validate_dynamic_child_ref(
2580        &mut self,
2581        decl: DeclType,
2582        field_name: &str,
2583        child: &fdecl::ChildRef,
2584    ) -> bool {
2585        // Ensure the name is valid.
2586        //
2587        // We attempt to list all errors if possible.
2588        let mut valid = true;
2589        if !check_dynamic_name(
2590            Some(&child.name),
2591            decl,
2592            &format!("{}.child.name", field_name),
2593            &mut self.errors,
2594        ) {
2595            valid = false;
2596        }
2597        if !check_name(
2598            child.collection.as_ref(),
2599            decl,
2600            &format!("{}.child.collection", field_name),
2601            &mut self.errors,
2602        ) {
2603            valid = false;
2604        }
2605        valid
2606    }
2607
2608    /// Check a `CollectionRef` is valid and refers to an existing collection.
2609    fn validate_collection_ref(
2610        &mut self,
2611        decl: DeclType,
2612        field_name: &str,
2613        collection: &fdecl::CollectionRef,
2614    ) -> bool {
2615        // Ensure the name is valid.
2616        if !check_name(
2617            Some(&collection.name),
2618            decl,
2619            &format!("{}.collection.name", field_name),
2620            &mut self.errors,
2621        ) {
2622            return false;
2623        }
2624
2625        // Ensure the collection exists.
2626        if !self.all_collections.contains(&collection.name as &str) {
2627            self.errors.push(Error::invalid_collection(decl, field_name, &collection.name as &str));
2628            return false;
2629        }
2630
2631        true
2632    }
2633
2634    fn validate_offer_target(
2635        &mut self,
2636        decl: DeclType,
2637        allowable_names: AllowableIds,
2638        target: Option<&'a fdecl::Ref>,
2639        target_name: Option<&'a String>,
2640        offer_type: OfferType,
2641    ) {
2642        match target {
2643            Some(fdecl::Ref::Child(c)) => {
2644                self.validate_target_child(decl, allowable_names, c, target_name, offer_type);
2645            }
2646            Some(fdecl::Ref::Collection(c)) => {
2647                self.validate_target_collection(decl, allowable_names, c, target_name);
2648            }
2649            Some(fdecl::Ref::Capability(c)) => {
2650                // Only offers to dictionary capabilities are valid.
2651                #[cfg(fuchsia_api_level_at_least = "25")]
2652                if let Some(d) = self.all_dictionaries.get(&c.name.as_str()) {
2653                    if d.source_path.is_some() {
2654                        // If `source_path` is present that means this is an offer into a
2655                        // dynamic dictionary, which is not allowed.
2656                        self.errors.push(Error::invalid_field(decl, "target"));
2657                    }
2658                } else {
2659                    self.errors.push(Error::invalid_field(decl, "target"));
2660                }
2661                #[cfg(not(fuchsia_api_level_at_least = "25"))]
2662                {
2663                    let _ = c;
2664                    self.errors.push(Error::invalid_field(decl, "target"));
2665                }
2666            }
2667            Some(_) => {
2668                self.errors.push(Error::invalid_field(decl, "target"));
2669            }
2670            None => {
2671                self.errors.push(Error::missing_field(decl, "target"));
2672            }
2673        }
2674    }
2675
2676    fn validate_target_child(
2677        &mut self,
2678        decl: DeclType,
2679        allowable_names: AllowableIds,
2680        child: &'a fdecl::ChildRef,
2681        target_name: Option<&'a String>,
2682        offer_type: OfferType,
2683    ) {
2684        if !self.validate_child_ref(decl, "target", child, offer_type) {
2685            return;
2686        }
2687        if let Some(target_name) = target_name {
2688            let names_for_target = self
2689                .target_ids
2690                .entry(TargetId::Component(
2691                    &child.name,
2692                    child.collection.as_ref().map(|s| s.as_str()),
2693                ))
2694                .or_insert(HashMap::new());
2695            if let Some(prev_state) = names_for_target.insert(target_name, allowable_names) {
2696                if prev_state == AllowableIds::One || prev_state != allowable_names {
2697                    self.errors.push(Error::duplicate_field(
2698                        decl,
2699                        "target_name",
2700                        target_name as &str,
2701                    ));
2702                }
2703            }
2704            if let Some(collection) = child.collection.as_ref() {
2705                if let Some(names_for_target) =
2706                    self.target_ids.get(&TargetId::Collection(&collection))
2707                {
2708                    if names_for_target.contains_key(&target_name.as_str()) {
2709                        // This dynamic offer conflicts with a static offer to the same collection.
2710                        self.errors.push(Error::duplicate_field(
2711                            decl,
2712                            "target_name",
2713                            target_name as &str,
2714                        ));
2715                    }
2716                }
2717            }
2718        }
2719    }
2720
2721    fn validate_target_collection(
2722        &mut self,
2723        decl: DeclType,
2724        allowable_names: AllowableIds,
2725        collection: &'a fdecl::CollectionRef,
2726        target_name: Option<&'a String>,
2727    ) {
2728        if !self.validate_collection_ref(decl, "target", &collection) {
2729            return;
2730        }
2731        if let Some(target_name) = target_name {
2732            let names_for_target = self
2733                .target_ids
2734                .entry(TargetId::Collection(&collection.name))
2735                .or_insert(HashMap::new());
2736            if let Some(prev_state) = names_for_target.insert(target_name, allowable_names) {
2737                if prev_state == AllowableIds::One || prev_state != allowable_names {
2738                    self.errors.push(Error::duplicate_field(
2739                        decl,
2740                        "target_name",
2741                        target_name as &str,
2742                    ));
2743                }
2744            }
2745        }
2746    }
2747
2748    fn validate_route_from_self(
2749        &mut self,
2750        decl: DeclType,
2751        source: Option<&fdecl::Ref>,
2752        source_name: Option<&String>,
2753        source_dictionary: Option<&String>,
2754        capability_checker: impl Fn(&Self) -> &dyn Container,
2755    ) {
2756        let (Some(fdecl::Ref::Self_(_)), Some(name)) = (source, source_name) else {
2757            return;
2758        };
2759        match source_dictionary {
2760            Some(source_dictionary) => {
2761                #[cfg(fuchsia_api_level_at_least = "25")]
2762                {
2763                    use cm_types::IterablePath;
2764                    if let Ok(path) = cm_types::RelativePath::new(source_dictionary) {
2765                        if let Some(first_segment) = path.iter_segments().next().map(|s| s.as_str())
2766                        {
2767                            if !self.all_dictionaries.contains_key(first_segment) {
2768                                self.errors.push(Error::invalid_capability(
2769                                    decl,
2770                                    "source",
2771                                    first_segment,
2772                                ));
2773                            }
2774                        }
2775                    }
2776                }
2777                #[cfg(not(fuchsia_api_level_at_least = "25"))]
2778                let _ = source_dictionary;
2779            }
2780            None => {
2781                if !(capability_checker)(self).contains(name) {
2782                    self.errors.push(Error::invalid_capability(decl, "source", name));
2783                }
2784            }
2785        }
2786    }
2787
2788    fn source_dependency_from_ref(
2789        &self,
2790        source_name: Option<&'a String>,
2791        source_dictionary: Option<&'a String>,
2792        ref_: Option<&'a fdecl::Ref>,
2793    ) -> Option<DependencyNode<'a>> {
2794        if ref_.is_none() {
2795            return None;
2796        }
2797        match ref_.unwrap() {
2798            fdecl::Ref::Child(fdecl::ChildRef { name, collection }) => {
2799                Some(DependencyNode::Child(name.as_str(), collection.as_ref().map(|s| s.as_str())))
2800            }
2801            fdecl::Ref::Collection(fdecl::CollectionRef { name, .. }) => {
2802                Some(DependencyNode::Collection(name.as_str()))
2803            }
2804            fdecl::Ref::Capability(fdecl::CapabilityRef { name, .. }) => {
2805                Some(DependencyNode::Capability(name.as_str()))
2806            }
2807            fdecl::Ref::Self_(_) => {
2808                #[cfg(fuchsia_api_level_at_least = "25")]
2809                if let Some(source_dictionary) = source_dictionary {
2810                    let root_dict = source_dictionary.split('/').next().unwrap();
2811                    if self.all_dictionaries.contains_key(root_dict) {
2812                        Some(DependencyNode::Capability(root_dict))
2813                    } else {
2814                        Some(DependencyNode::Self_)
2815                    }
2816                } else if let Some(source_name) = source_name {
2817                    if self.all_storages.contains_key(source_name.as_str())
2818                        || self.all_dictionaries.contains_key(source_name.as_str())
2819                    {
2820                        Some(DependencyNode::Capability(source_name))
2821                    } else {
2822                        Some(DependencyNode::Self_)
2823                    }
2824                } else {
2825                    Some(DependencyNode::Self_)
2826                }
2827                #[cfg(not(fuchsia_api_level_at_least = "25"))]
2828                {
2829                    let _ = source_dictionary;
2830                    if let Some(source_name) = source_name {
2831                        if self.all_storages.contains_key(source_name.as_str()) {
2832                            Some(DependencyNode::Capability(source_name))
2833                        } else {
2834                            Some(DependencyNode::Self_)
2835                        }
2836                    } else {
2837                        Some(DependencyNode::Self_)
2838                    }
2839                }
2840            }
2841            fdecl::Ref::Parent(_) => {
2842                // We don't care about dependency cycles with the parent, as any potential issues
2843                // with that are resolved by cycle detection in the parent's manifest.
2844                None
2845            }
2846            fdecl::Ref::Framework(_) => {
2847                // We don't care about dependency cycles with the framework, as the framework
2848                // always outlives the component.
2849                None
2850            }
2851            fdecl::Ref::Debug(_) => {
2852                // We don't care about dependency cycles with any debug capabilities from the
2853                // environment, as those are put there by our parent, and any potential cycles with
2854                // our parent are handled by cycle detection in the parent's manifest.
2855                None
2856            }
2857            fdecl::Ref::VoidType(_) => None,
2858            fdecl::RefUnknown!() => {
2859                // We were unable to understand this FIDL value
2860                None
2861            }
2862        }
2863    }
2864
2865    fn target_dependency_from_ref(
2866        &self,
2867        ref_: Option<&'a fdecl::Ref>,
2868    ) -> Option<DependencyNode<'a>> {
2869        self.source_dependency_from_ref(None, None, ref_)
2870    }
2871
2872    fn dynamic_children_in_collection(&self, collection: &'a str) -> Vec<&'a str> {
2873        self.dynamic_children
2874            .iter()
2875            .filter_map(|(n, c)| if *c == collection { Some(*n) } else { None })
2876            .collect()
2877    }
2878
2879    // The following functions can be used to convert a type-specific collection of capabilities
2880    // into [Container].
2881    fn service_checker(&self) -> &dyn Container {
2882        &self.all_services
2883    }
2884    fn protocol_checker(&self) -> &dyn Container {
2885        &self.all_protocols
2886    }
2887    fn directory_checker(&self) -> &dyn Container {
2888        &self.all_directories
2889    }
2890    fn runner_checker(&self) -> &dyn Container {
2891        &self.all_runners
2892    }
2893    fn resolver_checker(&self) -> &dyn Container {
2894        &self.all_resolvers
2895    }
2896
2897    #[cfg(fuchsia_api_level_at_least = "25")]
2898    fn dictionary_checker(&self) -> &dyn Container {
2899        &self.all_dictionaries
2900    }
2901
2902    #[cfg(fuchsia_api_level_at_least = "HEAD")]
2903    fn config_checker(&self) -> &dyn Container {
2904        &self.all_configs
2905    }
2906    fn storage_checker(&self) -> &dyn Container {
2907        &self.all_storages
2908    }
2909    fn event_stream_checker(&self) -> &dyn Container {
2910        // Components can't define their own event streams. If someone tries to route an event
2911        // stream from Self it should generate some other error. So just return `true` to bypass
2912        // the logic.
2913        struct AlwaysTrueContainer {}
2914        impl Container for AlwaysTrueContainer {
2915            fn contains(&self, _key: &str) -> bool {
2916                true
2917            }
2918        }
2919        static CONTAINER: AlwaysTrueContainer = AlwaysTrueContainer {};
2920        &CONTAINER
2921    }
2922}
2923
2924#[cfg(test)]
2925mod tests {
2926    use super::*;
2927    use cm_types::MAX_LONG_NAME_LENGTH;
2928    use test_case::test_case;
2929    use {
2930        fidl_fuchsia_component_decl as fdecl, fidl_fuchsia_data as fdata, fidl_fuchsia_io as fio,
2931    };
2932
2933    macro_rules! test_validate {
2934        (
2935            $(
2936                $test_name:ident => {
2937                    input = $input:expr,
2938                    result = $result:expr,
2939                },
2940            )+
2941        ) => {
2942            $(
2943                #[test]
2944                fn $test_name() {
2945                    validate_test($input, $result);
2946                }
2947            )+
2948        }
2949    }
2950
2951    macro_rules! test_validate_any_result {
2952        (
2953            $(
2954                $test_name:ident => {
2955                    input = $input:expr,
2956                    results = $results:expr,
2957                },
2958            )+
2959        ) => {
2960            $(
2961                #[test]
2962                fn $test_name() {
2963                    validate_test_any_result($input, $results);
2964                }
2965            )+
2966        }
2967    }
2968
2969    macro_rules! test_validate_values_data {
2970        (
2971            $(
2972                $test_name:ident => {
2973                    input = $input:expr,
2974                    result = $result:expr,
2975                },
2976            )+
2977        ) => {
2978            $(
2979                #[test]
2980                fn $test_name() {
2981                    validate_values_data_test($input, $result);
2982                }
2983            )+
2984        }
2985    }
2986
2987    macro_rules! test_validate_capabilities {
2988        (
2989            $(
2990                $test_name:ident => {
2991                    input = $input:expr,
2992                    as_builtin = $as_builtin:expr,
2993                    result = $result:expr,
2994                },
2995            )+
2996        ) => {
2997            $(
2998                #[test]
2999                fn $test_name() {
3000                    validate_capabilities_test($input, $as_builtin, $result);
3001                }
3002            )+
3003        }
3004    }
3005
3006    macro_rules! test_dependency {
3007        (
3008            $(
3009                ($test_name:ident) => {
3010                    ty = $ty:expr,
3011                    offer_decl = $offer_decl:expr,
3012                },
3013            )+
3014        ) => {
3015            $(
3016                #[test]
3017                fn $test_name() {
3018                    let mut decl = new_component_decl();
3019                    let dependencies = vec![
3020                        ("a", "b"),
3021                        ("b", "a"),
3022                    ];
3023                    let offers = dependencies.into_iter().map(|(from,to)| {
3024                        let mut offer_decl = $offer_decl;
3025                        offer_decl.source = Some(fdecl::Ref::Child(
3026                           fdecl::ChildRef { name: from.to_string(), collection: None },
3027                        ));
3028                        offer_decl.target = Some(fdecl::Ref::Child(
3029                           fdecl::ChildRef { name: to.to_string(), collection: None },
3030                        ));
3031                        $ty(offer_decl)
3032                    }).collect();
3033                    let children = ["a", "b"].iter().map(|name| {
3034                        fdecl::Child {
3035                            name: Some(name.to_string()),
3036                            url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
3037                            startup: Some(fdecl::StartupMode::Lazy),
3038                            on_terminate: None,
3039                            environment: None,
3040                            ..Default::default()
3041                        }
3042                    }).collect();
3043                    decl.offers = Some(offers);
3044                    decl.children = Some(children);
3045                    let result = Err(ErrorList::new(vec![
3046                        Error::dependency_cycle(
3047                            directed_graph::Error::CyclesDetected([vec!["child a", "child b", "child a"]].iter().cloned().collect()).format_cycle()),
3048                    ]));
3049                    validate_test(decl, result);
3050                }
3051            )+
3052        }
3053    }
3054
3055    macro_rules! test_weak_dependency {
3056        (
3057            $(
3058                ($test_name:ident) => {
3059                    ty = $ty:expr,
3060                    offer_decl = $offer_decl:expr,
3061                },
3062            )+
3063        ) => {
3064            $(
3065                #[test_case(fdecl::DependencyType::Weak)]
3066                fn $test_name(weak_dep: fdecl::DependencyType) {
3067                    let mut decl = new_component_decl();
3068                    let offers = vec![
3069                        {
3070                            let mut offer_decl = $offer_decl;
3071                            offer_decl.source = Some(fdecl::Ref::Child(
3072                               fdecl::ChildRef { name: "a".to_string(), collection: None },
3073                            ));
3074                            offer_decl.target = Some(fdecl::Ref::Child(
3075                               fdecl::ChildRef { name: "b".to_string(), collection: None },
3076                            ));
3077                            offer_decl.dependency_type = Some(fdecl::DependencyType::Strong);
3078                            $ty(offer_decl)
3079                        },
3080                        {
3081                            let mut offer_decl = $offer_decl;
3082                            offer_decl.source = Some(fdecl::Ref::Child(
3083                               fdecl::ChildRef { name: "b".to_string(), collection: None },
3084                            ));
3085                            offer_decl.target = Some(fdecl::Ref::Child(
3086                               fdecl::ChildRef { name: "a".to_string(), collection: None },
3087                            ));
3088                            offer_decl.dependency_type = Some(weak_dep);
3089                            $ty(offer_decl)
3090                        },
3091                    ];
3092                    let children = ["a", "b"].iter().map(|name| {
3093                        fdecl::Child {
3094                            name: Some(name.to_string()),
3095                            url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
3096                            startup: Some(fdecl::StartupMode::Lazy),
3097                            on_terminate: None,
3098                            environment: None,
3099                            ..Default::default()
3100                        }
3101                    }).collect();
3102                    decl.offers = Some(offers);
3103                    decl.children = Some(children);
3104                    let result = Ok(());
3105                    validate_test(decl, result);
3106                }
3107            )+
3108        }
3109    }
3110
3111    #[track_caller]
3112    fn validate_test(input: fdecl::Component, expected_res: Result<(), ErrorList>) {
3113        let res = validate(&input);
3114        assert_eq!(res, expected_res);
3115    }
3116
3117    #[track_caller]
3118    fn validate_test_any_result(input: fdecl::Component, expected_res: Vec<Result<(), ErrorList>>) {
3119        let res = format!("{:?}", validate(&input));
3120        let expected_res_debug = format!("{:?}", expected_res);
3121
3122        let matched_exp =
3123            expected_res.into_iter().find(|expected| res == format!("{:?}", expected));
3124
3125        assert!(
3126            matched_exp.is_some(),
3127            "assertion failed: Expected one of:\n{:?}\nActual:\n{:?}",
3128            expected_res_debug,
3129            res
3130        );
3131    }
3132
3133    #[track_caller]
3134    fn validate_values_data_test(
3135        input: fdecl::ConfigValuesData,
3136        expected_res: Result<(), ErrorList>,
3137    ) {
3138        let res = validate_values_data(&input);
3139        assert_eq!(res, expected_res);
3140    }
3141
3142    #[track_caller]
3143    fn validate_capabilities_test(
3144        input: Vec<fdecl::Capability>,
3145        as_builtin: bool,
3146        expected_res: Result<(), ErrorList>,
3147    ) {
3148        let res = validate_capabilities(&input, as_builtin);
3149        assert_eq!(res, expected_res);
3150    }
3151
3152    fn new_component_decl() -> fdecl::Component {
3153        fdecl::Component {
3154            program: None,
3155            uses: None,
3156            exposes: None,
3157            offers: None,
3158            facets: None,
3159            capabilities: None,
3160            children: None,
3161            collections: None,
3162            environments: None,
3163            ..Default::default()
3164        }
3165    }
3166
3167    test_validate_any_result! {
3168        test_validate_use_disallows_nested_dirs => {
3169            input = {
3170                let mut decl = new_component_decl();
3171                decl.uses = Some(vec![
3172                    fdecl::Use::Directory(fdecl::UseDirectory {
3173                        dependency_type: Some(fdecl::DependencyType::Strong),
3174                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3175                        source_name: Some("abc".to_string()),
3176                        target_path: Some("/foo/bar".to_string()),
3177                        rights: Some(fio::Operations::CONNECT),
3178                        subdir: None,
3179                        ..Default::default()
3180                    }),
3181                    fdecl::Use::Directory(fdecl::UseDirectory {
3182                        dependency_type: Some(fdecl::DependencyType::Strong),
3183                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3184                        source_name: Some("abc".to_string()),
3185                        target_path: Some("/foo/bar/baz".to_string()),
3186                        rights: Some(fio::Operations::CONNECT),
3187                        subdir: None,
3188                        ..Default::default()
3189                    }),
3190                ]);
3191                decl
3192            },
3193            results = vec![
3194                Err(ErrorList::new(vec![
3195                    Error::invalid_path_overlap(
3196                        DeclType::UseDirectory, "/foo/bar/baz", DeclType::UseDirectory, "/foo/bar"),
3197                ])),
3198                Err(ErrorList::new(vec![
3199                    Error::invalid_path_overlap(
3200                        DeclType::UseDirectory, "/foo/bar", DeclType::UseDirectory, "/foo/bar/baz"),
3201                ])),
3202            ],
3203        },
3204        test_validate_use_disallows_nested_dirs_storage => {
3205            input = {
3206                let mut decl = new_component_decl();
3207                decl.uses = Some(vec![
3208                    fdecl::Use::Storage(fdecl::UseStorage {
3209                        source_name: Some("abc".to_string()),
3210                        target_path: Some("/foo/bar".to_string()),
3211                        ..Default::default()
3212                    }),
3213                    fdecl::Use::Storage(fdecl::UseStorage {
3214                        source_name: Some("abc".to_string()),
3215                        target_path: Some("/foo/bar/baz".to_string()),
3216                        ..Default::default()
3217                    }),
3218                ]);
3219                decl
3220            },
3221            results = vec![
3222                Err(ErrorList::new(vec![
3223                    Error::invalid_path_overlap(
3224                        DeclType::UseStorage, "/foo/bar/baz", DeclType::UseStorage, "/foo/bar"),
3225                ])),
3226                Err(ErrorList::new(vec![
3227                    Error::invalid_path_overlap(
3228                        DeclType::UseStorage, "/foo/bar", DeclType::UseStorage, "/foo/bar/baz"),
3229                ])),
3230            ],
3231        },
3232        test_validate_use_disallows_nested_dirs_directory_and_storage => {
3233            input = {
3234                let mut decl = new_component_decl();
3235                decl.uses = Some(vec![
3236                    fdecl::Use::Directory(fdecl::UseDirectory {
3237                        dependency_type: Some(fdecl::DependencyType::Strong),
3238                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3239                        source_name: Some("abc".to_string()),
3240                        target_path: Some("/foo/bar".to_string()),
3241                        rights: Some(fio::Operations::CONNECT),
3242                        subdir: None,
3243                        ..Default::default()
3244                    }),
3245                    fdecl::Use::Storage(fdecl::UseStorage {
3246                        source_name: Some("abc".to_string()),
3247                        target_path: Some("/foo/bar/baz".to_string()),
3248                        ..Default::default()
3249                    }),
3250                ]);
3251                decl
3252            },
3253            results = vec![
3254                Err(ErrorList::new(vec![
3255                    Error::invalid_path_overlap(
3256                        DeclType::UseStorage, "/foo/bar/baz", DeclType::UseDirectory, "/foo/bar"),
3257                ])),
3258                Err(ErrorList::new(vec![
3259                    Error::invalid_path_overlap(
3260                        DeclType::UseDirectory, "/foo/bar", DeclType::UseStorage, "/foo/bar/baz"),
3261                ])),
3262            ],
3263        },
3264        test_validate_use_disallows_common_prefixes_protocol => {
3265            input = {
3266                let mut decl = new_component_decl();
3267                decl.uses = Some(vec![
3268                    fdecl::Use::Directory(fdecl::UseDirectory {
3269                        dependency_type: Some(fdecl::DependencyType::Strong),
3270                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3271                        source_name: Some("abc".to_string()),
3272                        target_path: Some("/foo/bar".to_string()),
3273                        rights: Some(fio::Operations::CONNECT),
3274                        subdir: None,
3275                        ..Default::default()
3276                    }),
3277                    fdecl::Use::Protocol(fdecl::UseProtocol {
3278                        dependency_type: Some(fdecl::DependencyType::Strong),
3279                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3280                        source_name: Some("crow".to_string()),
3281                        target_path: Some("/foo/bar/fuchsia.2".to_string()),
3282                        ..Default::default()
3283                    }),
3284                ]);
3285                decl
3286            },
3287            results = vec![
3288                Err(ErrorList::new(vec![
3289                    Error::invalid_path_overlap(
3290                        DeclType::UseProtocol, "/foo/bar/fuchsia.2", DeclType::UseDirectory, "/foo/bar"),
3291                ])),
3292                Err(ErrorList::new(vec![
3293                    Error::invalid_path_overlap(
3294                        DeclType::UseDirectory, "/foo/bar", DeclType::UseProtocol, "/foo/bar/fuchsia.2"),
3295                ])),
3296            ],
3297        },
3298        test_validate_use_disallows_common_prefixes_service => {
3299            input = {
3300                let mut decl = new_component_decl();
3301                decl.uses = Some(vec![
3302                    fdecl::Use::Directory(fdecl::UseDirectory {
3303                        dependency_type: Some(fdecl::DependencyType::Strong),
3304                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3305                        source_name: Some("abc".to_string()),
3306                        target_path: Some("/foo/bar".to_string()),
3307                        rights: Some(fio::Operations::CONNECT),
3308                        subdir: None,
3309                        ..Default::default()
3310                    }),
3311                    fdecl::Use::Service(fdecl::UseService {
3312                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3313                        source_name: Some("space".to_string()),
3314                        target_path: Some("/foo/bar/baz/fuchsia.logger.Log".to_string()),
3315                        dependency_type: Some(fdecl::DependencyType::Strong),
3316                        ..Default::default()
3317                    }),
3318                ]);
3319                decl
3320            },
3321            results = vec![
3322                Err(ErrorList::new(vec![
3323                    Error::invalid_path_overlap(
3324                        DeclType::UseService, "/foo/bar/baz/fuchsia.logger.Log", DeclType::UseDirectory, "/foo/bar"),
3325                ])),
3326                Err(ErrorList::new(vec![
3327                    Error::invalid_path_overlap(
3328                        DeclType::UseDirectory, "/foo/bar", DeclType::UseService, "/foo/bar/baz/fuchsia.logger.Log"),
3329                ])),
3330            ],
3331        },
3332        test_validate_use_disallows_pkg => {
3333            input = {
3334                let mut decl = new_component_decl();
3335                decl.uses = Some(vec![
3336                    fdecl::Use::Directory(fdecl::UseDirectory {
3337                        dependency_type: Some(fdecl::DependencyType::Strong),
3338                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3339                        source_name: Some("abc".to_string()),
3340                        target_path: Some("/pkg".to_string()),
3341                        rights: Some(fio::Operations::CONNECT),
3342                        subdir: None,
3343                        ..Default::default()
3344                    }),
3345                ]);
3346                decl
3347            },
3348            results = vec![
3349                Err(ErrorList::new(vec![
3350                    Error::pkg_path_overlap(DeclType::UseDirectory, "/pkg"),
3351                ])),
3352            ],
3353        },
3354        test_validate_use_disallows_pkg_overlap => {
3355            input = {
3356                let mut decl = new_component_decl();
3357                decl.uses = Some(vec![
3358                    fdecl::Use::Directory(fdecl::UseDirectory {
3359                        dependency_type: Some(fdecl::DependencyType::Strong),
3360                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3361                        source_name: Some("abc".to_string()),
3362                        target_path: Some("/pkg/foo".to_string()),
3363                        rights: Some(fio::Operations::CONNECT),
3364                        subdir: None,
3365                        ..Default::default()
3366                    }),
3367                ]);
3368                decl
3369            },
3370            results = vec![
3371                Err(ErrorList::new(vec![
3372                    Error::pkg_path_overlap(DeclType::UseDirectory, "/pkg/foo"),
3373                ])),
3374            ],
3375        },
3376        test_validate_use_optional_config_correct => {
3377            input = {
3378                let mut decl = new_component_decl();
3379                decl.uses = Some(vec![
3380                    fdecl::Use::Config(fdecl::UseConfiguration {
3381                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3382                        source_name: Some("abc".to_string()),
3383                        target_name: Some("foo".to_string()),
3384                        availability: Some(fdecl::Availability::Optional),
3385                        type_: Some(fdecl::ConfigType {
3386                            layout: fdecl::ConfigTypeLayout::Bool,
3387                            parameters: Some(Vec::new()),
3388                            constraints: Vec::new(),
3389                        }),
3390                        ..Default::default()
3391                    }),
3392                ]);
3393                decl.config = Some(fdecl::ConfigSchema {
3394                    fields: Some(vec![fdecl::ConfigField {
3395                        key: Some("foo".into()),
3396                        type_: Some(fdecl::ConfigType {
3397                            layout: fdecl::ConfigTypeLayout::Bool,
3398                            parameters: Some(Vec::new()),
3399                            constraints: Vec::new(),
3400                        }),
3401                        mutability: None,
3402                        ..Default::default()}]),
3403                    checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3404                    value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3405                    ..Default::default()
3406                     });
3407                decl
3408            },
3409            results = vec![Ok(())],
3410        },
3411        test_validate_use_optional_config_no_config_schema => {
3412            input = {
3413                let mut decl = new_component_decl();
3414                decl.uses = Some(vec![
3415                    fdecl::Use::Config(fdecl::UseConfiguration {
3416                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3417                        source_name: Some("abc".to_string()),
3418                        target_name: Some("foo".to_string()),
3419                        availability: Some(fdecl::Availability::Optional),
3420                        type_: Some(fdecl::ConfigType {
3421                            layout: fdecl::ConfigTypeLayout::Bool,
3422                            parameters: None,
3423                            constraints: Vec::new(),
3424                        }),
3425                        ..Default::default()
3426                    }),
3427                ]);
3428                decl
3429            },
3430            results = vec![
3431                Err(ErrorList::new(vec![
3432                    Error::missing_field(DeclType::ConfigField, "config"),
3433                ])),
3434            ],
3435        },
3436        test_validate_use_optional_config_no_config_field => {
3437            input = {
3438                let mut decl = new_component_decl();
3439                decl.uses = Some(vec![
3440                    fdecl::Use::Config(fdecl::UseConfiguration {
3441                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3442                        source_name: Some("abc".to_string()),
3443                        target_name: Some("foo".to_string()),
3444                        availability: Some(fdecl::Availability::Optional),
3445                        type_: Some(fdecl::ConfigType {
3446                            layout: fdecl::ConfigTypeLayout::Bool,
3447                            parameters: None,
3448                            constraints: Vec::new(),
3449                        }),
3450                        ..Default::default()
3451                    }),
3452                ]);
3453                decl.config = Some(fdecl::ConfigSchema {
3454                    fields: Some(vec![]),
3455                    checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3456                    value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3457                    ..Default::default()
3458                     });
3459                decl
3460            },
3461            results = vec![
3462                Err(ErrorList::new(vec![
3463                    Error::missing_field(DeclType::ConfigField, "foo"),
3464                ])),
3465            ],
3466        },
3467        test_validate_use_optional_config_bad_type => {
3468            input = {
3469                let mut decl = new_component_decl();
3470                decl.uses = Some(vec![
3471                    fdecl::Use::Config(fdecl::UseConfiguration {
3472                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3473                        source_name: Some("abc".to_string()),
3474                        target_name: Some("foo".to_string()),
3475                        availability: Some(fdecl::Availability::Optional),
3476                        type_: Some(fdecl::ConfigType {
3477                            layout: fdecl::ConfigTypeLayout::Bool,
3478                            parameters: None,
3479                            constraints: Vec::new(),
3480                        }),
3481                        ..Default::default()
3482                    }),
3483                ]);
3484                decl.config = Some(fdecl::ConfigSchema {
3485                    fields: Some(vec![fdecl::ConfigField {
3486                        key: Some("foo".into()),
3487                        type_: Some(fdecl::ConfigType {
3488                            layout: fdecl::ConfigTypeLayout::Int16,
3489                            parameters: Some(Vec::new()),
3490                            constraints: Vec::new(),
3491                        }),
3492                        mutability: None,
3493                        ..Default::default()}]),
3494                    checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3495                    value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3496                    ..Default::default()
3497                     });
3498                decl
3499            },
3500            results = vec![
3501                Err(ErrorList::new(vec![
3502                    Error::invalid_field(DeclType::ConfigField, "foo"),
3503                ])),
3504            ],
3505        },
3506    }
3507
3508    test_validate_values_data! {
3509        test_values_data_ok => {
3510            input = fdecl::ConfigValuesData {
3511                values: Some(vec![
3512                    fdecl::ConfigValueSpec {
3513                        value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(true))),
3514                        ..Default::default()
3515                    }
3516                ]),
3517                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3518                ..Default::default()
3519            },
3520            result = Ok(()),
3521        },
3522        test_values_data_no_checksum => {
3523            input = fdecl::ConfigValuesData {
3524                values: Some(vec![]),
3525                checksum: None,
3526                ..Default::default()
3527            },
3528            result = Err(ErrorList::new(vec![
3529                Error::missing_field(DeclType::ConfigValuesData, "checksum")
3530            ])),
3531        },
3532        test_values_data_unknown_checksum => {
3533            input = fdecl::ConfigValuesData {
3534                values: Some(vec![]),
3535                checksum: Some(fdecl::ConfigChecksum::unknown_variant_for_testing()),
3536                ..Default::default()
3537            },
3538            result = Err(ErrorList::new(vec![
3539                Error::invalid_field(DeclType::ConfigValuesData, "checksum")
3540            ])),
3541        },
3542        test_values_data_no_values => {
3543            input = fdecl::ConfigValuesData {
3544                values: None,
3545                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3546                ..Default::default()
3547            },
3548            result = Err(ErrorList::new(vec![
3549                Error::missing_field(DeclType::ConfigValuesData, "values")
3550            ])),
3551        },
3552        test_values_data_no_inner_value => {
3553            input = fdecl::ConfigValuesData {
3554                values: Some(vec![
3555                    fdecl::ConfigValueSpec::default()
3556                ]),
3557                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3558                ..Default::default()
3559            },
3560            result = Err(ErrorList::new(vec![
3561                Error::missing_field(DeclType::ConfigValueSpec, "value")
3562            ])),
3563        },
3564        test_values_data_unknown_inner_value => {
3565            input = fdecl::ConfigValuesData {
3566                values: Some(vec![
3567                    fdecl::ConfigValueSpec {
3568                        value: Some(fdecl::ConfigValue::unknown_variant_for_testing()),
3569                        ..Default::default()
3570                    }
3571                ]),
3572                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3573                ..Default::default()
3574            },
3575            result = Err(ErrorList::new(vec![
3576                Error::invalid_field(DeclType::ConfigValueSpec, "value")
3577            ])),
3578        },
3579        test_values_data_unknown_single_value => {
3580            input = fdecl::ConfigValuesData {
3581                values: Some(vec![
3582                    fdecl::ConfigValueSpec {
3583                        value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::unknown_variant_for_testing())),
3584                        ..Default::default()
3585                    }
3586                ]),
3587                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3588                ..Default::default()
3589            },
3590            result = Err(ErrorList::new(vec![
3591                Error::invalid_field(DeclType::ConfigValueSpec, "value")
3592            ])),
3593        },
3594        test_values_data_unknown_list_value => {
3595            input = fdecl::ConfigValuesData {
3596                values: Some(vec![
3597                    fdecl::ConfigValueSpec {
3598                        value: Some(fdecl::ConfigValue::Vector(fdecl::ConfigVectorValue::unknown_variant_for_testing())),
3599                        ..Default::default()
3600                    }
3601                ]),
3602                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3603                ..Default::default()
3604            },
3605            result = Err(ErrorList::new(vec![
3606                Error::invalid_field(DeclType::ConfigValueSpec, "value")
3607            ])),
3608        },
3609    }
3610
3611    test_validate! {
3612        // uses
3613        test_validate_uses_empty => {
3614            input = {
3615                let mut decl = new_component_decl();
3616                decl.program = Some(fdecl::Program {
3617                    runner: Some("elf".to_string()),
3618                    info: Some(fdata::Dictionary {
3619                        entries: None,
3620                        ..Default::default()
3621                    }),
3622                    ..Default::default()
3623                });
3624                decl.uses = Some(vec![
3625                    fdecl::Use::Service(fdecl::UseService {
3626                        source: None,
3627                        source_name: None,
3628                        target_path: None,
3629                        dependency_type: None,
3630                        ..Default::default()
3631                    }),
3632                    fdecl::Use::Protocol(fdecl::UseProtocol {
3633                        dependency_type: None,
3634                        source: None,
3635                        source_name: None,
3636                        target_path: None,
3637                        ..Default::default()
3638                    }),
3639                    fdecl::Use::Directory(fdecl::UseDirectory {
3640                        dependency_type: None,
3641                        source: None,
3642                        source_name: None,
3643                        target_path: None,
3644                        rights: None,
3645                        subdir: None,
3646                        ..Default::default()
3647                    }),
3648                    fdecl::Use::Storage(fdecl::UseStorage {
3649                        source_name: None,
3650                        target_path: None,
3651                        ..Default::default()
3652                    }),
3653                    fdecl::Use::EventStream(fdecl::UseEventStream {
3654                        source_name: None,
3655                        source: None,
3656                        target_path: None,
3657                        ..Default::default()
3658                    }),
3659                    fdecl::Use::Runner(fdecl::UseRunner {
3660                        source_name: None,
3661                        source: None,
3662                        ..Default::default()
3663                    }),
3664                ]);
3665                decl
3666            },
3667            result = Err(ErrorList::new(vec![
3668                Error::missing_field(DeclType::UseService, "source"),
3669                Error::missing_field(DeclType::UseService, "source_name"),
3670                Error::missing_field(DeclType::UseService, "target_path"),
3671                Error::missing_field(DeclType::UseService, "dependency_type"),
3672                Error::missing_field(DeclType::UseProtocol, "source"),
3673                Error::missing_field(DeclType::UseProtocol, "source_name"),
3674                Error::missing_field(DeclType::UseProtocol, "target_path"),
3675                Error::missing_field(DeclType::UseProtocol, "dependency_type"),
3676                Error::missing_field(DeclType::UseDirectory, "source"),
3677                Error::missing_field(DeclType::UseDirectory, "source_name"),
3678                Error::missing_field(DeclType::UseDirectory, "target_path"),
3679                Error::missing_field(DeclType::UseDirectory, "dependency_type"),
3680                Error::missing_field(DeclType::UseDirectory, "rights"),
3681                Error::missing_field(DeclType::UseStorage, "source_name"),
3682                Error::missing_field(DeclType::UseStorage, "target_path"),
3683                Error::missing_field(DeclType::UseEventStream, "source"),
3684                Error::missing_field(DeclType::UseEventStream, "source_name"),
3685                Error::missing_field(DeclType::UseEventStream, "target_path"),
3686                Error::missing_field(DeclType::UseRunner, "source"),
3687                Error::missing_field(DeclType::UseRunner, "source_name"),
3688            ])),
3689        },
3690        test_validate_missing_program_info => {
3691            input = {
3692                let mut decl = new_component_decl();
3693                decl.program = Some(fdecl::Program {
3694                    runner: Some("runner".to_string()),
3695                    info: None,
3696                    ..Default::default()
3697                });
3698                decl
3699            },
3700            result = Err(ErrorList::new(vec![
3701                Error::missing_field(DeclType::Program, "info")
3702            ])),
3703        },
3704        test_validate_uses_invalid_identifiers => {
3705            input = {
3706                let mut decl = new_component_decl();
3707                decl.uses = Some(vec![
3708                    fdecl::Use::Service(fdecl::UseService {
3709                        source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
3710                            name: "^bad".to_string(),
3711                        })),
3712                        source_name: Some("foo/".to_string()),
3713                        target_path: Some("a/foo".to_string()),
3714                        dependency_type: Some(fdecl::DependencyType::Strong),
3715                        ..Default::default()
3716                    }),
3717                    fdecl::Use::Protocol(fdecl::UseProtocol {
3718                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3719                            name: "^bad".to_string(),
3720                            collection: None,
3721                        })),
3722                        source_name: Some("foo/".to_string()),
3723                        target_path: Some("b/foo".to_string()),
3724                        dependency_type: Some(fdecl::DependencyType::Strong),
3725                        ..Default::default()
3726                    }),
3727                    fdecl::Use::Directory(fdecl::UseDirectory {
3728                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3729                            name: "^bad".to_string(),
3730                            collection: None,
3731                        })),
3732                        source_name: Some("foo/".to_string()),
3733                        target_path: Some("c".to_string()),
3734                        rights: Some(fio::Operations::CONNECT),
3735                        subdir: Some("/foo".to_string()),
3736                        dependency_type: Some(fdecl::DependencyType::Strong),
3737                        ..Default::default()
3738                    }),
3739                    fdecl::Use::Storage(fdecl::UseStorage {
3740                        source_name: Some("foo/".to_string()),
3741                        target_path: Some("d".to_string()),
3742                        ..Default::default()
3743                    }),
3744                    fdecl::Use::EventStream(fdecl::UseEventStream {
3745                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3746                            name: "^bad".to_string(),
3747                            collection: None,
3748                        })),
3749                        source_name: Some("foo/".to_string()),
3750                        target_path: Some("e".to_string()),
3751                        ..Default::default()
3752                    }),
3753                    fdecl::Use::Runner(fdecl::UseRunner {
3754                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3755                            name: "^bad".to_string(),
3756                            collection: None,
3757                        })),
3758                        source_name: Some("foo/".to_string()),
3759                        ..Default::default()
3760                    }),
3761                ]);
3762                decl
3763            },
3764            result = Err(ErrorList::new(vec![
3765                Error::invalid_field(DeclType::UseService, "source.capability.name"),
3766                Error::invalid_field(DeclType::UseService, "source_name"),
3767                Error::invalid_field(DeclType::UseService, "target_path"),
3768                Error::invalid_field(DeclType::UseProtocol, "source.child.name"),
3769                Error::invalid_field(DeclType::UseProtocol, "source_name"),
3770                Error::invalid_field(DeclType::UseProtocol, "target_path"),
3771                Error::invalid_field(DeclType::UseDirectory, "source.child.name"),
3772                Error::invalid_field(DeclType::UseDirectory, "source_name"),
3773                Error::invalid_field(DeclType::UseDirectory, "target_path"),
3774                Error::invalid_field(DeclType::UseDirectory, "subdir"),
3775                Error::invalid_field(DeclType::UseStorage, "source_name"),
3776                Error::invalid_field(DeclType::UseStorage, "target_path"),
3777                Error::invalid_field(DeclType::UseEventStream, "source.child.name"),
3778                Error::invalid_field(DeclType::UseEventStream, "source_name"),
3779                Error::invalid_field(DeclType::UseEventStream, "target_path"),
3780                Error::invalid_field(DeclType::UseRunner, "source.child.name"),
3781                Error::invalid_field(DeclType::UseRunner, "source_name"),
3782            ])),
3783        },
3784        test_validate_uses_missing_source => {
3785            input = {
3786                fdecl::Component {
3787                    uses: Some(vec![
3788                        fdecl::Use::Protocol(fdecl::UseProtocol {
3789                            dependency_type: Some(fdecl::DependencyType::Strong),
3790                            source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
3791                                name: "this-storage-doesnt-exist".to_string(),
3792                            })),
3793                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3794                            target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3795                            ..Default::default()
3796                        })
3797                    ]),
3798                    ..new_component_decl()
3799                }
3800            },
3801            result = Err(ErrorList::new(vec![
3802                Error::invalid_capability(DeclType::UseProtocol, "source", "this-storage-doesnt-exist"),
3803            ])),
3804        },
3805        test_validate_uses_invalid_child => {
3806            input = {
3807                fdecl::Component {
3808                    uses: Some(vec![
3809                        fdecl::Use::Protocol(fdecl::UseProtocol {
3810                            dependency_type: Some(fdecl::DependencyType::Strong),
3811                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3812                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3813                            target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3814                            ..Default::default()
3815                        }),
3816                        fdecl::Use::Service(fdecl::UseService {
3817                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3818                            source_name: Some("service_name".to_string()),
3819                            target_path: Some("/svc/service_name".to_string()),
3820                            dependency_type: Some(fdecl::DependencyType::Strong),
3821                            ..Default::default()
3822                        }),
3823                        fdecl::Use::Directory(fdecl::UseDirectory {
3824                            dependency_type: Some(fdecl::DependencyType::Strong),
3825                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3826                            source_name: Some("DirectoryName".to_string()),
3827                            target_path: Some("/data/DirectoryName".to_string()),
3828                            rights: Some(fio::Operations::CONNECT),
3829                            subdir: None,
3830                            ..Default::default()
3831                        }),
3832                        fdecl::Use::Runner(fdecl::UseRunner {
3833                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3834                            source_name: Some("RunnerName".to_string()),
3835                            ..Default::default()
3836                        }),
3837                    ]),
3838                    ..new_component_decl()
3839                }
3840            },
3841            result = Err(ErrorList::new(vec![
3842                Error::invalid_child(DeclType::UseProtocol, "source", "no-such-child"),
3843                Error::invalid_child(DeclType::UseService, "source", "no-such-child"),
3844                Error::invalid_child(DeclType::UseDirectory, "source", "no-such-child"),
3845                Error::invalid_child(DeclType::UseRunner, "source", "no-such-child"),
3846            ])),
3847        },
3848        test_validate_uses_invalid_capability_from_self => {
3849            input = {
3850                let mut decl = new_component_decl();
3851                decl.uses = Some(vec![
3852                    fdecl::Use::Service(fdecl::UseService {
3853                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3854                        source_name: Some("fuchsia.some.library.SomeService".into()),
3855                        target_path: Some("/svc/foo".into()),
3856                        dependency_type: Some(fdecl::DependencyType::Strong),
3857                        ..Default::default()
3858                    }),
3859                    fdecl::Use::Protocol(fdecl::UseProtocol {
3860                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3861                        source_name: Some("fuchsia.some.library.SomeProtocol".into()),
3862                        target_path: Some("/svc/bar".into()),
3863                        dependency_type: Some(fdecl::DependencyType::Strong),
3864                        ..Default::default()
3865                    }),
3866                    fdecl::Use::Directory(fdecl::UseDirectory {
3867                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3868                        source_name: Some("dir".into()),
3869                        target_path: Some("/assets".into()),
3870                        dependency_type: Some(fdecl::DependencyType::Strong),
3871                        rights: Some(fio::Operations::CONNECT),
3872                        ..Default::default()
3873                    }),
3874                    fdecl::Use::Runner(fdecl::UseRunner {
3875                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3876                        source_name: Some("source_elf".into()),
3877                        ..Default::default()
3878                    }),
3879                    fdecl::Use::Config(fdecl::UseConfiguration {
3880                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3881                        source_name: Some("source_config".into()),
3882                        target_name: Some("config".into()),
3883                        type_: Some(fdecl::ConfigType {
3884                            layout: fdecl::ConfigTypeLayout::Bool,
3885                            parameters: Some(Vec::new()),
3886                            constraints: Vec::new(),
3887                        }),
3888                        ..Default::default()
3889                    }),
3890                    fdecl::Use::Protocol(fdecl::UseProtocol {
3891                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3892                        source_name: Some("fuchsia.some.library.SomeProtocol".into()),
3893                        source_dictionary: Some("dict/inner".into()),
3894                        target_path: Some("/svc/baz".into()),
3895                        dependency_type: Some(fdecl::DependencyType::Strong),
3896                        ..Default::default()
3897                    }),
3898                ]);
3899                decl
3900            },
3901            result = Err(ErrorList::new(vec![
3902                Error::invalid_capability(
3903                    DeclType::UseService,
3904                    "source",
3905                    "fuchsia.some.library.SomeService"),
3906                Error::invalid_capability(
3907                    DeclType::UseProtocol,
3908                    "source",
3909                    "fuchsia.some.library.SomeProtocol"),
3910                Error::invalid_capability(DeclType::UseDirectory, "source", "dir"),
3911                Error::invalid_capability(DeclType::UseRunner, "source", "source_elf"),
3912                Error::invalid_capability(DeclType::UseConfiguration, "source", "source_config"),
3913                Error::invalid_capability(DeclType::UseProtocol, "source", "dict"),
3914            ])),
3915        },
3916        test_validate_use_from_child_offer_to_child_strong_cycle => {
3917            input = {
3918                fdecl::Component {
3919                    capabilities: Some(vec![
3920                        fdecl::Capability::Service(fdecl::Service {
3921                            name: Some("a".to_string()),
3922                            source_path: Some("/a".to_string()),
3923                            ..Default::default()
3924                        })]),
3925                    uses: Some(vec![
3926                        fdecl::Use::Protocol(fdecl::UseProtocol {
3927                            dependency_type: Some(fdecl::DependencyType::Strong),
3928                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3929                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3930                            target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3931                            ..Default::default()
3932                        }),
3933                        fdecl::Use::Service(fdecl::UseService {
3934                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3935                            source_name: Some("service_name".to_string()),
3936                            target_path: Some("/svc/service_name".to_string()),
3937                            dependency_type: Some(fdecl::DependencyType::Strong),
3938                            ..Default::default()
3939                        }),
3940                        fdecl::Use::Directory(fdecl::UseDirectory {
3941                            dependency_type: Some(fdecl::DependencyType::Strong),
3942                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3943                            source_name: Some("DirectoryName".to_string()),
3944                            target_path: Some("/data/DirectoryName".to_string()),
3945                            rights: Some(fio::Operations::CONNECT),
3946                            subdir: None,
3947                            ..Default::default()
3948                        }),
3949                    ]),
3950                    offers: Some(vec![
3951                        fdecl::Offer::Service(fdecl::OfferService {
3952                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3953                            source_name: Some("a".to_string()),
3954                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
3955                            target_name: Some("a".to_string()),
3956                            ..Default::default()
3957                        })
3958                    ]),
3959                    children: Some(vec![
3960                        fdecl::Child {
3961                            name: Some("child".to_string()),
3962                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3963                            startup: Some(fdecl::StartupMode::Lazy),
3964                            on_terminate: None,
3965                            ..Default::default()
3966                        }
3967                    ]),
3968                    ..new_component_decl()
3969                }
3970            },
3971            result = Err(ErrorList::new(vec![
3972                Error::dependency_cycle("{{self -> child child -> self}}".to_string()),
3973            ])),
3974        },
3975        test_validate_use_from_child_storage_no_cycle => {
3976            input = {
3977                fdecl::Component {
3978                    capabilities: Some(vec![
3979                        fdecl::Capability::Storage(fdecl::Storage {
3980                            name: Some("cdata".to_string()),
3981                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None } )),
3982                            backing_dir: Some("minfs".to_string()),
3983                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
3984                            ..Default::default()
3985                        }),
3986                        fdecl::Capability::Storage(fdecl::Storage {
3987                            name: Some("pdata".to_string()),
3988                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
3989                            backing_dir: Some("minfs".to_string()),
3990                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
3991                            ..Default::default()
3992                        }),
3993                    ]),
3994                    uses: Some(vec![
3995                        fdecl::Use::Protocol(fdecl::UseProtocol {
3996                            dependency_type: Some(fdecl::DependencyType::Strong),
3997                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child1".to_string(), collection: None})),
3998                            source_name: Some("a".to_string()),
3999                            target_path: Some("/svc/a".to_string()),
4000                            ..Default::default()
4001                        }),
4002                    ]),
4003                    offers: Some(vec![
4004                        fdecl::Offer::Storage(fdecl::OfferStorage {
4005                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4006                            source_name: Some("cdata".to_string()),
4007                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4008                            target_name: Some("cdata".to_string()),
4009                            ..Default::default()
4010                        }),
4011                        fdecl::Offer::Storage(fdecl::OfferStorage {
4012                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4013                            source_name: Some("pdata".to_string()),
4014                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4015                            target_name: Some("pdata".to_string()),
4016                            ..Default::default()
4017                        }),
4018                    ]),
4019                    children: Some(vec![
4020                        fdecl::Child {
4021                            name: Some("child1".to_string()),
4022                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4023                            startup: Some(fdecl::StartupMode::Lazy),
4024                            on_terminate: None,
4025                            ..Default::default()
4026                        },
4027                        fdecl::Child {
4028                            name: Some("child2".to_string()),
4029                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4030                            startup: Some(fdecl::StartupMode::Lazy),
4031                            on_terminate: None,
4032                            ..Default::default()
4033                        }
4034                    ]),
4035                    ..new_component_decl()
4036                }
4037            },
4038            result = Ok(()),
4039        },
4040        test_validate_use_from_child_storage_cycle => {
4041            input = {
4042                fdecl::Component {
4043                    capabilities: Some(vec![
4044                        fdecl::Capability::Storage(fdecl::Storage {
4045                            name: Some("data".to_string()),
4046                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4047                            backing_dir: Some("minfs".to_string()),
4048                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4049                            ..Default::default()
4050                        }),
4051                    ]),
4052                    uses: Some(vec![
4053                        fdecl::Use::Protocol(fdecl::UseProtocol {
4054                            dependency_type: Some(fdecl::DependencyType::Strong),
4055                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4056                            source_name: Some("a".to_string()),
4057                            target_path: Some("/svc/a".to_string()),
4058                            ..Default::default()
4059                        }),
4060                    ]),
4061                    offers: Some(vec![
4062                        fdecl::Offer::Storage(fdecl::OfferStorage {
4063                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4064                            source_name: Some("data".to_string()),
4065                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4066                            target_name: Some("data".to_string()),
4067                            ..Default::default()
4068                        }),
4069                    ]),
4070                    children: Some(vec![
4071                        fdecl::Child {
4072                            name: Some("child".to_string()),
4073                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4074                            startup: Some(fdecl::StartupMode::Lazy),
4075                            on_terminate: None,
4076                            ..Default::default()
4077                        },
4078                    ]),
4079                    ..new_component_decl()
4080                }
4081            },
4082            result = Err(ErrorList::new(vec![
4083                Error::dependency_cycle("{{self -> capability data -> child child -> self}}".to_string()),
4084            ])),
4085        },
4086        test_validate_storage_strong_cycle_between_children => {
4087            input = {
4088                fdecl::Component {
4089                    capabilities: Some(vec![
4090                        fdecl::Capability::Storage(fdecl::Storage {
4091                            name: Some("data".to_string()),
4092                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None } )),
4093                            backing_dir: Some("minfs".to_string()),
4094                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4095                            ..Default::default()
4096                        })
4097                    ]),
4098                    offers: Some(vec![
4099                        fdecl::Offer::Storage(fdecl::OfferStorage {
4100                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4101                            source_name: Some("data".to_string()),
4102                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4103                            target_name: Some("data".to_string()),
4104                            ..Default::default()
4105                        }),
4106                        fdecl::Offer::Service(fdecl::OfferService {
4107                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4108                            source_name: Some("a".to_string()),
4109                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4110                            target_name: Some("a".to_string()),
4111                            ..Default::default()
4112                        }),
4113                    ]),
4114                    children: Some(vec![
4115                        fdecl::Child {
4116                            name: Some("child1".to_string()),
4117                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4118                            startup: Some(fdecl::StartupMode::Lazy),
4119                            on_terminate: None,
4120                            ..Default::default()
4121                        },
4122                        fdecl::Child {
4123                            name: Some("child2".to_string()),
4124                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4125                            startup: Some(fdecl::StartupMode::Lazy),
4126                            on_terminate: None,
4127                            ..Default::default()
4128                        }
4129                    ]),
4130                    ..new_component_decl()
4131                }
4132            },
4133            result = Err(ErrorList::new(vec![
4134                Error::dependency_cycle("{{child child1 -> capability data -> child child2 -> child child1}}".to_string()),
4135            ])),
4136        },
4137        test_validate_strong_cycle_between_children_through_environment_debug => {
4138            input = {
4139                fdecl::Component {
4140                    environments: Some(vec![
4141                        fdecl::Environment {
4142                            name: Some("env".to_string()),
4143                            extends: Some(fdecl::EnvironmentExtends::Realm),
4144                            debug_capabilities: Some(vec![
4145                                fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
4146                                    source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4147                                    source_name: Some("fuchsia.foo.Bar".to_string()),
4148                                    target_name: Some("fuchsia.foo.Bar".to_string()),
4149                                    ..Default::default()
4150                                }),
4151                            ]),
4152                            ..Default::default()
4153                        },
4154                    ]),
4155                    offers: Some(vec![
4156                        fdecl::Offer::Service(fdecl::OfferService {
4157                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4158                            source_name: Some("a".to_string()),
4159                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4160                            target_name: Some("a".to_string()),
4161                            ..Default::default()
4162                        }),
4163                    ]),
4164                    children: Some(vec![
4165                        fdecl::Child {
4166                            name: Some("child1".to_string()),
4167                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4168                            startup: Some(fdecl::StartupMode::Lazy),
4169                            on_terminate: None,
4170                            ..Default::default()
4171                        },
4172                        fdecl::Child {
4173                            name: Some("child2".to_string()),
4174                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4175                            startup: Some(fdecl::StartupMode::Lazy),
4176                            environment: Some("env".to_string()),
4177                            on_terminate: None,
4178                            ..Default::default()
4179                        }
4180                    ]),
4181                    ..new_component_decl()
4182                }
4183            },
4184            result = Err(ErrorList::new(vec![
4185                Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}".to_string()),
4186            ])),
4187        },
4188        test_validate_strong_cycle_between_children_through_environment_runner => {
4189            input = {
4190                fdecl::Component {
4191                    environments: Some(vec![
4192                        fdecl::Environment {
4193                            name: Some("env".to_string()),
4194                            extends: Some(fdecl::EnvironmentExtends::Realm),
4195                            runners: Some(vec![
4196                                fdecl::RunnerRegistration {
4197                                    source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4198                                    source_name: Some("coff".to_string()),
4199                                    target_name: Some("coff".to_string()),
4200                                    ..Default::default()
4201                                }
4202                            ]),
4203                            ..Default::default()
4204                        },
4205                    ]),
4206                    offers: Some(vec![
4207                        fdecl::Offer::Service(fdecl::OfferService {
4208                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4209                            source_name: Some("a".to_string()),
4210                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4211                            target_name: Some("a".to_string()),
4212                            ..Default::default()
4213                        }),
4214                    ]),
4215                    children: Some(vec![
4216                        fdecl::Child {
4217                            name: Some("child1".to_string()),
4218                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4219                            startup: Some(fdecl::StartupMode::Lazy),
4220                            on_terminate: None,
4221                            ..Default::default()
4222                        },
4223                        fdecl::Child {
4224                            name: Some("child2".to_string()),
4225                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4226                            startup: Some(fdecl::StartupMode::Lazy),
4227                            environment: Some("env".to_string()),
4228                            on_terminate: None,
4229                            ..Default::default()
4230                        }
4231                    ]),
4232                    ..new_component_decl()
4233                }
4234            },
4235            result = Err(ErrorList::new(vec![
4236                Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}".to_string()),
4237            ])),
4238        },
4239        test_validate_strong_cycle_between_children_through_environment_resolver => {
4240            input = {
4241                fdecl::Component {
4242                    environments: Some(vec![
4243                        fdecl::Environment {
4244                            name: Some("env".to_string()),
4245                            extends: Some(fdecl::EnvironmentExtends::Realm),
4246                            resolvers: Some(vec![
4247                                fdecl::ResolverRegistration {
4248                                    resolver: Some("gopher".to_string()),
4249                                    source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4250                                    scheme: Some("gopher".to_string()),
4251                                    ..Default::default()
4252                                }
4253                            ]),
4254                            ..Default::default()
4255                        },
4256                    ]),
4257                    offers: Some(vec![
4258                        fdecl::Offer::Service(fdecl::OfferService {
4259                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4260                            source_name: Some("a".to_string()),
4261                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4262                            target_name: Some("a".to_string()),
4263                            ..Default::default()
4264                        }),
4265                    ]),
4266                    children: Some(vec![
4267                        fdecl::Child {
4268                            name: Some("child1".to_string()),
4269                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4270                            startup: Some(fdecl::StartupMode::Lazy),
4271                            on_terminate: None,
4272                            ..Default::default()
4273                        },
4274                        fdecl::Child {
4275                            name: Some("child2".to_string()),
4276                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4277                            startup: Some(fdecl::StartupMode::Lazy),
4278                            environment: Some("env".to_string()),
4279                            on_terminate: None,
4280                            ..Default::default()
4281                        }
4282                    ]),
4283                    ..new_component_decl()
4284                }
4285            },
4286            result = Err(ErrorList::new(vec![
4287                Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}".to_string()),
4288            ])),
4289        },
4290        test_validate_strong_cycle_between_self_and_two_children => {
4291            input = {
4292                fdecl::Component {
4293                    capabilities: Some(vec![
4294                        fdecl::Capability::Protocol(fdecl::Protocol {
4295                            name: Some("fuchsia.foo.Bar".to_string()),
4296                            source_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4297                            ..Default::default()
4298                        })
4299                    ]),
4300                    offers: Some(vec![
4301                        fdecl::Offer::Protocol(fdecl::OfferProtocol {
4302                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4303                            source_name: Some("fuchsia.foo.Bar".to_string()),
4304                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4305                            target_name: Some("fuchsia.foo.Bar".to_string()),
4306                            dependency_type: Some(fdecl::DependencyType::Strong),
4307                            ..Default::default()
4308                        }),
4309                        fdecl::Offer::Protocol(fdecl::OfferProtocol {
4310                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4311                            source_name: Some("fuchsia.bar.Baz".to_string()),
4312                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4313                            target_name: Some("fuchsia.bar.Baz".to_string()),
4314                            dependency_type: Some(fdecl::DependencyType::Strong),
4315                            ..Default::default()
4316                        }),
4317                    ]),
4318                    uses: Some(vec![
4319                        fdecl::Use::Protocol(fdecl::UseProtocol {
4320                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child2".to_string(), collection: None})),
4321                            source_name: Some("fuchsia.baz.Foo".to_string()),
4322                            target_path: Some("/svc/fuchsia.baz.Foo".to_string()),
4323                            dependency_type: Some(fdecl::DependencyType::Strong),
4324                            ..Default::default()
4325                        }),
4326                    ]),
4327                    children: Some(vec![
4328                        fdecl::Child {
4329                            name: Some("child1".to_string()),
4330                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4331                            startup: Some(fdecl::StartupMode::Lazy),
4332                            on_terminate: None,
4333                            ..Default::default()
4334                        },
4335                        fdecl::Child {
4336                            name: Some("child2".to_string()),
4337                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4338                            startup: Some(fdecl::StartupMode::Lazy),
4339                            on_terminate: None,
4340                            ..Default::default()
4341                        }
4342                    ]),
4343                    ..new_component_decl()
4344                }
4345            },
4346            result = Err(ErrorList::new(vec![
4347                Error::dependency_cycle("{{self -> child child1 -> child child2 -> self}}".to_string()),
4348            ])),
4349        },
4350        test_validate_strong_cycle_with_self_storage => {
4351            input = {
4352                fdecl::Component {
4353                    capabilities: Some(vec![
4354                        fdecl::Capability::Storage(fdecl::Storage {
4355                            name: Some("data".to_string()),
4356                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4357                            backing_dir: Some("minfs".to_string()),
4358                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4359                            ..Default::default()
4360                        }),
4361                        fdecl::Capability::Directory(fdecl::Directory {
4362                            name: Some("minfs".to_string()),
4363                            source_path: Some("/minfs".to_string()),
4364                            rights: Some(fio::RW_STAR_DIR),
4365                            ..Default::default()
4366                        }),
4367                    ]),
4368                    offers: Some(vec![
4369                        fdecl::Offer::Storage(fdecl::OfferStorage {
4370                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4371                            source_name: Some("data".to_string()),
4372                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4373                            target_name: Some("data".to_string()),
4374                            ..Default::default()
4375                        }),
4376                    ]),
4377                    uses: Some(vec![
4378                        fdecl::Use::Protocol(fdecl::UseProtocol {
4379                            dependency_type: Some(fdecl::DependencyType::Strong),
4380                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4381                            source_name: Some("fuchsia.foo.Bar".to_string()),
4382                            target_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4383                            ..Default::default()
4384                        }),
4385                    ]),
4386                    children: Some(vec![
4387                        fdecl::Child {
4388                            name: Some("child".to_string()),
4389                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4390                            startup: Some(fdecl::StartupMode::Lazy),
4391                            ..Default::default()
4392                        },
4393                    ]),
4394                    ..new_component_decl()
4395                }
4396            },
4397            result = Err(ErrorList::new(vec![
4398                Error::dependency_cycle("{{self -> capability data -> child child -> self}}".to_string()),
4399            ])),
4400        },
4401        test_validate_strong_cycle_with_self_storage_admin_protocol => {
4402            input = {
4403                fdecl::Component {
4404                    capabilities: Some(vec![
4405                        fdecl::Capability::Storage(fdecl::Storage {
4406                            name: Some("data".to_string()),
4407                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4408                            backing_dir: Some("minfs".to_string()),
4409                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4410                            ..Default::default()
4411                        }),
4412                        fdecl::Capability::Directory(fdecl::Directory {
4413                            name: Some("minfs".to_string()),
4414                            source_path: Some("/minfs".to_string()),
4415                            rights: Some(fio::RW_STAR_DIR),
4416                            ..Default::default()
4417                        }),
4418                    ]),
4419                    offers: Some(vec![
4420                        fdecl::Offer::Protocol(fdecl::OfferProtocol {
4421                            source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef { name: "data".to_string() })),
4422                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4423                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4424                            target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4425                            dependency_type: Some(fdecl::DependencyType::Strong),
4426                            ..Default::default()
4427                        }),
4428                    ]),
4429                    uses: Some(vec![
4430                        fdecl::Use::Protocol(fdecl::UseProtocol {
4431                            dependency_type: Some(fdecl::DependencyType::Strong),
4432                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4433                            source_name: Some("fuchsia.foo.Bar".to_string()),
4434                            target_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4435                            ..Default::default()
4436                        }),
4437                    ]),
4438                    children: Some(vec![
4439                        fdecl::Child {
4440                            name: Some("child".to_string()),
4441                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4442                            startup: Some(fdecl::StartupMode::Lazy),
4443                            ..Default::default()
4444                        },
4445                    ]),
4446                    ..new_component_decl()
4447                }
4448            },
4449            result = Err(ErrorList::new(vec![
4450                Error::dependency_cycle("{{self -> capability data -> child child -> self}}".to_string()),
4451            ])),
4452        },
4453        test_validate_strong_cycle_with_dictionary => {
4454            input = fdecl::Component {
4455                offers: Some(vec![
4456                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
4457                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4458                        source_name: Some("dict".into()),
4459                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4460                            name: "a".into(),
4461                            collection: None,
4462                        })),
4463                        target_name: Some("dict".into()),
4464                        dependency_type: Some(fdecl::DependencyType::Strong),
4465                        ..Default::default()
4466                    }),
4467                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4468                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4469                            name: "b".into(),
4470                            collection: None,
4471                        })),
4472                        source_name: Some("1".into()),
4473                        target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4474                            name: "dict".into(),
4475                        })),
4476                        target_name: Some("1".into()),
4477                        dependency_type: Some(fdecl::DependencyType::Strong),
4478                        ..Default::default()
4479                    }),
4480                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4481                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4482                            name: "a".into(),
4483                            collection: None,
4484                        })),
4485                        source_name: Some("2".into()),
4486                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4487                            name: "b".into(),
4488                            collection: None,
4489                        })),
4490                        target_name: Some("2".into()),
4491                        dependency_type: Some(fdecl::DependencyType::Strong),
4492                        ..Default::default()
4493                    }),
4494                ]),
4495                children: Some(vec![
4496                    fdecl::Child {
4497                        name: Some("a".into()),
4498                        url: Some("fuchsia-pkg://child".into()),
4499                        startup: Some(fdecl::StartupMode::Lazy),
4500                        ..Default::default()
4501                    },
4502                    fdecl::Child {
4503                        name: Some("b".into()),
4504                        url: Some("fuchsia-pkg://child".into()),
4505                        startup: Some(fdecl::StartupMode::Lazy),
4506                        ..Default::default()
4507                    },
4508                ]),
4509                capabilities: Some(vec![
4510                    fdecl::Capability::Dictionary(fdecl::Dictionary {
4511                        name: Some("dict".into()),
4512                        ..Default::default()
4513                    }),
4514                ]),
4515                ..Default::default()
4516            },
4517            result = Err(ErrorList::new(vec![
4518                Error::dependency_cycle("{{child a -> child b -> capability dict -> child a}}".to_string()),
4519            ])),
4520        },
4521        test_validate_strong_cycle_with_dictionary_indirect => {
4522            input = fdecl::Component {
4523                offers: Some(vec![
4524                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4525                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4526                        source_name: Some("3".into()),
4527                        source_dictionary: Some("dict".into()),
4528                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4529                            name: "a".into(),
4530                            collection: None,
4531                        })),
4532                        target_name: Some("3".into()),
4533                        dependency_type: Some(fdecl::DependencyType::Strong),
4534                        ..Default::default()
4535                    }),
4536                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4537                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4538                            name: "b".into(),
4539                            collection: None,
4540                        })),
4541                        source_name: Some("1".into()),
4542                        target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4543                            name: "dict".into(),
4544                        })),
4545                        target_name: Some("1".into()),
4546                        dependency_type: Some(fdecl::DependencyType::Strong),
4547                        ..Default::default()
4548                    }),
4549                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4550                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4551                            name: "a".into(),
4552                            collection: None,
4553                        })),
4554                        source_name: Some("2".into()),
4555                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4556                            name: "b".into(),
4557                            collection: None,
4558                        })),
4559                        target_name: Some("2".into()),
4560                        dependency_type: Some(fdecl::DependencyType::Strong),
4561                        ..Default::default()
4562                    }),
4563                ]),
4564                children: Some(vec![
4565                    fdecl::Child {
4566                        name: Some("a".into()),
4567                        url: Some("fuchsia-pkg://child".into()),
4568                        startup: Some(fdecl::StartupMode::Lazy),
4569                        ..Default::default()
4570                    },
4571                    fdecl::Child {
4572                        name: Some("b".into()),
4573                        url: Some("fuchsia-pkg://child".into()),
4574                        startup: Some(fdecl::StartupMode::Lazy),
4575                        ..Default::default()
4576                    },
4577                ]),
4578                capabilities: Some(vec![
4579                    fdecl::Capability::Dictionary(fdecl::Dictionary {
4580                        name: Some("dict".into()),
4581                        ..Default::default()
4582                    }),
4583                ]),
4584                ..Default::default()
4585            },
4586            result = Err(ErrorList::new(vec![
4587                Error::dependency_cycle("{{child a -> child b -> capability dict -> child a}}".to_string()),
4588            ])),
4589        },
4590        test_validate_use_from_child_offer_to_child_weak_cycle => {
4591            input = {
4592                fdecl::Component {
4593                    capabilities: Some(vec![
4594                        fdecl::Capability::Service(fdecl::Service {
4595                            name: Some("a".to_string()),
4596                            source_path: Some("/a".to_string()),
4597                            ..Default::default()
4598                        })]),
4599                    uses: Some(vec![
4600                        fdecl::Use::Protocol(fdecl::UseProtocol {
4601                            dependency_type: Some(fdecl::DependencyType::Weak),
4602                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4603                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4604                            target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
4605                            ..Default::default()
4606                        }),
4607                        fdecl::Use::Service(fdecl::UseService {
4608                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4609                            source_name: Some("service_name".to_string()),
4610                            target_path: Some("/svc/service_name".to_string()),
4611                            dependency_type: Some(fdecl::DependencyType::Weak),
4612                            ..Default::default()
4613                        }),
4614                        fdecl::Use::Directory(fdecl::UseDirectory {
4615                            dependency_type: Some(fdecl::DependencyType::Weak),
4616                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4617                            source_name: Some("DirectoryName".to_string()),
4618                            target_path: Some("/data/DirectoryName".to_string()),
4619                            rights: Some(fio::Operations::CONNECT),
4620                            subdir: None,
4621                            ..Default::default()
4622                        }),
4623                    ]),
4624                    offers: Some(vec![
4625                        fdecl::Offer::Service(fdecl::OfferService {
4626                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4627                            source_name: Some("a".to_string()),
4628                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4629                            target_name: Some("a".to_string()),
4630                            ..Default::default()
4631                        })
4632                    ]),
4633                    children: Some(vec![
4634                        fdecl::Child {
4635                            name: Some("child".to_string()),
4636                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4637                            startup: Some(fdecl::StartupMode::Lazy),
4638                            on_terminate: None,
4639                            ..Default::default()
4640                        }
4641                    ]),
4642                    ..new_component_decl()
4643                }
4644            },
4645            result = Ok(()),
4646        },
4647        test_validate_expose_from_self_to_framework_and_parent => {
4648            input = {
4649                fdecl::Component {
4650                    capabilities: Some(vec![
4651                        fdecl::Capability::Protocol(fdecl::Protocol {
4652                            name: Some("a".to_string()),
4653                            source_path: Some("/a".to_string()),
4654                            ..Default::default()
4655                        }),
4656                    ]),
4657                    exposes: Some(vec![
4658                        fdecl::Expose::Protocol(fdecl::ExposeProtocol {
4659                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4660                            source_name: Some("a".to_string()),
4661                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4662                            target_name: Some("a".to_string()),
4663                            ..Default::default()
4664                        }),
4665                        fdecl::Expose::Protocol(fdecl::ExposeProtocol {
4666                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4667                            source_name: Some("a".to_string()),
4668                            target: Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})),
4669                            target_name: Some("a".to_string()),
4670                            ..Default::default()
4671                        }),
4672                    ]),
4673                    ..new_component_decl()
4674                }
4675            },
4676            result = Ok(()),
4677        },
4678        test_validate_use_from_not_child_weak => {
4679            input = {
4680                fdecl::Component {
4681                    uses: Some(vec![
4682                        fdecl::Use::Protocol(fdecl::UseProtocol {
4683                            dependency_type: Some(fdecl::DependencyType::Weak),
4684                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4685                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4686                            target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
4687                            ..Default::default()
4688                        }),
4689                    ]),
4690                    ..new_component_decl()
4691                }
4692            },
4693            result = Err(ErrorList::new(vec![
4694                Error::invalid_field(DeclType::UseProtocol, "dependency_type"),
4695            ])),
4696        },
4697        test_validate_event_stream_offer_valid_decls => {
4698            input = {
4699                let mut decl = new_component_decl();
4700                decl.offers = Some(vec![
4701                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4702                        source_name: Some("stopped".to_string()),
4703                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4704                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4705                        target_name: Some("stopped".to_string()),
4706                        ..Default::default()
4707                    }),
4708                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4709                        source_name: Some("started".to_string()),
4710                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4711                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4712                        target_name: Some("started".to_string()),
4713                        ..Default::default()
4714                    }),
4715                ]);
4716                decl.children = Some(vec![fdecl::Child{
4717                    name: Some("test".to_string()),
4718                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4719                    startup: Some(fdecl::StartupMode::Lazy),
4720                    on_terminate: None,
4721                    environment: None,
4722                    ..Default::default()
4723                },
4724                fdecl::Child{
4725                    name: Some("test2".to_string()),
4726                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4727                    startup: Some(fdecl::StartupMode::Lazy),
4728                    on_terminate: None,
4729                    environment: None,
4730                    ..Default::default()
4731                }
4732                ]);
4733                decl
4734            },
4735            result = Ok(()),
4736        },
4737        test_validate_event_stream_offer_to_framework_invalid => {
4738            input = {
4739                let mut decl = new_component_decl();
4740                decl.offers = Some(vec![
4741                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4742                        source_name: Some("stopped".to_string()),
4743                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4744                        target: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
4745                        target_name: Some("stopped".to_string()),
4746                        ..Default::default()
4747                    }),
4748                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4749                        source_name: Some("started".to_string()),
4750                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4751                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4752                        target_name: Some("started".to_string()),
4753                        ..Default::default()
4754                    }),
4755                ]);
4756                decl.children = Some(vec![fdecl::Child{
4757                    name: Some("test".to_string()),
4758                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4759                    startup: Some(fdecl::StartupMode::Lazy),
4760                    on_terminate: None,
4761                    environment: None,
4762                    ..Default::default()
4763                },
4764                fdecl::Child{
4765                    name: Some("test2".to_string()),
4766                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4767                    startup: Some(fdecl::StartupMode::Lazy),
4768                    on_terminate: None,
4769                    environment: None,
4770                    ..Default::default()
4771                }
4772                ]);
4773                decl
4774            },
4775            result = Err(ErrorList::new(vec![
4776                Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "target".to_string() }),
4777                Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "target".to_string() }),
4778            ])),
4779        },
4780        test_validate_event_stream_offer_to_scope_zero_length_invalid => {
4781            input = {
4782                let mut decl = new_component_decl();
4783                decl.offers = Some(vec![
4784                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4785                        source_name: Some("started".to_string()),
4786                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4787                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4788                        scope: Some(vec![]),
4789                        target_name: Some("started".to_string()),
4790                        ..Default::default()
4791                    }),
4792                ]);
4793                decl.children = Some(vec![fdecl::Child{
4794                    name: Some("test".to_string()),
4795                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4796                    startup: Some(fdecl::StartupMode::Lazy),
4797                    on_terminate: None,
4798                    environment: None,
4799                    ..Default::default()
4800                },
4801                fdecl::Child{
4802                    name: Some("test2".to_string()),
4803                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4804                    startup: Some(fdecl::StartupMode::Lazy),
4805                    on_terminate: None,
4806                    environment: None,
4807                    ..Default::default()
4808                }
4809                ]);
4810                decl
4811            },
4812            result = Err(ErrorList::new(vec![
4813                Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "scope".to_string() }),
4814            ])),
4815        },
4816        test_validate_event_stream_offer_to_scope_framework_invalid => {
4817            input = {
4818                let mut decl = new_component_decl();
4819                decl.offers = Some(vec![
4820                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4821                        source_name: Some("started".to_string()),
4822                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4823                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4824                        scope: Some(vec![fdecl::Ref::Framework(fdecl::FrameworkRef{})]),
4825                        target_name: Some("started".to_string()),
4826                        ..Default::default()
4827                    }),
4828                ]);
4829                decl.children = Some(vec![fdecl::Child{
4830                    name: Some("test".to_string()),
4831                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4832                    startup: Some(fdecl::StartupMode::Lazy),
4833                    on_terminate: None,
4834                    environment: None,
4835                    ..Default::default()
4836                },
4837                fdecl::Child{
4838                    name: Some("test2".to_string()),
4839                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4840                    startup: Some(fdecl::StartupMode::Lazy),
4841                    on_terminate: None,
4842                    environment: None,
4843                    ..Default::default()
4844                }
4845                ]);
4846                decl
4847            },
4848            result = Err(ErrorList::new(vec![
4849                Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "scope".to_string() }),
4850            ])),
4851        },
4852        test_validate_event_stream_offer_to_scope_valid => {
4853            input = {
4854                let mut decl = new_component_decl();
4855                decl.offers = Some(vec![
4856                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4857                        source_name: Some("started".to_string()),
4858                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4859                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4860                        scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
4861                        target_name: Some("started".to_string()),
4862                        ..Default::default()
4863                    }),
4864                ]);
4865                decl.children = Some(vec![fdecl::Child{
4866                    name: Some("test".to_string()),
4867                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4868                    startup: Some(fdecl::StartupMode::Lazy),
4869                    on_terminate: None,
4870                    environment: None,
4871                    ..Default::default()
4872                },
4873                fdecl::Child{
4874                    name: Some("test2".to_string()),
4875                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4876                    startup: Some(fdecl::StartupMode::Lazy),
4877                    on_terminate: None,
4878                    environment: None,
4879                    ..Default::default()
4880                }
4881                ]);
4882                decl
4883            },
4884            result = Ok(()),
4885        },
4886        test_validate_event_stream_offer_to_scope_with_capability_requested => {
4887            input = {
4888                let mut decl = new_component_decl();
4889                decl.offers = Some(vec![
4890                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4891                        source_name: Some("capability_requested".to_string()),
4892                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4893                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4894                        scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
4895                        target_name: Some("started".to_string()),
4896                        ..Default::default()
4897                    }),
4898                ]);
4899                decl.children = Some(vec![fdecl::Child{
4900                    name: Some("test".to_string()),
4901                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4902                    startup: Some(fdecl::StartupMode::Lazy),
4903                    on_terminate: None,
4904                    environment: None,
4905                    ..Default::default()
4906                },
4907                fdecl::Child{
4908                    name: Some("test2".to_string()),
4909                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4910                    startup: Some(fdecl::StartupMode::Lazy),
4911                    on_terminate: None,
4912                    environment: None,
4913                    ..Default::default()
4914                }
4915                ]);
4916                decl
4917            },
4918            result = Ok(()),
4919        },
4920        test_validate_event_stream_offer_with_no_source_name_invalid => {
4921            input = {
4922                let mut decl = new_component_decl();
4923                decl.offers = Some(vec![
4924                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4925                        source_name: None,
4926                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4927                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4928                        scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
4929                        target_name: Some("started".to_string()),
4930                        ..Default::default()
4931                    }),
4932                ]);
4933                decl.children = Some(vec![fdecl::Child{
4934                    name: Some("test".to_string()),
4935                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4936                    startup: Some(fdecl::StartupMode::Lazy),
4937                    on_terminate: None,
4938                    environment: None,
4939                    ..Default::default()
4940                },
4941                fdecl::Child{
4942                    name: Some("test2".to_string()),
4943                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4944                    startup: Some(fdecl::StartupMode::Lazy),
4945                    on_terminate: None,
4946                    environment: None,
4947                    ..Default::default()
4948                }
4949                ]);
4950                decl
4951            },
4952            result = Err(ErrorList::new(vec![
4953                Error::MissingField(DeclField { decl: DeclType::OfferEventStream, field: "source_name".to_string() }),
4954            ])),
4955        },
4956        test_validate_event_stream_offer_invalid_source => {
4957            input = {
4958                let mut decl = new_component_decl();
4959                decl.offers = Some(vec![
4960                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4961                        source_name: Some("stopped".to_string()),
4962                        source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
4963                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4964                        target_name: Some("stopped".to_string()),
4965                        ..Default::default()
4966                    }),
4967                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4968                        source_name: Some("started".to_string()),
4969                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4970                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4971                        target_name: Some("started".to_string()),
4972                        ..Default::default()
4973                    }),
4974                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4975                        source_name: Some("capability_requested".to_string()),
4976                        source: Some(fdecl::Ref::Debug(fdecl::DebugRef{})),
4977                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4978                        target_name: Some("capability_requested".to_string()),
4979                        ..Default::default()
4980                    }),
4981                ]);
4982                decl.children = Some(vec![fdecl::Child{
4983                    name: Some("test".to_string()),
4984                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4985                    startup: Some(fdecl::StartupMode::Lazy),
4986                    on_terminate: None,
4987                    environment: None,
4988                    ..Default::default()
4989                },
4990                fdecl::Child{
4991                    name: Some("test2".to_string()),
4992                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4993                    startup: Some(fdecl::StartupMode::Lazy),
4994                    on_terminate: None,
4995                    environment: None,
4996                    ..Default::default()
4997                }
4998                ]);
4999                decl
5000            },
5001            result = Err(ErrorList::new(vec![
5002                Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "source".to_string() }),
5003            ])),
5004        },
5005
5006        test_validate_event_stream_offer_missing_source => {
5007            input = {
5008                let mut decl = new_component_decl();
5009                decl.offers = Some(vec![
5010                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
5011                        source_name: Some("stopped".to_string()),
5012                        source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
5013                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5014                        target_name: Some("stopped".to_string()),
5015                        ..Default::default()
5016                    }),
5017                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
5018                        source_name: Some("started".to_string()),
5019                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5020                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5021                        target_name: Some("started".to_string()),
5022                        ..Default::default()
5023                    }),
5024                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
5025                        source_name: Some("capability_requested".to_string()),
5026                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5027                        target_name: Some("capability_requested".to_string()),
5028                        ..Default::default()
5029                    }),
5030                ]);
5031                decl.children = Some(vec![fdecl::Child{
5032                    name: Some("test".to_string()),
5033                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5034                    startup: Some(fdecl::StartupMode::Lazy),
5035                    on_terminate: None,
5036                    environment: None,
5037                    ..Default::default()
5038                },
5039                fdecl::Child{
5040                    name: Some("test2".to_string()),
5041                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
5042                    startup: Some(fdecl::StartupMode::Lazy),
5043                    on_terminate: None,
5044                    environment: None,
5045                    ..Default::default()
5046                }
5047                ]);
5048                decl
5049            },
5050            result = Err(ErrorList::new(vec![
5051                Error::MissingField(DeclField { decl: DeclType::OfferEventStream, field: "source".to_string() }),
5052            ])),
5053        },
5054        test_validate_event_stream_must_have_target_path => {
5055            input = {
5056                let mut decl = new_component_decl();
5057                decl.uses = Some(vec![
5058                    fdecl::Use::EventStream(fdecl::UseEventStream {
5059                        source_name: Some("bar".to_string()),
5060                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5061                        ..Default::default()
5062                    }),
5063                ]);
5064                decl
5065            },
5066            result = Err(ErrorList::new(vec![
5067                Error::MissingField(DeclField { decl: DeclType::UseEventStream, field: "target_path".to_string() })
5068            ])),
5069        },
5070        test_validate_event_stream_must_have_source_names => {
5071            input = {
5072                let mut decl = new_component_decl();
5073                decl.uses = Some(vec![
5074                    fdecl::Use::EventStream(fdecl::UseEventStream {
5075                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5076                        target_path: Some("/svc/something".to_string()),
5077                        ..Default::default()
5078                    }),
5079                ]);
5080                decl
5081            },
5082            result = Err(ErrorList::new(vec![
5083                Error::MissingField(DeclField { decl: DeclType::UseEventStream, field: "source_name".to_string() })
5084            ])),
5085        },
5086        test_validate_event_stream_scope_must_be_child_or_collection => {
5087            input = {
5088                let mut decl = new_component_decl();
5089                decl.uses = Some(vec![
5090                    fdecl::Use::EventStream(fdecl::UseEventStream {
5091                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5092                        target_path: Some("/svc/something".to_string()),
5093                        source_name: Some("some_source".to_string()),
5094                        scope: Some(vec![fdecl::Ref::Framework(fdecl::FrameworkRef{})]),
5095                        ..Default::default()
5096                    }),
5097                ]);
5098                decl
5099            },
5100            result = Err(ErrorList::new(vec![
5101                Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "scope".to_string() })
5102            ])),
5103        },
5104        test_validate_event_stream_source_must_be_parent_or_child => {
5105            input = {
5106                let mut decl = new_component_decl();
5107                decl.uses = Some(vec![
5108                    fdecl::Use::EventStream(fdecl::UseEventStream {
5109                        source: Some(fdecl::Ref::Debug(fdecl::DebugRef{})),
5110                        target_path: Some("/svc/something".to_string()),
5111                        source_name: Some("some_source".to_string()),
5112                        scope: Some(vec![]),
5113                        ..Default::default()
5114                    }),
5115                    fdecl::Use::EventStream(fdecl::UseEventStream {
5116                        source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
5117                        target_path: Some("/svc/something_else".to_string()),
5118                        source_name: Some("some_source".to_string()),
5119                        scope: Some(vec![]),
5120                        ..Default::default()
5121                    }),
5122                    fdecl::Use::EventStream(fdecl::UseEventStream {
5123                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5124                        target_path: Some("/svc/yet_something_else".to_string()),
5125                        source_name: Some("some_source".to_string()),
5126                        scope: Some(vec![]),
5127                        ..Default::default()
5128                    }),
5129                ]);
5130                decl
5131            },
5132            result = Err(ErrorList::new(vec![
5133                Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() }),
5134                Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() }),
5135                Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() })
5136            ])),
5137        },
5138        test_validate_no_runner => {
5139            input = {
5140                let mut decl = new_component_decl();
5141                decl.program = Some(fdecl::Program {
5142                    runner: None,
5143                    info: Some(fdata::Dictionary {
5144                        entries: None,
5145                        ..Default::default()
5146                    }),
5147                    ..Default::default()
5148                });
5149                decl
5150            },
5151            result = Err(ErrorList::new(vec![
5152                Error::MissingRunner,
5153            ])),
5154        },
5155        test_validate_uses_runner => {
5156            input = {
5157                let mut decl = new_component_decl();
5158                decl.program = Some(fdecl::Program {
5159                    runner: None,
5160                    info: Some(fdata::Dictionary {
5161                        entries: None,
5162                        ..Default::default()
5163                    }),
5164                    ..Default::default()
5165                });
5166                decl.uses = Some(vec![
5167                    fdecl::Use::Runner(fdecl::UseRunner {
5168                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5169                        source_name: Some("runner".to_string()),
5170                        ..Default::default()
5171                    }),
5172                ]);
5173                decl
5174            },
5175            result = Ok(()),
5176        },
5177        test_validate_program_and_uses_runner_match => {
5178            input = {
5179                let mut decl = new_component_decl();
5180                decl.program = Some(fdecl::Program {
5181                    runner: Some("runner".to_string()),
5182                    info: Some(fdata::Dictionary {
5183                        entries: None,
5184                        ..Default::default()
5185                    }),
5186                    ..Default::default()
5187                });
5188                decl.uses = Some(vec![
5189                    fdecl::Use::Runner(fdecl::UseRunner {
5190                        source: Some(fdecl::Ref::Environment(fdecl::EnvironmentRef {})),
5191                        source_name: Some("runner".to_string()),
5192                        ..Default::default()
5193                    }),
5194                ]);
5195                decl
5196            },
5197            result = Ok(()),
5198        },
5199        test_validate_runner_names_conflict => {
5200            input = {
5201                let mut decl = new_component_decl();
5202                decl.program = Some(fdecl::Program {
5203                    runner: Some("runner".to_string()),
5204                    info: Some(fdata::Dictionary {
5205                        entries: None,
5206                        ..Default::default()
5207                    }),
5208                    ..Default::default()
5209                });
5210                decl.uses = Some(vec![
5211                    fdecl::Use::Runner(fdecl::UseRunner {
5212                        source: Some(fdecl::Ref::Environment(fdecl::EnvironmentRef {})),
5213                        source_name: Some("other.runner".to_string()),
5214                        ..Default::default()
5215                    }),
5216                ]);
5217                decl
5218            },
5219            result = Err(ErrorList::new(vec![
5220                Error::ConflictingRunners,
5221            ])),
5222        },
5223        test_validate_uses_runner_not_environement => {
5224            input = {
5225                let mut decl = new_component_decl();
5226                decl.program = Some(fdecl::Program {
5227                    runner: Some("runner".to_string()),
5228                    info: Some(fdata::Dictionary {
5229                        entries: None,
5230                        ..Default::default()
5231                    }),
5232                    ..Default::default()
5233                });
5234                decl.uses = Some(vec![
5235                    fdecl::Use::Runner(fdecl::UseRunner {
5236                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5237                        source_name: Some("runner".to_string()),
5238                        ..Default::default()
5239                    }),
5240                ]);
5241                decl
5242            },
5243            result = Err(ErrorList::new(vec![
5244                Error::ConflictingRunners,
5245            ])),
5246        },
5247        test_validate_uses_long_identifiers => {
5248            input = {
5249                let mut decl = new_component_decl();
5250                decl.program = Some(fdecl::Program {
5251                    runner: Some("elf".to_string()),
5252                    info: Some(fdata::Dictionary {
5253                        entries: None,
5254                        ..Default::default()
5255                    }),
5256                    ..Default::default()
5257                });
5258                decl.uses = Some(vec![
5259                    fdecl::Use::Service(fdecl::UseService {
5260                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5261                        source_name: Some(format!("{}", "a".repeat(256))),
5262                        target_path: Some("/a".repeat(2048)),
5263                        dependency_type: Some(fdecl::DependencyType::Strong),
5264                        ..Default::default()
5265                    }),
5266                    fdecl::Use::Protocol(fdecl::UseProtocol {
5267                        dependency_type: Some(fdecl::DependencyType::Strong),
5268                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5269                        source_name: Some(format!("{}", "a".repeat(256))),
5270                        target_path: Some("/b".repeat(2048)),
5271                        ..Default::default()
5272                    }),
5273                    fdecl::Use::Directory(fdecl::UseDirectory {
5274                        dependency_type: Some(fdecl::DependencyType::Strong),
5275                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5276                        source_name: Some(format!("{}", "a".repeat(256))),
5277                        target_path: Some("/c".repeat(2048)),
5278                        rights: Some(fio::Operations::CONNECT),
5279                        subdir: None,
5280                        ..Default::default()
5281                    }),
5282                    fdecl::Use::Storage(fdecl::UseStorage {
5283                        source_name: Some("cache".to_string()),
5284                        target_path: Some("/d".repeat(2048)),
5285                        ..Default::default()
5286                    }),
5287                ]);
5288                decl
5289            },
5290            result = Err(ErrorList::new(vec![
5291                Error::field_too_long(DeclType::UseService, "source_name"),
5292                Error::field_too_long(DeclType::UseService, "target_path"),
5293                Error::field_too_long(DeclType::UseProtocol, "source_name"),
5294                Error::field_too_long(DeclType::UseProtocol, "target_path"),
5295                Error::field_too_long(DeclType::UseDirectory, "source_name"),
5296                Error::field_too_long(DeclType::UseDirectory, "target_path"),
5297                Error::field_too_long(DeclType::UseStorage, "target_path"),
5298            ])),
5299        },
5300        test_validate_conflicting_paths => {
5301            input = {
5302                let mut decl = new_component_decl();
5303                decl.uses = Some(vec![
5304                    fdecl::Use::Service(fdecl::UseService {
5305                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5306                        source_name: Some("foo".to_string()),
5307                        target_path: Some("/bar".to_string()),
5308                        dependency_type: Some(fdecl::DependencyType::Strong),
5309                        ..Default::default()
5310                    }),
5311                    fdecl::Use::Protocol(fdecl::UseProtocol {
5312                        dependency_type: Some(fdecl::DependencyType::Strong),
5313                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5314                        source_name: Some("space".to_string()),
5315                        target_path: Some("/bar".to_string()),
5316                        ..Default::default()
5317                    }),
5318                    fdecl::Use::Directory(fdecl::UseDirectory {
5319                        dependency_type: Some(fdecl::DependencyType::Strong),
5320                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5321                        source_name: Some("crow".to_string()),
5322                        target_path: Some("/bar".to_string()),
5323                        rights: Some(fio::Operations::CONNECT),
5324                        subdir: None,
5325                        ..Default::default()
5326                    }),
5327                ]);
5328                decl
5329            },
5330            result = Err(ErrorList::new(vec![
5331                Error::duplicate_field(DeclType::UseProtocol, "target_path", "/bar"),
5332                Error::duplicate_field(DeclType::UseDirectory, "target_path", "/bar"),
5333            ])),
5334        },
5335        // exposes
5336        test_validate_exposes_empty => {
5337            input = {
5338                let mut decl = new_component_decl();
5339                decl.exposes = Some(vec![
5340                    fdecl::Expose::Service(fdecl::ExposeService {
5341                        source: None,
5342                        source_name: None,
5343                        target_name: None,
5344                        target: None,
5345                        ..Default::default()
5346                    }),
5347                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5348                        source: None,
5349                        source_name: None,
5350                        target_name: None,
5351                        target: None,
5352                        ..Default::default()
5353                    }),
5354                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5355                        source: None,
5356                        source_name: None,
5357                        target_name: None,
5358                        target: None,
5359                        rights: None,
5360                        subdir: None,
5361                        ..Default::default()
5362                    }),
5363                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5364                        source: None,
5365                        source_name: None,
5366                        target: None,
5367                        target_name: None,
5368                        ..Default::default()
5369                    }),
5370                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5371                        source: None,
5372                        source_name: None,
5373                        target: None,
5374                        target_name: None,
5375                        ..Default::default()
5376                    }),
5377                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5378                        ..Default::default()
5379                    }),
5380                ]);
5381                decl
5382            },
5383            result = Err(ErrorList::new(vec![
5384                Error::missing_field(DeclType::ExposeService, "source"),
5385                Error::missing_field(DeclType::ExposeService, "target"),
5386                Error::missing_field(DeclType::ExposeService, "source_name"),
5387                Error::missing_field(DeclType::ExposeService, "target_name"),
5388                Error::missing_field(DeclType::ExposeProtocol, "source"),
5389                Error::missing_field(DeclType::ExposeProtocol, "target"),
5390                Error::missing_field(DeclType::ExposeProtocol, "source_name"),
5391                Error::missing_field(DeclType::ExposeProtocol, "target_name"),
5392                Error::missing_field(DeclType::ExposeDirectory, "source"),
5393                Error::missing_field(DeclType::ExposeDirectory, "target"),
5394                Error::missing_field(DeclType::ExposeDirectory, "source_name"),
5395                Error::missing_field(DeclType::ExposeDirectory, "target_name"),
5396                Error::missing_field(DeclType::ExposeRunner, "source"),
5397                Error::missing_field(DeclType::ExposeRunner, "target"),
5398                Error::missing_field(DeclType::ExposeRunner, "source_name"),
5399                Error::missing_field(DeclType::ExposeRunner, "target_name"),
5400                Error::missing_field(DeclType::ExposeResolver, "source"),
5401                Error::missing_field(DeclType::ExposeResolver, "target"),
5402                Error::missing_field(DeclType::ExposeResolver, "source_name"),
5403                Error::missing_field(DeclType::ExposeResolver, "target_name"),
5404                Error::missing_field(DeclType::ExposeDictionary, "source"),
5405                Error::missing_field(DeclType::ExposeDictionary, "target"),
5406                Error::missing_field(DeclType::ExposeDictionary, "source_name"),
5407                Error::missing_field(DeclType::ExposeDictionary, "target_name"),
5408            ])),
5409        },
5410        test_validate_exposes_extraneous => {
5411            input = {
5412                let mut decl = new_component_decl();
5413                decl.exposes = Some(vec![
5414                    fdecl::Expose::Service(fdecl::ExposeService {
5415                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5416                            name: "logger".to_string(),
5417                            collection: Some("modular".to_string()),
5418                        })),
5419                        source_name: Some("logger".to_string()),
5420                        target_name: Some("logger".to_string()),
5421                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5422                        ..Default::default()
5423                    }),
5424                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5425                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5426                            name: "logger".to_string(),
5427                            collection: Some("modular".to_string()),
5428                        })),
5429                        source_name: Some("legacy_logger".to_string()),
5430                        target_name: Some("legacy_logger".to_string()),
5431                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5432                        ..Default::default()
5433                    }),
5434                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5435                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5436                            name: "netstack".to_string(),
5437                            collection: Some("modular".to_string()),
5438                        })),
5439                        source_name: Some("data".to_string()),
5440                        target_name: Some("data".to_string()),
5441                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5442                        rights: Some(fio::Operations::CONNECT),
5443                        subdir: None,
5444                        ..Default::default()
5445                    }),
5446                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5447                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5448                            name: "netstack".to_string(),
5449                            collection: Some("modular".to_string()),
5450                        })),
5451                        source_name: Some("elf".to_string()),
5452                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5453                        target_name: Some("elf".to_string()),
5454                        ..Default::default()
5455                    }),
5456                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5457                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5458                            name: "netstack".to_string(),
5459                            collection: Some("modular".to_string()),
5460                        })),
5461                        source_name: Some("pkg".to_string()),
5462                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5463                        target_name: Some("pkg".to_string()),
5464                        ..Default::default()
5465                    }),
5466                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5467                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5468                            name: "netstack".to_string(),
5469                            collection: Some("modular".to_string()),
5470                        })),
5471                        source_name: Some("dict".to_string()),
5472                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5473                        target_name: Some("dict".to_string()),
5474                        ..Default::default()
5475                    }),
5476                ]);
5477                decl
5478            },
5479            result = Err(ErrorList::new(vec![
5480                Error::extraneous_field(DeclType::ExposeService, "source.child.collection"),
5481                Error::extraneous_field(DeclType::ExposeProtocol, "source.child.collection"),
5482                Error::extraneous_field(DeclType::ExposeDirectory, "source.child.collection"),
5483                Error::extraneous_field(DeclType::ExposeRunner, "source.child.collection"),
5484                Error::extraneous_field(DeclType::ExposeResolver, "source.child.collection"),
5485                Error::extraneous_field(DeclType::ExposeDictionary, "source.child.collection"),
5486            ])),
5487        },
5488        test_validate_exposes_invalid_identifiers => {
5489            input = {
5490                let mut decl = new_component_decl();
5491                decl.exposes = Some(vec![
5492                    fdecl::Expose::Service(fdecl::ExposeService {
5493                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5494                            name: "^bad".to_string(),
5495                            collection: None,
5496                        })),
5497                        source_name: Some("foo/".to_string()),
5498                        target_name: Some("/".to_string()),
5499                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5500                        ..Default::default()
5501                    }),
5502                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5503                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5504                            name: "^bad".to_string(),
5505                            collection: None,
5506                        })),
5507                        source_name: Some("foo/".to_string()),
5508                        target_name: Some("/".to_string()),
5509                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5510                        ..Default::default()
5511                    }),
5512                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5513                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5514                            name: "^bad".to_string(),
5515                            collection: None,
5516                        })),
5517                        source_name: Some("foo/".to_string()),
5518                        target_name: Some("/".to_string()),
5519                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5520                        rights: Some(fio::Operations::CONNECT),
5521                        subdir: Some("/foo".to_string()),
5522                        ..Default::default()
5523                    }),
5524                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5525                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5526                            name: "^bad".to_string(),
5527                            collection: None,
5528                        })),
5529                        source_name: Some("/path".to_string()),
5530                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5531                        target_name: Some("elf!".to_string()),
5532                        ..Default::default()
5533                    }),
5534                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5535                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5536                            name: "^bad".to_string(),
5537                            collection: None,
5538                        })),
5539                        source_name: Some("/path".to_string()),
5540                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5541                        target_name: Some("pkg!".to_string()),
5542                        ..Default::default()
5543                    }),
5544                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5545                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5546                            name: "^bad".to_string(),
5547                            collection: None,
5548                        })),
5549                        source_name: Some("/path".to_string()),
5550                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5551                        target_name: Some("pkg!".to_string()),
5552                        ..Default::default()
5553                    }),
5554                ]);
5555                decl
5556            },
5557            result = Err(ErrorList::new(vec![
5558                Error::invalid_field(DeclType::ExposeService, "source.child.name"),
5559                Error::invalid_field(DeclType::ExposeService, "source_name"),
5560                Error::invalid_field(DeclType::ExposeService, "target_name"),
5561                Error::invalid_field(DeclType::ExposeProtocol, "source.child.name"),
5562                Error::invalid_field(DeclType::ExposeProtocol, "source_name"),
5563                Error::invalid_field(DeclType::ExposeProtocol, "target_name"),
5564                Error::invalid_field(DeclType::ExposeDirectory, "source.child.name"),
5565                Error::invalid_field(DeclType::ExposeDirectory, "source_name"),
5566                Error::invalid_field(DeclType::ExposeDirectory, "target_name"),
5567                Error::invalid_field(DeclType::ExposeDirectory, "subdir"),
5568                Error::invalid_field(DeclType::ExposeRunner, "source.child.name"),
5569                Error::invalid_field(DeclType::ExposeRunner, "source_name"),
5570                Error::invalid_field(DeclType::ExposeRunner, "target_name"),
5571                Error::invalid_field(DeclType::ExposeResolver, "source.child.name"),
5572                Error::invalid_field(DeclType::ExposeResolver, "source_name"),
5573                Error::invalid_field(DeclType::ExposeResolver, "target_name"),
5574                Error::invalid_field(DeclType::ExposeDictionary, "source.child.name"),
5575                Error::invalid_field(DeclType::ExposeDictionary, "source_name"),
5576                Error::invalid_field(DeclType::ExposeDictionary, "target_name"),
5577            ])),
5578        },
5579        test_validate_exposes_invalid_source_target => {
5580            input = {
5581                let mut decl = new_component_decl();
5582                decl.children = Some(vec![fdecl::Child{
5583                    name: Some("logger".to_string()),
5584                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5585                    startup: Some(fdecl::StartupMode::Lazy),
5586                    on_terminate: None,
5587                    environment: None,
5588                    ..Default::default()
5589                }]);
5590                decl.exposes = Some(vec![
5591                    fdecl::Expose::Service(fdecl::ExposeService {
5592                        source: None,
5593                        source_name: Some("a".to_string()),
5594                        target_name: Some("b".to_string()),
5595                        target: None,
5596                        ..Default::default()
5597                    }),
5598                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5599                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5600                        source_name: Some("c".to_string()),
5601                        target_name: Some("d".to_string()),
5602                        target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5603                        ..Default::default()
5604                    }),
5605                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5606                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "z".to_string()})),
5607                        source_name: Some("e".to_string()),
5608                        target_name: Some("f".to_string()),
5609                        target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "z".to_string()})),
5610                        rights: Some(fio::Operations::CONNECT),
5611                        subdir: None,
5612                        ..Default::default()
5613                    }),
5614                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5615                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5616                        source_name: Some("g".to_string()),
5617                        target_name: Some("h".to_string()),
5618                        target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5619                        rights: Some(fio::Operations::CONNECT),
5620                        subdir: None,
5621                        ..Default::default()
5622                    }),
5623                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5624                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5625                        source_name: Some("i".to_string()),
5626                        target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5627                        target_name: Some("j".to_string()),
5628                        ..Default::default()
5629                    }),
5630                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5631                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5632                        source_name: Some("k".to_string()),
5633                        target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5634                        target_name: Some("l".to_string()),
5635                        ..Default::default()
5636                    }),
5637                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5638                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5639                            name: "logger".to_string(),
5640                            collection: None,
5641                        })),
5642                        source_name: Some("m".to_string()),
5643                        target_name: Some("n".to_string()),
5644                        target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5645                        ..Default::default()
5646                    }),
5647                ]);
5648                decl
5649            },
5650            result = Err(ErrorList::new(vec![
5651                Error::missing_field(DeclType::ExposeService, "source"),
5652                Error::missing_field(DeclType::ExposeService, "target"),
5653                Error::invalid_field(DeclType::ExposeProtocol, "source"),
5654                Error::invalid_field(DeclType::ExposeProtocol, "target"),
5655                Error::invalid_field(DeclType::ExposeDirectory, "source"),
5656                Error::invalid_field(DeclType::ExposeDirectory, "target"),
5657                Error::invalid_field(DeclType::ExposeDirectory, "source"),
5658                Error::invalid_field(DeclType::ExposeDirectory, "target"),
5659                Error::invalid_field(DeclType::ExposeRunner, "source"),
5660                Error::invalid_field(DeclType::ExposeRunner, "target"),
5661                Error::invalid_field(DeclType::ExposeResolver, "source"),
5662                Error::invalid_field(DeclType::ExposeResolver, "target"),
5663                Error::invalid_field(DeclType::ExposeDictionary, "target"),
5664            ])),
5665        },
5666        test_validate_exposes_invalid_source_collection => {
5667            input = {
5668                let mut decl = new_component_decl();
5669                decl.collections = Some(vec![fdecl::Collection{
5670                    name: Some("col".to_string()),
5671                    durability: Some(fdecl::Durability::Transient),
5672                    allowed_offers: None,
5673                    allow_long_names: None,
5674                    ..Default::default()
5675                }]);
5676                decl.exposes = Some(vec![
5677                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5678                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
5679                        source_name: Some("a".to_string()),
5680                        target_name: Some("a".to_string()),
5681                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5682                        ..Default::default()
5683                    }),
5684                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5685                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5686                        source_name: Some("b".to_string()),
5687                        target_name: Some("b".to_string()),
5688                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5689                        rights: Some(fio::Operations::CONNECT),
5690                        subdir: None,
5691                        ..Default::default()
5692                    }),
5693                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5694                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5695                        source_name: Some("c".to_string()),
5696                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5697                        target_name: Some("c".to_string()),
5698                        ..Default::default()
5699                    }),
5700                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5701                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5702                        source_name: Some("d".to_string()),
5703                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5704                        target_name: Some("d".to_string()),
5705                        ..Default::default()
5706                    }),
5707                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5708                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5709                        source_name: Some("e".to_string()),
5710                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5711                        target_name: Some("e".to_string()),
5712                        ..Default::default()
5713                    }),
5714                ]);
5715                decl
5716            },
5717            result = Err(ErrorList::new(vec![
5718                Error::invalid_field(DeclType::ExposeProtocol, "source"),
5719                Error::invalid_field(DeclType::ExposeDirectory, "source"),
5720                Error::invalid_field(DeclType::ExposeRunner, "source"),
5721                Error::invalid_field(DeclType::ExposeResolver, "source"),
5722                Error::invalid_field(DeclType::ExposeDictionary, "source"),
5723            ])),
5724        },
5725        test_validate_exposes_sources_collection => {
5726            input = {
5727                let mut decl = new_component_decl();
5728                decl.collections = Some(vec![
5729                    fdecl::Collection {
5730                        name: Some("col".to_string()),
5731                        durability: Some(fdecl::Durability::Transient),
5732                        allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
5733                        allow_long_names: None,
5734                        ..Default::default()
5735                    }
5736                ]);
5737                decl.exposes = Some(vec![
5738                    fdecl::Expose::Service(fdecl::ExposeService {
5739                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
5740                        source_name: Some("a".to_string()),
5741                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5742                        target_name: Some("a".to_string()),
5743                        ..Default::default()
5744                    })
5745                ]);
5746                decl
5747            },
5748            result = Ok(()),
5749        },
5750        test_validate_exposes_long_identifiers => {
5751            input = {
5752                let mut decl = new_component_decl();
5753                decl.exposes = Some(vec![
5754                    fdecl::Expose::Service(fdecl::ExposeService {
5755                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5756                            name: "b".repeat(256),
5757                            collection: None,
5758                        })),
5759                        source_name: Some(format!("{}", "a".repeat(1025))),
5760                        target_name: Some(format!("{}", "b".repeat(1025))),
5761                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5762                        ..Default::default()
5763                    }),
5764                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5765                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5766                            name: "b".repeat(256),
5767                            collection: None,
5768                        })),
5769                        source_name: Some(format!("{}", "a".repeat(256))),
5770                        target_name: Some(format!("{}", "b".repeat(256))),
5771                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5772                        ..Default::default()
5773                    }),
5774                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5775                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5776                            name: "b".repeat(256),
5777                            collection: None,
5778                        })),
5779                        source_name: Some(format!("{}", "a".repeat(256))),
5780                        target_name: Some(format!("{}", "b".repeat(256))),
5781                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5782                        rights: Some(fio::Operations::CONNECT),
5783                        subdir: None,
5784                        ..Default::default()
5785                    }),
5786                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5787                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5788                            name: "b".repeat(256),
5789                            collection: None,
5790                        })),
5791                        source_name: Some("a".repeat(256)),
5792                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5793                        target_name: Some("b".repeat(256)),
5794                        ..Default::default()
5795                    }),
5796                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5797                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5798                            name: "b".repeat(256),
5799                            collection: None,
5800                        })),
5801                        source_name: Some("a".repeat(256)),
5802                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5803                        target_name: Some("b".repeat(256)),
5804                        ..Default::default()
5805                    }),
5806                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5807                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5808                            name: "b".repeat(256),
5809                            collection: None,
5810                        })),
5811                        source_name: Some("a".repeat(256)),
5812                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5813                        target_name: Some("b".repeat(256)),
5814                        ..Default::default()
5815                    }),
5816                ]);
5817                decl
5818            },
5819            result = Err(ErrorList::new(vec![
5820                Error::field_too_long(DeclType::ExposeService, "source.child.name"),
5821                Error::field_too_long(DeclType::ExposeService, "source_name"),
5822                Error::field_too_long(DeclType::ExposeService, "target_name"),
5823                Error::field_too_long(DeclType::ExposeProtocol, "source.child.name"),
5824                Error::field_too_long(DeclType::ExposeProtocol, "source_name"),
5825                Error::field_too_long(DeclType::ExposeProtocol, "target_name"),
5826                Error::field_too_long(DeclType::ExposeDirectory, "source.child.name"),
5827                Error::field_too_long(DeclType::ExposeDirectory, "source_name"),
5828                Error::field_too_long(DeclType::ExposeDirectory, "target_name"),
5829                Error::field_too_long(DeclType::ExposeRunner, "source.child.name"),
5830                Error::field_too_long(DeclType::ExposeRunner, "source_name"),
5831                Error::field_too_long(DeclType::ExposeRunner, "target_name"),
5832                Error::field_too_long(DeclType::ExposeResolver, "source.child.name"),
5833                Error::field_too_long(DeclType::ExposeResolver, "source_name"),
5834                Error::field_too_long(DeclType::ExposeResolver, "target_name"),
5835                Error::field_too_long(DeclType::ExposeDictionary, "source.child.name"),
5836                Error::field_too_long(DeclType::ExposeDictionary, "source_name"),
5837                Error::field_too_long(DeclType::ExposeDictionary, "target_name"),
5838            ])),
5839        },
5840        test_validate_exposes_invalid_child => {
5841            input = {
5842                let mut decl = new_component_decl();
5843                decl.exposes = Some(vec![
5844                    fdecl::Expose::Service(fdecl::ExposeService {
5845                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5846                            name: "netstack".to_string(),
5847                            collection: None,
5848                        })),
5849                        source_name: Some("fuchsia.logger.Log".to_string()),
5850                        target_name: Some("fuchsia.logger.Log".to_string()),
5851                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5852                        ..Default::default()
5853                    }),
5854                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5855                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5856                            name: "netstack".to_string(),
5857                            collection: None,
5858                        })),
5859                        source_name: Some("fuchsia.logger.LegacyLog".to_string()),
5860                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
5861                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5862                        ..Default::default()
5863                    }),
5864                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5865                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5866                            name: "netstack".to_string(),
5867                            collection: None,
5868                        })),
5869                        source_name: Some("data".to_string()),
5870                        target_name: Some("data".to_string()),
5871                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5872                        rights: Some(fio::Operations::CONNECT),
5873                        subdir: None,
5874                        ..Default::default()
5875                    }),
5876                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5877                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5878                            name: "netstack".to_string(),
5879                            collection: None,
5880                        })),
5881                        source_name: Some("elf".to_string()),
5882                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5883                        target_name: Some("elf".to_string()),
5884                        ..Default::default()
5885                    }),
5886                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5887                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5888                            name: "netstack".to_string(),
5889                            collection: None,
5890                        })),
5891                        source_name: Some("pkg".to_string()),
5892                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5893                        target_name: Some("pkg".to_string()),
5894                        ..Default::default()
5895                    }),
5896                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5897                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5898                            name: "netstack".to_string(),
5899                            collection: None,
5900                        })),
5901                        source_name: Some("dict".to_string()),
5902                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5903                        target_name: Some("dict".to_string()),
5904                        ..Default::default()
5905                    }),
5906                ]);
5907                decl
5908            },
5909            result = Err(ErrorList::new(vec![
5910                Error::invalid_child(DeclType::ExposeService, "source", "netstack"),
5911                Error::invalid_child(DeclType::ExposeProtocol, "source", "netstack"),
5912                Error::invalid_child(DeclType::ExposeDirectory, "source", "netstack"),
5913                Error::invalid_child(DeclType::ExposeRunner, "source", "netstack"),
5914                Error::invalid_child(DeclType::ExposeResolver, "source", "netstack"),
5915                Error::invalid_child(DeclType::ExposeDictionary, "source", "netstack"),
5916            ])),
5917        },
5918        test_validate_exposes_invalid_source_capability => {
5919            input = {
5920                fdecl::Component {
5921                    exposes: Some(vec![
5922                        fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5923                            source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
5924                                name: "this-storage-doesnt-exist".to_string(),
5925                            })),
5926                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
5927                            target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
5928                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5929                            ..Default::default()
5930                        }),
5931                    ]),
5932                    ..new_component_decl()
5933                }
5934            },
5935            result = Err(ErrorList::new(vec![
5936                Error::invalid_capability(DeclType::ExposeProtocol, "source", "this-storage-doesnt-exist"),
5937            ])),
5938        },
5939        test_validate_exposes_duplicate_target => {
5940            input = {
5941                let mut decl = new_component_decl();
5942                decl.exposes = Some(vec![
5943                    fdecl::Expose::Service(fdecl::ExposeService {
5944                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
5945                            name: "coll".into(),
5946                        })),
5947                        source_name: Some("netstack".to_string()),
5948                        target_name: Some("fuchsia.net.Stack".to_string()),
5949                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5950                        ..Default::default()
5951                    }),
5952                    fdecl::Expose::Service(fdecl::ExposeService {
5953                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
5954                            name: "coll2".into(),
5955                        })),
5956                        source_name: Some("netstack2".to_string()),
5957                        target_name: Some("fuchsia.net.Stack".to_string()),
5958                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5959                        ..Default::default()
5960                    }),
5961                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5962                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5963                        source_name: Some("fonts".to_string()),
5964                        target_name: Some("fuchsia.fonts.Provider".to_string()),
5965                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5966                        ..Default::default()
5967                    }),
5968                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5969                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5970                        source_name: Some("fonts2".to_string()),
5971                        target_name: Some("fuchsia.fonts.Provider".to_string()),
5972                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5973                        ..Default::default()
5974                    }),
5975                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5976                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5977                        source_name: Some("assets".to_string()),
5978                        target_name: Some("stuff".to_string()),
5979                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5980                        rights: None,
5981                        subdir: None,
5982                        ..Default::default()
5983                    }),
5984                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5985                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5986                        source_name: Some("assets2".to_string()),
5987                        target_name: Some("stuff".to_string()),
5988                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5989                        rights: None,
5990                        subdir: None,
5991                        ..Default::default()
5992                    }),
5993                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5994                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5995                        source_name: Some("source_elf".to_string()),
5996                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5997                        target_name: Some("elf".to_string()),
5998                        ..Default::default()
5999                    }),
6000                    fdecl::Expose::Runner(fdecl::ExposeRunner {
6001                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6002                        source_name: Some("source_elf".to_string()),
6003                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6004                        target_name: Some("elf".to_string()),
6005                        ..Default::default()
6006                    }),
6007                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
6008                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6009                        source_name: Some("source_pkg".to_string()),
6010                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6011                        target_name: Some("pkg".to_string()),
6012                        ..Default::default()
6013                    }),
6014                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
6015                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6016                        source_name: Some("source_pkg".to_string()),
6017                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6018                        target_name: Some("pkg".to_string()),
6019                        ..Default::default()
6020                    }),
6021                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
6022                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6023                        source_name: Some("source_dict".to_string()),
6024                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6025                        target_name: Some("dict".to_string()),
6026                        ..Default::default()
6027                    }),
6028                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
6029                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6030                        source_name: Some("source_dict".to_string()),
6031                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6032                        target_name: Some("dict".to_string()),
6033                        ..Default::default()
6034                    }),
6035                ]);
6036                decl.collections = Some(vec![
6037                    fdecl::Collection {
6038                        name: Some("coll".into()),
6039                        durability: Some(fdecl::Durability::Transient),
6040                        ..Default::default()
6041                    },
6042                    fdecl::Collection {
6043                        name: Some("coll2".into()),
6044                        durability: Some(fdecl::Durability::Transient),
6045                        ..Default::default()
6046                    },
6047                ]);
6048                decl.capabilities = Some(vec![
6049                    fdecl::Capability::Service(fdecl::Service {
6050                        name: Some("netstack".to_string()),
6051                        source_path: Some("/path".to_string()),
6052                        ..Default::default()
6053                    }),
6054                    fdecl::Capability::Service(fdecl::Service {
6055                        name: Some("netstack2".to_string()),
6056                        source_path: Some("/path".to_string()),
6057                        ..Default::default()
6058                    }),
6059                    fdecl::Capability::Protocol(fdecl::Protocol {
6060                        name: Some("fonts".to_string()),
6061                        source_path: Some("/path".to_string()),
6062                        ..Default::default()
6063                    }),
6064                    fdecl::Capability::Protocol(fdecl::Protocol {
6065                        name: Some("fonts2".to_string()),
6066                        source_path: Some("/path".to_string()),
6067                        ..Default::default()
6068                    }),
6069                    fdecl::Capability::Directory(fdecl::Directory {
6070                        name: Some("assets".to_string()),
6071                        source_path: Some("/path".to_string()),
6072                        rights: Some(fio::Operations::CONNECT),
6073                        ..Default::default()
6074                    }),
6075                    fdecl::Capability::Directory(fdecl::Directory {
6076                        name: Some("assets2".to_string()),
6077                        source_path: Some("/path".to_string()),
6078                        rights: Some(fio::Operations::CONNECT),
6079                        ..Default::default()
6080                    }),
6081                    fdecl::Capability::Runner(fdecl::Runner {
6082                        name: Some("source_elf".to_string()),
6083                        source_path: Some("/path".to_string()),
6084                        ..Default::default()
6085                    }),
6086                    fdecl::Capability::Resolver(fdecl::Resolver {
6087                        name: Some("source_pkg".to_string()),
6088                        source_path: Some("/path".to_string()),
6089                        ..Default::default()
6090                    }),
6091                    fdecl::Capability::Dictionary(fdecl::Dictionary {
6092                        name: Some("source_dict".to_string()),
6093                        ..Default::default()
6094                    }),
6095                ]);
6096                decl
6097            },
6098            result = Err(ErrorList::new(vec![
6099                // Duplicate services are allowed.
6100                Error::duplicate_field(DeclType::ExposeProtocol, "target_name",
6101                                    "fuchsia.fonts.Provider"),
6102                Error::duplicate_field(DeclType::ExposeDirectory, "target_name",
6103                                    "stuff"),
6104                Error::duplicate_field(DeclType::ExposeRunner, "target_name",
6105                                    "elf"),
6106                Error::duplicate_field(DeclType::ExposeResolver, "target_name", "pkg"),
6107                Error::duplicate_field(DeclType::ExposeDictionary, "target_name", "dict"),
6108            ])),
6109        },
6110        test_validate_exposes_invalid_capability_from_self => {
6111            input = {
6112                let mut decl = new_component_decl();
6113                decl.exposes = Some(vec![
6114                    fdecl::Expose::Service(fdecl::ExposeService {
6115                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6116                        source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
6117                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6118                        target_name: Some("foo".to_string()),
6119                        ..Default::default()
6120                    }),
6121                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6122                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6123                        source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
6124                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6125                        target_name: Some("bar".to_string()),
6126                        ..Default::default()
6127                    }),
6128                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
6129                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6130                        source_name: Some("dir".to_string()),
6131                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6132                        target_name: Some("assets".to_string()),
6133                        rights: None,
6134                        subdir: None,
6135                        ..Default::default()
6136                    }),
6137                    fdecl::Expose::Runner(fdecl::ExposeRunner {
6138                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6139                        source_name: Some("source_elf".to_string()),
6140                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6141                        target_name: Some("elf".to_string()),
6142                        ..Default::default()
6143                    }),
6144                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
6145                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6146                        source_name: Some("source_pkg".to_string()),
6147                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6148                        target_name: Some("pkg".to_string()),
6149                        ..Default::default()
6150                    }),
6151                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
6152                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6153                        source_name: Some("source_dict".to_string()),
6154                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6155                        target_name: Some("dict".to_string()),
6156                        ..Default::default()
6157                    }),
6158                    fdecl::Expose::Config(fdecl::ExposeConfiguration {
6159                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6160                        source_name: Some("source_config".to_string()),
6161                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6162                        target_name: Some("config".to_string()),
6163                        ..Default::default()
6164                    }),
6165                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6166                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6167                        source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
6168                        source_dictionary: Some("dict/inner".to_string()),
6169                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6170                        target_name: Some("baz".to_string()),
6171                        ..Default::default()
6172                    }),
6173                ]);
6174                decl
6175            },
6176            result = Err(ErrorList::new(vec![
6177                Error::invalid_capability(
6178                    DeclType::ExposeService,
6179                    "source",
6180                    "fuchsia.some.library.SomeProtocol"),
6181                Error::invalid_capability(
6182                    DeclType::ExposeProtocol,
6183                    "source",
6184                    "fuchsia.some.library.SomeProtocol"),
6185                Error::invalid_capability(DeclType::ExposeDirectory, "source", "dir"),
6186                Error::invalid_capability(DeclType::ExposeRunner, "source", "source_elf"),
6187                Error::invalid_capability(DeclType::ExposeResolver, "source", "source_pkg"),
6188                Error::invalid_capability(DeclType::ExposeDictionary, "source", "source_dict"),
6189                Error::invalid_capability(DeclType::ExposeConfig, "source", "source_config"),
6190                Error::invalid_capability(DeclType::ExposeProtocol, "source", "dict"),
6191            ])),
6192        },
6193
6194        test_validate_exposes_availability_service => {
6195            input = {
6196                let mut decl = generate_expose_different_source_and_availability_decl(
6197                    |source, availability, target_name|
6198                        fdecl::Expose::Service(fdecl::ExposeService {
6199                            source: Some(source),
6200                            source_name: Some("fuchsia.examples.Echo".to_string()),
6201                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6202                            target_name: Some(target_name.to_string()),
6203                            availability: Some(availability),
6204                            ..Default::default()
6205                        })
6206                );
6207                decl.capabilities = Some(vec![
6208                    fdecl::Capability::Service(fdecl::Service {
6209                        name: Some("fuchsia.examples.Echo".to_string()),
6210                        source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
6211                        ..Default::default()
6212                    }),
6213                ]);
6214                decl
6215            },
6216            result = {
6217                Err(ErrorList::new(vec![
6218                    Error::availability_must_be_optional(
6219                        DeclType::ExposeService,
6220                        "availability",
6221                        Some(&"fuchsia.examples.Echo".to_string()),
6222                    ),
6223                    Error::availability_must_be_optional(
6224                        DeclType::ExposeService,
6225                        "availability",
6226                        Some(&"fuchsia.examples.Echo".to_string()),
6227                    ),
6228                ]))
6229            },
6230        },
6231        test_validate_exposes_availability_protocol => {
6232            input = {
6233                let mut decl = generate_expose_different_source_and_availability_decl(
6234                    |source, availability, target_name|
6235                        fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6236                            source: Some(source),
6237                            source_name: Some("fuchsia.examples.Echo".to_string()),
6238                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6239                            target_name: Some(target_name.to_string()),
6240                            availability: Some(availability),
6241                            ..Default::default()
6242                        })
6243                );
6244                decl.capabilities = Some(vec![
6245                    fdecl::Capability::Protocol(fdecl::Protocol {
6246                        name: Some("fuchsia.examples.Echo".to_string()),
6247                        source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
6248                        ..Default::default()
6249                    }),
6250                ]);
6251                decl
6252            },
6253            result = {
6254                Err(ErrorList::new(vec![
6255                    Error::availability_must_be_optional(
6256                        DeclType::ExposeProtocol,
6257                        "availability",
6258                        Some(&"fuchsia.examples.Echo".to_string()),
6259                    ),
6260                    Error::availability_must_be_optional(
6261                        DeclType::ExposeProtocol,
6262                        "availability",
6263                        Some(&"fuchsia.examples.Echo".to_string()),
6264                    ),
6265                ]))
6266            },
6267        },
6268        test_validate_exposes_availability_directory => {
6269            input = {
6270                let mut decl = generate_expose_different_source_and_availability_decl(
6271                    |source, availability, target_name|
6272                        fdecl::Expose::Directory(fdecl::ExposeDirectory {
6273                            source: Some(source),
6274                            source_name: Some("fuchsia.examples.Echo".to_string()),
6275                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6276                            target_name: Some(target_name.to_string()),
6277                            availability: Some(availability),
6278                            ..Default::default()
6279                        })
6280                );
6281                decl.capabilities = Some(vec![
6282                    fdecl::Capability::Directory(fdecl::Directory {
6283                        name: Some("fuchsia.examples.Echo".to_string()),
6284                        source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
6285                        rights: Some(fio::Operations::READ_BYTES),
6286                        ..Default::default()
6287                    }),
6288                ]);
6289                decl
6290            },
6291            result = {
6292                Err(ErrorList::new(vec![
6293                    Error::availability_must_be_optional(
6294                        DeclType::ExposeDirectory,
6295                        "availability",
6296                        Some(&"fuchsia.examples.Echo".to_string()),
6297                    ),
6298                    Error::availability_must_be_optional(
6299                        DeclType::ExposeDirectory,
6300                        "availability",
6301                        Some(&"fuchsia.examples.Echo".to_string()),
6302                    ),
6303                ]))
6304            },
6305        },
6306
6307        // offers
6308        test_validate_offers_empty => {
6309            input = {
6310                let mut decl = new_component_decl();
6311                decl.offers = Some(vec![
6312                    fdecl::Offer::Service(fdecl::OfferService {
6313                        source: None,
6314                        source_name: None,
6315                        target: None,
6316                        target_name: None,
6317                        ..Default::default()
6318                    }),
6319                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6320                        source: None,
6321                        source_name: None,
6322                        target: None,
6323                        target_name: None,
6324                        dependency_type: None,
6325                        ..Default::default()
6326                    }),
6327                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6328                        source: None,
6329                        source_name: None,
6330                        target: None,
6331                        target_name: None,
6332                        rights: None,
6333                        subdir: None,
6334                        dependency_type: None,
6335                        ..Default::default()
6336                    }),
6337                    fdecl::Offer::Storage(fdecl::OfferStorage {
6338                        source_name: None,
6339                        source: None,
6340                        target: None,
6341                        target_name: None,
6342                        ..Default::default()
6343                    }),
6344                    fdecl::Offer::Runner(fdecl::OfferRunner {
6345                        source: None,
6346                        source_name: None,
6347                        target: None,
6348                        target_name: None,
6349                        ..Default::default()
6350                    }),
6351                    fdecl::Offer::Resolver(fdecl::OfferResolver {
6352                        ..Default::default()
6353                    }),
6354                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6355                        ..Default::default()
6356                    }),
6357                ]);
6358                decl
6359            },
6360            // TODO(dgonyeo): we need to handle the availability being unset until we've soft
6361            // migrated all manifests
6362            result = Err(ErrorList::new(vec![
6363                Error::missing_field(DeclType::OfferService, "source"),
6364                Error::missing_field(DeclType::OfferService, "source_name"),
6365                Error::missing_field(DeclType::OfferService, "target"),
6366                Error::missing_field(DeclType::OfferService, "target_name"),
6367                //Error::missing_field(DeclType::OfferService, "availability"),
6368                Error::missing_field(DeclType::OfferProtocol, "source"),
6369                Error::missing_field(DeclType::OfferProtocol, "source_name"),
6370                Error::missing_field(DeclType::OfferProtocol, "target"),
6371                Error::missing_field(DeclType::OfferProtocol, "target_name"),
6372                Error::missing_field(DeclType::OfferProtocol, "dependency_type"),
6373                //Error::missing_field(DeclType::OfferProtocol, "availability"),
6374                Error::missing_field(DeclType::OfferDirectory, "source"),
6375                Error::missing_field(DeclType::OfferDirectory, "source_name"),
6376                Error::missing_field(DeclType::OfferDirectory, "target"),
6377                Error::missing_field(DeclType::OfferDirectory, "target_name"),
6378                Error::missing_field(DeclType::OfferDirectory, "dependency_type"),
6379                //Error::missing_field(DeclType::OfferDirectory, "availability"),
6380                Error::missing_field(DeclType::OfferStorage, "source"),
6381                Error::missing_field(DeclType::OfferStorage, "source_name"),
6382                Error::missing_field(DeclType::OfferStorage, "target"),
6383                Error::missing_field(DeclType::OfferStorage, "target_name"),
6384                //Error::missing_field(DeclType::OfferStorage, "availability"),
6385                Error::missing_field(DeclType::OfferRunner, "source"),
6386                Error::missing_field(DeclType::OfferRunner, "source_name"),
6387                Error::missing_field(DeclType::OfferRunner, "target"),
6388                Error::missing_field(DeclType::OfferRunner, "target_name"),
6389                //Error::missing_field(DeclType::OfferRunner, "availability"),
6390                Error::missing_field(DeclType::OfferResolver, "source"),
6391                Error::missing_field(DeclType::OfferResolver, "source_name"),
6392                Error::missing_field(DeclType::OfferResolver, "target"),
6393                Error::missing_field(DeclType::OfferResolver, "target_name"),
6394                Error::missing_field(DeclType::OfferDictionary, "source"),
6395                Error::missing_field(DeclType::OfferDictionary, "source_name"),
6396                Error::missing_field(DeclType::OfferDictionary, "target"),
6397                Error::missing_field(DeclType::OfferDictionary, "target_name"),
6398                Error::missing_field(DeclType::OfferDictionary, "dependency_type"),
6399            ])),
6400        },
6401        test_validate_offers_long_identifiers => {
6402            input = {
6403                let mut decl = new_component_decl();
6404                decl.offers = Some(vec![
6405                    fdecl::Offer::Service(fdecl::OfferService {
6406                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6407                            name: "a".repeat(256),
6408                            collection: None,
6409                        })),
6410                        source_name: Some(format!("{}", "a".repeat(256))),
6411                        target: Some(fdecl::Ref::Child(
6412                        fdecl::ChildRef {
6413                            name: "b".repeat(256),
6414                            collection: None,
6415                        }
6416                        )),
6417                        target_name: Some(format!("{}", "b".repeat(256))),
6418                        ..Default::default()
6419                    }),
6420                    fdecl::Offer::Service(fdecl::OfferService {
6421                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6422                        source_name: Some("a".to_string()),
6423                        target: Some(fdecl::Ref::Collection(
6424                        fdecl::CollectionRef {
6425                            name: "b".repeat(256),
6426                        }
6427                        )),
6428                        target_name: Some(format!("{}", "b".repeat(256))),
6429                        ..Default::default()
6430                    }),
6431                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6432                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6433                            name: "a".repeat(256),
6434                            collection: None,
6435                        })),
6436                        source_name: Some(format!("{}", "a".repeat(256))),
6437                        target: Some(fdecl::Ref::Child(
6438                        fdecl::ChildRef {
6439                            name: "b".repeat(256),
6440                            collection: None,
6441                        }
6442                        )),
6443                        target_name: Some(format!("{}", "b".repeat(256))),
6444                        dependency_type: Some(fdecl::DependencyType::Strong),
6445                        ..Default::default()
6446                    }),
6447                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6448                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6449                        source_name: Some("a".to_string()),
6450                        target: Some(fdecl::Ref::Collection(
6451                        fdecl::CollectionRef {
6452                            name: "b".repeat(256),
6453                        }
6454                        )),
6455                        target_name: Some(format!("{}", "b".repeat(256))),
6456                        dependency_type: Some(fdecl::DependencyType::Weak),
6457                        ..Default::default()
6458                    }),
6459                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6460                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6461                            name: "a".repeat(256),
6462                            collection: None,
6463                        })),
6464                        source_name: Some(format!("{}", "a".repeat(256))),
6465                        target: Some(fdecl::Ref::Child(
6466                        fdecl::ChildRef {
6467                            name: "b".repeat(256),
6468                            collection: None,
6469                        }
6470                        )),
6471                        target_name: Some(format!("{}", "b".repeat(256))),
6472                        rights: Some(fio::Operations::CONNECT),
6473                        subdir: None,
6474                        dependency_type: Some(fdecl::DependencyType::Strong),
6475                        ..Default::default()
6476                    }),
6477                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6478                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6479                        source_name: Some("a".to_string()),
6480                        target: Some(fdecl::Ref::Collection(
6481                        fdecl::CollectionRef {
6482                            name: "b".repeat(256),
6483                        }
6484                        )),
6485                        target_name: Some(format!("{}", "b".repeat(256))),
6486                        rights: Some(fio::Operations::CONNECT),
6487                        subdir: None,
6488                        dependency_type: Some(fdecl::DependencyType::Weak),
6489                        ..Default::default()
6490                    }),
6491                    fdecl::Offer::Storage(fdecl::OfferStorage {
6492                        source_name: Some("data".to_string()),
6493                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6494                        target: Some(fdecl::Ref::Child(
6495                            fdecl::ChildRef {
6496                                name: "b".repeat(256),
6497                                collection: None,
6498                            }
6499                        )),
6500                        target_name: Some("data".to_string()),
6501                        ..Default::default()
6502                    }),
6503                    fdecl::Offer::Storage(fdecl::OfferStorage {
6504                        source_name: Some("data".to_string()),
6505                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6506                        target: Some(fdecl::Ref::Collection(
6507                            fdecl::CollectionRef { name: "b".repeat(256) }
6508                        )),
6509                        target_name: Some("data".to_string()),
6510                        ..Default::default()
6511                    }),
6512                    fdecl::Offer::Runner(fdecl::OfferRunner {
6513                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6514                            name: "a".repeat(256),
6515                            collection: None,
6516                        })),
6517                        source_name: Some("b".repeat(256)),
6518                        target: Some(fdecl::Ref::Collection(
6519                        fdecl::CollectionRef {
6520                            name: "c".repeat(256),
6521                        }
6522                        )),
6523                        target_name: Some("d".repeat(256)),
6524                        ..Default::default()
6525                    }),
6526                    fdecl::Offer::Resolver(fdecl::OfferResolver {
6527                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6528                            name: "a".repeat(256),
6529                            collection: None,
6530                        })),
6531                        source_name: Some("b".repeat(256)),
6532                        target: Some(fdecl::Ref::Collection(
6533                            fdecl::CollectionRef {
6534                                name: "c".repeat(256),
6535                            }
6536                        )),
6537                        target_name: Some("d".repeat(256)),
6538                        ..Default::default()
6539                    }),
6540                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6541                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6542                            name: "a".repeat(256),
6543                            collection: None,
6544                        })),
6545                        source_name: Some("b".repeat(256)),
6546                        target: Some(fdecl::Ref::Collection(
6547                            fdecl::CollectionRef {
6548                                name: "c".repeat(256),
6549                            }
6550                        )),
6551                        target_name: Some("d".repeat(256)),
6552                        dependency_type: Some(fdecl::DependencyType::Strong),
6553                        ..Default::default()
6554                    }),
6555                ]);
6556                decl
6557            },
6558            result = Err(ErrorList::new(vec![
6559                Error::field_too_long(DeclType::OfferService, "source.child.name"),
6560                Error::field_too_long(DeclType::OfferService, "source_name"),
6561                Error::field_too_long(DeclType::OfferService, "target.child.name"),
6562                Error::field_too_long(DeclType::OfferService, "target_name"),
6563                Error::field_too_long(DeclType::OfferService, "target.collection.name"),
6564                Error::field_too_long(DeclType::OfferService, "target_name"),
6565                Error::field_too_long(DeclType::OfferProtocol, "source.child.name"),
6566                Error::field_too_long(DeclType::OfferProtocol, "source_name"),
6567                Error::field_too_long(DeclType::OfferProtocol, "target.child.name"),
6568                Error::field_too_long(DeclType::OfferProtocol, "target_name"),
6569                Error::field_too_long(DeclType::OfferProtocol, "target.collection.name"),
6570                Error::field_too_long(DeclType::OfferProtocol, "target_name"),
6571                Error::field_too_long(DeclType::OfferDirectory, "source.child.name"),
6572                Error::field_too_long(DeclType::OfferDirectory, "source_name"),
6573                Error::field_too_long(DeclType::OfferDirectory, "target.child.name"),
6574                Error::field_too_long(DeclType::OfferDirectory, "target_name"),
6575                Error::field_too_long(DeclType::OfferDirectory, "target.collection.name"),
6576                Error::field_too_long(DeclType::OfferDirectory, "target_name"),
6577                Error::field_too_long(DeclType::OfferStorage, "target.child.name"),
6578                Error::field_too_long(DeclType::OfferStorage, "target.collection.name"),
6579                Error::field_too_long(DeclType::OfferRunner, "source.child.name"),
6580                Error::field_too_long(DeclType::OfferRunner, "source_name"),
6581                Error::field_too_long(DeclType::OfferRunner, "target.collection.name"),
6582                Error::field_too_long(DeclType::OfferRunner, "target_name"),
6583                Error::field_too_long(DeclType::OfferResolver, "source.child.name"),
6584                Error::field_too_long(DeclType::OfferResolver, "source_name"),
6585                Error::field_too_long(DeclType::OfferResolver, "target.collection.name"),
6586                Error::field_too_long(DeclType::OfferResolver, "target_name"),
6587                Error::field_too_long(DeclType::OfferDictionary, "source.child.name"),
6588                Error::field_too_long(DeclType::OfferDictionary, "source_name"),
6589                Error::field_too_long(DeclType::OfferDictionary, "target.collection.name"),
6590                Error::field_too_long(DeclType::OfferDictionary, "target_name"),
6591            ])),
6592        },
6593        test_validate_offers_extraneous => {
6594            input = {
6595                let mut decl = new_component_decl();
6596                decl.offers = Some(vec![
6597                    fdecl::Offer::Service(fdecl::OfferService {
6598                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6599                            name: "logger".to_string(),
6600                            collection: Some("modular".to_string()),
6601                        })),
6602                        source_name: Some("fuchsia.logger.Log".to_string()),
6603                        target: Some(fdecl::Ref::Child(
6604                            fdecl::ChildRef {
6605                                name: "netstack".to_string(),
6606                                collection: Some("modular".to_string()),
6607                            }
6608                        )),
6609                        target_name: Some("fuchsia.logger.Log".to_string()),
6610                        ..Default::default()
6611                    }),
6612                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6613                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6614                            name: "logger".to_string(),
6615                            collection: Some("modular".to_string()),
6616                        })),
6617                        source_name: Some("fuchsia.logger.Log".to_string()),
6618                        target: Some(fdecl::Ref::Child(
6619                            fdecl::ChildRef {
6620                                name: "netstack".to_string(),
6621                                collection: Some("modular".to_string()),
6622                            }
6623                        )),
6624                        target_name: Some("fuchsia.logger.Log".to_string()),
6625                        dependency_type: Some(fdecl::DependencyType::Strong),
6626                        ..Default::default()
6627                    }),
6628                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6629                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6630                            name: "logger".to_string(),
6631                            collection: Some("modular".to_string()),
6632                        })),
6633                        source_name: Some("assets".to_string()),
6634                        target: Some(fdecl::Ref::Child(
6635                            fdecl::ChildRef {
6636                                name: "netstack".to_string(),
6637                                collection: Some("modular".to_string()),
6638                            }
6639                        )),
6640                        target_name: Some("assets".to_string()),
6641                        rights: Some(fio::Operations::CONNECT),
6642                        subdir: None,
6643                        dependency_type: Some(fdecl::DependencyType::Weak),
6644                        ..Default::default()
6645                    }),
6646                    fdecl::Offer::Storage(fdecl::OfferStorage {
6647                        source_name: Some("data".to_string()),
6648                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{ })),
6649                        target: Some(fdecl::Ref::Child(
6650                            fdecl::ChildRef {
6651                                name: "netstack".to_string(),
6652                                collection: Some("modular".to_string()),
6653                            }
6654                        )),
6655                        target_name: Some("data".to_string()),
6656                        ..Default::default()
6657                    }),
6658                    fdecl::Offer::Runner(fdecl::OfferRunner {
6659                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6660                            name: "logger".to_string(),
6661                            collection: Some("modular".to_string()),
6662                        })),
6663                        source_name: Some("elf".to_string()),
6664                        target: Some(fdecl::Ref::Child(
6665                            fdecl::ChildRef {
6666                                name: "netstack".to_string(),
6667                                collection: Some("modular".to_string()),
6668                            }
6669                        )),
6670                        target_name: Some("elf".to_string()),
6671                        ..Default::default()
6672                    }),
6673                    fdecl::Offer::Resolver(fdecl::OfferResolver {
6674                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6675                            name: "logger".to_string(),
6676                            collection: Some("modular".to_string()),
6677                        })),
6678                        source_name: Some("pkg".to_string()),
6679                        target: Some(fdecl::Ref::Child(
6680                            fdecl::ChildRef {
6681                                name: "netstack".to_string(),
6682                                collection: Some("modular".to_string()),
6683                            }
6684                        )),
6685                        target_name: Some("pkg".to_string()),
6686                        ..Default::default()
6687                    }),
6688                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6689                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6690                            name: "logger".to_string(),
6691                            collection: Some("modular".to_string()),
6692                        })),
6693                        source_name: Some("dict".to_string()),
6694                        target: Some(fdecl::Ref::Child(
6695                            fdecl::ChildRef {
6696                                name: "netstack".to_string(),
6697                                collection: Some("modular".to_string()),
6698                            }
6699                        )),
6700                        target_name: Some("dict".to_string()),
6701                        dependency_type: Some(fdecl::DependencyType::Strong),
6702                        ..Default::default()
6703                    }),
6704                ]);
6705                decl.capabilities = Some(vec![
6706                    fdecl::Capability::Protocol(fdecl::Protocol {
6707                        name: Some("fuchsia.logger.Log".to_string()),
6708                        source_path: Some("/svc/logger".to_string()),
6709                        ..Default::default()
6710                    }),
6711                    fdecl::Capability::Directory(fdecl::Directory {
6712                        name: Some("assets".to_string()),
6713                        source_path: Some("/data/assets".to_string()),
6714                        rights: Some(fio::Operations::CONNECT),
6715                        ..Default::default()
6716                    }),
6717                ]);
6718                decl
6719            },
6720            result = Err(ErrorList::new(vec![
6721                Error::extraneous_field(DeclType::OfferService, "source.child.collection"),
6722                Error::extraneous_field(DeclType::OfferService, "target.child.collection"),
6723                Error::extraneous_field(DeclType::OfferProtocol, "source.child.collection"),
6724                Error::extraneous_field(DeclType::OfferProtocol, "target.child.collection"),
6725                Error::extraneous_field(DeclType::OfferDirectory, "source.child.collection"),
6726                Error::extraneous_field(DeclType::OfferDirectory, "target.child.collection"),
6727                Error::extraneous_field(DeclType::OfferStorage, "target.child.collection"),
6728                Error::extraneous_field(DeclType::OfferRunner, "source.child.collection"),
6729                Error::extraneous_field(DeclType::OfferRunner, "target.child.collection"),
6730                Error::extraneous_field(DeclType::OfferResolver, "source.child.collection"),
6731                Error::extraneous_field(DeclType::OfferResolver, "target.child.collection"),
6732                Error::extraneous_field(DeclType::OfferDictionary, "source.child.collection"),
6733                Error::extraneous_field(DeclType::OfferDictionary, "target.child.collection"),
6734            ])),
6735        },
6736        test_validate_offers_invalid_filtered_service_fields => {
6737            input = {
6738                let mut decl = new_component_decl();
6739                decl.offers = Some(vec![
6740                    fdecl::Offer::Service(fdecl::OfferService {
6741                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6742                        source_name: Some("fuchsia.logger.Log".to_string()),
6743                        target: Some(fdecl::Ref::Child(
6744                            fdecl::ChildRef {
6745                                name: "logger".to_string(),
6746                                collection: None,
6747                            }
6748                        )),
6749                        target_name: Some("fuchsia.logger.Log".to_string()),
6750                        source_instance_filter: Some(vec![]),
6751                        ..Default::default()
6752                    }),
6753                    fdecl::Offer::Service(fdecl::OfferService {
6754                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6755                        source_name: Some("fuchsia.logger.Log".to_string()),
6756                        target: Some(fdecl::Ref::Child(
6757                            fdecl::ChildRef {
6758                                name: "logger".to_string(),
6759                                collection: None,
6760                            }
6761                        )),
6762                        target_name: Some("fuchsia.logger.Log2".to_string()),
6763                        source_instance_filter: Some(vec!["^badname".to_string()]),
6764                        ..Default::default()
6765                    }),
6766                    fdecl::Offer::Service(fdecl::OfferService {
6767                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6768                        source_name: Some("fuchsia.logger.Log".to_string()),
6769                        target: Some(fdecl::Ref::Child(
6770                            fdecl::ChildRef {
6771                                name: "logger".to_string(),
6772                                collection: None,
6773                            }
6774                        )),
6775                        target_name: Some("fuchsia.logger.Log1".to_string()),
6776                        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()}]),
6777                        ..Default::default()
6778                    }),
6779                    fdecl::Offer::Service(fdecl::OfferService {
6780                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6781                        source_name: Some("fuchsia.logger.Log".to_string()),
6782                        target: Some(fdecl::Ref::Child(
6783                            fdecl::ChildRef {
6784                                name: "logger".to_string(),
6785                                collection: None,
6786                            }
6787                        )),
6788                        target_name: Some("fuchsia.logger.Log3".to_string()),
6789                        renamed_instances: Some(vec![
6790                            fdecl::NameMapping {
6791                                source_name: "^badname".to_string(),
6792                                target_name: "^badname".to_string(),
6793                            }
6794                        ]),
6795                        ..Default::default()
6796                    })
6797                ]);
6798                decl.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                decl
6809            },
6810            result = Err(ErrorList::new(vec![
6811                Error::invalid_field(DeclType::OfferService, "source_instance_filter"),
6812                Error::invalid_field(DeclType::OfferService, "source_instance_filter"),
6813                Error::invalid_field(DeclType::OfferService, "renamed_instances"),
6814                Error::invalid_field(DeclType::OfferService, "renamed_instances.source_name"),
6815                Error::invalid_field(DeclType::OfferService, "renamed_instances.target_name"),
6816            ])),
6817        },
6818        test_validate_offers_invalid_identifiers => {
6819            input = {
6820                let mut decl = new_component_decl();
6821                decl.offers = Some(vec![
6822                    fdecl::Offer::Service(fdecl::OfferService {
6823                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6824                            name: "^bad".to_string(),
6825                            collection: None,
6826                        })),
6827                        source_name: Some("foo/".to_string()),
6828                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6829                            name: "%bad".to_string(),
6830                            collection: None,
6831                        })),
6832                        target_name: Some("/".to_string()),
6833                        ..Default::default()
6834                    }),
6835                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6836                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6837                            name: "^bad".to_string(),
6838                            collection: None,
6839                        })),
6840                        source_name: Some("foo/".to_string()),
6841                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6842                            name: "%bad".to_string(),
6843                            collection: None,
6844                        })),
6845                        target_name: Some("/".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: "^bad".to_string(),
6852                            collection: None,
6853                        })),
6854                        source_name: Some("foo/".to_string()),
6855                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6856                            name: "%bad".to_string(),
6857                            collection: None,
6858                        })),
6859                        target_name: Some("/".to_string()),
6860                        rights: Some(fio::Operations::CONNECT),
6861                        subdir: Some("/foo".to_string()),
6862                        dependency_type: Some(fdecl::DependencyType::Strong),
6863                        ..Default::default()
6864                    }),
6865                    fdecl::Offer::Runner(fdecl::OfferRunner {
6866                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6867                            name: "^bad".to_string(),
6868                            collection: None,
6869                        })),
6870                        source_name: Some("/path".to_string()),
6871                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6872                            name: "%bad".to_string(),
6873                            collection: None,
6874                        })),
6875                        target_name: Some("elf!".to_string()),
6876                        ..Default::default()
6877                    }),
6878                    fdecl::Offer::Resolver(fdecl::OfferResolver {
6879                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6880                            name: "^bad".to_string(),
6881                            collection: None,
6882                        })),
6883                        source_name: Some("/path".to_string()),
6884                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6885                            name: "%bad".to_string(),
6886                            collection: None,
6887                        })),
6888                        target_name: Some("pkg!".to_string()),
6889                        ..Default::default()
6890                    }),
6891                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6892                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6893                            name: "^bad".to_string(),
6894                            collection: None,
6895                        })),
6896                        source_name: Some("/path".to_string()),
6897                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6898                            name: "%bad".to_string(),
6899                            collection: None,
6900                        })),
6901                        target_name: Some("pkg!".to_string()),
6902                        dependency_type: Some(fdecl::DependencyType::Strong),
6903                        ..Default::default()
6904                    }),
6905                ]);
6906                decl
6907            },
6908            result = Err(ErrorList::new(vec![
6909                Error::invalid_field(DeclType::OfferService, "source.child.name"),
6910                Error::invalid_field(DeclType::OfferService, "source_name"),
6911                Error::invalid_field(DeclType::OfferService, "target.child.name"),
6912                Error::invalid_field(DeclType::OfferService, "target_name"),
6913                Error::invalid_field(DeclType::OfferProtocol, "source.child.name"),
6914                Error::invalid_field(DeclType::OfferProtocol, "source_name"),
6915                Error::invalid_field(DeclType::OfferProtocol, "target.child.name"),
6916                Error::invalid_field(DeclType::OfferProtocol, "target_name"),
6917                Error::invalid_field(DeclType::OfferDirectory, "source.child.name"),
6918                Error::invalid_field(DeclType::OfferDirectory, "source_name"),
6919                Error::invalid_field(DeclType::OfferDirectory, "target.child.name"),
6920                Error::invalid_field(DeclType::OfferDirectory, "target_name"),
6921                Error::invalid_field(DeclType::OfferDirectory, "subdir"),
6922                Error::invalid_field(DeclType::OfferRunner, "source.child.name"),
6923                Error::invalid_field(DeclType::OfferRunner, "source_name"),
6924                Error::invalid_field(DeclType::OfferRunner, "target.child.name"),
6925                Error::invalid_field(DeclType::OfferRunner, "target_name"),
6926                Error::invalid_field(DeclType::OfferResolver, "source.child.name"),
6927                Error::invalid_field(DeclType::OfferResolver, "source_name"),
6928                Error::invalid_field(DeclType::OfferResolver, "target.child.name"),
6929                Error::invalid_field(DeclType::OfferResolver, "target_name"),
6930                Error::invalid_field(DeclType::OfferDictionary, "source.child.name"),
6931                Error::invalid_field(DeclType::OfferDictionary, "source_name"),
6932                Error::invalid_field(DeclType::OfferDictionary, "target.child.name"),
6933                Error::invalid_field(DeclType::OfferDictionary, "target_name"),
6934            ])),
6935        },
6936        test_validate_offers_target_equals_source => {
6937            input = {
6938                let mut decl = new_component_decl();
6939                decl.offers = Some(vec![
6940                    fdecl::Offer::Service(fdecl::OfferService {
6941                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6942                            name: "logger".to_string(),
6943                            collection: None,
6944                        })),
6945                        source_name: Some("logger".to_string()),
6946                        target: Some(fdecl::Ref::Child(
6947                        fdecl::ChildRef {
6948                            name: "logger".to_string(),
6949                            collection: None,
6950                        }
6951                        )),
6952                        target_name: Some("logger".to_string()),
6953                        ..Default::default()
6954                    }),
6955                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6956                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6957                            name: "logger".to_string(),
6958                            collection: None,
6959                        })),
6960                        source_name: Some("legacy_logger".to_string()),
6961                        target: Some(fdecl::Ref::Child(
6962                        fdecl::ChildRef {
6963                            name: "logger".to_string(),
6964                            collection: None,
6965                        }
6966                        )),
6967                        target_name: Some("weak_legacy_logger".to_string()),
6968                        dependency_type: Some(fdecl::DependencyType::Weak),
6969                        ..Default::default()
6970                    }),
6971                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6972                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6973                            name: "logger".to_string(),
6974                            collection: None,
6975                        })),
6976                        source_name: Some("legacy_logger".to_string()),
6977                        target: Some(fdecl::Ref::Child(
6978                        fdecl::ChildRef {
6979                            name: "logger".to_string(),
6980                            collection: None,
6981                        }
6982                        )),
6983                        target_name: Some("strong_legacy_logger".to_string()),
6984                        dependency_type: Some(fdecl::DependencyType::Strong),
6985                        ..Default::default()
6986                    }),
6987                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6988                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6989                            name: "logger".to_string(),
6990                            collection: None,
6991                        })),
6992                        source_name: Some("assets".to_string()),
6993                        target: Some(fdecl::Ref::Child(
6994                        fdecl::ChildRef {
6995                            name: "logger".to_string(),
6996                            collection: None,
6997                        }
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::Runner(fdecl::OfferRunner {
7006                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7007                            name: "logger".to_string(),
7008                            collection: None,
7009                        })),
7010                        source_name: Some("web".to_string()),
7011                        target: Some(fdecl::Ref::Child(
7012                        fdecl::ChildRef {
7013                            name: "logger".to_string(),
7014                            collection: None,
7015                        }
7016                        )),
7017                        target_name: Some("web".to_string()),
7018                        ..Default::default()
7019                    }),
7020                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7021                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7022                            name: "logger".to_string(),
7023                            collection: None,
7024                        })),
7025                        source_name: Some("pkg".to_string()),
7026                        target: Some(fdecl::Ref::Child(
7027                        fdecl::ChildRef {
7028                            name: "logger".to_string(),
7029                            collection: None,
7030                        }
7031                        )),
7032                        target_name: Some("pkg".to_string()),
7033                        ..Default::default()
7034                    }),
7035                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7036                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7037                            name: "logger".to_string(),
7038                            collection: None,
7039                        })),
7040                        source_name: Some("dict".to_string()),
7041                        target: Some(fdecl::Ref::Child(
7042                        fdecl::ChildRef {
7043                            name: "logger".to_string(),
7044                            collection: None,
7045                        }
7046                        )),
7047                        target_name: Some("dict".to_string()),
7048                        dependency_type: Some(fdecl::DependencyType::Strong),
7049                        ..Default::default()
7050                    }),
7051                ]);
7052                decl.children = Some(vec![fdecl::Child{
7053                    name: Some("logger".to_string()),
7054                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
7055                    startup: Some(fdecl::StartupMode::Lazy),
7056                    on_terminate: None,
7057                    environment: None,
7058                    ..Default::default()
7059                }]);
7060                decl
7061            },
7062            result = Err(ErrorList::new(vec![
7063                Error::dependency_cycle("{{child logger -> child logger}}".to_string()),
7064            ])),
7065        },
7066        test_validate_offers_storage_target_equals_source => {
7067            input = fdecl::Component {
7068                offers: Some(vec![
7069                    fdecl::Offer::Storage(fdecl::OfferStorage {
7070                        source_name: Some("data".to_string()),
7071                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef { })),
7072                        target: Some(fdecl::Ref::Child(
7073                            fdecl::ChildRef {
7074                                name: "logger".to_string(),
7075                                collection: None,
7076                            }
7077                        )),
7078                        target_name: Some("data".to_string()),
7079                        ..Default::default()
7080                    })
7081                ]),
7082                capabilities: Some(vec![
7083                    fdecl::Capability::Storage(fdecl::Storage {
7084                        name: Some("data".to_string()),
7085                        backing_dir: Some("minfs".to_string()),
7086                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7087                            name: "logger".to_string(),
7088                            collection: None,
7089                        })),
7090                        subdir: None,
7091                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
7092                        ..Default::default()
7093                    }),
7094                ]),
7095                children: Some(vec![
7096                    fdecl::Child {
7097                        name: Some("logger".to_string()),
7098                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
7099                        startup: Some(fdecl::StartupMode::Lazy),
7100                        on_terminate: None,
7101                        environment: None,
7102                        ..Default::default()
7103                    },
7104                ]),
7105                ..new_component_decl()
7106            },
7107            result = Err(ErrorList::new(vec![
7108                Error::dependency_cycle("{{child logger -> capability data -> child logger}}".to_string()),
7109            ])),
7110        },
7111        test_validate_offers_invalid_child => {
7112            input = {
7113                let mut decl = new_component_decl();
7114                decl.offers = Some(vec![
7115                    fdecl::Offer::Service(fdecl::OfferService {
7116                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7117                            name: "logger".to_string(),
7118                            collection: None,
7119                        })),
7120                        source_name: Some("fuchsia.logger.Log".to_string()),
7121                        target: Some(fdecl::Ref::Child(
7122                        fdecl::ChildRef {
7123                            name: "netstack".to_string(),
7124                            collection: None,
7125                        }
7126                        )),
7127                        target_name: Some("fuchsia.logger.Log".to_string()),
7128                        ..Default::default()
7129                    }),
7130                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7131                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7132                            name: "logger".to_string(),
7133                            collection: None,
7134                        })),
7135                        source_name: Some("fuchsia.logger.LegacyLog".to_string()),
7136                        target: Some(fdecl::Ref::Child(
7137                        fdecl::ChildRef {
7138                            name: "netstack".to_string(),
7139                            collection: None,
7140                        }
7141                        )),
7142                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7143                        dependency_type: Some(fdecl::DependencyType::Strong),
7144                        ..Default::default()
7145                    }),
7146                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7147                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7148                            name: "logger".to_string(),
7149                            collection: None,
7150                        })),
7151                        source_name: Some("assets".to_string()),
7152                        target: Some(fdecl::Ref::Collection(
7153                        fdecl::CollectionRef { name: "modular".to_string() }
7154                        )),
7155                        target_name: Some("assets".to_string()),
7156                        rights: Some(fio::Operations::CONNECT),
7157                        subdir: None,
7158                        dependency_type: Some(fdecl::DependencyType::Weak),
7159                        ..Default::default()
7160                    }),
7161                ]);
7162                decl.capabilities = Some(vec![
7163                    fdecl::Capability::Storage(fdecl::Storage {
7164                        name: Some("memfs".to_string()),
7165                        backing_dir: Some("memfs".to_string()),
7166                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7167                            name: "logger".to_string(),
7168                            collection: None,
7169                        })),
7170                        subdir: None,
7171                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
7172                        ..Default::default()
7173                    }),
7174                ]);
7175                decl.children = Some(vec![
7176                    fdecl::Child {
7177                        name: Some("netstack".to_string()),
7178                        url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
7179                        startup: Some(fdecl::StartupMode::Lazy),
7180                        on_terminate: None,
7181                        environment: None,
7182                        ..Default::default()
7183                    },
7184                ]);
7185                decl.collections = Some(vec![
7186                    fdecl::Collection {
7187                        name: Some("modular".to_string()),
7188                        durability: Some(fdecl::Durability::Transient),
7189                        environment: None,
7190                        allowed_offers: Some(fdecl::AllowedOffers::StaticAndDynamic),
7191                        allow_long_names: None,
7192                        ..Default::default()
7193                    },
7194                ]);
7195                decl
7196            },
7197            result = Err(ErrorList::new(vec![
7198                Error::invalid_child(DeclType::Storage, "source", "logger"),
7199                Error::invalid_child(DeclType::OfferService, "source", "logger"),
7200                Error::invalid_child(DeclType::OfferProtocol, "source", "logger"),
7201                Error::invalid_child(DeclType::OfferDirectory, "source", "logger"),
7202            ])),
7203        },
7204        test_validate_offers_invalid_source_capability => {
7205            input = {
7206                fdecl::Component {
7207                    offers: Some(vec![
7208                        fdecl::Offer::Protocol(fdecl::OfferProtocol {
7209                            source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
7210                                name: "this-storage-doesnt-exist".to_string(),
7211                            })),
7212                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
7213                            target: Some(fdecl::Ref::Child(
7214                            fdecl::ChildRef {
7215                                name: "netstack".to_string(),
7216                                collection: None,
7217                            }
7218                            )),
7219                            target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
7220                            dependency_type: Some(fdecl::DependencyType::Strong),
7221                            ..Default::default()
7222                        }),
7223                    ]),
7224                    ..new_component_decl()
7225                }
7226            },
7227            result = Err(ErrorList::new(vec![
7228                Error::invalid_capability(DeclType::OfferProtocol, "source", "this-storage-doesnt-exist"),
7229                Error::invalid_child(DeclType::OfferProtocol, "target", "netstack"),
7230            ])),
7231        },
7232        test_validate_offers_target => {
7233            input = {
7234                let mut decl = new_component_decl();
7235                decl.offers = Some(vec![
7236                    fdecl::Offer::Service(fdecl::OfferService {
7237                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
7238                            name: "modular".into()
7239                        })),
7240                        source_name: Some("logger".to_string()),
7241                        target: Some(fdecl::Ref::Child(
7242                        fdecl::ChildRef {
7243                            name: "netstack".to_string(),
7244                            collection: None,
7245                        }
7246                        )),
7247                        target_name: Some("fuchsia.logger.Log".to_string()),
7248                        ..Default::default()
7249                    }),
7250                    fdecl::Offer::Service(fdecl::OfferService {
7251                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
7252                            name: "modular".into()
7253                        })),
7254                        source_name: Some("logger".to_string()),
7255                        target: Some(fdecl::Ref::Child(
7256                        fdecl::ChildRef {
7257                            name: "netstack".to_string(),
7258                            collection: None,
7259                        }
7260                        )),
7261                        target_name: Some("fuchsia.logger.Log".to_string()),
7262                        ..Default::default()
7263                    }),
7264                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7265                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7266                        source_name: Some("fuchsia.logger.LegacyLog".to_string()),
7267                        target: Some(fdecl::Ref::Child(
7268                        fdecl::ChildRef {
7269                            name: "netstack".to_string(),
7270                            collection: None,
7271                        }
7272                        )),
7273                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7274                        dependency_type: Some(fdecl::DependencyType::Strong),
7275                        ..Default::default()
7276                    }),
7277                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7278                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7279                        source_name: Some("fuchsia.logger.LegacyLog".to_string()),
7280                        target: Some(fdecl::Ref::Child(
7281                        fdecl::ChildRef {
7282                            name: "netstack".to_string(),
7283                            collection: None,
7284                        }
7285                        )),
7286                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7287                        dependency_type: Some(fdecl::DependencyType::Strong),
7288                        ..Default::default()
7289                    }),
7290                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7291                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7292                        source_name: Some("assets".to_string()),
7293                        target: Some(fdecl::Ref::Collection(
7294                        fdecl::CollectionRef { name: "modular".to_string() }
7295                        )),
7296                        target_name: Some("assets".to_string()),
7297                        rights: Some(fio::Operations::CONNECT),
7298                        subdir: None,
7299                        dependency_type: Some(fdecl::DependencyType::Strong),
7300                        ..Default::default()
7301                    }),
7302                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7303                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7304                        source_name: Some("assets".to_string()),
7305                        target: Some(fdecl::Ref::Collection(
7306                        fdecl::CollectionRef { name: "modular".to_string() }
7307                        )),
7308                        target_name: Some("assets".to_string()),
7309                        rights: Some(fio::Operations::CONNECT),
7310                        subdir: None,
7311                        dependency_type: Some(fdecl::DependencyType::Weak),
7312                        ..Default::default()
7313                    }),
7314                    fdecl::Offer::Storage(fdecl::OfferStorage {
7315                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7316                        source_name: Some("data".to_string()),
7317                        target: Some(fdecl::Ref::Collection(
7318                        fdecl::CollectionRef { name: "modular".to_string() }
7319                        )),
7320                        target_name: Some("data".to_string()),
7321                        ..Default::default()
7322                    }),
7323                    fdecl::Offer::Storage(fdecl::OfferStorage {
7324                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7325                        source_name: Some("data".to_string()),
7326                        target: Some(fdecl::Ref::Collection(
7327                        fdecl::CollectionRef { name: "modular".to_string() }
7328                        )),
7329                        target_name: Some("data".to_string()),
7330                        ..Default::default()
7331                    }),
7332                    fdecl::Offer::Runner(fdecl::OfferRunner {
7333                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7334                        source_name: Some("elf".to_string()),
7335                        target: Some(fdecl::Ref::Collection(
7336                        fdecl::CollectionRef { name: "modular".to_string() }
7337                        )),
7338                        target_name: Some("duplicated".to_string()),
7339                        ..Default::default()
7340                    }),
7341                    fdecl::Offer::Runner(fdecl::OfferRunner {
7342                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7343                        source_name: Some("elf".to_string()),
7344                        target: Some(fdecl::Ref::Collection(
7345                        fdecl::CollectionRef { name: "modular".to_string() }
7346                        )),
7347                        target_name: Some("duplicated".to_string()),
7348                        ..Default::default()
7349                    }),
7350                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7351                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7352                        source_name: Some("pkg".to_string()),
7353                        target: Some(fdecl::Ref::Collection(
7354                        fdecl::CollectionRef { name: "modular".to_string() }
7355                        )),
7356                        target_name: Some("duplicated".to_string()),
7357                        ..Default::default()
7358                    }),
7359                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
7360                        source_name: Some("started".to_string()),
7361                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7362                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "netstack".to_string(), collection: None})),
7363                        target_name: Some("started".to_string()),
7364                        ..Default::default()
7365                    }),
7366                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
7367                        source_name: Some("started".to_string()),
7368                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7369                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "netstack".to_string(), collection: None})),
7370                        target_name: Some("started".to_string()),
7371                        ..Default::default()
7372                    }),
7373                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7374                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7375                        source_name: Some("a".to_string()),
7376                        target: Some(fdecl::Ref::Collection(
7377                            fdecl::CollectionRef { name: "modular".to_string() }
7378                        )),
7379                        target_name: Some("dict".to_string()),
7380                        dependency_type: Some(fdecl::DependencyType::Strong),
7381                        ..Default::default()
7382                    }),
7383                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7384                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7385                        source_name: Some("b".to_string()),
7386                        target: Some(fdecl::Ref::Collection(
7387                            fdecl::CollectionRef { name: "modular".to_string() }
7388                        )),
7389                        target_name: Some("dict".to_string()),
7390                        dependency_type: Some(fdecl::DependencyType::Strong),
7391                        ..Default::default()
7392                    }),
7393                ]);
7394                decl.children = Some(vec![
7395                    fdecl::Child{
7396                        name: Some("netstack".to_string()),
7397                        url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
7398                        startup: Some(fdecl::StartupMode::Eager),
7399                        on_terminate: None,
7400                        environment: None,
7401                        ..Default::default()
7402                    },
7403                ]);
7404                decl.collections = Some(vec![
7405                    fdecl::Collection{
7406                        name: Some("modular".to_string()),
7407                        durability: Some(fdecl::Durability::Transient),
7408                        environment: None,
7409                        allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7410                        allow_long_names: None,
7411                        ..Default::default()
7412                    },
7413                ]);
7414                decl
7415            },
7416            result = Err(ErrorList::new(vec![
7417                // Duplicate services are allowed, for aggregation.
7418                Error::duplicate_field(DeclType::OfferProtocol, "target_name", "fuchsia.logger.LegacyLog"),
7419                Error::duplicate_field(DeclType::OfferDirectory, "target_name", "assets"),
7420                Error::duplicate_field(DeclType::OfferStorage, "target_name", "data"),
7421                Error::duplicate_field(DeclType::OfferRunner, "target_name", "duplicated"),
7422                Error::duplicate_field(DeclType::OfferResolver, "target_name", "duplicated"),
7423                Error::duplicate_field(DeclType::OfferEventStream, "target_name", "started"),
7424                Error::duplicate_field(DeclType::OfferDictionary, "target_name", "dict"),
7425            ])),
7426        },
7427        test_validate_offers_target_invalid => {
7428            input = {
7429                let mut decl = new_component_decl();
7430                decl.offers = Some(vec![
7431                    fdecl::Offer::Service(fdecl::OfferService {
7432                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7433                        source_name: Some("logger".to_string()),
7434                        target: Some(fdecl::Ref::Child(
7435                        fdecl::ChildRef {
7436                            name: "netstack".to_string(),
7437                            collection: None,
7438                        }
7439                        )),
7440                        target_name: Some("fuchsia.logger.Log".to_string()),
7441                        ..Default::default()
7442                    }),
7443                    fdecl::Offer::Service(fdecl::OfferService {
7444                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7445                        source_name: Some("logger".to_string()),
7446                        target: Some(fdecl::Ref::Collection(
7447                        fdecl::CollectionRef { name: "modular".to_string(), }
7448                        )),
7449                        target_name: Some("fuchsia.logger.Log".to_string()),
7450                        ..Default::default()
7451                    }),
7452                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7453                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7454                        source_name: Some("legacy_logger".to_string()),
7455                        target: Some(fdecl::Ref::Child(
7456                        fdecl::ChildRef {
7457                            name: "netstack".to_string(),
7458                            collection: None,
7459                        }
7460                        )),
7461                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7462                        dependency_type: Some(fdecl::DependencyType::Weak),
7463                        ..Default::default()
7464                    }),
7465                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7466                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7467                        source_name: Some("legacy_logger".to_string()),
7468                        target: Some(fdecl::Ref::Collection(
7469                        fdecl::CollectionRef { name: "modular".to_string(), }
7470                        )),
7471                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7472                        dependency_type: Some(fdecl::DependencyType::Strong),
7473                        ..Default::default()
7474                    }),
7475                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7476                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7477                        source_name: Some("assets".to_string()),
7478                        target: Some(fdecl::Ref::Child(
7479                        fdecl::ChildRef {
7480                            name: "netstack".to_string(),
7481                            collection: None,
7482                        }
7483                        )),
7484                        target_name: Some("data".to_string()),
7485                        rights: Some(fio::Operations::CONNECT),
7486                        subdir: None,
7487                        dependency_type: Some(fdecl::DependencyType::Strong),
7488                        ..Default::default()
7489                    }),
7490                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7491                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7492                        source_name: Some("assets".to_string()),
7493                        target: Some(fdecl::Ref::Collection(
7494                        fdecl::CollectionRef { name: "modular".to_string(), }
7495                        )),
7496                        target_name: Some("data".to_string()),
7497                        rights: Some(fio::Operations::CONNECT),
7498                        subdir: None,
7499                        dependency_type: Some(fdecl::DependencyType::Weak),
7500                        ..Default::default()
7501                    }),
7502                    fdecl::Offer::Storage(fdecl::OfferStorage {
7503                        source_name: Some("data".to_string()),
7504                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7505                        target: Some(fdecl::Ref::Child(
7506                            fdecl::ChildRef {
7507                                name: "netstack".to_string(),
7508                                collection: None,
7509                            }
7510                        )),
7511                        target_name: Some("data".to_string()),
7512                        ..Default::default()
7513                    }),
7514                    fdecl::Offer::Storage(fdecl::OfferStorage {
7515                        source_name: Some("data".to_string()),
7516                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7517                        target: Some(fdecl::Ref::Collection(
7518                            fdecl::CollectionRef { name: "modular".to_string(), }
7519                        )),
7520                        target_name: Some("data".to_string()),
7521                        ..Default::default()
7522                    }),
7523                    fdecl::Offer::Runner(fdecl::OfferRunner {
7524                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7525                        source_name: Some("elf".to_string()),
7526                        target: Some(fdecl::Ref::Child(
7527                            fdecl::ChildRef {
7528                                name: "netstack".to_string(),
7529                                collection: None,
7530                            }
7531                        )),
7532                        target_name: Some("elf".to_string()),
7533                        ..Default::default()
7534                    }),
7535                    fdecl::Offer::Runner(fdecl::OfferRunner {
7536                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7537                        source_name: Some("elf".to_string()),
7538                        target: Some(fdecl::Ref::Collection(
7539                        fdecl::CollectionRef { name: "modular".to_string(), }
7540                        )),
7541                        target_name: Some("elf".to_string()),
7542                        ..Default::default()
7543                    }),
7544                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7545                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7546                        source_name: Some("pkg".to_string()),
7547                        target: Some(fdecl::Ref::Child(
7548                            fdecl::ChildRef {
7549                                name: "netstack".to_string(),
7550                                collection: None,
7551                            }
7552                        )),
7553                        target_name: Some("pkg".to_string()),
7554                        ..Default::default()
7555                    }),
7556                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7557                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7558                        source_name: Some("pkg".to_string()),
7559                        target: Some(fdecl::Ref::Collection(
7560                        fdecl::CollectionRef { name: "modular".to_string(), }
7561                        )),
7562                        target_name: Some("pkg".to_string()),
7563                        ..Default::default()
7564                    }),
7565                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7566                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7567                        source_name: Some("pkg".to_string()),
7568                        target: Some(fdecl::Ref::Child(
7569                            fdecl::ChildRef {
7570                                name: "netstack".to_string(),
7571                                collection: None,
7572                            }
7573                        )),
7574                        target_name: Some("pkg".to_string()),
7575                        dependency_type: Some(fdecl::DependencyType::Strong),
7576                        ..Default::default()
7577                    }),
7578                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7579                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7580                        source_name: Some("pkg".to_string()),
7581                        target: Some(fdecl::Ref::Collection(
7582                        fdecl::CollectionRef { name: "modular".to_string(), }
7583                        )),
7584                        target_name: Some("pkg".to_string()),
7585                        dependency_type: Some(fdecl::DependencyType::Strong),
7586                        ..Default::default()
7587                    }),
7588                ]);
7589                decl
7590            },
7591            result = Err(ErrorList::new(vec![
7592                Error::invalid_child(DeclType::OfferService, "target", "netstack"),
7593                Error::invalid_collection(DeclType::OfferService, "target", "modular"),
7594                Error::invalid_child(DeclType::OfferProtocol, "target", "netstack"),
7595                Error::invalid_collection(DeclType::OfferProtocol, "target", "modular"),
7596                Error::invalid_child(DeclType::OfferDirectory, "target", "netstack"),
7597                Error::invalid_collection(DeclType::OfferDirectory, "target", "modular"),
7598                Error::invalid_child(DeclType::OfferStorage, "target", "netstack"),
7599                Error::invalid_collection(DeclType::OfferStorage, "target", "modular"),
7600                Error::invalid_child(DeclType::OfferRunner, "target", "netstack"),
7601                Error::invalid_collection(DeclType::OfferRunner, "target", "modular"),
7602                Error::invalid_child(DeclType::OfferResolver, "target", "netstack"),
7603                Error::invalid_collection(DeclType::OfferResolver, "target", "modular"),
7604                Error::invalid_child(DeclType::OfferDictionary, "target", "netstack"),
7605                Error::invalid_collection(DeclType::OfferDictionary, "target", "modular"),
7606            ])),
7607        },
7608        test_validate_offers_target_dictionary => {
7609            input = fdecl::Component {
7610                offers: Some(vec![
7611                    // Offer to static dictionary is ok
7612                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7613                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7614                        source_name: Some("p".to_string()),
7615                        target: Some(fdecl::Ref::Capability(
7616                            fdecl::CapabilityRef {
7617                                name: "dict".into(),
7618                            },
7619                        )),
7620                        target_name: Some("p".into()),
7621                        dependency_type: Some(fdecl::DependencyType::Strong),
7622                        ..Default::default()
7623                    }),
7624                    // Offer to dynamic dictionary is forbidden
7625                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7626                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7627                        source_name: Some("p".to_string()),
7628                        target: Some(fdecl::Ref::Capability(
7629                            fdecl::CapabilityRef {
7630                                name: "dynamic".into(),
7631                            },
7632                        )),
7633                        target_name: Some("p".into()),
7634                        dependency_type: Some(fdecl::DependencyType::Strong),
7635                        ..Default::default()
7636                    }),
7637                ]),
7638                capabilities: Some(vec![
7639                    fdecl::Capability::Dictionary(fdecl::Dictionary {
7640                        name: Some("dict".into()),
7641                        ..Default::default()
7642                    }),
7643                    fdecl::Capability::Dictionary(fdecl::Dictionary {
7644                        name: Some("dynamic".into()),
7645                        source_path: Some("/out/dir".into()),
7646                        ..Default::default()
7647                    }),
7648                ]),
7649                ..Default::default()
7650            },
7651            result = Err(ErrorList::new(vec![
7652                Error::invalid_field(DeclType::OfferProtocol, "target"),
7653            ])),
7654        },
7655        test_validate_offers_invalid_source_collection => {
7656            input = {
7657                let mut decl = new_component_decl();
7658                decl.collections = Some(vec![
7659                    fdecl::Collection {
7660                        name: Some("col".to_string()),
7661                        durability: Some(fdecl::Durability::Transient),
7662                        allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7663                        allow_long_names: None,
7664                        ..Default::default()
7665                    }
7666                ]);
7667                decl.children = Some(vec![
7668                    fdecl::Child {
7669                        name: Some("child".to_string()),
7670                        url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7671                        startup: Some(fdecl::StartupMode::Lazy),
7672                        on_terminate: None,
7673                        ..Default::default()
7674                    }
7675                ]);
7676                decl.offers = Some(vec![
7677                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7678                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7679                        source_name: Some("a".to_string()),
7680                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7681                        target_name: Some("a".to_string()),
7682                        dependency_type: Some(fdecl::DependencyType::Strong),
7683                        ..Default::default()
7684                    }),
7685                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7686                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7687                        source_name: Some("b".to_string()),
7688                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7689                        target_name: Some("b".to_string()),
7690                        rights: Some(fio::Operations::CONNECT),
7691                        subdir: None,
7692                        dependency_type: Some(fdecl::DependencyType::Strong),
7693                        ..Default::default()
7694                    }),
7695                    fdecl::Offer::Storage(fdecl::OfferStorage {
7696                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7697                        source_name: Some("c".to_string()),
7698                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7699                        target_name: Some("c".to_string()),
7700                        ..Default::default()
7701                    }),
7702                    fdecl::Offer::Runner(fdecl::OfferRunner {
7703                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7704                        source_name: Some("d".to_string()),
7705                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7706                        target_name: Some("d".to_string()),
7707                        ..Default::default()
7708                    }),
7709                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7710                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7711                        source_name: Some("e".to_string()),
7712                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7713                        target_name: Some("e".to_string()),
7714                        ..Default::default()
7715                    }),
7716                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7717                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7718                        source_name: Some("f".to_string()),
7719                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7720                        target_name: Some("f".to_string()),
7721                        dependency_type: Some(fdecl::DependencyType::Strong),
7722                        ..Default::default()
7723                    }),
7724                ]);
7725                decl
7726            },
7727            result = Err(ErrorList::new(vec![
7728                Error::invalid_field(DeclType::OfferProtocol, "source"),
7729                Error::invalid_field(DeclType::OfferDirectory, "source"),
7730                Error::invalid_field(DeclType::OfferStorage, "source"),
7731                Error::invalid_field(DeclType::OfferRunner, "source"),
7732                Error::invalid_field(DeclType::OfferResolver, "source"),
7733                Error::invalid_field(DeclType::OfferDictionary, "source"),
7734            ])),
7735        },
7736        test_validate_offers_source_collection => {
7737            input = {
7738                let mut decl = new_component_decl();
7739                decl.collections = Some(vec![
7740                    fdecl::Collection {
7741                        name: Some("col".to_string()),
7742                        durability: Some(fdecl::Durability::Transient),
7743                        allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7744                        allow_long_names: None,
7745                        ..Default::default()
7746                    }
7747                ]);
7748                decl.children = Some(vec![
7749                    fdecl::Child {
7750                        name: Some("child".to_string()),
7751                        url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7752                        startup: Some(fdecl::StartupMode::Lazy),
7753                        on_terminate: None,
7754                        ..Default::default()
7755                    }
7756                ]);
7757                decl.offers = Some(vec![
7758                    fdecl::Offer::Service(fdecl::OfferService {
7759                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7760                        source_name: Some("a".to_string()),
7761                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7762                        target_name: Some("a".to_string()),
7763                        ..Default::default()
7764                    })
7765                ]);
7766                decl
7767            },
7768            result = Ok(()),
7769        },
7770        test_validate_offers_invalid_capability_from_self => {
7771            input = {
7772                let mut decl = new_component_decl();
7773                decl.children = Some(vec![
7774                    fdecl::Child {
7775                        name: Some("child".to_string()),
7776                        url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7777                        startup: Some(fdecl::StartupMode::Lazy),
7778                        ..Default::default()
7779                    }
7780                ]);
7781                decl.offers = Some(vec![
7782                    fdecl::Offer::Service(fdecl::OfferService {
7783                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7784                        source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7785                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7786                            name: "child".into(),
7787                            collection: None
7788                        })),
7789                        target_name: Some("foo".into()),
7790                        ..Default::default()
7791                    }),
7792                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7793                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7794                        source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7795                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7796                            name: "child".into(),
7797                            collection: None
7798                        })),
7799                        target_name: Some("bar".into()),
7800                        dependency_type: Some(fdecl::DependencyType::Strong),
7801                        ..Default::default()
7802                    }),
7803                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7804                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7805                        source_name: Some("dir".into()),
7806                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7807                            name: "child".into(),
7808                            collection: None
7809                        })),
7810                        target_name: Some("assets".into()),
7811                        dependency_type: Some(fdecl::DependencyType::Strong),
7812                        ..Default::default()
7813                    }),
7814                    fdecl::Offer::Runner(fdecl::OfferRunner {
7815                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7816                        source_name: Some("source_elf".into()),
7817                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7818                            name: "child".into(),
7819                            collection: None
7820                        })),
7821                        target_name: Some("elf".into()),
7822                        ..Default::default()
7823                    }),
7824                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7825                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7826                        source_name: Some("source_pkg".into()),
7827                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7828                            name: "child".into(),
7829                            collection: None
7830                        })),
7831                        target_name: Some("pkg".into()),
7832                        ..Default::default()
7833                    }),
7834                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7835                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7836                        source_name: Some("source_dict".into()),
7837                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7838                            name: "child".into(),
7839                            collection: None
7840                        })),
7841                        target_name: Some("dict".into()),
7842                        dependency_type: Some(fdecl::DependencyType::Strong),
7843                        ..Default::default()
7844                    }),
7845                    fdecl::Offer::Storage(fdecl::OfferStorage {
7846                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7847                        source_name: Some("source_storage".into()),
7848                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7849                            name: "child".into(),
7850                            collection: None
7851                        })),
7852                        target_name: Some("storage".into()),
7853                        ..Default::default()
7854                    }),
7855                    fdecl::Offer::Config(fdecl::OfferConfiguration {
7856                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7857                        source_name: Some("source_config".into()),
7858                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7859                            name: "child".into(),
7860                            collection: None
7861                        })),
7862                        target_name: Some("config".into()),
7863                        ..Default::default()
7864                    }),
7865                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7866                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7867                        source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7868                        source_dictionary: Some("dict/inner".into()),
7869                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7870                            name: "child".into(),
7871                            collection: None
7872                        })),
7873                        target_name: Some("baz".into()),
7874                        dependency_type: Some(fdecl::DependencyType::Strong),
7875                        ..Default::default()
7876                    }),
7877                ]);
7878                decl
7879            },
7880            result = Err(ErrorList::new(vec![
7881                Error::invalid_capability(
7882                    DeclType::OfferService,
7883                    "source",
7884                    "fuchsia.some.library.SomeProtocol"),
7885                Error::invalid_capability(
7886                    DeclType::OfferProtocol,
7887                    "source",
7888                    "fuchsia.some.library.SomeProtocol"),
7889                Error::invalid_capability(DeclType::OfferDirectory, "source", "dir"),
7890                Error::invalid_capability(DeclType::OfferRunner, "source", "source_elf"),
7891                Error::invalid_capability(DeclType::OfferResolver, "source", "source_pkg"),
7892                Error::invalid_capability(DeclType::OfferDictionary, "source", "source_dict"),
7893                Error::invalid_capability(DeclType::OfferStorage, "source", "source_storage"),
7894                Error::invalid_capability(DeclType::OfferConfig, "source", "source_config"),
7895                Error::invalid_capability(DeclType::OfferProtocol, "source", "dict"),
7896            ])),
7897        },
7898        test_validate_offers_long_dependency_cycle => {
7899            input = {
7900                let mut decl = new_component_decl();
7901                let dependencies = vec![
7902                    ("d", "b"),
7903                    ("a", "b"),
7904                    ("b", "c"),
7905                    ("b", "d"),
7906                    ("c", "a"),
7907                ];
7908                let offers = dependencies.into_iter().map(|(from,to)|
7909                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7910                        source: Some(fdecl::Ref::Child(
7911                        fdecl::ChildRef { name: from.to_string(), collection: None },
7912                        )),
7913                        source_name: Some(format!("thing_{}", from)),
7914                        target: Some(fdecl::Ref::Child(
7915                        fdecl::ChildRef { name: to.to_string(), collection: None },
7916                        )),
7917                        target_name: Some(format!("thing_{}", from)),
7918                        dependency_type: Some(fdecl::DependencyType::Strong),
7919                        ..Default::default()
7920                    })).collect();
7921                let children = ["a", "b", "c", "d"].iter().map(|name| {
7922                    fdecl::Child {
7923                        name: Some(name.to_string()),
7924                        url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
7925                        startup: Some(fdecl::StartupMode::Lazy),
7926                        on_terminate: None,
7927                        environment: None,
7928                        ..Default::default()
7929                    }
7930                }).collect();
7931                decl.offers = Some(offers);
7932                decl.children = Some(children);
7933                decl
7934            },
7935            result = Err(ErrorList::new(vec![
7936                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()),
7937            ])),
7938        },
7939        test_validate_offers_not_required_invalid_source_service => {
7940            input = {
7941                let mut decl = generate_offer_different_source_and_availability_decl(
7942                    |source, availability, target_name|
7943                        fdecl::Offer::Service(fdecl::OfferService {
7944                            source: Some(source),
7945                            source_name: Some("fuchsia.examples.Echo".to_string()),
7946                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7947                                name: "sink".to_string(),
7948                                collection: None,
7949                            })),
7950                            target_name: Some(target_name.into()),
7951                            availability: Some(availability),
7952                            ..Default::default()
7953                        })
7954                );
7955                decl.capabilities = Some(vec![
7956                    fdecl::Capability::Service(fdecl::Service {
7957                        name: Some("fuchsia.examples.Echo".to_string()),
7958                        source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
7959                        ..Default::default()
7960                    }),
7961                ]);
7962                decl
7963            },
7964            result = {
7965                Err(ErrorList::new(vec![
7966                    Error::availability_must_be_optional(
7967                        DeclType::OfferService,
7968                        "availability",
7969                        Some(&"fuchsia.examples.Echo".to_string()),
7970                    ),
7971                    Error::availability_must_be_optional(
7972                        DeclType::OfferService,
7973                        "availability",
7974                        Some(&"fuchsia.examples.Echo".to_string()),
7975                    ),
7976                ]))
7977            },
7978        },
7979        test_validate_offers_not_required_invalid_source_protocol => {
7980            input = {
7981                let mut decl = generate_offer_different_source_and_availability_decl(
7982                    |source, availability, target_name|
7983                        fdecl::Offer::Protocol(fdecl::OfferProtocol {
7984                            source: Some(source),
7985                            source_name: Some("fuchsia.examples.Echo".to_string()),
7986                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7987                                name: "sink".to_string(),
7988                                collection: None,
7989                            })),
7990                            target_name: Some(target_name.into()),
7991                            dependency_type: Some(fdecl::DependencyType::Strong),
7992                            availability: Some(availability),
7993                            ..Default::default()
7994                        })
7995                );
7996                decl.capabilities = Some(vec![
7997                    fdecl::Capability::Protocol(fdecl::Protocol {
7998                        name: Some("fuchsia.examples.Echo".to_string()),
7999                        source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
8000                        ..Default::default()
8001                    }),
8002                ]);
8003                decl
8004            },
8005            result = {
8006                Err(ErrorList::new(vec![
8007                    Error::availability_must_be_optional(
8008                        DeclType::OfferProtocol,
8009                        "availability",
8010                        Some(&"fuchsia.examples.Echo".to_string()),
8011                    ),
8012                    Error::availability_must_be_optional(
8013                        DeclType::OfferProtocol,
8014                        "availability",
8015                        Some(&"fuchsia.examples.Echo".to_string()),
8016                    ),
8017                ]))
8018            },
8019        },
8020        test_validate_offers_not_required_invalid_source_directory => {
8021            input = {
8022                let mut decl = generate_offer_different_source_and_availability_decl(
8023                    |source, availability, target_name|
8024                        fdecl::Offer::Directory(fdecl::OfferDirectory {
8025                            source: Some(source),
8026                            source_name: Some("assets".to_string()),
8027                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8028                                name: "sink".to_string(),
8029                                collection: None,
8030                            })),
8031                            target_name: Some(target_name.into()),
8032                            rights: Some(fio::Operations::CONNECT),
8033                            subdir: None,
8034                            dependency_type: Some(fdecl::DependencyType::Weak),
8035                            availability: Some(availability),
8036                            ..Default::default()
8037                        })
8038                );
8039                decl.capabilities = Some(vec![
8040                    fdecl::Capability::Directory(fdecl::Directory {
8041                        name: Some("assets".to_string()),
8042                        source_path: Some("/assets".to_string()),
8043                        rights: Some(fio::Operations::CONNECT),
8044                        ..Default::default()
8045                    }),
8046                ]);
8047                decl
8048            },
8049            result = {
8050                Err(ErrorList::new(vec![
8051                    Error::availability_must_be_optional(
8052                        DeclType::OfferDirectory,
8053                        "availability",
8054                        Some(&"assets".to_string()),
8055                    ),
8056                    Error::availability_must_be_optional(
8057                        DeclType::OfferDirectory,
8058                        "availability",
8059                        Some(&"assets".to_string()),
8060                    ),
8061                ]))
8062            },
8063        },
8064        test_validate_offers_not_required_invalid_source_storage => {
8065            input = {
8066                let mut decl = new_component_decl();
8067                decl.children = Some(vec![
8068                    fdecl::Child {
8069                        name: Some("sink".to_string()),
8070                        url: Some("fuchsia-pkg://fuchsia.com/sink#meta/sink.cm".to_string()),
8071                        startup: Some(fdecl::StartupMode::Lazy),
8072                        on_terminate: None,
8073                        environment: None,
8074                        ..Default::default()
8075                    },
8076                ]);
8077                decl.capabilities = Some(vec![
8078                    fdecl::Capability::Storage(fdecl::Storage {
8079                        name: Some("data".to_string()),
8080                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8081                        backing_dir: Some("minfs".to_string()),
8082                        subdir: None,
8083                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
8084                        ..Default::default()
8085                    }),
8086                ]);
8087                let new_offer = |source: fdecl::Ref, availability: fdecl::Availability,
8088                                        target_name: &str|
8089                {
8090                    fdecl::Offer::Storage(fdecl::OfferStorage {
8091                        source: Some(source),
8092                        source_name: Some("data".to_string()),
8093                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8094                            name: "sink".to_string(),
8095                            collection: None,
8096                        })),
8097                        target_name: Some(target_name.into()),
8098                        availability: Some(availability),
8099                        ..Default::default()
8100                    })
8101                };
8102                decl.offers = Some(vec![
8103                    // These offers are fine, offers with a source of parent or void can be
8104                    // optional.
8105                    new_offer(
8106                        fdecl::Ref::Parent(fdecl::ParentRef {}),
8107                        fdecl::Availability::Required,
8108                        "data0",
8109                    ),
8110                    new_offer(
8111                        fdecl::Ref::Parent(fdecl::ParentRef {}),
8112                        fdecl::Availability::Optional,
8113                        "data1",
8114                    ),
8115                    new_offer(
8116                        fdecl::Ref::Parent(fdecl::ParentRef {}),
8117                        fdecl::Availability::SameAsTarget,
8118                        "data2",
8119                    ),
8120                    new_offer(
8121                        fdecl::Ref::VoidType(fdecl::VoidRef {}),
8122                        fdecl::Availability::Optional,
8123                        "data3",
8124                    ),
8125                    // These offers are not fine, offers with a source other than parent or void
8126                    // must be required.
8127                    new_offer(
8128                        fdecl::Ref::Self_(fdecl::SelfRef {}),
8129                        fdecl::Availability::Optional,
8130                        "data4",
8131                    ),
8132                    new_offer(
8133                        fdecl::Ref::Self_(fdecl::SelfRef {}),
8134                        fdecl::Availability::SameAsTarget,
8135                        "data5",
8136                    ),
8137                    // These offers are also not fine, offers with a source of void must be optional
8138                    new_offer(
8139                        fdecl::Ref::VoidType(fdecl::VoidRef {}),
8140                        fdecl::Availability::Required,
8141                        "data6",
8142                    ),
8143                    new_offer(
8144                        fdecl::Ref::VoidType(fdecl::VoidRef {}),
8145                        fdecl::Availability::SameAsTarget,
8146                        "data7",
8147                    ),
8148                ]);
8149                decl
8150            },
8151            result = {
8152                Err(ErrorList::new(vec![
8153                    Error::availability_must_be_optional(
8154                        DeclType::OfferStorage,
8155                        "availability",
8156                        Some(&"data".to_string()),
8157                    ),
8158                    Error::availability_must_be_optional(
8159                        DeclType::OfferStorage,
8160                        "availability",
8161                        Some(&"data".to_string()),
8162                    ),
8163                ]))
8164            },
8165        },
8166
8167        test_validate_offers_valid_service_aggregation => {
8168            input = {
8169                let mut decl = new_component_decl();
8170                decl.offers = Some(vec![
8171                    fdecl::Offer::Service(fdecl::OfferService {
8172                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
8173                            name: "coll_a".to_string()
8174                        })),
8175                        source_name: Some("fuchsia.logger.Log".to_string()),
8176                        target: Some(fdecl::Ref::Child(
8177                            fdecl::ChildRef {
8178                                name: "child_c".to_string(),
8179                                collection: None,
8180                            }
8181                        )),
8182                        target_name: Some("fuchsia.logger.Log".to_string()),
8183                        source_instance_filter: None,
8184                        ..Default::default()
8185                    }),
8186                    fdecl::Offer::Service(fdecl::OfferService {
8187                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
8188                            name: "coll_b".to_string()
8189                        })),
8190                        source_name: Some("fuchsia.logger.Log".to_string()),
8191                        target: Some(fdecl::Ref::Child(
8192                            fdecl::ChildRef {
8193                                name: "child_c".to_string(),
8194                                collection: None,
8195                            }
8196                        )),
8197                        target_name: Some("fuchsia.logger.Log".to_string()),
8198                        source_instance_filter: Some(vec!["a_different_default".to_string()]),
8199                        ..Default::default()
8200                    })
8201                ]);
8202                decl.children = Some(vec![
8203                    fdecl::Child {
8204                        name: Some("child_c".to_string()),
8205                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
8206                        startup: Some(fdecl::StartupMode::Lazy),
8207                        ..Default::default()
8208                    },
8209                ]);
8210                decl.collections = Some(vec![
8211                    fdecl::Collection {
8212                        name: Some("coll_a".into()),
8213                        durability: Some(fdecl::Durability::Transient),
8214                        ..Default::default()
8215                    },
8216                    fdecl::Collection {
8217                        name: Some("coll_b".into()),
8218                        durability: Some(fdecl::Durability::Transient),
8219                        ..Default::default()
8220                    },
8221                ]);
8222                decl
8223            },
8224            result = Ok(()),
8225        },
8226
8227        // dictionaries
8228        test_validate_source_dictionary => {
8229            input = fdecl::Component {
8230                program: Some(fdecl::Program {
8231                    runner: Some("elf".into()),
8232                    info: Some(fdata::Dictionary {
8233                        entries: None,
8234                        ..Default::default()
8235                    }),
8236                    ..Default::default()
8237                }),
8238                uses: Some(vec![
8239                    fdecl::Use::Protocol(fdecl::UseProtocol {
8240                        dependency_type: Some(fdecl::DependencyType::Strong),
8241                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8242                        source_dictionary: Some("bad//".into()),
8243                        source_name: Some("foo".into()),
8244                        target_path: Some("/svc/foo".into()),
8245                        ..Default::default()
8246                    }),
8247                ]),
8248                exposes: Some(vec![
8249                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
8250                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8251                            name: "missing".into(),
8252                            collection: None,
8253                        })),
8254                        source_dictionary: Some("in/dict".into()),
8255                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8256                        source_name: Some("foo".into()),
8257                        target_name: Some("bar".into()),
8258                        ..Default::default()
8259                    }),
8260                ]),
8261                offers: Some(vec![
8262                    fdecl::Offer::Service(fdecl::OfferService {
8263                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8264                        source_dictionary: Some("bad//".into()),
8265                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8266                            name: "child".into(),
8267                            collection: None,
8268                        })),
8269                        source_name: Some("foo".into()),
8270                        target_name: Some("bar".into()),
8271                        ..Default::default()
8272                    }),
8273                ]),
8274                children: Some(vec![
8275                    fdecl::Child {
8276                        name: Some("child".into()),
8277                        url: Some("fuchsia-pkg://child".into()),
8278                        startup: Some(fdecl::StartupMode::Lazy),
8279                        ..Default::default()
8280                    },
8281                ]),
8282                ..Default::default()
8283            },
8284            result = Err(ErrorList::new(vec![
8285                Error::invalid_field(DeclType::UseProtocol, "source_dictionary"),
8286                Error::invalid_child(DeclType::ExposeDirectory, "source", "missing"),
8287                Error::invalid_field(DeclType::OfferService, "source_dictionary"),
8288            ])),
8289        },
8290        test_validate_dictionary_too_long => {
8291            input = fdecl::Component {
8292                program: Some(fdecl::Program {
8293                    runner: Some("elf".into()),
8294                    info: Some(fdata::Dictionary {
8295                        entries: None,
8296                        ..Default::default()
8297                    }),
8298                    ..Default::default()
8299                }),
8300                uses: Some(vec![
8301                    fdecl::Use::Protocol(fdecl::UseProtocol {
8302                        dependency_type: Some(fdecl::DependencyType::Strong),
8303                        source: Some(fdecl::Ref::Parent( fdecl::ParentRef {} )),
8304                        source_dictionary: Some("a".repeat(4096)),
8305                        source_name: Some("foo".into()),
8306                        target_path: Some("/svc/foo".into()),
8307                        ..Default::default()
8308                    }),
8309                ]),
8310                ..Default::default()
8311            },
8312            result = Err(ErrorList::new(vec![
8313                Error::field_too_long(DeclType::UseProtocol, "source_dictionary"),
8314            ])),
8315        },
8316
8317        // environments
8318        test_validate_environment_empty => {
8319            input = {
8320                let mut decl = new_component_decl();
8321                decl.environments = Some(vec![fdecl::Environment {
8322                    name: None,
8323                    extends: None,
8324                    runners: None,
8325                    resolvers: None,
8326                    stop_timeout_ms: None,
8327                    debug_capabilities: None,
8328                    ..Default::default()
8329                }]);
8330                decl
8331            },
8332            result = Err(ErrorList::new(vec![
8333                Error::missing_field(DeclType::Environment, "name"),
8334                Error::missing_field(DeclType::Environment, "extends"),
8335            ])),
8336        },
8337
8338        test_validate_environment_no_stop_timeout => {
8339            input = {
8340                let mut decl = new_component_decl();
8341                decl.environments = Some(vec![fdecl::Environment {
8342                    name: Some("env".to_string()),
8343                    extends: Some(fdecl::EnvironmentExtends::None),
8344                    runners: None,
8345                    resolvers: None,
8346                    stop_timeout_ms: None,
8347                    ..Default::default()
8348                }]);
8349                decl
8350            },
8351            result = Err(ErrorList::new(vec![Error::missing_field(DeclType::Environment, "stop_timeout_ms")])),
8352        },
8353
8354        test_validate_environment_extends_stop_timeout => {
8355            input = {  let mut decl = new_component_decl();
8356                decl.environments = Some(vec![fdecl::Environment {
8357                    name: Some("env".to_string()),
8358                    extends: Some(fdecl::EnvironmentExtends::Realm),
8359                    runners: None,
8360                    resolvers: None,
8361                    stop_timeout_ms: None,
8362                    ..Default::default()
8363                }]);
8364                decl
8365            },
8366            result = Ok(()),
8367        },
8368        test_validate_environment_long_identifiers => {
8369            input = {
8370                let mut decl = new_component_decl();
8371                decl.environments = Some(vec![fdecl::Environment {
8372                    name: Some("a".repeat(256)),
8373                    extends: Some(fdecl::EnvironmentExtends::None),
8374                    runners: Some(vec![
8375                        fdecl::RunnerRegistration {
8376                            source_name: Some("a".repeat(256)),
8377                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8378                            target_name: Some("a".repeat(256)),
8379                            ..Default::default()
8380                        },
8381                    ]),
8382                    resolvers: Some(vec![
8383                        fdecl::ResolverRegistration {
8384                            resolver: Some("a".repeat(256)),
8385                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8386                            scheme: Some("a".repeat(256)),
8387                            ..Default::default()
8388                        },
8389                    ]),
8390                    stop_timeout_ms: Some(1234),
8391                    ..Default::default()
8392                }]);
8393                decl
8394            },
8395            result = Err(ErrorList::new(vec![
8396                Error::field_too_long(DeclType::Environment, "name"),
8397                Error::field_too_long(DeclType::RunnerRegistration, "source_name"),
8398                Error::field_too_long(DeclType::RunnerRegistration, "target_name"),
8399                Error::field_too_long(DeclType::ResolverRegistration, "resolver"),
8400                Error::field_too_long(DeclType::ResolverRegistration, "scheme"),
8401            ])),
8402        },
8403        test_validate_environment_empty_runner_resolver_fields => {
8404            input = {
8405                let mut decl = new_component_decl();
8406                decl.environments = Some(vec![fdecl::Environment {
8407                    name: Some("a".to_string()),
8408                    extends: Some(fdecl::EnvironmentExtends::None),
8409                    runners: Some(vec![
8410                        fdecl::RunnerRegistration {
8411                            source_name: None,
8412                            source: None,
8413                            target_name: None,
8414                            ..Default::default()
8415                        },
8416                    ]),
8417                    resolvers: Some(vec![
8418                        fdecl::ResolverRegistration {
8419                            resolver: None,
8420                            source: None,
8421                            scheme: None,
8422                            ..Default::default()
8423                        },
8424                    ]),
8425                    stop_timeout_ms: Some(1234),
8426                    ..Default::default()
8427                }]);
8428                decl
8429            },
8430            result = Err(ErrorList::new(vec![
8431                Error::missing_field(DeclType::RunnerRegistration, "source_name"),
8432                Error::missing_field(DeclType::RunnerRegistration, "source"),
8433                Error::missing_field(DeclType::RunnerRegistration, "target_name"),
8434                Error::missing_field(DeclType::ResolverRegistration, "resolver"),
8435                Error::missing_field(DeclType::ResolverRegistration, "source"),
8436                Error::missing_field(DeclType::ResolverRegistration, "scheme"),
8437            ])),
8438        },
8439        test_validate_environment_invalid_fields => {
8440            input = {
8441                let mut decl = new_component_decl();
8442                decl.environments = Some(vec![fdecl::Environment {
8443                    name: Some("a".to_string()),
8444                    extends: Some(fdecl::EnvironmentExtends::None),
8445                    runners: Some(vec![
8446                        fdecl::RunnerRegistration {
8447                            source_name: Some("^a".to_string()),
8448                            source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
8449                            target_name: Some("%a".to_string()),
8450                            ..Default::default()
8451                        },
8452                    ]),
8453                    resolvers: Some(vec![
8454                        fdecl::ResolverRegistration {
8455                            resolver: Some("^a".to_string()),
8456                            source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
8457                            scheme: Some("9scheme".to_string()),
8458                            ..Default::default()
8459                        },
8460                    ]),
8461                    stop_timeout_ms: Some(1234),
8462                    ..Default::default()
8463                }]);
8464                decl
8465            },
8466            result = Err(ErrorList::new(vec![
8467                Error::invalid_field(DeclType::RunnerRegistration, "source_name"),
8468                Error::invalid_field(DeclType::RunnerRegistration, "source"),
8469                Error::invalid_field(DeclType::RunnerRegistration, "target_name"),
8470                Error::invalid_field(DeclType::ResolverRegistration, "resolver"),
8471                Error::invalid_field(DeclType::ResolverRegistration, "source"),
8472                Error::invalid_field(DeclType::ResolverRegistration, "scheme"),
8473            ])),
8474        },
8475        test_validate_environment_missing_runner => {
8476            input = {
8477                let mut decl = new_component_decl();
8478                decl.environments = Some(vec![fdecl::Environment {
8479                    name: Some("a".to_string()),
8480                    extends: Some(fdecl::EnvironmentExtends::None),
8481                    runners: Some(vec![
8482                        fdecl::RunnerRegistration {
8483                            source_name: Some("dart".to_string()),
8484                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
8485                            target_name: Some("dart".to_string()),
8486                            ..Default::default()
8487                        },
8488                    ]),
8489                    resolvers: None,
8490                    stop_timeout_ms: Some(1234),
8491                    ..Default::default()
8492                }]);
8493                decl
8494            },
8495            result = Err(ErrorList::new(vec![
8496                Error::invalid_runner(DeclType::RunnerRegistration, "source_name", "dart"),
8497            ])),
8498        },
8499        test_validate_environment_duplicate_registrations => {
8500            input = {
8501                let mut decl = new_component_decl();
8502                decl.environments = Some(vec![fdecl::Environment {
8503                    name: Some("a".to_string()),
8504                    extends: Some(fdecl::EnvironmentExtends::None),
8505                    runners: Some(vec![
8506                        fdecl::RunnerRegistration {
8507                            source_name: Some("dart".to_string()),
8508                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8509                            target_name: Some("dart".to_string()),
8510                            ..Default::default()
8511                        },
8512                        fdecl::RunnerRegistration {
8513                            source_name: Some("other-dart".to_string()),
8514                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8515                            target_name: Some("dart".to_string()),
8516                            ..Default::default()
8517                        },
8518                    ]),
8519                    resolvers: Some(vec![
8520                        fdecl::ResolverRegistration {
8521                            resolver: Some("pkg_resolver".to_string()),
8522                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8523                            scheme: Some("fuchsia-pkg".to_string()),
8524                            ..Default::default()
8525                        },
8526                        fdecl::ResolverRegistration {
8527                            resolver: Some("base_resolver".to_string()),
8528                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8529                            scheme: Some("fuchsia-pkg".to_string()),
8530                            ..Default::default()
8531                        },
8532                    ]),
8533                    stop_timeout_ms: Some(1234),
8534                    ..Default::default()
8535                }]);
8536                decl
8537            },
8538            result = Err(ErrorList::new(vec![
8539                Error::duplicate_field(DeclType::RunnerRegistration, "target_name", "dart"),
8540                Error::duplicate_field(DeclType::ResolverRegistration, "scheme", "fuchsia-pkg"),
8541            ])),
8542        },
8543        test_validate_environment_from_missing_child => {
8544            input = {
8545                let mut decl = new_component_decl();
8546                decl.environments = Some(vec![fdecl::Environment {
8547                    name: Some("a".to_string()),
8548                    extends: Some(fdecl::EnvironmentExtends::None),
8549                    runners: Some(vec![
8550                        fdecl::RunnerRegistration {
8551                            source_name: Some("elf".to_string()),
8552                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8553                                name: "missing".to_string(),
8554                                collection: None,
8555                            })),
8556                            target_name: Some("elf".to_string()),
8557                            ..Default::default()
8558                        },
8559                    ]),
8560                    resolvers: Some(vec![
8561                        fdecl::ResolverRegistration {
8562                            resolver: Some("pkg_resolver".to_string()),
8563                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8564                                name: "missing".to_string(),
8565                                collection: None,
8566                            })),
8567                            scheme: Some("fuchsia-pkg".to_string()),
8568                            ..Default::default()
8569                        },
8570                    ]),
8571                    stop_timeout_ms: Some(1234),
8572                    ..Default::default()
8573                }]);
8574                decl
8575            },
8576            result = Err(ErrorList::new(vec![
8577                Error::invalid_child(DeclType::RunnerRegistration, "source", "missing"),
8578                Error::invalid_child(DeclType::ResolverRegistration, "source", "missing"),
8579            ])),
8580        },
8581        test_validate_environment_runner_child_cycle => {
8582            input = {
8583                let mut decl = new_component_decl();
8584                decl.environments = Some(vec![fdecl::Environment {
8585                    name: Some("env".to_string()),
8586                    extends: Some(fdecl::EnvironmentExtends::None),
8587                    runners: Some(vec![
8588                        fdecl::RunnerRegistration {
8589                            source_name: Some("elf".to_string()),
8590                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8591                                name: "child".to_string(),
8592                                collection: None,
8593                            })),
8594                            target_name: Some("elf".to_string()),
8595                            ..Default::default()
8596                        },
8597                    ]),
8598                    resolvers: None,
8599                    stop_timeout_ms: Some(1234),
8600                    ..Default::default()
8601                }]);
8602                decl.children = Some(vec![fdecl::Child {
8603                    name: Some("child".to_string()),
8604                    startup: Some(fdecl::StartupMode::Lazy),
8605                    on_terminate: None,
8606                    url: Some("fuchsia-pkg://child".to_string()),
8607                    environment: Some("env".to_string()),
8608                    ..Default::default()
8609                }]);
8610                decl
8611            },
8612            result = Err(ErrorList::new(vec![
8613                Error::dependency_cycle(
8614                    directed_graph::Error::CyclesDetected([vec!["child child", "environment env", "child child"]].iter().cloned().collect()).format_cycle()
8615                ),
8616            ])),
8617        },
8618        test_validate_environment_resolver_child_cycle => {
8619            input = {
8620                let mut decl = new_component_decl();
8621                decl.environments = Some(vec![fdecl::Environment {
8622                    name: Some("env".to_string()),
8623                    extends: Some(fdecl::EnvironmentExtends::None),
8624                    runners: None,
8625                    resolvers: Some(vec![
8626                        fdecl::ResolverRegistration {
8627                            resolver: Some("pkg_resolver".to_string()),
8628                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8629                                name: "child".to_string(),
8630                                collection: None,
8631                            })),
8632                            scheme: Some("fuchsia-pkg".to_string()),
8633                            ..Default::default()
8634                        },
8635                    ]),
8636                    stop_timeout_ms: Some(1234),
8637                    ..Default::default()
8638                }]);
8639                decl.children = Some(vec![fdecl::Child {
8640                    name: Some("child".to_string()),
8641                    startup: Some(fdecl::StartupMode::Lazy),
8642                    on_terminate: None,
8643                    url: Some("fuchsia-pkg://child".to_string()),
8644                    environment: Some("env".to_string()),
8645                    ..Default::default()
8646                }]);
8647                decl
8648            },
8649            result = Err(ErrorList::new(vec![
8650                Error::dependency_cycle(
8651                    directed_graph::Error::CyclesDetected([vec!["child child", "environment env", "child child"]].iter().cloned().collect()).format_cycle()
8652                ),
8653            ])),
8654        },
8655        test_validate_environment_resolver_multiple_children_cycle => {
8656            input = {
8657                let mut decl = new_component_decl();
8658                decl.environments = Some(vec![fdecl::Environment {
8659                    name: Some("env".to_string()),
8660                    extends: Some(fdecl::EnvironmentExtends::None),
8661                    runners: None,
8662                    resolvers: Some(vec![
8663                        fdecl::ResolverRegistration {
8664                            resolver: Some("pkg_resolver".to_string()),
8665                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8666                                name: "a".to_string(),
8667                                collection: None,
8668                            })),
8669                            scheme: Some("fuchsia-pkg".to_string()),
8670                            ..Default::default()
8671                        },
8672                    ]),
8673                    stop_timeout_ms: Some(1234),
8674                    ..Default::default()
8675                }]);
8676                decl.children = Some(vec![
8677                    fdecl::Child {
8678                        name: Some("a".to_string()),
8679                        startup: Some(fdecl::StartupMode::Lazy),
8680                        on_terminate: None,
8681                        url: Some("fuchsia-pkg://child-a".to_string()),
8682                        environment: None,
8683                        ..Default::default()
8684                    },
8685                    fdecl::Child {
8686                        name: Some("b".to_string()),
8687                        startup: Some(fdecl::StartupMode::Lazy),
8688                        on_terminate: None,
8689                        url: Some("fuchsia-pkg://child-b".to_string()),
8690                        environment: Some("env".to_string()),
8691                        ..Default::default()
8692                    },
8693                ]);
8694                decl.offers = Some(vec![fdecl::Offer::Service(fdecl::OfferService {
8695                    source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8696                        name: "b".to_string(),
8697                        collection: None,
8698                    })),
8699                    source_name: Some("thing".to_string()),
8700                    target: Some(fdecl::Ref::Child(
8701                    fdecl::ChildRef {
8702                        name: "a".to_string(),
8703                        collection: None,
8704                    }
8705                    )),
8706                    target_name: Some("thing".to_string()),
8707                    ..Default::default()
8708                })]);
8709                decl
8710            },
8711            result = Err(ErrorList::new(vec![
8712                Error::dependency_cycle(
8713                    directed_graph::Error::CyclesDetected([vec!["child a", "environment env", "child b", "child a"]].iter().cloned().collect()).format_cycle()
8714                ),
8715            ])),
8716        },
8717        test_validate_environment_debug_empty => {
8718            input = {
8719                let mut decl = new_component_decl();
8720                decl.environments = Some(vec![
8721                    fdecl::Environment {
8722                        name: Some("a".to_string()),
8723                        extends: Some(fdecl::EnvironmentExtends::None),
8724                        stop_timeout_ms: Some(2),
8725                        debug_capabilities:Some(vec![
8726                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8727                                source: None,
8728                                source_name: None,
8729                                target_name: None,
8730                                ..Default::default()
8731                            }),
8732                    ]),
8733                    ..Default::default()
8734                }]);
8735                decl
8736            },
8737            result = Err(ErrorList::new(vec![
8738                Error::missing_field(DeclType::DebugProtocolRegistration, "source"),
8739                Error::missing_field(DeclType::DebugProtocolRegistration, "source_name"),
8740                Error::missing_field(DeclType::DebugProtocolRegistration, "target_name"),
8741            ])),
8742        },
8743        test_validate_environment_debug_log_identifier => {
8744            input = {
8745                let mut decl = new_component_decl();
8746                decl.environments = Some(vec![
8747                    fdecl::Environment {
8748                        name: Some("a".to_string()),
8749                        extends: Some(fdecl::EnvironmentExtends::None),
8750                        stop_timeout_ms: Some(2),
8751                        debug_capabilities:Some(vec![
8752                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8753                                source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8754                                    name: "a".repeat(256),
8755                                    collection: None,
8756                                })),
8757                                source_name: Some(format!("{}", "a".repeat(256))),
8758                                target_name: Some(format!("{}", "b".repeat(256))),
8759                                ..Default::default()
8760                            }),
8761                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8762                                source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8763                                source_name: Some("a".to_string()),
8764                                target_name: Some(format!("{}", "b".repeat(256))),
8765                                ..Default::default()
8766                            }),
8767                    ]),
8768                    ..Default::default()
8769                }]);
8770                decl
8771            },
8772            result = Err(ErrorList::new(vec![
8773                Error::field_too_long(DeclType::DebugProtocolRegistration, "source.child.name"),
8774                Error::field_too_long(DeclType::DebugProtocolRegistration, "source_name"),
8775                Error::field_too_long(DeclType::DebugProtocolRegistration, "target_name"),
8776                Error::field_too_long(DeclType::DebugProtocolRegistration, "target_name"),
8777            ])),
8778        },
8779        test_validate_environment_debug_log_extraneous => {
8780            input = {
8781                let mut decl = new_component_decl();
8782                decl.environments = Some(vec![
8783                    fdecl::Environment {
8784                        name: Some("a".to_string()),
8785                        extends: Some(fdecl::EnvironmentExtends::None),
8786                        stop_timeout_ms: Some(2),
8787                        debug_capabilities:Some(vec![
8788                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8789                                source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8790                                    name: "logger".to_string(),
8791                                    collection: Some("modular".to_string()),
8792                                })),
8793                                source_name: Some("fuchsia.logger.Log".to_string()),
8794                                target_name: Some("fuchsia.logger.Log".to_string()),
8795                                ..Default::default()
8796                            }),
8797                    ]),
8798                    ..Default::default()
8799                }]);
8800                decl
8801            },
8802            result = Err(ErrorList::new(vec![
8803                Error::extraneous_field(DeclType::DebugProtocolRegistration, "source.child.collection"),
8804            ])),
8805        },
8806        test_validate_environment_debug_log_invalid_identifiers => {
8807            input = {
8808                let mut decl = new_component_decl();
8809                decl.environments = Some(vec![
8810                    fdecl::Environment {
8811                        name: Some("a".to_string()),
8812                        extends: Some(fdecl::EnvironmentExtends::None),
8813                        stop_timeout_ms: Some(2),
8814                        debug_capabilities:Some(vec![
8815                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8816                                source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8817                                    name: "^bad".to_string(),
8818                                    collection: None,
8819                                })),
8820                                source_name: Some("foo/".to_string()),
8821                                target_name: Some("/".to_string()),
8822                                ..Default::default()
8823                            }),
8824                    ]),
8825                    ..Default::default()
8826                }]);
8827                decl
8828            },
8829            result = Err(ErrorList::new(vec![
8830                Error::invalid_field(DeclType::DebugProtocolRegistration, "source.child.name"),
8831                Error::invalid_field(DeclType::DebugProtocolRegistration, "source_name"),
8832                Error::invalid_field(DeclType::DebugProtocolRegistration, "target_name"),
8833            ])),
8834        },
8835        test_validate_environment_debug_log_invalid_child => {
8836            input = {
8837                let mut decl = new_component_decl();
8838                decl.environments = Some(vec![
8839                    fdecl::Environment {
8840                        name: Some("a".to_string()),
8841                        extends: Some(fdecl::EnvironmentExtends::None),
8842                        stop_timeout_ms: Some(2),
8843                        debug_capabilities:Some(vec![
8844                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8845                                source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8846                                    name: "logger".to_string(),
8847                                    collection: None,
8848                                })),
8849                                source_name: Some("fuchsia.logger.LegacyLog".to_string()),
8850                                target_name: Some("fuchsia.logger.LegacyLog".to_string()),
8851                                ..Default::default()
8852                            }),
8853                    ]),
8854                    ..Default::default()
8855                }]);
8856                decl.children = Some(vec![
8857                    fdecl::Child {
8858                        name: Some("netstack".to_string()),
8859                        url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
8860                        startup: Some(fdecl::StartupMode::Lazy),
8861                        on_terminate: None,
8862                        environment: None,
8863                        ..Default::default()
8864                    },
8865                ]);
8866                decl
8867            },
8868            result = Err(ErrorList::new(vec![
8869                Error::invalid_child(DeclType::DebugProtocolRegistration, "source", "logger"),
8870
8871            ])),
8872        },
8873        test_validate_environment_debug_source_capability => {
8874            input = {
8875                let mut decl = new_component_decl();
8876                decl.environments = Some(vec![
8877                    fdecl::Environment {
8878                        name: Some("a".to_string()),
8879                        extends: Some(fdecl::EnvironmentExtends::None),
8880                        stop_timeout_ms: Some(2),
8881                        debug_capabilities:Some(vec![
8882                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8883                                source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
8884                                    name: "storage".to_string(),
8885                                })),
8886                                source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
8887                                target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
8888                                ..Default::default()
8889                            }),
8890                    ]),
8891                    ..Default::default()
8892                }]);
8893                decl
8894            },
8895            result = Err(ErrorList::new(vec![
8896                Error::invalid_field(DeclType::DebugProtocolRegistration, "source"),
8897            ])),
8898        },
8899
8900        // children
8901        test_validate_children_empty => {
8902            input = {
8903                let mut decl = new_component_decl();
8904                decl.children = Some(vec![fdecl::Child{
8905                    name: None,
8906                    url: None,
8907                    startup: None,
8908                    on_terminate: None,
8909                    environment: None,
8910                    ..Default::default()
8911                }]);
8912                decl
8913            },
8914            result = Err(ErrorList::new(vec![
8915                Error::missing_field(DeclType::Child, "name"),
8916                Error::missing_field(DeclType::Child, "url"),
8917                Error::missing_field(DeclType::Child, "startup"),
8918                // `on_terminate` is allowed to be None
8919            ])),
8920        },
8921        test_validate_children_invalid_identifiers => {
8922            input = {
8923                let mut decl = new_component_decl();
8924                decl.children = Some(vec![fdecl::Child{
8925                    name: Some("^bad".to_string()),
8926                    url: Some("scheme://invalid-port:99999999/path#frag".to_string()),
8927                    startup: Some(fdecl::StartupMode::Lazy),
8928                    on_terminate: None,
8929                    environment: None,
8930                    ..Default::default()
8931                }]);
8932                decl
8933            },
8934            result = Err(ErrorList::new(vec![
8935                Error::invalid_field(DeclType::Child, "name"),
8936                Error::invalid_url(DeclType::Child, "url", "\"scheme://invalid-port:99999999/path#frag\": Malformed URL: InvalidPort."),
8937            ])),
8938        },
8939        test_validate_children_long_identifiers => {
8940            input = {
8941                let mut decl = new_component_decl();
8942                decl.children = Some(vec![fdecl::Child{
8943                    name: Some("a".repeat(1025)),
8944                    url: Some(format!("fuchsia-pkg://{}", "a".repeat(4083))),
8945                    startup: Some(fdecl::StartupMode::Lazy),
8946                    on_terminate: None,
8947                    environment: Some("a".repeat(1025)),
8948                    ..Default::default()
8949                }]);
8950                decl
8951            },
8952            result = Err(ErrorList::new(vec![
8953                Error::field_too_long(DeclType::Child, "name"),
8954                Error::field_too_long(DeclType::Child, "url"),
8955                Error::field_too_long(DeclType::Child, "environment"),
8956                Error::invalid_environment(DeclType::Child, "environment", "a".repeat(1025)),
8957            ])),
8958        },
8959        test_validate_child_references_unknown_env => {
8960            input = {
8961                let mut decl = new_component_decl();
8962                decl.children = Some(vec![fdecl::Child{
8963                    name: Some("foo".to_string()),
8964                    url: Some("fuchsia-pkg://foo".to_string()),
8965                    startup: Some(fdecl::StartupMode::Lazy),
8966                    on_terminate: None,
8967                    environment: Some("test_env".to_string()),
8968                    ..Default::default()
8969                }]);
8970                decl
8971            },
8972            result = Err(ErrorList::new(vec![
8973                Error::invalid_environment(DeclType::Child, "environment", "test_env"),
8974            ])),
8975        },
8976
8977        // collections
8978        test_validate_collections_empty => {
8979            input = {
8980                let mut decl = new_component_decl();
8981                decl.collections = Some(vec![fdecl::Collection{
8982                    name: None,
8983                    durability: None,
8984                    environment: None,
8985                    allowed_offers: None,
8986                    allow_long_names: None,
8987                    ..Default::default()
8988                }]);
8989                decl
8990            },
8991            result = Err(ErrorList::new(vec![
8992                Error::missing_field(DeclType::Collection, "name"),
8993                Error::missing_field(DeclType::Collection, "durability"),
8994            ])),
8995        },
8996        test_validate_collections_invalid_identifiers => {
8997            input = {
8998                let mut decl = new_component_decl();
8999                decl.collections = Some(vec![fdecl::Collection{
9000                    name: Some("^bad".to_string()),
9001                    durability: Some(fdecl::Durability::Transient),
9002                    environment: None,
9003                    allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
9004                    allow_long_names: None,
9005                    ..Default::default()
9006                }]);
9007                decl
9008            },
9009            result = Err(ErrorList::new(vec![
9010                Error::invalid_field(DeclType::Collection, "name"),
9011            ])),
9012        },
9013        test_validate_collections_long_identifiers => {
9014            input = {
9015                let mut decl = new_component_decl();
9016                decl.collections = Some(vec![fdecl::Collection{
9017                    name: Some("a".repeat(1025)),
9018                    durability: Some(fdecl::Durability::Transient),
9019                    environment: None,
9020                    allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
9021                    allow_long_names: None,
9022                    ..Default::default()
9023                }]);
9024                decl
9025            },
9026            result = Err(ErrorList::new(vec![
9027                Error::field_too_long(DeclType::Collection, "name"),
9028            ])),
9029        },
9030        test_validate_collection_references_unknown_env => {
9031            input = {
9032                let mut decl = new_component_decl();
9033                decl.collections = Some(vec![fdecl::Collection {
9034                    name: Some("foo".to_string()),
9035                    durability: Some(fdecl::Durability::Transient),
9036                    environment: Some("test_env".to_string()),
9037                    allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
9038                    allow_long_names: None,
9039                    ..Default::default()
9040                }]);
9041                decl
9042            },
9043            result = Err(ErrorList::new(vec![
9044                Error::invalid_environment(DeclType::Collection, "environment", "test_env"),
9045            ])),
9046        },
9047
9048        // capabilities
9049        test_validate_capabilities_empty => {
9050            input = {
9051                let mut decl = new_component_decl();
9052                decl.capabilities = Some(vec![
9053                    fdecl::Capability::Service(fdecl::Service {
9054                        name: None,
9055                        source_path: None,
9056                        ..Default::default()
9057                    }),
9058                    fdecl::Capability::Protocol(fdecl::Protocol {
9059                        name: None,
9060                        source_path: None,
9061                        ..Default::default()
9062                    }),
9063                    fdecl::Capability::Directory(fdecl::Directory {
9064                        name: None,
9065                        source_path: None,
9066                        rights: None,
9067                        ..Default::default()
9068                    }),
9069                    fdecl::Capability::Storage(fdecl::Storage {
9070                        name: None,
9071                        source: None,
9072                        backing_dir: None,
9073                        subdir: None,
9074                        storage_id: None,
9075                        ..Default::default()
9076                    }),
9077                    fdecl::Capability::Runner(fdecl::Runner {
9078                        name: None,
9079                        source_path: None,
9080                        ..Default::default()
9081                    }),
9082                    fdecl::Capability::Resolver(fdecl::Resolver {
9083                        name: None,
9084                        source_path: None,
9085                        ..Default::default()
9086                    }),
9087                    fdecl::Capability::Dictionary(fdecl::Dictionary {
9088                        ..Default::default()
9089                    }),
9090                ]);
9091                decl
9092            },
9093            result = Err(ErrorList::new(vec![
9094                Error::missing_field(DeclType::Dictionary, "name"),
9095                Error::missing_field(DeclType::Service, "name"),
9096                Error::missing_field(DeclType::Service, "source_path"),
9097                Error::missing_field(DeclType::Protocol, "name"),
9098                Error::missing_field(DeclType::Protocol, "source_path"),
9099                Error::missing_field(DeclType::Directory, "name"),
9100                Error::missing_field(DeclType::Directory, "source_path"),
9101                Error::missing_field(DeclType::Directory, "rights"),
9102                Error::missing_field(DeclType::Storage, "source"),
9103                Error::missing_field(DeclType::Storage, "name"),
9104                Error::missing_field(DeclType::Storage, "storage_id"),
9105                Error::missing_field(DeclType::Storage, "backing_dir"),
9106                Error::missing_field(DeclType::Runner, "name"),
9107                Error::missing_field(DeclType::Runner, "source_path"),
9108                Error::missing_field(DeclType::Resolver, "name"),
9109                Error::missing_field(DeclType::Resolver, "source_path"),
9110            ])),
9111        },
9112        test_validate_capabilities_invalid_identifiers => {
9113            input = {
9114                let mut decl = new_component_decl();
9115                decl.capabilities = Some(vec![
9116                    fdecl::Capability::Service(fdecl::Service {
9117                        name: Some("^bad".to_string()),
9118                        source_path: Some("&bad".to_string()),
9119                        ..Default::default()
9120                    }),
9121                    fdecl::Capability::Protocol(fdecl::Protocol {
9122                        name: Some("^bad".to_string()),
9123                        source_path: Some("&bad".to_string()),
9124                        ..Default::default()
9125                    }),
9126                    fdecl::Capability::Directory(fdecl::Directory {
9127                        name: Some("^bad".to_string()),
9128                        source_path: Some("&bad".to_string()),
9129                        rights: Some(fio::Operations::CONNECT),
9130                        ..Default::default()
9131                    }),
9132                    fdecl::Capability::Storage(fdecl::Storage {
9133                        name: Some("^bad".to_string()),
9134                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9135                            name: "/bad".to_string()
9136                        })),
9137                        backing_dir: Some("&bad".to_string()),
9138                        subdir: None,
9139                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9140                        ..Default::default()
9141                    }),
9142                    fdecl::Capability::Runner(fdecl::Runner {
9143                        name: Some("^bad".to_string()),
9144                        source_path: Some("&bad".to_string()),
9145                        ..Default::default()
9146                    }),
9147                    fdecl::Capability::Resolver(fdecl::Resolver {
9148                        name: Some("^bad".to_string()),
9149                        source_path: Some("&bad".to_string()),
9150                        ..Default::default()
9151                    }),
9152                    fdecl::Capability::Dictionary(fdecl::Dictionary {
9153                        name: Some("^bad".to_string()),
9154                        ..Default::default()
9155                    }),
9156                ]);
9157                decl
9158            },
9159            result = Err(ErrorList::new(vec![
9160                Error::invalid_field(DeclType::Dictionary, "name"),
9161                Error::invalid_field(DeclType::Service, "name"),
9162                Error::invalid_field(DeclType::Service, "source_path"),
9163                Error::invalid_field(DeclType::Protocol, "name"),
9164                Error::invalid_field(DeclType::Protocol, "source_path"),
9165                Error::invalid_field(DeclType::Directory, "name"),
9166                Error::invalid_field(DeclType::Directory, "source_path"),
9167                Error::invalid_field(DeclType::Storage, "source"),
9168                Error::invalid_field(DeclType::Storage, "name"),
9169                Error::invalid_field(DeclType::Storage, "backing_dir"),
9170                Error::invalid_field(DeclType::Runner, "name"),
9171                Error::invalid_field(DeclType::Runner, "source_path"),
9172                Error::invalid_field(DeclType::Resolver, "name"),
9173                Error::invalid_field(DeclType::Resolver, "source_path"),
9174            ])),
9175        },
9176        test_validate_capabilities_invalid_child => {
9177            input = {
9178                let mut decl = new_component_decl();
9179                decl.capabilities = Some(vec![
9180                    fdecl::Capability::Storage(fdecl::Storage {
9181                        name: Some("foo".to_string()),
9182                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9183                            name: "invalid".to_string(),
9184                        })),
9185                        backing_dir: Some("foo".to_string()),
9186                        subdir: None,
9187                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9188                        ..Default::default()
9189                    }),
9190                ]);
9191                decl
9192            },
9193            result = Err(ErrorList::new(vec![
9194                Error::invalid_field(DeclType::Storage, "source"),
9195            ])),
9196        },
9197        test_validate_capabilities_long_identifiers => {
9198            input = {
9199                let mut decl = new_component_decl();
9200                decl.capabilities = Some(vec![
9201                    fdecl::Capability::Service(fdecl::Service {
9202                        name: Some("a".repeat(256)),
9203                        source_path: Some("/c".repeat(2048)),
9204                        ..Default::default()
9205                    }),
9206                    fdecl::Capability::Protocol(fdecl::Protocol {
9207                        name: Some("a".repeat(256)),
9208                        source_path: Some("/c".repeat(2048)),
9209                        ..Default::default()
9210                    }),
9211                    fdecl::Capability::Directory(fdecl::Directory {
9212                        name: Some("a".repeat(256)),
9213                        source_path: Some("/c".repeat(2048)),
9214                        rights: Some(fio::Operations::CONNECT),
9215                        ..Default::default()
9216                    }),
9217                    fdecl::Capability::Storage(fdecl::Storage {
9218                        name: Some("a".repeat(256)),
9219                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
9220                            name: "b".repeat(256),
9221                            collection: None,
9222                        })),
9223                        backing_dir: Some(format!("{}", "c".repeat(256))),
9224                        subdir: None,
9225                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9226                        ..Default::default()
9227                    }),
9228                    fdecl::Capability::Runner(fdecl::Runner {
9229                        name: Some("a".repeat(256)),
9230                        source_path: Some("/c".repeat(2048)),
9231                        ..Default::default()
9232                    }),
9233                    fdecl::Capability::Resolver(fdecl::Resolver {
9234                        name: Some("a".repeat(256)),
9235                        source_path: Some("/c".repeat(2048)),
9236                        ..Default::default()
9237                    }),
9238                    fdecl::Capability::Dictionary(fdecl::Dictionary {
9239                        name: Some("a".repeat(256)),
9240                        ..Default::default()
9241                    }),
9242                ]);
9243                decl
9244            },
9245            result = Err(ErrorList::new(vec![
9246                Error::field_too_long(DeclType::Dictionary, "name"),
9247                Error::field_too_long(DeclType::Service, "name"),
9248                Error::field_too_long(DeclType::Service, "source_path"),
9249                Error::field_too_long(DeclType::Protocol, "name"),
9250                Error::field_too_long(DeclType::Protocol, "source_path"),
9251                Error::field_too_long(DeclType::Directory, "name"),
9252                Error::field_too_long(DeclType::Directory, "source_path"),
9253                Error::field_too_long(DeclType::Storage, "source.child.name"),
9254                Error::field_too_long(DeclType::Storage, "name"),
9255                Error::field_too_long(DeclType::Storage, "backing_dir"),
9256                Error::field_too_long(DeclType::Runner, "name"),
9257                Error::field_too_long(DeclType::Runner, "source_path"),
9258                Error::field_too_long(DeclType::Resolver, "name"),
9259                Error::field_too_long(DeclType::Resolver, "source_path"),
9260            ])),
9261        },
9262        test_validate_capabilities_duplicate_name => {
9263            input = {
9264                let mut decl = new_component_decl();
9265                decl.capabilities = Some(vec![
9266                    fdecl::Capability::Service(fdecl::Service {
9267                        name: Some("service".to_string()),
9268                        source_path: Some("/service".to_string()),
9269                        ..Default::default()
9270                    }),
9271                    fdecl::Capability::Service(fdecl::Service {
9272                        name: Some("service".to_string()),
9273                        source_path: Some("/service".to_string()),
9274                        ..Default::default()
9275                    }),
9276                    fdecl::Capability::Protocol(fdecl::Protocol {
9277                        name: Some("protocol".to_string()),
9278                        source_path: Some("/protocol".to_string()),
9279                        ..Default::default()
9280                    }),
9281                    fdecl::Capability::Protocol(fdecl::Protocol {
9282                        name: Some("protocol".to_string()),
9283                        source_path: Some("/protocol".to_string()),
9284                        ..Default::default()
9285                    }),
9286                    fdecl::Capability::Directory(fdecl::Directory {
9287                        name: Some("directory".to_string()),
9288                        source_path: Some("/directory".to_string()),
9289                        rights: Some(fio::Operations::CONNECT),
9290                        ..Default::default()
9291                    }),
9292                    fdecl::Capability::Directory(fdecl::Directory {
9293                        name: Some("directory".to_string()),
9294                        source_path: Some("/directory".to_string()),
9295                        rights: Some(fio::Operations::CONNECT),
9296                        ..Default::default()
9297                    }),
9298                    fdecl::Capability::Storage(fdecl::Storage {
9299                        name: Some("storage".to_string()),
9300                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
9301                        backing_dir: Some("directory".to_string()),
9302                        subdir: None,
9303                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9304                        ..Default::default()
9305                    }),
9306                    fdecl::Capability::Storage(fdecl::Storage {
9307                        name: Some("storage".to_string()),
9308                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
9309                        backing_dir: Some("directory".to_string()),
9310                        subdir: None,
9311                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9312                        ..Default::default()
9313                    }),
9314                    fdecl::Capability::Runner(fdecl::Runner {
9315                        name: Some("runner".to_string()),
9316                        source_path: Some("/runner".to_string()),
9317                        ..Default::default()
9318                    }),
9319                    fdecl::Capability::Runner(fdecl::Runner {
9320                        name: Some("runner".to_string()),
9321                        source_path: Some("/runner".to_string()),
9322                        ..Default::default()
9323                    }),
9324                    fdecl::Capability::Resolver(fdecl::Resolver {
9325                        name: Some("resolver".to_string()),
9326                        source_path: Some("/resolver".to_string()),
9327                        ..Default::default()
9328                    }),
9329                    fdecl::Capability::Resolver(fdecl::Resolver {
9330                        name: Some("resolver".to_string()),
9331                        source_path: Some("/resolver".to_string()),
9332                        ..Default::default()
9333                    }),
9334                    fdecl::Capability::Dictionary(fdecl::Dictionary {
9335                        name: Some("dictionary".to_string()),
9336                        ..Default::default()
9337                    }),
9338                    fdecl::Capability::Dictionary(fdecl::Dictionary {
9339                        name: Some("dictionary".to_string()),
9340                        ..Default::default()
9341                    }),
9342                ]);
9343                decl
9344            },
9345            result = Err(ErrorList::new(vec![
9346                Error::duplicate_field(DeclType::Dictionary, "name", "dictionary"),
9347                Error::duplicate_field(DeclType::Service, "name", "service"),
9348                Error::duplicate_field(DeclType::Protocol, "name", "protocol"),
9349                Error::duplicate_field(DeclType::Directory, "name", "directory"),
9350                Error::duplicate_field(DeclType::Storage, "name", "storage"),
9351                Error::duplicate_field(DeclType::Runner, "name", "runner"),
9352                Error::duplicate_field(DeclType::Resolver, "name", "resolver"),
9353            ])),
9354        },
9355        test_validate_invalid_service_aggregation_conflicting_filter => {
9356            input = {
9357                let mut decl = new_component_decl();
9358                decl.offers = Some(vec![
9359                    fdecl::Offer::Service(fdecl::OfferService {
9360                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9361                            name: "coll_a".to_string()
9362                        })),
9363                        source_name: Some("fuchsia.logger.Log".to_string()),
9364                        target: Some(fdecl::Ref::Child(
9365                            fdecl::ChildRef {
9366                                name: "child_c".to_string(),
9367                                collection: None,
9368                            }
9369                        )),
9370                        target_name: Some("fuchsia.logger.Log1".to_string()),
9371                        source_instance_filter: Some(vec!["default".to_string()]),
9372                        ..Default::default()
9373                    }),
9374                    fdecl::Offer::Service(fdecl::OfferService {
9375                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9376                            name: "coll_b".to_string()
9377                        })),
9378                        source_name: Some("fuchsia.logger.Log".to_string()),
9379                        target: Some(fdecl::Ref::Child(
9380                            fdecl::ChildRef {
9381                                name: "child_c".to_string(),
9382                                collection: None,
9383                            }
9384                        )),
9385                        target_name: Some("fuchsia.logger.Log1".to_string()),
9386                        source_instance_filter: Some(vec!["default".to_string()]),
9387                        ..Default::default()
9388                    }),
9389                ]);
9390                decl.collections = Some(vec![
9391                    fdecl::Collection {
9392                        name: Some("coll_a".to_string()),
9393                        durability: Some(fdecl::Durability::Transient),
9394                        ..Default::default()
9395                    },
9396                    fdecl::Collection {
9397                        name: Some("coll_b".to_string()),
9398                        durability: Some(fdecl::Durability::Transient),
9399                        ..Default::default()
9400                    },
9401                ]);
9402                decl.children = Some(vec![
9403                    fdecl::Child {
9404                        name: Some("child_c".to_string()),
9405                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9406                        startup: Some(fdecl::StartupMode::Lazy),
9407                        ..Default::default()
9408                    },
9409                ]);
9410                decl
9411            },
9412            result = Err(ErrorList::new(vec![
9413                Error::invalid_aggregate_offer("Conflicting source_instance_filter in aggregate \
9414                   service offer, instance_name 'default' seen in filter lists multiple times"),
9415            ])),
9416        },
9417
9418        test_validate_invalid_service_aggregation_conflicting_source_name => {
9419            input = {
9420                let mut decl = new_component_decl();
9421                decl.offers = Some(vec![
9422                    fdecl::Offer::Service(fdecl::OfferService {
9423                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9424                            name: "coll_a".into()
9425                        })),
9426                        source_name: Some("fuchsia.logger.Log".to_string()),
9427                        target: Some(fdecl::Ref::Child(
9428                            fdecl::ChildRef {
9429                                name: "child_c".to_string(),
9430                                collection: None,
9431                            }
9432                        )),
9433                        target_name: Some("fuchsia.logger.Log2".to_string()),
9434                        source_instance_filter: Some(vec!["default2".to_string()]),
9435                        ..Default::default()
9436                    }),
9437                    fdecl::Offer::Service(fdecl::OfferService {
9438                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9439                            name: "coll_b".into()
9440                        })),
9441                        source_name: Some("fuchsia.logger.LogAlt".to_string()),
9442                        target: Some(fdecl::Ref::Child(
9443                            fdecl::ChildRef {
9444                                name: "child_c".to_string(),
9445                                collection: None,
9446                            }
9447                        )),
9448                        target_name: Some("fuchsia.logger.Log2".to_string()),
9449                        source_instance_filter: Some(vec!["default".to_string()]),
9450                        ..Default::default()
9451                    })
9452                ]);
9453                decl.collections = Some(vec![
9454                    fdecl::Collection {
9455                        name: Some("coll_a".to_string()),
9456                        durability: Some(fdecl::Durability::Transient),
9457                        ..Default::default()
9458                    },
9459                    fdecl::Collection {
9460                        name: Some("coll_b".to_string()),
9461                        durability: Some(fdecl::Durability::Transient),
9462                        ..Default::default()
9463                    },
9464                ]);
9465                decl.children = Some(vec![
9466                    fdecl::Child {
9467                        name: Some("child_c".to_string()),
9468                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9469                        startup: Some(fdecl::StartupMode::Lazy),
9470                        on_terminate: None,
9471                        environment: None,
9472                        ..Default::default()
9473                    },
9474                ]);
9475                decl
9476            },
9477            result = Err(ErrorList::new(vec![
9478                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."),
9479            ])),
9480        },
9481
9482        test_validate_resolvers_missing_from_offer => {
9483            input = {
9484                let mut decl = new_component_decl();
9485                decl.offers = Some(vec![fdecl::Offer::Resolver(fdecl::OfferResolver {
9486                    source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
9487                    source_name: Some("a".to_string()),
9488                    target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
9489                    target_name: Some("a".to_string()),
9490                    ..Default::default()
9491                })]);
9492                decl.children = Some(vec![fdecl::Child {
9493                    name: Some("child".to_string()),
9494                    url: Some("test:///child".to_string()),
9495                    startup: Some(fdecl::StartupMode::Eager),
9496                    on_terminate: None,
9497                    environment: None,
9498                    ..Default::default()
9499                }]);
9500                decl
9501            },
9502            result = Err(ErrorList::new(vec![
9503                Error::invalid_capability(DeclType::OfferResolver, "source", "a"),
9504            ])),
9505        },
9506        test_validate_resolvers_missing_from_expose => {
9507            input = {
9508                let mut decl = new_component_decl();
9509                decl.exposes = Some(vec![fdecl::Expose::Resolver(fdecl::ExposeResolver {
9510                    source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
9511                    source_name: Some("a".to_string()),
9512                    target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
9513                    target_name: Some("a".to_string()),
9514                    ..Default::default()
9515                })]);
9516                decl
9517            },
9518            result = Err(ErrorList::new(vec![
9519                Error::invalid_capability(DeclType::ExposeResolver, "source", "a"),
9520            ])),
9521        },
9522
9523        test_validate_config_missing_config => {
9524            input = {
9525                let mut decl = new_component_decl();
9526                decl.config = Some(fdecl::ConfigSchema{
9527                    fields: None,
9528                    checksum: None,
9529                    value_source: None,
9530                    ..Default::default()
9531                });
9532                decl
9533            },
9534            result = Err(ErrorList::new(vec![
9535                Error::missing_field(DeclType::ConfigSchema, "fields"),
9536                Error::missing_field(DeclType::ConfigSchema, "checksum"),
9537                Error::missing_field(DeclType::ConfigSchema, "value_source"),
9538            ])),
9539        },
9540
9541        test_validate_config_missing_config_field => {
9542            input = {
9543                let mut decl = new_component_decl();
9544                decl.config = Some(fdecl::ConfigSchema{
9545                    fields: Some(vec![
9546                        fdecl::ConfigField {
9547                            key: None,
9548                            type_: None,
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 = Err(ErrorList::new(vec![
9559                Error::missing_field(DeclType::ConfigField, "key"),
9560                Error::missing_field(DeclType::ConfigField, "value_type"),
9561            ])),
9562        },
9563
9564        test_validate_config_bool => {
9565            input = {
9566                let mut decl = new_component_decl();
9567                decl.config = Some(fdecl::ConfigSchema{
9568                    fields: Some(vec![
9569                        fdecl::ConfigField {
9570                            key: Some("test".to_string()),
9571                            type_: Some(fdecl::ConfigType {
9572                                layout: fdecl::ConfigTypeLayout::Bool,
9573                                parameters: Some(vec![]),
9574                                constraints: vec![]
9575                            }),
9576                            ..Default::default()
9577                        }
9578                    ]),
9579                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9580                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9581                    ..Default::default()
9582                });
9583                decl
9584            },
9585            result = Ok(()),
9586        },
9587
9588        test_validate_config_bool_extra_constraint => {
9589            input = {
9590                let mut decl = new_component_decl();
9591                decl.config = Some(fdecl::ConfigSchema{
9592                    fields: Some(vec![
9593                        fdecl::ConfigField {
9594                            key: Some("test".to_string()),
9595                            type_: Some(fdecl::ConfigType {
9596                                layout: fdecl::ConfigTypeLayout::Bool,
9597                                parameters: Some(vec![]),
9598                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9599                            }),
9600                            ..Default::default()
9601                        }
9602                    ]),
9603                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9604                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9605                    ..Default::default()
9606                });
9607                decl
9608            },
9609            result = Err(ErrorList::new(vec![
9610                Error::extraneous_field(DeclType::ConfigType, "constraints")
9611            ])),
9612        },
9613
9614        test_validate_config_bool_missing_parameters => {
9615            input = {
9616                let mut decl = new_component_decl();
9617                decl.config = Some(fdecl::ConfigSchema{
9618                    fields: Some(vec![
9619                        fdecl::ConfigField {
9620                            key: Some("test".to_string()),
9621                            type_: Some(fdecl::ConfigType {
9622                                layout: fdecl::ConfigTypeLayout::Bool,
9623                                parameters: None,
9624                                constraints: vec![]
9625                            }),
9626                            ..Default::default()
9627                        }
9628                    ]),
9629                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9630                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9631                    ..Default::default()
9632                });
9633                decl
9634            },
9635            result = Err(ErrorList::new(vec![
9636                Error::missing_field(DeclType::ConfigType, "parameters")
9637            ])),
9638        },
9639
9640        test_validate_config_string => {
9641            input = {
9642                let mut decl = new_component_decl();
9643                decl.config = Some(fdecl::ConfigSchema{
9644                    fields: Some(vec![
9645                        fdecl::ConfigField {
9646                            key: Some("test".to_string()),
9647                            type_: Some(fdecl::ConfigType {
9648                                layout: fdecl::ConfigTypeLayout::String,
9649                                parameters: Some(vec![]),
9650                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9651                            }),
9652                            ..Default::default()
9653                        }
9654                    ]),
9655                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9656                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9657                    ..Default::default()
9658                });
9659                decl
9660            },
9661            result = Ok(()),
9662        },
9663
9664        test_validate_config_string_missing_parameter => {
9665            input = {
9666                let mut decl = new_component_decl();
9667                decl.config = Some(fdecl::ConfigSchema{
9668                    fields: Some(vec![
9669                        fdecl::ConfigField {
9670                            key: Some("test".to_string()),
9671                            type_: Some(fdecl::ConfigType {
9672                                layout: fdecl::ConfigTypeLayout::String,
9673                                parameters: None,
9674                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9675                            }),
9676                            ..Default::default()
9677                        }
9678                    ]),
9679                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9680                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9681                    ..Default::default()
9682                });
9683                decl
9684            },
9685            result = Err(ErrorList::new(vec![
9686                Error::missing_field(DeclType::ConfigType, "parameters")
9687            ])),
9688        },
9689
9690        test_validate_config_string_missing_constraint => {
9691            input = {
9692                let mut decl = new_component_decl();
9693                decl.config = Some(fdecl::ConfigSchema{
9694                    fields: Some(vec![
9695                        fdecl::ConfigField {
9696                            key: Some("test".to_string()),
9697                            type_: Some(fdecl::ConfigType {
9698                                layout: fdecl::ConfigTypeLayout::String,
9699                                parameters: Some(vec![]),
9700                                constraints: vec![]
9701                            }),
9702                            ..Default::default()
9703                        }
9704                    ]),
9705                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9706                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9707                    ..Default::default()
9708                });
9709                decl
9710            },
9711            result = Err(ErrorList::new(vec![
9712                Error::missing_field(DeclType::ConfigType, "constraints")
9713            ])),
9714        },
9715
9716        test_validate_config_string_extra_constraint => {
9717            input = {
9718                let mut decl = new_component_decl();
9719                decl.config = Some(fdecl::ConfigSchema{
9720                    fields: Some(vec![
9721                        fdecl::ConfigField {
9722                            key: Some("test".to_string()),
9723                            type_: Some(fdecl::ConfigType {
9724                                layout: fdecl::ConfigTypeLayout::String,
9725                                parameters: Some(vec![]),
9726                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10), fdecl::LayoutConstraint::MaxSize(10)]
9727                            }),
9728                            ..Default::default()
9729                        }
9730                    ]),
9731                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9732                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9733                    ..Default::default()
9734                });
9735                decl
9736            },
9737            result = Err(ErrorList::new(vec![
9738                Error::extraneous_field(DeclType::ConfigType, "constraints")
9739            ])),
9740        },
9741
9742        test_validate_config_vector_bool => {
9743            input = {
9744                let mut decl = new_component_decl();
9745                decl.config = Some(fdecl::ConfigSchema{
9746                    fields: Some(vec![
9747                        fdecl::ConfigField {
9748                            key: Some("test".to_string()),
9749                            type_: Some(fdecl::ConfigType {
9750                                layout: fdecl::ConfigTypeLayout::Vector,
9751                                parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9752                                    layout: fdecl::ConfigTypeLayout::Bool,
9753                                    parameters: Some(vec![]),
9754                                    constraints: vec![],
9755                                })]),
9756                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9757                            }),
9758                            ..Default::default()
9759                        }
9760                    ]),
9761                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9762                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9763                    ..Default::default()
9764                });
9765                decl
9766            },
9767            result = Ok(()),
9768        },
9769
9770        test_validate_config_vector_extra_parameter => {
9771            input = {
9772                let mut decl = new_component_decl();
9773                decl.config = Some(fdecl::ConfigSchema{
9774                    fields: Some(vec![
9775                        fdecl::ConfigField {
9776                            key: Some("test".to_string()),
9777                            type_: Some(fdecl::ConfigType {
9778                                layout: fdecl::ConfigTypeLayout::Vector,
9779                                parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9780                                    layout: fdecl::ConfigTypeLayout::Bool,
9781                                    parameters: Some(vec![]),
9782                                    constraints: vec![],
9783                                }), fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9784                                    layout: fdecl::ConfigTypeLayout::Uint8,
9785                                    parameters: Some(vec![]),
9786                                    constraints: vec![],
9787                                })]),
9788                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9789                            }),
9790                            ..Default::default()
9791                        }
9792                    ]),
9793                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9794                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9795                    ..Default::default()
9796                });
9797                decl
9798            },
9799            result = Err(ErrorList::new(vec![
9800                Error::extraneous_field(DeclType::ConfigType, "parameters")
9801            ])),
9802        },
9803
9804        test_validate_config_vector_missing_parameter => {
9805            input = {
9806                let mut decl = new_component_decl();
9807                decl.config = Some(fdecl::ConfigSchema{
9808                    fields: Some(vec![
9809                        fdecl::ConfigField {
9810                            key: Some("test".to_string()),
9811                            type_: Some(fdecl::ConfigType {
9812                                layout: fdecl::ConfigTypeLayout::Vector,
9813                                parameters: Some(vec![]),
9814                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9815                            }),
9816                            ..Default::default()
9817                        }
9818                    ]),
9819                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9820                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9821                    ..Default::default()
9822                });
9823                decl
9824            },
9825            result = Err(ErrorList::new(vec![
9826                Error::missing_field(DeclType::ConfigType, "parameters")
9827            ])),
9828        },
9829
9830        test_validate_config_vector_string => {
9831            input = {
9832                let mut decl = new_component_decl();
9833                decl.config = Some(fdecl::ConfigSchema{
9834                    fields: Some(vec![
9835                        fdecl::ConfigField {
9836                            key: Some("test".to_string()),
9837                            type_: Some(fdecl::ConfigType {
9838                                layout: fdecl::ConfigTypeLayout::Vector,
9839                                parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9840                                    layout: fdecl::ConfigTypeLayout::String,
9841                                    parameters: Some(vec![]),
9842                                    constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9843                                })]),
9844                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9845                            }),
9846                            ..Default::default()
9847                        }
9848                    ]),
9849                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9850                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9851                    ..Default::default()
9852                });
9853                decl
9854            },
9855            result = Ok(()),
9856        },
9857
9858        test_validate_config_vector_vector => {
9859            input = {
9860                let mut decl = new_component_decl();
9861                decl.config = Some(fdecl::ConfigSchema{
9862                    fields: Some(vec![
9863                        fdecl::ConfigField {
9864                            key: Some("test".to_string()),
9865                            type_: Some(fdecl::ConfigType {
9866                                layout: fdecl::ConfigTypeLayout::Vector,
9867                                parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9868                                    layout: fdecl::ConfigTypeLayout::Vector,
9869                                    parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9870                                        layout: fdecl::ConfigTypeLayout::String,
9871                                        parameters: Some(vec![]),
9872                                        constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9873                                    })]),
9874                                    constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9875                                })]),
9876                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9877                            }),
9878                            ..Default::default()
9879                        }
9880                    ]),
9881                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9882                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9883                    ..Default::default()
9884                });
9885                decl
9886            },
9887            result = Err(ErrorList::new(vec![
9888                Error::nested_vector()
9889            ])),
9890        },
9891
9892        test_validate_exposes_invalid_aggregation_different_availability => {
9893            input = {
9894                let mut decl = new_component_decl();
9895                decl.exposes = Some(vec![
9896                    fdecl::Expose::Service(fdecl::ExposeService {
9897                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9898                            name: "coll_a".into()
9899                        })),
9900                        source_name: Some("fuchsia.logger.Log".to_string()),
9901                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
9902                        target_name: Some("fuchsia.logger.Log".to_string()),
9903                        availability: Some(fdecl::Availability::Required),
9904                        ..Default::default()
9905                    }),
9906                    fdecl::Expose::Service(fdecl::ExposeService {
9907                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9908                            name: "coll_b".into()
9909                        })),
9910                        source_name: Some("fuchsia.logger.Log".to_string()),
9911                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
9912                        target_name: Some("fuchsia.logger.Log".to_string()),
9913                        availability: Some(fdecl::Availability::Optional),
9914                        ..Default::default()
9915                    })
9916                ]);
9917                decl.collections = Some(vec![
9918                    fdecl::Collection {
9919                        name: Some("coll_a".to_string()),
9920                        durability: Some(fdecl::Durability::Transient),
9921                        ..Default::default()
9922                    },
9923                    fdecl::Collection {
9924                        name: Some("coll_b".to_string()),
9925                        durability: Some(fdecl::Durability::Transient),
9926                        ..Default::default()
9927                    },
9928                ]);
9929                decl
9930            },
9931            result = Err(ErrorList::new(vec![
9932                Error::DifferentAvailabilityInAggregation(AvailabilityList(vec![
9933                    fdecl::Availability::Required,
9934                    fdecl::Availability::Optional,
9935                ]))
9936            ])),
9937        },
9938
9939        test_validate_offers_invalid_aggregation_different_availability => {
9940            input = {
9941                let mut decl = new_component_decl();
9942                decl.offers = Some(vec![
9943                    fdecl::Offer::Service(fdecl::OfferService {
9944                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9945                            name: "coll_a".into()
9946                        })),
9947                        source_name: Some("fuchsia.logger.Log".to_string()),
9948                        target: Some(fdecl::Ref::Child(
9949                            fdecl::ChildRef {
9950                                name: "child_c".to_string(),
9951                                collection: None,
9952                            }
9953                        )),
9954                        target_name: Some("fuchsia.logger.Log".to_string()),
9955                        source_instance_filter: Some(vec!["default".to_string()]),
9956                        availability: Some(fdecl::Availability::Required),
9957                        ..Default::default()
9958                    }),
9959                    fdecl::Offer::Service(fdecl::OfferService {
9960                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9961                            name: "coll_b".into()
9962                        })),
9963                        source_name: Some("fuchsia.logger.Log".to_string()),
9964                        target: Some(fdecl::Ref::Child(
9965                            fdecl::ChildRef {
9966                                name: "child_c".to_string(),
9967                                collection: None,
9968                            }
9969                        )),
9970                        target_name: Some("fuchsia.logger.Log".to_string()),
9971                        source_instance_filter: Some(vec!["a_different_default".to_string()]),
9972                        availability: Some(fdecl::Availability::Optional),
9973                        ..Default::default()
9974                    })
9975                ]);
9976                decl.collections = Some(vec![
9977                    fdecl::Collection {
9978                        name: Some("coll_a".to_string()),
9979                        durability: Some(fdecl::Durability::Transient),
9980                        ..Default::default()
9981                    },
9982                    fdecl::Collection {
9983                        name: Some("coll_b".to_string()),
9984                        durability: Some(fdecl::Durability::Transient),
9985                        ..Default::default()
9986                    },
9987                ]);
9988                decl.children = Some(vec![
9989                    fdecl::Child {
9990                        name: Some("child_c".to_string()),
9991                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9992                        startup: Some(fdecl::StartupMode::Lazy),
9993                        on_terminate: None,
9994                        environment: None,
9995                        ..Default::default()
9996                    },
9997                ]);
9998                decl
9999            },
10000            result = Err(ErrorList::new(vec![
10001                Error::DifferentAvailabilityInAggregation(AvailabilityList(vec![
10002                    fdecl::Availability::Required,
10003                    fdecl::Availability::Optional,
10004                ]))
10005            ])),
10006        },
10007    }
10008
10009    test_validate_capabilities! {
10010        test_validate_capabilities_individually_ok => {
10011            input = vec![
10012                fdecl::Capability::Protocol(fdecl::Protocol {
10013                    name: Some("foo_svc".into()),
10014                    source_path: Some("/svc/foo".into()),
10015                    ..Default::default()
10016                }),
10017                fdecl::Capability::Directory(fdecl::Directory {
10018                    name: Some("foo_dir".into()),
10019                    source_path: Some("/foo".into()),
10020                    rights: Some(fio::Operations::CONNECT),
10021                    ..Default::default()
10022                }),
10023            ],
10024            as_builtin = false,
10025            result = Ok(()),
10026        },
10027        test_validate_capabilities_individually_err => {
10028            input = vec![
10029                fdecl::Capability::Protocol(fdecl::Protocol {
10030                    name: None,
10031                    source_path: None,
10032                    ..Default::default()
10033                }),
10034                fdecl::Capability::Directory(fdecl::Directory {
10035                    name: None,
10036                    source_path: None,
10037                    rights: None,
10038                    ..Default::default()
10039                }),
10040            ],
10041            as_builtin = false,
10042            result = Err(ErrorList::new(vec![
10043                Error::missing_field(DeclType::Protocol, "name"),
10044                Error::missing_field(DeclType::Protocol, "source_path"),
10045                Error::missing_field(DeclType::Directory, "name"),
10046                Error::missing_field(DeclType::Directory, "source_path"),
10047                Error::missing_field(DeclType::Directory, "rights"),
10048            ])),
10049        },
10050        test_validate_builtin_capabilities_individually_ok => {
10051            input = vec![
10052                fdecl::Capability::Protocol(fdecl::Protocol {
10053                    name: Some("foo_protocol".into()),
10054                    source_path: None,
10055                    ..Default::default()
10056                }),
10057                fdecl::Capability::Directory(fdecl::Directory {
10058                    name: Some("foo_dir".into()),
10059                    source_path: None,
10060                    rights: Some(fio::Operations::CONNECT),
10061                    ..Default::default()
10062                }),
10063                fdecl::Capability::Service(fdecl::Service {
10064                    name: Some("foo_svc".into()),
10065                    source_path: None,
10066                    ..Default::default()
10067                }),
10068                fdecl::Capability::Runner(fdecl::Runner {
10069                    name: Some("foo_runner".into()),
10070                    source_path: None,
10071                    ..Default::default()
10072                }),
10073                fdecl::Capability::Resolver(fdecl::Resolver {
10074                    name: Some("foo_resolver".into()),
10075                    source_path: None,
10076                    ..Default::default()
10077                }),
10078            ],
10079            as_builtin = true,
10080            result = Ok(()),
10081        },
10082        test_validate_builtin_capabilities_individually_err => {
10083            input = vec![
10084                fdecl::Capability::Protocol(fdecl::Protocol {
10085                    name: None,
10086                    source_path: Some("/svc/foo".into()),
10087                    ..Default::default()
10088                }),
10089                fdecl::Capability::Directory(fdecl::Directory {
10090                    name: None,
10091                    source_path: Some("/foo".into()),
10092                    rights: None,
10093                    ..Default::default()
10094                }),
10095                fdecl::Capability::Service(fdecl::Service {
10096                    name: None,
10097                    source_path: Some("/svc/foo".into()),
10098                    ..Default::default()
10099                }),
10100                fdecl::Capability::Runner(fdecl::Runner {
10101                    name: None,
10102                    source_path:  Some("/foo".into()),
10103                    ..Default::default()
10104                }),
10105                fdecl::Capability::Resolver(fdecl::Resolver {
10106                    name: None,
10107                    source_path:  Some("/foo".into()),
10108                    ..Default::default()
10109                }),
10110                fdecl::Capability::Storage(fdecl::Storage {
10111                    name: None,
10112                    ..Default::default()
10113                }),
10114            ],
10115            as_builtin = true,
10116            result = Err(ErrorList::new(vec![
10117                Error::missing_field(DeclType::Protocol, "name"),
10118                Error::extraneous_source_path(DeclType::Protocol, "/svc/foo"),
10119                Error::missing_field(DeclType::Directory, "name"),
10120                Error::extraneous_source_path(DeclType::Directory, "/foo"),
10121                Error::missing_field(DeclType::Directory, "rights"),
10122                Error::missing_field(DeclType::Service, "name"),
10123                Error::extraneous_source_path(DeclType::Service, "/svc/foo"),
10124                Error::missing_field(DeclType::Runner, "name"),
10125                Error::extraneous_source_path(DeclType::Runner, "/foo"),
10126                Error::missing_field(DeclType::Resolver, "name"),
10127                Error::extraneous_source_path(DeclType::Resolver, "/foo"),
10128                Error::CapabilityCannotBeBuiltin(DeclType::Storage),
10129            ])),
10130        },
10131        test_validate_delivery_type_ok => {
10132            input = vec![
10133                fdecl::Capability::Protocol(fdecl::Protocol {
10134                    name: Some("foo_svc1".into()),
10135                    source_path: Some("/svc/foo1".into()),
10136                    ..Default::default()
10137                }),
10138                fdecl::Capability::Protocol(fdecl::Protocol {
10139                    name: Some("foo_svc2".into()),
10140                    source_path: Some("/svc/foo2".into()),
10141                    delivery: Some(fdecl::DeliveryType::Immediate),
10142                    ..Default::default()
10143                }),
10144                fdecl::Capability::Protocol(fdecl::Protocol {
10145                    name: Some("foo_svc3".into()),
10146                    source_path: Some("/svc/foo3".into()),
10147                    delivery: Some(fdecl::DeliveryType::OnReadable),
10148                    ..Default::default()
10149                }),
10150            ],
10151            as_builtin = false,
10152            result = Ok(()),
10153        },
10154        test_validate_delivery_type_err => {
10155            input = vec![
10156                fdecl::Capability::Protocol(fdecl::Protocol {
10157                    name: Some("foo_svc".into()),
10158                    source_path: Some("/svc/foo".into()),
10159                    delivery: Some(fdecl::DeliveryType::unknown()),
10160                    ..Default::default()
10161                }),
10162            ],
10163            as_builtin = false,
10164            result = Err(ErrorList::new(vec![
10165                Error::invalid_field(DeclType::Protocol, "delivery"),
10166            ])),
10167        },
10168    }
10169
10170    /// Passes different source and availability options to `new_expose` to
10171    /// generate a component declaration.
10172    fn generate_expose_different_source_and_availability_decl(
10173        new_expose: impl Fn(fdecl::Ref, fdecl::Availability, &str) -> fdecl::Expose,
10174    ) -> fdecl::Component {
10175        let mut decl = new_component_decl();
10176        let child = "child";
10177        decl.children = Some(vec![fdecl::Child {
10178            name: Some(child.to_string()),
10179            url: Some("fuchsia-pkg://fuchsia.com/source#meta/source.cm".to_string()),
10180            startup: Some(fdecl::StartupMode::Lazy),
10181            ..Default::default()
10182        }]);
10183        decl.exposes = Some(vec![
10184            // Optional expose from self is okay.
10185            new_expose(
10186                fdecl::Ref::Self_(fdecl::SelfRef {}),
10187                fdecl::Availability::Optional,
10188                "fuchsia.examples.Echo1",
10189            ),
10190            // Optional expose from child is okay.
10191            new_expose(
10192                fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
10193                fdecl::Availability::Optional,
10194                "fuchsia.examples.Echo2",
10195            ),
10196            // Optional expose from void is okay.
10197            new_expose(
10198                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10199                fdecl::Availability::Optional,
10200                "fuchsia.examples.Echo3",
10201            ),
10202            // Optional expose from framework is okay.
10203            new_expose(
10204                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10205                fdecl::Availability::Optional,
10206                "fuchsia.examples.Echo4",
10207            ),
10208            // Transitional expose from self is okay.
10209            new_expose(
10210                fdecl::Ref::Self_(fdecl::SelfRef {}),
10211                fdecl::Availability::Transitional,
10212                "fuchsia.examples.Echo5",
10213            ),
10214            // Transitional expose from child is okay.
10215            new_expose(
10216                fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
10217                fdecl::Availability::Transitional,
10218                "fuchsia.examples.Echo6",
10219            ),
10220            // Transitional expose from void is okay.
10221            new_expose(
10222                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10223                fdecl::Availability::Transitional,
10224                "fuchsia.examples.Echo7",
10225            ),
10226            // Transitional expose from framework is okay.
10227            new_expose(
10228                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10229                fdecl::Availability::Transitional,
10230                "fuchsia.examples.Echo8",
10231            ),
10232            // Same-as-target expose from self is okay.
10233            new_expose(
10234                fdecl::Ref::Self_(fdecl::SelfRef {}),
10235                fdecl::Availability::SameAsTarget,
10236                "fuchsia.examples.Echo9",
10237            ),
10238            // Same-as-target expose from child is okay.
10239            new_expose(
10240                fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
10241                fdecl::Availability::SameAsTarget,
10242                "fuchsia.examples.Echo10",
10243            ),
10244            // Same-as-target expose from framework is okay.
10245            new_expose(
10246                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10247                fdecl::Availability::SameAsTarget,
10248                "fuchsia.examples.Echo11",
10249            ),
10250            // Required expose from void is an error.
10251            new_expose(
10252                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10253                fdecl::Availability::Required,
10254                "fuchsia.examples.Echo12",
10255            ),
10256            // Same-as-target expose from void is an error.
10257            new_expose(
10258                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10259                fdecl::Availability::SameAsTarget,
10260                "fuchsia.examples.Echo13",
10261            ),
10262        ]);
10263        decl
10264    }
10265
10266    /// Passes different source and availability options to `new_offer` to
10267    /// generate a component declaration.
10268    fn generate_offer_different_source_and_availability_decl(
10269        new_offer: impl Fn(fdecl::Ref, fdecl::Availability, &str) -> fdecl::Offer,
10270    ) -> fdecl::Component {
10271        let mut decl = new_component_decl();
10272        decl.children = Some(vec![
10273            fdecl::Child {
10274                name: Some("source".to_string()),
10275                url: Some("fuchsia-pkg://fuchsia.com/source#meta/source.cm".to_string()),
10276                startup: Some(fdecl::StartupMode::Lazy),
10277                on_terminate: None,
10278                environment: None,
10279                ..Default::default()
10280            },
10281            fdecl::Child {
10282                name: Some("sink".to_string()),
10283                url: Some("fuchsia-pkg://fuchsia.com/sink#meta/sink.cm".to_string()),
10284                startup: Some(fdecl::StartupMode::Lazy),
10285                on_terminate: None,
10286                environment: None,
10287                ..Default::default()
10288            },
10289        ]);
10290        decl.offers = Some(vec![
10291            // These offers are fine, offers with a source of parent or void can be
10292            // optional.
10293            new_offer(
10294                fdecl::Ref::Parent(fdecl::ParentRef {}),
10295                fdecl::Availability::Required,
10296                "fuchsia.examples.Echo0",
10297            ),
10298            new_offer(
10299                fdecl::Ref::Parent(fdecl::ParentRef {}),
10300                fdecl::Availability::Optional,
10301                "fuchsia.examples.Echo1",
10302            ),
10303            new_offer(
10304                fdecl::Ref::Parent(fdecl::ParentRef {}),
10305                fdecl::Availability::SameAsTarget,
10306                "fuchsia.examples.Echo2",
10307            ),
10308            new_offer(
10309                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10310                fdecl::Availability::Optional,
10311                "fuchsia.examples.Echo3",
10312            ),
10313            // These offers are fine, offers with a source other than parent or void
10314            // can also be optional.
10315            new_offer(
10316                fdecl::Ref::Self_(fdecl::SelfRef {}),
10317                fdecl::Availability::Optional,
10318                "fuchsia.examples.Echo4",
10319            ),
10320            new_offer(
10321                fdecl::Ref::Self_(fdecl::SelfRef {}),
10322                fdecl::Availability::SameAsTarget,
10323                "fuchsia.examples.Echo5",
10324            ),
10325            new_offer(
10326                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10327                fdecl::Availability::Optional,
10328                "fuchsia.examples.Echo6",
10329            ),
10330            new_offer(
10331                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10332                fdecl::Availability::SameAsTarget,
10333                "fuchsia.examples.Echo7",
10334            ),
10335            new_offer(
10336                fdecl::Ref::Child(fdecl::ChildRef { name: "source".to_string(), collection: None }),
10337                fdecl::Availability::Optional,
10338                "fuchsia.examples.Echo8",
10339            ),
10340            new_offer(
10341                fdecl::Ref::Child(fdecl::ChildRef { name: "source".to_string(), collection: None }),
10342                fdecl::Availability::SameAsTarget,
10343                "fuchsia.examples.Echo9",
10344            ),
10345            // These offers are also not fine, offers with a source of void must be optional
10346            new_offer(
10347                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10348                fdecl::Availability::Required,
10349                "fuchsia.examples.Echo10",
10350            ),
10351            new_offer(
10352                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10353                fdecl::Availability::SameAsTarget,
10354                "fuchsia.examples.Echo11",
10355            ),
10356        ]);
10357        decl
10358    }
10359
10360    #[test]
10361    fn test_validate_dynamic_offers_empty() {
10362        assert_eq!(validate_dynamic_offers(vec![], &vec![], &fdecl::Component::default()), Ok(()));
10363    }
10364
10365    #[test]
10366    fn test_validate_dynamic_offers_okay() {
10367        assert_eq!(
10368            validate_dynamic_offers(
10369                vec![],
10370                &vec![
10371                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
10372                        dependency_type: Some(fdecl::DependencyType::Strong),
10373                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef)),
10374                        source_name: Some("thing".to_string()),
10375                        target_name: Some("thing".repeat(26)),
10376                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10377                            name: "foo".to_string(),
10378                            collection: Some("foo".to_string()),
10379                        })),
10380                        ..Default::default()
10381                    }),
10382                    fdecl::Offer::Service(fdecl::OfferService {
10383                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10384                        source_name: Some("thang".repeat(26)),
10385                        target_name: Some("thang".repeat(26)),
10386                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10387                            name: "foo".to_string(),
10388                            collection: Some("foo".to_string()),
10389                        })),
10390                        ..Default::default()
10391                    }),
10392                    fdecl::Offer::Directory(fdecl::OfferDirectory {
10393                        dependency_type: Some(fdecl::DependencyType::Strong),
10394                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10395                        source_name: Some("thung1".repeat(26)),
10396                        target_name: Some("thung1".repeat(26)),
10397                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10398                            name: "foo".to_string(),
10399                            collection: Some("foo".to_string()),
10400                        })),
10401                        ..Default::default()
10402                    }),
10403                    fdecl::Offer::Storage(fdecl::OfferStorage {
10404                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10405                        source_name: Some("thung2".repeat(26)),
10406                        target_name: Some("thung2".repeat(26)),
10407                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10408                            name: "foo".to_string(),
10409                            collection: Some("foo".to_string()),
10410                        })),
10411                        ..Default::default()
10412                    }),
10413                    fdecl::Offer::Runner(fdecl::OfferRunner {
10414                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10415                        source_name: Some("thung3".repeat(26)),
10416                        target_name: Some("thung3".repeat(26)),
10417                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10418                            name: "foo".to_string(),
10419                            collection: Some("foo".to_string()),
10420                        })),
10421                        ..Default::default()
10422                    }),
10423                    fdecl::Offer::Resolver(fdecl::OfferResolver {
10424                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10425                        source_name: Some("thung4".repeat(26)),
10426                        target_name: Some("thung4".repeat(26)),
10427                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10428                            name: "foo".to_string(),
10429                            collection: Some("foo".to_string()),
10430                        })),
10431                        ..Default::default()
10432                    }),
10433                ],
10434                &fdecl::Component {
10435                    capabilities: Some(vec![fdecl::Capability::Protocol(fdecl::Protocol {
10436                        name: Some("thing".to_string()),
10437                        source_path: Some("/svc/foo".into()),
10438                        ..Default::default()
10439                    }),]),
10440                    ..Default::default()
10441                }
10442            ),
10443            Ok(())
10444        );
10445    }
10446
10447    #[test]
10448    fn test_validate_dynamic_offers_valid_service_aggregation() {
10449        assert_eq!(
10450            validate_dynamic_offers(
10451                vec![],
10452                &vec![
10453                    fdecl::Offer::Service(fdecl::OfferService {
10454                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10455                            name: "child_a".to_string(),
10456                            collection: None
10457                        })),
10458                        source_name: Some("fuchsia.logger.Log".to_string()),
10459                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10460                            name: "child_c".to_string(),
10461                            collection: None,
10462                        })),
10463                        target_name: Some("fuchsia.logger.Log".to_string()),
10464                        source_instance_filter: Some(vec!["default".to_string()]),
10465                        ..Default::default()
10466                    }),
10467                    fdecl::Offer::Service(fdecl::OfferService {
10468                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10469                            name: "child_b".to_string(),
10470                            collection: None
10471                        })),
10472                        source_name: Some("fuchsia.logger.Log".to_string()),
10473                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10474                            name: "child_c".to_string(),
10475                            collection: None,
10476                        })),
10477                        target_name: Some("fuchsia.logger.Log".to_string()),
10478                        source_instance_filter: Some(vec!["a_different_default".to_string()]),
10479                        ..Default::default()
10480                    })
10481                ],
10482                &fdecl::Component {
10483                    children: Some(vec![
10484                        fdecl::Child {
10485                            name: Some("child_a".to_string()),
10486                            url: Some(
10487                                "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10488                                    .to_string()
10489                            ),
10490                            startup: Some(fdecl::StartupMode::Lazy),
10491                            on_terminate: None,
10492                            environment: None,
10493                            ..Default::default()
10494                        },
10495                        fdecl::Child {
10496                            name: Some("child_b".to_string()),
10497                            url: Some(
10498                                "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10499                                    .to_string()
10500                            ),
10501                            startup: Some(fdecl::StartupMode::Lazy),
10502                            on_terminate: None,
10503                            environment: None,
10504                            ..Default::default()
10505                        },
10506                        fdecl::Child {
10507                            name: Some("child_c".to_string()),
10508                            url: Some(
10509                                "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10510                                    .to_string()
10511                            ),
10512                            startup: Some(fdecl::StartupMode::Lazy),
10513                            on_terminate: None,
10514                            environment: None,
10515                            ..Default::default()
10516                        },
10517                    ]),
10518                    ..Default::default()
10519                }
10520            ),
10521            Ok(())
10522        );
10523    }
10524
10525    #[test]
10526    fn test_validate_dynamic_service_aggregation_missing_filter() {
10527        assert_eq!(
10528            validate_dynamic_offers(
10529                vec![],
10530                &vec![
10531                    fdecl::Offer::Service(fdecl::OfferService {
10532                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10533                            name: "child_a".to_string(),
10534                            collection: None
10535                        })),
10536                        source_name: Some("fuchsia.logger.Log".to_string()),
10537                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10538                            name: "child_c".to_string(),
10539                            collection: None,
10540                        })),
10541                        target_name: Some("fuchsia.logger.Log".to_string()),
10542                        source_instance_filter: Some(vec!["default".to_string()]),
10543                        ..Default::default()
10544                    }),
10545                    fdecl::Offer::Service(fdecl::OfferService {
10546                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10547                            name: "child_b".to_string(),
10548                            collection: None
10549                        })),
10550                        source_name: Some("fuchsia.logger.Log".to_string()),
10551                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10552                            name: "child_c".to_string(),
10553                            collection: None,
10554                        })),
10555                        target_name: Some("fuchsia.logger.Log".to_string()),
10556                        source_instance_filter: None,
10557                        ..Default::default()
10558                    }),
10559                ],
10560                &fdecl::Component {
10561                    children: Some(vec![
10562                        fdecl::Child {
10563                            name: Some("child_a".to_string()),
10564                            url: Some(
10565                                "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10566                                    .to_string()
10567                            ),
10568                            startup: Some(fdecl::StartupMode::Lazy),
10569                            ..Default::default()
10570                        },
10571                        fdecl::Child {
10572                            name: Some("child_b".to_string()),
10573                            url: Some(
10574                                "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10575                                    .to_string()
10576                            ),
10577                            startup: Some(fdecl::StartupMode::Lazy),
10578                            ..Default::default()
10579                        },
10580                        fdecl::Child {
10581                            name: Some("child_c".to_string()),
10582                            url: Some(
10583                                "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10584                                    .to_string()
10585                            ),
10586                            startup: Some(fdecl::StartupMode::Lazy),
10587                            ..Default::default()
10588                        },
10589                    ]),
10590                    ..Default::default()
10591                },
10592            ),
10593            Err(ErrorList::new(vec![Error::invalid_aggregate_offer(
10594                "source_instance_filter must be set for dynamic aggregate service offers"
10595            ),]))
10596        );
10597    }
10598
10599    #[test]
10600    fn test_validate_dynamic_offers_omit_target() {
10601        assert_eq!(
10602            validate_dynamic_offers(
10603                vec![],
10604                &vec![
10605                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
10606                        dependency_type: Some(fdecl::DependencyType::Strong),
10607                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef)),
10608                        source_name: Some("thing".to_string()),
10609                        target_name: Some("thing".to_string()),
10610                        ..Default::default()
10611                    }),
10612                    fdecl::Offer::Service(fdecl::OfferService {
10613                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10614                        source_name: Some("thang".to_string()),
10615                        target_name: Some("thang".to_string()),
10616                        ..Default::default()
10617                    }),
10618                    fdecl::Offer::Directory(fdecl::OfferDirectory {
10619                        dependency_type: Some(fdecl::DependencyType::Strong),
10620                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10621                        source_name: Some("thung1".to_string()),
10622                        target_name: Some("thung1".to_string()),
10623                        ..Default::default()
10624                    }),
10625                    fdecl::Offer::Storage(fdecl::OfferStorage {
10626                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10627                        source_name: Some("thung2".to_string()),
10628                        target_name: Some("thung2".to_string()),
10629                        ..Default::default()
10630                    }),
10631                    fdecl::Offer::Runner(fdecl::OfferRunner {
10632                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10633                        source_name: Some("thung3".to_string()),
10634                        target_name: Some("thung3".to_string()),
10635                        ..Default::default()
10636                    }),
10637                    fdecl::Offer::Resolver(fdecl::OfferResolver {
10638                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10639                        source_name: Some("thung4".to_string()),
10640                        target_name: Some("thung4".to_string()),
10641                        ..Default::default()
10642                    }),
10643                ],
10644                &fdecl::Component {
10645                    capabilities: Some(vec![fdecl::Capability::Protocol(fdecl::Protocol {
10646                        name: Some("thing".to_string()),
10647                        source_path: Some("/svc/foo".into()),
10648                        ..Default::default()
10649                    }),]),
10650                    ..Default::default()
10651                }
10652            ),
10653            Err(ErrorList::new(vec![
10654                Error::missing_field(DeclType::OfferProtocol, "target"),
10655                Error::missing_field(DeclType::OfferService, "target"),
10656                Error::missing_field(DeclType::OfferDirectory, "target"),
10657                Error::missing_field(DeclType::OfferStorage, "target"),
10658                Error::missing_field(DeclType::OfferRunner, "target"),
10659                Error::missing_field(DeclType::OfferResolver, "target"),
10660            ]))
10661        );
10662    }
10663
10664    #[test]
10665    fn test_validate_dynamic_offers_collection_collision() {
10666        assert_eq!(
10667            validate_dynamic_offers(
10668                vec![],
10669                &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10670                    dependency_type: Some(fdecl::DependencyType::Strong),
10671                    source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10672                    source_name: Some("thing".to_string()),
10673                    target_name: Some("thing".to_string()),
10674                    target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10675                        name: "child".to_string(),
10676                        collection: Some("coll".to_string()),
10677                    })),
10678                    ..Default::default()
10679                }),],
10680                &fdecl::Component {
10681                    offers: Some(vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10682                        dependency_type: Some(fdecl::DependencyType::Strong),
10683                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10684                        source_name: Some("thing".to_string()),
10685                        target_name: Some("thing".to_string()),
10686                        target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10687                            name: "coll".into()
10688                        })),
10689                        ..Default::default()
10690                    }),]),
10691                    collections: Some(vec![fdecl::Collection {
10692                        name: Some("coll".to_string()),
10693                        durability: Some(fdecl::Durability::Transient),
10694                        ..Default::default()
10695                    },]),
10696                    ..Default::default()
10697                }
10698            ),
10699            Err(ErrorList::new(vec![Error::duplicate_field(
10700                DeclType::OfferProtocol,
10701                "target_name",
10702                "thing"
10703            ),]))
10704        );
10705    }
10706
10707    #[test]
10708    fn test_validate_dynamic_offers_cycle_collection_to_static_child() {
10709        assert_eq!(
10710            validate_dynamic_offers(
10711                vec![("dyn", "coll")],
10712                &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10713                    source_name: Some("bar".to_string()),
10714                    target_name: Some("bar".to_string()),
10715                    source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10716                        name: "static_child".into(),
10717                        collection: None,
10718                    })),
10719                    target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10720                        name: "dyn".to_string(),
10721                        collection: Some("coll".to_string()),
10722                    })),
10723                    dependency_type: Some(fdecl::DependencyType::Strong),
10724                    ..Default::default()
10725                }),],
10726                &fdecl::Component {
10727                    offers: Some(vec![fdecl::Offer::Service(fdecl::OfferService {
10728                        source_name: Some("foo".to_string()),
10729                        target_name: Some("foo".to_string()),
10730                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10731                            name: "coll".into(),
10732                        })),
10733                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10734                            name: "static_child".into(),
10735                            collection: None,
10736                        })),
10737                        ..Default::default()
10738                    })]),
10739                    children: Some(vec![fdecl::Child {
10740                        name: Some("static_child".into()),
10741                        url: Some("url#child.cm".into()),
10742                        startup: Some(fdecl::StartupMode::Lazy),
10743                        ..Default::default()
10744                    }]),
10745                    collections: Some(vec![fdecl::Collection {
10746                        name: Some("coll".into()),
10747                        durability: Some(fdecl::Durability::Transient),
10748                        ..Default::default()
10749                    }]),
10750                    ..Default::default()
10751                }
10752            ),
10753            Err(ErrorList::new(vec![Error::dependency_cycle(
10754                directed_graph::Error::CyclesDetected(
10755                    [vec![
10756                        "child coll:dyn",
10757                        "collection coll",
10758                        "child static_child",
10759                        "child coll:dyn",
10760                    ]]
10761                    .iter()
10762                    .cloned()
10763                    .collect()
10764                )
10765                .format_cycle()
10766            )]))
10767        );
10768    }
10769
10770    #[test]
10771    fn test_validate_dynamic_offers_cycle_collection_to_dynamic_child() {
10772        assert_eq!(
10773            validate_dynamic_offers(
10774                vec![("dyn", "coll1"), ("dyn", "coll2")],
10775                &vec![
10776                    fdecl::Offer::Service(fdecl::OfferService {
10777                        source_name: Some("foo".to_string()),
10778                        target_name: Some("foo".to_string()),
10779                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10780                            name: "coll2".into(),
10781                        })),
10782                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10783                            name: "dyn".into(),
10784                            collection: Some("coll1".into()),
10785                        })),
10786                        ..Default::default()
10787                    }),
10788                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
10789                        source_name: Some("bar".to_string()),
10790                        target_name: Some("bar".to_string()),
10791                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10792                            name: "dyn".into(),
10793                            collection: Some("coll1".into()),
10794                        })),
10795                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10796                            name: "dyn".to_string(),
10797                            collection: Some("coll2".to_string()),
10798                        })),
10799                        dependency_type: Some(fdecl::DependencyType::Strong),
10800                        ..Default::default()
10801                    }),
10802                ],
10803                &fdecl::Component {
10804                    collections: Some(vec![
10805                        fdecl::Collection {
10806                            name: Some("coll1".into()),
10807                            durability: Some(fdecl::Durability::Transient),
10808                            ..Default::default()
10809                        },
10810                        fdecl::Collection {
10811                            name: Some("coll2".into()),
10812                            durability: Some(fdecl::Durability::Transient),
10813                            ..Default::default()
10814                        },
10815                    ]),
10816                    ..Default::default()
10817                }
10818            ),
10819            Err(ErrorList::new(vec![Error::dependency_cycle(
10820                directed_graph::Error::CyclesDetected(
10821                    [vec![
10822                        "child coll1:dyn",
10823                        "child coll2:dyn",
10824                        "collection coll2",
10825                        "child coll1:dyn",
10826                    ]]
10827                    .iter()
10828                    .cloned()
10829                    .collect()
10830                )
10831                .format_cycle()
10832            )]))
10833        );
10834    }
10835
10836    #[test]
10837    fn test_validate_dynamic_offers_cycle_collection_to_collection() {
10838        assert_eq!(
10839            validate_dynamic_offers(
10840                vec![("dyn", "coll1"), ("dyn", "coll2")],
10841                &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10842                    source_name: Some("bar".to_string()),
10843                    target_name: Some("bar".to_string()),
10844                    source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10845                        name: "dyn".into(),
10846                        collection: Some("coll2".parse().unwrap()),
10847                    })),
10848                    target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10849                        name: "dyn".into(),
10850                        collection: Some("coll1".parse().unwrap()),
10851                    })),
10852                    dependency_type: Some(fdecl::DependencyType::Strong),
10853                    ..Default::default()
10854                }),],
10855                &fdecl::Component {
10856                    offers: Some(vec![fdecl::Offer::Service(fdecl::OfferService {
10857                        source_name: Some("foo".to_string()),
10858                        target_name: Some("foo".to_string()),
10859                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10860                            name: "coll1".into(),
10861                        })),
10862                        target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10863                            name: "coll2".into(),
10864                        })),
10865                        ..Default::default()
10866                    })]),
10867                    collections: Some(vec![
10868                        fdecl::Collection {
10869                            name: Some("coll1".into()),
10870                            durability: Some(fdecl::Durability::Transient),
10871                            ..Default::default()
10872                        },
10873                        fdecl::Collection {
10874                            name: Some("coll2".into()),
10875                            durability: Some(fdecl::Durability::Transient),
10876                            ..Default::default()
10877                        },
10878                    ]),
10879                    ..Default::default()
10880                }
10881            ),
10882            Err(ErrorList::new(vec![Error::dependency_cycle(
10883                directed_graph::Error::CyclesDetected(
10884                    [vec![
10885                        "child coll1:dyn",
10886                        "collection coll1",
10887                        "child coll2:dyn",
10888                        "child coll1:dyn",
10889                    ]]
10890                    .iter()
10891                    .cloned()
10892                    .collect()
10893                )
10894                .format_cycle()
10895            )]))
10896        );
10897    }
10898
10899    #[test]
10900    fn test_validate_dynamic_child() {
10901        assert_eq!(
10902            Ok(()),
10903            validate_dynamic_child(&fdecl::Child {
10904                name: Some("a".repeat(MAX_LONG_NAME_LENGTH).to_string()),
10905                url: Some("test:///child".to_string()),
10906                startup: Some(fdecl::StartupMode::Lazy),
10907                on_terminate: None,
10908                environment: None,
10909                ..Default::default()
10910            })
10911        );
10912    }
10913
10914    #[test]
10915    fn test_validate_dynamic_child_environment_is_invalid() {
10916        assert_eq!(
10917            validate_dynamic_child(&fdecl::Child {
10918                name: Some("a".repeat(MAX_LONG_NAME_LENGTH).to_string()),
10919                url: Some("test:///child".to_string()),
10920                startup: Some(fdecl::StartupMode::Lazy),
10921                on_terminate: None,
10922                environment: Some("env".to_string()),
10923                ..Default::default()
10924            }),
10925            Err(ErrorList::new(vec![Error::DynamicChildWithEnvironment]))
10926        );
10927    }
10928
10929    #[test]
10930    fn test_validate_dynamic_offers_missing_stuff() {
10931        assert_eq!(
10932            validate_dynamic_offers(
10933                vec![],
10934                &vec![
10935                    fdecl::Offer::Protocol(fdecl::OfferProtocol::default()),
10936                    fdecl::Offer::Service(fdecl::OfferService::default()),
10937                    fdecl::Offer::Directory(fdecl::OfferDirectory::default()),
10938                    fdecl::Offer::Storage(fdecl::OfferStorage::default()),
10939                    fdecl::Offer::Runner(fdecl::OfferRunner::default()),
10940                    fdecl::Offer::Resolver(fdecl::OfferResolver::default()),
10941                ],
10942                &fdecl::Component::default()
10943            ),
10944            Err(ErrorList::new(vec![
10945                Error::missing_field(DeclType::OfferProtocol, "source"),
10946                Error::missing_field(DeclType::OfferProtocol, "source_name"),
10947                Error::missing_field(DeclType::OfferProtocol, "target"),
10948                Error::missing_field(DeclType::OfferProtocol, "target_name"),
10949                Error::missing_field(DeclType::OfferProtocol, "dependency_type"),
10950                Error::missing_field(DeclType::OfferService, "source"),
10951                Error::missing_field(DeclType::OfferService, "source_name"),
10952                Error::missing_field(DeclType::OfferService, "target"),
10953                Error::missing_field(DeclType::OfferService, "target_name"),
10954                Error::missing_field(DeclType::OfferDirectory, "source"),
10955                Error::missing_field(DeclType::OfferDirectory, "source_name"),
10956                Error::missing_field(DeclType::OfferDirectory, "target"),
10957                Error::missing_field(DeclType::OfferDirectory, "target_name"),
10958                Error::missing_field(DeclType::OfferDirectory, "dependency_type"),
10959                Error::missing_field(DeclType::OfferStorage, "source"),
10960                Error::missing_field(DeclType::OfferStorage, "source_name"),
10961                Error::missing_field(DeclType::OfferStorage, "target"),
10962                Error::missing_field(DeclType::OfferStorage, "target_name"),
10963                Error::missing_field(DeclType::OfferRunner, "source"),
10964                Error::missing_field(DeclType::OfferRunner, "source_name"),
10965                Error::missing_field(DeclType::OfferRunner, "target"),
10966                Error::missing_field(DeclType::OfferRunner, "target_name"),
10967                Error::missing_field(DeclType::OfferResolver, "source"),
10968                Error::missing_field(DeclType::OfferResolver, "source_name"),
10969                Error::missing_field(DeclType::OfferResolver, "target"),
10970                Error::missing_field(DeclType::OfferResolver, "target_name"),
10971            ]))
10972        );
10973    }
10974
10975    test_dependency! {
10976        (test_validate_offers_protocol_dependency_cycle) => {
10977            ty = fdecl::Offer::Protocol,
10978            offer_decl = fdecl::OfferProtocol {
10979                source: None,  // Filled by macro
10980                target: None,  // Filled by macro
10981                source_name: Some(format!("thing")),
10982                target_name: Some(format!("thing")),
10983                dependency_type: Some(fdecl::DependencyType::Strong),
10984                ..Default::default()
10985            },
10986        },
10987        (test_validate_offers_directory_dependency_cycle) => {
10988            ty = fdecl::Offer::Directory,
10989            offer_decl = fdecl::OfferDirectory {
10990                source: None,  // Filled by macro
10991                target: None,  // Filled by macro
10992                source_name: Some(format!("thing")),
10993                target_name: Some(format!("thing")),
10994                rights: Some(fio::Operations::CONNECT),
10995                subdir: None,
10996                dependency_type: Some(fdecl::DependencyType::Strong),
10997                ..Default::default()
10998            },
10999        },
11000        (test_validate_offers_service_dependency_cycle) => {
11001            ty = fdecl::Offer::Service,
11002            offer_decl = fdecl::OfferService {
11003                source: None,  // Filled by macro
11004                target: None,  // Filled by macro
11005                source_name: Some(format!("thing")),
11006                target_name: Some(format!("thing")),
11007                ..Default::default()
11008            },
11009        },
11010        (test_validate_offers_runner_dependency_cycle) => {
11011            ty = fdecl::Offer::Runner,
11012            offer_decl = fdecl::OfferRunner {
11013                source: None,  // Filled by macro
11014                target: None,  // Filled by macro
11015                source_name: Some(format!("thing")),
11016                target_name: Some(format!("thing")),
11017                ..Default::default()
11018            },
11019        },
11020        (test_validate_offers_resolver_dependency_cycle) => {
11021            ty = fdecl::Offer::Resolver,
11022            offer_decl = fdecl::OfferResolver {
11023                source: None,  // Filled by macro
11024                target: None,  // Filled by macro
11025                source_name: Some(format!("thing")),
11026                target_name: Some(format!("thing")),
11027                ..Default::default()
11028            },
11029        },
11030    }
11031    test_weak_dependency! {
11032        (test_validate_offers_protocol_weak_dependency_cycle) => {
11033            ty = fdecl::Offer::Protocol,
11034            offer_decl = fdecl::OfferProtocol {
11035                source: None,  // Filled by macro
11036                target: None,  // Filled by macro
11037                source_name: Some(format!("thing")),
11038                target_name: Some(format!("thing")),
11039                dependency_type: None, // Filled by macro
11040                ..Default::default()
11041            },
11042        },
11043        (test_validate_offers_directory_weak_dependency_cycle) => {
11044            ty = fdecl::Offer::Directory,
11045            offer_decl = fdecl::OfferDirectory {
11046                source: None,  // Filled by macro
11047                target: None,  // Filled by macro
11048                source_name: Some(format!("thing")),
11049                target_name: Some(format!("thing")),
11050                rights: Some(fio::Operations::CONNECT),
11051                subdir: None,
11052                dependency_type: None,  // Filled by macro
11053                ..Default::default()
11054            },
11055        },
11056        (test_validate_offers_service_weak_dependency_cycle) => {
11057            ty = fdecl::Offer::Service,
11058            offer_decl = fdecl::OfferService {
11059                source: None,  // Filled by macro
11060                target: None,  // Filled by macro
11061                source_name: Some(format!("thing")),
11062                target_name: Some(format!("thing")),
11063                dependency_type: None, // Filled by macro
11064                ..Default::default()
11065            },
11066        },
11067    }
11068}