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 get_dependencies_from_uses<'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
403fn get_dependencies_from_capabilities<'a>(
404    strong_dependencies: &mut DirectedGraph<DependencyNode<'a>>,
405    decl: &'a fdecl::Component,
406) {
407    if let Some(capabilities) = decl.capabilities.as_ref() {
408        for cap in capabilities {
409            match cap {
410                #[cfg(fuchsia_api_level_at_least = "25")]
411                fdecl::Capability::Dictionary(dictionary) => {
412                    if dictionary.source_path.as_ref().is_some() {
413                        if let Some(name) = dictionary.name.as_ref() {
414                            // If `source_path` is set that means the dictionary is provided by the program,
415                            // which implies a dependency from `self` to the dictionary declaration.
416                            strong_dependencies
417                                .add_edge(DependencyNode::Self_, DependencyNode::Capability(name));
418                        }
419                    }
420                }
421                fdecl::Capability::Storage(storage) => {
422                    if let (Some(name), Some(_backing_dir)) =
423                        (storage.name.as_ref(), storage.backing_dir.as_ref())
424                    {
425                        if let Some(source_node) = ref_to_dependency_node(storage.source.as_ref()) {
426                            strong_dependencies
427                                .add_edge(source_node, DependencyNode::Capability(name));
428                        }
429                    }
430                }
431                _ => continue,
432            }
433        }
434    }
435}
436
437fn get_dependencies_from_environments<'a>(
438    strong_dependencies: &mut DirectedGraph<DependencyNode<'a>>,
439    decl: &'a fdecl::Component,
440) {
441    if let Some(environment) = decl.environments.as_ref() {
442        for environment in environment {
443            if let Some(name) = &environment.name {
444                let target = DependencyNode::Environment(name);
445                if let Some(debugs) = environment.debug_capabilities.as_ref() {
446                    for debug in debugs {
447                        if let fdecl::DebugRegistration::Protocol(o) = debug {
448                            if let Some(source_node) = ref_to_dependency_node(o.source.as_ref()) {
449                                strong_dependencies.add_edge(source_node, target);
450                            }
451                        }
452                    }
453                }
454                if let Some(runners) = environment.runners.as_ref() {
455                    for runner in runners {
456                        if let Some(source_node) = ref_to_dependency_node(runner.source.as_ref()) {
457                            strong_dependencies.add_edge(source_node, target);
458                        }
459                    }
460                }
461                if let Some(resolvers) = environment.resolvers.as_ref() {
462                    for resolver in resolvers {
463                        if let Some(source_node) = ref_to_dependency_node(resolver.source.as_ref())
464                        {
465                            strong_dependencies.add_edge(source_node, target);
466                        }
467                    }
468                }
469            }
470        }
471    }
472}
473
474fn generate_dependency_graph<'a>(
475    strong_dependencies: &mut DirectedGraph<DependencyNode<'a>>,
476    decl: &'a fdecl::Component,
477    dynamic_children: &Vec<(&'a str, &'a str)>,
478    dynamic_offers: Option<&'a Vec<fdecl::Offer>>,
479) {
480    get_dependencies_from_uses(strong_dependencies, decl);
481
482    let mut all_offers: Vec<&fdecl::Offer> = vec![];
483
484    if let Some(dynamic_offers) = dynamic_offers.as_ref() {
485        for dynamic_offer in dynamic_offers.iter() {
486            all_offers.push(dynamic_offer);
487        }
488    }
489
490    if let Some(offers) = decl.offers.as_ref() {
491        for offer in offers.iter() {
492            all_offers.push(offer);
493        }
494    }
495
496    for offer in all_offers {
497        let (_dependency_type, source, _target) = match offer {
498            fdecl::Offer::Protocol(o) => (o.dependency_type, &o.source, &o.target),
499            #[cfg(fuchsia_api_level_at_least = "25")]
500            fdecl::Offer::Dictionary(o) => (o.dependency_type, &o.source, &o.target),
501            fdecl::Offer::Directory(o) => (o.dependency_type, &o.source, &o.target),
502            fdecl::Offer::Service(o) => (None, &o.source, &o.target),
503            fdecl::Offer::Storage(o) => (None, &o.source, &o.target),
504            fdecl::Offer::Runner(o) => (Some(fdecl::DependencyType::Strong), &o.source, &o.target),
505            fdecl::Offer::Resolver(o) => {
506                (Some(fdecl::DependencyType::Strong), &o.source, &o.target)
507            }
508            fdecl::Offer::Config(o) => (Some(fdecl::DependencyType::Strong), &o.source, &o.target),
509            _ => continue,
510        };
511
512        // If `source` is a collection, add dependency edges from all its dynamic children
513        // to the collection, since the collection forms an aggregate.
514        if let Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: collection })) =
515            source.as_ref()
516        {
517            for name in
518                ValidationContext::dynamic_children_in_collection(dynamic_children, &collection)
519            {
520                strong_dependencies.add_edge(
521                    DependencyNode::Child(&name, Some(&collection)),
522                    DependencyNode::Collection(collection),
523                );
524            }
525        }
526    }
527
528    get_dependencies_from_capabilities(strong_dependencies, decl);
529    get_dependencies_from_environments(strong_dependencies, decl);
530
531    if let Some(children) = decl.children.as_ref() {
532        for child in children {
533            if let Some(name) = child.name.as_ref() {
534                if let Some(env) = child.environment.as_ref() {
535                    let source = DependencyNode::Environment(env.as_str());
536                    let target = DependencyNode::Child(name, None);
537                    strong_dependencies.add_edge(source, target);
538                }
539            }
540        }
541    }
542
543    if let Some(collections) = decl.collections.as_ref() {
544        for collection in collections {
545            if let Some(env) = collection.environment.as_ref() {
546                if let Some(name) = collection.name.as_ref() {
547                    let source = DependencyNode::Environment(env.as_str());
548                    let target = DependencyNode::Collection(name.as_str());
549                    strong_dependencies.add_edge(source, target);
550                }
551            }
552        }
553    }
554}
555
556impl<'a> ValidationContext<'a> {
557    fn validate(
558        mut self,
559        decl: &'a fdecl::Component,
560        dynamic_offers: Option<&'a Vec<fdecl::Offer>>,
561    ) -> Result<(), Vec<Error>> {
562        // Collect all environment names first, so that references to them can be checked.
563        if let Some(envs) = &decl.environments {
564            self.collect_environment_names(&envs);
565        }
566
567        // Validate "children" and build the set of all children.
568        if let Some(children) = decl.children.as_ref() {
569            for child in children {
570                self.validate_child_decl(&child);
571            }
572        }
573
574        // Validate "collections" and build the set of all collections.
575        if let Some(collections) = decl.collections.as_ref() {
576            for collection in collections {
577                self.validate_collection_decl(&collection);
578            }
579        }
580
581        // Validate "capabilities" and build the set of all capabilities.
582        if let Some(capabilities) = decl.capabilities.as_ref() {
583            #[cfg(fuchsia_api_level_at_least = "25")]
584            self.load_dictionary_names(capabilities.iter().filter_map(
585                |capability| match capability {
586                    fdecl::Capability::Dictionary(dictionary_decl) => Some(dictionary_decl),
587                    _ => None,
588                },
589            ));
590            self.validate_capability_decls(capabilities, false);
591        }
592
593        // Validate "uses".
594        let mut use_runner_name = None;
595        let mut use_runner_source = None;
596        if let Some(uses) = decl.uses.as_ref() {
597            (use_runner_name, use_runner_source) = self.validate_use_decls(uses);
598        }
599
600        // Validate "program".
601        if let Some(program) = decl.program.as_ref() {
602            self.validate_program(program, use_runner_name, use_runner_source);
603        }
604
605        // Validate "exposes".
606        if let Some(exposes) = decl.exposes.as_ref() {
607            let mut expose_to_parent_ids = HashMap::new();
608            let mut expose_to_framework_ids = HashMap::new();
609            for expose in exposes.iter() {
610                self.validate_expose_decl(
611                    &expose,
612                    &mut expose_to_parent_ids,
613                    &mut expose_to_framework_ids,
614                );
615            }
616            self.validate_expose_group(&exposes);
617        }
618
619        // Validate "offers".
620        if let Some(offers) = decl.offers.as_ref() {
621            for offer in offers.iter() {
622                self.validate_offer_decl(&offer, OfferType::Static);
623            }
624            self.validate_offer_group(&offers, OfferType::Static);
625        }
626
627        if let Some(dynamic_offers) = dynamic_offers.as_ref() {
628            for dynamic_offer in dynamic_offers.iter() {
629                self.validate_offer_decl(&dynamic_offer, OfferType::Dynamic);
630            }
631            self.validate_offer_group(&dynamic_offers, OfferType::Dynamic);
632        }
633
634        // Validate "environments" after all other declarations are processed.
635        if let Some(environment) = decl.environments.as_ref() {
636            for environment in environment {
637                self.validate_environment_decl(&environment);
638            }
639        }
640
641        // Validate "config"
642        #[cfg(fuchsia_api_level_at_least = "20")]
643        self.validate_config(decl.config.as_ref(), decl.uses.as_ref());
644
645        // Check that there are no strong cyclical dependencies
646        generate_dependency_graph(
647            &mut self.strong_dependencies,
648            &decl,
649            &self.dynamic_children,
650            dynamic_offers,
651        );
652        if let Err(e) = self.strong_dependencies.topological_sort() {
653            self.errors.push(Error::dependency_cycle(e.format_cycle()));
654        }
655
656        if self.errors.is_empty() {
657            Ok(())
658        } else {
659            Err(self.errors)
660        }
661    }
662
663    /// Collects all the environment names, watching for duplicates.
664    fn collect_environment_names(&mut self, envs: &'a [fdecl::Environment]) {
665        for env in envs {
666            if let Some(name) = env.name.as_ref() {
667                if !self.all_environment_names.insert(name) {
668                    self.errors.push(Error::duplicate_field(DeclType::Environment, "name", name));
669                }
670            }
671        }
672    }
673
674    // Validates a config schema. Checks that each field's layout matches the expected constraints
675    // and properties.
676    #[cfg(fuchsia_api_level_at_least = "20")]
677    fn validate_config(
678        &mut self,
679        config: Option<&fdecl::ConfigSchema>,
680        uses: Option<&Vec<fdecl::Use>>,
681    ) {
682        use std::collections::BTreeMap;
683
684        // Get all of the `use` configs that are optional without a default.
685        let optional_use_keys: BTreeMap<String, fdecl::ConfigType> =
686            uses.map_or(BTreeMap::new(), |u| {
687                u.iter()
688                    .map(|u| {
689                        let fdecl::Use::Config(config) = u else {
690                            return None;
691                        };
692                        if config.availability == Some(fdecl::Availability::Required)
693                            || config.availability == None
694                        {
695                            return None;
696                        }
697                        if let Some(_) = config.default.as_ref() {
698                            return None;
699                        }
700                        let Some(key) = config.target_name.clone() else {
701                            return None;
702                        };
703                        let Some(value) = config.type_.clone() else {
704                            return None;
705                        };
706                        Some((key, value))
707                    })
708                    .flatten()
709                    .collect()
710            });
711
712        // Validate default values in use configs.
713        for u in uses.iter().flat_map(|x| x.iter()) {
714            let fdecl::Use::Config(config) = u else { continue };
715            let Some(default) = config.default.as_ref() else { continue };
716            validate_value_spec(&fdecl::ConfigValueSpec {
717                value: Some(default.clone()),
718                ..Default::default()
719            })
720            .map_err(|mut e| self.errors.append(&mut e.errs))
721            .ok();
722        }
723
724        let Some(config) = config else {
725            if !optional_use_keys.is_empty() {
726                self.errors.push(Error::missing_field(DeclType::ConfigField, "config"))
727            }
728            return;
729        };
730
731        if let Some(fields) = &config.fields {
732            for field in fields {
733                if field.key.is_none() {
734                    self.errors.push(Error::missing_field(DeclType::ConfigField, "key"));
735                }
736                if let Some(type_) = &field.type_ {
737                    self.validate_config_type(type_, true);
738                } else {
739                    self.errors.push(Error::missing_field(DeclType::ConfigField, "value_type"));
740                }
741            }
742        } else {
743            self.errors.push(Error::missing_field(DeclType::ConfigSchema, "fields"));
744        }
745
746        if let Some(checksum) = &config.checksum {
747            match checksum {
748                fdecl::ConfigChecksum::Sha256(_) => {}
749                fdecl::ConfigChecksumUnknown!() => {
750                    self.errors.push(Error::invalid_field(DeclType::ConfigSchema, "checksum"));
751                }
752            }
753        } else {
754            self.errors.push(Error::missing_field(DeclType::ConfigSchema, "checksum"));
755        }
756
757        'outer: for (key, value) in optional_use_keys.iter() {
758            for field in config.fields.iter().flatten() {
759                if field.key.as_ref() == Some(key) {
760                    if field.type_.as_ref() != Some(value) {
761                        self.errors.push(Error::invalid_field(DeclType::ConfigField, key.clone()));
762                    }
763                    continue 'outer;
764                }
765            }
766            self.errors.push(Error::missing_field(DeclType::ConfigField, key.clone()));
767        }
768
769        match config.value_source {
770            None => self.errors.push(Error::missing_field(DeclType::ConfigSchema, "value_source")),
771            #[cfg(fuchsia_api_level_at_least = "HEAD")]
772            Some(fdecl::ConfigValueSource::Capabilities(_)) => {
773                if !optional_use_keys.is_empty() {
774                    self.errors
775                        .push(Error::invalid_field(DeclType::ConfigValueSource, "ValueSource"))
776                }
777            }
778            Some(fdecl::ConfigValueSource::__SourceBreaking { .. }) => {
779                self.errors.push(Error::invalid_field(DeclType::ConfigValueSource, "ValueSource"))
780            }
781            _ => (),
782        };
783    }
784
785    #[cfg(fuchsia_api_level_at_least = "20")]
786    fn validate_config_type(&mut self, type_: &fdecl::ConfigType, accept_vectors: bool) {
787        match &type_.layout {
788            fdecl::ConfigTypeLayout::Bool
789            | fdecl::ConfigTypeLayout::Uint8
790            | fdecl::ConfigTypeLayout::Uint16
791            | fdecl::ConfigTypeLayout::Uint32
792            | fdecl::ConfigTypeLayout::Uint64
793            | fdecl::ConfigTypeLayout::Int8
794            | fdecl::ConfigTypeLayout::Int16
795            | fdecl::ConfigTypeLayout::Int32
796            | fdecl::ConfigTypeLayout::Int64 => {
797                // These layouts have no parameters or constraints
798                if let Some(parameters) = &type_.parameters {
799                    if !parameters.is_empty() {
800                        self.errors
801                            .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
802                    }
803                } else {
804                    self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"));
805                }
806
807                if !type_.constraints.is_empty() {
808                    self.errors.push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
809                }
810            }
811            fdecl::ConfigTypeLayout::String => {
812                // String has exactly one constraint and no parameter
813                if let Some(parameters) = &type_.parameters {
814                    if !parameters.is_empty() {
815                        self.errors
816                            .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
817                    }
818                } else {
819                    self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"));
820                }
821
822                if type_.constraints.is_empty() {
823                    self.errors.push(Error::missing_field(DeclType::ConfigType, "constraints"));
824                } else if type_.constraints.len() > 1 {
825                    self.errors.push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
826                } else if let fdecl::LayoutConstraint::MaxSize(_) = &type_.constraints[0] {
827                } else {
828                    self.errors.push(Error::invalid_field(DeclType::ConfigType, "constraints"));
829                }
830            }
831            fdecl::ConfigTypeLayout::Vector => {
832                if accept_vectors {
833                    // Vector has exactly one constraint and one parameter
834                    if let Some(parameters) = &type_.parameters {
835                        if parameters.is_empty() {
836                            self.errors
837                                .push(Error::missing_field(DeclType::ConfigType, "parameters"));
838                        } else if parameters.len() > 1 {
839                            self.errors
840                                .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
841                        } else if let fdecl::LayoutParameter::NestedType(nested_type) =
842                            &parameters[0]
843                        {
844                            self.validate_config_type(nested_type, false);
845                        } else {
846                            self.errors
847                                .push(Error::invalid_field(DeclType::ConfigType, "parameters"));
848                        }
849                    } else {
850                        self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"))
851                    }
852
853                    if type_.constraints.is_empty() {
854                        self.errors.push(Error::missing_field(DeclType::ConfigType, "constraints"));
855                    } else if type_.constraints.len() > 1 {
856                        self.errors
857                            .push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
858                    } else if let fdecl::LayoutConstraint::MaxSize(_) = &type_.constraints[0] {
859                    } else {
860                        self.errors.push(Error::invalid_field(DeclType::ConfigType, "constraints"));
861                    }
862                } else {
863                    self.errors.push(Error::nested_vector());
864                }
865            }
866            _ => self.errors.push(Error::invalid_field(DeclType::ConfigType, "layout")),
867        }
868    }
869
870    fn validate_capability_decls(
871        &mut self,
872        capabilities: &'a [fdecl::Capability],
873        as_builtin: bool,
874    ) {
875        for capability in capabilities {
876            self.validate_capability_decl(capability, as_builtin);
877        }
878    }
879
880    /// Validates an individual capability declaration as either a built-in capability or (if
881    /// `as_builtin = false`) as a component or namespace capability.
882    // Storage capabilities are not currently allowed as built-ins, but there's no deep reason for this.
883    // Update this method to allow built-in storage capabilities as needed.
884    fn validate_capability_decl(&mut self, capability: &'a fdecl::Capability, as_builtin: bool) {
885        match capability {
886            fdecl::Capability::Service(service) => self.validate_service_decl(&service, as_builtin),
887            fdecl::Capability::Protocol(protocol) => {
888                self.validate_protocol_decl(&protocol, as_builtin)
889            }
890            fdecl::Capability::Directory(directory) => {
891                self.validate_directory_decl(&directory, as_builtin)
892            }
893            fdecl::Capability::Storage(storage) => {
894                if as_builtin {
895                    self.errors.push(Error::CapabilityCannotBeBuiltin(DeclType::Storage))
896                } else {
897                    self.validate_storage_decl(&storage)
898                }
899            }
900            fdecl::Capability::Runner(runner) => self.validate_runner_decl(&runner, as_builtin),
901            fdecl::Capability::Resolver(resolver) => {
902                self.validate_resolver_decl(&resolver, as_builtin)
903            }
904            fdecl::Capability::EventStream(event) => {
905                if as_builtin {
906                    self.validate_event_stream_decl(&event)
907                } else {
908                    self.errors.push(Error::CapabilityMustBeBuiltin(DeclType::EventStream))
909                }
910            }
911            #[cfg(fuchsia_api_level_at_least = "25")]
912            fdecl::Capability::Dictionary(dictionary) => {
913                self.validate_dictionary_decl(&dictionary);
914            }
915            #[cfg(fuchsia_api_level_at_least = "HEAD")]
916            fdecl::Capability::Config(config) => {
917                self.validate_configuration_decl(&config);
918            }
919            fdecl::CapabilityUnknown!() => self.errors.push(Error::UnknownCapability),
920        }
921    }
922
923    /// Returns the `source_name` and `source` of the runner in `uses`, if present.
924    fn validate_use_decls(
925        &mut self,
926        uses: &'a [fdecl::Use],
927    ) -> (Option<&'a String>, Option<&'a fdecl::Ref>) {
928        // Validate individual fields.
929        for use_ in uses.iter() {
930            self.validate_use_decl(&use_);
931        }
932        self.validate_use_paths(&uses);
933
934        #[cfg(fuchsia_api_level_at_least = "HEAD")]
935        {
936            let mut use_runner_name = None;
937            let mut use_runner_source = None;
938            for use_ in uses.iter() {
939                if let fdecl::Use::Runner(use_runner) = use_ {
940                    if use_runner_name.is_some() {
941                        self.errors.push(Error::MultipleRunnersUsed);
942                    }
943
944                    use_runner_name = use_runner.source_name.as_ref();
945                    use_runner_source = use_runner.source.as_ref();
946                }
947            }
948            return (use_runner_name, use_runner_source);
949        }
950        #[cfg(fuchsia_api_level_less_than = "HEAD")]
951        return (None, None);
952    }
953
954    fn validate_use_decl(&mut self, use_: &'a fdecl::Use) {
955        match use_ {
956            fdecl::Use::Service(u) => {
957                let decl = DeclType::UseService;
958                self.validate_use_fields(
959                    decl,
960                    Self::service_checker,
961                    u.source.as_ref(),
962                    u.source_name.as_ref(),
963                    get_source_dictionary!(u),
964                    u.target_path.as_ref(),
965                    u.dependency_type.as_ref(),
966                    u.availability.as_ref(),
967                );
968                if u.dependency_type.is_none() {
969                    self.errors.push(Error::missing_field(decl, "dependency_type"));
970                }
971            }
972            fdecl::Use::Protocol(u) => {
973                let decl = DeclType::UseProtocol;
974                self.validate_use_fields(
975                    decl,
976                    Self::protocol_checker,
977                    u.source.as_ref(),
978                    u.source_name.as_ref(),
979                    get_source_dictionary!(u),
980                    u.target_path.as_ref(),
981                    u.dependency_type.as_ref(),
982                    u.availability.as_ref(),
983                );
984                if u.dependency_type.is_none() {
985                    self.errors.push(Error::missing_field(decl, "dependency_type"));
986                }
987            }
988            fdecl::Use::Directory(u) => {
989                let decl = DeclType::UseDirectory;
990                self.validate_use_fields(
991                    decl,
992                    Self::directory_checker,
993                    u.source.as_ref(),
994                    u.source_name.as_ref(),
995                    get_source_dictionary!(u),
996                    u.target_path.as_ref(),
997                    u.dependency_type.as_ref(),
998                    u.availability.as_ref(),
999                );
1000                if u.dependency_type.is_none() {
1001                    self.errors.push(Error::missing_field(decl, "dependency_type"));
1002                }
1003                if u.rights.is_none() {
1004                    self.errors.push(Error::missing_field(DeclType::UseDirectory, "rights"));
1005                }
1006                if let Some(subdir) = u.subdir.as_ref() {
1007                    check_relative_path(
1008                        Some(subdir),
1009                        DeclType::UseDirectory,
1010                        "subdir",
1011                        &mut self.errors,
1012                    );
1013                }
1014            }
1015            fdecl::Use::Storage(u) => {
1016                const SOURCE: Option<fdecl::Ref> = Some(fdecl::Ref::Parent(fdecl::ParentRef {}));
1017                const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
1018                    Some(fdecl::DependencyType::Strong);
1019                self.validate_use_fields(
1020                    DeclType::UseStorage,
1021                    Self::storage_checker,
1022                    SOURCE.as_ref(),
1023                    u.source_name.as_ref(),
1024                    None,
1025                    u.target_path.as_ref(),
1026                    DEPENDENCY_TYPE.as_ref(),
1027                    u.availability.as_ref(),
1028                );
1029            }
1030            fdecl::Use::EventStream(u) => {
1031                const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
1032                    Some(fdecl::DependencyType::Strong);
1033                let decl = DeclType::UseEventStream;
1034                self.validate_use_fields(
1035                    decl,
1036                    Self::event_stream_checker,
1037                    u.source.as_ref(),
1038                    u.source_name.as_ref(),
1039                    None,
1040                    u.target_path.as_ref(),
1041                    DEPENDENCY_TYPE.as_ref(),
1042                    u.availability.as_ref(),
1043                );
1044                // Additional validation.
1045                match u.source {
1046                    Some(fdecl::Ref::Child(_)) | Some(fdecl::Ref::Parent(_)) => {
1047                        // Allowed.
1048                    }
1049                    Some(fdecl::Ref::Framework(_))
1050                    | Some(fdecl::Ref::Self_(_))
1051                    | Some(fdecl::Ref::Debug(_)) => {
1052                        // Allowed in general but not for event streams, add an error.
1053                        self.errors.push(Error::invalid_field(decl, "source"));
1054                    }
1055                    Some(fdecl::Ref::Collection(_)) | Some(fdecl::RefUnknown!()) | None => {
1056                        // Already handled by validate_use_fields.
1057                    }
1058                }
1059                if let Some(scope) = &u.scope {
1060                    for reference in scope {
1061                        if !matches!(reference, fdecl::Ref::Child(_) | fdecl::Ref::Collection(_)) {
1062                            self.errors.push(Error::invalid_field(decl, "scope"));
1063                        }
1064                    }
1065                }
1066            }
1067            #[cfg(fuchsia_api_level_at_least = "HEAD")]
1068            fdecl::Use::Runner(u) => {
1069                const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
1070                    Some(fdecl::DependencyType::Strong);
1071                const AVAILABILITY: Option<fdecl::Availability> =
1072                    Some(fdecl::Availability::Required);
1073                let decl = DeclType::UseRunner;
1074                self.validate_use_fields(
1075                    decl,
1076                    Self::runner_checker,
1077                    u.source.as_ref(),
1078                    u.source_name.as_ref(),
1079                    get_source_dictionary!(u),
1080                    None,
1081                    DEPENDENCY_TYPE.as_ref(),
1082                    AVAILABILITY.as_ref(),
1083                );
1084            }
1085            #[cfg(fuchsia_api_level_at_least = "HEAD")]
1086            fdecl::Use::Config(u) => {
1087                const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
1088                    Some(fdecl::DependencyType::Strong);
1089                let decl = DeclType::UseConfiguration;
1090                self.validate_use_fields(
1091                    decl,
1092                    Self::config_checker,
1093                    u.source.as_ref(),
1094                    u.source_name.as_ref(),
1095                    None,
1096                    None,
1097                    DEPENDENCY_TYPE.as_ref(),
1098                    u.availability.as_ref(),
1099                );
1100            }
1101            fdecl::UseUnknown!() => {
1102                self.errors.push(Error::invalid_field(DeclType::Component, "use"));
1103            }
1104        }
1105    }
1106
1107    /// Validates the "program" declaration. This does not check runner-specific properties
1108    /// since those are checked by the runner.
1109    fn validate_program(
1110        &mut self,
1111        program: &fdecl::Program,
1112        use_runner_name: Option<&String>,
1113        _use_runner_source: Option<&fdecl::Ref>,
1114    ) {
1115        match &program.runner {
1116            Some(_) =>
1117            {
1118                #[cfg(fuchsia_api_level_at_least = "HEAD")]
1119                if use_runner_name.is_some() {
1120                    if use_runner_name != program.runner.as_ref()
1121                        || _use_runner_source
1122                            != Some(&fdecl::Ref::Environment(fdecl::EnvironmentRef))
1123                    {
1124                        self.errors.push(Error::ConflictingRunners);
1125                    }
1126                }
1127            }
1128            None => {
1129                if use_runner_name.is_none() {
1130                    self.errors.push(Error::MissingRunner);
1131                }
1132            }
1133        }
1134
1135        if program.info.is_none() {
1136            self.errors.push(Error::missing_field(DeclType::Program, "info"));
1137        }
1138    }
1139
1140    /// Validates that paths-based capabilities (service, directory, protocol)
1141    /// are different, are not prefixes of each other, and do not collide "/pkg".
1142    fn validate_use_paths(&mut self, uses: &[fdecl::Use]) {
1143        #[derive(Debug, PartialEq, Clone, Copy)]
1144        struct PathCapability<'a> {
1145            decl: DeclType,
1146            dir: &'a Path,
1147            use_: &'a fdecl::Use,
1148        }
1149        let mut used_paths = HashMap::new();
1150        for use_ in uses.iter() {
1151            match use_ {
1152                fdecl::Use::Service(fdecl::UseService { target_path: Some(path), .. })
1153                | fdecl::Use::Protocol(fdecl::UseProtocol { target_path: Some(path), .. })
1154                | fdecl::Use::Directory(fdecl::UseDirectory { target_path: Some(path), .. })
1155                | fdecl::Use::Storage(fdecl::UseStorage { target_path: Some(path), .. }) => {
1156                    let capability = match use_ {
1157                        fdecl::Use::Service(_) => {
1158                            let dir = match Path::new(path).parent() {
1159                                Some(p) => p,
1160                                None => continue, // Invalid path, validated elsewhere
1161                            };
1162                            PathCapability { decl: DeclType::UseService, dir, use_ }
1163                        }
1164                        fdecl::Use::Protocol(_) => {
1165                            let dir = match Path::new(path).parent() {
1166                                Some(p) => p,
1167                                None => continue, // Invalid path, validated elsewhere
1168                            };
1169                            PathCapability { decl: DeclType::UseProtocol, dir, use_ }
1170                        }
1171                        fdecl::Use::Directory(_) => PathCapability {
1172                            decl: DeclType::UseDirectory,
1173                            dir: Path::new(path),
1174                            use_,
1175                        },
1176                        fdecl::Use::Storage(_) => PathCapability {
1177                            decl: DeclType::UseStorage,
1178                            dir: Path::new(path),
1179                            use_,
1180                        },
1181                        _ => unreachable!(),
1182                    };
1183                    if used_paths.insert(path, capability).is_some() {
1184                        // Disallow multiple capabilities for the same path.
1185                        self.errors.push(Error::duplicate_field(
1186                            capability.decl,
1187                            "target_path",
1188                            path,
1189                        ));
1190                    }
1191                }
1192                _ => {}
1193            }
1194        }
1195        for ((&path_a, capability_a), (&path_b, capability_b)) in
1196            used_paths.iter().tuple_combinations()
1197        {
1198            if match (capability_a.use_, capability_b.use_) {
1199                // Directories and storage can't be the same or partially overlap.
1200                (fdecl::Use::Directory(_), fdecl::Use::Directory(_))
1201                | (fdecl::Use::Storage(_), fdecl::Use::Directory(_))
1202                | (fdecl::Use::Directory(_), fdecl::Use::Storage(_))
1203                | (fdecl::Use::Storage(_), fdecl::Use::Storage(_)) => {
1204                    capability_b.dir == capability_a.dir
1205                        || capability_b.dir.starts_with(capability_a.dir)
1206                        || capability_a.dir.starts_with(capability_b.dir)
1207                }
1208
1209                // Protocols and Services can't overlap with Directories.
1210                (_, fdecl::Use::Directory(_)) | (fdecl::Use::Directory(_), _) => {
1211                    capability_b.dir == capability_a.dir
1212                        || capability_b.dir.starts_with(capability_a.dir)
1213                        || capability_a.dir.starts_with(capability_b.dir)
1214                }
1215
1216                // Protocols and Services containing directories may be same, but
1217                // partial overlap is disallowed.
1218                (_, _) => {
1219                    capability_b.dir != capability_a.dir
1220                        && (capability_b.dir.starts_with(capability_a.dir)
1221                            || capability_a.dir.starts_with(capability_b.dir))
1222                }
1223            } {
1224                self.errors.push(Error::invalid_path_overlap(
1225                    capability_a.decl,
1226                    path_a,
1227                    capability_b.decl,
1228                    path_b,
1229                ));
1230            }
1231        }
1232        for (used_path, capability) in used_paths.iter() {
1233            if used_path.as_str() == "/pkg" || used_path.starts_with("/pkg/") {
1234                self.errors.push(Error::pkg_path_overlap(capability.decl, *used_path));
1235            }
1236        }
1237    }
1238
1239    fn validate_use_fields(
1240        &mut self,
1241        decl: DeclType,
1242        // This takes a callback that returns a [Container], instead of the &[Container] directly,
1243        // to avoid a borrow checker error that would occur from a simultaneous borrow on
1244        // &mut self.
1245        capability_checker: impl Fn(&Self) -> &dyn Container,
1246        source: Option<&'a fdecl::Ref>,
1247        source_name: Option<&'a String>,
1248        source_dictionary: Option<&'a String>,
1249        target_path: Option<&'a String>,
1250        dependency_type: Option<&fdecl::DependencyType>,
1251        availability: Option<&'a fdecl::Availability>,
1252    ) {
1253        self.validate_use_source(decl, source, source_dictionary);
1254
1255        check_name(source_name, decl, "source_name", &mut self.errors);
1256        if source_dictionary.is_some() {
1257            check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
1258        }
1259        if decl != DeclType::UseRunner && decl != DeclType::UseConfiguration {
1260            check_path(target_path, decl, "target_path", &mut self.errors);
1261        }
1262        check_use_availability(decl, availability, &mut self.errors);
1263
1264        // Only allow `weak` dependency with `use from child`.
1265        let is_use_from_child = match source {
1266            Some(fdecl::Ref::Child(_)) => true,
1267            _ => false,
1268        };
1269        match (is_use_from_child, dependency_type) {
1270            (false, Some(fdecl::DependencyType::Weak)) => {
1271                self.errors.push(Error::invalid_field(decl, "dependency_type"));
1272            }
1273            _ => {}
1274        }
1275
1276        self.validate_route_from_self(
1277            decl,
1278            source,
1279            source_name,
1280            source_dictionary,
1281            capability_checker,
1282        );
1283    }
1284
1285    fn validate_use_source(
1286        &mut self,
1287        decl: DeclType,
1288        source: Option<&'a fdecl::Ref>,
1289        source_dictionary: Option<&'a String>,
1290    ) {
1291        match (source, source_dictionary) {
1292            // These sources support source_dictionary.
1293            (Some(fdecl::Ref::Parent(_)), _) => {}
1294            (Some(fdecl::Ref::Self_(_)), _) => {}
1295            (Some(fdecl::Ref::Child(child)), _) => {
1296                self.validate_child_ref(decl, "source", &child, OfferType::Static);
1297                return;
1298            }
1299            // These sources don't.
1300            (Some(fdecl::Ref::Framework(_)), None) => {}
1301            (Some(fdecl::Ref::Debug(_)), None) => {}
1302            (Some(fdecl::Ref::Capability(c)), None) => {
1303                self.validate_source_capability(&c, decl, "source");
1304                return;
1305            }
1306            #[cfg(fuchsia_api_level_at_least = "HEAD")]
1307            (Some(fdecl::Ref::Environment(_)), None) => {}
1308            (Some(fdecl::Ref::Collection(collection)), None) if decl == DeclType::UseService => {
1309                self.validate_collection_ref(decl, "source", &collection);
1310                return;
1311            }
1312            // `source` is required.
1313            (None, _) => self.errors.push(Error::missing_field(decl, "source")),
1314            // Any combination that was not recognized above must be invalid.
1315            (_, _) => self.errors.push(Error::invalid_field(decl, "source")),
1316        }
1317    }
1318
1319    fn validate_child_decl(&mut self, child: &'a fdecl::Child) {
1320        if let Err(mut e) = validate_child(child, check_name) {
1321            self.errors.append(&mut e.errs);
1322        }
1323        if let Some(name) = child.name.as_ref() {
1324            let name: &str = name;
1325            if self.all_children.insert(name, child).is_some() {
1326                self.errors.push(Error::duplicate_field(DeclType::Child, "name", name));
1327            }
1328        }
1329        if let Some(environment) = child.environment.as_ref() {
1330            if !self.all_environment_names.contains(environment.as_str()) {
1331                self.errors.push(Error::invalid_environment(
1332                    DeclType::Child,
1333                    "environment",
1334                    environment,
1335                ));
1336            }
1337        }
1338    }
1339
1340    fn validate_collection_decl(&mut self, collection: &'a fdecl::Collection) {
1341        let name = collection.name.as_ref();
1342        if check_name(name, DeclType::Collection, "name", &mut self.errors) {
1343            let name: &str = name.unwrap();
1344            if !self.all_collections.insert(name) {
1345                self.errors.push(Error::duplicate_field(DeclType::Collection, "name", name));
1346            }
1347        }
1348        if collection.durability.is_none() {
1349            self.errors.push(Error::missing_field(DeclType::Collection, "durability"));
1350        }
1351        if let Some(environment) = collection.environment.as_ref() {
1352            if !self.all_environment_names.contains(environment.as_str()) {
1353                self.errors.push(Error::invalid_environment(
1354                    DeclType::Collection,
1355                    "environment",
1356                    environment,
1357                ));
1358            }
1359        }
1360        // Allow `allowed_offers` & `allow_long_names` to be unset/unvalidated, for backwards compatibility.
1361    }
1362
1363    fn validate_environment_decl(&mut self, environment: &'a fdecl::Environment) {
1364        let name = environment.name.as_ref();
1365        check_name(name, DeclType::Environment, "name", &mut self.errors);
1366        if environment.extends.is_none() {
1367            self.errors.push(Error::missing_field(DeclType::Environment, "extends"));
1368        }
1369        if let Some(runners) = environment.runners.as_ref() {
1370            let mut registered_runners = HashSet::new();
1371            for runner in runners {
1372                self.validate_runner_registration(runner, name.clone(), &mut registered_runners);
1373            }
1374        }
1375        if let Some(resolvers) = environment.resolvers.as_ref() {
1376            let mut registered_schemes = HashSet::new();
1377            for resolver in resolvers {
1378                self.validate_resolver_registration(
1379                    resolver,
1380                    name.clone(),
1381                    &mut registered_schemes,
1382                );
1383            }
1384        }
1385
1386        match environment.extends.as_ref() {
1387            Some(fdecl::EnvironmentExtends::None) => {
1388                if environment.stop_timeout_ms.is_none() {
1389                    self.errors
1390                        .push(Error::missing_field(DeclType::Environment, "stop_timeout_ms"));
1391                }
1392            }
1393            None | Some(fdecl::EnvironmentExtends::Realm) => {}
1394        }
1395
1396        if let Some(debugs) = environment.debug_capabilities.as_ref() {
1397            for debug in debugs {
1398                self.validate_environment_debug_registration(debug);
1399            }
1400        }
1401    }
1402
1403    fn validate_runner_registration(
1404        &mut self,
1405        runner_registration: &'a fdecl::RunnerRegistration,
1406        environment_name: Option<&'a String>,
1407        runner_names: &mut HashSet<&'a str>,
1408    ) {
1409        check_name(
1410            runner_registration.source_name.as_ref(),
1411            DeclType::RunnerRegistration,
1412            "source_name",
1413            &mut self.errors,
1414        );
1415        self.validate_registration_source(
1416            environment_name,
1417            runner_registration.source.as_ref(),
1418            DeclType::RunnerRegistration,
1419        );
1420        // If the source is `self`, ensure we have a corresponding Runner.
1421        if let (Some(fdecl::Ref::Self_(_)), Some(ref name)) =
1422            (&runner_registration.source, &runner_registration.source_name)
1423        {
1424            if !self.all_runners.contains(name as &str) {
1425                self.errors.push(Error::invalid_runner(
1426                    DeclType::RunnerRegistration,
1427                    "source_name",
1428                    name,
1429                ));
1430            }
1431        }
1432
1433        check_name(
1434            runner_registration.target_name.as_ref(),
1435            DeclType::RunnerRegistration,
1436            "target_name",
1437            &mut self.errors,
1438        );
1439        if let Some(name) = runner_registration.target_name.as_ref() {
1440            if !runner_names.insert(name.as_str()) {
1441                self.errors.push(Error::duplicate_field(
1442                    DeclType::RunnerRegistration,
1443                    "target_name",
1444                    name,
1445                ));
1446            }
1447        }
1448    }
1449
1450    fn validate_resolver_registration(
1451        &mut self,
1452        resolver_registration: &'a fdecl::ResolverRegistration,
1453        environment_name: Option<&'a String>,
1454        schemes: &mut HashSet<&'a str>,
1455    ) {
1456        check_name(
1457            resolver_registration.resolver.as_ref(),
1458            DeclType::ResolverRegistration,
1459            "resolver",
1460            &mut self.errors,
1461        );
1462        self.validate_registration_source(
1463            environment_name,
1464            resolver_registration.source.as_ref(),
1465            DeclType::ResolverRegistration,
1466        );
1467        check_url_scheme(
1468            resolver_registration.scheme.as_ref(),
1469            DeclType::ResolverRegistration,
1470            "scheme",
1471            &mut self.errors,
1472        );
1473        if let Some(scheme) = resolver_registration.scheme.as_ref() {
1474            if !schemes.insert(scheme.as_str()) {
1475                self.errors.push(Error::duplicate_field(
1476                    DeclType::ResolverRegistration,
1477                    "scheme",
1478                    scheme,
1479                ));
1480            }
1481        }
1482    }
1483
1484    fn validate_registration_source(
1485        &mut self,
1486        environment_name: Option<&'a String>,
1487        source: Option<&'a fdecl::Ref>,
1488        ty: DeclType,
1489    ) {
1490        match source {
1491            Some(fdecl::Ref::Parent(_)) => {}
1492            Some(fdecl::Ref::Self_(_)) => {}
1493            Some(fdecl::Ref::Child(child_ref)) => {
1494                // Make sure the child is valid.
1495                self.validate_child_ref(ty, "source", &child_ref, OfferType::Static);
1496            }
1497            Some(_) => {
1498                self.errors.push(Error::invalid_field(ty, "source"));
1499            }
1500            None => {
1501                self.errors.push(Error::missing_field(ty, "source"));
1502            }
1503        }
1504
1505        let source = self.source_dependency_from_ref(None, None, source);
1506        if let Some(source) = source {
1507            if let Some(env_name) = &environment_name {
1508                let target = DependencyNode::Environment(env_name);
1509                self.strong_dependencies.add_edge(source, target);
1510            }
1511        }
1512    }
1513
1514    fn validate_service_decl(&mut self, service: &'a fdecl::Service, as_builtin: bool) {
1515        if check_name(service.name.as_ref(), DeclType::Service, "name", &mut self.errors) {
1516            let name = service.name.as_ref().unwrap();
1517            if !self.all_capability_ids.insert(name) {
1518                self.errors.push(Error::duplicate_field(DeclType::Service, "name", name.as_str()));
1519            }
1520            self.all_services.insert(name);
1521        }
1522        match as_builtin {
1523            true => {
1524                if let Some(path) = service.source_path.as_ref() {
1525                    self.errors.push(Error::extraneous_source_path(DeclType::Service, path))
1526                }
1527            }
1528            false => {
1529                check_path(
1530                    service.source_path.as_ref(),
1531                    DeclType::Service,
1532                    "source_path",
1533                    &mut self.errors,
1534                );
1535            }
1536        }
1537    }
1538
1539    fn validate_protocol_decl(&mut self, protocol: &'a fdecl::Protocol, as_builtin: bool) {
1540        if check_name(protocol.name.as_ref(), DeclType::Protocol, "name", &mut self.errors) {
1541            let name = protocol.name.as_ref().unwrap();
1542            if !self.all_capability_ids.insert(name) {
1543                self.errors.push(Error::duplicate_field(DeclType::Protocol, "name", name.as_str()));
1544            }
1545            self.all_protocols.insert(name);
1546        }
1547        match as_builtin {
1548            true => {
1549                if let Some(path) = protocol.source_path.as_ref() {
1550                    self.errors.push(Error::extraneous_source_path(DeclType::Protocol, path))
1551                }
1552            }
1553            false => {
1554                check_path(
1555                    protocol.source_path.as_ref(),
1556                    DeclType::Protocol,
1557                    "source_path",
1558                    &mut self.errors,
1559                );
1560            }
1561        }
1562
1563        #[cfg(fuchsia_api_level_at_least = "HEAD")]
1564        match protocol.delivery {
1565            Some(delivery) => match cm_types::DeliveryType::try_from(delivery) {
1566                Ok(_) => {}
1567                Err(_) => self.errors.push(Error::invalid_field(DeclType::Protocol, "delivery")),
1568            },
1569            None => {}
1570        }
1571    }
1572
1573    fn validate_directory_decl(&mut self, directory: &'a fdecl::Directory, as_builtin: bool) {
1574        if check_name(directory.name.as_ref(), DeclType::Directory, "name", &mut self.errors) {
1575            let name = directory.name.as_ref().unwrap();
1576            if !self.all_capability_ids.insert(name) {
1577                self.errors.push(Error::duplicate_field(
1578                    DeclType::Directory,
1579                    "name",
1580                    name.as_str(),
1581                ));
1582            }
1583            self.all_directories.insert(name);
1584        }
1585        match as_builtin {
1586            true => {
1587                if let Some(path) = directory.source_path.as_ref() {
1588                    self.errors.push(Error::extraneous_source_path(DeclType::Directory, path))
1589                }
1590            }
1591            false => {
1592                check_path(
1593                    directory.source_path.as_ref(),
1594                    DeclType::Directory,
1595                    "source_path",
1596                    &mut self.errors,
1597                );
1598            }
1599        }
1600        if directory.rights.is_none() {
1601            self.errors.push(Error::missing_field(DeclType::Directory, "rights"));
1602        }
1603    }
1604
1605    fn validate_storage_decl(&mut self, storage: &'a fdecl::Storage) {
1606        match storage.source.as_ref() {
1607            Some(fdecl::Ref::Parent(_)) => {}
1608            Some(fdecl::Ref::Self_(_)) => {}
1609            Some(fdecl::Ref::Child(child)) => {
1610                let _ =
1611                    self.validate_child_ref(DeclType::Storage, "source", &child, OfferType::Static);
1612            }
1613            Some(_) => {
1614                self.errors.push(Error::invalid_field(DeclType::Storage, "source"));
1615            }
1616            None => {
1617                self.errors.push(Error::missing_field(DeclType::Storage, "source"));
1618            }
1619        };
1620        if check_name(storage.name.as_ref(), DeclType::Storage, "name", &mut self.errors) {
1621            let name = storage.name.as_ref().unwrap();
1622            if !self.all_capability_ids.insert(name) {
1623                self.errors.push(Error::duplicate_field(DeclType::Storage, "name", name.as_str()));
1624            }
1625            self.all_storages.insert(name, storage.source.as_ref());
1626        }
1627        if storage.storage_id.is_none() {
1628            self.errors.push(Error::missing_field(DeclType::Storage, "storage_id"));
1629        }
1630        check_name(
1631            storage.backing_dir.as_ref(),
1632            DeclType::Storage,
1633            "backing_dir",
1634            &mut self.errors,
1635        );
1636    }
1637
1638    fn validate_runner_decl(&mut self, runner: &'a fdecl::Runner, as_builtin: bool) {
1639        if check_name(runner.name.as_ref(), DeclType::Runner, "name", &mut self.errors) {
1640            let name = runner.name.as_ref().unwrap();
1641            if !self.all_capability_ids.insert(name) {
1642                self.errors.push(Error::duplicate_field(DeclType::Runner, "name", name.as_str()));
1643            }
1644            self.all_runners.insert(name);
1645        }
1646        match as_builtin {
1647            true => {
1648                if let Some(path) = runner.source_path.as_ref() {
1649                    self.errors.push(Error::extraneous_source_path(DeclType::Runner, path))
1650                }
1651            }
1652            false => {
1653                check_path(
1654                    runner.source_path.as_ref(),
1655                    DeclType::Runner,
1656                    "source_path",
1657                    &mut self.errors,
1658                );
1659            }
1660        }
1661    }
1662
1663    fn validate_resolver_decl(&mut self, resolver: &'a fdecl::Resolver, as_builtin: bool) {
1664        if check_name(resolver.name.as_ref(), DeclType::Resolver, "name", &mut self.errors) {
1665            let name = resolver.name.as_ref().unwrap();
1666            if !self.all_capability_ids.insert(name) {
1667                self.errors.push(Error::duplicate_field(DeclType::Resolver, "name", name.as_str()));
1668            }
1669            self.all_resolvers.insert(name);
1670        }
1671        match as_builtin {
1672            true => {
1673                if let Some(path) = resolver.source_path.as_ref() {
1674                    self.errors.push(Error::extraneous_source_path(DeclType::Resolver, path))
1675                }
1676            }
1677            false => {
1678                check_path(
1679                    resolver.source_path.as_ref(),
1680                    DeclType::Resolver,
1681                    "source_path",
1682                    &mut self.errors,
1683                );
1684            }
1685        }
1686    }
1687
1688    // Dictionaries can reference other dictionaries in the same manifest, so before processing any
1689    // dictionary declarations this function should be called to do a first pass to pre-populate
1690    // the dictionary map.
1691    #[cfg(fuchsia_api_level_at_least = "25")]
1692    fn load_dictionary_names(&mut self, dictionaries: impl Iterator<Item = &'a fdecl::Dictionary>) {
1693        for dictionary in dictionaries {
1694            let decl = DeclType::Dictionary;
1695            if check_name(dictionary.name.as_ref(), decl, "name", &mut self.errors) {
1696                let name = dictionary.name.as_ref().unwrap();
1697                if !self.all_capability_ids.insert(name) {
1698                    self.errors.push(Error::duplicate_field(decl, "name", name.as_str()));
1699                }
1700                self.all_dictionaries.insert(name, &dictionary);
1701            }
1702        }
1703    }
1704
1705    #[cfg(fuchsia_api_level_at_least = "25")]
1706    fn validate_dictionary_decl(&mut self, dictionary: &'a fdecl::Dictionary) {
1707        let decl = DeclType::Dictionary;
1708        if let Some(path) = dictionary.source_path.as_ref() {
1709            if dictionary.source.is_some() {
1710                self.errors.push(Error::extraneous_field(decl, "source"));
1711            }
1712            check_path(Some(path), DeclType::Dictionary, "source_path", &mut self.errors);
1713        }
1714    }
1715
1716    #[cfg(fuchsia_api_level_at_least = "HEAD")]
1717    fn validate_configuration_decl(&mut self, config: &'a fdecl::Configuration) {
1718        let decl = DeclType::Configuration;
1719        if check_name(config.name.as_ref(), decl, "name", &mut self.errors) {
1720            let name = config.name.as_ref().unwrap();
1721            if !self.all_capability_ids.insert(name) {
1722                self.errors.push(Error::duplicate_field(decl, "name", name.as_str()));
1723            }
1724            self.all_configs.insert(name);
1725        }
1726    }
1727
1728    fn validate_environment_debug_registration(&mut self, debug: &'a fdecl::DebugRegistration) {
1729        match debug {
1730            fdecl::DebugRegistration::Protocol(o) => {
1731                let decl = DeclType::DebugProtocolRegistration;
1732                self.validate_environment_debug_fields(
1733                    decl,
1734                    o.source.as_ref(),
1735                    o.source_name.as_ref(),
1736                    o.target_name.as_ref(),
1737                );
1738
1739                if let (Some(fdecl::Ref::Self_(_)), Some(ref name)) = (&o.source, &o.source_name) {
1740                    if !self.all_protocols.contains(&name as &str) {
1741                        self.errors.push(Error::invalid_field(decl, "source"));
1742                    }
1743                }
1744            }
1745            _ => {
1746                self.errors.push(Error::invalid_field(DeclType::Environment, "debug"));
1747            }
1748        }
1749    }
1750
1751    fn validate_environment_debug_fields(
1752        &mut self,
1753        decl: DeclType,
1754        source: Option<&fdecl::Ref>,
1755        source_name: Option<&String>,
1756        target_name: Option<&'a String>,
1757    ) {
1758        // We don't support "source" from "capability" for now.
1759        match source {
1760            Some(fdecl::Ref::Parent(_)) => {}
1761            Some(fdecl::Ref::Self_(_)) => {}
1762            Some(fdecl::Ref::Framework(_)) => {}
1763            Some(fdecl::Ref::Child(child)) => {
1764                let _ = self.validate_child_ref(decl, "source", &child, OfferType::Static);
1765            }
1766            Some(_) => self.errors.push(Error::invalid_field(decl, "source")),
1767            None => self.errors.push(Error::missing_field(decl, "source")),
1768        }
1769        check_name(source_name, decl, "source_name", &mut self.errors);
1770        check_name(target_name, decl, "target_name", &mut self.errors);
1771    }
1772
1773    fn validate_event_stream_decl(&mut self, event: &'a fdecl::EventStream) {
1774        if check_name(event.name.as_ref(), DeclType::EventStream, "name", &mut self.errors) {
1775            let name = event.name.as_ref().unwrap();
1776            if !self.all_capability_ids.insert(name) {
1777                self.errors.push(Error::duplicate_field(
1778                    DeclType::EventStream,
1779                    "name",
1780                    name.as_str(),
1781                ));
1782            }
1783        }
1784    }
1785
1786    fn validate_source_collection(
1787        &mut self,
1788        collection: &fdecl::CollectionRef,
1789        decl_type: DeclType,
1790    ) -> bool {
1791        let num_errors = self.errors.len();
1792        if check_name(Some(&collection.name), decl_type, "source.collection.name", &mut self.errors)
1793            && !self.all_collections.contains(&collection.name as &str)
1794        {
1795            self.errors.push(Error::invalid_collection(
1796                decl_type,
1797                "source",
1798                &collection.name as &str,
1799            ));
1800        }
1801        num_errors == self.errors.len()
1802    }
1803
1804    fn validate_filtered_service_fields(
1805        &mut self,
1806        decl_type: DeclType,
1807        source_instance_filter: Option<&Vec<String>>,
1808        renamed_instances: Option<&Vec<fdecl::NameMapping>>,
1809    ) {
1810        if let Some(source_instance_filter) = source_instance_filter {
1811            if source_instance_filter.is_empty() {
1812                // if the  source_instance_filter is empty the offered service will have 0 instances,
1813                // which means the offer shouldn't have been created at all.
1814                self.errors.push(Error::invalid_field(decl_type, "source_instance_filter"));
1815            }
1816            for name in source_instance_filter {
1817                check_name(Some(name), decl_type, "source_instance_filter", &mut self.errors);
1818            }
1819        }
1820        if let Some(renamed_instances) = renamed_instances {
1821            // Multiple sources shouldn't map to the same target name
1822            let mut seen_target_names = HashSet::<String>::new();
1823            for mapping in renamed_instances {
1824                check_name(
1825                    Some(&mapping.source_name),
1826                    decl_type,
1827                    "renamed_instances.source_name",
1828                    &mut self.errors,
1829                );
1830                check_name(
1831                    Some(&mapping.target_name),
1832                    decl_type,
1833                    "renamed_instances.target_name",
1834                    &mut self.errors,
1835                );
1836                if !seen_target_names.insert(mapping.target_name.clone()) {
1837                    self.errors.push(Error::invalid_field(decl_type, "renamed_instances"));
1838                    break;
1839                }
1840            }
1841        }
1842    }
1843
1844    fn validate_source_capability(
1845        &mut self,
1846        capability: &fdecl::CapabilityRef,
1847        decl_type: DeclType,
1848        field: &str,
1849    ) -> bool {
1850        let num_errors = self.errors.len();
1851        if check_name(Some(&capability.name), decl_type, "source.capability.name", &mut self.errors)
1852            && !self.all_capability_ids.contains(capability.name.as_str())
1853        {
1854            self.errors.push(Error::invalid_capability(decl_type, field, &capability.name));
1855        }
1856        num_errors == self.errors.len()
1857    }
1858
1859    /// Return a key that can be used in `HashMap` to group aggregate declarations.
1860    ///
1861    /// Returns `None` if the input resembles an invalid declaration.
1862    fn make_group_key(
1863        target_name: Option<&'a String>,
1864        target: Option<&'a fdecl::Ref>,
1865    ) -> Option<(&'a str, RefKey<'a>)> {
1866        if target_name.is_none() {
1867            return None;
1868        }
1869        let target_name = target_name.unwrap().as_str();
1870        if target.is_none() {
1871            return None;
1872        }
1873        let target = match target.unwrap() {
1874            fdecl::Ref::Parent(_) => RefKey::Parent,
1875            fdecl::Ref::Self_(_) => RefKey::Self_,
1876            fdecl::Ref::Child(r) => RefKey::Child(r.name.as_str()),
1877            fdecl::Ref::Collection(r) => RefKey::Collection(r.name.as_str()),
1878            fdecl::Ref::Framework(_) => RefKey::Framework,
1879            fdecl::Ref::Capability(_) => RefKey::Capability,
1880            fdecl::Ref::Debug(_) => RefKey::Debug,
1881            fdecl::RefUnknown!() => {
1882                return None;
1883            }
1884        };
1885        Some((target_name, target))
1886    }
1887
1888    fn validate_aggregation_has_same_availability(
1889        &mut self,
1890        route_group: &Vec<impl HasAvailability>,
1891    ) {
1892        // Use `BtreeSet` for stable ordering of items in error message.
1893        let availability_of_sources: BTreeSet<_> =
1894            route_group.iter().map(|r| r.availability()).collect();
1895
1896        // All sources that feed into an aggregation operation should have the same availability.
1897        if availability_of_sources.len() > 1 {
1898            self.errors.push(Error::different_availability_in_aggregation(
1899                availability_of_sources.into_iter().collect(),
1900            ));
1901        }
1902    }
1903
1904    // Checks a group of expose decls to confirm that any duplicate exposes are
1905    // valid aggregate expose declarations.
1906    fn validate_expose_group(&mut self, exposes: &'a Vec<fdecl::Expose>) {
1907        let mut expose_groups: HashMap<_, Vec<fdecl::ExposeService>> = HashMap::new();
1908        let service_exposes = exposes.into_iter().filter_map(|o| {
1909            if let fdecl::Expose::Service(s) = o {
1910                Some(s)
1911            } else {
1912                None
1913            }
1914        });
1915        for expose in service_exposes {
1916            let key = Self::make_group_key(expose.target_name.as_ref(), expose.target.as_ref());
1917            if let Some(key) = key {
1918                expose_groups.entry(key).or_insert_with(|| vec![]).push(expose.clone());
1919            }
1920        }
1921        for expose_group in expose_groups.into_values() {
1922            if expose_group.len() == 1 {
1923                // If there are not multiple exposes for a (target_name, target) pair then there are
1924                // no aggregation conditions to check.
1925                continue;
1926            }
1927
1928            self.validate_aggregation_has_same_availability(&expose_group);
1929        }
1930    }
1931
1932    fn validate_expose_decl(
1933        &mut self,
1934        expose: &'a fdecl::Expose,
1935        expose_to_parent_ids: &mut HashMap<&'a str, AllowableIds>,
1936        expose_to_framework_ids: &mut HashMap<&'a str, AllowableIds>,
1937    ) {
1938        match expose {
1939            fdecl::Expose::Service(e) => {
1940                let decl = DeclType::ExposeService;
1941                self.validate_expose_fields(
1942                    decl,
1943                    AllowableIds::Many,
1944                    CollectionSource::Allow,
1945                    Self::service_checker,
1946                    e.source.as_ref(),
1947                    e.source_name.as_ref(),
1948                    get_source_dictionary!(e),
1949                    e.target.as_ref(),
1950                    e.target_name.as_ref(),
1951                    e.availability.as_ref(),
1952                    expose_to_parent_ids,
1953                    expose_to_framework_ids,
1954                );
1955            }
1956            fdecl::Expose::Protocol(e) => {
1957                let decl = DeclType::ExposeProtocol;
1958                self.validate_expose_fields(
1959                    decl,
1960                    AllowableIds::One,
1961                    CollectionSource::Deny,
1962                    Self::protocol_checker,
1963                    e.source.as_ref(),
1964                    e.source_name.as_ref(),
1965                    get_source_dictionary!(e),
1966                    e.target.as_ref(),
1967                    e.target_name.as_ref(),
1968                    e.availability.as_ref(),
1969                    expose_to_parent_ids,
1970                    expose_to_framework_ids,
1971                );
1972            }
1973            fdecl::Expose::Directory(e) => {
1974                let decl = DeclType::ExposeDirectory;
1975                self.validate_expose_fields(
1976                    decl,
1977                    AllowableIds::One,
1978                    CollectionSource::Deny,
1979                    Self::directory_checker,
1980                    e.source.as_ref(),
1981                    e.source_name.as_ref(),
1982                    get_source_dictionary!(e),
1983                    e.target.as_ref(),
1984                    e.target_name.as_ref(),
1985                    e.availability.as_ref(),
1986                    expose_to_parent_ids,
1987                    expose_to_framework_ids,
1988                );
1989
1990                // Subdir makes sense when routing, but when exposing to framework the subdirectory
1991                // can be exposed directly.
1992                match e.target.as_ref() {
1993                    Some(fdecl::Ref::Framework(_)) => {
1994                        if e.subdir.is_some() {
1995                            self.errors.push(Error::invalid_field(decl, "subdir"));
1996                        }
1997                    }
1998                    _ => {}
1999                }
2000
2001                if let Some(subdir) = e.subdir.as_ref() {
2002                    check_relative_path(Some(subdir), decl, "subdir", &mut self.errors);
2003                }
2004            }
2005            fdecl::Expose::Runner(e) => {
2006                let decl = DeclType::ExposeRunner;
2007                self.validate_expose_fields(
2008                    decl,
2009                    AllowableIds::One,
2010                    CollectionSource::Deny,
2011                    Self::runner_checker,
2012                    e.source.as_ref(),
2013                    e.source_name.as_ref(),
2014                    get_source_dictionary!(e),
2015                    e.target.as_ref(),
2016                    e.target_name.as_ref(),
2017                    Some(&fdecl::Availability::Required),
2018                    expose_to_parent_ids,
2019                    expose_to_framework_ids,
2020                );
2021            }
2022            fdecl::Expose::Resolver(e) => {
2023                let decl = DeclType::ExposeResolver;
2024                self.validate_expose_fields(
2025                    decl,
2026                    AllowableIds::One,
2027                    CollectionSource::Deny,
2028                    Self::resolver_checker,
2029                    e.source.as_ref(),
2030                    e.source_name.as_ref(),
2031                    get_source_dictionary!(e),
2032                    e.target.as_ref(),
2033                    e.target_name.as_ref(),
2034                    Some(&fdecl::Availability::Required),
2035                    expose_to_parent_ids,
2036                    expose_to_framework_ids,
2037                );
2038            }
2039            #[cfg(fuchsia_api_level_at_least = "25")]
2040            fdecl::Expose::Dictionary(e) => {
2041                let decl = DeclType::ExposeDictionary;
2042                self.validate_expose_fields(
2043                    decl,
2044                    AllowableIds::One,
2045                    CollectionSource::Deny,
2046                    Self::dictionary_checker,
2047                    e.source.as_ref(),
2048                    e.source_name.as_ref(),
2049                    get_source_dictionary!(e),
2050                    e.target.as_ref(),
2051                    e.target_name.as_ref(),
2052                    Some(&fdecl::Availability::Required),
2053                    expose_to_parent_ids,
2054                    expose_to_framework_ids,
2055                );
2056            }
2057            #[cfg(fuchsia_api_level_at_least = "HEAD")]
2058            fdecl::Expose::Config(e) => {
2059                let decl = DeclType::ExposeConfig;
2060                self.validate_expose_fields(
2061                    decl,
2062                    AllowableIds::One,
2063                    CollectionSource::Deny,
2064                    Self::config_checker,
2065                    e.source.as_ref(),
2066                    e.source_name.as_ref(),
2067                    None,
2068                    e.target.as_ref(),
2069                    e.target_name.as_ref(),
2070                    e.availability.as_ref(),
2071                    expose_to_parent_ids,
2072                    expose_to_framework_ids,
2073                );
2074            }
2075            _ => {
2076                self.errors.push(Error::invalid_field(DeclType::Component, "expose"));
2077            }
2078        }
2079    }
2080
2081    fn validate_expose_fields(
2082        &mut self,
2083        decl: DeclType,
2084        allowable_ids: AllowableIds,
2085        collection_source: CollectionSource,
2086        // This takes a callback that returns a [Container], instead of the &[Container] directly,
2087        // to avoid a borrow checker error that would occur from a simultaneous borrow on
2088        // &mut self.
2089        capability_checker: impl Fn(&Self) -> &dyn Container,
2090        source: Option<&fdecl::Ref>,
2091        source_name: Option<&String>,
2092        source_dictionary: Option<&String>,
2093        target: Option<&fdecl::Ref>,
2094        target_name: Option<&'a String>,
2095        availability: Option<&fdecl::Availability>,
2096        expose_to_parent_ids: &mut HashMap<&'a str, AllowableIds>,
2097        expose_to_framework_ids: &mut HashMap<&'a str, AllowableIds>,
2098    ) {
2099        self.validate_expose_source(decl, collection_source, source, source_dictionary);
2100        check_route_availability(decl, availability, source, source_name, &mut self.errors);
2101        match target {
2102            Some(r) => match r {
2103                fdecl::Ref::Parent(_) => {}
2104                fdecl::Ref::Framework(_) => {}
2105                _ => {
2106                    self.errors.push(Error::invalid_field(decl, "target"));
2107                }
2108            },
2109            None => {
2110                self.errors.push(Error::missing_field(decl, "target"));
2111            }
2112        }
2113        check_name(source_name, decl, "source_name", &mut self.errors);
2114        if source_dictionary.is_some() {
2115            check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
2116        }
2117        if check_name(target_name, decl, "target_name", &mut self.errors) {
2118            let maybe_ids_set = match target {
2119                Some(fdecl::Ref::Parent(_)) => Some(expose_to_parent_ids),
2120                Some(fdecl::Ref::Framework(_)) => Some(expose_to_framework_ids),
2121                _ => None,
2122            };
2123            if let Some(ids_set) = maybe_ids_set {
2124                let target_name = target_name.unwrap();
2125                if let Some(prev_state) = ids_set.insert(target_name, allowable_ids) {
2126                    if prev_state == AllowableIds::One || prev_state != allowable_ids {
2127                        self.errors.push(Error::duplicate_field(decl, "target_name", target_name));
2128                    }
2129                }
2130            }
2131        }
2132
2133        self.validate_route_from_self(
2134            decl,
2135            source,
2136            source_name,
2137            source_dictionary,
2138            capability_checker,
2139        );
2140    }
2141
2142    fn validate_expose_source(
2143        &mut self,
2144        decl: DeclType,
2145        collection_source: CollectionSource,
2146        source: Option<&fdecl::Ref>,
2147        source_dictionary: Option<&String>,
2148    ) {
2149        match (source, source_dictionary) {
2150            // These sources support source_dictionary.
2151            (Some(fdecl::Ref::Self_(_)), _) => {}
2152            (Some(fdecl::Ref::Child(child)), _) => {
2153                let _ = self.validate_child_ref(decl, "source", &child, OfferType::Static);
2154            }
2155            // These sources don't.
2156            (Some(fdecl::Ref::VoidType(_)), None) => {}
2157            (Some(fdecl::Ref::Framework(_)), None) => {}
2158            (Some(fdecl::Ref::Capability(c)), None) => {
2159                self.validate_source_capability(c, decl, "source");
2160            }
2161            (Some(fdecl::Ref::Collection(c)), None)
2162                if collection_source == CollectionSource::Allow =>
2163            {
2164                self.validate_source_collection(c, decl);
2165            }
2166            // `source` is required.
2167            (None, _) => {
2168                self.errors.push(Error::missing_field(decl, "source"));
2169            }
2170            // Any combination that was not recognized above must be invalid.
2171            (_, _) => {
2172                self.errors.push(Error::invalid_field(decl, "source"));
2173            }
2174        }
2175    }
2176
2177    /// Adds a strong dependency between two nodes in the dependency graph between `source` and
2178    /// `target`.
2179    ///
2180    /// `source_name` is the name of the capability being routed (if applicable). The function is
2181    /// a no-op if `source` or `target` is `None`; this behavior is a convenience so that the
2182    /// caller can directly pass the result of `DependencyNode::try_from_ref`.
2183    fn add_strong_dep(
2184        &mut self,
2185        source: Option<DependencyNode<'a>>,
2186        target: Option<DependencyNode<'a>>,
2187    ) {
2188        if source.is_none() || target.is_none() {
2189            return;
2190        }
2191        let source = source.unwrap();
2192        let target = target.unwrap();
2193        match (source, target) {
2194            (DependencyNode::Self_, DependencyNode::Self_) => {
2195                // `self` dependencies (e.g. `use from self`) are allowed.
2196            }
2197            (source, target) => {
2198                self.strong_dependencies.add_edge(source, target);
2199            }
2200        }
2201    }
2202
2203    // Checks a group of offer decls to confirm that any duplicate offers are
2204    // valid aggregate offer declarations.
2205    fn validate_offer_group(&mut self, offers: &'a Vec<fdecl::Offer>, offer_type: OfferType) {
2206        let mut offer_groups: HashMap<_, Vec<fdecl::OfferService>> = HashMap::new();
2207        let service_offers = offers.into_iter().filter_map(|o| {
2208            if let fdecl::Offer::Service(s) = o {
2209                Some(s)
2210            } else {
2211                None
2212            }
2213        });
2214        for offer in service_offers {
2215            let key = Self::make_group_key(offer.target_name.as_ref(), offer.target.as_ref());
2216            if let Some(key) = key {
2217                offer_groups.entry(key).or_insert_with(|| vec![]).push(offer.clone());
2218            }
2219        }
2220        for offer_group in offer_groups.into_values() {
2221            if offer_group.len() == 1 {
2222                // If there are not multiple offers for a (target_name, target) pair then there are
2223                // no aggregation conditions to check.
2224                continue;
2225            }
2226
2227            self.validate_aggregation_has_same_availability(&offer_group);
2228
2229            let mut source_instance_filter_entries: HashSet<String> = HashSet::new();
2230            let mut service_source_names: HashSet<String> = HashSet::new();
2231            for o in offer_group {
2232                // Currently only service capabilities can be aggregated
2233                match (o.source_instance_filter, offer_type) {
2234                    (Some(source_instance_filter), _) => {
2235                        for instance_name in source_instance_filter {
2236                            if !source_instance_filter_entries.insert(instance_name.clone()) {
2237                                // If the source instance in the filter has been seen before this
2238                                // means there is a conflicting aggregate service offer.
2239                                self.errors.push(Error::invalid_aggregate_offer(format!(
2240                                    "Conflicting source_instance_filter in aggregate service \
2241                                    offer, instance_name '{}' seen in filter lists multiple times",
2242                                    instance_name,
2243                                )));
2244                            }
2245                        }
2246                    }
2247                    (None, OfferType::Static) => {}
2248                    (None, OfferType::Dynamic) => {
2249                        // Dynamic offers must include a filter.
2250                        self.errors.push(Error::invalid_aggregate_offer(
2251                            "source_instance_filter must be set for dynamic aggregate service \
2252                            offers",
2253                        ));
2254                    }
2255                }
2256                service_source_names.insert(
2257                    o.source_name
2258                        .expect("Offer Service declarations must always contain source_name"),
2259                );
2260            }
2261
2262            if service_source_names.len() > 1 {
2263                self.errors.push(Error::invalid_aggregate_offer(format!(
2264                    "All aggregate service offers must have the same source_name, saw {}. Use \
2265                    renamed_instances to rename instance names to avoid conflict.",
2266                    service_source_names.into_iter().sorted().collect::<Vec<String>>().join(", ")
2267                )));
2268            }
2269        }
2270    }
2271
2272    fn validate_offer_decl(&mut self, offer: &'a fdecl::Offer, offer_type: OfferType) {
2273        match offer {
2274            fdecl::Offer::Service(o) => {
2275                let decl = DeclType::OfferService;
2276                self.validate_offer_fields(
2277                    decl,
2278                    AllowableIds::Many,
2279                    CollectionSource::Allow,
2280                    Self::service_checker,
2281                    o.source.as_ref(),
2282                    o.source_name.as_ref(),
2283                    get_source_dictionary!(o),
2284                    o.target.as_ref(),
2285                    o.target_name.as_ref(),
2286                    #[cfg(fuchsia_api_level_at_least = "HEAD")]
2287                    Some(o.dependency_type.as_ref().unwrap_or(&fdecl::DependencyType::Strong)),
2288                    #[cfg(fuchsia_api_level_less_than = "HEAD")]
2289                    Some(&fdecl::DependencyType::Strong),
2290                    o.availability.as_ref(),
2291                    offer_type,
2292                );
2293                self.validate_filtered_service_fields(
2294                    decl,
2295                    o.source_instance_filter.as_ref(),
2296                    o.renamed_instances.as_ref(),
2297                );
2298            }
2299            fdecl::Offer::Protocol(o) => {
2300                let decl = DeclType::OfferProtocol;
2301                self.validate_offer_fields(
2302                    decl,
2303                    AllowableIds::One,
2304                    CollectionSource::Deny,
2305                    Self::protocol_checker,
2306                    o.source.as_ref(),
2307                    o.source_name.as_ref(),
2308                    get_source_dictionary!(o),
2309                    o.target.as_ref(),
2310                    o.target_name.as_ref(),
2311                    o.dependency_type.as_ref(),
2312                    o.availability.as_ref(),
2313                    offer_type,
2314                );
2315            }
2316            fdecl::Offer::Directory(o) => {
2317                let decl = DeclType::OfferDirectory;
2318                self.validate_offer_fields(
2319                    decl,
2320                    AllowableIds::One,
2321                    CollectionSource::Deny,
2322                    Self::directory_checker,
2323                    o.source.as_ref(),
2324                    o.source_name.as_ref(),
2325                    get_source_dictionary!(o),
2326                    o.target.as_ref(),
2327                    o.target_name.as_ref(),
2328                    o.dependency_type.as_ref(),
2329                    o.availability.as_ref(),
2330                    offer_type,
2331                );
2332                if let Some(subdir) = o.subdir.as_ref() {
2333                    check_relative_path(
2334                        Some(subdir),
2335                        DeclType::OfferDirectory,
2336                        "subdir",
2337                        &mut self.errors,
2338                    );
2339                }
2340            }
2341            fdecl::Offer::Storage(o) => {
2342                let decl = DeclType::OfferStorage;
2343                self.validate_storage_offer_fields(
2344                    decl,
2345                    Self::storage_checker,
2346                    o.source.as_ref(),
2347                    o.source_name.as_ref(),
2348                    o.target.as_ref(),
2349                    o.target_name.as_ref(),
2350                    o.availability.as_ref(),
2351                    offer_type,
2352                );
2353                self.add_strong_dep(
2354                    self.source_dependency_from_ref(
2355                        o.source_name.as_ref(),
2356                        None,
2357                        o.source.as_ref(),
2358                    ),
2359                    self.target_dependency_from_ref(o.target.as_ref()),
2360                );
2361            }
2362            fdecl::Offer::Runner(o) => {
2363                let decl = DeclType::OfferRunner;
2364                self.validate_offer_fields(
2365                    decl,
2366                    AllowableIds::One,
2367                    CollectionSource::Deny,
2368                    Self::runner_checker,
2369                    o.source.as_ref(),
2370                    o.source_name.as_ref(),
2371                    get_source_dictionary!(o),
2372                    o.target.as_ref(),
2373                    o.target_name.as_ref(),
2374                    Some(&fdecl::DependencyType::Strong),
2375                    Some(&fdecl::Availability::Required),
2376                    offer_type,
2377                );
2378            }
2379            fdecl::Offer::Resolver(o) => {
2380                let decl = DeclType::OfferResolver;
2381                self.validate_offer_fields(
2382                    decl,
2383                    AllowableIds::One,
2384                    CollectionSource::Deny,
2385                    Self::resolver_checker,
2386                    o.source.as_ref(),
2387                    o.source_name.as_ref(),
2388                    get_source_dictionary!(o),
2389                    o.target.as_ref(),
2390                    o.target_name.as_ref(),
2391                    Some(&fdecl::DependencyType::Strong),
2392                    Some(&fdecl::Availability::Required),
2393                    offer_type,
2394                );
2395            }
2396            fdecl::Offer::EventStream(e) => {
2397                self.validate_event_stream_offer_fields(e, offer_type);
2398            }
2399            #[cfg(fuchsia_api_level_at_least = "25")]
2400            fdecl::Offer::Dictionary(o) => {
2401                let decl = DeclType::OfferDictionary;
2402                self.validate_offer_fields(
2403                    decl,
2404                    AllowableIds::One,
2405                    CollectionSource::Deny,
2406                    Self::dictionary_checker,
2407                    o.source.as_ref(),
2408                    o.source_name.as_ref(),
2409                    get_source_dictionary!(o),
2410                    o.target.as_ref(),
2411                    o.target_name.as_ref(),
2412                    o.dependency_type.as_ref(),
2413                    o.availability.as_ref(),
2414                    offer_type,
2415                );
2416            }
2417            #[cfg(fuchsia_api_level_at_least = "HEAD")]
2418            fdecl::Offer::Config(o) => {
2419                let decl = DeclType::OfferConfig;
2420                self.validate_offer_fields(
2421                    decl,
2422                    AllowableIds::One,
2423                    CollectionSource::Deny,
2424                    Self::config_checker,
2425                    o.source.as_ref(),
2426                    o.source_name.as_ref(),
2427                    None,
2428                    o.target.as_ref(),
2429                    o.target_name.as_ref(),
2430                    Some(&fdecl::DependencyType::Strong),
2431                    o.availability.as_ref(),
2432                    offer_type,
2433                );
2434            }
2435            fdecl::OfferUnknown!() => {
2436                self.errors.push(Error::invalid_field(DeclType::Component, "offer"));
2437            }
2438        }
2439    }
2440
2441    fn validate_offer_fields(
2442        &mut self,
2443        decl: DeclType,
2444        allowable_names: AllowableIds,
2445        collection_source: CollectionSource,
2446        // This takes a callback that returns a [Container], instead of the &[Container] directly,
2447        // to avoid a borrow checker error that would occur from a simultaneous borrow on
2448        // &mut self.
2449        capability_checker: impl Fn(&Self) -> &dyn Container,
2450        source: Option<&'a fdecl::Ref>,
2451        source_name: Option<&'a String>,
2452        source_dictionary: Option<&'a String>,
2453        target: Option<&'a fdecl::Ref>,
2454        target_name: Option<&'a String>,
2455        dependency_type: Option<&'a fdecl::DependencyType>,
2456        availability: Option<&'a fdecl::Availability>,
2457        offer_type: OfferType,
2458    ) {
2459        self.validate_offer_source(decl, collection_source, source, source_dictionary, offer_type);
2460        check_route_availability(decl, availability, source, source_name, &mut self.errors);
2461        check_offer_name(source_name, decl, "source_name", offer_type, &mut self.errors);
2462        if source_dictionary.is_some() {
2463            check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
2464        }
2465        self.validate_offer_target(decl, allowable_names, target, target_name, offer_type);
2466        check_offer_name(target_name, decl, "target_name", offer_type, &mut self.errors);
2467
2468        if dependency_type.is_none() {
2469            self.errors.push(Error::missing_field(decl, "dependency_type"));
2470        } else if dependency_type == Some(&fdecl::DependencyType::Strong) {
2471            self.add_strong_dep(
2472                self.source_dependency_from_ref(source_name, source_dictionary, source),
2473                self.target_dependency_from_ref(target),
2474            );
2475
2476            // If `target` is a collection, we should add a dependency edge to all dynamic
2477            // instances in this collection.
2478            if let Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: collection })) =
2479                target.as_ref()
2480            {
2481                for name in
2482                    Self::dynamic_children_in_collection(&self.dynamic_children, &collection)
2483                {
2484                    self.add_strong_dep(
2485                        self.source_dependency_from_ref(source_name, source_dictionary, source),
2486                        Some(DependencyNode::Child(name, Some(&collection))),
2487                    );
2488                }
2489            }
2490        }
2491
2492        self.validate_route_from_self(
2493            decl,
2494            source,
2495            source_name,
2496            source_dictionary,
2497            capability_checker,
2498        );
2499    }
2500
2501    fn validate_offer_source(
2502        &mut self,
2503        decl: DeclType,
2504        collection_source: CollectionSource,
2505        source: Option<&'a fdecl::Ref>,
2506        source_dictionary: Option<&'a String>,
2507        offer_type: OfferType,
2508    ) {
2509        match (source, source_dictionary) {
2510            // These sources support source_dictionary.
2511            (Some(fdecl::Ref::Parent(_)), _) => {}
2512            (Some(fdecl::Ref::Self_(_)), _) => {}
2513            (Some(fdecl::Ref::Child(child)), _) => {
2514                self.validate_child_ref(decl, "source", &child, offer_type);
2515            }
2516            // These sources don't.
2517            (Some(fdecl::Ref::VoidType(_)), None) => {}
2518            (Some(fdecl::Ref::Framework(_)), None) => {}
2519            (Some(fdecl::Ref::Capability(c)), None) => {
2520                self.validate_source_capability(c, decl, "source");
2521            }
2522            (Some(fdecl::Ref::Collection(c)), None)
2523                if collection_source == CollectionSource::Allow =>
2524            {
2525                self.validate_source_collection(c, decl);
2526            }
2527            // `source` is required.
2528            (None, _) => self.errors.push(Error::missing_field(decl, "source")),
2529            // Any combination that was not recognized above must be invalid.
2530            (_, _) => self.errors.push(Error::invalid_field(decl, "source")),
2531        }
2532    }
2533
2534    fn validate_storage_offer_fields(
2535        &mut self,
2536        decl: DeclType,
2537        // This takes a callback that returns a [Container], instead of the &[Container] directly,
2538        // to avoid a borrow checker error that would occur from a simultaneous borrow on
2539        // &mut self.
2540        capability_checker: impl Fn(&Self) -> &dyn Container,
2541        source: Option<&'a fdecl::Ref>,
2542        source_name: Option<&'a String>,
2543        target: Option<&'a fdecl::Ref>,
2544        target_name: Option<&'a String>,
2545        availability: Option<&fdecl::Availability>,
2546        offer_type: OfferType,
2547    ) {
2548        match source {
2549            Some(fdecl::Ref::Parent(_) | fdecl::Ref::VoidType(_) | fdecl::Ref::Self_(_)) => {}
2550            Some(_) => {
2551                self.errors.push(Error::invalid_field(decl, "source"));
2552            }
2553            None => {
2554                self.errors.push(Error::missing_field(decl, "source"));
2555            }
2556        }
2557        check_route_availability(decl, availability, source, source_name, &mut self.errors);
2558        check_offer_name(source_name, decl, "source_name", offer_type, &mut self.errors);
2559        self.validate_offer_target(decl, AllowableIds::One, target, target_name, offer_type);
2560        check_offer_name(target_name, decl, "target_name", offer_type, &mut self.errors);
2561
2562        if let (Some(fdecl::Ref::Self_(_)), Some(name)) = (source, source_name) {
2563            if !(capability_checker)(self).contains(name) {
2564                self.errors.push(Error::invalid_capability(decl, "source", name));
2565            }
2566        }
2567    }
2568
2569    fn validate_event_stream_offer_fields(
2570        &mut self,
2571        event_stream: &'a fdecl::OfferEventStream,
2572        offer_type: OfferType,
2573    ) {
2574        let decl = DeclType::OfferEventStream;
2575        check_name(event_stream.source_name.as_ref(), decl, "source_name", &mut self.errors);
2576        if event_stream.target == Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})) {
2577            // Expose to framework from framework is never valid.
2578            self.errors.push(Error::invalid_field(DeclType::OfferEventStream, "target"));
2579        }
2580        if let Some(scope) = &event_stream.scope {
2581            if scope.is_empty() {
2582                self.errors.push(Error::invalid_field(decl, "scope"));
2583            }
2584            for value in scope {
2585                match value {
2586                    fdecl::Ref::Child(child) => {
2587                        self.validate_child_ref(
2588                            DeclType::OfferEventStream,
2589                            "scope",
2590                            &child,
2591                            offer_type,
2592                        );
2593                    }
2594                    fdecl::Ref::Collection(collection) => {
2595                        self.validate_collection_ref(
2596                            DeclType::OfferEventStream,
2597                            "scope",
2598                            &collection,
2599                        );
2600                    }
2601                    _ => {
2602                        self.errors.push(Error::invalid_field(DeclType::OfferEventStream, "scope"));
2603                    }
2604                }
2605            }
2606        }
2607        // Only parent, framework, child, and void are valid.
2608        match event_stream.source {
2609            Some(
2610                fdecl::Ref::Parent(_)
2611                | fdecl::Ref::Framework(_)
2612                | fdecl::Ref::Child(_)
2613                | fdecl::Ref::VoidType(_),
2614            ) => {}
2615            Some(_) => {
2616                self.errors.push(Error::invalid_field(decl, "source"));
2617            }
2618            None => {
2619                self.errors.push(Error::missing_field(decl, "source"));
2620            }
2621        };
2622
2623        check_route_availability(
2624            decl,
2625            event_stream.availability.as_ref(),
2626            event_stream.source.as_ref(),
2627            event_stream.source_name.as_ref(),
2628            &mut self.errors,
2629        );
2630
2631        self.validate_offer_target(
2632            decl,
2633            AllowableIds::One,
2634            event_stream.target.as_ref(),
2635            event_stream.target_name.as_ref(),
2636            offer_type,
2637        );
2638        check_name(event_stream.target_name.as_ref(), decl, "target_name", &mut self.errors);
2639    }
2640
2641    /// Check a `ChildRef` contains a valid child that exists.
2642    fn validate_child_ref(
2643        &mut self,
2644        decl: DeclType,
2645        field_name: &str,
2646        child: &fdecl::ChildRef,
2647        offer_type: OfferType,
2648    ) -> bool {
2649        if offer_type == OfferType::Dynamic && child.collection.is_some() {
2650            return self.validate_dynamic_child_ref(decl, field_name, child);
2651        }
2652        // Ensure the name is valid, and the reference refers to a static child.
2653        //
2654        // We attempt to list all errors if possible.
2655        let mut valid = true;
2656        if !check_name(
2657            Some(&child.name),
2658            decl,
2659            &format!("{}.child.name", field_name),
2660            &mut self.errors,
2661        ) {
2662            valid = false;
2663        }
2664        if child.collection.is_some() {
2665            self.errors
2666                .push(Error::extraneous_field(decl, format!("{}.child.collection", field_name)));
2667            valid = false;
2668        }
2669        if !valid {
2670            return false;
2671        }
2672
2673        // Ensure the child exists.
2674        let name: &str = &child.name;
2675        if !self.all_children.contains_key(name) {
2676            self.errors.push(Error::invalid_child(decl, field_name, name));
2677            return false;
2678        }
2679
2680        true
2681    }
2682
2683    /// Check a `ChildRef` contains a valid dynamic child.
2684    ///
2685    /// The manifest we're validating doesn't contain dynamic children so we can't check if the dynamic
2686    /// child actually exists, but we can confirm things like the name is valid.
2687    fn validate_dynamic_child_ref(
2688        &mut self,
2689        decl: DeclType,
2690        field_name: &str,
2691        child: &fdecl::ChildRef,
2692    ) -> bool {
2693        // Ensure the name is valid.
2694        //
2695        // We attempt to list all errors if possible.
2696        let mut valid = true;
2697        if !check_dynamic_name(
2698            Some(&child.name),
2699            decl,
2700            &format!("{}.child.name", field_name),
2701            &mut self.errors,
2702        ) {
2703            valid = false;
2704        }
2705        if !check_name(
2706            child.collection.as_ref(),
2707            decl,
2708            &format!("{}.child.collection", field_name),
2709            &mut self.errors,
2710        ) {
2711            valid = false;
2712        }
2713        valid
2714    }
2715
2716    /// Check a `CollectionRef` is valid and refers to an existing collection.
2717    fn validate_collection_ref(
2718        &mut self,
2719        decl: DeclType,
2720        field_name: &str,
2721        collection: &fdecl::CollectionRef,
2722    ) -> bool {
2723        // Ensure the name is valid.
2724        if !check_name(
2725            Some(&collection.name),
2726            decl,
2727            &format!("{}.collection.name", field_name),
2728            &mut self.errors,
2729        ) {
2730            return false;
2731        }
2732
2733        // Ensure the collection exists.
2734        if !self.all_collections.contains(&collection.name as &str) {
2735            self.errors.push(Error::invalid_collection(decl, field_name, &collection.name as &str));
2736            return false;
2737        }
2738
2739        true
2740    }
2741
2742    fn validate_offer_target(
2743        &mut self,
2744        decl: DeclType,
2745        allowable_names: AllowableIds,
2746        target: Option<&'a fdecl::Ref>,
2747        target_name: Option<&'a String>,
2748        offer_type: OfferType,
2749    ) {
2750        match target {
2751            Some(fdecl::Ref::Child(c)) => {
2752                self.validate_target_child(decl, allowable_names, c, target_name, offer_type);
2753            }
2754            Some(fdecl::Ref::Collection(c)) => {
2755                self.validate_target_collection(decl, allowable_names, c, target_name);
2756            }
2757            Some(fdecl::Ref::Capability(c)) => {
2758                // Only offers to dictionary capabilities are valid.
2759                #[cfg(fuchsia_api_level_at_least = "25")]
2760                if let Some(d) = self.all_dictionaries.get(&c.name.as_str()) {
2761                    if d.source_path.is_some() {
2762                        // If `source_path` is present that means this is an offer into a
2763                        // dynamic dictionary, which is not allowed.
2764                        self.errors.push(Error::invalid_field(decl, "target"));
2765                    }
2766                } else {
2767                    self.errors.push(Error::invalid_field(decl, "target"));
2768                }
2769                #[cfg(not(fuchsia_api_level_at_least = "25"))]
2770                {
2771                    let _ = c;
2772                    self.errors.push(Error::invalid_field(decl, "target"));
2773                }
2774            }
2775            Some(_) => {
2776                self.errors.push(Error::invalid_field(decl, "target"));
2777            }
2778            None => {
2779                self.errors.push(Error::missing_field(decl, "target"));
2780            }
2781        }
2782    }
2783
2784    fn validate_target_child(
2785        &mut self,
2786        decl: DeclType,
2787        allowable_names: AllowableIds,
2788        child: &'a fdecl::ChildRef,
2789        target_name: Option<&'a String>,
2790        offer_type: OfferType,
2791    ) {
2792        if !self.validate_child_ref(decl, "target", child, offer_type) {
2793            return;
2794        }
2795        if let Some(target_name) = target_name {
2796            let names_for_target = self
2797                .target_ids
2798                .entry(TargetId::Component(
2799                    &child.name,
2800                    child.collection.as_ref().map(|s| s.as_str()),
2801                ))
2802                .or_insert(HashMap::new());
2803            if let Some(prev_state) = names_for_target.insert(target_name, allowable_names) {
2804                if prev_state == AllowableIds::One || prev_state != allowable_names {
2805                    self.errors.push(Error::duplicate_field(
2806                        decl,
2807                        "target_name",
2808                        target_name as &str,
2809                    ));
2810                }
2811            }
2812            if let Some(collection) = child.collection.as_ref() {
2813                if let Some(names_for_target) =
2814                    self.target_ids.get(&TargetId::Collection(&collection))
2815                {
2816                    if names_for_target.contains_key(&target_name.as_str()) {
2817                        // This dynamic offer conflicts with a static offer to the same collection.
2818                        self.errors.push(Error::duplicate_field(
2819                            decl,
2820                            "target_name",
2821                            target_name as &str,
2822                        ));
2823                    }
2824                }
2825            }
2826        }
2827    }
2828
2829    fn validate_target_collection(
2830        &mut self,
2831        decl: DeclType,
2832        allowable_names: AllowableIds,
2833        collection: &'a fdecl::CollectionRef,
2834        target_name: Option<&'a String>,
2835    ) {
2836        if !self.validate_collection_ref(decl, "target", &collection) {
2837            return;
2838        }
2839        if let Some(target_name) = target_name {
2840            let names_for_target = self
2841                .target_ids
2842                .entry(TargetId::Collection(&collection.name))
2843                .or_insert(HashMap::new());
2844            if let Some(prev_state) = names_for_target.insert(target_name, allowable_names) {
2845                if prev_state == AllowableIds::One || prev_state != allowable_names {
2846                    self.errors.push(Error::duplicate_field(
2847                        decl,
2848                        "target_name",
2849                        target_name as &str,
2850                    ));
2851                }
2852            }
2853        }
2854    }
2855
2856    fn validate_route_from_self(
2857        &mut self,
2858        decl: DeclType,
2859        source: Option<&fdecl::Ref>,
2860        source_name: Option<&String>,
2861        source_dictionary: Option<&String>,
2862        capability_checker: impl Fn(&Self) -> &dyn Container,
2863    ) {
2864        let (Some(fdecl::Ref::Self_(_)), Some(name)) = (source, source_name) else {
2865            return;
2866        };
2867        match source_dictionary {
2868            Some(source_dictionary) => {
2869                #[cfg(fuchsia_api_level_at_least = "25")]
2870                {
2871                    use cm_types::IterablePath;
2872                    if let Ok(path) = cm_types::RelativePath::new(source_dictionary) {
2873                        if let Some(first_segment) = path.iter_segments().next().map(|s| s.as_str())
2874                        {
2875                            if !self.all_dictionaries.contains_key(first_segment) {
2876                                self.errors.push(Error::invalid_capability(
2877                                    decl,
2878                                    "source",
2879                                    first_segment,
2880                                ));
2881                            }
2882                        }
2883                    }
2884                }
2885                #[cfg(not(fuchsia_api_level_at_least = "25"))]
2886                let _ = source_dictionary;
2887            }
2888            None => {
2889                if !(capability_checker)(self).contains(name) {
2890                    self.errors.push(Error::invalid_capability(decl, "source", name));
2891                }
2892            }
2893        }
2894    }
2895
2896    fn source_dependency_from_ref(
2897        &self,
2898        source_name: Option<&'a String>,
2899        source_dictionary: Option<&'a String>,
2900        ref_: Option<&'a fdecl::Ref>,
2901    ) -> Option<DependencyNode<'a>> {
2902        if ref_.is_none() {
2903            return None;
2904        }
2905        match ref_.unwrap() {
2906            fdecl::Ref::Child(fdecl::ChildRef { name, collection }) => {
2907                Some(DependencyNode::Child(name.as_str(), collection.as_ref().map(|s| s.as_str())))
2908            }
2909            fdecl::Ref::Collection(fdecl::CollectionRef { name, .. }) => {
2910                Some(DependencyNode::Collection(name.as_str()))
2911            }
2912            fdecl::Ref::Capability(fdecl::CapabilityRef { name, .. }) => {
2913                Some(DependencyNode::Capability(name.as_str()))
2914            }
2915            fdecl::Ref::Self_(_) => {
2916                #[cfg(fuchsia_api_level_at_least = "25")]
2917                if let Some(source_dictionary) = source_dictionary {
2918                    let root_dict = source_dictionary.split('/').next().unwrap();
2919                    if self.all_dictionaries.contains_key(root_dict) {
2920                        Some(DependencyNode::Capability(root_dict))
2921                    } else {
2922                        Some(DependencyNode::Self_)
2923                    }
2924                } else if let Some(source_name) = source_name {
2925                    if self.all_storages.contains_key(source_name.as_str())
2926                        || self.all_dictionaries.contains_key(source_name.as_str())
2927                    {
2928                        Some(DependencyNode::Capability(source_name))
2929                    } else {
2930                        Some(DependencyNode::Self_)
2931                    }
2932                } else {
2933                    Some(DependencyNode::Self_)
2934                }
2935                #[cfg(not(fuchsia_api_level_at_least = "25"))]
2936                {
2937                    let _ = source_dictionary;
2938                    if let Some(source_name) = source_name {
2939                        if self.all_storages.contains_key(source_name.as_str()) {
2940                            Some(DependencyNode::Capability(source_name))
2941                        } else {
2942                            Some(DependencyNode::Self_)
2943                        }
2944                    } else {
2945                        Some(DependencyNode::Self_)
2946                    }
2947                }
2948            }
2949            fdecl::Ref::Parent(_) => {
2950                // We don't care about dependency cycles with the parent, as any potential issues
2951                // with that are resolved by cycle detection in the parent's manifest.
2952                None
2953            }
2954            fdecl::Ref::Framework(_) => {
2955                // We don't care about dependency cycles with the framework, as the framework
2956                // always outlives the component.
2957                None
2958            }
2959            fdecl::Ref::Debug(_) => {
2960                // We don't care about dependency cycles with any debug capabilities from the
2961                // environment, as those are put there by our parent, and any potential cycles with
2962                // our parent are handled by cycle detection in the parent's manifest.
2963                None
2964            }
2965            fdecl::Ref::VoidType(_) => None,
2966            fdecl::RefUnknown!() => {
2967                // We were unable to understand this FIDL value
2968                None
2969            }
2970        }
2971    }
2972
2973    fn target_dependency_from_ref(
2974        &self,
2975        ref_: Option<&'a fdecl::Ref>,
2976    ) -> Option<DependencyNode<'a>> {
2977        self.source_dependency_from_ref(None, None, ref_)
2978    }
2979
2980    fn dynamic_children_in_collection(
2981        dynamic_children: &Vec<(&'a str, &'a str)>,
2982        collection: &'a str,
2983    ) -> Vec<&'a str> {
2984        dynamic_children
2985            .iter()
2986            .filter_map(|(n, c)| if *c == collection { Some(*n) } else { None })
2987            .collect()
2988    }
2989
2990    // The following functions can be used to convert a type-specific collection of capabilities
2991    // into [Container].
2992    fn service_checker(&self) -> &dyn Container {
2993        &self.all_services
2994    }
2995    fn protocol_checker(&self) -> &dyn Container {
2996        &self.all_protocols
2997    }
2998    fn directory_checker(&self) -> &dyn Container {
2999        &self.all_directories
3000    }
3001    fn runner_checker(&self) -> &dyn Container {
3002        &self.all_runners
3003    }
3004    fn resolver_checker(&self) -> &dyn Container {
3005        &self.all_resolvers
3006    }
3007
3008    #[cfg(fuchsia_api_level_at_least = "25")]
3009    fn dictionary_checker(&self) -> &dyn Container {
3010        &self.all_dictionaries
3011    }
3012
3013    #[cfg(fuchsia_api_level_at_least = "HEAD")]
3014    fn config_checker(&self) -> &dyn Container {
3015        &self.all_configs
3016    }
3017    fn storage_checker(&self) -> &dyn Container {
3018        &self.all_storages
3019    }
3020    fn event_stream_checker(&self) -> &dyn Container {
3021        // Components can't define their own event streams. If someone tries to route an event
3022        // stream from Self it should generate some other error. So just return `true` to bypass
3023        // the logic.
3024        struct AlwaysTrueContainer {}
3025        impl Container for AlwaysTrueContainer {
3026            fn contains(&self, _key: &str) -> bool {
3027                true
3028            }
3029        }
3030        static CONTAINER: AlwaysTrueContainer = AlwaysTrueContainer {};
3031        &CONTAINER
3032    }
3033}
3034
3035#[cfg(test)]
3036mod tests {
3037    use super::*;
3038    use cm_types::MAX_LONG_NAME_LENGTH;
3039    use test_case::test_case;
3040    use {
3041        fidl_fuchsia_component_decl as fdecl, fidl_fuchsia_data as fdata, fidl_fuchsia_io as fio,
3042    };
3043
3044    macro_rules! test_validate {
3045        (
3046            $(
3047                $test_name:ident => {
3048                    input = $input:expr,
3049                    result = $result:expr,
3050                },
3051            )+
3052        ) => {
3053            $(
3054                #[test]
3055                fn $test_name() {
3056                    validate_test($input, $result);
3057                }
3058            )+
3059        }
3060    }
3061
3062    macro_rules! test_validate_any_result {
3063        (
3064            $(
3065                $test_name:ident => {
3066                    input = $input:expr,
3067                    results = $results:expr,
3068                },
3069            )+
3070        ) => {
3071            $(
3072                #[test]
3073                fn $test_name() {
3074                    validate_test_any_result($input, $results);
3075                }
3076            )+
3077        }
3078    }
3079
3080    macro_rules! test_validate_values_data {
3081        (
3082            $(
3083                $test_name:ident => {
3084                    input = $input:expr,
3085                    result = $result:expr,
3086                },
3087            )+
3088        ) => {
3089            $(
3090                #[test]
3091                fn $test_name() {
3092                    validate_values_data_test($input, $result);
3093                }
3094            )+
3095        }
3096    }
3097
3098    macro_rules! test_validate_capabilities {
3099        (
3100            $(
3101                $test_name:ident => {
3102                    input = $input:expr,
3103                    as_builtin = $as_builtin:expr,
3104                    result = $result:expr,
3105                },
3106            )+
3107        ) => {
3108            $(
3109                #[test]
3110                fn $test_name() {
3111                    validate_capabilities_test($input, $as_builtin, $result);
3112                }
3113            )+
3114        }
3115    }
3116
3117    macro_rules! test_dependency {
3118        (
3119            $(
3120                ($test_name:ident) => {
3121                    ty = $ty:expr,
3122                    offer_decl = $offer_decl:expr,
3123                },
3124            )+
3125        ) => {
3126            $(
3127                #[test]
3128                fn $test_name() {
3129                    let mut decl = new_component_decl();
3130                    let dependencies = vec![
3131                        ("a", "b"),
3132                        ("b", "a"),
3133                    ];
3134                    let offers = dependencies.into_iter().map(|(from,to)| {
3135                        let mut offer_decl = $offer_decl;
3136                        offer_decl.source = Some(fdecl::Ref::Child(
3137                           fdecl::ChildRef { name: from.to_string(), collection: None },
3138                        ));
3139                        offer_decl.target = Some(fdecl::Ref::Child(
3140                           fdecl::ChildRef { name: to.to_string(), collection: None },
3141                        ));
3142                        $ty(offer_decl)
3143                    }).collect();
3144                    let children = ["a", "b"].iter().map(|name| {
3145                        fdecl::Child {
3146                            name: Some(name.to_string()),
3147                            url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
3148                            startup: Some(fdecl::StartupMode::Lazy),
3149                            on_terminate: None,
3150                            environment: None,
3151                            ..Default::default()
3152                        }
3153                    }).collect();
3154                    decl.offers = Some(offers);
3155                    decl.children = Some(children);
3156                    let result = Err(ErrorList::new(vec![
3157                        Error::dependency_cycle(
3158                            directed_graph::Error::CyclesDetected([vec!["child a", "child b", "child a"]].iter().cloned().collect()).format_cycle()),
3159                    ]));
3160                    validate_test(decl, result);
3161                }
3162            )+
3163        }
3164    }
3165
3166    macro_rules! test_weak_dependency {
3167        (
3168            $(
3169                ($test_name:ident) => {
3170                    ty = $ty:expr,
3171                    offer_decl = $offer_decl:expr,
3172                },
3173            )+
3174        ) => {
3175            $(
3176                #[test_case(fdecl::DependencyType::Weak)]
3177                fn $test_name(weak_dep: fdecl::DependencyType) {
3178                    let mut decl = new_component_decl();
3179                    let offers = vec![
3180                        {
3181                            let mut offer_decl = $offer_decl;
3182                            offer_decl.source = Some(fdecl::Ref::Child(
3183                               fdecl::ChildRef { name: "a".to_string(), collection: None },
3184                            ));
3185                            offer_decl.target = Some(fdecl::Ref::Child(
3186                               fdecl::ChildRef { name: "b".to_string(), collection: None },
3187                            ));
3188                            offer_decl.dependency_type = Some(fdecl::DependencyType::Strong);
3189                            $ty(offer_decl)
3190                        },
3191                        {
3192                            let mut offer_decl = $offer_decl;
3193                            offer_decl.source = Some(fdecl::Ref::Child(
3194                               fdecl::ChildRef { name: "b".to_string(), collection: None },
3195                            ));
3196                            offer_decl.target = Some(fdecl::Ref::Child(
3197                               fdecl::ChildRef { name: "a".to_string(), collection: None },
3198                            ));
3199                            offer_decl.dependency_type = Some(weak_dep);
3200                            $ty(offer_decl)
3201                        },
3202                    ];
3203                    let children = ["a", "b"].iter().map(|name| {
3204                        fdecl::Child {
3205                            name: Some(name.to_string()),
3206                            url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
3207                            startup: Some(fdecl::StartupMode::Lazy),
3208                            on_terminate: None,
3209                            environment: None,
3210                            ..Default::default()
3211                        }
3212                    }).collect();
3213                    decl.offers = Some(offers);
3214                    decl.children = Some(children);
3215                    let result = Ok(());
3216                    validate_test(decl, result);
3217                }
3218            )+
3219        }
3220    }
3221
3222    #[track_caller]
3223    fn validate_test(input: fdecl::Component, expected_res: Result<(), ErrorList>) {
3224        let res = validate(&input);
3225        assert_eq!(res, expected_res);
3226    }
3227
3228    #[track_caller]
3229    fn validate_test_any_result(input: fdecl::Component, expected_res: Vec<Result<(), ErrorList>>) {
3230        let res = format!("{:?}", validate(&input));
3231        let expected_res_debug = format!("{:?}", expected_res);
3232
3233        let matched_exp =
3234            expected_res.into_iter().find(|expected| res == format!("{:?}", expected));
3235
3236        assert!(
3237            matched_exp.is_some(),
3238            "assertion failed: Expected one of:\n{:?}\nActual:\n{:?}",
3239            expected_res_debug,
3240            res
3241        );
3242    }
3243
3244    #[track_caller]
3245    fn validate_values_data_test(
3246        input: fdecl::ConfigValuesData,
3247        expected_res: Result<(), ErrorList>,
3248    ) {
3249        let res = validate_values_data(&input);
3250        assert_eq!(res, expected_res);
3251    }
3252
3253    #[track_caller]
3254    fn validate_capabilities_test(
3255        input: Vec<fdecl::Capability>,
3256        as_builtin: bool,
3257        expected_res: Result<(), ErrorList>,
3258    ) {
3259        let res = validate_capabilities(&input, as_builtin);
3260        assert_eq!(res, expected_res);
3261    }
3262
3263    fn new_component_decl() -> fdecl::Component {
3264        fdecl::Component {
3265            program: None,
3266            uses: None,
3267            exposes: None,
3268            offers: None,
3269            facets: None,
3270            capabilities: None,
3271            children: None,
3272            collections: None,
3273            environments: None,
3274            ..Default::default()
3275        }
3276    }
3277
3278    test_validate_any_result! {
3279        test_validate_use_disallows_nested_dirs => {
3280            input = {
3281                let mut decl = new_component_decl();
3282                decl.uses = Some(vec![
3283                    fdecl::Use::Directory(fdecl::UseDirectory {
3284                        dependency_type: Some(fdecl::DependencyType::Strong),
3285                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3286                        source_name: Some("abc".to_string()),
3287                        target_path: Some("/foo/bar".to_string()),
3288                        rights: Some(fio::Operations::CONNECT),
3289                        subdir: None,
3290                        ..Default::default()
3291                    }),
3292                    fdecl::Use::Directory(fdecl::UseDirectory {
3293                        dependency_type: Some(fdecl::DependencyType::Strong),
3294                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3295                        source_name: Some("abc".to_string()),
3296                        target_path: Some("/foo/bar/baz".to_string()),
3297                        rights: Some(fio::Operations::CONNECT),
3298                        subdir: None,
3299                        ..Default::default()
3300                    }),
3301                ]);
3302                decl
3303            },
3304            results = vec![
3305                Err(ErrorList::new(vec![
3306                    Error::invalid_path_overlap(
3307                        DeclType::UseDirectory, "/foo/bar/baz", DeclType::UseDirectory, "/foo/bar"),
3308                ])),
3309                Err(ErrorList::new(vec![
3310                    Error::invalid_path_overlap(
3311                        DeclType::UseDirectory, "/foo/bar", DeclType::UseDirectory, "/foo/bar/baz"),
3312                ])),
3313            ],
3314        },
3315        test_validate_use_disallows_nested_dirs_storage => {
3316            input = {
3317                let mut decl = new_component_decl();
3318                decl.uses = Some(vec![
3319                    fdecl::Use::Storage(fdecl::UseStorage {
3320                        source_name: Some("abc".to_string()),
3321                        target_path: Some("/foo/bar".to_string()),
3322                        ..Default::default()
3323                    }),
3324                    fdecl::Use::Storage(fdecl::UseStorage {
3325                        source_name: Some("abc".to_string()),
3326                        target_path: Some("/foo/bar/baz".to_string()),
3327                        ..Default::default()
3328                    }),
3329                ]);
3330                decl
3331            },
3332            results = vec![
3333                Err(ErrorList::new(vec![
3334                    Error::invalid_path_overlap(
3335                        DeclType::UseStorage, "/foo/bar/baz", DeclType::UseStorage, "/foo/bar"),
3336                ])),
3337                Err(ErrorList::new(vec![
3338                    Error::invalid_path_overlap(
3339                        DeclType::UseStorage, "/foo/bar", DeclType::UseStorage, "/foo/bar/baz"),
3340                ])),
3341            ],
3342        },
3343        test_validate_use_disallows_nested_dirs_directory_and_storage => {
3344            input = {
3345                let mut decl = new_component_decl();
3346                decl.uses = Some(vec![
3347                    fdecl::Use::Directory(fdecl::UseDirectory {
3348                        dependency_type: Some(fdecl::DependencyType::Strong),
3349                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3350                        source_name: Some("abc".to_string()),
3351                        target_path: Some("/foo/bar".to_string()),
3352                        rights: Some(fio::Operations::CONNECT),
3353                        subdir: None,
3354                        ..Default::default()
3355                    }),
3356                    fdecl::Use::Storage(fdecl::UseStorage {
3357                        source_name: Some("abc".to_string()),
3358                        target_path: Some("/foo/bar/baz".to_string()),
3359                        ..Default::default()
3360                    }),
3361                ]);
3362                decl
3363            },
3364            results = vec![
3365                Err(ErrorList::new(vec![
3366                    Error::invalid_path_overlap(
3367                        DeclType::UseStorage, "/foo/bar/baz", DeclType::UseDirectory, "/foo/bar"),
3368                ])),
3369                Err(ErrorList::new(vec![
3370                    Error::invalid_path_overlap(
3371                        DeclType::UseDirectory, "/foo/bar", DeclType::UseStorage, "/foo/bar/baz"),
3372                ])),
3373            ],
3374        },
3375        test_validate_use_disallows_common_prefixes_protocol => {
3376            input = {
3377                let mut decl = new_component_decl();
3378                decl.uses = Some(vec![
3379                    fdecl::Use::Directory(fdecl::UseDirectory {
3380                        dependency_type: Some(fdecl::DependencyType::Strong),
3381                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3382                        source_name: Some("abc".to_string()),
3383                        target_path: Some("/foo/bar".to_string()),
3384                        rights: Some(fio::Operations::CONNECT),
3385                        subdir: None,
3386                        ..Default::default()
3387                    }),
3388                    fdecl::Use::Protocol(fdecl::UseProtocol {
3389                        dependency_type: Some(fdecl::DependencyType::Strong),
3390                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3391                        source_name: Some("crow".to_string()),
3392                        target_path: Some("/foo/bar/fuchsia.2".to_string()),
3393                        ..Default::default()
3394                    }),
3395                ]);
3396                decl
3397            },
3398            results = vec![
3399                Err(ErrorList::new(vec![
3400                    Error::invalid_path_overlap(
3401                        DeclType::UseProtocol, "/foo/bar/fuchsia.2", DeclType::UseDirectory, "/foo/bar"),
3402                ])),
3403                Err(ErrorList::new(vec![
3404                    Error::invalid_path_overlap(
3405                        DeclType::UseDirectory, "/foo/bar", DeclType::UseProtocol, "/foo/bar/fuchsia.2"),
3406                ])),
3407            ],
3408        },
3409        test_validate_use_disallows_common_prefixes_service => {
3410            input = {
3411                let mut decl = new_component_decl();
3412                decl.uses = Some(vec![
3413                    fdecl::Use::Directory(fdecl::UseDirectory {
3414                        dependency_type: Some(fdecl::DependencyType::Strong),
3415                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3416                        source_name: Some("abc".to_string()),
3417                        target_path: Some("/foo/bar".to_string()),
3418                        rights: Some(fio::Operations::CONNECT),
3419                        subdir: None,
3420                        ..Default::default()
3421                    }),
3422                    fdecl::Use::Service(fdecl::UseService {
3423                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3424                        source_name: Some("space".to_string()),
3425                        target_path: Some("/foo/bar/baz/fuchsia.logger.Log".to_string()),
3426                        dependency_type: Some(fdecl::DependencyType::Strong),
3427                        ..Default::default()
3428                    }),
3429                ]);
3430                decl
3431            },
3432            results = vec![
3433                Err(ErrorList::new(vec![
3434                    Error::invalid_path_overlap(
3435                        DeclType::UseService, "/foo/bar/baz/fuchsia.logger.Log", DeclType::UseDirectory, "/foo/bar"),
3436                ])),
3437                Err(ErrorList::new(vec![
3438                    Error::invalid_path_overlap(
3439                        DeclType::UseDirectory, "/foo/bar", DeclType::UseService, "/foo/bar/baz/fuchsia.logger.Log"),
3440                ])),
3441            ],
3442        },
3443        test_validate_use_disallows_pkg => {
3444            input = {
3445                let mut decl = new_component_decl();
3446                decl.uses = Some(vec![
3447                    fdecl::Use::Directory(fdecl::UseDirectory {
3448                        dependency_type: Some(fdecl::DependencyType::Strong),
3449                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3450                        source_name: Some("abc".to_string()),
3451                        target_path: Some("/pkg".to_string()),
3452                        rights: Some(fio::Operations::CONNECT),
3453                        subdir: None,
3454                        ..Default::default()
3455                    }),
3456                ]);
3457                decl
3458            },
3459            results = vec![
3460                Err(ErrorList::new(vec![
3461                    Error::pkg_path_overlap(DeclType::UseDirectory, "/pkg"),
3462                ])),
3463            ],
3464        },
3465        test_validate_use_disallows_pkg_overlap => {
3466            input = {
3467                let mut decl = new_component_decl();
3468                decl.uses = Some(vec![
3469                    fdecl::Use::Directory(fdecl::UseDirectory {
3470                        dependency_type: Some(fdecl::DependencyType::Strong),
3471                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3472                        source_name: Some("abc".to_string()),
3473                        target_path: Some("/pkg/foo".to_string()),
3474                        rights: Some(fio::Operations::CONNECT),
3475                        subdir: None,
3476                        ..Default::default()
3477                    }),
3478                ]);
3479                decl
3480            },
3481            results = vec![
3482                Err(ErrorList::new(vec![
3483                    Error::pkg_path_overlap(DeclType::UseDirectory, "/pkg/foo"),
3484                ])),
3485            ],
3486        },
3487        test_validate_use_optional_config_correct => {
3488            input = {
3489                let mut decl = new_component_decl();
3490                decl.uses = Some(vec![
3491                    fdecl::Use::Config(fdecl::UseConfiguration {
3492                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3493                        source_name: Some("abc".to_string()),
3494                        target_name: Some("foo".to_string()),
3495                        availability: Some(fdecl::Availability::Optional),
3496                        type_: Some(fdecl::ConfigType {
3497                            layout: fdecl::ConfigTypeLayout::Bool,
3498                            parameters: Some(Vec::new()),
3499                            constraints: Vec::new(),
3500                        }),
3501                        ..Default::default()
3502                    }),
3503                ]);
3504                decl.config = Some(fdecl::ConfigSchema {
3505                    fields: Some(vec![fdecl::ConfigField {
3506                        key: Some("foo".into()),
3507                        type_: Some(fdecl::ConfigType {
3508                            layout: fdecl::ConfigTypeLayout::Bool,
3509                            parameters: Some(Vec::new()),
3510                            constraints: Vec::new(),
3511                        }),
3512                        mutability: None,
3513                        ..Default::default()}]),
3514                    checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3515                    value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3516                    ..Default::default()
3517                     });
3518                decl
3519            },
3520            results = vec![Ok(())],
3521        },
3522        test_validate_use_optional_config_no_config_schema => {
3523            input = {
3524                let mut decl = new_component_decl();
3525                decl.uses = Some(vec![
3526                    fdecl::Use::Config(fdecl::UseConfiguration {
3527                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3528                        source_name: Some("abc".to_string()),
3529                        target_name: Some("foo".to_string()),
3530                        availability: Some(fdecl::Availability::Optional),
3531                        type_: Some(fdecl::ConfigType {
3532                            layout: fdecl::ConfigTypeLayout::Bool,
3533                            parameters: None,
3534                            constraints: Vec::new(),
3535                        }),
3536                        ..Default::default()
3537                    }),
3538                ]);
3539                decl
3540            },
3541            results = vec![
3542                Err(ErrorList::new(vec![
3543                    Error::missing_field(DeclType::ConfigField, "config"),
3544                ])),
3545            ],
3546        },
3547        test_validate_use_optional_config_no_config_field => {
3548            input = {
3549                let mut decl = new_component_decl();
3550                decl.uses = Some(vec![
3551                    fdecl::Use::Config(fdecl::UseConfiguration {
3552                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3553                        source_name: Some("abc".to_string()),
3554                        target_name: Some("foo".to_string()),
3555                        availability: Some(fdecl::Availability::Optional),
3556                        type_: Some(fdecl::ConfigType {
3557                            layout: fdecl::ConfigTypeLayout::Bool,
3558                            parameters: None,
3559                            constraints: Vec::new(),
3560                        }),
3561                        ..Default::default()
3562                    }),
3563                ]);
3564                decl.config = Some(fdecl::ConfigSchema {
3565                    fields: Some(vec![]),
3566                    checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3567                    value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3568                    ..Default::default()
3569                     });
3570                decl
3571            },
3572            results = vec![
3573                Err(ErrorList::new(vec![
3574                    Error::missing_field(DeclType::ConfigField, "foo"),
3575                ])),
3576            ],
3577        },
3578        test_validate_use_optional_config_bad_type => {
3579            input = {
3580                let mut decl = new_component_decl();
3581                decl.uses = Some(vec![
3582                    fdecl::Use::Config(fdecl::UseConfiguration {
3583                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3584                        source_name: Some("abc".to_string()),
3585                        target_name: Some("foo".to_string()),
3586                        availability: Some(fdecl::Availability::Optional),
3587                        type_: Some(fdecl::ConfigType {
3588                            layout: fdecl::ConfigTypeLayout::Bool,
3589                            parameters: None,
3590                            constraints: Vec::new(),
3591                        }),
3592                        ..Default::default()
3593                    }),
3594                ]);
3595                decl.config = Some(fdecl::ConfigSchema {
3596                    fields: Some(vec![fdecl::ConfigField {
3597                        key: Some("foo".into()),
3598                        type_: Some(fdecl::ConfigType {
3599                            layout: fdecl::ConfigTypeLayout::Int16,
3600                            parameters: Some(Vec::new()),
3601                            constraints: Vec::new(),
3602                        }),
3603                        mutability: None,
3604                        ..Default::default()}]),
3605                    checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3606                    value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3607                    ..Default::default()
3608                     });
3609                decl
3610            },
3611            results = vec![
3612                Err(ErrorList::new(vec![
3613                    Error::invalid_field(DeclType::ConfigField, "foo"),
3614                ])),
3615            ],
3616        },
3617    }
3618
3619    test_validate_values_data! {
3620        test_values_data_ok => {
3621            input = fdecl::ConfigValuesData {
3622                values: Some(vec![
3623                    fdecl::ConfigValueSpec {
3624                        value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(true))),
3625                        ..Default::default()
3626                    }
3627                ]),
3628                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3629                ..Default::default()
3630            },
3631            result = Ok(()),
3632        },
3633        test_values_data_no_checksum => {
3634            input = fdecl::ConfigValuesData {
3635                values: Some(vec![]),
3636                checksum: None,
3637                ..Default::default()
3638            },
3639            result = Err(ErrorList::new(vec![
3640                Error::missing_field(DeclType::ConfigValuesData, "checksum")
3641            ])),
3642        },
3643        test_values_data_unknown_checksum => {
3644            input = fdecl::ConfigValuesData {
3645                values: Some(vec![]),
3646                checksum: Some(fdecl::ConfigChecksum::unknown_variant_for_testing()),
3647                ..Default::default()
3648            },
3649            result = Err(ErrorList::new(vec![
3650                Error::invalid_field(DeclType::ConfigValuesData, "checksum")
3651            ])),
3652        },
3653        test_values_data_no_values => {
3654            input = fdecl::ConfigValuesData {
3655                values: None,
3656                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3657                ..Default::default()
3658            },
3659            result = Err(ErrorList::new(vec![
3660                Error::missing_field(DeclType::ConfigValuesData, "values")
3661            ])),
3662        },
3663        test_values_data_no_inner_value => {
3664            input = fdecl::ConfigValuesData {
3665                values: Some(vec![
3666                    fdecl::ConfigValueSpec::default()
3667                ]),
3668                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3669                ..Default::default()
3670            },
3671            result = Err(ErrorList::new(vec![
3672                Error::missing_field(DeclType::ConfigValueSpec, "value")
3673            ])),
3674        },
3675        test_values_data_unknown_inner_value => {
3676            input = fdecl::ConfigValuesData {
3677                values: Some(vec![
3678                    fdecl::ConfigValueSpec {
3679                        value: Some(fdecl::ConfigValue::unknown_variant_for_testing()),
3680                        ..Default::default()
3681                    }
3682                ]),
3683                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3684                ..Default::default()
3685            },
3686            result = Err(ErrorList::new(vec![
3687                Error::invalid_field(DeclType::ConfigValueSpec, "value")
3688            ])),
3689        },
3690        test_values_data_unknown_single_value => {
3691            input = fdecl::ConfigValuesData {
3692                values: Some(vec![
3693                    fdecl::ConfigValueSpec {
3694                        value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::unknown_variant_for_testing())),
3695                        ..Default::default()
3696                    }
3697                ]),
3698                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3699                ..Default::default()
3700            },
3701            result = Err(ErrorList::new(vec![
3702                Error::invalid_field(DeclType::ConfigValueSpec, "value")
3703            ])),
3704        },
3705        test_values_data_unknown_list_value => {
3706            input = fdecl::ConfigValuesData {
3707                values: Some(vec![
3708                    fdecl::ConfigValueSpec {
3709                        value: Some(fdecl::ConfigValue::Vector(fdecl::ConfigVectorValue::unknown_variant_for_testing())),
3710                        ..Default::default()
3711                    }
3712                ]),
3713                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3714                ..Default::default()
3715            },
3716            result = Err(ErrorList::new(vec![
3717                Error::invalid_field(DeclType::ConfigValueSpec, "value")
3718            ])),
3719        },
3720    }
3721
3722    test_validate! {
3723        // uses
3724        test_validate_uses_empty => {
3725            input = {
3726                let mut decl = new_component_decl();
3727                decl.program = Some(fdecl::Program {
3728                    runner: Some("elf".to_string()),
3729                    info: Some(fdata::Dictionary {
3730                        entries: None,
3731                        ..Default::default()
3732                    }),
3733                    ..Default::default()
3734                });
3735                decl.uses = Some(vec![
3736                    fdecl::Use::Service(fdecl::UseService {
3737                        source: None,
3738                        source_name: None,
3739                        target_path: None,
3740                        dependency_type: None,
3741                        ..Default::default()
3742                    }),
3743                    fdecl::Use::Protocol(fdecl::UseProtocol {
3744                        dependency_type: None,
3745                        source: None,
3746                        source_name: None,
3747                        target_path: None,
3748                        ..Default::default()
3749                    }),
3750                    fdecl::Use::Directory(fdecl::UseDirectory {
3751                        dependency_type: None,
3752                        source: None,
3753                        source_name: None,
3754                        target_path: None,
3755                        rights: None,
3756                        subdir: None,
3757                        ..Default::default()
3758                    }),
3759                    fdecl::Use::Storage(fdecl::UseStorage {
3760                        source_name: None,
3761                        target_path: None,
3762                        ..Default::default()
3763                    }),
3764                    fdecl::Use::EventStream(fdecl::UseEventStream {
3765                        source_name: None,
3766                        source: None,
3767                        target_path: None,
3768                        ..Default::default()
3769                    }),
3770                    fdecl::Use::Runner(fdecl::UseRunner {
3771                        source_name: None,
3772                        source: None,
3773                        ..Default::default()
3774                    }),
3775                ]);
3776                decl
3777            },
3778            result = Err(ErrorList::new(vec![
3779                Error::missing_field(DeclType::UseService, "source"),
3780                Error::missing_field(DeclType::UseService, "source_name"),
3781                Error::missing_field(DeclType::UseService, "target_path"),
3782                Error::missing_field(DeclType::UseService, "dependency_type"),
3783                Error::missing_field(DeclType::UseProtocol, "source"),
3784                Error::missing_field(DeclType::UseProtocol, "source_name"),
3785                Error::missing_field(DeclType::UseProtocol, "target_path"),
3786                Error::missing_field(DeclType::UseProtocol, "dependency_type"),
3787                Error::missing_field(DeclType::UseDirectory, "source"),
3788                Error::missing_field(DeclType::UseDirectory, "source_name"),
3789                Error::missing_field(DeclType::UseDirectory, "target_path"),
3790                Error::missing_field(DeclType::UseDirectory, "dependency_type"),
3791                Error::missing_field(DeclType::UseDirectory, "rights"),
3792                Error::missing_field(DeclType::UseStorage, "source_name"),
3793                Error::missing_field(DeclType::UseStorage, "target_path"),
3794                Error::missing_field(DeclType::UseEventStream, "source"),
3795                Error::missing_field(DeclType::UseEventStream, "source_name"),
3796                Error::missing_field(DeclType::UseEventStream, "target_path"),
3797                Error::missing_field(DeclType::UseRunner, "source"),
3798                Error::missing_field(DeclType::UseRunner, "source_name"),
3799            ])),
3800        },
3801        test_validate_missing_program_info => {
3802            input = {
3803                let mut decl = new_component_decl();
3804                decl.program = Some(fdecl::Program {
3805                    runner: Some("runner".to_string()),
3806                    info: None,
3807                    ..Default::default()
3808                });
3809                decl
3810            },
3811            result = Err(ErrorList::new(vec![
3812                Error::missing_field(DeclType::Program, "info")
3813            ])),
3814        },
3815        test_validate_uses_invalid_identifiers => {
3816            input = {
3817                let mut decl = new_component_decl();
3818                decl.uses = Some(vec![
3819                    fdecl::Use::Service(fdecl::UseService {
3820                        source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
3821                            name: "^bad".to_string(),
3822                        })),
3823                        source_name: Some("foo/".to_string()),
3824                        target_path: Some("a/foo".to_string()),
3825                        dependency_type: Some(fdecl::DependencyType::Strong),
3826                        ..Default::default()
3827                    }),
3828                    fdecl::Use::Protocol(fdecl::UseProtocol {
3829                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3830                            name: "^bad".to_string(),
3831                            collection: None,
3832                        })),
3833                        source_name: Some("foo/".to_string()),
3834                        target_path: Some("b/foo".to_string()),
3835                        dependency_type: Some(fdecl::DependencyType::Strong),
3836                        ..Default::default()
3837                    }),
3838                    fdecl::Use::Directory(fdecl::UseDirectory {
3839                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3840                            name: "^bad".to_string(),
3841                            collection: None,
3842                        })),
3843                        source_name: Some("foo/".to_string()),
3844                        target_path: Some("c".to_string()),
3845                        rights: Some(fio::Operations::CONNECT),
3846                        subdir: Some("/foo".to_string()),
3847                        dependency_type: Some(fdecl::DependencyType::Strong),
3848                        ..Default::default()
3849                    }),
3850                    fdecl::Use::Storage(fdecl::UseStorage {
3851                        source_name: Some("foo/".to_string()),
3852                        target_path: Some("d".to_string()),
3853                        ..Default::default()
3854                    }),
3855                    fdecl::Use::EventStream(fdecl::UseEventStream {
3856                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3857                            name: "^bad".to_string(),
3858                            collection: None,
3859                        })),
3860                        source_name: Some("foo/".to_string()),
3861                        target_path: Some("e".to_string()),
3862                        ..Default::default()
3863                    }),
3864                    fdecl::Use::Runner(fdecl::UseRunner {
3865                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3866                            name: "^bad".to_string(),
3867                            collection: None,
3868                        })),
3869                        source_name: Some("foo/".to_string()),
3870                        ..Default::default()
3871                    }),
3872                ]);
3873                decl
3874            },
3875            result = Err(ErrorList::new(vec![
3876                Error::invalid_field(DeclType::UseService, "source.capability.name"),
3877                Error::invalid_field(DeclType::UseService, "source_name"),
3878                Error::invalid_field(DeclType::UseService, "target_path"),
3879                Error::invalid_field(DeclType::UseProtocol, "source.child.name"),
3880                Error::invalid_field(DeclType::UseProtocol, "source_name"),
3881                Error::invalid_field(DeclType::UseProtocol, "target_path"),
3882                Error::invalid_field(DeclType::UseDirectory, "source.child.name"),
3883                Error::invalid_field(DeclType::UseDirectory, "source_name"),
3884                Error::invalid_field(DeclType::UseDirectory, "target_path"),
3885                Error::invalid_field(DeclType::UseDirectory, "subdir"),
3886                Error::invalid_field(DeclType::UseStorage, "source_name"),
3887                Error::invalid_field(DeclType::UseStorage, "target_path"),
3888                Error::invalid_field(DeclType::UseEventStream, "source.child.name"),
3889                Error::invalid_field(DeclType::UseEventStream, "source_name"),
3890                Error::invalid_field(DeclType::UseEventStream, "target_path"),
3891                Error::invalid_field(DeclType::UseRunner, "source.child.name"),
3892                Error::invalid_field(DeclType::UseRunner, "source_name"),
3893            ])),
3894        },
3895        test_validate_uses_missing_source => {
3896            input = {
3897                fdecl::Component {
3898                    uses: Some(vec![
3899                        fdecl::Use::Protocol(fdecl::UseProtocol {
3900                            dependency_type: Some(fdecl::DependencyType::Strong),
3901                            source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
3902                                name: "this-storage-doesnt-exist".to_string(),
3903                            })),
3904                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3905                            target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3906                            ..Default::default()
3907                        })
3908                    ]),
3909                    ..new_component_decl()
3910                }
3911            },
3912            result = Err(ErrorList::new(vec![
3913                Error::invalid_capability(DeclType::UseProtocol, "source", "this-storage-doesnt-exist"),
3914            ])),
3915        },
3916        test_validate_uses_invalid_child => {
3917            input = {
3918                fdecl::Component {
3919                    uses: Some(vec![
3920                        fdecl::Use::Protocol(fdecl::UseProtocol {
3921                            dependency_type: Some(fdecl::DependencyType::Strong),
3922                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3923                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3924                            target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3925                            ..Default::default()
3926                        }),
3927                        fdecl::Use::Service(fdecl::UseService {
3928                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3929                            source_name: Some("service_name".to_string()),
3930                            target_path: Some("/svc/service_name".to_string()),
3931                            dependency_type: Some(fdecl::DependencyType::Strong),
3932                            ..Default::default()
3933                        }),
3934                        fdecl::Use::Directory(fdecl::UseDirectory {
3935                            dependency_type: Some(fdecl::DependencyType::Strong),
3936                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3937                            source_name: Some("DirectoryName".to_string()),
3938                            target_path: Some("/data/DirectoryName".to_string()),
3939                            rights: Some(fio::Operations::CONNECT),
3940                            subdir: None,
3941                            ..Default::default()
3942                        }),
3943                        fdecl::Use::Runner(fdecl::UseRunner {
3944                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3945                            source_name: Some("RunnerName".to_string()),
3946                            ..Default::default()
3947                        }),
3948                    ]),
3949                    ..new_component_decl()
3950                }
3951            },
3952            result = Err(ErrorList::new(vec![
3953                Error::invalid_child(DeclType::UseProtocol, "source", "no-such-child"),
3954                Error::invalid_child(DeclType::UseService, "source", "no-such-child"),
3955                Error::invalid_child(DeclType::UseDirectory, "source", "no-such-child"),
3956                Error::invalid_child(DeclType::UseRunner, "source", "no-such-child"),
3957            ])),
3958        },
3959        test_validate_uses_invalid_capability_from_self => {
3960            input = {
3961                let mut decl = new_component_decl();
3962                decl.uses = Some(vec![
3963                    fdecl::Use::Service(fdecl::UseService {
3964                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3965                        source_name: Some("fuchsia.some.library.SomeService".into()),
3966                        target_path: Some("/svc/foo".into()),
3967                        dependency_type: Some(fdecl::DependencyType::Strong),
3968                        ..Default::default()
3969                    }),
3970                    fdecl::Use::Protocol(fdecl::UseProtocol {
3971                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3972                        source_name: Some("fuchsia.some.library.SomeProtocol".into()),
3973                        target_path: Some("/svc/bar".into()),
3974                        dependency_type: Some(fdecl::DependencyType::Strong),
3975                        ..Default::default()
3976                    }),
3977                    fdecl::Use::Directory(fdecl::UseDirectory {
3978                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3979                        source_name: Some("dir".into()),
3980                        target_path: Some("/assets".into()),
3981                        dependency_type: Some(fdecl::DependencyType::Strong),
3982                        rights: Some(fio::Operations::CONNECT),
3983                        ..Default::default()
3984                    }),
3985                    fdecl::Use::Runner(fdecl::UseRunner {
3986                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3987                        source_name: Some("source_elf".into()),
3988                        ..Default::default()
3989                    }),
3990                    fdecl::Use::Config(fdecl::UseConfiguration {
3991                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3992                        source_name: Some("source_config".into()),
3993                        target_name: Some("config".into()),
3994                        type_: Some(fdecl::ConfigType {
3995                            layout: fdecl::ConfigTypeLayout::Bool,
3996                            parameters: Some(Vec::new()),
3997                            constraints: Vec::new(),
3998                        }),
3999                        ..Default::default()
4000                    }),
4001                    fdecl::Use::Protocol(fdecl::UseProtocol {
4002                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4003                        source_name: Some("fuchsia.some.library.SomeProtocol".into()),
4004                        source_dictionary: Some("dict/inner".into()),
4005                        target_path: Some("/svc/baz".into()),
4006                        dependency_type: Some(fdecl::DependencyType::Strong),
4007                        ..Default::default()
4008                    }),
4009                ]);
4010                decl
4011            },
4012            result = Err(ErrorList::new(vec![
4013                Error::invalid_capability(
4014                    DeclType::UseService,
4015                    "source",
4016                    "fuchsia.some.library.SomeService"),
4017                Error::invalid_capability(
4018                    DeclType::UseProtocol,
4019                    "source",
4020                    "fuchsia.some.library.SomeProtocol"),
4021                Error::invalid_capability(DeclType::UseDirectory, "source", "dir"),
4022                Error::invalid_capability(DeclType::UseRunner, "source", "source_elf"),
4023                Error::invalid_capability(DeclType::UseConfiguration, "source", "source_config"),
4024                Error::invalid_capability(DeclType::UseProtocol, "source", "dict"),
4025            ])),
4026        },
4027        test_validate_use_from_child_offer_to_child_strong_cycle => {
4028            input = {
4029                fdecl::Component {
4030                    capabilities: Some(vec![
4031                        fdecl::Capability::Service(fdecl::Service {
4032                            name: Some("a".to_string()),
4033                            source_path: Some("/a".to_string()),
4034                            ..Default::default()
4035                        })]),
4036                    uses: Some(vec![
4037                        fdecl::Use::Protocol(fdecl::UseProtocol {
4038                            dependency_type: Some(fdecl::DependencyType::Strong),
4039                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4040                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4041                            target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
4042                            ..Default::default()
4043                        }),
4044                        fdecl::Use::Service(fdecl::UseService {
4045                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4046                            source_name: Some("service_name".to_string()),
4047                            target_path: Some("/svc/service_name".to_string()),
4048                            dependency_type: Some(fdecl::DependencyType::Strong),
4049                            ..Default::default()
4050                        }),
4051                        fdecl::Use::Directory(fdecl::UseDirectory {
4052                            dependency_type: Some(fdecl::DependencyType::Strong),
4053                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4054                            source_name: Some("DirectoryName".to_string()),
4055                            target_path: Some("/data/DirectoryName".to_string()),
4056                            rights: Some(fio::Operations::CONNECT),
4057                            subdir: None,
4058                            ..Default::default()
4059                        }),
4060                    ]),
4061                    offers: Some(vec![
4062                        fdecl::Offer::Service(fdecl::OfferService {
4063                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4064                            source_name: Some("a".to_string()),
4065                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4066                            target_name: Some("a".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 -> child child -> self}}".to_string()),
4084            ])),
4085        },
4086        test_validate_use_from_child_storage_no_cycle => {
4087            input = {
4088                fdecl::Component {
4089                    capabilities: Some(vec![
4090                        fdecl::Capability::Storage(fdecl::Storage {
4091                            name: Some("cdata".to_string()),
4092                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None } )),
4093                            backing_dir: Some("minfs".to_string()),
4094                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4095                            ..Default::default()
4096                        }),
4097                        fdecl::Capability::Storage(fdecl::Storage {
4098                            name: Some("pdata".to_string()),
4099                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4100                            backing_dir: Some("minfs".to_string()),
4101                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4102                            ..Default::default()
4103                        }),
4104                    ]),
4105                    uses: Some(vec![
4106                        fdecl::Use::Protocol(fdecl::UseProtocol {
4107                            dependency_type: Some(fdecl::DependencyType::Strong),
4108                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child1".to_string(), collection: None})),
4109                            source_name: Some("a".to_string()),
4110                            target_path: Some("/svc/a".to_string()),
4111                            ..Default::default()
4112                        }),
4113                    ]),
4114                    offers: Some(vec![
4115                        fdecl::Offer::Storage(fdecl::OfferStorage {
4116                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4117                            source_name: Some("cdata".to_string()),
4118                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4119                            target_name: Some("cdata".to_string()),
4120                            ..Default::default()
4121                        }),
4122                        fdecl::Offer::Storage(fdecl::OfferStorage {
4123                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4124                            source_name: Some("pdata".to_string()),
4125                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4126                            target_name: Some("pdata".to_string()),
4127                            ..Default::default()
4128                        }),
4129                    ]),
4130                    children: Some(vec![
4131                        fdecl::Child {
4132                            name: Some("child1".to_string()),
4133                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4134                            startup: Some(fdecl::StartupMode::Lazy),
4135                            on_terminate: None,
4136                            ..Default::default()
4137                        },
4138                        fdecl::Child {
4139                            name: Some("child2".to_string()),
4140                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4141                            startup: Some(fdecl::StartupMode::Lazy),
4142                            on_terminate: None,
4143                            ..Default::default()
4144                        }
4145                    ]),
4146                    ..new_component_decl()
4147                }
4148            },
4149            result = Ok(()),
4150        },
4151        test_validate_use_from_child_storage_cycle => {
4152            input = {
4153                fdecl::Component {
4154                    capabilities: Some(vec![
4155                        fdecl::Capability::Storage(fdecl::Storage {
4156                            name: Some("data".to_string()),
4157                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4158                            backing_dir: Some("minfs".to_string()),
4159                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4160                            ..Default::default()
4161                        }),
4162                    ]),
4163                    uses: Some(vec![
4164                        fdecl::Use::Protocol(fdecl::UseProtocol {
4165                            dependency_type: Some(fdecl::DependencyType::Strong),
4166                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4167                            source_name: Some("a".to_string()),
4168                            target_path: Some("/svc/a".to_string()),
4169                            ..Default::default()
4170                        }),
4171                    ]),
4172                    offers: Some(vec![
4173                        fdecl::Offer::Storage(fdecl::OfferStorage {
4174                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4175                            source_name: Some("data".to_string()),
4176                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4177                            target_name: Some("data".to_string()),
4178                            ..Default::default()
4179                        }),
4180                    ]),
4181                    children: Some(vec![
4182                        fdecl::Child {
4183                            name: Some("child".to_string()),
4184                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4185                            startup: Some(fdecl::StartupMode::Lazy),
4186                            on_terminate: None,
4187                            ..Default::default()
4188                        },
4189                    ]),
4190                    ..new_component_decl()
4191                }
4192            },
4193            result = Err(ErrorList::new(vec![
4194                Error::dependency_cycle("{{self -> capability data -> child child -> self}}".to_string()),
4195            ])),
4196        },
4197        test_validate_storage_strong_cycle_between_children => {
4198            input = {
4199                fdecl::Component {
4200                    capabilities: Some(vec![
4201                        fdecl::Capability::Storage(fdecl::Storage {
4202                            name: Some("data".to_string()),
4203                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None } )),
4204                            backing_dir: Some("minfs".to_string()),
4205                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4206                            ..Default::default()
4207                        })
4208                    ]),
4209                    offers: Some(vec![
4210                        fdecl::Offer::Storage(fdecl::OfferStorage {
4211                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4212                            source_name: Some("data".to_string()),
4213                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4214                            target_name: Some("data".to_string()),
4215                            ..Default::default()
4216                        }),
4217                        fdecl::Offer::Service(fdecl::OfferService {
4218                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4219                            source_name: Some("a".to_string()),
4220                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4221                            target_name: Some("a".to_string()),
4222                            ..Default::default()
4223                        }),
4224                    ]),
4225                    children: Some(vec![
4226                        fdecl::Child {
4227                            name: Some("child1".to_string()),
4228                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4229                            startup: Some(fdecl::StartupMode::Lazy),
4230                            on_terminate: None,
4231                            ..Default::default()
4232                        },
4233                        fdecl::Child {
4234                            name: Some("child2".to_string()),
4235                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4236                            startup: Some(fdecl::StartupMode::Lazy),
4237                            on_terminate: None,
4238                            ..Default::default()
4239                        }
4240                    ]),
4241                    ..new_component_decl()
4242                }
4243            },
4244            result = Err(ErrorList::new(vec![
4245                Error::dependency_cycle("{{child child1 -> capability data -> child child2 -> child child1}}".to_string()),
4246            ])),
4247        },
4248        test_validate_strong_cycle_between_children_through_environment_debug => {
4249            input = {
4250                fdecl::Component {
4251                    environments: Some(vec![
4252                        fdecl::Environment {
4253                            name: Some("env".to_string()),
4254                            extends: Some(fdecl::EnvironmentExtends::Realm),
4255                            debug_capabilities: Some(vec![
4256                                fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
4257                                    source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4258                                    source_name: Some("fuchsia.foo.Bar".to_string()),
4259                                    target_name: Some("fuchsia.foo.Bar".to_string()),
4260                                    ..Default::default()
4261                                }),
4262                            ]),
4263                            ..Default::default()
4264                        },
4265                    ]),
4266                    offers: Some(vec![
4267                        fdecl::Offer::Service(fdecl::OfferService {
4268                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4269                            source_name: Some("a".to_string()),
4270                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4271                            target_name: Some("a".to_string()),
4272                            ..Default::default()
4273                        }),
4274                    ]),
4275                    children: Some(vec![
4276                        fdecl::Child {
4277                            name: Some("child1".to_string()),
4278                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4279                            startup: Some(fdecl::StartupMode::Lazy),
4280                            on_terminate: None,
4281                            ..Default::default()
4282                        },
4283                        fdecl::Child {
4284                            name: Some("child2".to_string()),
4285                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4286                            startup: Some(fdecl::StartupMode::Lazy),
4287                            environment: Some("env".to_string()),
4288                            on_terminate: None,
4289                            ..Default::default()
4290                        }
4291                    ]),
4292                    ..new_component_decl()
4293                }
4294            },
4295            result = Err(ErrorList::new(vec![
4296                Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}".to_string()),
4297            ])),
4298        },
4299        test_validate_strong_cycle_between_children_through_environment_runner => {
4300            input = {
4301                fdecl::Component {
4302                    environments: Some(vec![
4303                        fdecl::Environment {
4304                            name: Some("env".to_string()),
4305                            extends: Some(fdecl::EnvironmentExtends::Realm),
4306                            runners: Some(vec![
4307                                fdecl::RunnerRegistration {
4308                                    source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4309                                    source_name: Some("coff".to_string()),
4310                                    target_name: Some("coff".to_string()),
4311                                    ..Default::default()
4312                                }
4313                            ]),
4314                            ..Default::default()
4315                        },
4316                    ]),
4317                    offers: Some(vec![
4318                        fdecl::Offer::Service(fdecl::OfferService {
4319                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4320                            source_name: Some("a".to_string()),
4321                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4322                            target_name: Some("a".to_string()),
4323                            ..Default::default()
4324                        }),
4325                    ]),
4326                    children: Some(vec![
4327                        fdecl::Child {
4328                            name: Some("child1".to_string()),
4329                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4330                            startup: Some(fdecl::StartupMode::Lazy),
4331                            on_terminate: None,
4332                            ..Default::default()
4333                        },
4334                        fdecl::Child {
4335                            name: Some("child2".to_string()),
4336                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4337                            startup: Some(fdecl::StartupMode::Lazy),
4338                            environment: Some("env".to_string()),
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("{{child child1 -> environment env -> child child2 -> child child1}}".to_string()),
4348            ])),
4349        },
4350        test_validate_strong_cycle_between_children_through_environment_resolver => {
4351            input = {
4352                fdecl::Component {
4353                    environments: Some(vec![
4354                        fdecl::Environment {
4355                            name: Some("env".to_string()),
4356                            extends: Some(fdecl::EnvironmentExtends::Realm),
4357                            resolvers: Some(vec![
4358                                fdecl::ResolverRegistration {
4359                                    resolver: Some("gopher".to_string()),
4360                                    source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4361                                    scheme: Some("gopher".to_string()),
4362                                    ..Default::default()
4363                                }
4364                            ]),
4365                            ..Default::default()
4366                        },
4367                    ]),
4368                    offers: Some(vec![
4369                        fdecl::Offer::Service(fdecl::OfferService {
4370                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4371                            source_name: Some("a".to_string()),
4372                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4373                            target_name: Some("a".to_string()),
4374                            ..Default::default()
4375                        }),
4376                    ]),
4377                    children: Some(vec![
4378                        fdecl::Child {
4379                            name: Some("child1".to_string()),
4380                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4381                            startup: Some(fdecl::StartupMode::Lazy),
4382                            on_terminate: None,
4383                            ..Default::default()
4384                        },
4385                        fdecl::Child {
4386                            name: Some("child2".to_string()),
4387                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4388                            startup: Some(fdecl::StartupMode::Lazy),
4389                            environment: Some("env".to_string()),
4390                            on_terminate: None,
4391                            ..Default::default()
4392                        }
4393                    ]),
4394                    ..new_component_decl()
4395                }
4396            },
4397            result = Err(ErrorList::new(vec![
4398                Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}".to_string()),
4399            ])),
4400        },
4401        test_validate_strong_cycle_between_self_and_two_children => {
4402            input = {
4403                fdecl::Component {
4404                    capabilities: Some(vec![
4405                        fdecl::Capability::Protocol(fdecl::Protocol {
4406                            name: Some("fuchsia.foo.Bar".to_string()),
4407                            source_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4408                            ..Default::default()
4409                        })
4410                    ]),
4411                    offers: Some(vec![
4412                        fdecl::Offer::Protocol(fdecl::OfferProtocol {
4413                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4414                            source_name: Some("fuchsia.foo.Bar".to_string()),
4415                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4416                            target_name: Some("fuchsia.foo.Bar".to_string()),
4417                            dependency_type: Some(fdecl::DependencyType::Strong),
4418                            ..Default::default()
4419                        }),
4420                        fdecl::Offer::Protocol(fdecl::OfferProtocol {
4421                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4422                            source_name: Some("fuchsia.bar.Baz".to_string()),
4423                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4424                            target_name: Some("fuchsia.bar.Baz".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                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child2".to_string(), collection: None})),
4432                            source_name: Some("fuchsia.baz.Foo".to_string()),
4433                            target_path: Some("/svc/fuchsia.baz.Foo".to_string()),
4434                            dependency_type: Some(fdecl::DependencyType::Strong),
4435                            ..Default::default()
4436                        }),
4437                    ]),
4438                    children: Some(vec![
4439                        fdecl::Child {
4440                            name: Some("child1".to_string()),
4441                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4442                            startup: Some(fdecl::StartupMode::Lazy),
4443                            on_terminate: None,
4444                            ..Default::default()
4445                        },
4446                        fdecl::Child {
4447                            name: Some("child2".to_string()),
4448                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4449                            startup: Some(fdecl::StartupMode::Lazy),
4450                            on_terminate: None,
4451                            ..Default::default()
4452                        }
4453                    ]),
4454                    ..new_component_decl()
4455                }
4456            },
4457            result = Err(ErrorList::new(vec![
4458                Error::dependency_cycle("{{self -> child child1 -> child child2 -> self}}".to_string()),
4459            ])),
4460        },
4461        test_validate_strong_cycle_with_self_storage => {
4462            input = {
4463                fdecl::Component {
4464                    capabilities: Some(vec![
4465                        fdecl::Capability::Storage(fdecl::Storage {
4466                            name: Some("data".to_string()),
4467                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4468                            backing_dir: Some("minfs".to_string()),
4469                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4470                            ..Default::default()
4471                        }),
4472                        fdecl::Capability::Directory(fdecl::Directory {
4473                            name: Some("minfs".to_string()),
4474                            source_path: Some("/minfs".to_string()),
4475                            rights: Some(fio::RW_STAR_DIR),
4476                            ..Default::default()
4477                        }),
4478                    ]),
4479                    offers: Some(vec![
4480                        fdecl::Offer::Storage(fdecl::OfferStorage {
4481                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4482                            source_name: Some("data".to_string()),
4483                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4484                            target_name: Some("data".to_string()),
4485                            ..Default::default()
4486                        }),
4487                    ]),
4488                    uses: Some(vec![
4489                        fdecl::Use::Protocol(fdecl::UseProtocol {
4490                            dependency_type: Some(fdecl::DependencyType::Strong),
4491                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4492                            source_name: Some("fuchsia.foo.Bar".to_string()),
4493                            target_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4494                            ..Default::default()
4495                        }),
4496                    ]),
4497                    children: Some(vec![
4498                        fdecl::Child {
4499                            name: Some("child".to_string()),
4500                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4501                            startup: Some(fdecl::StartupMode::Lazy),
4502                            ..Default::default()
4503                        },
4504                    ]),
4505                    ..new_component_decl()
4506                }
4507            },
4508            result = Err(ErrorList::new(vec![
4509                Error::dependency_cycle("{{self -> capability data -> child child -> self}}".to_string()),
4510            ])),
4511        },
4512        test_validate_strong_cycle_with_self_storage_admin_protocol => {
4513            input = {
4514                fdecl::Component {
4515                    capabilities: Some(vec![
4516                        fdecl::Capability::Storage(fdecl::Storage {
4517                            name: Some("data".to_string()),
4518                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4519                            backing_dir: Some("minfs".to_string()),
4520                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4521                            ..Default::default()
4522                        }),
4523                        fdecl::Capability::Directory(fdecl::Directory {
4524                            name: Some("minfs".to_string()),
4525                            source_path: Some("/minfs".to_string()),
4526                            rights: Some(fio::RW_STAR_DIR),
4527                            ..Default::default()
4528                        }),
4529                    ]),
4530                    offers: Some(vec![
4531                        fdecl::Offer::Protocol(fdecl::OfferProtocol {
4532                            source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef { name: "data".to_string() })),
4533                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4534                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4535                            target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4536                            dependency_type: Some(fdecl::DependencyType::Strong),
4537                            ..Default::default()
4538                        }),
4539                    ]),
4540                    uses: Some(vec![
4541                        fdecl::Use::Protocol(fdecl::UseProtocol {
4542                            dependency_type: Some(fdecl::DependencyType::Strong),
4543                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4544                            source_name: Some("fuchsia.foo.Bar".to_string()),
4545                            target_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4546                            ..Default::default()
4547                        }),
4548                    ]),
4549                    children: Some(vec![
4550                        fdecl::Child {
4551                            name: Some("child".to_string()),
4552                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4553                            startup: Some(fdecl::StartupMode::Lazy),
4554                            ..Default::default()
4555                        },
4556                    ]),
4557                    ..new_component_decl()
4558                }
4559            },
4560            result = Err(ErrorList::new(vec![
4561                Error::dependency_cycle("{{self -> capability data -> child child -> self}}".to_string()),
4562            ])),
4563        },
4564        test_validate_strong_cycle_with_dictionary => {
4565            input = fdecl::Component {
4566                offers: Some(vec![
4567                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
4568                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4569                        source_name: Some("dict".into()),
4570                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4571                            name: "a".into(),
4572                            collection: None,
4573                        })),
4574                        target_name: Some("dict".into()),
4575                        dependency_type: Some(fdecl::DependencyType::Strong),
4576                        ..Default::default()
4577                    }),
4578                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4579                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4580                            name: "b".into(),
4581                            collection: None,
4582                        })),
4583                        source_name: Some("1".into()),
4584                        target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4585                            name: "dict".into(),
4586                        })),
4587                        target_name: Some("1".into()),
4588                        dependency_type: Some(fdecl::DependencyType::Strong),
4589                        ..Default::default()
4590                    }),
4591                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4592                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4593                            name: "a".into(),
4594                            collection: None,
4595                        })),
4596                        source_name: Some("2".into()),
4597                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4598                            name: "b".into(),
4599                            collection: None,
4600                        })),
4601                        target_name: Some("2".into()),
4602                        dependency_type: Some(fdecl::DependencyType::Strong),
4603                        ..Default::default()
4604                    }),
4605                ]),
4606                children: Some(vec![
4607                    fdecl::Child {
4608                        name: Some("a".into()),
4609                        url: Some("fuchsia-pkg://child".into()),
4610                        startup: Some(fdecl::StartupMode::Lazy),
4611                        ..Default::default()
4612                    },
4613                    fdecl::Child {
4614                        name: Some("b".into()),
4615                        url: Some("fuchsia-pkg://child".into()),
4616                        startup: Some(fdecl::StartupMode::Lazy),
4617                        ..Default::default()
4618                    },
4619                ]),
4620                capabilities: Some(vec![
4621                    fdecl::Capability::Dictionary(fdecl::Dictionary {
4622                        name: Some("dict".into()),
4623                        ..Default::default()
4624                    }),
4625                ]),
4626                ..Default::default()
4627            },
4628            result = Err(ErrorList::new(vec![
4629                Error::dependency_cycle("{{child a -> child b -> capability dict -> child a}}".to_string()),
4630            ])),
4631        },
4632        test_validate_strong_cycle_with_dictionary_indirect => {
4633            input = fdecl::Component {
4634                offers: Some(vec![
4635                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4636                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4637                        source_name: Some("3".into()),
4638                        source_dictionary: Some("dict".into()),
4639                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4640                            name: "a".into(),
4641                            collection: None,
4642                        })),
4643                        target_name: Some("3".into()),
4644                        dependency_type: Some(fdecl::DependencyType::Strong),
4645                        ..Default::default()
4646                    }),
4647                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4648                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4649                            name: "b".into(),
4650                            collection: None,
4651                        })),
4652                        source_name: Some("1".into()),
4653                        target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4654                            name: "dict".into(),
4655                        })),
4656                        target_name: Some("1".into()),
4657                        dependency_type: Some(fdecl::DependencyType::Strong),
4658                        ..Default::default()
4659                    }),
4660                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4661                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4662                            name: "a".into(),
4663                            collection: None,
4664                        })),
4665                        source_name: Some("2".into()),
4666                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4667                            name: "b".into(),
4668                            collection: None,
4669                        })),
4670                        target_name: Some("2".into()),
4671                        dependency_type: Some(fdecl::DependencyType::Strong),
4672                        ..Default::default()
4673                    }),
4674                ]),
4675                children: Some(vec![
4676                    fdecl::Child {
4677                        name: Some("a".into()),
4678                        url: Some("fuchsia-pkg://child".into()),
4679                        startup: Some(fdecl::StartupMode::Lazy),
4680                        ..Default::default()
4681                    },
4682                    fdecl::Child {
4683                        name: Some("b".into()),
4684                        url: Some("fuchsia-pkg://child".into()),
4685                        startup: Some(fdecl::StartupMode::Lazy),
4686                        ..Default::default()
4687                    },
4688                ]),
4689                capabilities: Some(vec![
4690                    fdecl::Capability::Dictionary(fdecl::Dictionary {
4691                        name: Some("dict".into()),
4692                        ..Default::default()
4693                    }),
4694                ]),
4695                ..Default::default()
4696            },
4697            result = Err(ErrorList::new(vec![
4698                Error::dependency_cycle("{{child a -> child b -> capability dict -> child a}}".to_string()),
4699            ])),
4700        },
4701        test_validate_use_from_child_offer_to_child_weak_cycle => {
4702            input = {
4703                fdecl::Component {
4704                    capabilities: Some(vec![
4705                        fdecl::Capability::Service(fdecl::Service {
4706                            name: Some("a".to_string()),
4707                            source_path: Some("/a".to_string()),
4708                            ..Default::default()
4709                        })]),
4710                    uses: Some(vec![
4711                        fdecl::Use::Protocol(fdecl::UseProtocol {
4712                            dependency_type: Some(fdecl::DependencyType::Weak),
4713                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4714                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4715                            target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
4716                            ..Default::default()
4717                        }),
4718                        fdecl::Use::Service(fdecl::UseService {
4719                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4720                            source_name: Some("service_name".to_string()),
4721                            target_path: Some("/svc/service_name".to_string()),
4722                            dependency_type: Some(fdecl::DependencyType::Weak),
4723                            ..Default::default()
4724                        }),
4725                        fdecl::Use::Directory(fdecl::UseDirectory {
4726                            dependency_type: Some(fdecl::DependencyType::Weak),
4727                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4728                            source_name: Some("DirectoryName".to_string()),
4729                            target_path: Some("/data/DirectoryName".to_string()),
4730                            rights: Some(fio::Operations::CONNECT),
4731                            subdir: None,
4732                            ..Default::default()
4733                        }),
4734                    ]),
4735                    offers: Some(vec![
4736                        fdecl::Offer::Service(fdecl::OfferService {
4737                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4738                            source_name: Some("a".to_string()),
4739                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4740                            target_name: Some("a".to_string()),
4741                            ..Default::default()
4742                        })
4743                    ]),
4744                    children: Some(vec![
4745                        fdecl::Child {
4746                            name: Some("child".to_string()),
4747                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4748                            startup: Some(fdecl::StartupMode::Lazy),
4749                            on_terminate: None,
4750                            ..Default::default()
4751                        }
4752                    ]),
4753                    ..new_component_decl()
4754                }
4755            },
4756            result = Ok(()),
4757        },
4758        test_validate_expose_from_self_to_framework_and_parent => {
4759            input = {
4760                fdecl::Component {
4761                    capabilities: Some(vec![
4762                        fdecl::Capability::Protocol(fdecl::Protocol {
4763                            name: Some("a".to_string()),
4764                            source_path: Some("/a".to_string()),
4765                            ..Default::default()
4766                        }),
4767                    ]),
4768                    exposes: Some(vec![
4769                        fdecl::Expose::Protocol(fdecl::ExposeProtocol {
4770                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4771                            source_name: Some("a".to_string()),
4772                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4773                            target_name: Some("a".to_string()),
4774                            ..Default::default()
4775                        }),
4776                        fdecl::Expose::Protocol(fdecl::ExposeProtocol {
4777                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4778                            source_name: Some("a".to_string()),
4779                            target: Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})),
4780                            target_name: Some("a".to_string()),
4781                            ..Default::default()
4782                        }),
4783                    ]),
4784                    ..new_component_decl()
4785                }
4786            },
4787            result = Ok(()),
4788        },
4789        test_validate_use_from_not_child_weak => {
4790            input = {
4791                fdecl::Component {
4792                    uses: Some(vec![
4793                        fdecl::Use::Protocol(fdecl::UseProtocol {
4794                            dependency_type: Some(fdecl::DependencyType::Weak),
4795                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4796                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4797                            target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
4798                            ..Default::default()
4799                        }),
4800                    ]),
4801                    ..new_component_decl()
4802                }
4803            },
4804            result = Err(ErrorList::new(vec![
4805                Error::invalid_field(DeclType::UseProtocol, "dependency_type"),
4806            ])),
4807        },
4808        test_validate_event_stream_offer_valid_decls => {
4809            input = {
4810                let mut decl = new_component_decl();
4811                decl.offers = Some(vec![
4812                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4813                        source_name: Some("stopped".to_string()),
4814                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4815                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4816                        target_name: Some("stopped".to_string()),
4817                        ..Default::default()
4818                    }),
4819                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4820                        source_name: Some("started".to_string()),
4821                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4822                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4823                        target_name: Some("started".to_string()),
4824                        ..Default::default()
4825                    }),
4826                ]);
4827                decl.children = Some(vec![fdecl::Child{
4828                    name: Some("test".to_string()),
4829                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4830                    startup: Some(fdecl::StartupMode::Lazy),
4831                    on_terminate: None,
4832                    environment: None,
4833                    ..Default::default()
4834                },
4835                fdecl::Child{
4836                    name: Some("test2".to_string()),
4837                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4838                    startup: Some(fdecl::StartupMode::Lazy),
4839                    on_terminate: None,
4840                    environment: None,
4841                    ..Default::default()
4842                }
4843                ]);
4844                decl
4845            },
4846            result = Ok(()),
4847        },
4848        test_validate_event_stream_offer_to_framework_invalid => {
4849            input = {
4850                let mut decl = new_component_decl();
4851                decl.offers = Some(vec![
4852                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4853                        source_name: Some("stopped".to_string()),
4854                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4855                        target: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
4856                        target_name: Some("stopped".to_string()),
4857                        ..Default::default()
4858                    }),
4859                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4860                        source_name: Some("started".to_string()),
4861                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4862                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4863                        target_name: Some("started".to_string()),
4864                        ..Default::default()
4865                    }),
4866                ]);
4867                decl.children = Some(vec![fdecl::Child{
4868                    name: Some("test".to_string()),
4869                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4870                    startup: Some(fdecl::StartupMode::Lazy),
4871                    on_terminate: None,
4872                    environment: None,
4873                    ..Default::default()
4874                },
4875                fdecl::Child{
4876                    name: Some("test2".to_string()),
4877                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4878                    startup: Some(fdecl::StartupMode::Lazy),
4879                    on_terminate: None,
4880                    environment: None,
4881                    ..Default::default()
4882                }
4883                ]);
4884                decl
4885            },
4886            result = Err(ErrorList::new(vec![
4887                Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "target".to_string() }),
4888                Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "target".to_string() }),
4889            ])),
4890        },
4891        test_validate_event_stream_offer_to_scope_zero_length_invalid => {
4892            input = {
4893                let mut decl = new_component_decl();
4894                decl.offers = Some(vec![
4895                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4896                        source_name: Some("started".to_string()),
4897                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4898                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4899                        scope: Some(vec![]),
4900                        target_name: Some("started".to_string()),
4901                        ..Default::default()
4902                    }),
4903                ]);
4904                decl.children = Some(vec![fdecl::Child{
4905                    name: Some("test".to_string()),
4906                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4907                    startup: Some(fdecl::StartupMode::Lazy),
4908                    on_terminate: None,
4909                    environment: None,
4910                    ..Default::default()
4911                },
4912                fdecl::Child{
4913                    name: Some("test2".to_string()),
4914                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4915                    startup: Some(fdecl::StartupMode::Lazy),
4916                    on_terminate: None,
4917                    environment: None,
4918                    ..Default::default()
4919                }
4920                ]);
4921                decl
4922            },
4923            result = Err(ErrorList::new(vec![
4924                Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "scope".to_string() }),
4925            ])),
4926        },
4927        test_validate_event_stream_offer_to_scope_framework_invalid => {
4928            input = {
4929                let mut decl = new_component_decl();
4930                decl.offers = Some(vec![
4931                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4932                        source_name: Some("started".to_string()),
4933                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4934                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4935                        scope: Some(vec![fdecl::Ref::Framework(fdecl::FrameworkRef{})]),
4936                        target_name: Some("started".to_string()),
4937                        ..Default::default()
4938                    }),
4939                ]);
4940                decl.children = Some(vec![fdecl::Child{
4941                    name: Some("test".to_string()),
4942                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4943                    startup: Some(fdecl::StartupMode::Lazy),
4944                    on_terminate: None,
4945                    environment: None,
4946                    ..Default::default()
4947                },
4948                fdecl::Child{
4949                    name: Some("test2".to_string()),
4950                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4951                    startup: Some(fdecl::StartupMode::Lazy),
4952                    on_terminate: None,
4953                    environment: None,
4954                    ..Default::default()
4955                }
4956                ]);
4957                decl
4958            },
4959            result = Err(ErrorList::new(vec![
4960                Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "scope".to_string() }),
4961            ])),
4962        },
4963        test_validate_event_stream_offer_to_scope_valid => {
4964            input = {
4965                let mut decl = new_component_decl();
4966                decl.offers = Some(vec![
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                        scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
4972                        target_name: Some("started".to_string()),
4973                        ..Default::default()
4974                    }),
4975                ]);
4976                decl.children = Some(vec![fdecl::Child{
4977                    name: Some("test".to_string()),
4978                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4979                    startup: Some(fdecl::StartupMode::Lazy),
4980                    on_terminate: None,
4981                    environment: None,
4982                    ..Default::default()
4983                },
4984                fdecl::Child{
4985                    name: Some("test2".to_string()),
4986                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4987                    startup: Some(fdecl::StartupMode::Lazy),
4988                    on_terminate: None,
4989                    environment: None,
4990                    ..Default::default()
4991                }
4992                ]);
4993                decl
4994            },
4995            result = Ok(()),
4996        },
4997        test_validate_event_stream_offer_to_scope_with_capability_requested => {
4998            input = {
4999                let mut decl = new_component_decl();
5000                decl.offers = Some(vec![
5001                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
5002                        source_name: Some("capability_requested".to_string()),
5003                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5004                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5005                        scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
5006                        target_name: Some("started".to_string()),
5007                        ..Default::default()
5008                    }),
5009                ]);
5010                decl.children = Some(vec![fdecl::Child{
5011                    name: Some("test".to_string()),
5012                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5013                    startup: Some(fdecl::StartupMode::Lazy),
5014                    on_terminate: None,
5015                    environment: None,
5016                    ..Default::default()
5017                },
5018                fdecl::Child{
5019                    name: Some("test2".to_string()),
5020                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
5021                    startup: Some(fdecl::StartupMode::Lazy),
5022                    on_terminate: None,
5023                    environment: None,
5024                    ..Default::default()
5025                }
5026                ]);
5027                decl
5028            },
5029            result = Ok(()),
5030        },
5031        test_validate_event_stream_offer_with_no_source_name_invalid => {
5032            input = {
5033                let mut decl = new_component_decl();
5034                decl.offers = Some(vec![
5035                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
5036                        source_name: None,
5037                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5038                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5039                        scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
5040                        target_name: Some("started".to_string()),
5041                        ..Default::default()
5042                    }),
5043                ]);
5044                decl.children = Some(vec![fdecl::Child{
5045                    name: Some("test".to_string()),
5046                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5047                    startup: Some(fdecl::StartupMode::Lazy),
5048                    on_terminate: None,
5049                    environment: None,
5050                    ..Default::default()
5051                },
5052                fdecl::Child{
5053                    name: Some("test2".to_string()),
5054                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
5055                    startup: Some(fdecl::StartupMode::Lazy),
5056                    on_terminate: None,
5057                    environment: None,
5058                    ..Default::default()
5059                }
5060                ]);
5061                decl
5062            },
5063            result = Err(ErrorList::new(vec![
5064                Error::MissingField(DeclField { decl: DeclType::OfferEventStream, field: "source_name".to_string() }),
5065            ])),
5066        },
5067        test_validate_event_stream_offer_invalid_source => {
5068            input = {
5069                let mut decl = new_component_decl();
5070                decl.offers = Some(vec![
5071                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
5072                        source_name: Some("stopped".to_string()),
5073                        source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
5074                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5075                        target_name: Some("stopped".to_string()),
5076                        ..Default::default()
5077                    }),
5078                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
5079                        source_name: Some("started".to_string()),
5080                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5081                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5082                        target_name: Some("started".to_string()),
5083                        ..Default::default()
5084                    }),
5085                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
5086                        source_name: Some("capability_requested".to_string()),
5087                        source: Some(fdecl::Ref::Debug(fdecl::DebugRef{})),
5088                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5089                        target_name: Some("capability_requested".to_string()),
5090                        ..Default::default()
5091                    }),
5092                ]);
5093                decl.children = Some(vec![fdecl::Child{
5094                    name: Some("test".to_string()),
5095                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5096                    startup: Some(fdecl::StartupMode::Lazy),
5097                    on_terminate: None,
5098                    environment: None,
5099                    ..Default::default()
5100                },
5101                fdecl::Child{
5102                    name: Some("test2".to_string()),
5103                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
5104                    startup: Some(fdecl::StartupMode::Lazy),
5105                    on_terminate: None,
5106                    environment: None,
5107                    ..Default::default()
5108                }
5109                ]);
5110                decl
5111            },
5112            result = Err(ErrorList::new(vec![
5113                Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "source".to_string() }),
5114            ])),
5115        },
5116
5117        test_validate_event_stream_offer_missing_source => {
5118            input = {
5119                let mut decl = new_component_decl();
5120                decl.offers = Some(vec![
5121                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
5122                        source_name: Some("stopped".to_string()),
5123                        source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
5124                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5125                        target_name: Some("stopped".to_string()),
5126                        ..Default::default()
5127                    }),
5128                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
5129                        source_name: Some("started".to_string()),
5130                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5131                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5132                        target_name: Some("started".to_string()),
5133                        ..Default::default()
5134                    }),
5135                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
5136                        source_name: Some("capability_requested".to_string()),
5137                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5138                        target_name: Some("capability_requested".to_string()),
5139                        ..Default::default()
5140                    }),
5141                ]);
5142                decl.children = Some(vec![fdecl::Child{
5143                    name: Some("test".to_string()),
5144                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5145                    startup: Some(fdecl::StartupMode::Lazy),
5146                    on_terminate: None,
5147                    environment: None,
5148                    ..Default::default()
5149                },
5150                fdecl::Child{
5151                    name: Some("test2".to_string()),
5152                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
5153                    startup: Some(fdecl::StartupMode::Lazy),
5154                    on_terminate: None,
5155                    environment: None,
5156                    ..Default::default()
5157                }
5158                ]);
5159                decl
5160            },
5161            result = Err(ErrorList::new(vec![
5162                Error::MissingField(DeclField { decl: DeclType::OfferEventStream, field: "source".to_string() }),
5163            ])),
5164        },
5165        test_validate_event_stream_must_have_target_path => {
5166            input = {
5167                let mut decl = new_component_decl();
5168                decl.uses = Some(vec![
5169                    fdecl::Use::EventStream(fdecl::UseEventStream {
5170                        source_name: Some("bar".to_string()),
5171                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5172                        ..Default::default()
5173                    }),
5174                ]);
5175                decl
5176            },
5177            result = Err(ErrorList::new(vec![
5178                Error::MissingField(DeclField { decl: DeclType::UseEventStream, field: "target_path".to_string() })
5179            ])),
5180        },
5181        test_validate_event_stream_must_have_source_names => {
5182            input = {
5183                let mut decl = new_component_decl();
5184                decl.uses = Some(vec![
5185                    fdecl::Use::EventStream(fdecl::UseEventStream {
5186                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5187                        target_path: Some("/svc/something".to_string()),
5188                        ..Default::default()
5189                    }),
5190                ]);
5191                decl
5192            },
5193            result = Err(ErrorList::new(vec![
5194                Error::MissingField(DeclField { decl: DeclType::UseEventStream, field: "source_name".to_string() })
5195            ])),
5196        },
5197        test_validate_event_stream_scope_must_be_child_or_collection => {
5198            input = {
5199                let mut decl = new_component_decl();
5200                decl.uses = Some(vec![
5201                    fdecl::Use::EventStream(fdecl::UseEventStream {
5202                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5203                        target_path: Some("/svc/something".to_string()),
5204                        source_name: Some("some_source".to_string()),
5205                        scope: Some(vec![fdecl::Ref::Framework(fdecl::FrameworkRef{})]),
5206                        ..Default::default()
5207                    }),
5208                ]);
5209                decl
5210            },
5211            result = Err(ErrorList::new(vec![
5212                Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "scope".to_string() })
5213            ])),
5214        },
5215        test_validate_event_stream_source_must_be_parent_or_child => {
5216            input = {
5217                let mut decl = new_component_decl();
5218                decl.uses = Some(vec![
5219                    fdecl::Use::EventStream(fdecl::UseEventStream {
5220                        source: Some(fdecl::Ref::Debug(fdecl::DebugRef{})),
5221                        target_path: Some("/svc/something".to_string()),
5222                        source_name: Some("some_source".to_string()),
5223                        scope: Some(vec![]),
5224                        ..Default::default()
5225                    }),
5226                    fdecl::Use::EventStream(fdecl::UseEventStream {
5227                        source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
5228                        target_path: Some("/svc/something_else".to_string()),
5229                        source_name: Some("some_source".to_string()),
5230                        scope: Some(vec![]),
5231                        ..Default::default()
5232                    }),
5233                    fdecl::Use::EventStream(fdecl::UseEventStream {
5234                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5235                        target_path: Some("/svc/yet_something_else".to_string()),
5236                        source_name: Some("some_source".to_string()),
5237                        scope: Some(vec![]),
5238                        ..Default::default()
5239                    }),
5240                ]);
5241                decl
5242            },
5243            result = Err(ErrorList::new(vec![
5244                Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() }),
5245                Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() }),
5246                Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() })
5247            ])),
5248        },
5249        test_validate_no_runner => {
5250            input = {
5251                let mut decl = new_component_decl();
5252                decl.program = Some(fdecl::Program {
5253                    runner: None,
5254                    info: Some(fdata::Dictionary {
5255                        entries: None,
5256                        ..Default::default()
5257                    }),
5258                    ..Default::default()
5259                });
5260                decl
5261            },
5262            result = Err(ErrorList::new(vec![
5263                Error::MissingRunner,
5264            ])),
5265        },
5266        test_validate_uses_runner => {
5267            input = {
5268                let mut decl = new_component_decl();
5269                decl.program = Some(fdecl::Program {
5270                    runner: None,
5271                    info: Some(fdata::Dictionary {
5272                        entries: None,
5273                        ..Default::default()
5274                    }),
5275                    ..Default::default()
5276                });
5277                decl.uses = Some(vec![
5278                    fdecl::Use::Runner(fdecl::UseRunner {
5279                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5280                        source_name: Some("runner".to_string()),
5281                        ..Default::default()
5282                    }),
5283                ]);
5284                decl
5285            },
5286            result = Ok(()),
5287        },
5288        test_validate_program_and_uses_runner_match => {
5289            input = {
5290                let mut decl = new_component_decl();
5291                decl.program = Some(fdecl::Program {
5292                    runner: Some("runner".to_string()),
5293                    info: Some(fdata::Dictionary {
5294                        entries: None,
5295                        ..Default::default()
5296                    }),
5297                    ..Default::default()
5298                });
5299                decl.uses = Some(vec![
5300                    fdecl::Use::Runner(fdecl::UseRunner {
5301                        source: Some(fdecl::Ref::Environment(fdecl::EnvironmentRef {})),
5302                        source_name: Some("runner".to_string()),
5303                        ..Default::default()
5304                    }),
5305                ]);
5306                decl
5307            },
5308            result = Ok(()),
5309        },
5310        test_validate_runner_names_conflict => {
5311            input = {
5312                let mut decl = new_component_decl();
5313                decl.program = Some(fdecl::Program {
5314                    runner: Some("runner".to_string()),
5315                    info: Some(fdata::Dictionary {
5316                        entries: None,
5317                        ..Default::default()
5318                    }),
5319                    ..Default::default()
5320                });
5321                decl.uses = Some(vec![
5322                    fdecl::Use::Runner(fdecl::UseRunner {
5323                        source: Some(fdecl::Ref::Environment(fdecl::EnvironmentRef {})),
5324                        source_name: Some("other.runner".to_string()),
5325                        ..Default::default()
5326                    }),
5327                ]);
5328                decl
5329            },
5330            result = Err(ErrorList::new(vec![
5331                Error::ConflictingRunners,
5332            ])),
5333        },
5334        test_validate_uses_runner_not_environement => {
5335            input = {
5336                let mut decl = new_component_decl();
5337                decl.program = Some(fdecl::Program {
5338                    runner: Some("runner".to_string()),
5339                    info: Some(fdata::Dictionary {
5340                        entries: None,
5341                        ..Default::default()
5342                    }),
5343                    ..Default::default()
5344                });
5345                decl.uses = Some(vec![
5346                    fdecl::Use::Runner(fdecl::UseRunner {
5347                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5348                        source_name: Some("runner".to_string()),
5349                        ..Default::default()
5350                    }),
5351                ]);
5352                decl
5353            },
5354            result = Err(ErrorList::new(vec![
5355                Error::ConflictingRunners,
5356            ])),
5357        },
5358        test_validate_uses_long_identifiers => {
5359            input = {
5360                let mut decl = new_component_decl();
5361                decl.program = Some(fdecl::Program {
5362                    runner: Some("elf".to_string()),
5363                    info: Some(fdata::Dictionary {
5364                        entries: None,
5365                        ..Default::default()
5366                    }),
5367                    ..Default::default()
5368                });
5369                decl.uses = Some(vec![
5370                    fdecl::Use::Service(fdecl::UseService {
5371                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5372                        source_name: Some(format!("{}", "a".repeat(256))),
5373                        target_path: Some("/a".repeat(2048)),
5374                        dependency_type: Some(fdecl::DependencyType::Strong),
5375                        ..Default::default()
5376                    }),
5377                    fdecl::Use::Protocol(fdecl::UseProtocol {
5378                        dependency_type: Some(fdecl::DependencyType::Strong),
5379                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5380                        source_name: Some(format!("{}", "a".repeat(256))),
5381                        target_path: Some("/b".repeat(2048)),
5382                        ..Default::default()
5383                    }),
5384                    fdecl::Use::Directory(fdecl::UseDirectory {
5385                        dependency_type: Some(fdecl::DependencyType::Strong),
5386                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5387                        source_name: Some(format!("{}", "a".repeat(256))),
5388                        target_path: Some("/c".repeat(2048)),
5389                        rights: Some(fio::Operations::CONNECT),
5390                        subdir: None,
5391                        ..Default::default()
5392                    }),
5393                    fdecl::Use::Storage(fdecl::UseStorage {
5394                        source_name: Some("cache".to_string()),
5395                        target_path: Some("/d".repeat(2048)),
5396                        ..Default::default()
5397                    }),
5398                ]);
5399                decl
5400            },
5401            result = Err(ErrorList::new(vec![
5402                Error::field_too_long(DeclType::UseService, "source_name"),
5403                Error::field_too_long(DeclType::UseService, "target_path"),
5404                Error::field_too_long(DeclType::UseProtocol, "source_name"),
5405                Error::field_too_long(DeclType::UseProtocol, "target_path"),
5406                Error::field_too_long(DeclType::UseDirectory, "source_name"),
5407                Error::field_too_long(DeclType::UseDirectory, "target_path"),
5408                Error::field_too_long(DeclType::UseStorage, "target_path"),
5409            ])),
5410        },
5411        test_validate_conflicting_paths => {
5412            input = {
5413                let mut decl = new_component_decl();
5414                decl.uses = Some(vec![
5415                    fdecl::Use::Service(fdecl::UseService {
5416                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5417                        source_name: Some("foo".to_string()),
5418                        target_path: Some("/bar".to_string()),
5419                        dependency_type: Some(fdecl::DependencyType::Strong),
5420                        ..Default::default()
5421                    }),
5422                    fdecl::Use::Protocol(fdecl::UseProtocol {
5423                        dependency_type: Some(fdecl::DependencyType::Strong),
5424                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5425                        source_name: Some("space".to_string()),
5426                        target_path: Some("/bar".to_string()),
5427                        ..Default::default()
5428                    }),
5429                    fdecl::Use::Directory(fdecl::UseDirectory {
5430                        dependency_type: Some(fdecl::DependencyType::Strong),
5431                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5432                        source_name: Some("crow".to_string()),
5433                        target_path: Some("/bar".to_string()),
5434                        rights: Some(fio::Operations::CONNECT),
5435                        subdir: None,
5436                        ..Default::default()
5437                    }),
5438                ]);
5439                decl
5440            },
5441            result = Err(ErrorList::new(vec![
5442                Error::duplicate_field(DeclType::UseProtocol, "target_path", "/bar"),
5443                Error::duplicate_field(DeclType::UseDirectory, "target_path", "/bar"),
5444            ])),
5445        },
5446        // exposes
5447        test_validate_exposes_empty => {
5448            input = {
5449                let mut decl = new_component_decl();
5450                decl.exposes = Some(vec![
5451                    fdecl::Expose::Service(fdecl::ExposeService {
5452                        source: None,
5453                        source_name: None,
5454                        target_name: None,
5455                        target: None,
5456                        ..Default::default()
5457                    }),
5458                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5459                        source: None,
5460                        source_name: None,
5461                        target_name: None,
5462                        target: None,
5463                        ..Default::default()
5464                    }),
5465                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5466                        source: None,
5467                        source_name: None,
5468                        target_name: None,
5469                        target: None,
5470                        rights: None,
5471                        subdir: None,
5472                        ..Default::default()
5473                    }),
5474                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5475                        source: None,
5476                        source_name: None,
5477                        target: None,
5478                        target_name: None,
5479                        ..Default::default()
5480                    }),
5481                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5482                        source: None,
5483                        source_name: None,
5484                        target: None,
5485                        target_name: None,
5486                        ..Default::default()
5487                    }),
5488                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5489                        ..Default::default()
5490                    }),
5491                ]);
5492                decl
5493            },
5494            result = Err(ErrorList::new(vec![
5495                Error::missing_field(DeclType::ExposeService, "source"),
5496                Error::missing_field(DeclType::ExposeService, "target"),
5497                Error::missing_field(DeclType::ExposeService, "source_name"),
5498                Error::missing_field(DeclType::ExposeService, "target_name"),
5499                Error::missing_field(DeclType::ExposeProtocol, "source"),
5500                Error::missing_field(DeclType::ExposeProtocol, "target"),
5501                Error::missing_field(DeclType::ExposeProtocol, "source_name"),
5502                Error::missing_field(DeclType::ExposeProtocol, "target_name"),
5503                Error::missing_field(DeclType::ExposeDirectory, "source"),
5504                Error::missing_field(DeclType::ExposeDirectory, "target"),
5505                Error::missing_field(DeclType::ExposeDirectory, "source_name"),
5506                Error::missing_field(DeclType::ExposeDirectory, "target_name"),
5507                Error::missing_field(DeclType::ExposeRunner, "source"),
5508                Error::missing_field(DeclType::ExposeRunner, "target"),
5509                Error::missing_field(DeclType::ExposeRunner, "source_name"),
5510                Error::missing_field(DeclType::ExposeRunner, "target_name"),
5511                Error::missing_field(DeclType::ExposeResolver, "source"),
5512                Error::missing_field(DeclType::ExposeResolver, "target"),
5513                Error::missing_field(DeclType::ExposeResolver, "source_name"),
5514                Error::missing_field(DeclType::ExposeResolver, "target_name"),
5515                Error::missing_field(DeclType::ExposeDictionary, "source"),
5516                Error::missing_field(DeclType::ExposeDictionary, "target"),
5517                Error::missing_field(DeclType::ExposeDictionary, "source_name"),
5518                Error::missing_field(DeclType::ExposeDictionary, "target_name"),
5519            ])),
5520        },
5521        test_validate_exposes_extraneous => {
5522            input = {
5523                let mut decl = new_component_decl();
5524                decl.exposes = Some(vec![
5525                    fdecl::Expose::Service(fdecl::ExposeService {
5526                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5527                            name: "logger".to_string(),
5528                            collection: Some("modular".to_string()),
5529                        })),
5530                        source_name: Some("logger".to_string()),
5531                        target_name: Some("logger".to_string()),
5532                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5533                        ..Default::default()
5534                    }),
5535                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5536                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5537                            name: "logger".to_string(),
5538                            collection: Some("modular".to_string()),
5539                        })),
5540                        source_name: Some("legacy_logger".to_string()),
5541                        target_name: Some("legacy_logger".to_string()),
5542                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5543                        ..Default::default()
5544                    }),
5545                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5546                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5547                            name: "netstack".to_string(),
5548                            collection: Some("modular".to_string()),
5549                        })),
5550                        source_name: Some("data".to_string()),
5551                        target_name: Some("data".to_string()),
5552                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5553                        rights: Some(fio::Operations::CONNECT),
5554                        subdir: None,
5555                        ..Default::default()
5556                    }),
5557                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5558                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5559                            name: "netstack".to_string(),
5560                            collection: Some("modular".to_string()),
5561                        })),
5562                        source_name: Some("elf".to_string()),
5563                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5564                        target_name: Some("elf".to_string()),
5565                        ..Default::default()
5566                    }),
5567                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5568                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5569                            name: "netstack".to_string(),
5570                            collection: Some("modular".to_string()),
5571                        })),
5572                        source_name: Some("pkg".to_string()),
5573                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5574                        target_name: Some("pkg".to_string()),
5575                        ..Default::default()
5576                    }),
5577                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5578                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5579                            name: "netstack".to_string(),
5580                            collection: Some("modular".to_string()),
5581                        })),
5582                        source_name: Some("dict".to_string()),
5583                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5584                        target_name: Some("dict".to_string()),
5585                        ..Default::default()
5586                    }),
5587                ]);
5588                decl
5589            },
5590            result = Err(ErrorList::new(vec![
5591                Error::extraneous_field(DeclType::ExposeService, "source.child.collection"),
5592                Error::extraneous_field(DeclType::ExposeProtocol, "source.child.collection"),
5593                Error::extraneous_field(DeclType::ExposeDirectory, "source.child.collection"),
5594                Error::extraneous_field(DeclType::ExposeRunner, "source.child.collection"),
5595                Error::extraneous_field(DeclType::ExposeResolver, "source.child.collection"),
5596                Error::extraneous_field(DeclType::ExposeDictionary, "source.child.collection"),
5597            ])),
5598        },
5599        test_validate_exposes_invalid_identifiers => {
5600            input = {
5601                let mut decl = new_component_decl();
5602                decl.exposes = Some(vec![
5603                    fdecl::Expose::Service(fdecl::ExposeService {
5604                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5605                            name: "^bad".to_string(),
5606                            collection: None,
5607                        })),
5608                        source_name: Some("foo/".to_string()),
5609                        target_name: Some("/".to_string()),
5610                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5611                        ..Default::default()
5612                    }),
5613                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5614                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5615                            name: "^bad".to_string(),
5616                            collection: None,
5617                        })),
5618                        source_name: Some("foo/".to_string()),
5619                        target_name: Some("/".to_string()),
5620                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5621                        ..Default::default()
5622                    }),
5623                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5624                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5625                            name: "^bad".to_string(),
5626                            collection: None,
5627                        })),
5628                        source_name: Some("foo/".to_string()),
5629                        target_name: Some("/".to_string()),
5630                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5631                        rights: Some(fio::Operations::CONNECT),
5632                        subdir: Some("/foo".to_string()),
5633                        ..Default::default()
5634                    }),
5635                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5636                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5637                            name: "^bad".to_string(),
5638                            collection: None,
5639                        })),
5640                        source_name: Some("/path".to_string()),
5641                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5642                        target_name: Some("elf!".to_string()),
5643                        ..Default::default()
5644                    }),
5645                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5646                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5647                            name: "^bad".to_string(),
5648                            collection: None,
5649                        })),
5650                        source_name: Some("/path".to_string()),
5651                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5652                        target_name: Some("pkg!".to_string()),
5653                        ..Default::default()
5654                    }),
5655                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5656                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5657                            name: "^bad".to_string(),
5658                            collection: None,
5659                        })),
5660                        source_name: Some("/path".to_string()),
5661                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5662                        target_name: Some("pkg!".to_string()),
5663                        ..Default::default()
5664                    }),
5665                ]);
5666                decl
5667            },
5668            result = Err(ErrorList::new(vec![
5669                Error::invalid_field(DeclType::ExposeService, "source.child.name"),
5670                Error::invalid_field(DeclType::ExposeService, "source_name"),
5671                Error::invalid_field(DeclType::ExposeService, "target_name"),
5672                Error::invalid_field(DeclType::ExposeProtocol, "source.child.name"),
5673                Error::invalid_field(DeclType::ExposeProtocol, "source_name"),
5674                Error::invalid_field(DeclType::ExposeProtocol, "target_name"),
5675                Error::invalid_field(DeclType::ExposeDirectory, "source.child.name"),
5676                Error::invalid_field(DeclType::ExposeDirectory, "source_name"),
5677                Error::invalid_field(DeclType::ExposeDirectory, "target_name"),
5678                Error::invalid_field(DeclType::ExposeDirectory, "subdir"),
5679                Error::invalid_field(DeclType::ExposeRunner, "source.child.name"),
5680                Error::invalid_field(DeclType::ExposeRunner, "source_name"),
5681                Error::invalid_field(DeclType::ExposeRunner, "target_name"),
5682                Error::invalid_field(DeclType::ExposeResolver, "source.child.name"),
5683                Error::invalid_field(DeclType::ExposeResolver, "source_name"),
5684                Error::invalid_field(DeclType::ExposeResolver, "target_name"),
5685                Error::invalid_field(DeclType::ExposeDictionary, "source.child.name"),
5686                Error::invalid_field(DeclType::ExposeDictionary, "source_name"),
5687                Error::invalid_field(DeclType::ExposeDictionary, "target_name"),
5688            ])),
5689        },
5690        test_validate_exposes_invalid_source_target => {
5691            input = {
5692                let mut decl = new_component_decl();
5693                decl.children = Some(vec![fdecl::Child{
5694                    name: Some("logger".to_string()),
5695                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5696                    startup: Some(fdecl::StartupMode::Lazy),
5697                    on_terminate: None,
5698                    environment: None,
5699                    ..Default::default()
5700                }]);
5701                decl.exposes = Some(vec![
5702                    fdecl::Expose::Service(fdecl::ExposeService {
5703                        source: None,
5704                        source_name: Some("a".to_string()),
5705                        target_name: Some("b".to_string()),
5706                        target: None,
5707                        ..Default::default()
5708                    }),
5709                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5710                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5711                        source_name: Some("c".to_string()),
5712                        target_name: Some("d".to_string()),
5713                        target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5714                        ..Default::default()
5715                    }),
5716                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5717                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "z".to_string()})),
5718                        source_name: Some("e".to_string()),
5719                        target_name: Some("f".to_string()),
5720                        target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "z".to_string()})),
5721                        rights: Some(fio::Operations::CONNECT),
5722                        subdir: None,
5723                        ..Default::default()
5724                    }),
5725                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5726                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5727                        source_name: Some("g".to_string()),
5728                        target_name: Some("h".to_string()),
5729                        target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5730                        rights: Some(fio::Operations::CONNECT),
5731                        subdir: None,
5732                        ..Default::default()
5733                    }),
5734                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5735                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5736                        source_name: Some("i".to_string()),
5737                        target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5738                        target_name: Some("j".to_string()),
5739                        ..Default::default()
5740                    }),
5741                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5742                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5743                        source_name: Some("k".to_string()),
5744                        target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5745                        target_name: Some("l".to_string()),
5746                        ..Default::default()
5747                    }),
5748                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5749                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5750                            name: "logger".to_string(),
5751                            collection: None,
5752                        })),
5753                        source_name: Some("m".to_string()),
5754                        target_name: Some("n".to_string()),
5755                        target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5756                        ..Default::default()
5757                    }),
5758                ]);
5759                decl
5760            },
5761            result = Err(ErrorList::new(vec![
5762                Error::missing_field(DeclType::ExposeService, "source"),
5763                Error::missing_field(DeclType::ExposeService, "target"),
5764                Error::invalid_field(DeclType::ExposeProtocol, "source"),
5765                Error::invalid_field(DeclType::ExposeProtocol, "target"),
5766                Error::invalid_field(DeclType::ExposeDirectory, "source"),
5767                Error::invalid_field(DeclType::ExposeDirectory, "target"),
5768                Error::invalid_field(DeclType::ExposeDirectory, "source"),
5769                Error::invalid_field(DeclType::ExposeDirectory, "target"),
5770                Error::invalid_field(DeclType::ExposeRunner, "source"),
5771                Error::invalid_field(DeclType::ExposeRunner, "target"),
5772                Error::invalid_field(DeclType::ExposeResolver, "source"),
5773                Error::invalid_field(DeclType::ExposeResolver, "target"),
5774                Error::invalid_field(DeclType::ExposeDictionary, "target"),
5775            ])),
5776        },
5777        test_validate_exposes_invalid_source_collection => {
5778            input = {
5779                let mut decl = new_component_decl();
5780                decl.collections = Some(vec![fdecl::Collection{
5781                    name: Some("col".to_string()),
5782                    durability: Some(fdecl::Durability::Transient),
5783                    allowed_offers: None,
5784                    allow_long_names: None,
5785                    ..Default::default()
5786                }]);
5787                decl.exposes = Some(vec![
5788                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5789                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
5790                        source_name: Some("a".to_string()),
5791                        target_name: Some("a".to_string()),
5792                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5793                        ..Default::default()
5794                    }),
5795                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5796                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5797                        source_name: Some("b".to_string()),
5798                        target_name: Some("b".to_string()),
5799                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5800                        rights: Some(fio::Operations::CONNECT),
5801                        subdir: None,
5802                        ..Default::default()
5803                    }),
5804                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5805                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5806                        source_name: Some("c".to_string()),
5807                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5808                        target_name: Some("c".to_string()),
5809                        ..Default::default()
5810                    }),
5811                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5812                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5813                        source_name: Some("d".to_string()),
5814                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5815                        target_name: Some("d".to_string()),
5816                        ..Default::default()
5817                    }),
5818                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5819                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5820                        source_name: Some("e".to_string()),
5821                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5822                        target_name: Some("e".to_string()),
5823                        ..Default::default()
5824                    }),
5825                ]);
5826                decl
5827            },
5828            result = Err(ErrorList::new(vec![
5829                Error::invalid_field(DeclType::ExposeProtocol, "source"),
5830                Error::invalid_field(DeclType::ExposeDirectory, "source"),
5831                Error::invalid_field(DeclType::ExposeRunner, "source"),
5832                Error::invalid_field(DeclType::ExposeResolver, "source"),
5833                Error::invalid_field(DeclType::ExposeDictionary, "source"),
5834            ])),
5835        },
5836        test_validate_exposes_sources_collection => {
5837            input = {
5838                let mut decl = new_component_decl();
5839                decl.collections = Some(vec![
5840                    fdecl::Collection {
5841                        name: Some("col".to_string()),
5842                        durability: Some(fdecl::Durability::Transient),
5843                        allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
5844                        allow_long_names: None,
5845                        ..Default::default()
5846                    }
5847                ]);
5848                decl.exposes = Some(vec![
5849                    fdecl::Expose::Service(fdecl::ExposeService {
5850                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
5851                        source_name: Some("a".to_string()),
5852                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5853                        target_name: Some("a".to_string()),
5854                        ..Default::default()
5855                    })
5856                ]);
5857                decl
5858            },
5859            result = Ok(()),
5860        },
5861        test_validate_exposes_long_identifiers => {
5862            input = {
5863                let mut decl = new_component_decl();
5864                decl.exposes = Some(vec![
5865                    fdecl::Expose::Service(fdecl::ExposeService {
5866                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5867                            name: "b".repeat(256),
5868                            collection: None,
5869                        })),
5870                        source_name: Some(format!("{}", "a".repeat(1025))),
5871                        target_name: Some(format!("{}", "b".repeat(1025))),
5872                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5873                        ..Default::default()
5874                    }),
5875                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5876                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5877                            name: "b".repeat(256),
5878                            collection: None,
5879                        })),
5880                        source_name: Some(format!("{}", "a".repeat(256))),
5881                        target_name: Some(format!("{}", "b".repeat(256))),
5882                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5883                        ..Default::default()
5884                    }),
5885                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5886                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5887                            name: "b".repeat(256),
5888                            collection: None,
5889                        })),
5890                        source_name: Some(format!("{}", "a".repeat(256))),
5891                        target_name: Some(format!("{}", "b".repeat(256))),
5892                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5893                        rights: Some(fio::Operations::CONNECT),
5894                        subdir: None,
5895                        ..Default::default()
5896                    }),
5897                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5898                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5899                            name: "b".repeat(256),
5900                            collection: None,
5901                        })),
5902                        source_name: Some("a".repeat(256)),
5903                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5904                        target_name: Some("b".repeat(256)),
5905                        ..Default::default()
5906                    }),
5907                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5908                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5909                            name: "b".repeat(256),
5910                            collection: None,
5911                        })),
5912                        source_name: Some("a".repeat(256)),
5913                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5914                        target_name: Some("b".repeat(256)),
5915                        ..Default::default()
5916                    }),
5917                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5918                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5919                            name: "b".repeat(256),
5920                            collection: None,
5921                        })),
5922                        source_name: Some("a".repeat(256)),
5923                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5924                        target_name: Some("b".repeat(256)),
5925                        ..Default::default()
5926                    }),
5927                ]);
5928                decl
5929            },
5930            result = Err(ErrorList::new(vec![
5931                Error::field_too_long(DeclType::ExposeService, "source.child.name"),
5932                Error::field_too_long(DeclType::ExposeService, "source_name"),
5933                Error::field_too_long(DeclType::ExposeService, "target_name"),
5934                Error::field_too_long(DeclType::ExposeProtocol, "source.child.name"),
5935                Error::field_too_long(DeclType::ExposeProtocol, "source_name"),
5936                Error::field_too_long(DeclType::ExposeProtocol, "target_name"),
5937                Error::field_too_long(DeclType::ExposeDirectory, "source.child.name"),
5938                Error::field_too_long(DeclType::ExposeDirectory, "source_name"),
5939                Error::field_too_long(DeclType::ExposeDirectory, "target_name"),
5940                Error::field_too_long(DeclType::ExposeRunner, "source.child.name"),
5941                Error::field_too_long(DeclType::ExposeRunner, "source_name"),
5942                Error::field_too_long(DeclType::ExposeRunner, "target_name"),
5943                Error::field_too_long(DeclType::ExposeResolver, "source.child.name"),
5944                Error::field_too_long(DeclType::ExposeResolver, "source_name"),
5945                Error::field_too_long(DeclType::ExposeResolver, "target_name"),
5946                Error::field_too_long(DeclType::ExposeDictionary, "source.child.name"),
5947                Error::field_too_long(DeclType::ExposeDictionary, "source_name"),
5948                Error::field_too_long(DeclType::ExposeDictionary, "target_name"),
5949            ])),
5950        },
5951        test_validate_exposes_invalid_child => {
5952            input = {
5953                let mut decl = new_component_decl();
5954                decl.exposes = Some(vec![
5955                    fdecl::Expose::Service(fdecl::ExposeService {
5956                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5957                            name: "netstack".to_string(),
5958                            collection: None,
5959                        })),
5960                        source_name: Some("fuchsia.logger.Log".to_string()),
5961                        target_name: Some("fuchsia.logger.Log".to_string()),
5962                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5963                        ..Default::default()
5964                    }),
5965                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5966                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5967                            name: "netstack".to_string(),
5968                            collection: None,
5969                        })),
5970                        source_name: Some("fuchsia.logger.LegacyLog".to_string()),
5971                        target_name: Some("fuchsia.logger.LegacyLog".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::Child(fdecl::ChildRef {
5977                            name: "netstack".to_string(),
5978                            collection: None,
5979                        })),
5980                        source_name: Some("data".to_string()),
5981                        target_name: Some("data".to_string()),
5982                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5983                        rights: Some(fio::Operations::CONNECT),
5984                        subdir: None,
5985                        ..Default::default()
5986                    }),
5987                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5988                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5989                            name: "netstack".to_string(),
5990                            collection: None,
5991                        })),
5992                        source_name: Some("elf".to_string()),
5993                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5994                        target_name: Some("elf".to_string()),
5995                        ..Default::default()
5996                    }),
5997                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5998                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5999                            name: "netstack".to_string(),
6000                            collection: None,
6001                        })),
6002                        source_name: Some("pkg".to_string()),
6003                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6004                        target_name: Some("pkg".to_string()),
6005                        ..Default::default()
6006                    }),
6007                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
6008                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6009                            name: "netstack".to_string(),
6010                            collection: None,
6011                        })),
6012                        source_name: Some("dict".to_string()),
6013                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6014                        target_name: Some("dict".to_string()),
6015                        ..Default::default()
6016                    }),
6017                ]);
6018                decl
6019            },
6020            result = Err(ErrorList::new(vec![
6021                Error::invalid_child(DeclType::ExposeService, "source", "netstack"),
6022                Error::invalid_child(DeclType::ExposeProtocol, "source", "netstack"),
6023                Error::invalid_child(DeclType::ExposeDirectory, "source", "netstack"),
6024                Error::invalid_child(DeclType::ExposeRunner, "source", "netstack"),
6025                Error::invalid_child(DeclType::ExposeResolver, "source", "netstack"),
6026                Error::invalid_child(DeclType::ExposeDictionary, "source", "netstack"),
6027            ])),
6028        },
6029        test_validate_exposes_invalid_source_capability => {
6030            input = {
6031                fdecl::Component {
6032                    exposes: Some(vec![
6033                        fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6034                            source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
6035                                name: "this-storage-doesnt-exist".to_string(),
6036                            })),
6037                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
6038                            target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
6039                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6040                            ..Default::default()
6041                        }),
6042                    ]),
6043                    ..new_component_decl()
6044                }
6045            },
6046            result = Err(ErrorList::new(vec![
6047                Error::invalid_capability(DeclType::ExposeProtocol, "source", "this-storage-doesnt-exist"),
6048            ])),
6049        },
6050        test_validate_exposes_duplicate_target => {
6051            input = {
6052                let mut decl = new_component_decl();
6053                decl.exposes = Some(vec![
6054                    fdecl::Expose::Service(fdecl::ExposeService {
6055                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6056                            name: "coll".into(),
6057                        })),
6058                        source_name: Some("netstack".to_string()),
6059                        target_name: Some("fuchsia.net.Stack".to_string()),
6060                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6061                        ..Default::default()
6062                    }),
6063                    fdecl::Expose::Service(fdecl::ExposeService {
6064                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6065                            name: "coll2".into(),
6066                        })),
6067                        source_name: Some("netstack2".to_string()),
6068                        target_name: Some("fuchsia.net.Stack".to_string()),
6069                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6070                        ..Default::default()
6071                    }),
6072                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6073                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6074                        source_name: Some("fonts".to_string()),
6075                        target_name: Some("fuchsia.fonts.Provider".to_string()),
6076                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6077                        ..Default::default()
6078                    }),
6079                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6080                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6081                        source_name: Some("fonts2".to_string()),
6082                        target_name: Some("fuchsia.fonts.Provider".to_string()),
6083                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6084                        ..Default::default()
6085                    }),
6086                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
6087                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6088                        source_name: Some("assets".to_string()),
6089                        target_name: Some("stuff".to_string()),
6090                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6091                        rights: None,
6092                        subdir: None,
6093                        ..Default::default()
6094                    }),
6095                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
6096                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6097                        source_name: Some("assets2".to_string()),
6098                        target_name: Some("stuff".to_string()),
6099                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6100                        rights: None,
6101                        subdir: None,
6102                        ..Default::default()
6103                    }),
6104                    fdecl::Expose::Runner(fdecl::ExposeRunner {
6105                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6106                        source_name: Some("source_elf".to_string()),
6107                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6108                        target_name: Some("elf".to_string()),
6109                        ..Default::default()
6110                    }),
6111                    fdecl::Expose::Runner(fdecl::ExposeRunner {
6112                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6113                        source_name: Some("source_elf".to_string()),
6114                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6115                        target_name: Some("elf".to_string()),
6116                        ..Default::default()
6117                    }),
6118                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
6119                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6120                        source_name: Some("source_pkg".to_string()),
6121                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6122                        target_name: Some("pkg".to_string()),
6123                        ..Default::default()
6124                    }),
6125                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
6126                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6127                        source_name: Some("source_pkg".to_string()),
6128                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6129                        target_name: Some("pkg".to_string()),
6130                        ..Default::default()
6131                    }),
6132                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
6133                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6134                        source_name: Some("source_dict".to_string()),
6135                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6136                        target_name: Some("dict".to_string()),
6137                        ..Default::default()
6138                    }),
6139                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
6140                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6141                        source_name: Some("source_dict".to_string()),
6142                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6143                        target_name: Some("dict".to_string()),
6144                        ..Default::default()
6145                    }),
6146                ]);
6147                decl.collections = Some(vec![
6148                    fdecl::Collection {
6149                        name: Some("coll".into()),
6150                        durability: Some(fdecl::Durability::Transient),
6151                        ..Default::default()
6152                    },
6153                    fdecl::Collection {
6154                        name: Some("coll2".into()),
6155                        durability: Some(fdecl::Durability::Transient),
6156                        ..Default::default()
6157                    },
6158                ]);
6159                decl.capabilities = Some(vec![
6160                    fdecl::Capability::Service(fdecl::Service {
6161                        name: Some("netstack".to_string()),
6162                        source_path: Some("/path".to_string()),
6163                        ..Default::default()
6164                    }),
6165                    fdecl::Capability::Service(fdecl::Service {
6166                        name: Some("netstack2".to_string()),
6167                        source_path: Some("/path".to_string()),
6168                        ..Default::default()
6169                    }),
6170                    fdecl::Capability::Protocol(fdecl::Protocol {
6171                        name: Some("fonts".to_string()),
6172                        source_path: Some("/path".to_string()),
6173                        ..Default::default()
6174                    }),
6175                    fdecl::Capability::Protocol(fdecl::Protocol {
6176                        name: Some("fonts2".to_string()),
6177                        source_path: Some("/path".to_string()),
6178                        ..Default::default()
6179                    }),
6180                    fdecl::Capability::Directory(fdecl::Directory {
6181                        name: Some("assets".to_string()),
6182                        source_path: Some("/path".to_string()),
6183                        rights: Some(fio::Operations::CONNECT),
6184                        ..Default::default()
6185                    }),
6186                    fdecl::Capability::Directory(fdecl::Directory {
6187                        name: Some("assets2".to_string()),
6188                        source_path: Some("/path".to_string()),
6189                        rights: Some(fio::Operations::CONNECT),
6190                        ..Default::default()
6191                    }),
6192                    fdecl::Capability::Runner(fdecl::Runner {
6193                        name: Some("source_elf".to_string()),
6194                        source_path: Some("/path".to_string()),
6195                        ..Default::default()
6196                    }),
6197                    fdecl::Capability::Resolver(fdecl::Resolver {
6198                        name: Some("source_pkg".to_string()),
6199                        source_path: Some("/path".to_string()),
6200                        ..Default::default()
6201                    }),
6202                    fdecl::Capability::Dictionary(fdecl::Dictionary {
6203                        name: Some("source_dict".to_string()),
6204                        ..Default::default()
6205                    }),
6206                ]);
6207                decl
6208            },
6209            result = Err(ErrorList::new(vec![
6210                // Duplicate services are allowed.
6211                Error::duplicate_field(DeclType::ExposeProtocol, "target_name",
6212                                    "fuchsia.fonts.Provider"),
6213                Error::duplicate_field(DeclType::ExposeDirectory, "target_name",
6214                                    "stuff"),
6215                Error::duplicate_field(DeclType::ExposeRunner, "target_name",
6216                                    "elf"),
6217                Error::duplicate_field(DeclType::ExposeResolver, "target_name", "pkg"),
6218                Error::duplicate_field(DeclType::ExposeDictionary, "target_name", "dict"),
6219            ])),
6220        },
6221        test_validate_exposes_invalid_capability_from_self => {
6222            input = {
6223                let mut decl = new_component_decl();
6224                decl.exposes = Some(vec![
6225                    fdecl::Expose::Service(fdecl::ExposeService {
6226                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6227                        source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
6228                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6229                        target_name: Some("foo".to_string()),
6230                        ..Default::default()
6231                    }),
6232                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6233                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6234                        source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
6235                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6236                        target_name: Some("bar".to_string()),
6237                        ..Default::default()
6238                    }),
6239                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
6240                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6241                        source_name: Some("dir".to_string()),
6242                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6243                        target_name: Some("assets".to_string()),
6244                        rights: None,
6245                        subdir: None,
6246                        ..Default::default()
6247                    }),
6248                    fdecl::Expose::Runner(fdecl::ExposeRunner {
6249                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6250                        source_name: Some("source_elf".to_string()),
6251                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6252                        target_name: Some("elf".to_string()),
6253                        ..Default::default()
6254                    }),
6255                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
6256                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6257                        source_name: Some("source_pkg".to_string()),
6258                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6259                        target_name: Some("pkg".to_string()),
6260                        ..Default::default()
6261                    }),
6262                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
6263                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6264                        source_name: Some("source_dict".to_string()),
6265                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6266                        target_name: Some("dict".to_string()),
6267                        ..Default::default()
6268                    }),
6269                    fdecl::Expose::Config(fdecl::ExposeConfiguration {
6270                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6271                        source_name: Some("source_config".to_string()),
6272                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6273                        target_name: Some("config".to_string()),
6274                        ..Default::default()
6275                    }),
6276                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6277                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6278                        source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
6279                        source_dictionary: Some("dict/inner".to_string()),
6280                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6281                        target_name: Some("baz".to_string()),
6282                        ..Default::default()
6283                    }),
6284                ]);
6285                decl
6286            },
6287            result = Err(ErrorList::new(vec![
6288                Error::invalid_capability(
6289                    DeclType::ExposeService,
6290                    "source",
6291                    "fuchsia.some.library.SomeProtocol"),
6292                Error::invalid_capability(
6293                    DeclType::ExposeProtocol,
6294                    "source",
6295                    "fuchsia.some.library.SomeProtocol"),
6296                Error::invalid_capability(DeclType::ExposeDirectory, "source", "dir"),
6297                Error::invalid_capability(DeclType::ExposeRunner, "source", "source_elf"),
6298                Error::invalid_capability(DeclType::ExposeResolver, "source", "source_pkg"),
6299                Error::invalid_capability(DeclType::ExposeDictionary, "source", "source_dict"),
6300                Error::invalid_capability(DeclType::ExposeConfig, "source", "source_config"),
6301                Error::invalid_capability(DeclType::ExposeProtocol, "source", "dict"),
6302            ])),
6303        },
6304
6305        test_validate_exposes_availability_service => {
6306            input = {
6307                let mut decl = generate_expose_different_source_and_availability_decl(
6308                    |source, availability, target_name|
6309                        fdecl::Expose::Service(fdecl::ExposeService {
6310                            source: Some(source),
6311                            source_name: Some("fuchsia.examples.Echo".to_string()),
6312                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6313                            target_name: Some(target_name.to_string()),
6314                            availability: Some(availability),
6315                            ..Default::default()
6316                        })
6317                );
6318                decl.capabilities = Some(vec![
6319                    fdecl::Capability::Service(fdecl::Service {
6320                        name: Some("fuchsia.examples.Echo".to_string()),
6321                        source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
6322                        ..Default::default()
6323                    }),
6324                ]);
6325                decl
6326            },
6327            result = {
6328                Err(ErrorList::new(vec![
6329                    Error::availability_must_be_optional(
6330                        DeclType::ExposeService,
6331                        "availability",
6332                        Some(&"fuchsia.examples.Echo".to_string()),
6333                    ),
6334                    Error::availability_must_be_optional(
6335                        DeclType::ExposeService,
6336                        "availability",
6337                        Some(&"fuchsia.examples.Echo".to_string()),
6338                    ),
6339                ]))
6340            },
6341        },
6342        test_validate_exposes_availability_protocol => {
6343            input = {
6344                let mut decl = generate_expose_different_source_and_availability_decl(
6345                    |source, availability, target_name|
6346                        fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6347                            source: Some(source),
6348                            source_name: Some("fuchsia.examples.Echo".to_string()),
6349                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6350                            target_name: Some(target_name.to_string()),
6351                            availability: Some(availability),
6352                            ..Default::default()
6353                        })
6354                );
6355                decl.capabilities = Some(vec![
6356                    fdecl::Capability::Protocol(fdecl::Protocol {
6357                        name: Some("fuchsia.examples.Echo".to_string()),
6358                        source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
6359                        ..Default::default()
6360                    }),
6361                ]);
6362                decl
6363            },
6364            result = {
6365                Err(ErrorList::new(vec![
6366                    Error::availability_must_be_optional(
6367                        DeclType::ExposeProtocol,
6368                        "availability",
6369                        Some(&"fuchsia.examples.Echo".to_string()),
6370                    ),
6371                    Error::availability_must_be_optional(
6372                        DeclType::ExposeProtocol,
6373                        "availability",
6374                        Some(&"fuchsia.examples.Echo".to_string()),
6375                    ),
6376                ]))
6377            },
6378        },
6379        test_validate_exposes_availability_directory => {
6380            input = {
6381                let mut decl = generate_expose_different_source_and_availability_decl(
6382                    |source, availability, target_name|
6383                        fdecl::Expose::Directory(fdecl::ExposeDirectory {
6384                            source: Some(source),
6385                            source_name: Some("fuchsia.examples.Echo".to_string()),
6386                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6387                            target_name: Some(target_name.to_string()),
6388                            availability: Some(availability),
6389                            ..Default::default()
6390                        })
6391                );
6392                decl.capabilities = Some(vec![
6393                    fdecl::Capability::Directory(fdecl::Directory {
6394                        name: Some("fuchsia.examples.Echo".to_string()),
6395                        source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
6396                        rights: Some(fio::Operations::READ_BYTES),
6397                        ..Default::default()
6398                    }),
6399                ]);
6400                decl
6401            },
6402            result = {
6403                Err(ErrorList::new(vec![
6404                    Error::availability_must_be_optional(
6405                        DeclType::ExposeDirectory,
6406                        "availability",
6407                        Some(&"fuchsia.examples.Echo".to_string()),
6408                    ),
6409                    Error::availability_must_be_optional(
6410                        DeclType::ExposeDirectory,
6411                        "availability",
6412                        Some(&"fuchsia.examples.Echo".to_string()),
6413                    ),
6414                ]))
6415            },
6416        },
6417
6418        // offers
6419        test_validate_offers_empty => {
6420            input = {
6421                let mut decl = new_component_decl();
6422                decl.offers = Some(vec![
6423                    fdecl::Offer::Service(fdecl::OfferService {
6424                        source: None,
6425                        source_name: None,
6426                        target: None,
6427                        target_name: None,
6428                        ..Default::default()
6429                    }),
6430                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6431                        source: None,
6432                        source_name: None,
6433                        target: None,
6434                        target_name: None,
6435                        dependency_type: None,
6436                        ..Default::default()
6437                    }),
6438                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6439                        source: None,
6440                        source_name: None,
6441                        target: None,
6442                        target_name: None,
6443                        rights: None,
6444                        subdir: None,
6445                        dependency_type: None,
6446                        ..Default::default()
6447                    }),
6448                    fdecl::Offer::Storage(fdecl::OfferStorage {
6449                        source_name: None,
6450                        source: None,
6451                        target: None,
6452                        target_name: None,
6453                        ..Default::default()
6454                    }),
6455                    fdecl::Offer::Runner(fdecl::OfferRunner {
6456                        source: None,
6457                        source_name: None,
6458                        target: None,
6459                        target_name: None,
6460                        ..Default::default()
6461                    }),
6462                    fdecl::Offer::Resolver(fdecl::OfferResolver {
6463                        ..Default::default()
6464                    }),
6465                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6466                        ..Default::default()
6467                    }),
6468                ]);
6469                decl
6470            },
6471            // TODO(dgonyeo): we need to handle the availability being unset until we've soft
6472            // migrated all manifests
6473            result = Err(ErrorList::new(vec![
6474                Error::missing_field(DeclType::OfferService, "source"),
6475                Error::missing_field(DeclType::OfferService, "source_name"),
6476                Error::missing_field(DeclType::OfferService, "target"),
6477                Error::missing_field(DeclType::OfferService, "target_name"),
6478                //Error::missing_field(DeclType::OfferService, "availability"),
6479                Error::missing_field(DeclType::OfferProtocol, "source"),
6480                Error::missing_field(DeclType::OfferProtocol, "source_name"),
6481                Error::missing_field(DeclType::OfferProtocol, "target"),
6482                Error::missing_field(DeclType::OfferProtocol, "target_name"),
6483                Error::missing_field(DeclType::OfferProtocol, "dependency_type"),
6484                //Error::missing_field(DeclType::OfferProtocol, "availability"),
6485                Error::missing_field(DeclType::OfferDirectory, "source"),
6486                Error::missing_field(DeclType::OfferDirectory, "source_name"),
6487                Error::missing_field(DeclType::OfferDirectory, "target"),
6488                Error::missing_field(DeclType::OfferDirectory, "target_name"),
6489                Error::missing_field(DeclType::OfferDirectory, "dependency_type"),
6490                //Error::missing_field(DeclType::OfferDirectory, "availability"),
6491                Error::missing_field(DeclType::OfferStorage, "source"),
6492                Error::missing_field(DeclType::OfferStorage, "source_name"),
6493                Error::missing_field(DeclType::OfferStorage, "target"),
6494                Error::missing_field(DeclType::OfferStorage, "target_name"),
6495                //Error::missing_field(DeclType::OfferStorage, "availability"),
6496                Error::missing_field(DeclType::OfferRunner, "source"),
6497                Error::missing_field(DeclType::OfferRunner, "source_name"),
6498                Error::missing_field(DeclType::OfferRunner, "target"),
6499                Error::missing_field(DeclType::OfferRunner, "target_name"),
6500                //Error::missing_field(DeclType::OfferRunner, "availability"),
6501                Error::missing_field(DeclType::OfferResolver, "source"),
6502                Error::missing_field(DeclType::OfferResolver, "source_name"),
6503                Error::missing_field(DeclType::OfferResolver, "target"),
6504                Error::missing_field(DeclType::OfferResolver, "target_name"),
6505                Error::missing_field(DeclType::OfferDictionary, "source"),
6506                Error::missing_field(DeclType::OfferDictionary, "source_name"),
6507                Error::missing_field(DeclType::OfferDictionary, "target"),
6508                Error::missing_field(DeclType::OfferDictionary, "target_name"),
6509                Error::missing_field(DeclType::OfferDictionary, "dependency_type"),
6510            ])),
6511        },
6512        test_validate_offers_long_identifiers => {
6513            input = {
6514                let mut decl = new_component_decl();
6515                decl.offers = Some(vec![
6516                    fdecl::Offer::Service(fdecl::OfferService {
6517                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6518                            name: "a".repeat(256),
6519                            collection: None,
6520                        })),
6521                        source_name: Some(format!("{}", "a".repeat(256))),
6522                        target: Some(fdecl::Ref::Child(
6523                        fdecl::ChildRef {
6524                            name: "b".repeat(256),
6525                            collection: None,
6526                        }
6527                        )),
6528                        target_name: Some(format!("{}", "b".repeat(256))),
6529                        ..Default::default()
6530                    }),
6531                    fdecl::Offer::Service(fdecl::OfferService {
6532                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6533                        source_name: Some("a".to_string()),
6534                        target: Some(fdecl::Ref::Collection(
6535                        fdecl::CollectionRef {
6536                            name: "b".repeat(256),
6537                        }
6538                        )),
6539                        target_name: Some(format!("{}", "b".repeat(256))),
6540                        ..Default::default()
6541                    }),
6542                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6543                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6544                            name: "a".repeat(256),
6545                            collection: None,
6546                        })),
6547                        source_name: Some(format!("{}", "a".repeat(256))),
6548                        target: Some(fdecl::Ref::Child(
6549                        fdecl::ChildRef {
6550                            name: "b".repeat(256),
6551                            collection: None,
6552                        }
6553                        )),
6554                        target_name: Some(format!("{}", "b".repeat(256))),
6555                        dependency_type: Some(fdecl::DependencyType::Strong),
6556                        ..Default::default()
6557                    }),
6558                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6559                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6560                        source_name: Some("a".to_string()),
6561                        target: Some(fdecl::Ref::Collection(
6562                        fdecl::CollectionRef {
6563                            name: "b".repeat(256),
6564                        }
6565                        )),
6566                        target_name: Some(format!("{}", "b".repeat(256))),
6567                        dependency_type: Some(fdecl::DependencyType::Weak),
6568                        ..Default::default()
6569                    }),
6570                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6571                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6572                            name: "a".repeat(256),
6573                            collection: None,
6574                        })),
6575                        source_name: Some(format!("{}", "a".repeat(256))),
6576                        target: Some(fdecl::Ref::Child(
6577                        fdecl::ChildRef {
6578                            name: "b".repeat(256),
6579                            collection: None,
6580                        }
6581                        )),
6582                        target_name: Some(format!("{}", "b".repeat(256))),
6583                        rights: Some(fio::Operations::CONNECT),
6584                        subdir: None,
6585                        dependency_type: Some(fdecl::DependencyType::Strong),
6586                        ..Default::default()
6587                    }),
6588                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6589                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6590                        source_name: Some("a".to_string()),
6591                        target: Some(fdecl::Ref::Collection(
6592                        fdecl::CollectionRef {
6593                            name: "b".repeat(256),
6594                        }
6595                        )),
6596                        target_name: Some(format!("{}", "b".repeat(256))),
6597                        rights: Some(fio::Operations::CONNECT),
6598                        subdir: None,
6599                        dependency_type: Some(fdecl::DependencyType::Weak),
6600                        ..Default::default()
6601                    }),
6602                    fdecl::Offer::Storage(fdecl::OfferStorage {
6603                        source_name: Some("data".to_string()),
6604                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6605                        target: Some(fdecl::Ref::Child(
6606                            fdecl::ChildRef {
6607                                name: "b".repeat(256),
6608                                collection: None,
6609                            }
6610                        )),
6611                        target_name: Some("data".to_string()),
6612                        ..Default::default()
6613                    }),
6614                    fdecl::Offer::Storage(fdecl::OfferStorage {
6615                        source_name: Some("data".to_string()),
6616                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6617                        target: Some(fdecl::Ref::Collection(
6618                            fdecl::CollectionRef { name: "b".repeat(256) }
6619                        )),
6620                        target_name: Some("data".to_string()),
6621                        ..Default::default()
6622                    }),
6623                    fdecl::Offer::Runner(fdecl::OfferRunner {
6624                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6625                            name: "a".repeat(256),
6626                            collection: None,
6627                        })),
6628                        source_name: Some("b".repeat(256)),
6629                        target: Some(fdecl::Ref::Collection(
6630                        fdecl::CollectionRef {
6631                            name: "c".repeat(256),
6632                        }
6633                        )),
6634                        target_name: Some("d".repeat(256)),
6635                        ..Default::default()
6636                    }),
6637                    fdecl::Offer::Resolver(fdecl::OfferResolver {
6638                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6639                            name: "a".repeat(256),
6640                            collection: None,
6641                        })),
6642                        source_name: Some("b".repeat(256)),
6643                        target: Some(fdecl::Ref::Collection(
6644                            fdecl::CollectionRef {
6645                                name: "c".repeat(256),
6646                            }
6647                        )),
6648                        target_name: Some("d".repeat(256)),
6649                        ..Default::default()
6650                    }),
6651                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6652                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6653                            name: "a".repeat(256),
6654                            collection: None,
6655                        })),
6656                        source_name: Some("b".repeat(256)),
6657                        target: Some(fdecl::Ref::Collection(
6658                            fdecl::CollectionRef {
6659                                name: "c".repeat(256),
6660                            }
6661                        )),
6662                        target_name: Some("d".repeat(256)),
6663                        dependency_type: Some(fdecl::DependencyType::Strong),
6664                        ..Default::default()
6665                    }),
6666                ]);
6667                decl
6668            },
6669            result = Err(ErrorList::new(vec![
6670                Error::field_too_long(DeclType::OfferService, "source.child.name"),
6671                Error::field_too_long(DeclType::OfferService, "source_name"),
6672                Error::field_too_long(DeclType::OfferService, "target.child.name"),
6673                Error::field_too_long(DeclType::OfferService, "target_name"),
6674                Error::field_too_long(DeclType::OfferService, "target.collection.name"),
6675                Error::field_too_long(DeclType::OfferService, "target_name"),
6676                Error::field_too_long(DeclType::OfferProtocol, "source.child.name"),
6677                Error::field_too_long(DeclType::OfferProtocol, "source_name"),
6678                Error::field_too_long(DeclType::OfferProtocol, "target.child.name"),
6679                Error::field_too_long(DeclType::OfferProtocol, "target_name"),
6680                Error::field_too_long(DeclType::OfferProtocol, "target.collection.name"),
6681                Error::field_too_long(DeclType::OfferProtocol, "target_name"),
6682                Error::field_too_long(DeclType::OfferDirectory, "source.child.name"),
6683                Error::field_too_long(DeclType::OfferDirectory, "source_name"),
6684                Error::field_too_long(DeclType::OfferDirectory, "target.child.name"),
6685                Error::field_too_long(DeclType::OfferDirectory, "target_name"),
6686                Error::field_too_long(DeclType::OfferDirectory, "target.collection.name"),
6687                Error::field_too_long(DeclType::OfferDirectory, "target_name"),
6688                Error::field_too_long(DeclType::OfferStorage, "target.child.name"),
6689                Error::field_too_long(DeclType::OfferStorage, "target.collection.name"),
6690                Error::field_too_long(DeclType::OfferRunner, "source.child.name"),
6691                Error::field_too_long(DeclType::OfferRunner, "source_name"),
6692                Error::field_too_long(DeclType::OfferRunner, "target.collection.name"),
6693                Error::field_too_long(DeclType::OfferRunner, "target_name"),
6694                Error::field_too_long(DeclType::OfferResolver, "source.child.name"),
6695                Error::field_too_long(DeclType::OfferResolver, "source_name"),
6696                Error::field_too_long(DeclType::OfferResolver, "target.collection.name"),
6697                Error::field_too_long(DeclType::OfferResolver, "target_name"),
6698                Error::field_too_long(DeclType::OfferDictionary, "source.child.name"),
6699                Error::field_too_long(DeclType::OfferDictionary, "source_name"),
6700                Error::field_too_long(DeclType::OfferDictionary, "target.collection.name"),
6701                Error::field_too_long(DeclType::OfferDictionary, "target_name"),
6702            ])),
6703        },
6704        test_validate_offers_extraneous => {
6705            input = {
6706                let mut decl = new_component_decl();
6707                decl.offers = Some(vec![
6708                    fdecl::Offer::Service(fdecl::OfferService {
6709                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6710                            name: "logger".to_string(),
6711                            collection: Some("modular".to_string()),
6712                        })),
6713                        source_name: Some("fuchsia.logger.Log".to_string()),
6714                        target: Some(fdecl::Ref::Child(
6715                            fdecl::ChildRef {
6716                                name: "netstack".to_string(),
6717                                collection: Some("modular".to_string()),
6718                            }
6719                        )),
6720                        target_name: Some("fuchsia.logger.Log".to_string()),
6721                        ..Default::default()
6722                    }),
6723                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6724                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6725                            name: "logger".to_string(),
6726                            collection: Some("modular".to_string()),
6727                        })),
6728                        source_name: Some("fuchsia.logger.Log".to_string()),
6729                        target: Some(fdecl::Ref::Child(
6730                            fdecl::ChildRef {
6731                                name: "netstack".to_string(),
6732                                collection: Some("modular".to_string()),
6733                            }
6734                        )),
6735                        target_name: Some("fuchsia.logger.Log".to_string()),
6736                        dependency_type: Some(fdecl::DependencyType::Strong),
6737                        ..Default::default()
6738                    }),
6739                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6740                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6741                            name: "logger".to_string(),
6742                            collection: Some("modular".to_string()),
6743                        })),
6744                        source_name: Some("assets".to_string()),
6745                        target: Some(fdecl::Ref::Child(
6746                            fdecl::ChildRef {
6747                                name: "netstack".to_string(),
6748                                collection: Some("modular".to_string()),
6749                            }
6750                        )),
6751                        target_name: Some("assets".to_string()),
6752                        rights: Some(fio::Operations::CONNECT),
6753                        subdir: None,
6754                        dependency_type: Some(fdecl::DependencyType::Weak),
6755                        ..Default::default()
6756                    }),
6757                    fdecl::Offer::Storage(fdecl::OfferStorage {
6758                        source_name: Some("data".to_string()),
6759                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{ })),
6760                        target: Some(fdecl::Ref::Child(
6761                            fdecl::ChildRef {
6762                                name: "netstack".to_string(),
6763                                collection: Some("modular".to_string()),
6764                            }
6765                        )),
6766                        target_name: Some("data".to_string()),
6767                        ..Default::default()
6768                    }),
6769                    fdecl::Offer::Runner(fdecl::OfferRunner {
6770                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6771                            name: "logger".to_string(),
6772                            collection: Some("modular".to_string()),
6773                        })),
6774                        source_name: Some("elf".to_string()),
6775                        target: Some(fdecl::Ref::Child(
6776                            fdecl::ChildRef {
6777                                name: "netstack".to_string(),
6778                                collection: Some("modular".to_string()),
6779                            }
6780                        )),
6781                        target_name: Some("elf".to_string()),
6782                        ..Default::default()
6783                    }),
6784                    fdecl::Offer::Resolver(fdecl::OfferResolver {
6785                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6786                            name: "logger".to_string(),
6787                            collection: Some("modular".to_string()),
6788                        })),
6789                        source_name: Some("pkg".to_string()),
6790                        target: Some(fdecl::Ref::Child(
6791                            fdecl::ChildRef {
6792                                name: "netstack".to_string(),
6793                                collection: Some("modular".to_string()),
6794                            }
6795                        )),
6796                        target_name: Some("pkg".to_string()),
6797                        ..Default::default()
6798                    }),
6799                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6800                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6801                            name: "logger".to_string(),
6802                            collection: Some("modular".to_string()),
6803                        })),
6804                        source_name: Some("dict".to_string()),
6805                        target: Some(fdecl::Ref::Child(
6806                            fdecl::ChildRef {
6807                                name: "netstack".to_string(),
6808                                collection: Some("modular".to_string()),
6809                            }
6810                        )),
6811                        target_name: Some("dict".to_string()),
6812                        dependency_type: Some(fdecl::DependencyType::Strong),
6813                        ..Default::default()
6814                    }),
6815                ]);
6816                decl.capabilities = Some(vec![
6817                    fdecl::Capability::Protocol(fdecl::Protocol {
6818                        name: Some("fuchsia.logger.Log".to_string()),
6819                        source_path: Some("/svc/logger".to_string()),
6820                        ..Default::default()
6821                    }),
6822                    fdecl::Capability::Directory(fdecl::Directory {
6823                        name: Some("assets".to_string()),
6824                        source_path: Some("/data/assets".to_string()),
6825                        rights: Some(fio::Operations::CONNECT),
6826                        ..Default::default()
6827                    }),
6828                ]);
6829                decl
6830            },
6831            result = Err(ErrorList::new(vec![
6832                Error::extraneous_field(DeclType::OfferService, "source.child.collection"),
6833                Error::extraneous_field(DeclType::OfferService, "target.child.collection"),
6834                Error::extraneous_field(DeclType::OfferProtocol, "source.child.collection"),
6835                Error::extraneous_field(DeclType::OfferProtocol, "target.child.collection"),
6836                Error::extraneous_field(DeclType::OfferDirectory, "source.child.collection"),
6837                Error::extraneous_field(DeclType::OfferDirectory, "target.child.collection"),
6838                Error::extraneous_field(DeclType::OfferStorage, "target.child.collection"),
6839                Error::extraneous_field(DeclType::OfferRunner, "source.child.collection"),
6840                Error::extraneous_field(DeclType::OfferRunner, "target.child.collection"),
6841                Error::extraneous_field(DeclType::OfferResolver, "source.child.collection"),
6842                Error::extraneous_field(DeclType::OfferResolver, "target.child.collection"),
6843                Error::extraneous_field(DeclType::OfferDictionary, "source.child.collection"),
6844                Error::extraneous_field(DeclType::OfferDictionary, "target.child.collection"),
6845            ])),
6846        },
6847        test_validate_offers_invalid_filtered_service_fields => {
6848            input = {
6849                let mut decl = new_component_decl();
6850                decl.offers = Some(vec![
6851                    fdecl::Offer::Service(fdecl::OfferService {
6852                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6853                        source_name: Some("fuchsia.logger.Log".to_string()),
6854                        target: Some(fdecl::Ref::Child(
6855                            fdecl::ChildRef {
6856                                name: "logger".to_string(),
6857                                collection: None,
6858                            }
6859                        )),
6860                        target_name: Some("fuchsia.logger.Log".to_string()),
6861                        source_instance_filter: Some(vec![]),
6862                        ..Default::default()
6863                    }),
6864                    fdecl::Offer::Service(fdecl::OfferService {
6865                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6866                        source_name: Some("fuchsia.logger.Log".to_string()),
6867                        target: Some(fdecl::Ref::Child(
6868                            fdecl::ChildRef {
6869                                name: "logger".to_string(),
6870                                collection: None,
6871                            }
6872                        )),
6873                        target_name: Some("fuchsia.logger.Log2".to_string()),
6874                        source_instance_filter: Some(vec!["^badname".to_string()]),
6875                        ..Default::default()
6876                    }),
6877                    fdecl::Offer::Service(fdecl::OfferService {
6878                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6879                        source_name: Some("fuchsia.logger.Log".to_string()),
6880                        target: Some(fdecl::Ref::Child(
6881                            fdecl::ChildRef {
6882                                name: "logger".to_string(),
6883                                collection: None,
6884                            }
6885                        )),
6886                        target_name: Some("fuchsia.logger.Log1".to_string()),
6887                        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()}]),
6888                        ..Default::default()
6889                    }),
6890                    fdecl::Offer::Service(fdecl::OfferService {
6891                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6892                        source_name: Some("fuchsia.logger.Log".to_string()),
6893                        target: Some(fdecl::Ref::Child(
6894                            fdecl::ChildRef {
6895                                name: "logger".to_string(),
6896                                collection: None,
6897                            }
6898                        )),
6899                        target_name: Some("fuchsia.logger.Log3".to_string()),
6900                        renamed_instances: Some(vec![
6901                            fdecl::NameMapping {
6902                                source_name: "^badname".to_string(),
6903                                target_name: "^badname".to_string(),
6904                            }
6905                        ]),
6906                        ..Default::default()
6907                    })
6908                ]);
6909                decl.children = Some(vec![
6910                    fdecl::Child {
6911                        name: Some("logger".to_string()),
6912                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
6913                        startup: Some(fdecl::StartupMode::Lazy),
6914                        on_terminate: None,
6915                        environment: None,
6916                        ..Default::default()
6917                    },
6918                ]);
6919                decl
6920            },
6921            result = Err(ErrorList::new(vec![
6922                Error::invalid_field(DeclType::OfferService, "source_instance_filter"),
6923                Error::invalid_field(DeclType::OfferService, "source_instance_filter"),
6924                Error::invalid_field(DeclType::OfferService, "renamed_instances"),
6925                Error::invalid_field(DeclType::OfferService, "renamed_instances.source_name"),
6926                Error::invalid_field(DeclType::OfferService, "renamed_instances.target_name"),
6927            ])),
6928        },
6929        test_validate_offers_invalid_identifiers => {
6930            input = {
6931                let mut decl = new_component_decl();
6932                decl.offers = Some(vec![
6933                    fdecl::Offer::Service(fdecl::OfferService {
6934                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6935                            name: "^bad".to_string(),
6936                            collection: None,
6937                        })),
6938                        source_name: Some("foo/".to_string()),
6939                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6940                            name: "%bad".to_string(),
6941                            collection: None,
6942                        })),
6943                        target_name: Some("/".to_string()),
6944                        ..Default::default()
6945                    }),
6946                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6947                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6948                            name: "^bad".to_string(),
6949                            collection: None,
6950                        })),
6951                        source_name: Some("foo/".to_string()),
6952                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6953                            name: "%bad".to_string(),
6954                            collection: None,
6955                        })),
6956                        target_name: Some("/".to_string()),
6957                        dependency_type: Some(fdecl::DependencyType::Strong),
6958                        ..Default::default()
6959                    }),
6960                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6961                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6962                            name: "^bad".to_string(),
6963                            collection: None,
6964                        })),
6965                        source_name: Some("foo/".to_string()),
6966                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6967                            name: "%bad".to_string(),
6968                            collection: None,
6969                        })),
6970                        target_name: Some("/".to_string()),
6971                        rights: Some(fio::Operations::CONNECT),
6972                        subdir: Some("/foo".to_string()),
6973                        dependency_type: Some(fdecl::DependencyType::Strong),
6974                        ..Default::default()
6975                    }),
6976                    fdecl::Offer::Runner(fdecl::OfferRunner {
6977                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6978                            name: "^bad".to_string(),
6979                            collection: None,
6980                        })),
6981                        source_name: Some("/path".to_string()),
6982                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6983                            name: "%bad".to_string(),
6984                            collection: None,
6985                        })),
6986                        target_name: Some("elf!".to_string()),
6987                        ..Default::default()
6988                    }),
6989                    fdecl::Offer::Resolver(fdecl::OfferResolver {
6990                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6991                            name: "^bad".to_string(),
6992                            collection: None,
6993                        })),
6994                        source_name: Some("/path".to_string()),
6995                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6996                            name: "%bad".to_string(),
6997                            collection: None,
6998                        })),
6999                        target_name: Some("pkg!".to_string()),
7000                        ..Default::default()
7001                    }),
7002                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7003                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7004                            name: "^bad".to_string(),
7005                            collection: None,
7006                        })),
7007                        source_name: Some("/path".to_string()),
7008                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7009                            name: "%bad".to_string(),
7010                            collection: None,
7011                        })),
7012                        target_name: Some("pkg!".to_string()),
7013                        dependency_type: Some(fdecl::DependencyType::Strong),
7014                        ..Default::default()
7015                    }),
7016                ]);
7017                decl
7018            },
7019            result = Err(ErrorList::new(vec![
7020                Error::invalid_field(DeclType::OfferService, "source.child.name"),
7021                Error::invalid_field(DeclType::OfferService, "source_name"),
7022                Error::invalid_field(DeclType::OfferService, "target.child.name"),
7023                Error::invalid_field(DeclType::OfferService, "target_name"),
7024                Error::invalid_field(DeclType::OfferProtocol, "source.child.name"),
7025                Error::invalid_field(DeclType::OfferProtocol, "source_name"),
7026                Error::invalid_field(DeclType::OfferProtocol, "target.child.name"),
7027                Error::invalid_field(DeclType::OfferProtocol, "target_name"),
7028                Error::invalid_field(DeclType::OfferDirectory, "source.child.name"),
7029                Error::invalid_field(DeclType::OfferDirectory, "source_name"),
7030                Error::invalid_field(DeclType::OfferDirectory, "target.child.name"),
7031                Error::invalid_field(DeclType::OfferDirectory, "target_name"),
7032                Error::invalid_field(DeclType::OfferDirectory, "subdir"),
7033                Error::invalid_field(DeclType::OfferRunner, "source.child.name"),
7034                Error::invalid_field(DeclType::OfferRunner, "source_name"),
7035                Error::invalid_field(DeclType::OfferRunner, "target.child.name"),
7036                Error::invalid_field(DeclType::OfferRunner, "target_name"),
7037                Error::invalid_field(DeclType::OfferResolver, "source.child.name"),
7038                Error::invalid_field(DeclType::OfferResolver, "source_name"),
7039                Error::invalid_field(DeclType::OfferResolver, "target.child.name"),
7040                Error::invalid_field(DeclType::OfferResolver, "target_name"),
7041                Error::invalid_field(DeclType::OfferDictionary, "source.child.name"),
7042                Error::invalid_field(DeclType::OfferDictionary, "source_name"),
7043                Error::invalid_field(DeclType::OfferDictionary, "target.child.name"),
7044                Error::invalid_field(DeclType::OfferDictionary, "target_name"),
7045            ])),
7046        },
7047        test_validate_offers_target_equals_source => {
7048            input = {
7049                let mut decl = new_component_decl();
7050                decl.offers = Some(vec![
7051                    fdecl::Offer::Service(fdecl::OfferService {
7052                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7053                            name: "logger".to_string(),
7054                            collection: None,
7055                        })),
7056                        source_name: Some("logger".to_string()),
7057                        target: Some(fdecl::Ref::Child(
7058                        fdecl::ChildRef {
7059                            name: "logger".to_string(),
7060                            collection: None,
7061                        }
7062                        )),
7063                        target_name: Some("logger".to_string()),
7064                        ..Default::default()
7065                    }),
7066                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7067                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7068                            name: "logger".to_string(),
7069                            collection: None,
7070                        })),
7071                        source_name: Some("legacy_logger".to_string()),
7072                        target: Some(fdecl::Ref::Child(
7073                        fdecl::ChildRef {
7074                            name: "logger".to_string(),
7075                            collection: None,
7076                        }
7077                        )),
7078                        target_name: Some("weak_legacy_logger".to_string()),
7079                        dependency_type: Some(fdecl::DependencyType::Weak),
7080                        ..Default::default()
7081                    }),
7082                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7083                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7084                            name: "logger".to_string(),
7085                            collection: None,
7086                        })),
7087                        source_name: Some("legacy_logger".to_string()),
7088                        target: Some(fdecl::Ref::Child(
7089                        fdecl::ChildRef {
7090                            name: "logger".to_string(),
7091                            collection: None,
7092                        }
7093                        )),
7094                        target_name: Some("strong_legacy_logger".to_string()),
7095                        dependency_type: Some(fdecl::DependencyType::Strong),
7096                        ..Default::default()
7097                    }),
7098                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7099                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7100                            name: "logger".to_string(),
7101                            collection: None,
7102                        })),
7103                        source_name: Some("assets".to_string()),
7104                        target: Some(fdecl::Ref::Child(
7105                        fdecl::ChildRef {
7106                            name: "logger".to_string(),
7107                            collection: None,
7108                        }
7109                        )),
7110                        target_name: Some("assets".to_string()),
7111                        rights: Some(fio::Operations::CONNECT),
7112                        subdir: None,
7113                        dependency_type: Some(fdecl::DependencyType::Strong),
7114                        ..Default::default()
7115                    }),
7116                    fdecl::Offer::Runner(fdecl::OfferRunner {
7117                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7118                            name: "logger".to_string(),
7119                            collection: None,
7120                        })),
7121                        source_name: Some("web".to_string()),
7122                        target: Some(fdecl::Ref::Child(
7123                        fdecl::ChildRef {
7124                            name: "logger".to_string(),
7125                            collection: None,
7126                        }
7127                        )),
7128                        target_name: Some("web".to_string()),
7129                        ..Default::default()
7130                    }),
7131                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7132                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7133                            name: "logger".to_string(),
7134                            collection: None,
7135                        })),
7136                        source_name: Some("pkg".to_string()),
7137                        target: Some(fdecl::Ref::Child(
7138                        fdecl::ChildRef {
7139                            name: "logger".to_string(),
7140                            collection: None,
7141                        }
7142                        )),
7143                        target_name: Some("pkg".to_string()),
7144                        ..Default::default()
7145                    }),
7146                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7147                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7148                            name: "logger".to_string(),
7149                            collection: None,
7150                        })),
7151                        source_name: Some("dict".to_string()),
7152                        target: Some(fdecl::Ref::Child(
7153                        fdecl::ChildRef {
7154                            name: "logger".to_string(),
7155                            collection: None,
7156                        }
7157                        )),
7158                        target_name: Some("dict".to_string()),
7159                        dependency_type: Some(fdecl::DependencyType::Strong),
7160                        ..Default::default()
7161                    }),
7162                ]);
7163                decl.children = Some(vec![fdecl::Child{
7164                    name: Some("logger".to_string()),
7165                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
7166                    startup: Some(fdecl::StartupMode::Lazy),
7167                    on_terminate: None,
7168                    environment: None,
7169                    ..Default::default()
7170                }]);
7171                decl
7172            },
7173            result = Err(ErrorList::new(vec![
7174                Error::dependency_cycle("{{child logger -> child logger}}".to_string()),
7175            ])),
7176        },
7177        test_validate_offers_storage_target_equals_source => {
7178            input = fdecl::Component {
7179                offers: Some(vec![
7180                    fdecl::Offer::Storage(fdecl::OfferStorage {
7181                        source_name: Some("data".to_string()),
7182                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef { })),
7183                        target: Some(fdecl::Ref::Child(
7184                            fdecl::ChildRef {
7185                                name: "logger".to_string(),
7186                                collection: None,
7187                            }
7188                        )),
7189                        target_name: Some("data".to_string()),
7190                        ..Default::default()
7191                    })
7192                ]),
7193                capabilities: Some(vec![
7194                    fdecl::Capability::Storage(fdecl::Storage {
7195                        name: Some("data".to_string()),
7196                        backing_dir: Some("minfs".to_string()),
7197                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7198                            name: "logger".to_string(),
7199                            collection: None,
7200                        })),
7201                        subdir: None,
7202                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
7203                        ..Default::default()
7204                    }),
7205                ]),
7206                children: Some(vec![
7207                    fdecl::Child {
7208                        name: Some("logger".to_string()),
7209                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
7210                        startup: Some(fdecl::StartupMode::Lazy),
7211                        on_terminate: None,
7212                        environment: None,
7213                        ..Default::default()
7214                    },
7215                ]),
7216                ..new_component_decl()
7217            },
7218            result = Err(ErrorList::new(vec![
7219                Error::dependency_cycle("{{child logger -> capability data -> child logger}}".to_string()),
7220            ])),
7221        },
7222        test_validate_offers_invalid_child => {
7223            input = {
7224                let mut decl = new_component_decl();
7225                decl.offers = Some(vec![
7226                    fdecl::Offer::Service(fdecl::OfferService {
7227                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7228                            name: "logger".to_string(),
7229                            collection: None,
7230                        })),
7231                        source_name: Some("fuchsia.logger.Log".to_string()),
7232                        target: Some(fdecl::Ref::Child(
7233                        fdecl::ChildRef {
7234                            name: "netstack".to_string(),
7235                            collection: None,
7236                        }
7237                        )),
7238                        target_name: Some("fuchsia.logger.Log".to_string()),
7239                        ..Default::default()
7240                    }),
7241                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7242                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7243                            name: "logger".to_string(),
7244                            collection: None,
7245                        })),
7246                        source_name: Some("fuchsia.logger.LegacyLog".to_string()),
7247                        target: Some(fdecl::Ref::Child(
7248                        fdecl::ChildRef {
7249                            name: "netstack".to_string(),
7250                            collection: None,
7251                        }
7252                        )),
7253                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7254                        dependency_type: Some(fdecl::DependencyType::Strong),
7255                        ..Default::default()
7256                    }),
7257                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7258                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7259                            name: "logger".to_string(),
7260                            collection: None,
7261                        })),
7262                        source_name: Some("assets".to_string()),
7263                        target: Some(fdecl::Ref::Collection(
7264                        fdecl::CollectionRef { name: "modular".to_string() }
7265                        )),
7266                        target_name: Some("assets".to_string()),
7267                        rights: Some(fio::Operations::CONNECT),
7268                        subdir: None,
7269                        dependency_type: Some(fdecl::DependencyType::Weak),
7270                        ..Default::default()
7271                    }),
7272                ]);
7273                decl.capabilities = Some(vec![
7274                    fdecl::Capability::Storage(fdecl::Storage {
7275                        name: Some("memfs".to_string()),
7276                        backing_dir: Some("memfs".to_string()),
7277                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7278                            name: "logger".to_string(),
7279                            collection: None,
7280                        })),
7281                        subdir: None,
7282                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
7283                        ..Default::default()
7284                    }),
7285                ]);
7286                decl.children = Some(vec![
7287                    fdecl::Child {
7288                        name: Some("netstack".to_string()),
7289                        url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
7290                        startup: Some(fdecl::StartupMode::Lazy),
7291                        on_terminate: None,
7292                        environment: None,
7293                        ..Default::default()
7294                    },
7295                ]);
7296                decl.collections = Some(vec![
7297                    fdecl::Collection {
7298                        name: Some("modular".to_string()),
7299                        durability: Some(fdecl::Durability::Transient),
7300                        environment: None,
7301                        allowed_offers: Some(fdecl::AllowedOffers::StaticAndDynamic),
7302                        allow_long_names: None,
7303                        ..Default::default()
7304                    },
7305                ]);
7306                decl
7307            },
7308            result = Err(ErrorList::new(vec![
7309                Error::invalid_child(DeclType::Storage, "source", "logger"),
7310                Error::invalid_child(DeclType::OfferService, "source", "logger"),
7311                Error::invalid_child(DeclType::OfferProtocol, "source", "logger"),
7312                Error::invalid_child(DeclType::OfferDirectory, "source", "logger"),
7313            ])),
7314        },
7315        test_validate_offers_invalid_source_capability => {
7316            input = {
7317                fdecl::Component {
7318                    offers: Some(vec![
7319                        fdecl::Offer::Protocol(fdecl::OfferProtocol {
7320                            source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
7321                                name: "this-storage-doesnt-exist".to_string(),
7322                            })),
7323                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
7324                            target: Some(fdecl::Ref::Child(
7325                            fdecl::ChildRef {
7326                                name: "netstack".to_string(),
7327                                collection: None,
7328                            }
7329                            )),
7330                            target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
7331                            dependency_type: Some(fdecl::DependencyType::Strong),
7332                            ..Default::default()
7333                        }),
7334                    ]),
7335                    ..new_component_decl()
7336                }
7337            },
7338            result = Err(ErrorList::new(vec![
7339                Error::invalid_capability(DeclType::OfferProtocol, "source", "this-storage-doesnt-exist"),
7340                Error::invalid_child(DeclType::OfferProtocol, "target", "netstack"),
7341            ])),
7342        },
7343        test_validate_offers_target => {
7344            input = {
7345                let mut decl = new_component_decl();
7346                decl.offers = Some(vec![
7347                    fdecl::Offer::Service(fdecl::OfferService {
7348                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
7349                            name: "modular".into()
7350                        })),
7351                        source_name: Some("logger".to_string()),
7352                        target: Some(fdecl::Ref::Child(
7353                        fdecl::ChildRef {
7354                            name: "netstack".to_string(),
7355                            collection: None,
7356                        }
7357                        )),
7358                        target_name: Some("fuchsia.logger.Log".to_string()),
7359                        ..Default::default()
7360                    }),
7361                    fdecl::Offer::Service(fdecl::OfferService {
7362                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
7363                            name: "modular".into()
7364                        })),
7365                        source_name: Some("logger".to_string()),
7366                        target: Some(fdecl::Ref::Child(
7367                        fdecl::ChildRef {
7368                            name: "netstack".to_string(),
7369                            collection: None,
7370                        }
7371                        )),
7372                        target_name: Some("fuchsia.logger.Log".to_string()),
7373                        ..Default::default()
7374                    }),
7375                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7376                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7377                        source_name: Some("fuchsia.logger.LegacyLog".to_string()),
7378                        target: Some(fdecl::Ref::Child(
7379                        fdecl::ChildRef {
7380                            name: "netstack".to_string(),
7381                            collection: None,
7382                        }
7383                        )),
7384                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7385                        dependency_type: Some(fdecl::DependencyType::Strong),
7386                        ..Default::default()
7387                    }),
7388                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7389                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7390                        source_name: Some("fuchsia.logger.LegacyLog".to_string()),
7391                        target: Some(fdecl::Ref::Child(
7392                        fdecl::ChildRef {
7393                            name: "netstack".to_string(),
7394                            collection: None,
7395                        }
7396                        )),
7397                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7398                        dependency_type: Some(fdecl::DependencyType::Strong),
7399                        ..Default::default()
7400                    }),
7401                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7402                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7403                        source_name: Some("assets".to_string()),
7404                        target: Some(fdecl::Ref::Collection(
7405                        fdecl::CollectionRef { name: "modular".to_string() }
7406                        )),
7407                        target_name: Some("assets".to_string()),
7408                        rights: Some(fio::Operations::CONNECT),
7409                        subdir: None,
7410                        dependency_type: Some(fdecl::DependencyType::Strong),
7411                        ..Default::default()
7412                    }),
7413                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7414                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7415                        source_name: Some("assets".to_string()),
7416                        target: Some(fdecl::Ref::Collection(
7417                        fdecl::CollectionRef { name: "modular".to_string() }
7418                        )),
7419                        target_name: Some("assets".to_string()),
7420                        rights: Some(fio::Operations::CONNECT),
7421                        subdir: None,
7422                        dependency_type: Some(fdecl::DependencyType::Weak),
7423                        ..Default::default()
7424                    }),
7425                    fdecl::Offer::Storage(fdecl::OfferStorage {
7426                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7427                        source_name: Some("data".to_string()),
7428                        target: Some(fdecl::Ref::Collection(
7429                        fdecl::CollectionRef { name: "modular".to_string() }
7430                        )),
7431                        target_name: Some("data".to_string()),
7432                        ..Default::default()
7433                    }),
7434                    fdecl::Offer::Storage(fdecl::OfferStorage {
7435                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7436                        source_name: Some("data".to_string()),
7437                        target: Some(fdecl::Ref::Collection(
7438                        fdecl::CollectionRef { name: "modular".to_string() }
7439                        )),
7440                        target_name: Some("data".to_string()),
7441                        ..Default::default()
7442                    }),
7443                    fdecl::Offer::Runner(fdecl::OfferRunner {
7444                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7445                        source_name: Some("elf".to_string()),
7446                        target: Some(fdecl::Ref::Collection(
7447                        fdecl::CollectionRef { name: "modular".to_string() }
7448                        )),
7449                        target_name: Some("duplicated".to_string()),
7450                        ..Default::default()
7451                    }),
7452                    fdecl::Offer::Runner(fdecl::OfferRunner {
7453                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7454                        source_name: Some("elf".to_string()),
7455                        target: Some(fdecl::Ref::Collection(
7456                        fdecl::CollectionRef { name: "modular".to_string() }
7457                        )),
7458                        target_name: Some("duplicated".to_string()),
7459                        ..Default::default()
7460                    }),
7461                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7462                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7463                        source_name: Some("pkg".to_string()),
7464                        target: Some(fdecl::Ref::Collection(
7465                        fdecl::CollectionRef { name: "modular".to_string() }
7466                        )),
7467                        target_name: Some("duplicated".to_string()),
7468                        ..Default::default()
7469                    }),
7470                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
7471                        source_name: Some("started".to_string()),
7472                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7473                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "netstack".to_string(), collection: None})),
7474                        target_name: Some("started".to_string()),
7475                        ..Default::default()
7476                    }),
7477                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
7478                        source_name: Some("started".to_string()),
7479                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7480                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "netstack".to_string(), collection: None})),
7481                        target_name: Some("started".to_string()),
7482                        ..Default::default()
7483                    }),
7484                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7485                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7486                        source_name: Some("a".to_string()),
7487                        target: Some(fdecl::Ref::Collection(
7488                            fdecl::CollectionRef { name: "modular".to_string() }
7489                        )),
7490                        target_name: Some("dict".to_string()),
7491                        dependency_type: Some(fdecl::DependencyType::Strong),
7492                        ..Default::default()
7493                    }),
7494                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7495                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7496                        source_name: Some("b".to_string()),
7497                        target: Some(fdecl::Ref::Collection(
7498                            fdecl::CollectionRef { name: "modular".to_string() }
7499                        )),
7500                        target_name: Some("dict".to_string()),
7501                        dependency_type: Some(fdecl::DependencyType::Strong),
7502                        ..Default::default()
7503                    }),
7504                ]);
7505                decl.children = Some(vec![
7506                    fdecl::Child{
7507                        name: Some("netstack".to_string()),
7508                        url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
7509                        startup: Some(fdecl::StartupMode::Eager),
7510                        on_terminate: None,
7511                        environment: None,
7512                        ..Default::default()
7513                    },
7514                ]);
7515                decl.collections = Some(vec![
7516                    fdecl::Collection{
7517                        name: Some("modular".to_string()),
7518                        durability: Some(fdecl::Durability::Transient),
7519                        environment: None,
7520                        allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7521                        allow_long_names: None,
7522                        ..Default::default()
7523                    },
7524                ]);
7525                decl
7526            },
7527            result = Err(ErrorList::new(vec![
7528                // Duplicate services are allowed, for aggregation.
7529                Error::duplicate_field(DeclType::OfferProtocol, "target_name", "fuchsia.logger.LegacyLog"),
7530                Error::duplicate_field(DeclType::OfferDirectory, "target_name", "assets"),
7531                Error::duplicate_field(DeclType::OfferStorage, "target_name", "data"),
7532                Error::duplicate_field(DeclType::OfferRunner, "target_name", "duplicated"),
7533                Error::duplicate_field(DeclType::OfferResolver, "target_name", "duplicated"),
7534                Error::duplicate_field(DeclType::OfferEventStream, "target_name", "started"),
7535                Error::duplicate_field(DeclType::OfferDictionary, "target_name", "dict"),
7536            ])),
7537        },
7538        test_validate_offers_target_invalid => {
7539            input = {
7540                let mut decl = new_component_decl();
7541                decl.offers = Some(vec![
7542                    fdecl::Offer::Service(fdecl::OfferService {
7543                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7544                        source_name: Some("logger".to_string()),
7545                        target: Some(fdecl::Ref::Child(
7546                        fdecl::ChildRef {
7547                            name: "netstack".to_string(),
7548                            collection: None,
7549                        }
7550                        )),
7551                        target_name: Some("fuchsia.logger.Log".to_string()),
7552                        ..Default::default()
7553                    }),
7554                    fdecl::Offer::Service(fdecl::OfferService {
7555                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7556                        source_name: Some("logger".to_string()),
7557                        target: Some(fdecl::Ref::Collection(
7558                        fdecl::CollectionRef { name: "modular".to_string(), }
7559                        )),
7560                        target_name: Some("fuchsia.logger.Log".to_string()),
7561                        ..Default::default()
7562                    }),
7563                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7564                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7565                        source_name: Some("legacy_logger".to_string()),
7566                        target: Some(fdecl::Ref::Child(
7567                        fdecl::ChildRef {
7568                            name: "netstack".to_string(),
7569                            collection: None,
7570                        }
7571                        )),
7572                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7573                        dependency_type: Some(fdecl::DependencyType::Weak),
7574                        ..Default::default()
7575                    }),
7576                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7577                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7578                        source_name: Some("legacy_logger".to_string()),
7579                        target: Some(fdecl::Ref::Collection(
7580                        fdecl::CollectionRef { name: "modular".to_string(), }
7581                        )),
7582                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7583                        dependency_type: Some(fdecl::DependencyType::Strong),
7584                        ..Default::default()
7585                    }),
7586                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7587                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7588                        source_name: Some("assets".to_string()),
7589                        target: Some(fdecl::Ref::Child(
7590                        fdecl::ChildRef {
7591                            name: "netstack".to_string(),
7592                            collection: None,
7593                        }
7594                        )),
7595                        target_name: Some("data".to_string()),
7596                        rights: Some(fio::Operations::CONNECT),
7597                        subdir: None,
7598                        dependency_type: Some(fdecl::DependencyType::Strong),
7599                        ..Default::default()
7600                    }),
7601                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7602                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7603                        source_name: Some("assets".to_string()),
7604                        target: Some(fdecl::Ref::Collection(
7605                        fdecl::CollectionRef { name: "modular".to_string(), }
7606                        )),
7607                        target_name: Some("data".to_string()),
7608                        rights: Some(fio::Operations::CONNECT),
7609                        subdir: None,
7610                        dependency_type: Some(fdecl::DependencyType::Weak),
7611                        ..Default::default()
7612                    }),
7613                    fdecl::Offer::Storage(fdecl::OfferStorage {
7614                        source_name: Some("data".to_string()),
7615                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7616                        target: Some(fdecl::Ref::Child(
7617                            fdecl::ChildRef {
7618                                name: "netstack".to_string(),
7619                                collection: None,
7620                            }
7621                        )),
7622                        target_name: Some("data".to_string()),
7623                        ..Default::default()
7624                    }),
7625                    fdecl::Offer::Storage(fdecl::OfferStorage {
7626                        source_name: Some("data".to_string()),
7627                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7628                        target: Some(fdecl::Ref::Collection(
7629                            fdecl::CollectionRef { name: "modular".to_string(), }
7630                        )),
7631                        target_name: Some("data".to_string()),
7632                        ..Default::default()
7633                    }),
7634                    fdecl::Offer::Runner(fdecl::OfferRunner {
7635                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7636                        source_name: Some("elf".to_string()),
7637                        target: Some(fdecl::Ref::Child(
7638                            fdecl::ChildRef {
7639                                name: "netstack".to_string(),
7640                                collection: None,
7641                            }
7642                        )),
7643                        target_name: Some("elf".to_string()),
7644                        ..Default::default()
7645                    }),
7646                    fdecl::Offer::Runner(fdecl::OfferRunner {
7647                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7648                        source_name: Some("elf".to_string()),
7649                        target: Some(fdecl::Ref::Collection(
7650                        fdecl::CollectionRef { name: "modular".to_string(), }
7651                        )),
7652                        target_name: Some("elf".to_string()),
7653                        ..Default::default()
7654                    }),
7655                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7656                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7657                        source_name: Some("pkg".to_string()),
7658                        target: Some(fdecl::Ref::Child(
7659                            fdecl::ChildRef {
7660                                name: "netstack".to_string(),
7661                                collection: None,
7662                            }
7663                        )),
7664                        target_name: Some("pkg".to_string()),
7665                        ..Default::default()
7666                    }),
7667                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7668                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7669                        source_name: Some("pkg".to_string()),
7670                        target: Some(fdecl::Ref::Collection(
7671                        fdecl::CollectionRef { name: "modular".to_string(), }
7672                        )),
7673                        target_name: Some("pkg".to_string()),
7674                        ..Default::default()
7675                    }),
7676                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7677                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7678                        source_name: Some("pkg".to_string()),
7679                        target: Some(fdecl::Ref::Child(
7680                            fdecl::ChildRef {
7681                                name: "netstack".to_string(),
7682                                collection: None,
7683                            }
7684                        )),
7685                        target_name: Some("pkg".to_string()),
7686                        dependency_type: Some(fdecl::DependencyType::Strong),
7687                        ..Default::default()
7688                    }),
7689                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7690                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7691                        source_name: Some("pkg".to_string()),
7692                        target: Some(fdecl::Ref::Collection(
7693                        fdecl::CollectionRef { name: "modular".to_string(), }
7694                        )),
7695                        target_name: Some("pkg".to_string()),
7696                        dependency_type: Some(fdecl::DependencyType::Strong),
7697                        ..Default::default()
7698                    }),
7699                ]);
7700                decl
7701            },
7702            result = Err(ErrorList::new(vec![
7703                Error::invalid_child(DeclType::OfferService, "target", "netstack"),
7704                Error::invalid_collection(DeclType::OfferService, "target", "modular"),
7705                Error::invalid_child(DeclType::OfferProtocol, "target", "netstack"),
7706                Error::invalid_collection(DeclType::OfferProtocol, "target", "modular"),
7707                Error::invalid_child(DeclType::OfferDirectory, "target", "netstack"),
7708                Error::invalid_collection(DeclType::OfferDirectory, "target", "modular"),
7709                Error::invalid_child(DeclType::OfferStorage, "target", "netstack"),
7710                Error::invalid_collection(DeclType::OfferStorage, "target", "modular"),
7711                Error::invalid_child(DeclType::OfferRunner, "target", "netstack"),
7712                Error::invalid_collection(DeclType::OfferRunner, "target", "modular"),
7713                Error::invalid_child(DeclType::OfferResolver, "target", "netstack"),
7714                Error::invalid_collection(DeclType::OfferResolver, "target", "modular"),
7715                Error::invalid_child(DeclType::OfferDictionary, "target", "netstack"),
7716                Error::invalid_collection(DeclType::OfferDictionary, "target", "modular"),
7717            ])),
7718        },
7719        test_validate_offers_target_dictionary => {
7720            input = fdecl::Component {
7721                offers: Some(vec![
7722                    // Offer to static dictionary is ok
7723                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7724                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7725                        source_name: Some("p".to_string()),
7726                        target: Some(fdecl::Ref::Capability(
7727                            fdecl::CapabilityRef {
7728                                name: "dict".into(),
7729                            },
7730                        )),
7731                        target_name: Some("p".into()),
7732                        dependency_type: Some(fdecl::DependencyType::Strong),
7733                        ..Default::default()
7734                    }),
7735                    // Offer to dynamic dictionary is forbidden
7736                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7737                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7738                        source_name: Some("p".to_string()),
7739                        target: Some(fdecl::Ref::Capability(
7740                            fdecl::CapabilityRef {
7741                                name: "dynamic".into(),
7742                            },
7743                        )),
7744                        target_name: Some("p".into()),
7745                        dependency_type: Some(fdecl::DependencyType::Strong),
7746                        ..Default::default()
7747                    }),
7748                ]),
7749                capabilities: Some(vec![
7750                    fdecl::Capability::Dictionary(fdecl::Dictionary {
7751                        name: Some("dict".into()),
7752                        ..Default::default()
7753                    }),
7754                    fdecl::Capability::Dictionary(fdecl::Dictionary {
7755                        name: Some("dynamic".into()),
7756                        source_path: Some("/out/dir".into()),
7757                        ..Default::default()
7758                    }),
7759                ]),
7760                ..Default::default()
7761            },
7762            result = Err(ErrorList::new(vec![
7763                Error::invalid_field(DeclType::OfferProtocol, "target"),
7764            ])),
7765        },
7766        test_validate_offers_invalid_source_collection => {
7767            input = {
7768                let mut decl = new_component_decl();
7769                decl.collections = Some(vec![
7770                    fdecl::Collection {
7771                        name: Some("col".to_string()),
7772                        durability: Some(fdecl::Durability::Transient),
7773                        allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7774                        allow_long_names: None,
7775                        ..Default::default()
7776                    }
7777                ]);
7778                decl.children = Some(vec![
7779                    fdecl::Child {
7780                        name: Some("child".to_string()),
7781                        url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7782                        startup: Some(fdecl::StartupMode::Lazy),
7783                        on_terminate: None,
7784                        ..Default::default()
7785                    }
7786                ]);
7787                decl.offers = Some(vec![
7788                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7789                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7790                        source_name: Some("a".to_string()),
7791                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7792                        target_name: Some("a".to_string()),
7793                        dependency_type: Some(fdecl::DependencyType::Strong),
7794                        ..Default::default()
7795                    }),
7796                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7797                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7798                        source_name: Some("b".to_string()),
7799                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7800                        target_name: Some("b".to_string()),
7801                        rights: Some(fio::Operations::CONNECT),
7802                        subdir: None,
7803                        dependency_type: Some(fdecl::DependencyType::Strong),
7804                        ..Default::default()
7805                    }),
7806                    fdecl::Offer::Storage(fdecl::OfferStorage {
7807                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7808                        source_name: Some("c".to_string()),
7809                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7810                        target_name: Some("c".to_string()),
7811                        ..Default::default()
7812                    }),
7813                    fdecl::Offer::Runner(fdecl::OfferRunner {
7814                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7815                        source_name: Some("d".to_string()),
7816                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7817                        target_name: Some("d".to_string()),
7818                        ..Default::default()
7819                    }),
7820                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7821                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7822                        source_name: Some("e".to_string()),
7823                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7824                        target_name: Some("e".to_string()),
7825                        ..Default::default()
7826                    }),
7827                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7828                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7829                        source_name: Some("f".to_string()),
7830                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7831                        target_name: Some("f".to_string()),
7832                        dependency_type: Some(fdecl::DependencyType::Strong),
7833                        ..Default::default()
7834                    }),
7835                ]);
7836                decl
7837            },
7838            result = Err(ErrorList::new(vec![
7839                Error::invalid_field(DeclType::OfferProtocol, "source"),
7840                Error::invalid_field(DeclType::OfferDirectory, "source"),
7841                Error::invalid_field(DeclType::OfferStorage, "source"),
7842                Error::invalid_field(DeclType::OfferRunner, "source"),
7843                Error::invalid_field(DeclType::OfferResolver, "source"),
7844                Error::invalid_field(DeclType::OfferDictionary, "source"),
7845            ])),
7846        },
7847        test_validate_offers_source_collection => {
7848            input = {
7849                let mut decl = new_component_decl();
7850                decl.collections = Some(vec![
7851                    fdecl::Collection {
7852                        name: Some("col".to_string()),
7853                        durability: Some(fdecl::Durability::Transient),
7854                        allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7855                        allow_long_names: None,
7856                        ..Default::default()
7857                    }
7858                ]);
7859                decl.children = Some(vec![
7860                    fdecl::Child {
7861                        name: Some("child".to_string()),
7862                        url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7863                        startup: Some(fdecl::StartupMode::Lazy),
7864                        on_terminate: None,
7865                        ..Default::default()
7866                    }
7867                ]);
7868                decl.offers = Some(vec![
7869                    fdecl::Offer::Service(fdecl::OfferService {
7870                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7871                        source_name: Some("a".to_string()),
7872                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7873                        target_name: Some("a".to_string()),
7874                        ..Default::default()
7875                    })
7876                ]);
7877                decl
7878            },
7879            result = Ok(()),
7880        },
7881        test_validate_offers_invalid_capability_from_self => {
7882            input = {
7883                let mut decl = new_component_decl();
7884                decl.children = Some(vec![
7885                    fdecl::Child {
7886                        name: Some("child".to_string()),
7887                        url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7888                        startup: Some(fdecl::StartupMode::Lazy),
7889                        ..Default::default()
7890                    }
7891                ]);
7892                decl.offers = Some(vec![
7893                    fdecl::Offer::Service(fdecl::OfferService {
7894                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7895                        source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7896                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7897                            name: "child".into(),
7898                            collection: None
7899                        })),
7900                        target_name: Some("foo".into()),
7901                        ..Default::default()
7902                    }),
7903                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7904                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7905                        source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7906                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7907                            name: "child".into(),
7908                            collection: None
7909                        })),
7910                        target_name: Some("bar".into()),
7911                        dependency_type: Some(fdecl::DependencyType::Strong),
7912                        ..Default::default()
7913                    }),
7914                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7915                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7916                        source_name: Some("dir".into()),
7917                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7918                            name: "child".into(),
7919                            collection: None
7920                        })),
7921                        target_name: Some("assets".into()),
7922                        dependency_type: Some(fdecl::DependencyType::Strong),
7923                        ..Default::default()
7924                    }),
7925                    fdecl::Offer::Runner(fdecl::OfferRunner {
7926                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7927                        source_name: Some("source_elf".into()),
7928                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7929                            name: "child".into(),
7930                            collection: None
7931                        })),
7932                        target_name: Some("elf".into()),
7933                        ..Default::default()
7934                    }),
7935                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7936                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7937                        source_name: Some("source_pkg".into()),
7938                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7939                            name: "child".into(),
7940                            collection: None
7941                        })),
7942                        target_name: Some("pkg".into()),
7943                        ..Default::default()
7944                    }),
7945                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7946                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7947                        source_name: Some("source_dict".into()),
7948                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7949                            name: "child".into(),
7950                            collection: None
7951                        })),
7952                        target_name: Some("dict".into()),
7953                        dependency_type: Some(fdecl::DependencyType::Strong),
7954                        ..Default::default()
7955                    }),
7956                    fdecl::Offer::Storage(fdecl::OfferStorage {
7957                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7958                        source_name: Some("source_storage".into()),
7959                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7960                            name: "child".into(),
7961                            collection: None
7962                        })),
7963                        target_name: Some("storage".into()),
7964                        ..Default::default()
7965                    }),
7966                    fdecl::Offer::Config(fdecl::OfferConfiguration {
7967                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7968                        source_name: Some("source_config".into()),
7969                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7970                            name: "child".into(),
7971                            collection: None
7972                        })),
7973                        target_name: Some("config".into()),
7974                        ..Default::default()
7975                    }),
7976                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7977                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7978                        source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7979                        source_dictionary: Some("dict/inner".into()),
7980                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7981                            name: "child".into(),
7982                            collection: None
7983                        })),
7984                        target_name: Some("baz".into()),
7985                        dependency_type: Some(fdecl::DependencyType::Strong),
7986                        ..Default::default()
7987                    }),
7988                ]);
7989                decl
7990            },
7991            result = Err(ErrorList::new(vec![
7992                Error::invalid_capability(
7993                    DeclType::OfferService,
7994                    "source",
7995                    "fuchsia.some.library.SomeProtocol"),
7996                Error::invalid_capability(
7997                    DeclType::OfferProtocol,
7998                    "source",
7999                    "fuchsia.some.library.SomeProtocol"),
8000                Error::invalid_capability(DeclType::OfferDirectory, "source", "dir"),
8001                Error::invalid_capability(DeclType::OfferRunner, "source", "source_elf"),
8002                Error::invalid_capability(DeclType::OfferResolver, "source", "source_pkg"),
8003                Error::invalid_capability(DeclType::OfferDictionary, "source", "source_dict"),
8004                Error::invalid_capability(DeclType::OfferStorage, "source", "source_storage"),
8005                Error::invalid_capability(DeclType::OfferConfig, "source", "source_config"),
8006                Error::invalid_capability(DeclType::OfferProtocol, "source", "dict"),
8007            ])),
8008        },
8009        test_validate_offers_long_dependency_cycle => {
8010            input = {
8011                let mut decl = new_component_decl();
8012                let dependencies = vec![
8013                    ("d", "b"),
8014                    ("a", "b"),
8015                    ("b", "c"),
8016                    ("b", "d"),
8017                    ("c", "a"),
8018                ];
8019                let offers = dependencies.into_iter().map(|(from,to)|
8020                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
8021                        source: Some(fdecl::Ref::Child(
8022                        fdecl::ChildRef { name: from.to_string(), collection: None },
8023                        )),
8024                        source_name: Some(format!("thing_{}", from)),
8025                        target: Some(fdecl::Ref::Child(
8026                        fdecl::ChildRef { name: to.to_string(), collection: None },
8027                        )),
8028                        target_name: Some(format!("thing_{}", from)),
8029                        dependency_type: Some(fdecl::DependencyType::Strong),
8030                        ..Default::default()
8031                    })).collect();
8032                let children = ["a", "b", "c", "d"].iter().map(|name| {
8033                    fdecl::Child {
8034                        name: Some(name.to_string()),
8035                        url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
8036                        startup: Some(fdecl::StartupMode::Lazy),
8037                        on_terminate: None,
8038                        environment: None,
8039                        ..Default::default()
8040                    }
8041                }).collect();
8042                decl.offers = Some(offers);
8043                decl.children = Some(children);
8044                decl
8045            },
8046            result = Err(ErrorList::new(vec![
8047                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()),
8048            ])),
8049        },
8050        test_validate_offers_not_required_invalid_source_service => {
8051            input = {
8052                let mut decl = generate_offer_different_source_and_availability_decl(
8053                    |source, availability, target_name|
8054                        fdecl::Offer::Service(fdecl::OfferService {
8055                            source: Some(source),
8056                            source_name: Some("fuchsia.examples.Echo".to_string()),
8057                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8058                                name: "sink".to_string(),
8059                                collection: None,
8060                            })),
8061                            target_name: Some(target_name.into()),
8062                            availability: Some(availability),
8063                            ..Default::default()
8064                        })
8065                );
8066                decl.capabilities = Some(vec![
8067                    fdecl::Capability::Service(fdecl::Service {
8068                        name: Some("fuchsia.examples.Echo".to_string()),
8069                        source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
8070                        ..Default::default()
8071                    }),
8072                ]);
8073                decl
8074            },
8075            result = {
8076                Err(ErrorList::new(vec![
8077                    Error::availability_must_be_optional(
8078                        DeclType::OfferService,
8079                        "availability",
8080                        Some(&"fuchsia.examples.Echo".to_string()),
8081                    ),
8082                    Error::availability_must_be_optional(
8083                        DeclType::OfferService,
8084                        "availability",
8085                        Some(&"fuchsia.examples.Echo".to_string()),
8086                    ),
8087                ]))
8088            },
8089        },
8090        test_validate_offers_not_required_invalid_source_protocol => {
8091            input = {
8092                let mut decl = generate_offer_different_source_and_availability_decl(
8093                    |source, availability, target_name|
8094                        fdecl::Offer::Protocol(fdecl::OfferProtocol {
8095                            source: Some(source),
8096                            source_name: Some("fuchsia.examples.Echo".to_string()),
8097                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8098                                name: "sink".to_string(),
8099                                collection: None,
8100                            })),
8101                            target_name: Some(target_name.into()),
8102                            dependency_type: Some(fdecl::DependencyType::Strong),
8103                            availability: Some(availability),
8104                            ..Default::default()
8105                        })
8106                );
8107                decl.capabilities = Some(vec![
8108                    fdecl::Capability::Protocol(fdecl::Protocol {
8109                        name: Some("fuchsia.examples.Echo".to_string()),
8110                        source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
8111                        ..Default::default()
8112                    }),
8113                ]);
8114                decl
8115            },
8116            result = {
8117                Err(ErrorList::new(vec![
8118                    Error::availability_must_be_optional(
8119                        DeclType::OfferProtocol,
8120                        "availability",
8121                        Some(&"fuchsia.examples.Echo".to_string()),
8122                    ),
8123                    Error::availability_must_be_optional(
8124                        DeclType::OfferProtocol,
8125                        "availability",
8126                        Some(&"fuchsia.examples.Echo".to_string()),
8127                    ),
8128                ]))
8129            },
8130        },
8131        test_validate_offers_not_required_invalid_source_directory => {
8132            input = {
8133                let mut decl = generate_offer_different_source_and_availability_decl(
8134                    |source, availability, target_name|
8135                        fdecl::Offer::Directory(fdecl::OfferDirectory {
8136                            source: Some(source),
8137                            source_name: Some("assets".to_string()),
8138                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8139                                name: "sink".to_string(),
8140                                collection: None,
8141                            })),
8142                            target_name: Some(target_name.into()),
8143                            rights: Some(fio::Operations::CONNECT),
8144                            subdir: None,
8145                            dependency_type: Some(fdecl::DependencyType::Weak),
8146                            availability: Some(availability),
8147                            ..Default::default()
8148                        })
8149                );
8150                decl.capabilities = Some(vec![
8151                    fdecl::Capability::Directory(fdecl::Directory {
8152                        name: Some("assets".to_string()),
8153                        source_path: Some("/assets".to_string()),
8154                        rights: Some(fio::Operations::CONNECT),
8155                        ..Default::default()
8156                    }),
8157                ]);
8158                decl
8159            },
8160            result = {
8161                Err(ErrorList::new(vec![
8162                    Error::availability_must_be_optional(
8163                        DeclType::OfferDirectory,
8164                        "availability",
8165                        Some(&"assets".to_string()),
8166                    ),
8167                    Error::availability_must_be_optional(
8168                        DeclType::OfferDirectory,
8169                        "availability",
8170                        Some(&"assets".to_string()),
8171                    ),
8172                ]))
8173            },
8174        },
8175        test_validate_offers_not_required_invalid_source_storage => {
8176            input = {
8177                let mut decl = new_component_decl();
8178                decl.children = Some(vec![
8179                    fdecl::Child {
8180                        name: Some("sink".to_string()),
8181                        url: Some("fuchsia-pkg://fuchsia.com/sink#meta/sink.cm".to_string()),
8182                        startup: Some(fdecl::StartupMode::Lazy),
8183                        on_terminate: None,
8184                        environment: None,
8185                        ..Default::default()
8186                    },
8187                ]);
8188                decl.capabilities = Some(vec![
8189                    fdecl::Capability::Storage(fdecl::Storage {
8190                        name: Some("data".to_string()),
8191                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8192                        backing_dir: Some("minfs".to_string()),
8193                        subdir: None,
8194                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
8195                        ..Default::default()
8196                    }),
8197                ]);
8198                let new_offer = |source: fdecl::Ref, availability: fdecl::Availability,
8199                                        target_name: &str|
8200                {
8201                    fdecl::Offer::Storage(fdecl::OfferStorage {
8202                        source: Some(source),
8203                        source_name: Some("data".to_string()),
8204                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8205                            name: "sink".to_string(),
8206                            collection: None,
8207                        })),
8208                        target_name: Some(target_name.into()),
8209                        availability: Some(availability),
8210                        ..Default::default()
8211                    })
8212                };
8213                decl.offers = Some(vec![
8214                    // These offers are fine, offers with a source of parent or void can be
8215                    // optional.
8216                    new_offer(
8217                        fdecl::Ref::Parent(fdecl::ParentRef {}),
8218                        fdecl::Availability::Required,
8219                        "data0",
8220                    ),
8221                    new_offer(
8222                        fdecl::Ref::Parent(fdecl::ParentRef {}),
8223                        fdecl::Availability::Optional,
8224                        "data1",
8225                    ),
8226                    new_offer(
8227                        fdecl::Ref::Parent(fdecl::ParentRef {}),
8228                        fdecl::Availability::SameAsTarget,
8229                        "data2",
8230                    ),
8231                    new_offer(
8232                        fdecl::Ref::VoidType(fdecl::VoidRef {}),
8233                        fdecl::Availability::Optional,
8234                        "data3",
8235                    ),
8236                    // These offers are not fine, offers with a source other than parent or void
8237                    // must be required.
8238                    new_offer(
8239                        fdecl::Ref::Self_(fdecl::SelfRef {}),
8240                        fdecl::Availability::Optional,
8241                        "data4",
8242                    ),
8243                    new_offer(
8244                        fdecl::Ref::Self_(fdecl::SelfRef {}),
8245                        fdecl::Availability::SameAsTarget,
8246                        "data5",
8247                    ),
8248                    // These offers are also not fine, offers with a source of void must be optional
8249                    new_offer(
8250                        fdecl::Ref::VoidType(fdecl::VoidRef {}),
8251                        fdecl::Availability::Required,
8252                        "data6",
8253                    ),
8254                    new_offer(
8255                        fdecl::Ref::VoidType(fdecl::VoidRef {}),
8256                        fdecl::Availability::SameAsTarget,
8257                        "data7",
8258                    ),
8259                ]);
8260                decl
8261            },
8262            result = {
8263                Err(ErrorList::new(vec![
8264                    Error::availability_must_be_optional(
8265                        DeclType::OfferStorage,
8266                        "availability",
8267                        Some(&"data".to_string()),
8268                    ),
8269                    Error::availability_must_be_optional(
8270                        DeclType::OfferStorage,
8271                        "availability",
8272                        Some(&"data".to_string()),
8273                    ),
8274                ]))
8275            },
8276        },
8277
8278        test_validate_offers_valid_service_aggregation => {
8279            input = {
8280                let mut decl = new_component_decl();
8281                decl.offers = Some(vec![
8282                    fdecl::Offer::Service(fdecl::OfferService {
8283                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
8284                            name: "coll_a".to_string()
8285                        })),
8286                        source_name: Some("fuchsia.logger.Log".to_string()),
8287                        target: Some(fdecl::Ref::Child(
8288                            fdecl::ChildRef {
8289                                name: "child_c".to_string(),
8290                                collection: None,
8291                            }
8292                        )),
8293                        target_name: Some("fuchsia.logger.Log".to_string()),
8294                        source_instance_filter: None,
8295                        ..Default::default()
8296                    }),
8297                    fdecl::Offer::Service(fdecl::OfferService {
8298                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
8299                            name: "coll_b".to_string()
8300                        })),
8301                        source_name: Some("fuchsia.logger.Log".to_string()),
8302                        target: Some(fdecl::Ref::Child(
8303                            fdecl::ChildRef {
8304                                name: "child_c".to_string(),
8305                                collection: None,
8306                            }
8307                        )),
8308                        target_name: Some("fuchsia.logger.Log".to_string()),
8309                        source_instance_filter: Some(vec!["a_different_default".to_string()]),
8310                        ..Default::default()
8311                    })
8312                ]);
8313                decl.children = Some(vec![
8314                    fdecl::Child {
8315                        name: Some("child_c".to_string()),
8316                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
8317                        startup: Some(fdecl::StartupMode::Lazy),
8318                        ..Default::default()
8319                    },
8320                ]);
8321                decl.collections = Some(vec![
8322                    fdecl::Collection {
8323                        name: Some("coll_a".into()),
8324                        durability: Some(fdecl::Durability::Transient),
8325                        ..Default::default()
8326                    },
8327                    fdecl::Collection {
8328                        name: Some("coll_b".into()),
8329                        durability: Some(fdecl::Durability::Transient),
8330                        ..Default::default()
8331                    },
8332                ]);
8333                decl
8334            },
8335            result = Ok(()),
8336        },
8337
8338        // dictionaries
8339        test_validate_source_dictionary => {
8340            input = fdecl::Component {
8341                program: Some(fdecl::Program {
8342                    runner: Some("elf".into()),
8343                    info: Some(fdata::Dictionary {
8344                        entries: None,
8345                        ..Default::default()
8346                    }),
8347                    ..Default::default()
8348                }),
8349                uses: Some(vec![
8350                    fdecl::Use::Protocol(fdecl::UseProtocol {
8351                        dependency_type: Some(fdecl::DependencyType::Strong),
8352                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8353                        source_dictionary: Some("bad//".into()),
8354                        source_name: Some("foo".into()),
8355                        target_path: Some("/svc/foo".into()),
8356                        ..Default::default()
8357                    }),
8358                ]),
8359                exposes: Some(vec![
8360                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
8361                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8362                            name: "missing".into(),
8363                            collection: None,
8364                        })),
8365                        source_dictionary: Some("in/dict".into()),
8366                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8367                        source_name: Some("foo".into()),
8368                        target_name: Some("bar".into()),
8369                        ..Default::default()
8370                    }),
8371                ]),
8372                offers: Some(vec![
8373                    fdecl::Offer::Service(fdecl::OfferService {
8374                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8375                        source_dictionary: Some("bad//".into()),
8376                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8377                            name: "child".into(),
8378                            collection: None,
8379                        })),
8380                        source_name: Some("foo".into()),
8381                        target_name: Some("bar".into()),
8382                        ..Default::default()
8383                    }),
8384                ]),
8385                children: Some(vec![
8386                    fdecl::Child {
8387                        name: Some("child".into()),
8388                        url: Some("fuchsia-pkg://child".into()),
8389                        startup: Some(fdecl::StartupMode::Lazy),
8390                        ..Default::default()
8391                    },
8392                ]),
8393                ..Default::default()
8394            },
8395            result = Err(ErrorList::new(vec![
8396                Error::invalid_field(DeclType::UseProtocol, "source_dictionary"),
8397                Error::invalid_child(DeclType::ExposeDirectory, "source", "missing"),
8398                Error::invalid_field(DeclType::OfferService, "source_dictionary"),
8399            ])),
8400        },
8401        test_validate_dictionary_too_long => {
8402            input = fdecl::Component {
8403                program: Some(fdecl::Program {
8404                    runner: Some("elf".into()),
8405                    info: Some(fdata::Dictionary {
8406                        entries: None,
8407                        ..Default::default()
8408                    }),
8409                    ..Default::default()
8410                }),
8411                uses: Some(vec![
8412                    fdecl::Use::Protocol(fdecl::UseProtocol {
8413                        dependency_type: Some(fdecl::DependencyType::Strong),
8414                        source: Some(fdecl::Ref::Parent( fdecl::ParentRef {} )),
8415                        source_dictionary: Some("a".repeat(4096)),
8416                        source_name: Some("foo".into()),
8417                        target_path: Some("/svc/foo".into()),
8418                        ..Default::default()
8419                    }),
8420                ]),
8421                ..Default::default()
8422            },
8423            result = Err(ErrorList::new(vec![
8424                Error::field_too_long(DeclType::UseProtocol, "source_dictionary"),
8425            ])),
8426        },
8427
8428        // environments
8429        test_validate_environment_empty => {
8430            input = {
8431                let mut decl = new_component_decl();
8432                decl.environments = Some(vec![fdecl::Environment {
8433                    name: None,
8434                    extends: None,
8435                    runners: None,
8436                    resolvers: None,
8437                    stop_timeout_ms: None,
8438                    debug_capabilities: None,
8439                    ..Default::default()
8440                }]);
8441                decl
8442            },
8443            result = Err(ErrorList::new(vec![
8444                Error::missing_field(DeclType::Environment, "name"),
8445                Error::missing_field(DeclType::Environment, "extends"),
8446            ])),
8447        },
8448
8449        test_validate_environment_no_stop_timeout => {
8450            input = {
8451                let mut decl = new_component_decl();
8452                decl.environments = Some(vec![fdecl::Environment {
8453                    name: Some("env".to_string()),
8454                    extends: Some(fdecl::EnvironmentExtends::None),
8455                    runners: None,
8456                    resolvers: None,
8457                    stop_timeout_ms: None,
8458                    ..Default::default()
8459                }]);
8460                decl
8461            },
8462            result = Err(ErrorList::new(vec![Error::missing_field(DeclType::Environment, "stop_timeout_ms")])),
8463        },
8464
8465        test_validate_environment_extends_stop_timeout => {
8466            input = {  let mut decl = new_component_decl();
8467                decl.environments = Some(vec![fdecl::Environment {
8468                    name: Some("env".to_string()),
8469                    extends: Some(fdecl::EnvironmentExtends::Realm),
8470                    runners: None,
8471                    resolvers: None,
8472                    stop_timeout_ms: None,
8473                    ..Default::default()
8474                }]);
8475                decl
8476            },
8477            result = Ok(()),
8478        },
8479        test_validate_environment_long_identifiers => {
8480            input = {
8481                let mut decl = new_component_decl();
8482                decl.environments = Some(vec![fdecl::Environment {
8483                    name: Some("a".repeat(256)),
8484                    extends: Some(fdecl::EnvironmentExtends::None),
8485                    runners: Some(vec![
8486                        fdecl::RunnerRegistration {
8487                            source_name: Some("a".repeat(256)),
8488                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8489                            target_name: Some("a".repeat(256)),
8490                            ..Default::default()
8491                        },
8492                    ]),
8493                    resolvers: Some(vec![
8494                        fdecl::ResolverRegistration {
8495                            resolver: Some("a".repeat(256)),
8496                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8497                            scheme: Some("a".repeat(256)),
8498                            ..Default::default()
8499                        },
8500                    ]),
8501                    stop_timeout_ms: Some(1234),
8502                    ..Default::default()
8503                }]);
8504                decl
8505            },
8506            result = Err(ErrorList::new(vec![
8507                Error::field_too_long(DeclType::Environment, "name"),
8508                Error::field_too_long(DeclType::RunnerRegistration, "source_name"),
8509                Error::field_too_long(DeclType::RunnerRegistration, "target_name"),
8510                Error::field_too_long(DeclType::ResolverRegistration, "resolver"),
8511                Error::field_too_long(DeclType::ResolverRegistration, "scheme"),
8512            ])),
8513        },
8514        test_validate_environment_empty_runner_resolver_fields => {
8515            input = {
8516                let mut decl = new_component_decl();
8517                decl.environments = Some(vec![fdecl::Environment {
8518                    name: Some("a".to_string()),
8519                    extends: Some(fdecl::EnvironmentExtends::None),
8520                    runners: Some(vec![
8521                        fdecl::RunnerRegistration {
8522                            source_name: None,
8523                            source: None,
8524                            target_name: None,
8525                            ..Default::default()
8526                        },
8527                    ]),
8528                    resolvers: Some(vec![
8529                        fdecl::ResolverRegistration {
8530                            resolver: None,
8531                            source: None,
8532                            scheme: None,
8533                            ..Default::default()
8534                        },
8535                    ]),
8536                    stop_timeout_ms: Some(1234),
8537                    ..Default::default()
8538                }]);
8539                decl
8540            },
8541            result = Err(ErrorList::new(vec![
8542                Error::missing_field(DeclType::RunnerRegistration, "source_name"),
8543                Error::missing_field(DeclType::RunnerRegistration, "source"),
8544                Error::missing_field(DeclType::RunnerRegistration, "target_name"),
8545                Error::missing_field(DeclType::ResolverRegistration, "resolver"),
8546                Error::missing_field(DeclType::ResolverRegistration, "source"),
8547                Error::missing_field(DeclType::ResolverRegistration, "scheme"),
8548            ])),
8549        },
8550        test_validate_environment_invalid_fields => {
8551            input = {
8552                let mut decl = new_component_decl();
8553                decl.environments = Some(vec![fdecl::Environment {
8554                    name: Some("a".to_string()),
8555                    extends: Some(fdecl::EnvironmentExtends::None),
8556                    runners: Some(vec![
8557                        fdecl::RunnerRegistration {
8558                            source_name: Some("^a".to_string()),
8559                            source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
8560                            target_name: Some("%a".to_string()),
8561                            ..Default::default()
8562                        },
8563                    ]),
8564                    resolvers: Some(vec![
8565                        fdecl::ResolverRegistration {
8566                            resolver: Some("^a".to_string()),
8567                            source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
8568                            scheme: Some("9scheme".to_string()),
8569                            ..Default::default()
8570                        },
8571                    ]),
8572                    stop_timeout_ms: Some(1234),
8573                    ..Default::default()
8574                }]);
8575                decl
8576            },
8577            result = Err(ErrorList::new(vec![
8578                Error::invalid_field(DeclType::RunnerRegistration, "source_name"),
8579                Error::invalid_field(DeclType::RunnerRegistration, "source"),
8580                Error::invalid_field(DeclType::RunnerRegistration, "target_name"),
8581                Error::invalid_field(DeclType::ResolverRegistration, "resolver"),
8582                Error::invalid_field(DeclType::ResolverRegistration, "source"),
8583                Error::invalid_field(DeclType::ResolverRegistration, "scheme"),
8584            ])),
8585        },
8586        test_validate_environment_missing_runner => {
8587            input = {
8588                let mut decl = new_component_decl();
8589                decl.environments = Some(vec![fdecl::Environment {
8590                    name: Some("a".to_string()),
8591                    extends: Some(fdecl::EnvironmentExtends::None),
8592                    runners: Some(vec![
8593                        fdecl::RunnerRegistration {
8594                            source_name: Some("dart".to_string()),
8595                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
8596                            target_name: Some("dart".to_string()),
8597                            ..Default::default()
8598                        },
8599                    ]),
8600                    resolvers: None,
8601                    stop_timeout_ms: Some(1234),
8602                    ..Default::default()
8603                }]);
8604                decl
8605            },
8606            result = Err(ErrorList::new(vec![
8607                Error::invalid_runner(DeclType::RunnerRegistration, "source_name", "dart"),
8608            ])),
8609        },
8610        test_validate_environment_duplicate_registrations => {
8611            input = {
8612                let mut decl = new_component_decl();
8613                decl.environments = Some(vec![fdecl::Environment {
8614                    name: Some("a".to_string()),
8615                    extends: Some(fdecl::EnvironmentExtends::None),
8616                    runners: Some(vec![
8617                        fdecl::RunnerRegistration {
8618                            source_name: Some("dart".to_string()),
8619                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8620                            target_name: Some("dart".to_string()),
8621                            ..Default::default()
8622                        },
8623                        fdecl::RunnerRegistration {
8624                            source_name: Some("other-dart".to_string()),
8625                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8626                            target_name: Some("dart".to_string()),
8627                            ..Default::default()
8628                        },
8629                    ]),
8630                    resolvers: Some(vec![
8631                        fdecl::ResolverRegistration {
8632                            resolver: Some("pkg_resolver".to_string()),
8633                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8634                            scheme: Some("fuchsia-pkg".to_string()),
8635                            ..Default::default()
8636                        },
8637                        fdecl::ResolverRegistration {
8638                            resolver: Some("base_resolver".to_string()),
8639                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8640                            scheme: Some("fuchsia-pkg".to_string()),
8641                            ..Default::default()
8642                        },
8643                    ]),
8644                    stop_timeout_ms: Some(1234),
8645                    ..Default::default()
8646                }]);
8647                decl
8648            },
8649            result = Err(ErrorList::new(vec![
8650                Error::duplicate_field(DeclType::RunnerRegistration, "target_name", "dart"),
8651                Error::duplicate_field(DeclType::ResolverRegistration, "scheme", "fuchsia-pkg"),
8652            ])),
8653        },
8654        test_validate_environment_from_missing_child => {
8655            input = {
8656                let mut decl = new_component_decl();
8657                decl.environments = Some(vec![fdecl::Environment {
8658                    name: Some("a".to_string()),
8659                    extends: Some(fdecl::EnvironmentExtends::None),
8660                    runners: Some(vec![
8661                        fdecl::RunnerRegistration {
8662                            source_name: Some("elf".to_string()),
8663                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8664                                name: "missing".to_string(),
8665                                collection: None,
8666                            })),
8667                            target_name: Some("elf".to_string()),
8668                            ..Default::default()
8669                        },
8670                    ]),
8671                    resolvers: Some(vec![
8672                        fdecl::ResolverRegistration {
8673                            resolver: Some("pkg_resolver".to_string()),
8674                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8675                                name: "missing".to_string(),
8676                                collection: None,
8677                            })),
8678                            scheme: Some("fuchsia-pkg".to_string()),
8679                            ..Default::default()
8680                        },
8681                    ]),
8682                    stop_timeout_ms: Some(1234),
8683                    ..Default::default()
8684                }]);
8685                decl
8686            },
8687            result = Err(ErrorList::new(vec![
8688                Error::invalid_child(DeclType::RunnerRegistration, "source", "missing"),
8689                Error::invalid_child(DeclType::ResolverRegistration, "source", "missing"),
8690            ])),
8691        },
8692        test_validate_environment_runner_child_cycle => {
8693            input = {
8694                let mut decl = new_component_decl();
8695                decl.environments = Some(vec![fdecl::Environment {
8696                    name: Some("env".to_string()),
8697                    extends: Some(fdecl::EnvironmentExtends::None),
8698                    runners: Some(vec![
8699                        fdecl::RunnerRegistration {
8700                            source_name: Some("elf".to_string()),
8701                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8702                                name: "child".to_string(),
8703                                collection: None,
8704                            })),
8705                            target_name: Some("elf".to_string()),
8706                            ..Default::default()
8707                        },
8708                    ]),
8709                    resolvers: None,
8710                    stop_timeout_ms: Some(1234),
8711                    ..Default::default()
8712                }]);
8713                decl.children = Some(vec![fdecl::Child {
8714                    name: Some("child".to_string()),
8715                    startup: Some(fdecl::StartupMode::Lazy),
8716                    on_terminate: None,
8717                    url: Some("fuchsia-pkg://child".to_string()),
8718                    environment: Some("env".to_string()),
8719                    ..Default::default()
8720                }]);
8721                decl
8722            },
8723            result = Err(ErrorList::new(vec![
8724                Error::dependency_cycle(
8725                    directed_graph::Error::CyclesDetected([vec!["child child", "environment env", "child child"]].iter().cloned().collect()).format_cycle()
8726                ),
8727            ])),
8728        },
8729        test_validate_environment_resolver_child_cycle => {
8730            input = {
8731                let mut decl = new_component_decl();
8732                decl.environments = Some(vec![fdecl::Environment {
8733                    name: Some("env".to_string()),
8734                    extends: Some(fdecl::EnvironmentExtends::None),
8735                    runners: None,
8736                    resolvers: Some(vec![
8737                        fdecl::ResolverRegistration {
8738                            resolver: Some("pkg_resolver".to_string()),
8739                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8740                                name: "child".to_string(),
8741                                collection: None,
8742                            })),
8743                            scheme: Some("fuchsia-pkg".to_string()),
8744                            ..Default::default()
8745                        },
8746                    ]),
8747                    stop_timeout_ms: Some(1234),
8748                    ..Default::default()
8749                }]);
8750                decl.children = Some(vec![fdecl::Child {
8751                    name: Some("child".to_string()),
8752                    startup: Some(fdecl::StartupMode::Lazy),
8753                    on_terminate: None,
8754                    url: Some("fuchsia-pkg://child".to_string()),
8755                    environment: Some("env".to_string()),
8756                    ..Default::default()
8757                }]);
8758                decl
8759            },
8760            result = Err(ErrorList::new(vec![
8761                Error::dependency_cycle(
8762                    directed_graph::Error::CyclesDetected([vec!["child child", "environment env", "child child"]].iter().cloned().collect()).format_cycle()
8763                ),
8764            ])),
8765        },
8766        test_validate_environment_resolver_multiple_children_cycle => {
8767            input = {
8768                let mut decl = new_component_decl();
8769                decl.environments = Some(vec![fdecl::Environment {
8770                    name: Some("env".to_string()),
8771                    extends: Some(fdecl::EnvironmentExtends::None),
8772                    runners: None,
8773                    resolvers: Some(vec![
8774                        fdecl::ResolverRegistration {
8775                            resolver: Some("pkg_resolver".to_string()),
8776                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8777                                name: "a".to_string(),
8778                                collection: None,
8779                            })),
8780                            scheme: Some("fuchsia-pkg".to_string()),
8781                            ..Default::default()
8782                        },
8783                    ]),
8784                    stop_timeout_ms: Some(1234),
8785                    ..Default::default()
8786                }]);
8787                decl.children = Some(vec![
8788                    fdecl::Child {
8789                        name: Some("a".to_string()),
8790                        startup: Some(fdecl::StartupMode::Lazy),
8791                        on_terminate: None,
8792                        url: Some("fuchsia-pkg://child-a".to_string()),
8793                        environment: None,
8794                        ..Default::default()
8795                    },
8796                    fdecl::Child {
8797                        name: Some("b".to_string()),
8798                        startup: Some(fdecl::StartupMode::Lazy),
8799                        on_terminate: None,
8800                        url: Some("fuchsia-pkg://child-b".to_string()),
8801                        environment: Some("env".to_string()),
8802                        ..Default::default()
8803                    },
8804                ]);
8805                decl.offers = Some(vec![fdecl::Offer::Service(fdecl::OfferService {
8806                    source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8807                        name: "b".to_string(),
8808                        collection: None,
8809                    })),
8810                    source_name: Some("thing".to_string()),
8811                    target: Some(fdecl::Ref::Child(
8812                    fdecl::ChildRef {
8813                        name: "a".to_string(),
8814                        collection: None,
8815                    }
8816                    )),
8817                    target_name: Some("thing".to_string()),
8818                    ..Default::default()
8819                })]);
8820                decl
8821            },
8822            result = Err(ErrorList::new(vec![
8823                Error::dependency_cycle(
8824                    directed_graph::Error::CyclesDetected([vec!["child a", "environment env", "child b", "child a"]].iter().cloned().collect()).format_cycle()
8825                ),
8826            ])),
8827        },
8828        test_validate_environment_debug_empty => {
8829            input = {
8830                let mut decl = new_component_decl();
8831                decl.environments = Some(vec![
8832                    fdecl::Environment {
8833                        name: Some("a".to_string()),
8834                        extends: Some(fdecl::EnvironmentExtends::None),
8835                        stop_timeout_ms: Some(2),
8836                        debug_capabilities:Some(vec![
8837                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8838                                source: None,
8839                                source_name: None,
8840                                target_name: None,
8841                                ..Default::default()
8842                            }),
8843                    ]),
8844                    ..Default::default()
8845                }]);
8846                decl
8847            },
8848            result = Err(ErrorList::new(vec![
8849                Error::missing_field(DeclType::DebugProtocolRegistration, "source"),
8850                Error::missing_field(DeclType::DebugProtocolRegistration, "source_name"),
8851                Error::missing_field(DeclType::DebugProtocolRegistration, "target_name"),
8852            ])),
8853        },
8854        test_validate_environment_debug_log_identifier => {
8855            input = {
8856                let mut decl = new_component_decl();
8857                decl.environments = Some(vec![
8858                    fdecl::Environment {
8859                        name: Some("a".to_string()),
8860                        extends: Some(fdecl::EnvironmentExtends::None),
8861                        stop_timeout_ms: Some(2),
8862                        debug_capabilities:Some(vec![
8863                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8864                                source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8865                                    name: "a".repeat(256),
8866                                    collection: None,
8867                                })),
8868                                source_name: Some(format!("{}", "a".repeat(256))),
8869                                target_name: Some(format!("{}", "b".repeat(256))),
8870                                ..Default::default()
8871                            }),
8872                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8873                                source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8874                                source_name: Some("a".to_string()),
8875                                target_name: Some(format!("{}", "b".repeat(256))),
8876                                ..Default::default()
8877                            }),
8878                    ]),
8879                    ..Default::default()
8880                }]);
8881                decl
8882            },
8883            result = Err(ErrorList::new(vec![
8884                Error::field_too_long(DeclType::DebugProtocolRegistration, "source.child.name"),
8885                Error::field_too_long(DeclType::DebugProtocolRegistration, "source_name"),
8886                Error::field_too_long(DeclType::DebugProtocolRegistration, "target_name"),
8887                Error::field_too_long(DeclType::DebugProtocolRegistration, "target_name"),
8888            ])),
8889        },
8890        test_validate_environment_debug_log_extraneous => {
8891            input = {
8892                let mut decl = new_component_decl();
8893                decl.environments = Some(vec![
8894                    fdecl::Environment {
8895                        name: Some("a".to_string()),
8896                        extends: Some(fdecl::EnvironmentExtends::None),
8897                        stop_timeout_ms: Some(2),
8898                        debug_capabilities:Some(vec![
8899                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8900                                source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8901                                    name: "logger".to_string(),
8902                                    collection: Some("modular".to_string()),
8903                                })),
8904                                source_name: Some("fuchsia.logger.Log".to_string()),
8905                                target_name: Some("fuchsia.logger.Log".to_string()),
8906                                ..Default::default()
8907                            }),
8908                    ]),
8909                    ..Default::default()
8910                }]);
8911                decl
8912            },
8913            result = Err(ErrorList::new(vec![
8914                Error::extraneous_field(DeclType::DebugProtocolRegistration, "source.child.collection"),
8915            ])),
8916        },
8917        test_validate_environment_debug_log_invalid_identifiers => {
8918            input = {
8919                let mut decl = new_component_decl();
8920                decl.environments = Some(vec![
8921                    fdecl::Environment {
8922                        name: Some("a".to_string()),
8923                        extends: Some(fdecl::EnvironmentExtends::None),
8924                        stop_timeout_ms: Some(2),
8925                        debug_capabilities:Some(vec![
8926                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8927                                source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8928                                    name: "^bad".to_string(),
8929                                    collection: None,
8930                                })),
8931                                source_name: Some("foo/".to_string()),
8932                                target_name: Some("/".to_string()),
8933                                ..Default::default()
8934                            }),
8935                    ]),
8936                    ..Default::default()
8937                }]);
8938                decl
8939            },
8940            result = Err(ErrorList::new(vec![
8941                Error::invalid_field(DeclType::DebugProtocolRegistration, "source.child.name"),
8942                Error::invalid_field(DeclType::DebugProtocolRegistration, "source_name"),
8943                Error::invalid_field(DeclType::DebugProtocolRegistration, "target_name"),
8944            ])),
8945        },
8946        test_validate_environment_debug_log_invalid_child => {
8947            input = {
8948                let mut decl = new_component_decl();
8949                decl.environments = Some(vec![
8950                    fdecl::Environment {
8951                        name: Some("a".to_string()),
8952                        extends: Some(fdecl::EnvironmentExtends::None),
8953                        stop_timeout_ms: Some(2),
8954                        debug_capabilities:Some(vec![
8955                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8956                                source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8957                                    name: "logger".to_string(),
8958                                    collection: None,
8959                                })),
8960                                source_name: Some("fuchsia.logger.LegacyLog".to_string()),
8961                                target_name: Some("fuchsia.logger.LegacyLog".to_string()),
8962                                ..Default::default()
8963                            }),
8964                    ]),
8965                    ..Default::default()
8966                }]);
8967                decl.children = Some(vec![
8968                    fdecl::Child {
8969                        name: Some("netstack".to_string()),
8970                        url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
8971                        startup: Some(fdecl::StartupMode::Lazy),
8972                        on_terminate: None,
8973                        environment: None,
8974                        ..Default::default()
8975                    },
8976                ]);
8977                decl
8978            },
8979            result = Err(ErrorList::new(vec![
8980                Error::invalid_child(DeclType::DebugProtocolRegistration, "source", "logger"),
8981
8982            ])),
8983        },
8984        test_validate_environment_debug_source_capability => {
8985            input = {
8986                let mut decl = new_component_decl();
8987                decl.environments = Some(vec![
8988                    fdecl::Environment {
8989                        name: Some("a".to_string()),
8990                        extends: Some(fdecl::EnvironmentExtends::None),
8991                        stop_timeout_ms: Some(2),
8992                        debug_capabilities:Some(vec![
8993                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8994                                source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
8995                                    name: "storage".to_string(),
8996                                })),
8997                                source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
8998                                target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
8999                                ..Default::default()
9000                            }),
9001                    ]),
9002                    ..Default::default()
9003                }]);
9004                decl
9005            },
9006            result = Err(ErrorList::new(vec![
9007                Error::invalid_field(DeclType::DebugProtocolRegistration, "source"),
9008            ])),
9009        },
9010
9011        // children
9012        test_validate_children_empty => {
9013            input = {
9014                let mut decl = new_component_decl();
9015                decl.children = Some(vec![fdecl::Child{
9016                    name: None,
9017                    url: None,
9018                    startup: None,
9019                    on_terminate: None,
9020                    environment: None,
9021                    ..Default::default()
9022                }]);
9023                decl
9024            },
9025            result = Err(ErrorList::new(vec![
9026                Error::missing_field(DeclType::Child, "name"),
9027                Error::missing_field(DeclType::Child, "url"),
9028                Error::missing_field(DeclType::Child, "startup"),
9029                // `on_terminate` is allowed to be None
9030            ])),
9031        },
9032        test_validate_children_invalid_identifiers => {
9033            input = {
9034                let mut decl = new_component_decl();
9035                decl.children = Some(vec![fdecl::Child{
9036                    name: Some("^bad".to_string()),
9037                    url: Some("scheme://invalid-port:99999999/path#frag".to_string()),
9038                    startup: Some(fdecl::StartupMode::Lazy),
9039                    on_terminate: None,
9040                    environment: None,
9041                    ..Default::default()
9042                }]);
9043                decl
9044            },
9045            result = Err(ErrorList::new(vec![
9046                Error::invalid_field(DeclType::Child, "name"),
9047                Error::invalid_url(DeclType::Child, "url", "\"scheme://invalid-port:99999999/path#frag\": Malformed URL: InvalidPort."),
9048            ])),
9049        },
9050        test_validate_children_long_identifiers => {
9051            input = {
9052                let mut decl = new_component_decl();
9053                decl.children = Some(vec![fdecl::Child{
9054                    name: Some("a".repeat(1025)),
9055                    url: Some(format!("fuchsia-pkg://{}", "a".repeat(4083))),
9056                    startup: Some(fdecl::StartupMode::Lazy),
9057                    on_terminate: None,
9058                    environment: Some("a".repeat(1025)),
9059                    ..Default::default()
9060                }]);
9061                decl
9062            },
9063            result = Err(ErrorList::new(vec![
9064                Error::field_too_long(DeclType::Child, "name"),
9065                Error::field_too_long(DeclType::Child, "url"),
9066                Error::field_too_long(DeclType::Child, "environment"),
9067                Error::invalid_environment(DeclType::Child, "environment", "a".repeat(1025)),
9068            ])),
9069        },
9070        test_validate_child_references_unknown_env => {
9071            input = {
9072                let mut decl = new_component_decl();
9073                decl.children = Some(vec![fdecl::Child{
9074                    name: Some("foo".to_string()),
9075                    url: Some("fuchsia-pkg://foo".to_string()),
9076                    startup: Some(fdecl::StartupMode::Lazy),
9077                    on_terminate: None,
9078                    environment: Some("test_env".to_string()),
9079                    ..Default::default()
9080                }]);
9081                decl
9082            },
9083            result = Err(ErrorList::new(vec![
9084                Error::invalid_environment(DeclType::Child, "environment", "test_env"),
9085            ])),
9086        },
9087
9088        // collections
9089        test_validate_collections_empty => {
9090            input = {
9091                let mut decl = new_component_decl();
9092                decl.collections = Some(vec![fdecl::Collection{
9093                    name: None,
9094                    durability: None,
9095                    environment: None,
9096                    allowed_offers: None,
9097                    allow_long_names: None,
9098                    ..Default::default()
9099                }]);
9100                decl
9101            },
9102            result = Err(ErrorList::new(vec![
9103                Error::missing_field(DeclType::Collection, "name"),
9104                Error::missing_field(DeclType::Collection, "durability"),
9105            ])),
9106        },
9107        test_validate_collections_invalid_identifiers => {
9108            input = {
9109                let mut decl = new_component_decl();
9110                decl.collections = Some(vec![fdecl::Collection{
9111                    name: Some("^bad".to_string()),
9112                    durability: Some(fdecl::Durability::Transient),
9113                    environment: None,
9114                    allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
9115                    allow_long_names: None,
9116                    ..Default::default()
9117                }]);
9118                decl
9119            },
9120            result = Err(ErrorList::new(vec![
9121                Error::invalid_field(DeclType::Collection, "name"),
9122            ])),
9123        },
9124        test_validate_collections_long_identifiers => {
9125            input = {
9126                let mut decl = new_component_decl();
9127                decl.collections = Some(vec![fdecl::Collection{
9128                    name: Some("a".repeat(1025)),
9129                    durability: Some(fdecl::Durability::Transient),
9130                    environment: None,
9131                    allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
9132                    allow_long_names: None,
9133                    ..Default::default()
9134                }]);
9135                decl
9136            },
9137            result = Err(ErrorList::new(vec![
9138                Error::field_too_long(DeclType::Collection, "name"),
9139            ])),
9140        },
9141        test_validate_collection_references_unknown_env => {
9142            input = {
9143                let mut decl = new_component_decl();
9144                decl.collections = Some(vec![fdecl::Collection {
9145                    name: Some("foo".to_string()),
9146                    durability: Some(fdecl::Durability::Transient),
9147                    environment: Some("test_env".to_string()),
9148                    allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
9149                    allow_long_names: None,
9150                    ..Default::default()
9151                }]);
9152                decl
9153            },
9154            result = Err(ErrorList::new(vec![
9155                Error::invalid_environment(DeclType::Collection, "environment", "test_env"),
9156            ])),
9157        },
9158
9159        // capabilities
9160        test_validate_capabilities_empty => {
9161            input = {
9162                let mut decl = new_component_decl();
9163                decl.capabilities = Some(vec![
9164                    fdecl::Capability::Service(fdecl::Service {
9165                        name: None,
9166                        source_path: None,
9167                        ..Default::default()
9168                    }),
9169                    fdecl::Capability::Protocol(fdecl::Protocol {
9170                        name: None,
9171                        source_path: None,
9172                        ..Default::default()
9173                    }),
9174                    fdecl::Capability::Directory(fdecl::Directory {
9175                        name: None,
9176                        source_path: None,
9177                        rights: None,
9178                        ..Default::default()
9179                    }),
9180                    fdecl::Capability::Storage(fdecl::Storage {
9181                        name: None,
9182                        source: None,
9183                        backing_dir: None,
9184                        subdir: None,
9185                        storage_id: None,
9186                        ..Default::default()
9187                    }),
9188                    fdecl::Capability::Runner(fdecl::Runner {
9189                        name: None,
9190                        source_path: None,
9191                        ..Default::default()
9192                    }),
9193                    fdecl::Capability::Resolver(fdecl::Resolver {
9194                        name: None,
9195                        source_path: None,
9196                        ..Default::default()
9197                    }),
9198                    fdecl::Capability::Dictionary(fdecl::Dictionary {
9199                        ..Default::default()
9200                    }),
9201                ]);
9202                decl
9203            },
9204            result = Err(ErrorList::new(vec![
9205                Error::missing_field(DeclType::Dictionary, "name"),
9206                Error::missing_field(DeclType::Service, "name"),
9207                Error::missing_field(DeclType::Service, "source_path"),
9208                Error::missing_field(DeclType::Protocol, "name"),
9209                Error::missing_field(DeclType::Protocol, "source_path"),
9210                Error::missing_field(DeclType::Directory, "name"),
9211                Error::missing_field(DeclType::Directory, "source_path"),
9212                Error::missing_field(DeclType::Directory, "rights"),
9213                Error::missing_field(DeclType::Storage, "source"),
9214                Error::missing_field(DeclType::Storage, "name"),
9215                Error::missing_field(DeclType::Storage, "storage_id"),
9216                Error::missing_field(DeclType::Storage, "backing_dir"),
9217                Error::missing_field(DeclType::Runner, "name"),
9218                Error::missing_field(DeclType::Runner, "source_path"),
9219                Error::missing_field(DeclType::Resolver, "name"),
9220                Error::missing_field(DeclType::Resolver, "source_path"),
9221            ])),
9222        },
9223        test_validate_capabilities_invalid_identifiers => {
9224            input = {
9225                let mut decl = new_component_decl();
9226                decl.capabilities = Some(vec![
9227                    fdecl::Capability::Service(fdecl::Service {
9228                        name: Some("^bad".to_string()),
9229                        source_path: Some("&bad".to_string()),
9230                        ..Default::default()
9231                    }),
9232                    fdecl::Capability::Protocol(fdecl::Protocol {
9233                        name: Some("^bad".to_string()),
9234                        source_path: Some("&bad".to_string()),
9235                        ..Default::default()
9236                    }),
9237                    fdecl::Capability::Directory(fdecl::Directory {
9238                        name: Some("^bad".to_string()),
9239                        source_path: Some("&bad".to_string()),
9240                        rights: Some(fio::Operations::CONNECT),
9241                        ..Default::default()
9242                    }),
9243                    fdecl::Capability::Storage(fdecl::Storage {
9244                        name: Some("^bad".to_string()),
9245                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9246                            name: "/bad".to_string()
9247                        })),
9248                        backing_dir: Some("&bad".to_string()),
9249                        subdir: None,
9250                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9251                        ..Default::default()
9252                    }),
9253                    fdecl::Capability::Runner(fdecl::Runner {
9254                        name: Some("^bad".to_string()),
9255                        source_path: Some("&bad".to_string()),
9256                        ..Default::default()
9257                    }),
9258                    fdecl::Capability::Resolver(fdecl::Resolver {
9259                        name: Some("^bad".to_string()),
9260                        source_path: Some("&bad".to_string()),
9261                        ..Default::default()
9262                    }),
9263                    fdecl::Capability::Dictionary(fdecl::Dictionary {
9264                        name: Some("^bad".to_string()),
9265                        ..Default::default()
9266                    }),
9267                ]);
9268                decl
9269            },
9270            result = Err(ErrorList::new(vec![
9271                Error::invalid_field(DeclType::Dictionary, "name"),
9272                Error::invalid_field(DeclType::Service, "name"),
9273                Error::invalid_field(DeclType::Service, "source_path"),
9274                Error::invalid_field(DeclType::Protocol, "name"),
9275                Error::invalid_field(DeclType::Protocol, "source_path"),
9276                Error::invalid_field(DeclType::Directory, "name"),
9277                Error::invalid_field(DeclType::Directory, "source_path"),
9278                Error::invalid_field(DeclType::Storage, "source"),
9279                Error::invalid_field(DeclType::Storage, "name"),
9280                Error::invalid_field(DeclType::Storage, "backing_dir"),
9281                Error::invalid_field(DeclType::Runner, "name"),
9282                Error::invalid_field(DeclType::Runner, "source_path"),
9283                Error::invalid_field(DeclType::Resolver, "name"),
9284                Error::invalid_field(DeclType::Resolver, "source_path"),
9285            ])),
9286        },
9287        test_validate_capabilities_invalid_child => {
9288            input = {
9289                let mut decl = new_component_decl();
9290                decl.capabilities = Some(vec![
9291                    fdecl::Capability::Storage(fdecl::Storage {
9292                        name: Some("foo".to_string()),
9293                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9294                            name: "invalid".to_string(),
9295                        })),
9296                        backing_dir: Some("foo".to_string()),
9297                        subdir: None,
9298                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9299                        ..Default::default()
9300                    }),
9301                ]);
9302                decl
9303            },
9304            result = Err(ErrorList::new(vec![
9305                Error::invalid_field(DeclType::Storage, "source"),
9306            ])),
9307        },
9308        test_validate_capabilities_long_identifiers => {
9309            input = {
9310                let mut decl = new_component_decl();
9311                decl.capabilities = Some(vec![
9312                    fdecl::Capability::Service(fdecl::Service {
9313                        name: Some("a".repeat(256)),
9314                        source_path: Some("/c".repeat(2048)),
9315                        ..Default::default()
9316                    }),
9317                    fdecl::Capability::Protocol(fdecl::Protocol {
9318                        name: Some("a".repeat(256)),
9319                        source_path: Some("/c".repeat(2048)),
9320                        ..Default::default()
9321                    }),
9322                    fdecl::Capability::Directory(fdecl::Directory {
9323                        name: Some("a".repeat(256)),
9324                        source_path: Some("/c".repeat(2048)),
9325                        rights: Some(fio::Operations::CONNECT),
9326                        ..Default::default()
9327                    }),
9328                    fdecl::Capability::Storage(fdecl::Storage {
9329                        name: Some("a".repeat(256)),
9330                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
9331                            name: "b".repeat(256),
9332                            collection: None,
9333                        })),
9334                        backing_dir: Some(format!("{}", "c".repeat(256))),
9335                        subdir: None,
9336                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9337                        ..Default::default()
9338                    }),
9339                    fdecl::Capability::Runner(fdecl::Runner {
9340                        name: Some("a".repeat(256)),
9341                        source_path: Some("/c".repeat(2048)),
9342                        ..Default::default()
9343                    }),
9344                    fdecl::Capability::Resolver(fdecl::Resolver {
9345                        name: Some("a".repeat(256)),
9346                        source_path: Some("/c".repeat(2048)),
9347                        ..Default::default()
9348                    }),
9349                    fdecl::Capability::Dictionary(fdecl::Dictionary {
9350                        name: Some("a".repeat(256)),
9351                        ..Default::default()
9352                    }),
9353                ]);
9354                decl
9355            },
9356            result = Err(ErrorList::new(vec![
9357                Error::field_too_long(DeclType::Dictionary, "name"),
9358                Error::field_too_long(DeclType::Service, "name"),
9359                Error::field_too_long(DeclType::Service, "source_path"),
9360                Error::field_too_long(DeclType::Protocol, "name"),
9361                Error::field_too_long(DeclType::Protocol, "source_path"),
9362                Error::field_too_long(DeclType::Directory, "name"),
9363                Error::field_too_long(DeclType::Directory, "source_path"),
9364                Error::field_too_long(DeclType::Storage, "source.child.name"),
9365                Error::field_too_long(DeclType::Storage, "name"),
9366                Error::field_too_long(DeclType::Storage, "backing_dir"),
9367                Error::field_too_long(DeclType::Runner, "name"),
9368                Error::field_too_long(DeclType::Runner, "source_path"),
9369                Error::field_too_long(DeclType::Resolver, "name"),
9370                Error::field_too_long(DeclType::Resolver, "source_path"),
9371            ])),
9372        },
9373        test_validate_capabilities_duplicate_name => {
9374            input = {
9375                let mut decl = new_component_decl();
9376                decl.capabilities = Some(vec![
9377                    fdecl::Capability::Service(fdecl::Service {
9378                        name: Some("service".to_string()),
9379                        source_path: Some("/service".to_string()),
9380                        ..Default::default()
9381                    }),
9382                    fdecl::Capability::Service(fdecl::Service {
9383                        name: Some("service".to_string()),
9384                        source_path: Some("/service".to_string()),
9385                        ..Default::default()
9386                    }),
9387                    fdecl::Capability::Protocol(fdecl::Protocol {
9388                        name: Some("protocol".to_string()),
9389                        source_path: Some("/protocol".to_string()),
9390                        ..Default::default()
9391                    }),
9392                    fdecl::Capability::Protocol(fdecl::Protocol {
9393                        name: Some("protocol".to_string()),
9394                        source_path: Some("/protocol".to_string()),
9395                        ..Default::default()
9396                    }),
9397                    fdecl::Capability::Directory(fdecl::Directory {
9398                        name: Some("directory".to_string()),
9399                        source_path: Some("/directory".to_string()),
9400                        rights: Some(fio::Operations::CONNECT),
9401                        ..Default::default()
9402                    }),
9403                    fdecl::Capability::Directory(fdecl::Directory {
9404                        name: Some("directory".to_string()),
9405                        source_path: Some("/directory".to_string()),
9406                        rights: Some(fio::Operations::CONNECT),
9407                        ..Default::default()
9408                    }),
9409                    fdecl::Capability::Storage(fdecl::Storage {
9410                        name: Some("storage".to_string()),
9411                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
9412                        backing_dir: Some("directory".to_string()),
9413                        subdir: None,
9414                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9415                        ..Default::default()
9416                    }),
9417                    fdecl::Capability::Storage(fdecl::Storage {
9418                        name: Some("storage".to_string()),
9419                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
9420                        backing_dir: Some("directory".to_string()),
9421                        subdir: None,
9422                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9423                        ..Default::default()
9424                    }),
9425                    fdecl::Capability::Runner(fdecl::Runner {
9426                        name: Some("runner".to_string()),
9427                        source_path: Some("/runner".to_string()),
9428                        ..Default::default()
9429                    }),
9430                    fdecl::Capability::Runner(fdecl::Runner {
9431                        name: Some("runner".to_string()),
9432                        source_path: Some("/runner".to_string()),
9433                        ..Default::default()
9434                    }),
9435                    fdecl::Capability::Resolver(fdecl::Resolver {
9436                        name: Some("resolver".to_string()),
9437                        source_path: Some("/resolver".to_string()),
9438                        ..Default::default()
9439                    }),
9440                    fdecl::Capability::Resolver(fdecl::Resolver {
9441                        name: Some("resolver".to_string()),
9442                        source_path: Some("/resolver".to_string()),
9443                        ..Default::default()
9444                    }),
9445                    fdecl::Capability::Dictionary(fdecl::Dictionary {
9446                        name: Some("dictionary".to_string()),
9447                        ..Default::default()
9448                    }),
9449                    fdecl::Capability::Dictionary(fdecl::Dictionary {
9450                        name: Some("dictionary".to_string()),
9451                        ..Default::default()
9452                    }),
9453                ]);
9454                decl
9455            },
9456            result = Err(ErrorList::new(vec![
9457                Error::duplicate_field(DeclType::Dictionary, "name", "dictionary"),
9458                Error::duplicate_field(DeclType::Service, "name", "service"),
9459                Error::duplicate_field(DeclType::Protocol, "name", "protocol"),
9460                Error::duplicate_field(DeclType::Directory, "name", "directory"),
9461                Error::duplicate_field(DeclType::Storage, "name", "storage"),
9462                Error::duplicate_field(DeclType::Runner, "name", "runner"),
9463                Error::duplicate_field(DeclType::Resolver, "name", "resolver"),
9464            ])),
9465        },
9466        test_validate_invalid_service_aggregation_conflicting_filter => {
9467            input = {
9468                let mut decl = new_component_decl();
9469                decl.offers = Some(vec![
9470                    fdecl::Offer::Service(fdecl::OfferService {
9471                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9472                            name: "coll_a".to_string()
9473                        })),
9474                        source_name: Some("fuchsia.logger.Log".to_string()),
9475                        target: Some(fdecl::Ref::Child(
9476                            fdecl::ChildRef {
9477                                name: "child_c".to_string(),
9478                                collection: None,
9479                            }
9480                        )),
9481                        target_name: Some("fuchsia.logger.Log1".to_string()),
9482                        source_instance_filter: Some(vec!["default".to_string()]),
9483                        ..Default::default()
9484                    }),
9485                    fdecl::Offer::Service(fdecl::OfferService {
9486                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9487                            name: "coll_b".to_string()
9488                        })),
9489                        source_name: Some("fuchsia.logger.Log".to_string()),
9490                        target: Some(fdecl::Ref::Child(
9491                            fdecl::ChildRef {
9492                                name: "child_c".to_string(),
9493                                collection: None,
9494                            }
9495                        )),
9496                        target_name: Some("fuchsia.logger.Log1".to_string()),
9497                        source_instance_filter: Some(vec!["default".to_string()]),
9498                        ..Default::default()
9499                    }),
9500                ]);
9501                decl.collections = Some(vec![
9502                    fdecl::Collection {
9503                        name: Some("coll_a".to_string()),
9504                        durability: Some(fdecl::Durability::Transient),
9505                        ..Default::default()
9506                    },
9507                    fdecl::Collection {
9508                        name: Some("coll_b".to_string()),
9509                        durability: Some(fdecl::Durability::Transient),
9510                        ..Default::default()
9511                    },
9512                ]);
9513                decl.children = Some(vec![
9514                    fdecl::Child {
9515                        name: Some("child_c".to_string()),
9516                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9517                        startup: Some(fdecl::StartupMode::Lazy),
9518                        ..Default::default()
9519                    },
9520                ]);
9521                decl
9522            },
9523            result = Err(ErrorList::new(vec![
9524                Error::invalid_aggregate_offer("Conflicting source_instance_filter in aggregate \
9525                   service offer, instance_name 'default' seen in filter lists multiple times"),
9526            ])),
9527        },
9528
9529        test_validate_invalid_service_aggregation_conflicting_source_name => {
9530            input = {
9531                let mut decl = new_component_decl();
9532                decl.offers = Some(vec![
9533                    fdecl::Offer::Service(fdecl::OfferService {
9534                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9535                            name: "coll_a".into()
9536                        })),
9537                        source_name: Some("fuchsia.logger.Log".to_string()),
9538                        target: Some(fdecl::Ref::Child(
9539                            fdecl::ChildRef {
9540                                name: "child_c".to_string(),
9541                                collection: None,
9542                            }
9543                        )),
9544                        target_name: Some("fuchsia.logger.Log2".to_string()),
9545                        source_instance_filter: Some(vec!["default2".to_string()]),
9546                        ..Default::default()
9547                    }),
9548                    fdecl::Offer::Service(fdecl::OfferService {
9549                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9550                            name: "coll_b".into()
9551                        })),
9552                        source_name: Some("fuchsia.logger.LogAlt".to_string()),
9553                        target: Some(fdecl::Ref::Child(
9554                            fdecl::ChildRef {
9555                                name: "child_c".to_string(),
9556                                collection: None,
9557                            }
9558                        )),
9559                        target_name: Some("fuchsia.logger.Log2".to_string()),
9560                        source_instance_filter: Some(vec!["default".to_string()]),
9561                        ..Default::default()
9562                    })
9563                ]);
9564                decl.collections = Some(vec![
9565                    fdecl::Collection {
9566                        name: Some("coll_a".to_string()),
9567                        durability: Some(fdecl::Durability::Transient),
9568                        ..Default::default()
9569                    },
9570                    fdecl::Collection {
9571                        name: Some("coll_b".to_string()),
9572                        durability: Some(fdecl::Durability::Transient),
9573                        ..Default::default()
9574                    },
9575                ]);
9576                decl.children = Some(vec![
9577                    fdecl::Child {
9578                        name: Some("child_c".to_string()),
9579                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9580                        startup: Some(fdecl::StartupMode::Lazy),
9581                        on_terminate: None,
9582                        environment: None,
9583                        ..Default::default()
9584                    },
9585                ]);
9586                decl
9587            },
9588            result = Err(ErrorList::new(vec![
9589                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."),
9590            ])),
9591        },
9592
9593        test_validate_resolvers_missing_from_offer => {
9594            input = {
9595                let mut decl = new_component_decl();
9596                decl.offers = Some(vec![fdecl::Offer::Resolver(fdecl::OfferResolver {
9597                    source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
9598                    source_name: Some("a".to_string()),
9599                    target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
9600                    target_name: Some("a".to_string()),
9601                    ..Default::default()
9602                })]);
9603                decl.children = Some(vec![fdecl::Child {
9604                    name: Some("child".to_string()),
9605                    url: Some("test:///child".to_string()),
9606                    startup: Some(fdecl::StartupMode::Eager),
9607                    on_terminate: None,
9608                    environment: None,
9609                    ..Default::default()
9610                }]);
9611                decl
9612            },
9613            result = Err(ErrorList::new(vec![
9614                Error::invalid_capability(DeclType::OfferResolver, "source", "a"),
9615            ])),
9616        },
9617        test_validate_resolvers_missing_from_expose => {
9618            input = {
9619                let mut decl = new_component_decl();
9620                decl.exposes = Some(vec![fdecl::Expose::Resolver(fdecl::ExposeResolver {
9621                    source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
9622                    source_name: Some("a".to_string()),
9623                    target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
9624                    target_name: Some("a".to_string()),
9625                    ..Default::default()
9626                })]);
9627                decl
9628            },
9629            result = Err(ErrorList::new(vec![
9630                Error::invalid_capability(DeclType::ExposeResolver, "source", "a"),
9631            ])),
9632        },
9633
9634        test_validate_config_missing_config => {
9635            input = {
9636                let mut decl = new_component_decl();
9637                decl.config = Some(fdecl::ConfigSchema{
9638                    fields: None,
9639                    checksum: None,
9640                    value_source: None,
9641                    ..Default::default()
9642                });
9643                decl
9644            },
9645            result = Err(ErrorList::new(vec![
9646                Error::missing_field(DeclType::ConfigSchema, "fields"),
9647                Error::missing_field(DeclType::ConfigSchema, "checksum"),
9648                Error::missing_field(DeclType::ConfigSchema, "value_source"),
9649            ])),
9650        },
9651
9652        test_validate_config_missing_config_field => {
9653            input = {
9654                let mut decl = new_component_decl();
9655                decl.config = Some(fdecl::ConfigSchema{
9656                    fields: Some(vec![
9657                        fdecl::ConfigField {
9658                            key: None,
9659                            type_: None,
9660                            ..Default::default()
9661                        }
9662                    ]),
9663                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9664                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9665                    ..Default::default()
9666                });
9667                decl
9668            },
9669            result = Err(ErrorList::new(vec![
9670                Error::missing_field(DeclType::ConfigField, "key"),
9671                Error::missing_field(DeclType::ConfigField, "value_type"),
9672            ])),
9673        },
9674
9675        test_validate_config_bool => {
9676            input = {
9677                let mut decl = new_component_decl();
9678                decl.config = Some(fdecl::ConfigSchema{
9679                    fields: Some(vec![
9680                        fdecl::ConfigField {
9681                            key: Some("test".to_string()),
9682                            type_: Some(fdecl::ConfigType {
9683                                layout: fdecl::ConfigTypeLayout::Bool,
9684                                parameters: Some(vec![]),
9685                                constraints: vec![]
9686                            }),
9687                            ..Default::default()
9688                        }
9689                    ]),
9690                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9691                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9692                    ..Default::default()
9693                });
9694                decl
9695            },
9696            result = Ok(()),
9697        },
9698
9699        test_validate_config_bool_extra_constraint => {
9700            input = {
9701                let mut decl = new_component_decl();
9702                decl.config = Some(fdecl::ConfigSchema{
9703                    fields: Some(vec![
9704                        fdecl::ConfigField {
9705                            key: Some("test".to_string()),
9706                            type_: Some(fdecl::ConfigType {
9707                                layout: fdecl::ConfigTypeLayout::Bool,
9708                                parameters: Some(vec![]),
9709                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9710                            }),
9711                            ..Default::default()
9712                        }
9713                    ]),
9714                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9715                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9716                    ..Default::default()
9717                });
9718                decl
9719            },
9720            result = Err(ErrorList::new(vec![
9721                Error::extraneous_field(DeclType::ConfigType, "constraints")
9722            ])),
9723        },
9724
9725        test_validate_config_bool_missing_parameters => {
9726            input = {
9727                let mut decl = new_component_decl();
9728                decl.config = Some(fdecl::ConfigSchema{
9729                    fields: Some(vec![
9730                        fdecl::ConfigField {
9731                            key: Some("test".to_string()),
9732                            type_: Some(fdecl::ConfigType {
9733                                layout: fdecl::ConfigTypeLayout::Bool,
9734                                parameters: None,
9735                                constraints: vec![]
9736                            }),
9737                            ..Default::default()
9738                        }
9739                    ]),
9740                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9741                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9742                    ..Default::default()
9743                });
9744                decl
9745            },
9746            result = Err(ErrorList::new(vec![
9747                Error::missing_field(DeclType::ConfigType, "parameters")
9748            ])),
9749        },
9750
9751        test_validate_config_string => {
9752            input = {
9753                let mut decl = new_component_decl();
9754                decl.config = Some(fdecl::ConfigSchema{
9755                    fields: Some(vec![
9756                        fdecl::ConfigField {
9757                            key: Some("test".to_string()),
9758                            type_: Some(fdecl::ConfigType {
9759                                layout: fdecl::ConfigTypeLayout::String,
9760                                parameters: Some(vec![]),
9761                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9762                            }),
9763                            ..Default::default()
9764                        }
9765                    ]),
9766                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9767                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9768                    ..Default::default()
9769                });
9770                decl
9771            },
9772            result = Ok(()),
9773        },
9774
9775        test_validate_config_string_missing_parameter => {
9776            input = {
9777                let mut decl = new_component_decl();
9778                decl.config = Some(fdecl::ConfigSchema{
9779                    fields: Some(vec![
9780                        fdecl::ConfigField {
9781                            key: Some("test".to_string()),
9782                            type_: Some(fdecl::ConfigType {
9783                                layout: fdecl::ConfigTypeLayout::String,
9784                                parameters: None,
9785                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9786                            }),
9787                            ..Default::default()
9788                        }
9789                    ]),
9790                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9791                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9792                    ..Default::default()
9793                });
9794                decl
9795            },
9796            result = Err(ErrorList::new(vec![
9797                Error::missing_field(DeclType::ConfigType, "parameters")
9798            ])),
9799        },
9800
9801        test_validate_config_string_missing_constraint => {
9802            input = {
9803                let mut decl = new_component_decl();
9804                decl.config = Some(fdecl::ConfigSchema{
9805                    fields: Some(vec![
9806                        fdecl::ConfigField {
9807                            key: Some("test".to_string()),
9808                            type_: Some(fdecl::ConfigType {
9809                                layout: fdecl::ConfigTypeLayout::String,
9810                                parameters: Some(vec![]),
9811                                constraints: vec![]
9812                            }),
9813                            ..Default::default()
9814                        }
9815                    ]),
9816                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9817                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9818                    ..Default::default()
9819                });
9820                decl
9821            },
9822            result = Err(ErrorList::new(vec![
9823                Error::missing_field(DeclType::ConfigType, "constraints")
9824            ])),
9825        },
9826
9827        test_validate_config_string_extra_constraint => {
9828            input = {
9829                let mut decl = new_component_decl();
9830                decl.config = Some(fdecl::ConfigSchema{
9831                    fields: Some(vec![
9832                        fdecl::ConfigField {
9833                            key: Some("test".to_string()),
9834                            type_: Some(fdecl::ConfigType {
9835                                layout: fdecl::ConfigTypeLayout::String,
9836                                parameters: Some(vec![]),
9837                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10), fdecl::LayoutConstraint::MaxSize(10)]
9838                            }),
9839                            ..Default::default()
9840                        }
9841                    ]),
9842                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9843                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9844                    ..Default::default()
9845                });
9846                decl
9847            },
9848            result = Err(ErrorList::new(vec![
9849                Error::extraneous_field(DeclType::ConfigType, "constraints")
9850            ])),
9851        },
9852
9853        test_validate_config_vector_bool => {
9854            input = {
9855                let mut decl = new_component_decl();
9856                decl.config = Some(fdecl::ConfigSchema{
9857                    fields: Some(vec![
9858                        fdecl::ConfigField {
9859                            key: Some("test".to_string()),
9860                            type_: Some(fdecl::ConfigType {
9861                                layout: fdecl::ConfigTypeLayout::Vector,
9862                                parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9863                                    layout: fdecl::ConfigTypeLayout::Bool,
9864                                    parameters: Some(vec![]),
9865                                    constraints: vec![],
9866                                })]),
9867                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9868                            }),
9869                            ..Default::default()
9870                        }
9871                    ]),
9872                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9873                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9874                    ..Default::default()
9875                });
9876                decl
9877            },
9878            result = Ok(()),
9879        },
9880
9881        test_validate_config_vector_extra_parameter => {
9882            input = {
9883                let mut decl = new_component_decl();
9884                decl.config = Some(fdecl::ConfigSchema{
9885                    fields: Some(vec![
9886                        fdecl::ConfigField {
9887                            key: Some("test".to_string()),
9888                            type_: Some(fdecl::ConfigType {
9889                                layout: fdecl::ConfigTypeLayout::Vector,
9890                                parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9891                                    layout: fdecl::ConfigTypeLayout::Bool,
9892                                    parameters: Some(vec![]),
9893                                    constraints: vec![],
9894                                }), fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9895                                    layout: fdecl::ConfigTypeLayout::Uint8,
9896                                    parameters: Some(vec![]),
9897                                    constraints: vec![],
9898                                })]),
9899                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9900                            }),
9901                            ..Default::default()
9902                        }
9903                    ]),
9904                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9905                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9906                    ..Default::default()
9907                });
9908                decl
9909            },
9910            result = Err(ErrorList::new(vec![
9911                Error::extraneous_field(DeclType::ConfigType, "parameters")
9912            ])),
9913        },
9914
9915        test_validate_config_vector_missing_parameter => {
9916            input = {
9917                let mut decl = new_component_decl();
9918                decl.config = Some(fdecl::ConfigSchema{
9919                    fields: Some(vec![
9920                        fdecl::ConfigField {
9921                            key: Some("test".to_string()),
9922                            type_: Some(fdecl::ConfigType {
9923                                layout: fdecl::ConfigTypeLayout::Vector,
9924                                parameters: Some(vec![]),
9925                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9926                            }),
9927                            ..Default::default()
9928                        }
9929                    ]),
9930                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9931                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9932                    ..Default::default()
9933                });
9934                decl
9935            },
9936            result = Err(ErrorList::new(vec![
9937                Error::missing_field(DeclType::ConfigType, "parameters")
9938            ])),
9939        },
9940
9941        test_validate_config_vector_string => {
9942            input = {
9943                let mut decl = new_component_decl();
9944                decl.config = Some(fdecl::ConfigSchema{
9945                    fields: Some(vec![
9946                        fdecl::ConfigField {
9947                            key: Some("test".to_string()),
9948                            type_: Some(fdecl::ConfigType {
9949                                layout: fdecl::ConfigTypeLayout::Vector,
9950                                parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9951                                    layout: fdecl::ConfigTypeLayout::String,
9952                                    parameters: Some(vec![]),
9953                                    constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9954                                })]),
9955                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9956                            }),
9957                            ..Default::default()
9958                        }
9959                    ]),
9960                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9961                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9962                    ..Default::default()
9963                });
9964                decl
9965            },
9966            result = Ok(()),
9967        },
9968
9969        test_validate_config_vector_vector => {
9970            input = {
9971                let mut decl = new_component_decl();
9972                decl.config = Some(fdecl::ConfigSchema{
9973                    fields: Some(vec![
9974                        fdecl::ConfigField {
9975                            key: Some("test".to_string()),
9976                            type_: Some(fdecl::ConfigType {
9977                                layout: fdecl::ConfigTypeLayout::Vector,
9978                                parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9979                                    layout: fdecl::ConfigTypeLayout::Vector,
9980                                    parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9981                                        layout: fdecl::ConfigTypeLayout::String,
9982                                        parameters: Some(vec![]),
9983                                        constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9984                                    })]),
9985                                    constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9986                                })]),
9987                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9988                            }),
9989                            ..Default::default()
9990                        }
9991                    ]),
9992                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9993                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9994                    ..Default::default()
9995                });
9996                decl
9997            },
9998            result = Err(ErrorList::new(vec![
9999                Error::nested_vector()
10000            ])),
10001        },
10002
10003        test_validate_exposes_invalid_aggregation_different_availability => {
10004            input = {
10005                let mut decl = new_component_decl();
10006                decl.exposes = Some(vec![
10007                    fdecl::Expose::Service(fdecl::ExposeService {
10008                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10009                            name: "coll_a".into()
10010                        })),
10011                        source_name: Some("fuchsia.logger.Log".to_string()),
10012                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
10013                        target_name: Some("fuchsia.logger.Log".to_string()),
10014                        availability: Some(fdecl::Availability::Required),
10015                        ..Default::default()
10016                    }),
10017                    fdecl::Expose::Service(fdecl::ExposeService {
10018                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10019                            name: "coll_b".into()
10020                        })),
10021                        source_name: Some("fuchsia.logger.Log".to_string()),
10022                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
10023                        target_name: Some("fuchsia.logger.Log".to_string()),
10024                        availability: Some(fdecl::Availability::Optional),
10025                        ..Default::default()
10026                    })
10027                ]);
10028                decl.collections = Some(vec![
10029                    fdecl::Collection {
10030                        name: Some("coll_a".to_string()),
10031                        durability: Some(fdecl::Durability::Transient),
10032                        ..Default::default()
10033                    },
10034                    fdecl::Collection {
10035                        name: Some("coll_b".to_string()),
10036                        durability: Some(fdecl::Durability::Transient),
10037                        ..Default::default()
10038                    },
10039                ]);
10040                decl
10041            },
10042            result = Err(ErrorList::new(vec![
10043                Error::DifferentAvailabilityInAggregation(AvailabilityList(vec![
10044                    fdecl::Availability::Required,
10045                    fdecl::Availability::Optional,
10046                ]))
10047            ])),
10048        },
10049
10050        test_validate_offers_invalid_aggregation_different_availability => {
10051            input = {
10052                let mut decl = new_component_decl();
10053                decl.offers = Some(vec![
10054                    fdecl::Offer::Service(fdecl::OfferService {
10055                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10056                            name: "coll_a".into()
10057                        })),
10058                        source_name: Some("fuchsia.logger.Log".to_string()),
10059                        target: Some(fdecl::Ref::Child(
10060                            fdecl::ChildRef {
10061                                name: "child_c".to_string(),
10062                                collection: None,
10063                            }
10064                        )),
10065                        target_name: Some("fuchsia.logger.Log".to_string()),
10066                        source_instance_filter: Some(vec!["default".to_string()]),
10067                        availability: Some(fdecl::Availability::Required),
10068                        ..Default::default()
10069                    }),
10070                    fdecl::Offer::Service(fdecl::OfferService {
10071                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10072                            name: "coll_b".into()
10073                        })),
10074                        source_name: Some("fuchsia.logger.Log".to_string()),
10075                        target: Some(fdecl::Ref::Child(
10076                            fdecl::ChildRef {
10077                                name: "child_c".to_string(),
10078                                collection: None,
10079                            }
10080                        )),
10081                        target_name: Some("fuchsia.logger.Log".to_string()),
10082                        source_instance_filter: Some(vec!["a_different_default".to_string()]),
10083                        availability: Some(fdecl::Availability::Optional),
10084                        ..Default::default()
10085                    })
10086                ]);
10087                decl.collections = Some(vec![
10088                    fdecl::Collection {
10089                        name: Some("coll_a".to_string()),
10090                        durability: Some(fdecl::Durability::Transient),
10091                        ..Default::default()
10092                    },
10093                    fdecl::Collection {
10094                        name: Some("coll_b".to_string()),
10095                        durability: Some(fdecl::Durability::Transient),
10096                        ..Default::default()
10097                    },
10098                ]);
10099                decl.children = Some(vec![
10100                    fdecl::Child {
10101                        name: Some("child_c".to_string()),
10102                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
10103                        startup: Some(fdecl::StartupMode::Lazy),
10104                        on_terminate: None,
10105                        environment: None,
10106                        ..Default::default()
10107                    },
10108                ]);
10109                decl
10110            },
10111            result = Err(ErrorList::new(vec![
10112                Error::DifferentAvailabilityInAggregation(AvailabilityList(vec![
10113                    fdecl::Availability::Required,
10114                    fdecl::Availability::Optional,
10115                ]))
10116            ])),
10117        },
10118    }
10119
10120    test_validate_capabilities! {
10121        test_validate_capabilities_individually_ok => {
10122            input = vec![
10123                fdecl::Capability::Protocol(fdecl::Protocol {
10124                    name: Some("foo_svc".into()),
10125                    source_path: Some("/svc/foo".into()),
10126                    ..Default::default()
10127                }),
10128                fdecl::Capability::Directory(fdecl::Directory {
10129                    name: Some("foo_dir".into()),
10130                    source_path: Some("/foo".into()),
10131                    rights: Some(fio::Operations::CONNECT),
10132                    ..Default::default()
10133                }),
10134            ],
10135            as_builtin = false,
10136            result = Ok(()),
10137        },
10138        test_validate_capabilities_individually_err => {
10139            input = vec![
10140                fdecl::Capability::Protocol(fdecl::Protocol {
10141                    name: None,
10142                    source_path: None,
10143                    ..Default::default()
10144                }),
10145                fdecl::Capability::Directory(fdecl::Directory {
10146                    name: None,
10147                    source_path: None,
10148                    rights: None,
10149                    ..Default::default()
10150                }),
10151            ],
10152            as_builtin = false,
10153            result = Err(ErrorList::new(vec![
10154                Error::missing_field(DeclType::Protocol, "name"),
10155                Error::missing_field(DeclType::Protocol, "source_path"),
10156                Error::missing_field(DeclType::Directory, "name"),
10157                Error::missing_field(DeclType::Directory, "source_path"),
10158                Error::missing_field(DeclType::Directory, "rights"),
10159            ])),
10160        },
10161        test_validate_builtin_capabilities_individually_ok => {
10162            input = vec![
10163                fdecl::Capability::Protocol(fdecl::Protocol {
10164                    name: Some("foo_protocol".into()),
10165                    source_path: None,
10166                    ..Default::default()
10167                }),
10168                fdecl::Capability::Directory(fdecl::Directory {
10169                    name: Some("foo_dir".into()),
10170                    source_path: None,
10171                    rights: Some(fio::Operations::CONNECT),
10172                    ..Default::default()
10173                }),
10174                fdecl::Capability::Service(fdecl::Service {
10175                    name: Some("foo_svc".into()),
10176                    source_path: None,
10177                    ..Default::default()
10178                }),
10179                fdecl::Capability::Runner(fdecl::Runner {
10180                    name: Some("foo_runner".into()),
10181                    source_path: None,
10182                    ..Default::default()
10183                }),
10184                fdecl::Capability::Resolver(fdecl::Resolver {
10185                    name: Some("foo_resolver".into()),
10186                    source_path: None,
10187                    ..Default::default()
10188                }),
10189            ],
10190            as_builtin = true,
10191            result = Ok(()),
10192        },
10193        test_validate_builtin_capabilities_individually_err => {
10194            input = vec![
10195                fdecl::Capability::Protocol(fdecl::Protocol {
10196                    name: None,
10197                    source_path: Some("/svc/foo".into()),
10198                    ..Default::default()
10199                }),
10200                fdecl::Capability::Directory(fdecl::Directory {
10201                    name: None,
10202                    source_path: Some("/foo".into()),
10203                    rights: None,
10204                    ..Default::default()
10205                }),
10206                fdecl::Capability::Service(fdecl::Service {
10207                    name: None,
10208                    source_path: Some("/svc/foo".into()),
10209                    ..Default::default()
10210                }),
10211                fdecl::Capability::Runner(fdecl::Runner {
10212                    name: None,
10213                    source_path:  Some("/foo".into()),
10214                    ..Default::default()
10215                }),
10216                fdecl::Capability::Resolver(fdecl::Resolver {
10217                    name: None,
10218                    source_path:  Some("/foo".into()),
10219                    ..Default::default()
10220                }),
10221                fdecl::Capability::Storage(fdecl::Storage {
10222                    name: None,
10223                    ..Default::default()
10224                }),
10225            ],
10226            as_builtin = true,
10227            result = Err(ErrorList::new(vec![
10228                Error::missing_field(DeclType::Protocol, "name"),
10229                Error::extraneous_source_path(DeclType::Protocol, "/svc/foo"),
10230                Error::missing_field(DeclType::Directory, "name"),
10231                Error::extraneous_source_path(DeclType::Directory, "/foo"),
10232                Error::missing_field(DeclType::Directory, "rights"),
10233                Error::missing_field(DeclType::Service, "name"),
10234                Error::extraneous_source_path(DeclType::Service, "/svc/foo"),
10235                Error::missing_field(DeclType::Runner, "name"),
10236                Error::extraneous_source_path(DeclType::Runner, "/foo"),
10237                Error::missing_field(DeclType::Resolver, "name"),
10238                Error::extraneous_source_path(DeclType::Resolver, "/foo"),
10239                Error::CapabilityCannotBeBuiltin(DeclType::Storage),
10240            ])),
10241        },
10242        test_validate_delivery_type_ok => {
10243            input = vec![
10244                fdecl::Capability::Protocol(fdecl::Protocol {
10245                    name: Some("foo_svc1".into()),
10246                    source_path: Some("/svc/foo1".into()),
10247                    ..Default::default()
10248                }),
10249                fdecl::Capability::Protocol(fdecl::Protocol {
10250                    name: Some("foo_svc2".into()),
10251                    source_path: Some("/svc/foo2".into()),
10252                    delivery: Some(fdecl::DeliveryType::Immediate),
10253                    ..Default::default()
10254                }),
10255                fdecl::Capability::Protocol(fdecl::Protocol {
10256                    name: Some("foo_svc3".into()),
10257                    source_path: Some("/svc/foo3".into()),
10258                    delivery: Some(fdecl::DeliveryType::OnReadable),
10259                    ..Default::default()
10260                }),
10261            ],
10262            as_builtin = false,
10263            result = Ok(()),
10264        },
10265        test_validate_delivery_type_err => {
10266            input = vec![
10267                fdecl::Capability::Protocol(fdecl::Protocol {
10268                    name: Some("foo_svc".into()),
10269                    source_path: Some("/svc/foo".into()),
10270                    delivery: Some(fdecl::DeliveryType::unknown()),
10271                    ..Default::default()
10272                }),
10273            ],
10274            as_builtin = false,
10275            result = Err(ErrorList::new(vec![
10276                Error::invalid_field(DeclType::Protocol, "delivery"),
10277            ])),
10278        },
10279    }
10280
10281    /// Passes different source and availability options to `new_expose` to
10282    /// generate a component declaration.
10283    fn generate_expose_different_source_and_availability_decl(
10284        new_expose: impl Fn(fdecl::Ref, fdecl::Availability, &str) -> fdecl::Expose,
10285    ) -> fdecl::Component {
10286        let mut decl = new_component_decl();
10287        let child = "child";
10288        decl.children = Some(vec![fdecl::Child {
10289            name: Some(child.to_string()),
10290            url: Some("fuchsia-pkg://fuchsia.com/source#meta/source.cm".to_string()),
10291            startup: Some(fdecl::StartupMode::Lazy),
10292            ..Default::default()
10293        }]);
10294        decl.exposes = Some(vec![
10295            // Optional expose from self is okay.
10296            new_expose(
10297                fdecl::Ref::Self_(fdecl::SelfRef {}),
10298                fdecl::Availability::Optional,
10299                "fuchsia.examples.Echo1",
10300            ),
10301            // Optional expose from child is okay.
10302            new_expose(
10303                fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
10304                fdecl::Availability::Optional,
10305                "fuchsia.examples.Echo2",
10306            ),
10307            // Optional expose from void is okay.
10308            new_expose(
10309                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10310                fdecl::Availability::Optional,
10311                "fuchsia.examples.Echo3",
10312            ),
10313            // Optional expose from framework is okay.
10314            new_expose(
10315                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10316                fdecl::Availability::Optional,
10317                "fuchsia.examples.Echo4",
10318            ),
10319            // Transitional expose from self is okay.
10320            new_expose(
10321                fdecl::Ref::Self_(fdecl::SelfRef {}),
10322                fdecl::Availability::Transitional,
10323                "fuchsia.examples.Echo5",
10324            ),
10325            // Transitional expose from child is okay.
10326            new_expose(
10327                fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
10328                fdecl::Availability::Transitional,
10329                "fuchsia.examples.Echo6",
10330            ),
10331            // Transitional expose from void is okay.
10332            new_expose(
10333                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10334                fdecl::Availability::Transitional,
10335                "fuchsia.examples.Echo7",
10336            ),
10337            // Transitional expose from framework is okay.
10338            new_expose(
10339                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10340                fdecl::Availability::Transitional,
10341                "fuchsia.examples.Echo8",
10342            ),
10343            // Same-as-target expose from self is okay.
10344            new_expose(
10345                fdecl::Ref::Self_(fdecl::SelfRef {}),
10346                fdecl::Availability::SameAsTarget,
10347                "fuchsia.examples.Echo9",
10348            ),
10349            // Same-as-target expose from child is okay.
10350            new_expose(
10351                fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
10352                fdecl::Availability::SameAsTarget,
10353                "fuchsia.examples.Echo10",
10354            ),
10355            // Same-as-target expose from framework is okay.
10356            new_expose(
10357                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10358                fdecl::Availability::SameAsTarget,
10359                "fuchsia.examples.Echo11",
10360            ),
10361            // Required expose from void is an error.
10362            new_expose(
10363                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10364                fdecl::Availability::Required,
10365                "fuchsia.examples.Echo12",
10366            ),
10367            // Same-as-target expose from void is an error.
10368            new_expose(
10369                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10370                fdecl::Availability::SameAsTarget,
10371                "fuchsia.examples.Echo13",
10372            ),
10373        ]);
10374        decl
10375    }
10376
10377    /// Passes different source and availability options to `new_offer` to
10378    /// generate a component declaration.
10379    fn generate_offer_different_source_and_availability_decl(
10380        new_offer: impl Fn(fdecl::Ref, fdecl::Availability, &str) -> fdecl::Offer,
10381    ) -> fdecl::Component {
10382        let mut decl = new_component_decl();
10383        decl.children = Some(vec![
10384            fdecl::Child {
10385                name: Some("source".to_string()),
10386                url: Some("fuchsia-pkg://fuchsia.com/source#meta/source.cm".to_string()),
10387                startup: Some(fdecl::StartupMode::Lazy),
10388                on_terminate: None,
10389                environment: None,
10390                ..Default::default()
10391            },
10392            fdecl::Child {
10393                name: Some("sink".to_string()),
10394                url: Some("fuchsia-pkg://fuchsia.com/sink#meta/sink.cm".to_string()),
10395                startup: Some(fdecl::StartupMode::Lazy),
10396                on_terminate: None,
10397                environment: None,
10398                ..Default::default()
10399            },
10400        ]);
10401        decl.offers = Some(vec![
10402            // These offers are fine, offers with a source of parent or void can be
10403            // optional.
10404            new_offer(
10405                fdecl::Ref::Parent(fdecl::ParentRef {}),
10406                fdecl::Availability::Required,
10407                "fuchsia.examples.Echo0",
10408            ),
10409            new_offer(
10410                fdecl::Ref::Parent(fdecl::ParentRef {}),
10411                fdecl::Availability::Optional,
10412                "fuchsia.examples.Echo1",
10413            ),
10414            new_offer(
10415                fdecl::Ref::Parent(fdecl::ParentRef {}),
10416                fdecl::Availability::SameAsTarget,
10417                "fuchsia.examples.Echo2",
10418            ),
10419            new_offer(
10420                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10421                fdecl::Availability::Optional,
10422                "fuchsia.examples.Echo3",
10423            ),
10424            // These offers are fine, offers with a source other than parent or void
10425            // can also be optional.
10426            new_offer(
10427                fdecl::Ref::Self_(fdecl::SelfRef {}),
10428                fdecl::Availability::Optional,
10429                "fuchsia.examples.Echo4",
10430            ),
10431            new_offer(
10432                fdecl::Ref::Self_(fdecl::SelfRef {}),
10433                fdecl::Availability::SameAsTarget,
10434                "fuchsia.examples.Echo5",
10435            ),
10436            new_offer(
10437                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10438                fdecl::Availability::Optional,
10439                "fuchsia.examples.Echo6",
10440            ),
10441            new_offer(
10442                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10443                fdecl::Availability::SameAsTarget,
10444                "fuchsia.examples.Echo7",
10445            ),
10446            new_offer(
10447                fdecl::Ref::Child(fdecl::ChildRef { name: "source".to_string(), collection: None }),
10448                fdecl::Availability::Optional,
10449                "fuchsia.examples.Echo8",
10450            ),
10451            new_offer(
10452                fdecl::Ref::Child(fdecl::ChildRef { name: "source".to_string(), collection: None }),
10453                fdecl::Availability::SameAsTarget,
10454                "fuchsia.examples.Echo9",
10455            ),
10456            // These offers are also not fine, offers with a source of void must be optional
10457            new_offer(
10458                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10459                fdecl::Availability::Required,
10460                "fuchsia.examples.Echo10",
10461            ),
10462            new_offer(
10463                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10464                fdecl::Availability::SameAsTarget,
10465                "fuchsia.examples.Echo11",
10466            ),
10467        ]);
10468        decl
10469    }
10470
10471    #[test]
10472    fn test_validate_dynamic_offers_empty() {
10473        assert_eq!(validate_dynamic_offers(vec![], &vec![], &fdecl::Component::default()), Ok(()));
10474    }
10475
10476    #[test]
10477    fn test_validate_dynamic_offers_okay() {
10478        assert_eq!(
10479            validate_dynamic_offers(
10480                vec![],
10481                &vec![
10482                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
10483                        dependency_type: Some(fdecl::DependencyType::Strong),
10484                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef)),
10485                        source_name: Some("thing".to_string()),
10486                        target_name: Some("thing".repeat(26)),
10487                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10488                            name: "foo".to_string(),
10489                            collection: Some("foo".to_string()),
10490                        })),
10491                        ..Default::default()
10492                    }),
10493                    fdecl::Offer::Service(fdecl::OfferService {
10494                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10495                        source_name: Some("thang".repeat(26)),
10496                        target_name: Some("thang".repeat(26)),
10497                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10498                            name: "foo".to_string(),
10499                            collection: Some("foo".to_string()),
10500                        })),
10501                        ..Default::default()
10502                    }),
10503                    fdecl::Offer::Directory(fdecl::OfferDirectory {
10504                        dependency_type: Some(fdecl::DependencyType::Strong),
10505                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10506                        source_name: Some("thung1".repeat(26)),
10507                        target_name: Some("thung1".repeat(26)),
10508                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10509                            name: "foo".to_string(),
10510                            collection: Some("foo".to_string()),
10511                        })),
10512                        ..Default::default()
10513                    }),
10514                    fdecl::Offer::Storage(fdecl::OfferStorage {
10515                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10516                        source_name: Some("thung2".repeat(26)),
10517                        target_name: Some("thung2".repeat(26)),
10518                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10519                            name: "foo".to_string(),
10520                            collection: Some("foo".to_string()),
10521                        })),
10522                        ..Default::default()
10523                    }),
10524                    fdecl::Offer::Runner(fdecl::OfferRunner {
10525                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10526                        source_name: Some("thung3".repeat(26)),
10527                        target_name: Some("thung3".repeat(26)),
10528                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10529                            name: "foo".to_string(),
10530                            collection: Some("foo".to_string()),
10531                        })),
10532                        ..Default::default()
10533                    }),
10534                    fdecl::Offer::Resolver(fdecl::OfferResolver {
10535                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10536                        source_name: Some("thung4".repeat(26)),
10537                        target_name: Some("thung4".repeat(26)),
10538                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10539                            name: "foo".to_string(),
10540                            collection: Some("foo".to_string()),
10541                        })),
10542                        ..Default::default()
10543                    }),
10544                ],
10545                &fdecl::Component {
10546                    capabilities: Some(vec![fdecl::Capability::Protocol(fdecl::Protocol {
10547                        name: Some("thing".to_string()),
10548                        source_path: Some("/svc/foo".into()),
10549                        ..Default::default()
10550                    }),]),
10551                    ..Default::default()
10552                }
10553            ),
10554            Ok(())
10555        );
10556    }
10557
10558    #[test]
10559    fn test_validate_dynamic_offers_valid_service_aggregation() {
10560        assert_eq!(
10561            validate_dynamic_offers(
10562                vec![],
10563                &vec![
10564                    fdecl::Offer::Service(fdecl::OfferService {
10565                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10566                            name: "child_a".to_string(),
10567                            collection: None
10568                        })),
10569                        source_name: Some("fuchsia.logger.Log".to_string()),
10570                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10571                            name: "child_c".to_string(),
10572                            collection: None,
10573                        })),
10574                        target_name: Some("fuchsia.logger.Log".to_string()),
10575                        source_instance_filter: Some(vec!["default".to_string()]),
10576                        ..Default::default()
10577                    }),
10578                    fdecl::Offer::Service(fdecl::OfferService {
10579                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10580                            name: "child_b".to_string(),
10581                            collection: None
10582                        })),
10583                        source_name: Some("fuchsia.logger.Log".to_string()),
10584                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10585                            name: "child_c".to_string(),
10586                            collection: None,
10587                        })),
10588                        target_name: Some("fuchsia.logger.Log".to_string()),
10589                        source_instance_filter: Some(vec!["a_different_default".to_string()]),
10590                        ..Default::default()
10591                    })
10592                ],
10593                &fdecl::Component {
10594                    children: Some(vec![
10595                        fdecl::Child {
10596                            name: Some("child_a".to_string()),
10597                            url: Some(
10598                                "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10599                                    .to_string()
10600                            ),
10601                            startup: Some(fdecl::StartupMode::Lazy),
10602                            on_terminate: None,
10603                            environment: None,
10604                            ..Default::default()
10605                        },
10606                        fdecl::Child {
10607                            name: Some("child_b".to_string()),
10608                            url: Some(
10609                                "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10610                                    .to_string()
10611                            ),
10612                            startup: Some(fdecl::StartupMode::Lazy),
10613                            on_terminate: None,
10614                            environment: None,
10615                            ..Default::default()
10616                        },
10617                        fdecl::Child {
10618                            name: Some("child_c".to_string()),
10619                            url: Some(
10620                                "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10621                                    .to_string()
10622                            ),
10623                            startup: Some(fdecl::StartupMode::Lazy),
10624                            on_terminate: None,
10625                            environment: None,
10626                            ..Default::default()
10627                        },
10628                    ]),
10629                    ..Default::default()
10630                }
10631            ),
10632            Ok(())
10633        );
10634    }
10635
10636    #[test]
10637    fn test_validate_dynamic_service_aggregation_missing_filter() {
10638        assert_eq!(
10639            validate_dynamic_offers(
10640                vec![],
10641                &vec![
10642                    fdecl::Offer::Service(fdecl::OfferService {
10643                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10644                            name: "child_a".to_string(),
10645                            collection: None
10646                        })),
10647                        source_name: Some("fuchsia.logger.Log".to_string()),
10648                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10649                            name: "child_c".to_string(),
10650                            collection: None,
10651                        })),
10652                        target_name: Some("fuchsia.logger.Log".to_string()),
10653                        source_instance_filter: Some(vec!["default".to_string()]),
10654                        ..Default::default()
10655                    }),
10656                    fdecl::Offer::Service(fdecl::OfferService {
10657                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10658                            name: "child_b".to_string(),
10659                            collection: None
10660                        })),
10661                        source_name: Some("fuchsia.logger.Log".to_string()),
10662                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10663                            name: "child_c".to_string(),
10664                            collection: None,
10665                        })),
10666                        target_name: Some("fuchsia.logger.Log".to_string()),
10667                        source_instance_filter: None,
10668                        ..Default::default()
10669                    }),
10670                ],
10671                &fdecl::Component {
10672                    children: Some(vec![
10673                        fdecl::Child {
10674                            name: Some("child_a".to_string()),
10675                            url: Some(
10676                                "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10677                                    .to_string()
10678                            ),
10679                            startup: Some(fdecl::StartupMode::Lazy),
10680                            ..Default::default()
10681                        },
10682                        fdecl::Child {
10683                            name: Some("child_b".to_string()),
10684                            url: Some(
10685                                "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10686                                    .to_string()
10687                            ),
10688                            startup: Some(fdecl::StartupMode::Lazy),
10689                            ..Default::default()
10690                        },
10691                        fdecl::Child {
10692                            name: Some("child_c".to_string()),
10693                            url: Some(
10694                                "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10695                                    .to_string()
10696                            ),
10697                            startup: Some(fdecl::StartupMode::Lazy),
10698                            ..Default::default()
10699                        },
10700                    ]),
10701                    ..Default::default()
10702                },
10703            ),
10704            Err(ErrorList::new(vec![Error::invalid_aggregate_offer(
10705                "source_instance_filter must be set for dynamic aggregate service offers"
10706            ),]))
10707        );
10708    }
10709
10710    #[test]
10711    fn test_validate_dynamic_offers_omit_target() {
10712        assert_eq!(
10713            validate_dynamic_offers(
10714                vec![],
10715                &vec![
10716                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
10717                        dependency_type: Some(fdecl::DependencyType::Strong),
10718                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef)),
10719                        source_name: Some("thing".to_string()),
10720                        target_name: Some("thing".to_string()),
10721                        ..Default::default()
10722                    }),
10723                    fdecl::Offer::Service(fdecl::OfferService {
10724                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10725                        source_name: Some("thang".to_string()),
10726                        target_name: Some("thang".to_string()),
10727                        ..Default::default()
10728                    }),
10729                    fdecl::Offer::Directory(fdecl::OfferDirectory {
10730                        dependency_type: Some(fdecl::DependencyType::Strong),
10731                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10732                        source_name: Some("thung1".to_string()),
10733                        target_name: Some("thung1".to_string()),
10734                        ..Default::default()
10735                    }),
10736                    fdecl::Offer::Storage(fdecl::OfferStorage {
10737                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10738                        source_name: Some("thung2".to_string()),
10739                        target_name: Some("thung2".to_string()),
10740                        ..Default::default()
10741                    }),
10742                    fdecl::Offer::Runner(fdecl::OfferRunner {
10743                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10744                        source_name: Some("thung3".to_string()),
10745                        target_name: Some("thung3".to_string()),
10746                        ..Default::default()
10747                    }),
10748                    fdecl::Offer::Resolver(fdecl::OfferResolver {
10749                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10750                        source_name: Some("thung4".to_string()),
10751                        target_name: Some("thung4".to_string()),
10752                        ..Default::default()
10753                    }),
10754                ],
10755                &fdecl::Component {
10756                    capabilities: Some(vec![fdecl::Capability::Protocol(fdecl::Protocol {
10757                        name: Some("thing".to_string()),
10758                        source_path: Some("/svc/foo".into()),
10759                        ..Default::default()
10760                    }),]),
10761                    ..Default::default()
10762                }
10763            ),
10764            Err(ErrorList::new(vec![
10765                Error::missing_field(DeclType::OfferProtocol, "target"),
10766                Error::missing_field(DeclType::OfferService, "target"),
10767                Error::missing_field(DeclType::OfferDirectory, "target"),
10768                Error::missing_field(DeclType::OfferStorage, "target"),
10769                Error::missing_field(DeclType::OfferRunner, "target"),
10770                Error::missing_field(DeclType::OfferResolver, "target"),
10771            ]))
10772        );
10773    }
10774
10775    #[test]
10776    fn test_validate_dynamic_offers_collection_collision() {
10777        assert_eq!(
10778            validate_dynamic_offers(
10779                vec![],
10780                &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10781                    dependency_type: Some(fdecl::DependencyType::Strong),
10782                    source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10783                    source_name: Some("thing".to_string()),
10784                    target_name: Some("thing".to_string()),
10785                    target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10786                        name: "child".to_string(),
10787                        collection: Some("coll".to_string()),
10788                    })),
10789                    ..Default::default()
10790                }),],
10791                &fdecl::Component {
10792                    offers: Some(vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10793                        dependency_type: Some(fdecl::DependencyType::Strong),
10794                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10795                        source_name: Some("thing".to_string()),
10796                        target_name: Some("thing".to_string()),
10797                        target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10798                            name: "coll".into()
10799                        })),
10800                        ..Default::default()
10801                    }),]),
10802                    collections: Some(vec![fdecl::Collection {
10803                        name: Some("coll".to_string()),
10804                        durability: Some(fdecl::Durability::Transient),
10805                        ..Default::default()
10806                    },]),
10807                    ..Default::default()
10808                }
10809            ),
10810            Err(ErrorList::new(vec![Error::duplicate_field(
10811                DeclType::OfferProtocol,
10812                "target_name",
10813                "thing"
10814            ),]))
10815        );
10816    }
10817
10818    #[test]
10819    fn test_validate_dynamic_offers_cycle_collection_to_static_child() {
10820        assert_eq!(
10821            validate_dynamic_offers(
10822                vec![("dyn", "coll")],
10823                &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10824                    source_name: Some("bar".to_string()),
10825                    target_name: Some("bar".to_string()),
10826                    source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10827                        name: "static_child".into(),
10828                        collection: None,
10829                    })),
10830                    target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10831                        name: "dyn".to_string(),
10832                        collection: Some("coll".to_string()),
10833                    })),
10834                    dependency_type: Some(fdecl::DependencyType::Strong),
10835                    ..Default::default()
10836                }),],
10837                &fdecl::Component {
10838                    offers: Some(vec![fdecl::Offer::Service(fdecl::OfferService {
10839                        source_name: Some("foo".to_string()),
10840                        target_name: Some("foo".to_string()),
10841                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10842                            name: "coll".into(),
10843                        })),
10844                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10845                            name: "static_child".into(),
10846                            collection: None,
10847                        })),
10848                        ..Default::default()
10849                    })]),
10850                    children: Some(vec![fdecl::Child {
10851                        name: Some("static_child".into()),
10852                        url: Some("url#child.cm".into()),
10853                        startup: Some(fdecl::StartupMode::Lazy),
10854                        ..Default::default()
10855                    }]),
10856                    collections: Some(vec![fdecl::Collection {
10857                        name: Some("coll".into()),
10858                        durability: Some(fdecl::Durability::Transient),
10859                        ..Default::default()
10860                    }]),
10861                    ..Default::default()
10862                }
10863            ),
10864            Err(ErrorList::new(vec![Error::dependency_cycle(
10865                directed_graph::Error::CyclesDetected(
10866                    [vec![
10867                        "child coll:dyn",
10868                        "collection coll",
10869                        "child static_child",
10870                        "child coll:dyn",
10871                    ]]
10872                    .iter()
10873                    .cloned()
10874                    .collect()
10875                )
10876                .format_cycle()
10877            )]))
10878        );
10879    }
10880
10881    #[test]
10882    fn test_validate_dynamic_offers_cycle_collection_to_dynamic_child() {
10883        assert_eq!(
10884            validate_dynamic_offers(
10885                vec![("dyn", "coll1"), ("dyn", "coll2")],
10886                &vec![
10887                    fdecl::Offer::Service(fdecl::OfferService {
10888                        source_name: Some("foo".to_string()),
10889                        target_name: Some("foo".to_string()),
10890                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10891                            name: "coll2".into(),
10892                        })),
10893                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10894                            name: "dyn".into(),
10895                            collection: Some("coll1".into()),
10896                        })),
10897                        ..Default::default()
10898                    }),
10899                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
10900                        source_name: Some("bar".to_string()),
10901                        target_name: Some("bar".to_string()),
10902                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10903                            name: "dyn".into(),
10904                            collection: Some("coll1".into()),
10905                        })),
10906                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10907                            name: "dyn".to_string(),
10908                            collection: Some("coll2".to_string()),
10909                        })),
10910                        dependency_type: Some(fdecl::DependencyType::Strong),
10911                        ..Default::default()
10912                    }),
10913                ],
10914                &fdecl::Component {
10915                    collections: Some(vec![
10916                        fdecl::Collection {
10917                            name: Some("coll1".into()),
10918                            durability: Some(fdecl::Durability::Transient),
10919                            ..Default::default()
10920                        },
10921                        fdecl::Collection {
10922                            name: Some("coll2".into()),
10923                            durability: Some(fdecl::Durability::Transient),
10924                            ..Default::default()
10925                        },
10926                    ]),
10927                    ..Default::default()
10928                }
10929            ),
10930            Err(ErrorList::new(vec![Error::dependency_cycle(
10931                directed_graph::Error::CyclesDetected(
10932                    [vec![
10933                        "child coll1:dyn",
10934                        "child coll2:dyn",
10935                        "collection coll2",
10936                        "child coll1:dyn",
10937                    ]]
10938                    .iter()
10939                    .cloned()
10940                    .collect()
10941                )
10942                .format_cycle()
10943            )]))
10944        );
10945    }
10946
10947    #[test]
10948    fn test_validate_dynamic_offers_cycle_collection_to_collection() {
10949        assert_eq!(
10950            validate_dynamic_offers(
10951                vec![("dyn", "coll1"), ("dyn", "coll2")],
10952                &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10953                    source_name: Some("bar".to_string()),
10954                    target_name: Some("bar".to_string()),
10955                    source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10956                        name: "dyn".into(),
10957                        collection: Some("coll2".parse().unwrap()),
10958                    })),
10959                    target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10960                        name: "dyn".into(),
10961                        collection: Some("coll1".parse().unwrap()),
10962                    })),
10963                    dependency_type: Some(fdecl::DependencyType::Strong),
10964                    ..Default::default()
10965                }),],
10966                &fdecl::Component {
10967                    offers: Some(vec![fdecl::Offer::Service(fdecl::OfferService {
10968                        source_name: Some("foo".to_string()),
10969                        target_name: Some("foo".to_string()),
10970                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10971                            name: "coll1".into(),
10972                        })),
10973                        target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10974                            name: "coll2".into(),
10975                        })),
10976                        ..Default::default()
10977                    })]),
10978                    collections: Some(vec![
10979                        fdecl::Collection {
10980                            name: Some("coll1".into()),
10981                            durability: Some(fdecl::Durability::Transient),
10982                            ..Default::default()
10983                        },
10984                        fdecl::Collection {
10985                            name: Some("coll2".into()),
10986                            durability: Some(fdecl::Durability::Transient),
10987                            ..Default::default()
10988                        },
10989                    ]),
10990                    ..Default::default()
10991                }
10992            ),
10993            Err(ErrorList::new(vec![Error::dependency_cycle(
10994                directed_graph::Error::CyclesDetected(
10995                    [vec![
10996                        "child coll1:dyn",
10997                        "collection coll1",
10998                        "child coll2:dyn",
10999                        "child coll1:dyn",
11000                    ]]
11001                    .iter()
11002                    .cloned()
11003                    .collect()
11004                )
11005                .format_cycle()
11006            )]))
11007        );
11008    }
11009
11010    #[test]
11011    fn test_validate_dynamic_child() {
11012        assert_eq!(
11013            Ok(()),
11014            validate_dynamic_child(&fdecl::Child {
11015                name: Some("a".repeat(MAX_LONG_NAME_LENGTH).to_string()),
11016                url: Some("test:///child".to_string()),
11017                startup: Some(fdecl::StartupMode::Lazy),
11018                on_terminate: None,
11019                environment: None,
11020                ..Default::default()
11021            })
11022        );
11023    }
11024
11025    #[test]
11026    fn test_validate_dynamic_child_environment_is_invalid() {
11027        assert_eq!(
11028            validate_dynamic_child(&fdecl::Child {
11029                name: Some("a".repeat(MAX_LONG_NAME_LENGTH).to_string()),
11030                url: Some("test:///child".to_string()),
11031                startup: Some(fdecl::StartupMode::Lazy),
11032                on_terminate: None,
11033                environment: Some("env".to_string()),
11034                ..Default::default()
11035            }),
11036            Err(ErrorList::new(vec![Error::DynamicChildWithEnvironment]))
11037        );
11038    }
11039
11040    #[test]
11041    fn test_validate_dynamic_offers_missing_stuff() {
11042        assert_eq!(
11043            validate_dynamic_offers(
11044                vec![],
11045                &vec![
11046                    fdecl::Offer::Protocol(fdecl::OfferProtocol::default()),
11047                    fdecl::Offer::Service(fdecl::OfferService::default()),
11048                    fdecl::Offer::Directory(fdecl::OfferDirectory::default()),
11049                    fdecl::Offer::Storage(fdecl::OfferStorage::default()),
11050                    fdecl::Offer::Runner(fdecl::OfferRunner::default()),
11051                    fdecl::Offer::Resolver(fdecl::OfferResolver::default()),
11052                ],
11053                &fdecl::Component::default()
11054            ),
11055            Err(ErrorList::new(vec![
11056                Error::missing_field(DeclType::OfferProtocol, "source"),
11057                Error::missing_field(DeclType::OfferProtocol, "source_name"),
11058                Error::missing_field(DeclType::OfferProtocol, "target"),
11059                Error::missing_field(DeclType::OfferProtocol, "target_name"),
11060                Error::missing_field(DeclType::OfferProtocol, "dependency_type"),
11061                Error::missing_field(DeclType::OfferService, "source"),
11062                Error::missing_field(DeclType::OfferService, "source_name"),
11063                Error::missing_field(DeclType::OfferService, "target"),
11064                Error::missing_field(DeclType::OfferService, "target_name"),
11065                Error::missing_field(DeclType::OfferDirectory, "source"),
11066                Error::missing_field(DeclType::OfferDirectory, "source_name"),
11067                Error::missing_field(DeclType::OfferDirectory, "target"),
11068                Error::missing_field(DeclType::OfferDirectory, "target_name"),
11069                Error::missing_field(DeclType::OfferDirectory, "dependency_type"),
11070                Error::missing_field(DeclType::OfferStorage, "source"),
11071                Error::missing_field(DeclType::OfferStorage, "source_name"),
11072                Error::missing_field(DeclType::OfferStorage, "target"),
11073                Error::missing_field(DeclType::OfferStorage, "target_name"),
11074                Error::missing_field(DeclType::OfferRunner, "source"),
11075                Error::missing_field(DeclType::OfferRunner, "source_name"),
11076                Error::missing_field(DeclType::OfferRunner, "target"),
11077                Error::missing_field(DeclType::OfferRunner, "target_name"),
11078                Error::missing_field(DeclType::OfferResolver, "source"),
11079                Error::missing_field(DeclType::OfferResolver, "source_name"),
11080                Error::missing_field(DeclType::OfferResolver, "target"),
11081                Error::missing_field(DeclType::OfferResolver, "target_name"),
11082            ]))
11083        );
11084    }
11085
11086    test_dependency! {
11087        (test_validate_offers_protocol_dependency_cycle) => {
11088            ty = fdecl::Offer::Protocol,
11089            offer_decl = fdecl::OfferProtocol {
11090                source: None,  // Filled by macro
11091                target: None,  // Filled by macro
11092                source_name: Some(format!("thing")),
11093                target_name: Some(format!("thing")),
11094                dependency_type: Some(fdecl::DependencyType::Strong),
11095                ..Default::default()
11096            },
11097        },
11098        (test_validate_offers_directory_dependency_cycle) => {
11099            ty = fdecl::Offer::Directory,
11100            offer_decl = fdecl::OfferDirectory {
11101                source: None,  // Filled by macro
11102                target: None,  // Filled by macro
11103                source_name: Some(format!("thing")),
11104                target_name: Some(format!("thing")),
11105                rights: Some(fio::Operations::CONNECT),
11106                subdir: None,
11107                dependency_type: Some(fdecl::DependencyType::Strong),
11108                ..Default::default()
11109            },
11110        },
11111        (test_validate_offers_service_dependency_cycle) => {
11112            ty = fdecl::Offer::Service,
11113            offer_decl = fdecl::OfferService {
11114                source: None,  // Filled by macro
11115                target: None,  // Filled by macro
11116                source_name: Some(format!("thing")),
11117                target_name: Some(format!("thing")),
11118                ..Default::default()
11119            },
11120        },
11121        (test_validate_offers_runner_dependency_cycle) => {
11122            ty = fdecl::Offer::Runner,
11123            offer_decl = fdecl::OfferRunner {
11124                source: None,  // Filled by macro
11125                target: None,  // Filled by macro
11126                source_name: Some(format!("thing")),
11127                target_name: Some(format!("thing")),
11128                ..Default::default()
11129            },
11130        },
11131        (test_validate_offers_resolver_dependency_cycle) => {
11132            ty = fdecl::Offer::Resolver,
11133            offer_decl = fdecl::OfferResolver {
11134                source: None,  // Filled by macro
11135                target: None,  // Filled by macro
11136                source_name: Some(format!("thing")),
11137                target_name: Some(format!("thing")),
11138                ..Default::default()
11139            },
11140        },
11141    }
11142    test_weak_dependency! {
11143        (test_validate_offers_protocol_weak_dependency_cycle) => {
11144            ty = fdecl::Offer::Protocol,
11145            offer_decl = fdecl::OfferProtocol {
11146                source: None,  // Filled by macro
11147                target: None,  // Filled by macro
11148                source_name: Some(format!("thing")),
11149                target_name: Some(format!("thing")),
11150                dependency_type: None, // Filled by macro
11151                ..Default::default()
11152            },
11153        },
11154        (test_validate_offers_directory_weak_dependency_cycle) => {
11155            ty = fdecl::Offer::Directory,
11156            offer_decl = fdecl::OfferDirectory {
11157                source: None,  // Filled by macro
11158                target: None,  // Filled by macro
11159                source_name: Some(format!("thing")),
11160                target_name: Some(format!("thing")),
11161                rights: Some(fio::Operations::CONNECT),
11162                subdir: None,
11163                dependency_type: None,  // Filled by macro
11164                ..Default::default()
11165            },
11166        },
11167        (test_validate_offers_service_weak_dependency_cycle) => {
11168            ty = fdecl::Offer::Service,
11169            offer_decl = fdecl::OfferService {
11170                source: None,  // Filled by macro
11171                target: None,  // Filled by macro
11172                source_name: Some(format!("thing")),
11173                target_name: Some(format!("thing")),
11174                dependency_type: None, // Filled by macro
11175                ..Default::default()
11176            },
11177        },
11178    }
11179}