Skip to main content

cm_fidl_validator/
lib.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5pub(crate) mod util;
6
7pub mod error;
8
9pub use crate::util::check_url;
10
11use crate::error::*;
12use crate::util::*;
13use cm_graph::DependencyNode;
14use cm_types::IterablePath;
15use directed_graph::DirectedGraph;
16use fidl_fuchsia_component_decl_common as fdecl;
17use itertools::Itertools;
18use std::collections::{BTreeSet, HashMap, HashSet};
19use std::path::Path;
20
21trait HasAvailability {
22    fn availability(&self) -> fdecl::Availability;
23}
24
25impl HasAvailability for fdecl::ExposeService {
26    fn availability(&self) -> fdecl::Availability {
27        return self.availability.unwrap_or(fdecl::Availability::Required);
28    }
29}
30
31impl HasAvailability for fdecl::OfferService {
32    fn availability(&self) -> fdecl::Availability {
33        return self.availability.unwrap_or(fdecl::Availability::Required);
34    }
35}
36
37/// Validates Configuration Value Spec.
38///
39/// For now, this simply verifies that all semantically required fields are present.
40pub fn validate_value_spec(spec: &fdecl::ConfigValueSpec) -> Result<(), ErrorList> {
41    let mut errors = vec![];
42    if let Some(value) = &spec.value {
43        match value {
44            fdecl::ConfigValue::Single(s) => match s {
45                fdecl::ConfigSingleValue::Bool(_)
46                | fdecl::ConfigSingleValue::Uint8(_)
47                | fdecl::ConfigSingleValue::Uint16(_)
48                | fdecl::ConfigSingleValue::Uint32(_)
49                | fdecl::ConfigSingleValue::Uint64(_)
50                | fdecl::ConfigSingleValue::Int8(_)
51                | fdecl::ConfigSingleValue::Int16(_)
52                | fdecl::ConfigSingleValue::Int32(_)
53                | fdecl::ConfigSingleValue::Int64(_)
54                | fdecl::ConfigSingleValue::String(_) => {}
55                fdecl::ConfigSingleValueUnknown!() => {
56                    errors.push(Error::invalid_field(DeclType::ConfigValueSpec, "value"));
57                }
58            },
59            fdecl::ConfigValue::Vector(l) => match l {
60                fdecl::ConfigVectorValue::BoolVector(_)
61                | fdecl::ConfigVectorValue::Uint8Vector(_)
62                | fdecl::ConfigVectorValue::Uint16Vector(_)
63                | fdecl::ConfigVectorValue::Uint32Vector(_)
64                | fdecl::ConfigVectorValue::Uint64Vector(_)
65                | fdecl::ConfigVectorValue::Int8Vector(_)
66                | fdecl::ConfigVectorValue::Int16Vector(_)
67                | fdecl::ConfigVectorValue::Int32Vector(_)
68                | fdecl::ConfigVectorValue::Int64Vector(_)
69                | fdecl::ConfigVectorValue::StringVector(_) => {}
70                fdecl::ConfigVectorValueUnknown!() => {
71                    errors.push(Error::invalid_field(DeclType::ConfigValueSpec, "value"));
72                }
73            },
74            fdecl::ConfigValueUnknown!() => {
75                errors.push(Error::invalid_field(DeclType::ConfigValueSpec, "value"));
76            }
77        }
78    } else {
79        errors.push(Error::missing_field(DeclType::ConfigValueSpec, "value"));
80    }
81
82    if errors.is_empty() { Ok(()) } else { Err(ErrorList::new(errors)) }
83}
84
85/// Validates Configuration Values Data.
86///
87/// The Value Data may ultimately originate from a CVF file, or be directly constructed by the
88/// caller. Either way, Value Data should always be validated before it's used. For now, this
89/// simply verifies that all semantically required fields are present.
90///
91/// This method does not validate value data against a configuration schema.
92pub fn validate_values_data(data: &fdecl::ConfigValuesData) -> Result<(), ErrorList> {
93    let mut errors = vec![];
94    if let Some(values) = &data.values {
95        for spec in values {
96            if let Err(mut e) = validate_value_spec(spec) {
97                errors.append(&mut e.errs);
98            }
99        }
100    } else {
101        errors.push(Error::missing_field(DeclType::ConfigValuesData, "values"));
102    }
103
104    if let Some(checksum) = &data.checksum {
105        match checksum {
106            fdecl::ConfigChecksum::Sha256(_) => {}
107            fdecl::ConfigChecksumUnknown!() => {
108                errors.push(Error::invalid_field(DeclType::ConfigValuesData, "checksum"));
109            }
110        }
111    } else {
112        errors.push(Error::missing_field(DeclType::ConfigValuesData, "checksum"));
113    }
114
115    if errors.is_empty() { Ok(()) } else { Err(ErrorList::new(errors)) }
116}
117
118// `fdecl::Ref` is not hashable, so define this equivalent type for use in maps
119#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
120enum RefKey<'a> {
121    Parent,
122    Self_,
123    Child(&'a str),
124    Collection(&'a str),
125    Framework,
126    Capability,
127    Debug,
128}
129
130/// Validates a Component.
131///
132/// The Component may ultimately originate from a CM file, or be directly constructed by the
133/// caller. Either way, a Component should always be validated before it's used. Examples
134/// of what is validated (which may evolve in the future):
135///
136/// - That all semantically required fields are present
137/// - That a child_name referenced in a source actually exists in the list of children
138/// - That there are no duplicate target paths.
139/// - That only weak-dependency capabilities may be offered back to the
140///   component that exposed them.
141///
142/// `dependencies` is an output parameter that captures the capability dependency graph derived
143/// from `decl`. Callers may use this to traverse the capability graph in topological order.
144///
145/// All checks are local to this Component.
146pub fn validate<'a>(
147    decl: &'a fdecl::Component,
148    dependencies: &'a mut DirectedGraph<DependencyNode>,
149) -> Result<(), ErrorList> {
150    let ctx = ValidationContext::new(dependencies);
151    ctx.validate(decl).map_err(|errs| ErrorList::new(errs))
152}
153
154/// Validates a list of namespace or builtin Capabilities.
155fn validate_capabilities(
156    capabilities: &[fdecl::Capability],
157    as_builtin: bool,
158) -> Result<(), ErrorList> {
159    let mut deps = DirectedGraph::new();
160    let mut ctx = ValidationContext::new(&mut deps);
161
162    ctx.load_dictionary_names(capabilities.iter().filter_map(|capability| match capability {
163        fdecl::Capability::Dictionary(dictionary_decl) => Some(dictionary_decl),
164        _ => None,
165    }));
166
167    ctx.validate_capability_decls(capabilities, as_builtin);
168    if ctx.errors.is_empty() { Ok(()) } else { Err(ErrorList::new(ctx.errors)) }
169}
170
171// Validate builtin capabilities.
172pub fn validate_builtin_capabilities(
173    capabilities: &Vec<fdecl::Capability>,
174) -> Result<(), ErrorList> {
175    validate_capabilities(capabilities, true)
176}
177
178// Validate namespace capabilities.
179pub fn validate_namespace_capabilities(
180    capabilities: &Vec<fdecl::Capability>,
181) -> Result<(), ErrorList> {
182    validate_capabilities(capabilities, false)
183}
184
185/// An interface to call into either `check_dynamic_name()` or `check_name()`, depending on the context
186/// of the caller.
187type CheckChildNameFn = fn(Option<&String>, DeclType, &str, &mut Vec<Error>) -> bool;
188
189pub fn validate_dynamic_child(child: &fdecl::Child) -> Result<(), ErrorList> {
190    let mut errors = vec![];
191
192    if let Err(mut error_list) = validate_child(child, check_dynamic_name) {
193        errors.append(&mut error_list.errs);
194    }
195
196    if child.environment.is_some() {
197        errors.push(Error::DynamicChildWithEnvironment);
198    }
199
200    if errors.is_empty() { Ok(()) } else { Err(ErrorList { errs: errors }) }
201}
202
203/// Validates an independent Child. Performs the same validation on it as `validate`. A
204/// `check_name_fn` is passed into specify the function used to validate the child name.
205fn validate_child(
206    child: &fdecl::Child,
207    check_child_name: CheckChildNameFn,
208) -> Result<(), ErrorList> {
209    let mut errors = vec![];
210    check_child_name(child.name.as_ref(), DeclType::Child, "name", &mut errors);
211    check_url(child.url.as_ref(), DeclType::Child, "url", &mut errors);
212    if child.startup.is_none() {
213        errors.push(Error::missing_field(DeclType::Child, "startup"));
214    }
215    // Allow `on_terminate` to be unset since the default is almost always desired.
216    if child.environment.is_some() {
217        check_name(child.environment.as_ref(), DeclType::Child, "environment", &mut errors);
218    }
219    if errors.is_empty() { Ok(()) } else { Err(ErrorList { errs: errors }) }
220}
221
222/// Validates a collection of dynamic offers. Dynamic offers differ from static
223/// offers, in that
224///
225/// 1. a dynamic offer's `target` field must be omitted;
226/// 2. a dynamic offer's `source` _may_ be a dynamic child;
227/// 3. since this crate isn't really designed to handle dynamic children, we
228///    disable the checks that ensure that the source/target exist, and that the
229///    offers don't introduce any cycles.
230///
231/// `dependencies` is an output parameter that captures new dependencies from `new_dynamic_offers`.
232/// Any existing dependency edges are preserved. The input may be non empty and is normally the
233/// output of the previous [`validate`] or [`validate_dynamic_offers`].
234pub fn validate_dynamic_offers<'a>(
235    dynamic_children: Vec<(&'a str, &'a str)>,
236    static_children: impl IntoIterator<Item = &'a str>,
237    collections: impl IntoIterator<Item = &'a str>,
238    capabilities: &'a [fdecl::Capability],
239    static_offers: &'a [fdecl::Offer],
240    dependencies: &'a mut DirectedGraph<DependencyNode>,
241    new_dynamic_offers: &'a [fdecl::Offer],
242) -> Result<(), ErrorList> {
243    let mut ctx = ValidationContext::new(dependencies);
244    ctx.dynamic_children = dynamic_children;
245    ctx.validate_dynamic_offers(
246        static_children,
247        collections,
248        capabilities,
249        static_offers,
250        new_dynamic_offers,
251    )
252    .map_err(|errs| ErrorList::new(errs))
253}
254
255fn check_offer_name(
256    prop: Option<&String>,
257    decl: DeclType,
258    keyword: &str,
259    offer_type: OfferType,
260    errors: &mut Vec<Error>,
261) -> bool {
262    if offer_type == OfferType::Dynamic {
263        check_dynamic_name(prop, decl, keyword, errors)
264    } else {
265        check_name(prop, decl, keyword, errors)
266    }
267}
268
269struct ValidationContext<'a> {
270    all_children: HashSet<&'a str>,
271    all_collections: HashSet<&'a str>,
272    all_capability_ids: HashSet<&'a str>,
273    all_storages: HashMap<&'a str, Option<&'a fdecl::Ref>>,
274    all_services: HashSet<&'a str>,
275    all_protocols: HashSet<&'a str>,
276    all_directories: HashSet<&'a str>,
277    all_runners: HashSet<&'a str>,
278    all_resolvers: HashSet<&'a str>,
279    all_dictionaries: HashMap<&'a str, &'a fdecl::Dictionary>,
280
281    #[cfg(fuchsia_api_level_at_least = "HEAD")]
282    all_configs: HashSet<&'a str>,
283
284    all_environment_names: HashSet<&'a str>,
285    dynamic_children: Vec<(&'a str, &'a str)>,
286    strong_dependencies: &'a mut DirectedGraph<DependencyNode>,
287    target_ids: IdMap<'a>,
288    errors: Vec<Error>,
289}
290
291/// [Container] provides a capability type agnostic trait to check for the existence of a
292/// capability definition of a particular type. This is useful for writing common validation
293/// functions.
294trait Container {
295    fn contains(&self, key: &str) -> bool;
296}
297
298impl<'a> Container for HashSet<&'a str> {
299    fn contains(&self, key: &str) -> bool {
300        self.contains(key)
301    }
302}
303
304impl<'a, T> Container for HashMap<&'a str, T> {
305    fn contains(&self, key: &str) -> bool {
306        self.contains_key(key)
307    }
308}
309
310impl<'a> ValidationContext<'a> {
311    fn new(strong_dependencies: &'a mut DirectedGraph<DependencyNode>) -> Self {
312        Self {
313            strong_dependencies,
314            all_children: Default::default(),
315            all_collections: Default::default(),
316            all_capability_ids: Default::default(),
317            all_storages: Default::default(),
318            all_services: Default::default(),
319            all_protocols: Default::default(),
320            all_directories: Default::default(),
321            all_runners: Default::default(),
322            all_resolvers: Default::default(),
323            all_dictionaries: Default::default(),
324
325            #[cfg(fuchsia_api_level_at_least = "HEAD")]
326            all_configs: Default::default(),
327
328            all_environment_names: Default::default(),
329            dynamic_children: Default::default(),
330            target_ids: Default::default(),
331            errors: Default::default(),
332        }
333    }
334
335    fn validate(mut self, decl: &'a fdecl::Component) -> Result<(), Vec<Error>> {
336        // Collect all environment names first, so that references to them can be checked.
337        if let Some(envs) = &decl.environments {
338            self.collect_environment_names(&envs);
339        }
340
341        // Validate "children" and build the set of all children.
342        if let Some(children) = decl.children.as_ref() {
343            for child in children {
344                self.validate_child_decl(&child);
345            }
346        }
347
348        // Validate "collections" and build the set of all collections.
349        if let Some(collections) = decl.collections.as_ref() {
350            for collection in collections {
351                self.validate_collection_decl(&collection);
352            }
353        }
354
355        // Validate "capabilities" and build the set of all capabilities.
356        if let Some(capabilities) = decl.capabilities.as_ref() {
357            self.load_dictionary_names(capabilities.iter().filter_map(
358                |capability| match capability {
359                    fdecl::Capability::Dictionary(dictionary_decl) => Some(dictionary_decl),
360                    _ => None,
361                },
362            ));
363            self.validate_capability_decls(capabilities, false);
364        }
365
366        // Validate "uses".
367        let mut use_runner_name = None;
368        let mut use_runner_source = None;
369        if let Some(uses) = decl.uses.as_ref() {
370            (use_runner_name, use_runner_source) = self.validate_use_decls(uses);
371        }
372
373        // Validate "program".
374        if let Some(program) = decl.program.as_ref() {
375            self.validate_program(program, use_runner_name, use_runner_source);
376        }
377
378        // Validate "exposes".
379        if let Some(exposes) = decl.exposes.as_ref() {
380            let mut expose_to_parent_ids = HashMap::new();
381            let mut expose_to_framework_ids = HashMap::new();
382            for expose in exposes.iter() {
383                self.validate_expose_decl(
384                    &expose,
385                    &mut expose_to_parent_ids,
386                    &mut expose_to_framework_ids,
387                );
388            }
389            self.validate_expose_group(&exposes);
390        }
391
392        // Validate "offers".
393        if let Some(offers) = decl.offers.as_ref() {
394            for offer in offers.iter() {
395                self.validate_offer_decl(&offer, OfferType::Static);
396            }
397            self.validate_offer_group(&offers, OfferType::Static);
398        }
399
400        // Validate "environments" after all other declarations are processed.
401        if let Some(environment) = decl.environments.as_ref() {
402            for environment in environment {
403                self.validate_environment_decl(&environment);
404            }
405        }
406
407        // Validate "config"
408        self.validate_config(decl.config.as_ref(), decl.uses.as_ref());
409
410        // Check that there are no strong cyclical dependencies
411        cm_graph::generate_dependency_graph(
412            &mut self.strong_dependencies,
413            &decl,
414            &self.dynamic_children,
415        );
416        if let Err(e) = self.strong_dependencies.topological_sort() {
417            self.errors.push(Error::dependency_cycle(e.format_cycle()));
418        }
419
420        if self.errors.is_empty() { Ok(()) } else { Err(self.errors) }
421    }
422
423    fn validate_dynamic_offers(
424        mut self,
425        static_children: impl IntoIterator<Item = &'a str>,
426        collections: impl IntoIterator<Item = &'a str>,
427        capabilities: &'a [fdecl::Capability],
428        static_offers: &'a [fdecl::Offer],
429        new_dynamic_offers: &'a [fdecl::Offer],
430    ) -> Result<(), Vec<Error>> {
431        self.all_children.extend(static_children);
432        self.all_collections.extend(collections);
433
434        self.load_dictionary_names(capabilities.iter().filter_map(|capability| match capability {
435            fdecl::Capability::Dictionary(dictionary_decl) => Some(dictionary_decl),
436            _ => None,
437        }));
438        self.validate_capability_decls(capabilities, false);
439
440        for offer in static_offers.iter() {
441            self.validate_offer_decl(offer, OfferType::Static);
442            cm_graph::add_dependencies_from_offer(
443                &mut self.strong_dependencies,
444                offer,
445                &self.dynamic_children,
446            );
447        }
448        self.validate_offer_group(static_offers, OfferType::Static);
449
450        for dynamic_offer in new_dynamic_offers {
451            self.validate_offer_decl(dynamic_offer, OfferType::Dynamic);
452            cm_graph::add_dependencies_from_offer(
453                &mut self.strong_dependencies,
454                dynamic_offer,
455                &self.dynamic_children,
456            );
457        }
458        self.validate_offer_group(new_dynamic_offers, OfferType::Dynamic);
459
460        if let Err(e) = self.strong_dependencies.topological_sort() {
461            self.errors.push(Error::dependency_cycle(e.format_cycle()));
462        }
463
464        if self.errors.is_empty() { Ok(()) } else { Err(self.errors) }
465    }
466
467    /// Collects all the environment names, watching for duplicates.
468    fn collect_environment_names(&mut self, envs: &'a [fdecl::Environment]) {
469        for env in envs {
470            if let Some(name) = env.name.as_ref() {
471                if !self.all_environment_names.insert(name) {
472                    self.errors.push(Error::duplicate_field(DeclType::Environment, "name", name));
473                }
474            }
475        }
476    }
477
478    // Validates a config schema. Checks that each field's layout matches the expected constraints
479    // and properties.
480    fn validate_config(
481        &mut self,
482        config: Option<&fdecl::ConfigSchema>,
483        uses: Option<&Vec<fdecl::Use>>,
484    ) {
485        use std::collections::BTreeMap;
486
487        // Get all of the `use` configs that are optional without a default.
488        let optional_use_keys: BTreeMap<String, fdecl::ConfigType> =
489            uses.map_or(BTreeMap::new(), |u| {
490                u.iter()
491                    .map(|u| {
492                        let fdecl::Use::Config(config) = u else {
493                            return None;
494                        };
495                        if config.availability == Some(fdecl::Availability::Required)
496                            || config.availability == None
497                        {
498                            return None;
499                        }
500                        if let Some(_) = config.default.as_ref() {
501                            return None;
502                        }
503                        let Some(key) = config.target_name.clone() else {
504                            return None;
505                        };
506                        let Some(value) = config.type_.clone() else {
507                            return None;
508                        };
509                        Some((key, value))
510                    })
511                    .flatten()
512                    .collect()
513            });
514
515        // Validate default values in use configs.
516        for u in uses.iter().flat_map(|x| x.iter()) {
517            let fdecl::Use::Config(config) = u else { continue };
518            let Some(default) = config.default.as_ref() else { continue };
519            validate_value_spec(&fdecl::ConfigValueSpec {
520                value: Some(default.clone()),
521                ..Default::default()
522            })
523            .map_err(|mut e| self.errors.append(&mut e.errs))
524            .ok();
525        }
526
527        let Some(config) = config else {
528            if !optional_use_keys.is_empty() {
529                self.errors.push(Error::missing_field(DeclType::ConfigField, "config"))
530            }
531            return;
532        };
533
534        if let Some(fields) = &config.fields {
535            for field in fields {
536                if field.key.is_none() {
537                    self.errors.push(Error::missing_field(DeclType::ConfigField, "key"));
538                }
539                if let Some(type_) = &field.type_ {
540                    self.validate_config_type(type_, true);
541                } else {
542                    self.errors.push(Error::missing_field(DeclType::ConfigField, "value_type"));
543                }
544            }
545        } else {
546            self.errors.push(Error::missing_field(DeclType::ConfigSchema, "fields"));
547        }
548
549        if let Some(checksum) = &config.checksum {
550            match checksum {
551                fdecl::ConfigChecksum::Sha256(_) => {}
552                fdecl::ConfigChecksumUnknown!() => {
553                    self.errors.push(Error::invalid_field(DeclType::ConfigSchema, "checksum"));
554                }
555            }
556        } else {
557            self.errors.push(Error::missing_field(DeclType::ConfigSchema, "checksum"));
558        }
559
560        'outer: for (key, value) in optional_use_keys.iter() {
561            for field in config.fields.iter().flatten() {
562                if field.key.as_ref() == Some(key) {
563                    if field.type_.as_ref() != Some(value) {
564                        self.errors.push(Error::invalid_field(DeclType::ConfigField, key.clone()));
565                    }
566                    continue 'outer;
567                }
568            }
569            self.errors.push(Error::missing_field(DeclType::ConfigField, key.clone()));
570        }
571
572        match config.value_source {
573            None => self.errors.push(Error::missing_field(DeclType::ConfigSchema, "value_source")),
574            #[cfg(fuchsia_api_level_at_least = "HEAD")]
575            Some(fdecl::ConfigValueSource::Capabilities(_)) => {
576                if !optional_use_keys.is_empty() {
577                    self.errors
578                        .push(Error::invalid_field(DeclType::ConfigValueSource, "ValueSource"))
579                }
580            }
581            Some(fdecl::ConfigValueSource::__SourceBreaking { .. }) => {
582                self.errors.push(Error::invalid_field(DeclType::ConfigValueSource, "ValueSource"))
583            }
584            _ => (),
585        };
586    }
587
588    fn validate_config_type(&mut self, type_: &fdecl::ConfigType, accept_vectors: bool) {
589        match &type_.layout {
590            fdecl::ConfigTypeLayout::Bool
591            | fdecl::ConfigTypeLayout::Uint8
592            | fdecl::ConfigTypeLayout::Uint16
593            | fdecl::ConfigTypeLayout::Uint32
594            | fdecl::ConfigTypeLayout::Uint64
595            | fdecl::ConfigTypeLayout::Int8
596            | fdecl::ConfigTypeLayout::Int16
597            | fdecl::ConfigTypeLayout::Int32
598            | fdecl::ConfigTypeLayout::Int64 => {
599                // These layouts have no parameters or constraints
600                if let Some(parameters) = &type_.parameters {
601                    if !parameters.is_empty() {
602                        self.errors
603                            .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
604                    }
605                } else {
606                    self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"));
607                }
608
609                if !type_.constraints.is_empty() {
610                    self.errors.push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
611                }
612            }
613            fdecl::ConfigTypeLayout::String => {
614                // String has exactly one constraint and no parameter
615                if let Some(parameters) = &type_.parameters {
616                    if !parameters.is_empty() {
617                        self.errors
618                            .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
619                    }
620                } else {
621                    self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"));
622                }
623
624                if type_.constraints.is_empty() {
625                    self.errors.push(Error::missing_field(DeclType::ConfigType, "constraints"));
626                } else if type_.constraints.len() > 1 {
627                    self.errors.push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
628                } else if let fdecl::LayoutConstraint::MaxSize(_) = &type_.constraints[0] {
629                } else {
630                    self.errors.push(Error::invalid_field(DeclType::ConfigType, "constraints"));
631                }
632            }
633            fdecl::ConfigTypeLayout::Vector => {
634                if accept_vectors {
635                    // Vector has exactly one constraint and one parameter
636                    if let Some(parameters) = &type_.parameters {
637                        if parameters.is_empty() {
638                            self.errors
639                                .push(Error::missing_field(DeclType::ConfigType, "parameters"));
640                        } else if parameters.len() > 1 {
641                            self.errors
642                                .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
643                        } else if let fdecl::LayoutParameter::NestedType(nested_type) =
644                            &parameters[0]
645                        {
646                            self.validate_config_type(nested_type, false);
647                        } else {
648                            self.errors
649                                .push(Error::invalid_field(DeclType::ConfigType, "parameters"));
650                        }
651                    } else {
652                        self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"))
653                    }
654
655                    if type_.constraints.is_empty() {
656                        self.errors.push(Error::missing_field(DeclType::ConfigType, "constraints"));
657                    } else if type_.constraints.len() > 1 {
658                        self.errors
659                            .push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
660                    } else if let fdecl::LayoutConstraint::MaxSize(_) = &type_.constraints[0] {
661                    } else {
662                        self.errors.push(Error::invalid_field(DeclType::ConfigType, "constraints"));
663                    }
664                } else {
665                    self.errors.push(Error::nested_vector());
666                }
667            }
668            _ => self.errors.push(Error::invalid_field(DeclType::ConfigType, "layout")),
669        }
670    }
671
672    fn validate_capability_decls(
673        &mut self,
674        capabilities: &'a [fdecl::Capability],
675        as_builtin: bool,
676    ) {
677        for capability in capabilities {
678            self.validate_capability_decl(capability, as_builtin);
679        }
680    }
681
682    /// Validates an individual capability declaration as either a built-in capability or (if
683    /// `as_builtin = false`) as a component or namespace capability.
684    // Storage capabilities are not currently allowed as built-ins, but there's no deep reason for this.
685    // Update this method to allow built-in storage capabilities as needed.
686    fn validate_capability_decl(&mut self, capability: &'a fdecl::Capability, as_builtin: bool) {
687        match capability {
688            fdecl::Capability::Service(service) => self.validate_service_decl(&service, as_builtin),
689            fdecl::Capability::Protocol(protocol) => {
690                self.validate_protocol_decl(&protocol, as_builtin)
691            }
692            fdecl::Capability::Directory(directory) => {
693                self.validate_directory_decl(&directory, as_builtin)
694            }
695            fdecl::Capability::Storage(storage) => {
696                if as_builtin {
697                    self.errors.push(Error::CapabilityCannotBeBuiltin(DeclType::Storage))
698                } else {
699                    self.validate_storage_decl(&storage)
700                }
701            }
702            fdecl::Capability::Runner(runner) => self.validate_runner_decl(&runner, as_builtin),
703            fdecl::Capability::Resolver(resolver) => {
704                self.validate_resolver_decl(&resolver, as_builtin)
705            }
706            fdecl::Capability::EventStream(event) => {
707                if as_builtin {
708                    self.validate_event_stream_decl(&event)
709                } else {
710                    self.errors.push(Error::CapabilityMustBeBuiltin(DeclType::EventStream))
711                }
712            }
713            fdecl::Capability::Dictionary(dictionary) => {
714                self.validate_dictionary_decl(&dictionary);
715            }
716            #[cfg(fuchsia_api_level_at_least = "HEAD")]
717            fdecl::Capability::Config(config) => {
718                self.validate_configuration_decl(&config);
719            }
720            fdecl::CapabilityUnknown!() => self.errors.push(Error::UnknownCapability),
721        }
722    }
723
724    /// Returns the `source_name` and `source` of the runner in `uses`, if present.
725    fn validate_use_decls(
726        &mut self,
727        uses: &'a [fdecl::Use],
728    ) -> (Option<&'a String>, Option<&'a fdecl::Ref>) {
729        // Validate individual fields.
730        for use_ in uses.iter() {
731            self.validate_use_decl(&use_);
732        }
733        self.validate_use_paths(&uses);
734
735        #[cfg(fuchsia_api_level_at_least = "HEAD")]
736        {
737            let mut use_runner_name = None;
738            let mut use_runner_source = None;
739            for use_ in uses.iter() {
740                if let fdecl::Use::Runner(use_runner) = use_ {
741                    if use_runner_name.is_some() {
742                        self.errors.push(Error::MultipleRunnersUsed);
743                    }
744
745                    use_runner_name = use_runner.source_name.as_ref();
746                    use_runner_source = use_runner.source.as_ref();
747                }
748            }
749            return (use_runner_name, use_runner_source);
750        }
751        #[cfg(fuchsia_api_level_less_than = "HEAD")]
752        return (None, None);
753    }
754
755    fn validate_use_decl(&mut self, use_: &'a fdecl::Use) {
756        match use_ {
757            fdecl::Use::Service(u) => {
758                let decl = DeclType::UseService;
759                self.validate_use_fields(
760                    decl,
761                    Self::service_checker,
762                    u.source.as_ref(),
763                    u.source_name.as_ref(),
764                    u.source_dictionary.as_ref(),
765                    u.target_path.as_ref(),
766                    u.dependency_type.as_ref(),
767                    u.availability.as_ref(),
768                );
769                if u.dependency_type.is_none() {
770                    self.errors.push(Error::missing_field(decl, "dependency_type"));
771                }
772            }
773            fdecl::Use::Protocol(u) => {
774                let decl = DeclType::UseProtocol;
775                self.validate_use_fields(
776                    decl,
777                    Self::protocol_checker,
778                    u.source.as_ref(),
779                    u.source_name.as_ref(),
780                    u.source_dictionary.as_ref(),
781                    u.target_path.as_ref(),
782                    u.dependency_type.as_ref(),
783                    u.availability.as_ref(),
784                );
785                #[cfg(not(fuchsia_api_level_at_least = "29"))]
786                let has_numbered_handle = false;
787                #[cfg(fuchsia_api_level_at_least = "29")]
788                let has_numbered_handle = u.numbered_handle.is_some();
789                if u.target_path.is_none() && !has_numbered_handle {
790                    self.errors.push(Error::missing_field(decl, "target_path"));
791                }
792                if has_numbered_handle && u.target_path.is_some() {
793                    self.errors.push(Error::extraneous_field(decl, "numbered_handle"));
794                }
795                if u.dependency_type.is_none() {
796                    self.errors.push(Error::missing_field(decl, "dependency_type"));
797                }
798            }
799            fdecl::Use::Directory(u) => {
800                let decl = DeclType::UseDirectory;
801                self.validate_use_fields(
802                    decl,
803                    Self::directory_checker,
804                    u.source.as_ref(),
805                    u.source_name.as_ref(),
806                    u.source_dictionary.as_ref(),
807                    u.target_path.as_ref(),
808                    u.dependency_type.as_ref(),
809                    u.availability.as_ref(),
810                );
811                if u.dependency_type.is_none() {
812                    self.errors.push(Error::missing_field(decl, "dependency_type"));
813                }
814                if u.rights.is_none() {
815                    self.errors.push(Error::missing_field(DeclType::UseDirectory, "rights"));
816                }
817                if let Some(subdir) = u.subdir.as_ref() {
818                    check_relative_path(
819                        Some(subdir),
820                        DeclType::UseDirectory,
821                        "subdir",
822                        &mut self.errors,
823                    );
824                }
825            }
826            fdecl::Use::Storage(u) => {
827                const SOURCE: Option<fdecl::Ref> = Some(fdecl::Ref::Parent(fdecl::ParentRef {}));
828                const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
829                    Some(fdecl::DependencyType::Strong);
830                self.validate_use_fields(
831                    DeclType::UseStorage,
832                    Self::storage_checker,
833                    SOURCE.as_ref(),
834                    u.source_name.as_ref(),
835                    None,
836                    u.target_path.as_ref(),
837                    DEPENDENCY_TYPE.as_ref(),
838                    u.availability.as_ref(),
839                );
840            }
841            fdecl::Use::EventStream(u) => {
842                const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
843                    Some(fdecl::DependencyType::Strong);
844                let decl = DeclType::UseEventStream;
845                self.validate_use_fields(
846                    decl,
847                    Self::event_stream_checker,
848                    u.source.as_ref(),
849                    u.source_name.as_ref(),
850                    None,
851                    u.target_path.as_ref(),
852                    DEPENDENCY_TYPE.as_ref(),
853                    u.availability.as_ref(),
854                );
855                // Additional validation.
856                match u.source {
857                    Some(fdecl::Ref::Child(_)) | Some(fdecl::Ref::Parent(_)) => {
858                        // Allowed.
859                    }
860                    Some(fdecl::Ref::Framework(_))
861                    | Some(fdecl::Ref::Self_(_))
862                    | Some(fdecl::Ref::Debug(_)) => {
863                        // Allowed in general but not for event streams, add an error.
864                        self.errors.push(Error::invalid_field(decl, "source"));
865                    }
866                    Some(fdecl::Ref::Collection(_)) | Some(fdecl::RefUnknown!()) | None => {
867                        // Already handled by validate_use_fields.
868                    }
869                }
870                if let Some(scope) = &u.scope {
871                    for reference in scope {
872                        if !matches!(reference, fdecl::Ref::Child(_) | fdecl::Ref::Collection(_)) {
873                            self.errors.push(Error::invalid_field(decl, "scope"));
874                        }
875                    }
876                }
877            }
878            #[cfg(fuchsia_api_level_at_least = "HEAD")]
879            fdecl::Use::Runner(u) => {
880                const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
881                    Some(fdecl::DependencyType::Strong);
882                const AVAILABILITY: Option<fdecl::Availability> =
883                    Some(fdecl::Availability::Required);
884                let decl = DeclType::UseRunner;
885                self.validate_use_fields(
886                    decl,
887                    Self::runner_checker,
888                    u.source.as_ref(),
889                    u.source_name.as_ref(),
890                    u.source_dictionary.as_ref(),
891                    None,
892                    DEPENDENCY_TYPE.as_ref(),
893                    AVAILABILITY.as_ref(),
894                );
895            }
896            #[cfg(fuchsia_api_level_at_least = "HEAD")]
897            fdecl::Use::Config(u) => {
898                const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
899                    Some(fdecl::DependencyType::Strong);
900                let decl = DeclType::UseConfiguration;
901                self.validate_use_fields(
902                    decl,
903                    Self::config_checker,
904                    u.source.as_ref(),
905                    u.source_name.as_ref(),
906                    None,
907                    None,
908                    DEPENDENCY_TYPE.as_ref(),
909                    u.availability.as_ref(),
910                );
911            }
912            #[cfg(fuchsia_api_level_at_least = "29")]
913            fdecl::Use::Dictionary(u) => {
914                let decl = DeclType::UseDictionary;
915                self.validate_use_fields(
916                    decl,
917                    Self::dictionary_checker,
918                    u.source.as_ref(),
919                    u.source_name.as_ref(),
920                    u.source_dictionary.as_ref(),
921                    u.target_path.as_ref(),
922                    u.dependency_type.as_ref(),
923                    u.availability.as_ref(),
924                );
925                if u.dependency_type.is_none() {
926                    self.errors.push(Error::missing_field(decl, "dependency_type"));
927                }
928            }
929            fdecl::UseUnknown!() => {
930                self.errors.push(Error::invalid_field(DeclType::Component, "use"));
931            }
932        }
933    }
934
935    /// Validates the "program" declaration. This does not check runner-specific properties
936    /// since those are checked by the runner.
937    fn validate_program(
938        &mut self,
939        program: &fdecl::Program,
940        use_runner_name: Option<&String>,
941        _use_runner_source: Option<&fdecl::Ref>,
942    ) {
943        match &program.runner {
944            Some(_) =>
945            {
946                #[cfg(fuchsia_api_level_at_least = "HEAD")]
947                if use_runner_name.is_some() {
948                    if use_runner_name != program.runner.as_ref()
949                        || _use_runner_source
950                            != Some(&fdecl::Ref::Environment(fdecl::EnvironmentRef))
951                    {
952                        self.errors.push(Error::ConflictingRunners);
953                    }
954                }
955            }
956            None => {
957                if use_runner_name.is_none() {
958                    self.errors.push(Error::MissingRunner);
959                }
960            }
961        }
962
963        if program.info.is_none() {
964            self.errors.push(Error::missing_field(DeclType::Program, "info"));
965        }
966    }
967
968    /// Validates that paths-based capabilities (service, directory, protocol, etc.)
969    /// are different, are not prefixes of each other, and do not collide "/pkg".
970    fn validate_use_paths(&mut self, uses: &[fdecl::Use]) {
971        fn use_to_path<'a>(use_: &'a fdecl::Use) -> Option<&'a Path> {
972            match use_ {
973                #[cfg(fuchsia_api_level_at_least = "29")]
974                fdecl::Use::Dictionary(fdecl::UseDictionary {
975                    target_path: Some(path), ..
976                }) => Some(Path::new(path)),
977                fdecl::Use::Directory(fdecl::UseDirectory { target_path: Some(path), .. }) => {
978                    Some(Path::new(path))
979                }
980                fdecl::Use::Protocol(fdecl::UseProtocol { target_path: Some(path), .. }) => {
981                    Some(Path::new(path))
982                }
983                fdecl::Use::Service(fdecl::UseService { target_path: Some(path), .. }) => {
984                    Some(Path::new(path))
985                }
986                fdecl::Use::Storage(fdecl::UseStorage { target_path: Some(path), .. }) => {
987                    Some(Path::new(path))
988                }
989                _ => None,
990            }
991        }
992        enum NodeType {
993            Service,
994            Directory,
995            // This variant is never constructed if we're at an API version before "use dictionary"
996            // was added.
997            #[allow(unused)]
998            Dictionary,
999        }
1000        fn use_to_type(use_: &fdecl::Use) -> Option<NodeType> {
1001            match use_ {
1002                fdecl::Use::Config(_) => None,
1003                #[cfg(fuchsia_api_level_at_least = "29")]
1004                fdecl::Use::Dictionary(_) => Some(NodeType::Dictionary),
1005                fdecl::Use::Directory(_) => Some(NodeType::Directory),
1006                fdecl::Use::EventStream(_) => Some(NodeType::Service),
1007                fdecl::Use::Protocol(_) => Some(NodeType::Service),
1008                #[cfg(fuchsia_api_level_at_least = "HEAD")]
1009                fdecl::Use::Runner(_) => None,
1010                fdecl::Use::Service(_) => Some(NodeType::Directory),
1011                fdecl::Use::Storage(_) => Some(NodeType::Directory),
1012                _ => None,
1013            }
1014        }
1015        let mut paths_and_uses =
1016            uses.iter().filter_map(|use_| use_to_path(use_).map(|x| (x, use_))).collect::<Vec<_>>();
1017        let mut duplicate_fields = false;
1018        while let Some((path, use_)) = paths_and_uses.pop() {
1019            if paths_and_uses.iter().any(|(path_from_iter, use_from_iter)| {
1020                path == *path_from_iter
1021                    && !(DeclType::from(*use_from_iter) == DeclType::UseDictionary
1022                        && DeclType::from(use_) == DeclType::UseDictionary)
1023            }) {
1024                self.errors.push(Error::duplicate_field(
1025                    use_.into(),
1026                    "target_path",
1027                    path.to_str().unwrap(),
1028                ));
1029                duplicate_fields = true;
1030            }
1031        }
1032        if duplicate_fields {
1033            // When paths are identical instead of merely prefixes of each other, then the above is
1034            // a more ergonomic and easy to understand error. The logic below will also fire for
1035            return;
1036        }
1037        for (use_a, use_b) in uses.iter().tuple_combinations() {
1038            let Some(path_a) = use_to_path(use_a) else {
1039                continue;
1040            };
1041            let Some(path_b) = use_to_path(use_b) else {
1042                continue;
1043            };
1044            let Some(type_a) = use_to_type(use_a) else {
1045                continue;
1046            };
1047            let Some(type_b) = use_to_type(use_b) else {
1048                continue;
1049            };
1050            let mut conflicts = false;
1051            match (type_a, type_b) {
1052                (NodeType::Service, NodeType::Service)
1053                | (NodeType::Directory, NodeType::Service)
1054                | (NodeType::Service, NodeType::Directory)
1055                | (NodeType::Directory, NodeType::Directory) => {
1056                    if path_a.starts_with(path_b) || path_b.starts_with(path_a) {
1057                        conflicts = true;
1058                    }
1059                }
1060                (NodeType::Dictionary, NodeType::Service)
1061                | (NodeType::Dictionary, NodeType::Directory) => {
1062                    if path_a.starts_with(path_b) {
1063                        conflicts = true;
1064                    }
1065                }
1066                (NodeType::Service, NodeType::Dictionary)
1067                | (NodeType::Directory, NodeType::Dictionary) => {
1068                    if path_b.starts_with(path_a) {
1069                        conflicts = true;
1070                    }
1071                }
1072                (NodeType::Dictionary, NodeType::Dictionary) => {
1073                    // All combinations of two dictionaries are valid.
1074                }
1075            }
1076            if conflicts {
1077                self.errors.push(Error::invalid_path_overlap(
1078                    use_a.into(),
1079                    path_a.to_str().unwrap(),
1080                    use_b.into(),
1081                    path_b.to_str().unwrap(),
1082                ));
1083            }
1084        }
1085        for use_ in uses {
1086            let Some(used_path) = use_to_path(use_) else {
1087                continue;
1088            };
1089            if used_path.starts_with(Path::new("/pkg")) {
1090                self.errors.push(Error::pkg_path_overlap(use_.into(), used_path.to_str().unwrap()));
1091            }
1092        }
1093    }
1094
1095    fn validate_use_fields(
1096        &mut self,
1097        decl: DeclType,
1098        // This takes a callback that returns a [Container], instead of the &[Container] directly,
1099        // to avoid a borrow checker error that would occur from a simultaneous borrow on
1100        // &mut self.
1101        capability_checker: impl Fn(&Self) -> &dyn Container,
1102        source: Option<&'a fdecl::Ref>,
1103        source_name: Option<&'a String>,
1104        source_dictionary: Option<&'a String>,
1105        target_path: Option<&'a String>,
1106        dependency_type: Option<&fdecl::DependencyType>,
1107        availability: Option<&'a fdecl::Availability>,
1108    ) {
1109        self.validate_use_source(decl, source, source_dictionary);
1110
1111        check_name(source_name, decl, "source_name", &mut self.errors);
1112        if source_dictionary.is_some() {
1113            check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
1114        }
1115        match decl {
1116            DeclType::UseRunner | DeclType::UseConfiguration => {}
1117            #[cfg(fuchsia_api_level_at_least = "29")]
1118            DeclType::UseProtocol => {
1119                if target_path.is_some() {
1120                    check_path(target_path, decl, "target_path", &mut self.errors);
1121                } else {
1122                    // Expect this use to contain numbered_handle, verified by the caller.
1123                }
1124            }
1125            _ => {
1126                check_path(target_path, decl, "target_path", &mut self.errors);
1127            }
1128        }
1129        check_use_availability(decl, availability, &mut self.errors);
1130
1131        // Only allow `weak` dependency with `use from child`.
1132        let is_use_from_child = match source {
1133            Some(fdecl::Ref::Child(_)) => true,
1134            _ => false,
1135        };
1136        match (is_use_from_child, dependency_type) {
1137            (false, Some(fdecl::DependencyType::Weak)) => {
1138                self.errors.push(Error::invalid_field(decl, "dependency_type"));
1139            }
1140            _ => {}
1141        }
1142
1143        self.validate_route_from_self(
1144            decl,
1145            source,
1146            source_name,
1147            source_dictionary,
1148            capability_checker,
1149        );
1150    }
1151
1152    fn validate_use_source(
1153        &mut self,
1154        decl: DeclType,
1155        source: Option<&'a fdecl::Ref>,
1156        source_dictionary: Option<&'a String>,
1157    ) {
1158        match (source, source_dictionary) {
1159            // These sources support source_dictionary.
1160            (Some(fdecl::Ref::Parent(_)), _) => {}
1161            (Some(fdecl::Ref::Self_(_)), _) => {}
1162            (Some(fdecl::Ref::Child(child)), _) => {
1163                self.validate_child_ref(decl, "source", &child, OfferType::Static);
1164                return;
1165            }
1166            // These sources don't.
1167            (Some(fdecl::Ref::Framework(_)), None) => {}
1168            (Some(fdecl::Ref::Debug(_)), None) => {}
1169            (Some(fdecl::Ref::Capability(c)), None) => {
1170                self.validate_source_capability(&c, decl, "source");
1171                return;
1172            }
1173            #[cfg(fuchsia_api_level_at_least = "HEAD")]
1174            (Some(fdecl::Ref::Environment(_)), None) => {}
1175            (Some(fdecl::Ref::Collection(collection)), None) if decl == DeclType::UseService => {
1176                self.validate_collection_ref(decl, "source", &collection);
1177                return;
1178            }
1179            // `source` is required.
1180            (None, _) => self.errors.push(Error::missing_field(decl, "source")),
1181            // Any combination that was not recognized above must be invalid.
1182            (_, _) => self.errors.push(Error::invalid_field(decl, "source")),
1183        }
1184    }
1185
1186    fn validate_child_decl(&mut self, child: &'a fdecl::Child) {
1187        if let Err(mut e) = validate_child(child, check_name) {
1188            self.errors.append(&mut e.errs);
1189        }
1190        if let Some(name) = child.name.as_ref() {
1191            let name: &str = name;
1192            if !self.all_children.insert(name) {
1193                self.errors.push(Error::duplicate_field(DeclType::Child, "name", name));
1194            }
1195        }
1196        if let Some(environment) = child.environment.as_ref() {
1197            if !self.all_environment_names.contains(environment.as_str()) {
1198                self.errors.push(Error::invalid_environment(
1199                    DeclType::Child,
1200                    "environment",
1201                    environment,
1202                ));
1203            }
1204        }
1205    }
1206
1207    fn validate_collection_decl(&mut self, collection: &'a fdecl::Collection) {
1208        let name = collection.name.as_ref();
1209        if check_name(name, DeclType::Collection, "name", &mut self.errors) {
1210            let name: &str = name.unwrap();
1211            if !self.all_collections.insert(name) {
1212                self.errors.push(Error::duplicate_field(DeclType::Collection, "name", name));
1213            }
1214        }
1215        if collection.durability.is_none() {
1216            self.errors.push(Error::missing_field(DeclType::Collection, "durability"));
1217        }
1218        if let Some(environment) = collection.environment.as_ref() {
1219            if !self.all_environment_names.contains(environment.as_str()) {
1220                self.errors.push(Error::invalid_environment(
1221                    DeclType::Collection,
1222                    "environment",
1223                    environment,
1224                ));
1225            }
1226        }
1227        // Allow `allowed_offers` & `allow_long_names` to be unset/unvalidated, for backwards compatibility.
1228    }
1229
1230    fn validate_environment_decl(&mut self, environment: &'a fdecl::Environment) {
1231        let name = environment.name.as_ref();
1232        check_name(name, DeclType::Environment, "name", &mut self.errors);
1233        if environment.extends.is_none() {
1234            self.errors.push(Error::missing_field(DeclType::Environment, "extends"));
1235        }
1236        if let Some(runners) = environment.runners.as_ref() {
1237            let mut registered_runners = HashSet::new();
1238            for runner in runners {
1239                self.validate_runner_registration(runner, &mut registered_runners);
1240            }
1241        }
1242        if let Some(resolvers) = environment.resolvers.as_ref() {
1243            let mut registered_schemes = HashSet::new();
1244            for resolver in resolvers {
1245                self.validate_resolver_registration(resolver, &mut registered_schemes);
1246            }
1247        }
1248
1249        match environment.extends.as_ref() {
1250            Some(fdecl::EnvironmentExtends::None) => {
1251                if environment.stop_timeout_ms.is_none() {
1252                    self.errors
1253                        .push(Error::missing_field(DeclType::Environment, "stop_timeout_ms"));
1254                }
1255            }
1256            None | Some(fdecl::EnvironmentExtends::Realm) => {}
1257        }
1258
1259        if let Some(debugs) = environment.debug_capabilities.as_ref() {
1260            for debug in debugs {
1261                self.validate_environment_debug_registration(debug);
1262            }
1263        }
1264    }
1265
1266    fn validate_runner_registration(
1267        &mut self,
1268        runner_registration: &'a fdecl::RunnerRegistration,
1269        runner_names: &mut HashSet<&'a str>,
1270    ) {
1271        check_name(
1272            runner_registration.source_name.as_ref(),
1273            DeclType::RunnerRegistration,
1274            "source_name",
1275            &mut self.errors,
1276        );
1277        self.validate_registration_source(
1278            runner_registration.source.as_ref(),
1279            DeclType::RunnerRegistration,
1280        );
1281        // If the source is `self`, ensure we have a corresponding Runner.
1282        if let (Some(fdecl::Ref::Self_(_)), Some(name)) =
1283            (&runner_registration.source, runner_registration.source_name.as_ref())
1284        {
1285            if !self.all_runners.contains(name as &str) {
1286                self.errors.push(Error::invalid_runner(
1287                    DeclType::RunnerRegistration,
1288                    "source_name",
1289                    name,
1290                ));
1291            }
1292        }
1293
1294        check_name(
1295            runner_registration.target_name.as_ref(),
1296            DeclType::RunnerRegistration,
1297            "target_name",
1298            &mut self.errors,
1299        );
1300        if let Some(name) = runner_registration.target_name.as_ref() {
1301            if !runner_names.insert(name.as_str()) {
1302                self.errors.push(Error::duplicate_field(
1303                    DeclType::RunnerRegistration,
1304                    "target_name",
1305                    name,
1306                ));
1307            }
1308        }
1309    }
1310
1311    fn validate_resolver_registration(
1312        &mut self,
1313        resolver_registration: &'a fdecl::ResolverRegistration,
1314        schemes: &mut HashSet<&'a str>,
1315    ) {
1316        check_name(
1317            resolver_registration.resolver.as_ref(),
1318            DeclType::ResolverRegistration,
1319            "resolver",
1320            &mut self.errors,
1321        );
1322        self.validate_registration_source(
1323            resolver_registration.source.as_ref(),
1324            DeclType::ResolverRegistration,
1325        );
1326        check_url_scheme(
1327            resolver_registration.scheme.as_ref(),
1328            DeclType::ResolverRegistration,
1329            "scheme",
1330            &mut self.errors,
1331        );
1332        if let Some(scheme) = resolver_registration.scheme.as_ref() {
1333            if !schemes.insert(scheme.as_str()) {
1334                self.errors.push(Error::duplicate_field(
1335                    DeclType::ResolverRegistration,
1336                    "scheme",
1337                    scheme,
1338                ));
1339            }
1340        }
1341    }
1342
1343    fn validate_registration_source(&mut self, source: Option<&'a fdecl::Ref>, ty: DeclType) {
1344        match source {
1345            Some(fdecl::Ref::Parent(_)) => {}
1346            Some(fdecl::Ref::Self_(_)) => {}
1347            Some(fdecl::Ref::Child(child_ref)) => {
1348                // Make sure the child is valid.
1349                self.validate_child_ref(ty, "source", &child_ref, OfferType::Static);
1350            }
1351            Some(_) => {
1352                self.errors.push(Error::invalid_field(ty, "source"));
1353            }
1354            None => {
1355                self.errors.push(Error::missing_field(ty, "source"));
1356            }
1357        }
1358    }
1359
1360    fn validate_service_decl(&mut self, service: &'a fdecl::Service, as_builtin: bool) {
1361        if check_name(service.name.as_ref(), DeclType::Service, "name", &mut self.errors) {
1362            let name = service.name.as_ref().unwrap();
1363            if !self.all_capability_ids.insert(name) {
1364                self.errors.push(Error::duplicate_field(DeclType::Service, "name", name.as_str()));
1365            }
1366            self.all_services.insert(name);
1367        }
1368        match as_builtin {
1369            true => {
1370                if let Some(path) = service.source_path.as_ref() {
1371                    self.errors.push(Error::extraneous_source_path(DeclType::Service, path))
1372                }
1373            }
1374            false => {
1375                check_path(
1376                    service.source_path.as_ref(),
1377                    DeclType::Service,
1378                    "source_path",
1379                    &mut self.errors,
1380                );
1381            }
1382        }
1383    }
1384
1385    fn validate_protocol_decl(&mut self, protocol: &'a fdecl::Protocol, as_builtin: bool) {
1386        if check_name(protocol.name.as_ref(), DeclType::Protocol, "name", &mut self.errors) {
1387            let name = protocol.name.as_ref().unwrap();
1388            if !self.all_capability_ids.insert(name) {
1389                self.errors.push(Error::duplicate_field(DeclType::Protocol, "name", name.as_str()));
1390            }
1391            self.all_protocols.insert(name);
1392        }
1393        match as_builtin {
1394            true => {
1395                if let Some(path) = protocol.source_path.as_ref() {
1396                    self.errors.push(Error::extraneous_source_path(DeclType::Protocol, path))
1397                }
1398            }
1399            false => {
1400                check_path(
1401                    protocol.source_path.as_ref(),
1402                    DeclType::Protocol,
1403                    "source_path",
1404                    &mut self.errors,
1405                );
1406            }
1407        }
1408
1409        #[cfg(fuchsia_api_level_at_least = "HEAD")]
1410        match protocol.delivery {
1411            Some(delivery) => match cm_types::DeliveryType::try_from(delivery) {
1412                Ok(_) => {}
1413                Err(_) => self.errors.push(Error::invalid_field(DeclType::Protocol, "delivery")),
1414            },
1415            None => {}
1416        }
1417    }
1418
1419    fn validate_directory_decl(&mut self, directory: &'a fdecl::Directory, as_builtin: bool) {
1420        if check_name(directory.name.as_ref(), DeclType::Directory, "name", &mut self.errors) {
1421            let name = directory.name.as_ref().unwrap();
1422            if !self.all_capability_ids.insert(name) {
1423                self.errors.push(Error::duplicate_field(
1424                    DeclType::Directory,
1425                    "name",
1426                    name.as_str(),
1427                ));
1428            }
1429            self.all_directories.insert(name);
1430        }
1431        match as_builtin {
1432            true => {
1433                if let Some(path) = directory.source_path.as_ref() {
1434                    self.errors.push(Error::extraneous_source_path(DeclType::Directory, path))
1435                }
1436            }
1437            false => {
1438                check_path(
1439                    directory.source_path.as_ref(),
1440                    DeclType::Directory,
1441                    "source_path",
1442                    &mut self.errors,
1443                );
1444            }
1445        }
1446        if directory.rights.is_none() {
1447            self.errors.push(Error::missing_field(DeclType::Directory, "rights"));
1448        }
1449    }
1450
1451    fn validate_storage_decl(&mut self, storage: &'a fdecl::Storage) {
1452        match storage.source.as_ref() {
1453            Some(fdecl::Ref::Parent(_)) => {}
1454            Some(fdecl::Ref::Self_(_)) => {}
1455            Some(fdecl::Ref::Child(child)) => {
1456                let _ =
1457                    self.validate_child_ref(DeclType::Storage, "source", &child, OfferType::Static);
1458            }
1459            Some(_) => {
1460                self.errors.push(Error::invalid_field(DeclType::Storage, "source"));
1461            }
1462            None => {
1463                self.errors.push(Error::missing_field(DeclType::Storage, "source"));
1464            }
1465        };
1466        if check_name(storage.name.as_ref(), DeclType::Storage, "name", &mut self.errors) {
1467            let name = storage.name.as_ref().unwrap();
1468            if !self.all_capability_ids.insert(name) {
1469                self.errors.push(Error::duplicate_field(DeclType::Storage, "name", name.as_str()));
1470            }
1471            self.all_storages.insert(name, storage.source.as_ref());
1472        }
1473        if storage.storage_id.is_none() {
1474            self.errors.push(Error::missing_field(DeclType::Storage, "storage_id"));
1475        }
1476        check_name(
1477            storage.backing_dir.as_ref(),
1478            DeclType::Storage,
1479            "backing_dir",
1480            &mut self.errors,
1481        );
1482    }
1483
1484    fn validate_runner_decl(&mut self, runner: &'a fdecl::Runner, as_builtin: bool) {
1485        if check_name(runner.name.as_ref(), DeclType::Runner, "name", &mut self.errors) {
1486            let name = runner.name.as_ref().unwrap();
1487            if !self.all_capability_ids.insert(name) {
1488                self.errors.push(Error::duplicate_field(DeclType::Runner, "name", name.as_str()));
1489            }
1490            self.all_runners.insert(name);
1491        }
1492        match as_builtin {
1493            true => {
1494                if let Some(path) = runner.source_path.as_ref() {
1495                    self.errors.push(Error::extraneous_source_path(DeclType::Runner, path))
1496                }
1497            }
1498            false => {
1499                check_path(
1500                    runner.source_path.as_ref(),
1501                    DeclType::Runner,
1502                    "source_path",
1503                    &mut self.errors,
1504                );
1505            }
1506        }
1507    }
1508
1509    fn validate_resolver_decl(&mut self, resolver: &'a fdecl::Resolver, as_builtin: bool) {
1510        if check_name(resolver.name.as_ref(), DeclType::Resolver, "name", &mut self.errors) {
1511            let name = resolver.name.as_ref().unwrap();
1512            if !self.all_capability_ids.insert(name) {
1513                self.errors.push(Error::duplicate_field(DeclType::Resolver, "name", name.as_str()));
1514            }
1515            self.all_resolvers.insert(name);
1516        }
1517        match as_builtin {
1518            true => {
1519                if let Some(path) = resolver.source_path.as_ref() {
1520                    self.errors.push(Error::extraneous_source_path(DeclType::Resolver, path))
1521                }
1522            }
1523            false => {
1524                check_path(
1525                    resolver.source_path.as_ref(),
1526                    DeclType::Resolver,
1527                    "source_path",
1528                    &mut self.errors,
1529                );
1530            }
1531        }
1532    }
1533
1534    // Dictionaries can reference other dictionaries in the same manifest, so before processing any
1535    // dictionary declarations this function should be called to do a first pass to pre-populate
1536    // the dictionary map.
1537    fn load_dictionary_names(&mut self, dictionaries: impl Iterator<Item = &'a fdecl::Dictionary>) {
1538        for dictionary in dictionaries {
1539            let decl = DeclType::Dictionary;
1540            if check_name(dictionary.name.as_ref(), decl, "name", &mut self.errors) {
1541                let name = dictionary.name.as_ref().unwrap();
1542                if !self.all_capability_ids.insert(name) {
1543                    self.errors.push(Error::duplicate_field(decl, "name", name.as_str()));
1544                }
1545                self.all_dictionaries.insert(name, &dictionary);
1546            }
1547        }
1548    }
1549
1550    fn validate_dictionary_decl(&mut self, dictionary: &'a fdecl::Dictionary) {
1551        let decl = DeclType::Dictionary;
1552        if let Some(path) = dictionary.source_path.as_ref() {
1553            if dictionary.source.is_some() {
1554                self.errors.push(Error::extraneous_field(decl, "source"));
1555            }
1556            check_path(Some(path), DeclType::Dictionary, "source_path", &mut self.errors);
1557        }
1558    }
1559
1560    #[cfg(fuchsia_api_level_at_least = "HEAD")]
1561    fn validate_configuration_decl(&mut self, config: &'a fdecl::Configuration) {
1562        let decl = DeclType::Configuration;
1563        if check_name(config.name.as_ref(), decl, "name", &mut self.errors) {
1564            let name = config.name.as_ref().unwrap();
1565            if !self.all_capability_ids.insert(name) {
1566                self.errors.push(Error::duplicate_field(decl, "name", name.as_str()));
1567            }
1568            self.all_configs.insert(name);
1569        }
1570    }
1571
1572    fn validate_environment_debug_registration(&mut self, debug: &'a fdecl::DebugRegistration) {
1573        match debug {
1574            fdecl::DebugRegistration::Protocol(o) => {
1575                let decl = DeclType::DebugProtocolRegistration;
1576                self.validate_environment_debug_fields(
1577                    decl,
1578                    o.source.as_ref(),
1579                    o.source_name.as_ref(),
1580                    o.target_name.as_ref(),
1581                );
1582
1583                if let (Some(fdecl::Ref::Self_(_)), Some(name)) =
1584                    (&o.source, o.source_name.as_ref())
1585                {
1586                    if !self.all_protocols.contains(&name as &str) {
1587                        self.errors.push(Error::invalid_field(decl, "source"));
1588                    }
1589                }
1590            }
1591            _ => {
1592                self.errors.push(Error::invalid_field(DeclType::Environment, "debug"));
1593            }
1594        }
1595    }
1596
1597    fn validate_environment_debug_fields(
1598        &mut self,
1599        decl: DeclType,
1600        source: Option<&fdecl::Ref>,
1601        source_name: Option<&String>,
1602        target_name: Option<&'a String>,
1603    ) {
1604        // We don't support "source" from "capability" for now.
1605        match source {
1606            Some(fdecl::Ref::Parent(_)) => {}
1607            Some(fdecl::Ref::Self_(_)) => {}
1608            Some(fdecl::Ref::Framework(_)) => {}
1609            Some(fdecl::Ref::Child(child)) => {
1610                let _ = self.validate_child_ref(decl, "source", &child, OfferType::Static);
1611            }
1612            Some(_) => self.errors.push(Error::invalid_field(decl, "source")),
1613            None => self.errors.push(Error::missing_field(decl, "source")),
1614        }
1615        check_name(source_name, decl, "source_name", &mut self.errors);
1616        check_name(target_name, decl, "target_name", &mut self.errors);
1617    }
1618
1619    fn validate_event_stream_decl(&mut self, event: &'a fdecl::EventStream) {
1620        if check_name(event.name.as_ref(), DeclType::EventStream, "name", &mut self.errors) {
1621            let name = event.name.as_ref().unwrap();
1622            if !self.all_capability_ids.insert(name) {
1623                self.errors.push(Error::duplicate_field(
1624                    DeclType::EventStream,
1625                    "name",
1626                    name.as_str(),
1627                ));
1628            }
1629        }
1630    }
1631
1632    fn validate_source_collection(
1633        &mut self,
1634        collection: &fdecl::CollectionRef,
1635        decl_type: DeclType,
1636    ) -> bool {
1637        let num_errors = self.errors.len();
1638        if check_name(Some(&collection.name), decl_type, "source.collection.name", &mut self.errors)
1639            && !self.all_collections.contains(&collection.name as &str)
1640        {
1641            self.errors.push(Error::invalid_collection(
1642                decl_type,
1643                "source",
1644                &collection.name as &str,
1645            ));
1646        }
1647        num_errors == self.errors.len()
1648    }
1649
1650    fn validate_filtered_service_fields(
1651        &mut self,
1652        decl_type: DeclType,
1653        source_instance_filter: Option<&Vec<String>>,
1654        renamed_instances: Option<&Vec<fdecl::NameMapping>>,
1655    ) {
1656        if let Some(source_instance_filter) = source_instance_filter {
1657            if source_instance_filter.is_empty() {
1658                // if the  source_instance_filter is empty the offered service will have 0 instances,
1659                // which means the offer shouldn't have been created at all.
1660                self.errors.push(Error::invalid_field(decl_type, "source_instance_filter"));
1661            }
1662            for name in source_instance_filter {
1663                check_name(Some(name), decl_type, "source_instance_filter", &mut self.errors);
1664            }
1665        }
1666        if let Some(renamed_instances) = renamed_instances {
1667            // Multiple sources shouldn't map to the same target name
1668            let mut seen_target_names = HashSet::<String>::new();
1669            for mapping in renamed_instances {
1670                check_name(
1671                    Some(&mapping.source_name),
1672                    decl_type,
1673                    "renamed_instances.source_name",
1674                    &mut self.errors,
1675                );
1676                check_name(
1677                    Some(&mapping.target_name),
1678                    decl_type,
1679                    "renamed_instances.target_name",
1680                    &mut self.errors,
1681                );
1682                if !seen_target_names.insert(mapping.target_name.clone()) {
1683                    self.errors.push(Error::invalid_field(decl_type, "renamed_instances"));
1684                    break;
1685                }
1686            }
1687        }
1688    }
1689
1690    fn validate_source_capability(
1691        &mut self,
1692        capability: &fdecl::CapabilityRef,
1693        decl_type: DeclType,
1694        field: &str,
1695    ) -> bool {
1696        let num_errors = self.errors.len();
1697        if check_name(Some(&capability.name), decl_type, "source.capability.name", &mut self.errors)
1698            && !self.all_capability_ids.contains(capability.name.as_str())
1699        {
1700            self.errors.push(Error::invalid_capability(decl_type, field, &capability.name));
1701        }
1702        num_errors == self.errors.len()
1703    }
1704
1705    /// Return a key that can be used in `HashMap` to group aggregate declarations.
1706    ///
1707    /// Returns `None` if the input resembles an invalid declaration.
1708    fn make_group_key(
1709        target_name: Option<&'a String>,
1710        target: Option<&'a fdecl::Ref>,
1711    ) -> Option<(&'a str, RefKey<'a>)> {
1712        if target_name.is_none() {
1713            return None;
1714        }
1715        let target_name = target_name.unwrap().as_str();
1716        if target.is_none() {
1717            return None;
1718        }
1719        let target = match target.unwrap() {
1720            fdecl::Ref::Parent(_) => RefKey::Parent,
1721            fdecl::Ref::Self_(_) => RefKey::Self_,
1722            fdecl::Ref::Child(r) => RefKey::Child(r.name.as_str()),
1723            fdecl::Ref::Collection(r) => RefKey::Collection(r.name.as_str()),
1724            fdecl::Ref::Framework(_) => RefKey::Framework,
1725            fdecl::Ref::Capability(_) => RefKey::Capability,
1726            fdecl::Ref::Debug(_) => RefKey::Debug,
1727            fdecl::RefUnknown!() => {
1728                return None;
1729            }
1730        };
1731        Some((target_name, target))
1732    }
1733
1734    fn validate_aggregation_has_same_availability(
1735        &mut self,
1736        route_group: &Vec<impl HasAvailability>,
1737    ) {
1738        // Use `BtreeSet` for stable ordering of items in error message.
1739        let availability_of_sources: BTreeSet<_> =
1740            route_group.iter().map(|r| r.availability()).collect();
1741
1742        // All sources that feed into an aggregation operation should have the same availability.
1743        if availability_of_sources.len() > 1 {
1744            self.errors.push(Error::different_availability_in_aggregation(
1745                availability_of_sources.into_iter().collect(),
1746            ));
1747        }
1748    }
1749
1750    // Checks a group of expose decls to confirm that any duplicate exposes are
1751    // valid aggregate expose declarations.
1752    fn validate_expose_group(&mut self, exposes: &'a [fdecl::Expose]) {
1753        let mut expose_groups: HashMap<_, Vec<fdecl::ExposeService>> = HashMap::new();
1754        let service_exposes = exposes
1755            .iter()
1756            .filter_map(|o| if let fdecl::Expose::Service(s) = o { Some(s) } else { None });
1757        for expose in service_exposes {
1758            let key = Self::make_group_key(expose.target_name.as_ref(), expose.target.as_ref());
1759            if let Some(key) = key {
1760                expose_groups.entry(key).or_insert_with(|| vec![]).push(expose.clone());
1761            }
1762        }
1763        for expose_group in expose_groups.into_values() {
1764            if expose_group.len() == 1 {
1765                // If there are not multiple exposes for a (target_name, target) pair then there are
1766                // no aggregation conditions to check.
1767                continue;
1768            }
1769
1770            self.validate_aggregation_has_same_availability(&expose_group);
1771        }
1772    }
1773
1774    fn validate_expose_decl(
1775        &mut self,
1776        expose: &'a fdecl::Expose,
1777        expose_to_parent_ids: &mut HashMap<&'a str, AllowableIds>,
1778        expose_to_framework_ids: &mut HashMap<&'a str, AllowableIds>,
1779    ) {
1780        match expose {
1781            fdecl::Expose::Service(e) => {
1782                let decl = DeclType::ExposeService;
1783                self.validate_expose_fields(
1784                    decl,
1785                    AllowableIds::Many,
1786                    CollectionSource::Allow,
1787                    Self::service_checker,
1788                    e.source.as_ref(),
1789                    e.source_name.as_ref(),
1790                    e.source_dictionary.as_ref(),
1791                    e.target.as_ref(),
1792                    e.target_name.as_ref(),
1793                    e.availability.as_ref(),
1794                    expose_to_parent_ids,
1795                    expose_to_framework_ids,
1796                );
1797            }
1798            fdecl::Expose::Protocol(e) => {
1799                let decl = DeclType::ExposeProtocol;
1800                self.validate_expose_fields(
1801                    decl,
1802                    AllowableIds::One,
1803                    CollectionSource::Deny,
1804                    Self::protocol_checker,
1805                    e.source.as_ref(),
1806                    e.source_name.as_ref(),
1807                    e.source_dictionary.as_ref(),
1808                    e.target.as_ref(),
1809                    e.target_name.as_ref(),
1810                    e.availability.as_ref(),
1811                    expose_to_parent_ids,
1812                    expose_to_framework_ids,
1813                );
1814            }
1815            fdecl::Expose::Directory(e) => {
1816                let decl = DeclType::ExposeDirectory;
1817                self.validate_expose_fields(
1818                    decl,
1819                    AllowableIds::One,
1820                    CollectionSource::Deny,
1821                    Self::directory_checker,
1822                    e.source.as_ref(),
1823                    e.source_name.as_ref(),
1824                    e.source_dictionary.as_ref(),
1825                    e.target.as_ref(),
1826                    e.target_name.as_ref(),
1827                    e.availability.as_ref(),
1828                    expose_to_parent_ids,
1829                    expose_to_framework_ids,
1830                );
1831
1832                // Subdir makes sense when routing, but when exposing to framework the subdirectory
1833                // can be exposed directly.
1834                match e.target.as_ref() {
1835                    Some(fdecl::Ref::Framework(_)) => {
1836                        if e.subdir.is_some() {
1837                            self.errors.push(Error::invalid_field(decl, "subdir"));
1838                        }
1839                    }
1840                    _ => {}
1841                }
1842
1843                if let Some(subdir) = e.subdir.as_ref() {
1844                    check_relative_path(Some(subdir), decl, "subdir", &mut self.errors);
1845                }
1846            }
1847            fdecl::Expose::Runner(e) => {
1848                let decl = DeclType::ExposeRunner;
1849                self.validate_expose_fields(
1850                    decl,
1851                    AllowableIds::One,
1852                    CollectionSource::Deny,
1853                    Self::runner_checker,
1854                    e.source.as_ref(),
1855                    e.source_name.as_ref(),
1856                    e.source_dictionary.as_ref(),
1857                    e.target.as_ref(),
1858                    e.target_name.as_ref(),
1859                    Some(&fdecl::Availability::Required),
1860                    expose_to_parent_ids,
1861                    expose_to_framework_ids,
1862                );
1863            }
1864            fdecl::Expose::Resolver(e) => {
1865                let decl = DeclType::ExposeResolver;
1866                self.validate_expose_fields(
1867                    decl,
1868                    AllowableIds::One,
1869                    CollectionSource::Deny,
1870                    Self::resolver_checker,
1871                    e.source.as_ref(),
1872                    e.source_name.as_ref(),
1873                    e.source_dictionary.as_ref(),
1874                    e.target.as_ref(),
1875                    e.target_name.as_ref(),
1876                    Some(&fdecl::Availability::Required),
1877                    expose_to_parent_ids,
1878                    expose_to_framework_ids,
1879                );
1880            }
1881            fdecl::Expose::Dictionary(e) => {
1882                let decl = DeclType::ExposeDictionary;
1883                self.validate_expose_fields(
1884                    decl,
1885                    AllowableIds::One,
1886                    CollectionSource::Deny,
1887                    Self::dictionary_checker,
1888                    e.source.as_ref(),
1889                    e.source_name.as_ref(),
1890                    e.source_dictionary.as_ref(),
1891                    e.target.as_ref(),
1892                    e.target_name.as_ref(),
1893                    e.availability.as_ref(),
1894                    expose_to_parent_ids,
1895                    expose_to_framework_ids,
1896                );
1897            }
1898            #[cfg(fuchsia_api_level_at_least = "HEAD")]
1899            fdecl::Expose::Config(e) => {
1900                let decl = DeclType::ExposeConfig;
1901                self.validate_expose_fields(
1902                    decl,
1903                    AllowableIds::One,
1904                    CollectionSource::Deny,
1905                    Self::config_checker,
1906                    e.source.as_ref(),
1907                    e.source_name.as_ref(),
1908                    None,
1909                    e.target.as_ref(),
1910                    e.target_name.as_ref(),
1911                    e.availability.as_ref(),
1912                    expose_to_parent_ids,
1913                    expose_to_framework_ids,
1914                );
1915            }
1916            _ => {
1917                self.errors.push(Error::invalid_field(DeclType::Component, "expose"));
1918            }
1919        }
1920    }
1921
1922    fn validate_expose_fields(
1923        &mut self,
1924        decl: DeclType,
1925        allowable_ids: AllowableIds,
1926        collection_source: CollectionSource,
1927        // This takes a callback that returns a [Container], instead of the &[Container] directly,
1928        // to avoid a borrow checker error that would occur from a simultaneous borrow on
1929        // &mut self.
1930        capability_checker: impl Fn(&Self) -> &dyn Container,
1931        source: Option<&fdecl::Ref>,
1932        source_name: Option<&String>,
1933        source_dictionary: Option<&String>,
1934        target: Option<&fdecl::Ref>,
1935        target_name: Option<&'a String>,
1936        availability: Option<&fdecl::Availability>,
1937        expose_to_parent_ids: &mut HashMap<&'a str, AllowableIds>,
1938        expose_to_framework_ids: &mut HashMap<&'a str, AllowableIds>,
1939    ) {
1940        self.validate_expose_source(decl, collection_source, source, source_dictionary);
1941        check_route_availability(decl, availability, source, source_name, &mut self.errors);
1942        match target {
1943            Some(r) => match r {
1944                fdecl::Ref::Parent(_) => {}
1945                fdecl::Ref::Framework(_) => {}
1946                _ => {
1947                    self.errors.push(Error::invalid_field(decl, "target"));
1948                }
1949            },
1950            None => {
1951                self.errors.push(Error::missing_field(decl, "target"));
1952            }
1953        }
1954        check_name(source_name, decl, "source_name", &mut self.errors);
1955        if source_dictionary.is_some() {
1956            check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
1957        }
1958        if check_name(target_name, decl, "target_name", &mut self.errors) {
1959            let maybe_ids_set = match target {
1960                Some(fdecl::Ref::Parent(_)) => Some(expose_to_parent_ids),
1961                Some(fdecl::Ref::Framework(_)) => Some(expose_to_framework_ids),
1962                _ => None,
1963            };
1964            if let Some(ids_set) = maybe_ids_set {
1965                let target_name = target_name.unwrap();
1966                if let Some(prev_state) = ids_set.insert(target_name, allowable_ids) {
1967                    if prev_state == AllowableIds::One || prev_state != allowable_ids {
1968                        self.errors.push(Error::duplicate_field(decl, "target_name", target_name));
1969                    }
1970                }
1971            }
1972        }
1973
1974        self.validate_route_from_self(
1975            decl,
1976            source,
1977            source_name,
1978            source_dictionary,
1979            capability_checker,
1980        );
1981    }
1982
1983    fn validate_expose_source(
1984        &mut self,
1985        decl: DeclType,
1986        collection_source: CollectionSource,
1987        source: Option<&fdecl::Ref>,
1988        source_dictionary: Option<&String>,
1989    ) {
1990        match (source, source_dictionary) {
1991            // These sources support source_dictionary.
1992            (Some(fdecl::Ref::Self_(_)), _) => {}
1993            (Some(fdecl::Ref::Child(child)), _) => {
1994                let _ = self.validate_child_ref(decl, "source", &child, OfferType::Static);
1995            }
1996            // These sources don't.
1997            (Some(fdecl::Ref::VoidType(_)), None) => {}
1998            (Some(fdecl::Ref::Framework(_)), None) => {}
1999            (Some(fdecl::Ref::Capability(c)), None) => {
2000                self.validate_source_capability(c, decl, "source");
2001            }
2002            (Some(fdecl::Ref::Collection(c)), None)
2003                if collection_source == CollectionSource::Allow =>
2004            {
2005                self.validate_source_collection(c, decl);
2006            }
2007            // `source` is required.
2008            (None, _) => {
2009                self.errors.push(Error::missing_field(decl, "source"));
2010            }
2011            // Any combination that was not recognized above must be invalid.
2012            (_, _) => {
2013                self.errors.push(Error::invalid_field(decl, "source"));
2014            }
2015        }
2016    }
2017
2018    // Checks a group of offer decls to confirm that any duplicate offers are
2019    // valid aggregate offer declarations.
2020    fn validate_offer_group(&mut self, offers: &'a [fdecl::Offer], offer_type: OfferType) {
2021        let mut offer_groups: HashMap<_, Vec<fdecl::OfferService>> = HashMap::new();
2022        let service_offers = offers
2023            .iter()
2024            .filter_map(|o| if let fdecl::Offer::Service(s) = o { Some(s) } else { None });
2025        for offer in service_offers {
2026            let key = Self::make_group_key(offer.target_name.as_ref(), offer.target.as_ref());
2027            if let Some(key) = key {
2028                offer_groups.entry(key).or_insert_with(|| vec![]).push(offer.clone());
2029            }
2030        }
2031        for offer_group in offer_groups.into_values() {
2032            if offer_group.len() == 1 {
2033                // If there are not multiple offers for a (target_name, target) pair then there are
2034                // no aggregation conditions to check.
2035                continue;
2036            }
2037
2038            self.validate_aggregation_has_same_availability(&offer_group);
2039
2040            let mut source_instance_filter_entries: HashSet<String> = HashSet::new();
2041            let mut service_source_names: HashSet<String> = HashSet::new();
2042            for o in offer_group {
2043                // Currently only service capabilities can be aggregated
2044                match (o.source_instance_filter, offer_type) {
2045                    (Some(source_instance_filter), _) => {
2046                        for instance_name in source_instance_filter {
2047                            if !source_instance_filter_entries.insert(instance_name.clone()) {
2048                                // If the source instance in the filter has been seen before this
2049                                // means there is a conflicting aggregate service offer.
2050                                self.errors.push(Error::invalid_aggregate_offer(format!(
2051                                    "Conflicting source_instance_filter in aggregate service \
2052                                    offer, instance_name '{}' seen in filter lists multiple times",
2053                                    instance_name,
2054                                )));
2055                            }
2056                        }
2057                    }
2058                    (None, OfferType::Static) => {}
2059                    (None, OfferType::Dynamic) => {
2060                        // Dynamic offers must include a filter.
2061                        self.errors.push(Error::invalid_aggregate_offer(
2062                            "source_instance_filter must be set for dynamic aggregate service \
2063                            offers",
2064                        ));
2065                    }
2066                }
2067                service_source_names.insert(
2068                    o.source_name
2069                        .expect("Offer Service declarations must always contain source_name"),
2070                );
2071            }
2072
2073            if service_source_names.len() > 1 {
2074                self.errors.push(Error::invalid_aggregate_offer(format!(
2075                    "All aggregate service offers must have the same source_name, saw {}. Use \
2076                    renamed_instances to rename instance names to avoid conflict.",
2077                    service_source_names.into_iter().sorted().collect::<Vec<String>>().join(", ")
2078                )));
2079            }
2080        }
2081    }
2082
2083    fn validate_offer_decl(&mut self, offer: &'a fdecl::Offer, offer_type: OfferType) {
2084        match offer {
2085            fdecl::Offer::Service(o) => {
2086                let decl = DeclType::OfferService;
2087                self.validate_offer_fields(
2088                    decl,
2089                    AllowableIds::Many,
2090                    CollectionSource::Allow,
2091                    Self::service_checker,
2092                    o.source.as_ref(),
2093                    o.source_name.as_ref(),
2094                    o.source_dictionary.as_ref(),
2095                    o.target.as_ref(),
2096                    o.target_name.as_ref(),
2097                    #[cfg(fuchsia_api_level_at_least = "HEAD")]
2098                    Some(o.dependency_type.as_ref().unwrap_or(&fdecl::DependencyType::Strong)),
2099                    #[cfg(fuchsia_api_level_less_than = "HEAD")]
2100                    Some(&fdecl::DependencyType::Strong),
2101                    o.availability.as_ref(),
2102                    offer_type,
2103                );
2104                self.validate_filtered_service_fields(
2105                    decl,
2106                    o.source_instance_filter.as_ref(),
2107                    o.renamed_instances.as_ref(),
2108                );
2109            }
2110            fdecl::Offer::Protocol(o) => {
2111                let decl = DeclType::OfferProtocol;
2112                self.validate_offer_fields(
2113                    decl,
2114                    AllowableIds::One,
2115                    CollectionSource::Deny,
2116                    Self::protocol_checker,
2117                    o.source.as_ref(),
2118                    o.source_name.as_ref(),
2119                    o.source_dictionary.as_ref(),
2120                    o.target.as_ref(),
2121                    o.target_name.as_ref(),
2122                    o.dependency_type.as_ref(),
2123                    o.availability.as_ref(),
2124                    offer_type,
2125                );
2126            }
2127            fdecl::Offer::Directory(o) => {
2128                let decl = DeclType::OfferDirectory;
2129                self.validate_offer_fields(
2130                    decl,
2131                    AllowableIds::One,
2132                    CollectionSource::Deny,
2133                    Self::directory_checker,
2134                    o.source.as_ref(),
2135                    o.source_name.as_ref(),
2136                    o.source_dictionary.as_ref(),
2137                    o.target.as_ref(),
2138                    o.target_name.as_ref(),
2139                    o.dependency_type.as_ref(),
2140                    o.availability.as_ref(),
2141                    offer_type,
2142                );
2143                if let Some(subdir) = o.subdir.as_ref() {
2144                    check_relative_path(
2145                        Some(subdir),
2146                        DeclType::OfferDirectory,
2147                        "subdir",
2148                        &mut self.errors,
2149                    );
2150                }
2151            }
2152            fdecl::Offer::Storage(o) => {
2153                let decl = DeclType::OfferStorage;
2154                self.validate_storage_offer_fields(
2155                    decl,
2156                    Self::storage_checker,
2157                    o.source.as_ref(),
2158                    o.source_name.as_ref(),
2159                    o.target.as_ref(),
2160                    o.target_name.as_ref(),
2161                    o.availability.as_ref(),
2162                    offer_type,
2163                );
2164            }
2165            fdecl::Offer::Runner(o) => {
2166                let decl = DeclType::OfferRunner;
2167                self.validate_offer_fields(
2168                    decl,
2169                    AllowableIds::One,
2170                    CollectionSource::Deny,
2171                    Self::runner_checker,
2172                    o.source.as_ref(),
2173                    o.source_name.as_ref(),
2174                    o.source_dictionary.as_ref(),
2175                    o.target.as_ref(),
2176                    o.target_name.as_ref(),
2177                    Some(&fdecl::DependencyType::Strong),
2178                    Some(&fdecl::Availability::Required),
2179                    offer_type,
2180                );
2181            }
2182            fdecl::Offer::Resolver(o) => {
2183                let decl = DeclType::OfferResolver;
2184                self.validate_offer_fields(
2185                    decl,
2186                    AllowableIds::One,
2187                    CollectionSource::Deny,
2188                    Self::resolver_checker,
2189                    o.source.as_ref(),
2190                    o.source_name.as_ref(),
2191                    o.source_dictionary.as_ref(),
2192                    o.target.as_ref(),
2193                    o.target_name.as_ref(),
2194                    Some(&fdecl::DependencyType::Strong),
2195                    Some(&fdecl::Availability::Required),
2196                    offer_type,
2197                );
2198            }
2199            fdecl::Offer::EventStream(e) => {
2200                self.validate_event_stream_offer_fields(e, offer_type);
2201            }
2202            fdecl::Offer::Dictionary(o) => {
2203                let decl = DeclType::OfferDictionary;
2204                self.validate_offer_fields(
2205                    decl,
2206                    AllowableIds::One,
2207                    CollectionSource::Deny,
2208                    Self::dictionary_checker,
2209                    o.source.as_ref(),
2210                    o.source_name.as_ref(),
2211                    o.source_dictionary.as_ref(),
2212                    o.target.as_ref(),
2213                    o.target_name.as_ref(),
2214                    o.dependency_type.as_ref(),
2215                    o.availability.as_ref(),
2216                    offer_type,
2217                );
2218            }
2219            #[cfg(fuchsia_api_level_at_least = "HEAD")]
2220            fdecl::Offer::Config(o) => {
2221                let decl = DeclType::OfferConfig;
2222                self.validate_offer_fields(
2223                    decl,
2224                    AllowableIds::One,
2225                    CollectionSource::Deny,
2226                    Self::config_checker,
2227                    o.source.as_ref(),
2228                    o.source_name.as_ref(),
2229                    None,
2230                    o.target.as_ref(),
2231                    o.target_name.as_ref(),
2232                    Some(&fdecl::DependencyType::Strong),
2233                    o.availability.as_ref(),
2234                    offer_type,
2235                );
2236            }
2237            fdecl::OfferUnknown!() => {
2238                self.errors.push(Error::invalid_field(DeclType::Component, "offer"));
2239            }
2240        }
2241    }
2242
2243    fn validate_offer_fields(
2244        &mut self,
2245        decl: DeclType,
2246        allowable_names: AllowableIds,
2247        collection_source: CollectionSource,
2248        capability_checker: impl Fn(&Self) -> &dyn Container,
2249        source: Option<&'a fdecl::Ref>,
2250        source_name: Option<&'a String>,
2251        source_dictionary: Option<&'a String>,
2252        target: Option<&'a fdecl::Ref>,
2253        target_name: Option<&'a String>,
2254        dependency_type: Option<&'a fdecl::DependencyType>,
2255        availability: Option<&'a fdecl::Availability>,
2256        offer_type: OfferType,
2257    ) {
2258        self.validate_offer_source(decl, collection_source, source, source_dictionary, offer_type);
2259        check_route_availability(decl, availability, source, source_name, &mut self.errors);
2260        check_offer_name(source_name, decl, "source_name", offer_type, &mut self.errors);
2261        if source_dictionary.is_some() {
2262            check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
2263        }
2264        self.validate_offer_target(decl, allowable_names, target, target_name, offer_type);
2265        check_offer_name(target_name, decl, "target_name", offer_type, &mut self.errors);
2266
2267        if dependency_type.is_none() {
2268            self.errors.push(Error::missing_field(decl, "dependency_type"));
2269        }
2270
2271        self.validate_route_from_self(
2272            decl,
2273            source,
2274            source_name,
2275            source_dictionary,
2276            capability_checker,
2277        );
2278    }
2279
2280    fn validate_offer_source(
2281        &mut self,
2282        decl: DeclType,
2283        collection_source: CollectionSource,
2284        source: Option<&'a fdecl::Ref>,
2285        source_dictionary: Option<&'a String>,
2286        offer_type: OfferType,
2287    ) {
2288        match (source, source_dictionary) {
2289            // These sources support source_dictionary.
2290            (Some(fdecl::Ref::Parent(_)), _) => {}
2291            (Some(fdecl::Ref::Self_(_)), _) => {}
2292            (Some(fdecl::Ref::Child(child)), _) => {
2293                self.validate_child_ref(decl, "source", &child, offer_type);
2294            }
2295            // These sources don't.
2296            (Some(fdecl::Ref::VoidType(_)), None) => {}
2297            (Some(fdecl::Ref::Framework(_)), None) => {}
2298            (Some(fdecl::Ref::Capability(c)), None) => {
2299                self.validate_source_capability(c, decl, "source");
2300            }
2301            (Some(fdecl::Ref::Collection(c)), None)
2302                if collection_source == CollectionSource::Allow =>
2303            {
2304                self.validate_source_collection(c, decl);
2305            }
2306            // `source` is required.
2307            (None, _) => self.errors.push(Error::missing_field(decl, "source")),
2308            // Any combination that was not recognized above must be invalid.
2309            (_, _) => self.errors.push(Error::invalid_field(decl, "source")),
2310        }
2311    }
2312
2313    fn validate_storage_offer_fields(
2314        &mut self,
2315        decl: DeclType,
2316        // This takes a callback that returns a [Container], instead of the &[Container] directly,
2317        // to avoid a borrow checker error that would occur from a simultaneous borrow on
2318        // &mut self.
2319        capability_checker: impl Fn(&Self) -> &dyn Container,
2320        source: Option<&'a fdecl::Ref>,
2321        source_name: Option<&'a String>,
2322        target: Option<&'a fdecl::Ref>,
2323        target_name: Option<&'a String>,
2324        availability: Option<&fdecl::Availability>,
2325        offer_type: OfferType,
2326    ) {
2327        match source {
2328            Some(fdecl::Ref::Parent(_) | fdecl::Ref::VoidType(_) | fdecl::Ref::Self_(_)) => {}
2329            Some(_) => {
2330                self.errors.push(Error::invalid_field(decl, "source"));
2331            }
2332            None => {
2333                self.errors.push(Error::missing_field(decl, "source"));
2334            }
2335        }
2336        check_route_availability(decl, availability, source, source_name, &mut self.errors);
2337        check_offer_name(source_name, decl, "source_name", offer_type, &mut self.errors);
2338        self.validate_offer_target(decl, AllowableIds::One, target, target_name, offer_type);
2339        check_offer_name(target_name, decl, "target_name", offer_type, &mut self.errors);
2340
2341        if let (Some(fdecl::Ref::Self_(_)), Some(name)) = (source, source_name) {
2342            if !(capability_checker)(self).contains(name) {
2343                self.errors.push(Error::invalid_capability(decl, "source", name));
2344            }
2345        }
2346    }
2347
2348    fn validate_event_stream_offer_fields(
2349        &mut self,
2350        event_stream: &'a fdecl::OfferEventStream,
2351        offer_type: OfferType,
2352    ) {
2353        let decl = DeclType::OfferEventStream;
2354        check_name(event_stream.source_name.as_ref(), decl, "source_name", &mut self.errors);
2355        if event_stream.target == Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})) {
2356            // Expose to framework from framework is never valid.
2357            self.errors.push(Error::invalid_field(DeclType::OfferEventStream, "target"));
2358        }
2359        if let Some(scope) = &event_stream.scope {
2360            if scope.is_empty() {
2361                self.errors.push(Error::invalid_field(decl, "scope"));
2362            }
2363            for value in scope {
2364                match value {
2365                    fdecl::Ref::Child(child) => {
2366                        self.validate_child_ref(
2367                            DeclType::OfferEventStream,
2368                            "scope",
2369                            &child,
2370                            offer_type,
2371                        );
2372                    }
2373                    fdecl::Ref::Collection(collection) => {
2374                        self.validate_collection_ref(
2375                            DeclType::OfferEventStream,
2376                            "scope",
2377                            &collection,
2378                        );
2379                    }
2380                    _ => {
2381                        self.errors.push(Error::invalid_field(DeclType::OfferEventStream, "scope"));
2382                    }
2383                }
2384            }
2385        }
2386        // Only parent, framework, child, and void are valid.
2387        match event_stream.source {
2388            Some(
2389                fdecl::Ref::Parent(_)
2390                | fdecl::Ref::Framework(_)
2391                | fdecl::Ref::Child(_)
2392                | fdecl::Ref::VoidType(_),
2393            ) => {}
2394            Some(_) => {
2395                self.errors.push(Error::invalid_field(decl, "source"));
2396            }
2397            None => {
2398                self.errors.push(Error::missing_field(decl, "source"));
2399            }
2400        };
2401
2402        check_route_availability(
2403            decl,
2404            event_stream.availability.as_ref(),
2405            event_stream.source.as_ref(),
2406            event_stream.source_name.as_ref(),
2407            &mut self.errors,
2408        );
2409
2410        self.validate_offer_target(
2411            decl,
2412            AllowableIds::One,
2413            event_stream.target.as_ref(),
2414            event_stream.target_name.as_ref(),
2415            offer_type,
2416        );
2417        check_name(event_stream.target_name.as_ref(), decl, "target_name", &mut self.errors);
2418    }
2419
2420    /// Check a `ChildRef` contains a valid child that exists.
2421    fn validate_child_ref(
2422        &mut self,
2423        decl: DeclType,
2424        field_name: &str,
2425        child: &fdecl::ChildRef,
2426        offer_type: OfferType,
2427    ) -> bool {
2428        if offer_type == OfferType::Dynamic && child.collection.is_some() {
2429            return self.validate_dynamic_child_ref(decl, field_name, child);
2430        }
2431        // Ensure the name is valid, and the reference refers to a static child.
2432        //
2433        // We attempt to list all errors if possible.
2434        let mut valid = true;
2435        if !check_name(
2436            Some(&child.name),
2437            decl,
2438            &format!("{}.child.name", field_name),
2439            &mut self.errors,
2440        ) {
2441            valid = false;
2442        }
2443        if child.collection.is_some() {
2444            self.errors
2445                .push(Error::extraneous_field(decl, format!("{}.child.collection", field_name)));
2446            valid = false;
2447        }
2448        if !valid {
2449            return false;
2450        }
2451
2452        // Ensure the child exists.
2453        let name: &str = &child.name;
2454        if !self.all_children.contains(name) {
2455            self.errors.push(Error::invalid_child(decl, field_name, name));
2456            return false;
2457        }
2458
2459        true
2460    }
2461
2462    /// Check a `ChildRef` contains a valid dynamic child.
2463    ///
2464    /// The manifest we're validating doesn't contain dynamic children so we can't check if the dynamic
2465    /// child actually exists, but we can confirm things like the name is valid.
2466    fn validate_dynamic_child_ref(
2467        &mut self,
2468        decl: DeclType,
2469        field_name: &str,
2470        child: &fdecl::ChildRef,
2471    ) -> bool {
2472        // Ensure the name is valid.
2473        //
2474        // We attempt to list all errors if possible.
2475        let mut valid = true;
2476        if !check_dynamic_name(
2477            Some(&child.name),
2478            decl,
2479            &format!("{}.child.name", field_name),
2480            &mut self.errors,
2481        ) {
2482            valid = false;
2483        }
2484        if !check_name(
2485            child.collection.as_ref(),
2486            decl,
2487            &format!("{}.child.collection", field_name),
2488            &mut self.errors,
2489        ) {
2490            valid = false;
2491        }
2492        valid
2493    }
2494
2495    /// Check a `CollectionRef` is valid and refers to an existing collection.
2496    fn validate_collection_ref(
2497        &mut self,
2498        decl: DeclType,
2499        field_name: &str,
2500        collection: &fdecl::CollectionRef,
2501    ) -> bool {
2502        // Ensure the name is valid.
2503        if !check_name(
2504            Some(&collection.name),
2505            decl,
2506            &format!("{}.collection.name", field_name),
2507            &mut self.errors,
2508        ) {
2509            return false;
2510        }
2511
2512        // Ensure the collection exists.
2513        if !self.all_collections.contains(&collection.name as &str) {
2514            self.errors.push(Error::invalid_collection(decl, field_name, &collection.name as &str));
2515            return false;
2516        }
2517
2518        true
2519    }
2520
2521    fn validate_offer_target(
2522        &mut self,
2523        decl: DeclType,
2524        allowable_names: AllowableIds,
2525        target: Option<&'a fdecl::Ref>,
2526        target_name: Option<&'a String>,
2527        offer_type: OfferType,
2528    ) {
2529        match target {
2530            Some(fdecl::Ref::Child(c)) => {
2531                self.validate_target_child(decl, allowable_names, c, target_name, offer_type);
2532            }
2533            Some(fdecl::Ref::Collection(c)) => {
2534                self.validate_target_collection(decl, allowable_names, c, target_name);
2535            }
2536            Some(fdecl::Ref::Capability(c)) => {
2537                // Only offers to dictionary capabilities are valid.
2538                if let Some(d) = self.all_dictionaries.get(&c.name.as_str()) {
2539                    if d.source_path.is_some() {
2540                        // If `source_path` is present that means this is an offer into a
2541                        // dynamic dictionary, which is not allowed.
2542                        self.errors.push(Error::invalid_field(decl, "target"));
2543                    }
2544                } else {
2545                    self.errors.push(Error::invalid_field(decl, "target"));
2546                }
2547            }
2548            Some(_) => {
2549                self.errors.push(Error::invalid_field(decl, "target"));
2550            }
2551            None => {
2552                self.errors.push(Error::missing_field(decl, "target"));
2553            }
2554        }
2555    }
2556
2557    fn validate_target_child(
2558        &mut self,
2559        decl: DeclType,
2560        allowable_names: AllowableIds,
2561        child: &'a fdecl::ChildRef,
2562        target_name: Option<&'a String>,
2563        offer_type: OfferType,
2564    ) {
2565        if !self.validate_child_ref(decl, "target", child, offer_type) {
2566            return;
2567        }
2568        if let Some(target_name) = target_name {
2569            let names_for_target = self
2570                .target_ids
2571                .entry(TargetId::Component(
2572                    &child.name,
2573                    child.collection.as_ref().map(|s| s.as_str()),
2574                ))
2575                .or_insert(HashMap::new());
2576            if let Some(prev_state) = names_for_target.insert(target_name, allowable_names) {
2577                if prev_state == AllowableIds::One || prev_state != allowable_names {
2578                    self.errors.push(Error::duplicate_field(
2579                        decl,
2580                        "target_name",
2581                        target_name as &str,
2582                    ));
2583                }
2584            }
2585            if let Some(collection) = child.collection.as_ref() {
2586                if let Some(names_for_target) =
2587                    self.target_ids.get(&TargetId::Collection(&collection))
2588                {
2589                    if names_for_target.contains_key(&target_name.as_str()) {
2590                        // This dynamic offer conflicts with a static offer to the same collection.
2591                        self.errors.push(Error::duplicate_field(
2592                            decl,
2593                            "target_name",
2594                            target_name as &str,
2595                        ));
2596                    }
2597                }
2598            }
2599        }
2600    }
2601
2602    fn validate_target_collection(
2603        &mut self,
2604        decl: DeclType,
2605        allowable_names: AllowableIds,
2606        collection: &'a fdecl::CollectionRef,
2607        target_name: Option<&'a String>,
2608    ) {
2609        if !self.validate_collection_ref(decl, "target", &collection) {
2610            return;
2611        }
2612        if let Some(target_name) = target_name {
2613            let names_for_target = self
2614                .target_ids
2615                .entry(TargetId::Collection(&collection.name))
2616                .or_insert(HashMap::new());
2617            if let Some(prev_state) = names_for_target.insert(target_name, allowable_names) {
2618                if prev_state == AllowableIds::One || prev_state != allowable_names {
2619                    self.errors.push(Error::duplicate_field(
2620                        decl,
2621                        "target_name",
2622                        target_name as &str,
2623                    ));
2624                }
2625            }
2626        }
2627    }
2628
2629    fn validate_route_from_self(
2630        &mut self,
2631        decl: DeclType,
2632        source: Option<&fdecl::Ref>,
2633        source_name: Option<&String>,
2634        source_dictionary: Option<&String>,
2635        capability_checker: impl Fn(&Self) -> &dyn Container,
2636    ) {
2637        let (Some(fdecl::Ref::Self_(_)), Some(name)) = (source, source_name) else {
2638            return;
2639        };
2640        match source_dictionary {
2641            Some(source_dictionary) => {
2642                if let Ok(path) = cm_types::RelativePath::new(source_dictionary) {
2643                    if let Some(first_segment) = path.iter_segments().next().map(|s| s.as_str()) {
2644                        if !self.all_dictionaries.contains_key(first_segment) {
2645                            self.errors.push(Error::invalid_capability(
2646                                decl,
2647                                "source",
2648                                first_segment,
2649                            ));
2650                        }
2651                    }
2652                }
2653            }
2654            None => {
2655                if !(capability_checker)(self).contains(name) {
2656                    self.errors.push(Error::invalid_capability(decl, "source", name));
2657                }
2658            }
2659        }
2660    }
2661
2662    // The following functions can be used to convert a type-specific collection of capabilities
2663    // into [Container].
2664    fn service_checker(&self) -> &dyn Container {
2665        &self.all_services
2666    }
2667    fn protocol_checker(&self) -> &dyn Container {
2668        &self.all_protocols
2669    }
2670    fn directory_checker(&self) -> &dyn Container {
2671        &self.all_directories
2672    }
2673    fn runner_checker(&self) -> &dyn Container {
2674        &self.all_runners
2675    }
2676    fn resolver_checker(&self) -> &dyn Container {
2677        &self.all_resolvers
2678    }
2679
2680    fn dictionary_checker(&self) -> &dyn Container {
2681        &self.all_dictionaries
2682    }
2683
2684    #[cfg(fuchsia_api_level_at_least = "HEAD")]
2685    fn config_checker(&self) -> &dyn Container {
2686        &self.all_configs
2687    }
2688    fn storage_checker(&self) -> &dyn Container {
2689        &self.all_storages
2690    }
2691    fn event_stream_checker(&self) -> &dyn Container {
2692        // Components can't define their own event streams. If someone tries to route an event
2693        // stream from Self it should generate some other error. So just return `true` to bypass
2694        // the logic.
2695        struct AlwaysTrueContainer {}
2696        impl Container for AlwaysTrueContainer {
2697            fn contains(&self, _key: &str) -> bool {
2698                true
2699            }
2700        }
2701        static CONTAINER: AlwaysTrueContainer = AlwaysTrueContainer {};
2702        &CONTAINER
2703    }
2704}
2705
2706#[cfg(test)]
2707mod tests {
2708    use super::*;
2709    use cm_types::MAX_LONG_NAME_LENGTH;
2710    use fidl_fuchsia_component_decl_common as fdecl;
2711    use fidl_fuchsia_data_common as fdata;
2712    use fidl_fuchsia_io_common as fio;
2713    use test_case::test_case;
2714
2715    macro_rules! test_validate {
2716        (
2717            $(
2718                $test_name:ident => {
2719                    input = $input:expr,
2720                    result = $result:expr,
2721                },
2722            )+
2723        ) => {
2724            $(
2725                #[test]
2726                fn $test_name() {
2727                    validate_test($input, $result);
2728                }
2729            )+
2730        }
2731    }
2732
2733    macro_rules! test_validate_any_result {
2734        (
2735            $(
2736                $test_name:ident => {
2737                    input = $input:expr,
2738                    results = $results:expr,
2739                },
2740            )+
2741        ) => {
2742            $(
2743                #[test]
2744                fn $test_name() {
2745                    validate_test_any_result($input, $results);
2746                }
2747            )+
2748        }
2749    }
2750
2751    macro_rules! test_validate_values_data {
2752        (
2753            $(
2754                $test_name:ident => {
2755                    input = $input:expr,
2756                    result = $result:expr,
2757                },
2758            )+
2759        ) => {
2760            $(
2761                #[test]
2762                fn $test_name() {
2763                    validate_values_data_test($input, $result);
2764                }
2765            )+
2766        }
2767    }
2768
2769    macro_rules! test_validate_capabilities {
2770        (
2771            $(
2772                $test_name:ident => {
2773                    input = $input:expr,
2774                    as_builtin = $as_builtin:expr,
2775                    result = $result:expr,
2776                },
2777            )+
2778        ) => {
2779            $(
2780                #[test]
2781                fn $test_name() {
2782                    validate_capabilities_test($input, $as_builtin, $result);
2783                }
2784            )+
2785        }
2786    }
2787
2788    macro_rules! test_dependency {
2789        (
2790            $(
2791                ($test_name:ident) => {
2792                    ty = $ty:expr,
2793                    offer_decl = $offer_decl:expr,
2794                },
2795            )+
2796        ) => {
2797            $(
2798                #[test]
2799                fn $test_name() {
2800                    let mut decl = new_component_decl();
2801                    let dependencies = vec![
2802                        ("a", "b"),
2803                        ("b", "a"),
2804                    ];
2805                    let offers = dependencies.into_iter().map(|(from,to)| {
2806                        let mut offer_decl = $offer_decl;
2807                        offer_decl.source = Some(fdecl::Ref::Child(
2808                           fdecl::ChildRef { name: from.to_string(), collection: None },
2809                        ));
2810                        offer_decl.target = Some(fdecl::Ref::Child(
2811                           fdecl::ChildRef { name: to.to_string(), collection: None },
2812                        ));
2813                        $ty(offer_decl)
2814                    }).collect();
2815                    let children = ["a", "b"].iter().map(|name| {
2816                        fdecl::Child {
2817                            name: Some(name.to_string()),
2818                            url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
2819                            startup: Some(fdecl::StartupMode::Lazy),
2820                            on_terminate: None,
2821                            environment: None,
2822                            ..Default::default()
2823                        }
2824                    }).collect();
2825                    decl.offers = Some(offers);
2826                    decl.children = Some(children);
2827                    let result = Err(ErrorList::new(vec![
2828                        Error::dependency_cycle("{{child a -> child b -> child a}}")
2829                    ]));
2830                    validate_test(decl, result);
2831                }
2832            )+
2833        }
2834    }
2835
2836    macro_rules! test_weak_dependency {
2837        (
2838            $(
2839                ($test_name:ident) => {
2840                    ty = $ty:expr,
2841                    offer_decl = $offer_decl:expr,
2842                },
2843            )+
2844        ) => {
2845            $(
2846                #[test_case(fdecl::DependencyType::Weak)]
2847                fn $test_name(weak_dep: fdecl::DependencyType) {
2848                    let mut decl = new_component_decl();
2849                    let offers = vec![
2850                        {
2851                            let mut offer_decl = $offer_decl;
2852                            offer_decl.source = Some(fdecl::Ref::Child(
2853                               fdecl::ChildRef { name: "a".to_string(), collection: None },
2854                            ));
2855                            offer_decl.target = Some(fdecl::Ref::Child(
2856                               fdecl::ChildRef { name: "b".to_string(), collection: None },
2857                            ));
2858                            offer_decl.dependency_type = Some(fdecl::DependencyType::Strong);
2859                            $ty(offer_decl)
2860                        },
2861                        {
2862                            let mut offer_decl = $offer_decl;
2863                            offer_decl.source = Some(fdecl::Ref::Child(
2864                               fdecl::ChildRef { name: "b".to_string(), collection: None },
2865                            ));
2866                            offer_decl.target = Some(fdecl::Ref::Child(
2867                               fdecl::ChildRef { name: "a".to_string(), collection: None },
2868                            ));
2869                            offer_decl.dependency_type = Some(weak_dep);
2870                            $ty(offer_decl)
2871                        },
2872                    ];
2873                    let children = ["a", "b"].iter().map(|name| {
2874                        fdecl::Child {
2875                            name: Some(name.to_string()),
2876                            url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
2877                            startup: Some(fdecl::StartupMode::Lazy),
2878                            on_terminate: None,
2879                            environment: None,
2880                            ..Default::default()
2881                        }
2882                    }).collect();
2883                    decl.offers = Some(offers);
2884                    decl.children = Some(children);
2885                    let result = Ok(());
2886                    validate_test(decl, result);
2887                }
2888            )+
2889        }
2890    }
2891
2892    #[track_caller]
2893    fn validate_test(input: fdecl::Component, expected_res: Result<(), ErrorList>) {
2894        let res = validate(&input, &mut DirectedGraph::new());
2895        assert_eq!(res, expected_res);
2896    }
2897
2898    #[track_caller]
2899    fn validate_test_any_result(input: fdecl::Component, expected_res: Vec<Result<(), ErrorList>>) {
2900        let res = format!("{:?}", validate(&input, &mut DirectedGraph::new()));
2901        let expected_res_debug = format!("{:?}", expected_res);
2902
2903        let matched_exp =
2904            expected_res.into_iter().find(|expected| res == format!("{:?}", expected));
2905
2906        assert!(
2907            matched_exp.is_some(),
2908            "assertion failed: Expected one of:\n{:?}\nActual:\n{:?}",
2909            expected_res_debug,
2910            res
2911        );
2912    }
2913
2914    #[track_caller]
2915    fn validate_values_data_test(
2916        input: fdecl::ConfigValuesData,
2917        expected_res: Result<(), ErrorList>,
2918    ) {
2919        let res = validate_values_data(&input);
2920        assert_eq!(res, expected_res);
2921    }
2922
2923    #[track_caller]
2924    fn validate_capabilities_test(
2925        input: Vec<fdecl::Capability>,
2926        as_builtin: bool,
2927        expected_res: Result<(), ErrorList>,
2928    ) {
2929        let res = validate_capabilities(&input, as_builtin);
2930        assert_eq!(res, expected_res);
2931    }
2932
2933    fn new_component_decl() -> fdecl::Component {
2934        fdecl::Component {
2935            program: None,
2936            uses: None,
2937            exposes: None,
2938            offers: None,
2939            facets: None,
2940            capabilities: None,
2941            children: None,
2942            collections: None,
2943            environments: None,
2944            ..Default::default()
2945        }
2946    }
2947
2948    test_validate_any_result! {
2949        test_validate_use_disallows_nested_dirs => {
2950            input = {
2951                let mut decl = new_component_decl();
2952                decl.uses = Some(vec![
2953                    fdecl::Use::Directory(fdecl::UseDirectory {
2954                        dependency_type: Some(fdecl::DependencyType::Strong),
2955                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2956                        source_name: Some("abc".to_string()),
2957                        target_path: Some("/foo/bar".to_string()),
2958                        rights: Some(fio::Operations::CONNECT),
2959                        subdir: None,
2960                        ..Default::default()
2961                    }),
2962                    fdecl::Use::Directory(fdecl::UseDirectory {
2963                        dependency_type: Some(fdecl::DependencyType::Strong),
2964                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2965                        source_name: Some("abc".to_string()),
2966                        target_path: Some("/foo/bar/baz".to_string()),
2967                        rights: Some(fio::Operations::CONNECT),
2968                        subdir: None,
2969                        ..Default::default()
2970                    }),
2971                ]);
2972                decl
2973            },
2974            results = vec![
2975                Err(ErrorList::new(vec![
2976                    Error::invalid_path_overlap(
2977                        DeclType::UseDirectory, "/foo/bar/baz", DeclType::UseDirectory, "/foo/bar"),
2978                ])),
2979                Err(ErrorList::new(vec![
2980                    Error::invalid_path_overlap(
2981                        DeclType::UseDirectory, "/foo/bar", DeclType::UseDirectory, "/foo/bar/baz"),
2982                ])),
2983            ],
2984        },
2985        test_validate_use_disallows_nested_dirs_storage => {
2986            input = {
2987                let mut decl = new_component_decl();
2988                decl.uses = Some(vec![
2989                    fdecl::Use::Storage(fdecl::UseStorage {
2990                        source_name: Some("abc".to_string()),
2991                        target_path: Some("/foo/bar".to_string()),
2992                        ..Default::default()
2993                    }),
2994                    fdecl::Use::Storage(fdecl::UseStorage {
2995                        source_name: Some("abc".to_string()),
2996                        target_path: Some("/foo/bar/baz".to_string()),
2997                        ..Default::default()
2998                    }),
2999                ]);
3000                decl
3001            },
3002            results = vec![
3003                Err(ErrorList::new(vec![
3004                    Error::invalid_path_overlap(
3005                        DeclType::UseStorage, "/foo/bar/baz", DeclType::UseStorage, "/foo/bar"),
3006                ])),
3007                Err(ErrorList::new(vec![
3008                    Error::invalid_path_overlap(
3009                        DeclType::UseStorage, "/foo/bar", DeclType::UseStorage, "/foo/bar/baz"),
3010                ])),
3011            ],
3012        },
3013        test_validate_use_disallows_nested_dirs_directory_and_storage => {
3014            input = {
3015                let mut decl = new_component_decl();
3016                decl.uses = Some(vec![
3017                    fdecl::Use::Directory(fdecl::UseDirectory {
3018                        dependency_type: Some(fdecl::DependencyType::Strong),
3019                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3020                        source_name: Some("abc".to_string()),
3021                        target_path: Some("/foo/bar".to_string()),
3022                        rights: Some(fio::Operations::CONNECT),
3023                        subdir: None,
3024                        ..Default::default()
3025                    }),
3026                    fdecl::Use::Storage(fdecl::UseStorage {
3027                        source_name: Some("abc".to_string()),
3028                        target_path: Some("/foo/bar/baz".to_string()),
3029                        ..Default::default()
3030                    }),
3031                ]);
3032                decl
3033            },
3034            results = vec![
3035                Err(ErrorList::new(vec![
3036                    Error::invalid_path_overlap(
3037                        DeclType::UseStorage, "/foo/bar/baz", DeclType::UseDirectory, "/foo/bar"),
3038                ])),
3039                Err(ErrorList::new(vec![
3040                    Error::invalid_path_overlap(
3041                        DeclType::UseDirectory, "/foo/bar", DeclType::UseStorage, "/foo/bar/baz"),
3042                ])),
3043            ],
3044        },
3045        test_validate_use_disallows_common_prefixes_protocol => {
3046            input = {
3047                let mut decl = new_component_decl();
3048                decl.uses = Some(vec![
3049                    fdecl::Use::Directory(fdecl::UseDirectory {
3050                        dependency_type: Some(fdecl::DependencyType::Strong),
3051                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3052                        source_name: Some("abc".to_string()),
3053                        target_path: Some("/foo/bar".to_string()),
3054                        rights: Some(fio::Operations::CONNECT),
3055                        subdir: None,
3056                        ..Default::default()
3057                    }),
3058                    fdecl::Use::Protocol(fdecl::UseProtocol {
3059                        dependency_type: Some(fdecl::DependencyType::Strong),
3060                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3061                        source_name: Some("crow".to_string()),
3062                        target_path: Some("/foo/bar/fuchsia.2".to_string()),
3063                        ..Default::default()
3064                    }),
3065                ]);
3066                decl
3067            },
3068            results = vec![
3069                Err(ErrorList::new(vec![
3070                    Error::invalid_path_overlap(
3071                        DeclType::UseProtocol, "/foo/bar/fuchsia.2", DeclType::UseDirectory, "/foo/bar"),
3072                ])),
3073                Err(ErrorList::new(vec![
3074                    Error::invalid_path_overlap(
3075                        DeclType::UseDirectory, "/foo/bar", DeclType::UseProtocol, "/foo/bar/fuchsia.2"),
3076                ])),
3077            ],
3078        },
3079        test_validate_use_disallows_common_prefixes_service => {
3080            input = {
3081                let mut decl = new_component_decl();
3082                decl.uses = Some(vec![
3083                    fdecl::Use::Directory(fdecl::UseDirectory {
3084                        dependency_type: Some(fdecl::DependencyType::Strong),
3085                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3086                        source_name: Some("abc".to_string()),
3087                        target_path: Some("/foo/bar".to_string()),
3088                        rights: Some(fio::Operations::CONNECT),
3089                        subdir: None,
3090                        ..Default::default()
3091                    }),
3092                    fdecl::Use::Service(fdecl::UseService {
3093                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3094                        source_name: Some("space".to_string()),
3095                        target_path: Some("/foo/bar/baz/fuchsia.logger.Log".to_string()),
3096                        dependency_type: Some(fdecl::DependencyType::Strong),
3097                        ..Default::default()
3098                    }),
3099                ]);
3100                decl
3101            },
3102            results = vec![
3103                Err(ErrorList::new(vec![
3104                    Error::invalid_path_overlap(
3105                        DeclType::UseService, "/foo/bar/baz/fuchsia.logger.Log", DeclType::UseDirectory, "/foo/bar"),
3106                ])),
3107                Err(ErrorList::new(vec![
3108                    Error::invalid_path_overlap(
3109                        DeclType::UseDirectory, "/foo/bar", DeclType::UseService, "/foo/bar/baz/fuchsia.logger.Log"),
3110                ])),
3111            ],
3112        },
3113        test_validate_use_disallows_pkg => {
3114            input = {
3115                let mut decl = new_component_decl();
3116                decl.uses = Some(vec![
3117                    fdecl::Use::Directory(fdecl::UseDirectory {
3118                        dependency_type: Some(fdecl::DependencyType::Strong),
3119                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3120                        source_name: Some("abc".to_string()),
3121                        target_path: Some("/pkg".to_string()),
3122                        rights: Some(fio::Operations::CONNECT),
3123                        subdir: None,
3124                        ..Default::default()
3125                    }),
3126                ]);
3127                decl
3128            },
3129            results = vec![
3130                Err(ErrorList::new(vec![
3131                    Error::pkg_path_overlap(DeclType::UseDirectory, "/pkg"),
3132                ])),
3133            ],
3134        },
3135        test_validate_use_disallows_pkg_overlap => {
3136            input = {
3137                let mut decl = new_component_decl();
3138                decl.uses = Some(vec![
3139                    fdecl::Use::Directory(fdecl::UseDirectory {
3140                        dependency_type: Some(fdecl::DependencyType::Strong),
3141                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3142                        source_name: Some("abc".to_string()),
3143                        target_path: Some("/pkg/foo".to_string()),
3144                        rights: Some(fio::Operations::CONNECT),
3145                        subdir: None,
3146                        ..Default::default()
3147                    }),
3148                ]);
3149                decl
3150            },
3151            results = vec![
3152                Err(ErrorList::new(vec![
3153                    Error::pkg_path_overlap(DeclType::UseDirectory, "/pkg/foo"),
3154                ])),
3155            ],
3156        },
3157        test_validate_use_optional_config_correct => {
3158            input = {
3159                let mut decl = new_component_decl();
3160                decl.uses = Some(vec![
3161                    fdecl::Use::Config(fdecl::UseConfiguration {
3162                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3163                        source_name: Some("abc".to_string()),
3164                        target_name: Some("foo".to_string()),
3165                        availability: Some(fdecl::Availability::Optional),
3166                        type_: Some(fdecl::ConfigType {
3167                            layout: fdecl::ConfigTypeLayout::Bool,
3168                            parameters: Some(Vec::new()),
3169                            constraints: Vec::new(),
3170                        }),
3171                        ..Default::default()
3172                    }),
3173                ]);
3174                decl.config = Some(fdecl::ConfigSchema {
3175                    fields: Some(vec![fdecl::ConfigField {
3176                        key: Some("foo".into()),
3177                        type_: Some(fdecl::ConfigType {
3178                            layout: fdecl::ConfigTypeLayout::Bool,
3179                            parameters: Some(Vec::new()),
3180                            constraints: Vec::new(),
3181                        }),
3182                        mutability: None,
3183                        ..Default::default()}]),
3184                    checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3185                    value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3186                    ..Default::default()
3187                     });
3188                decl
3189            },
3190            results = vec![Ok(())],
3191        },
3192        test_validate_use_optional_config_no_config_schema => {
3193            input = {
3194                let mut decl = new_component_decl();
3195                decl.uses = Some(vec![
3196                    fdecl::Use::Config(fdecl::UseConfiguration {
3197                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3198                        source_name: Some("abc".to_string()),
3199                        target_name: Some("foo".to_string()),
3200                        availability: Some(fdecl::Availability::Optional),
3201                        type_: Some(fdecl::ConfigType {
3202                            layout: fdecl::ConfigTypeLayout::Bool,
3203                            parameters: None,
3204                            constraints: Vec::new(),
3205                        }),
3206                        ..Default::default()
3207                    }),
3208                ]);
3209                decl
3210            },
3211            results = vec![
3212                Err(ErrorList::new(vec![
3213                    Error::missing_field(DeclType::ConfigField, "config"),
3214                ])),
3215            ],
3216        },
3217        test_validate_use_optional_config_no_config_field => {
3218            input = {
3219                let mut decl = new_component_decl();
3220                decl.uses = Some(vec![
3221                    fdecl::Use::Config(fdecl::UseConfiguration {
3222                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3223                        source_name: Some("abc".to_string()),
3224                        target_name: Some("foo".to_string()),
3225                        availability: Some(fdecl::Availability::Optional),
3226                        type_: Some(fdecl::ConfigType {
3227                            layout: fdecl::ConfigTypeLayout::Bool,
3228                            parameters: None,
3229                            constraints: Vec::new(),
3230                        }),
3231                        ..Default::default()
3232                    }),
3233                ]);
3234                decl.config = Some(fdecl::ConfigSchema {
3235                    fields: Some(vec![]),
3236                    checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3237                    value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3238                    ..Default::default()
3239                     });
3240                decl
3241            },
3242            results = vec![
3243                Err(ErrorList::new(vec![
3244                    Error::missing_field(DeclType::ConfigField, "foo"),
3245                ])),
3246            ],
3247        },
3248        test_validate_use_optional_config_bad_type => {
3249            input = {
3250                let mut decl = new_component_decl();
3251                decl.uses = Some(vec![
3252                    fdecl::Use::Config(fdecl::UseConfiguration {
3253                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3254                        source_name: Some("abc".to_string()),
3255                        target_name: Some("foo".to_string()),
3256                        availability: Some(fdecl::Availability::Optional),
3257                        type_: Some(fdecl::ConfigType {
3258                            layout: fdecl::ConfigTypeLayout::Bool,
3259                            parameters: None,
3260                            constraints: Vec::new(),
3261                        }),
3262                        ..Default::default()
3263                    }),
3264                ]);
3265                decl.config = Some(fdecl::ConfigSchema {
3266                    fields: Some(vec![fdecl::ConfigField {
3267                        key: Some("foo".into()),
3268                        type_: Some(fdecl::ConfigType {
3269                            layout: fdecl::ConfigTypeLayout::Int16,
3270                            parameters: Some(Vec::new()),
3271                            constraints: Vec::new(),
3272                        }),
3273                        mutability: None,
3274                        ..Default::default()}]),
3275                    checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3276                    value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3277                    ..Default::default()
3278                     });
3279                decl
3280            },
3281            results = vec![
3282                Err(ErrorList::new(vec![
3283                    Error::invalid_field(DeclType::ConfigField, "foo"),
3284                ])),
3285            ],
3286        },
3287    }
3288
3289    #[cfg(fuchsia_api_level_at_least = "29")]
3290    test_validate_any_result! {
3291        test_validate_use_dictionary => {
3292            input = {
3293                let mut decl = new_component_decl();
3294                decl.uses = Some(vec![
3295                    fdecl::Use::Dictionary(fdecl::UseDictionary {
3296                        dependency_type: Some(fdecl::DependencyType::Strong),
3297                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3298                        source_name: Some("toolbox".to_string()),
3299                        target_path: Some("/svc".to_string()),
3300                        availability: Some(fdecl::Availability::Required),
3301                        ..Default::default()
3302                    }),
3303                ]);
3304                decl
3305            },
3306            results = vec![
3307                Ok(()),
3308            ],
3309        },
3310        test_validate_use_dictionary_invalid_name => {
3311            input = {
3312                let mut decl = new_component_decl();
3313                decl.uses = Some(vec![
3314                    fdecl::Use::Dictionary(fdecl::UseDictionary {
3315                        dependency_type: Some(fdecl::DependencyType::Strong),
3316                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3317                        source_name: Some("toolbox@".to_string()),
3318                        target_path: Some("/svc".to_string()),
3319                        availability: Some(fdecl::Availability::Required),
3320                        ..Default::default()
3321                    }),
3322                ]);
3323                decl
3324            },
3325            results = vec![
3326                Err(ErrorList::new(vec![Error::invalid_field(DeclType::UseDictionary, "source_name")])),
3327            ],
3328        },
3329        test_validate_use_dictionary_invalid_path => {
3330            input = {
3331                let mut decl = new_component_decl();
3332                decl.uses = Some(vec![
3333                    fdecl::Use::Dictionary(fdecl::UseDictionary {
3334                        dependency_type: Some(fdecl::DependencyType::Strong),
3335                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3336                        source_name: Some("toolbox".to_string()),
3337                        target_path: Some("/svc@".to_string()),
3338                        availability: Some(fdecl::Availability::Required),
3339                        ..Default::default()
3340                    }),
3341                ]);
3342                decl
3343            },
3344            results = vec![
3345                Err(ErrorList::new(vec![Error::field_invalid_segment(DeclType::UseDictionary, "target_path")])),
3346            ],
3347        },
3348        test_validate_use_dictionary_disallows_pkg_overlap => {
3349            input = {
3350                let mut decl = new_component_decl();
3351                decl.uses = Some(vec![
3352                    fdecl::Use::Dictionary(fdecl::UseDictionary {
3353                        dependency_type: Some(fdecl::DependencyType::Strong),
3354                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3355                        source_name: Some("toolbox".to_string()),
3356                        target_path: Some("/pkg/toolbox".to_string()),
3357                        availability: Some(fdecl::Availability::Required),
3358                        ..Default::default()
3359                    }),
3360                ]);
3361                decl
3362            },
3363            results = vec![
3364                Err(ErrorList::new(vec![
3365                    Error::pkg_path_overlap(DeclType::UseDictionary, "/pkg/toolbox"),
3366                ])),
3367            ],
3368        },
3369        test_validate_use_dictionary_path_overlap_directory => {
3370            input = {
3371                let mut decl = new_component_decl();
3372                decl.uses = Some(vec![
3373                    fdecl::Use::Dictionary(fdecl::UseDictionary {
3374                        dependency_type: Some(fdecl::DependencyType::Strong),
3375                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3376                        source_name: Some("toolbox".to_string()),
3377                        target_path: Some("/foo/bar".to_string()),
3378                        availability: Some(fdecl::Availability::Required),
3379                        ..Default::default()
3380                    }),
3381                    fdecl::Use::Directory(fdecl::UseDirectory {
3382                        dependency_type: Some(fdecl::DependencyType::Strong),
3383                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3384                        source_name: Some("data".to_string()),
3385                        target_path: Some("/foo".to_string()),
3386                        rights: Some(fio::Operations::CONNECT),
3387                        subdir: None,
3388                        ..Default::default()
3389                    }),
3390                ]);
3391                decl
3392            },
3393            results = vec![
3394                Err(ErrorList::new(vec![
3395                    Error::invalid_path_overlap(
3396                        DeclType::UseDirectory,
3397                        "/foo",
3398                        DeclType::UseDictionary,
3399                        "/foo/bar",
3400                    ),
3401                ])),
3402                Err(ErrorList::new(vec![
3403                    Error::invalid_path_overlap(
3404                        DeclType::UseDictionary,
3405                        "/foo/bar",
3406                        DeclType::UseDirectory,
3407                        "/foo",
3408                    ),
3409                ])),
3410            ],
3411        },
3412        test_validate_use_dictionary_path_overlap_protocol => {
3413            input = {
3414                let mut decl = new_component_decl();
3415                decl.uses = Some(vec![
3416                    fdecl::Use::Dictionary(fdecl::UseDictionary {
3417                        dependency_type: Some(fdecl::DependencyType::Strong),
3418                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3419                        source_name: Some("toolbox".to_string()),
3420                        target_path: Some("/svc/toolbox".to_string()),
3421                        availability: Some(fdecl::Availability::Required),
3422                        ..Default::default()
3423                    }),
3424                    fdecl::Use::Protocol(fdecl::UseProtocol {
3425                        dependency_type: Some(fdecl::DependencyType::Strong),
3426                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3427                        source_name: Some("fuchsia.examples.Echo".to_string()),
3428                        target_path: Some("/svc/fuchsia.examples.Echo".to_string()),
3429                        ..Default::default()
3430                    }),
3431                ]);
3432                decl
3433            },
3434            results = vec![
3435                Ok(()),
3436            ],
3437        },
3438        test_validate_use_dictionary_path_overlap_protocol_2 => {
3439            input = {
3440                let mut decl = new_component_decl();
3441                decl.uses = Some(vec![
3442                    fdecl::Use::Dictionary(fdecl::UseDictionary {
3443                        dependency_type: Some(fdecl::DependencyType::Strong),
3444                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3445                        source_name: Some("toolbox".to_string()),
3446                        target_path: Some("/svc".to_string()),
3447                        availability: Some(fdecl::Availability::Required),
3448                        ..Default::default()
3449                    }),
3450                    fdecl::Use::Protocol(fdecl::UseProtocol {
3451                        dependency_type: Some(fdecl::DependencyType::Strong),
3452                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3453                        source_name: Some("fuchsia.examples.Echo".to_string()),
3454                        target_path: Some("/svc/fuchsia.examples.Echo".to_string()),
3455                        ..Default::default()
3456                    }),
3457                ]);
3458                decl
3459            },
3460            results = vec![
3461                Ok(()),
3462            ],
3463        },
3464        test_validate_use_dictionary_path_identical_dictionary => {
3465            input = {
3466                let mut decl = new_component_decl();
3467                decl.uses = Some(vec![
3468                    fdecl::Use::Dictionary(fdecl::UseDictionary {
3469                        dependency_type: Some(fdecl::DependencyType::Strong),
3470                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3471                        source_name: Some("toolbox-1".to_string()),
3472                        target_path: Some("/svc".to_string()),
3473                        availability: Some(fdecl::Availability::Required),
3474                        ..Default::default()
3475                    }),
3476                    fdecl::Use::Dictionary(fdecl::UseDictionary {
3477                        dependency_type: Some(fdecl::DependencyType::Strong),
3478                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3479                        source_name: Some("toolbox-2".to_string()),
3480                        target_path: Some("/svc".to_string()),
3481                        availability: Some(fdecl::Availability::Required),
3482                        ..Default::default()
3483                    }),
3484                    fdecl::Use::Protocol(fdecl::UseProtocol {
3485                        dependency_type: Some(fdecl::DependencyType::Strong),
3486                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3487                        source_name: Some("fuchsia.examples.Echo".to_string()),
3488                        target_path: Some("/svc/fuchsia.examples.Echo".to_string()),
3489                        ..Default::default()
3490                    }),
3491                ]);
3492                decl
3493            },
3494            results = vec![
3495                Ok(()),
3496            ],
3497        },
3498        test_validate_use_two_dictionaires_and_a_protocol_at_the_same_path => {
3499            input = {
3500                fdecl::Component {
3501                    program: Some(fdecl::Program {
3502                        runner: Some("realm_builder".to_string()),
3503                        info: Some(fdata::Dictionary {
3504                            entries: Some(vec![ ]),
3505                            ..Default::default()
3506                        }),
3507                        ..Default::default()
3508                    }),
3509                    uses: Some(vec![
3510                        fdecl::Use::Dictionary(fdecl::UseDictionary {
3511                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3512                            source_name: Some("my_dictionary".to_string()),
3513                            target_path: Some("/svc".to_string()),
3514                            dependency_type: Some(fdecl::DependencyType::Strong),
3515                            availability: Some(fdecl::Availability::Required),
3516                            source_dictionary: None,
3517                            ..Default::default()
3518                        }),
3519                        fdecl::Use::Dictionary(fdecl::UseDictionary {
3520                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
3521                            source_name: Some("my_dictionary2".to_string()),
3522                            target_path: Some("/svc".to_string()),
3523                            dependency_type: Some(fdecl::DependencyType::Strong),
3524                            availability: Some(fdecl::Availability::Required),
3525                            source_dictionary: None,
3526                            ..Default::default()
3527                        }),
3528                        fdecl::Use::Protocol(fdecl::UseProtocol {
3529                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
3530                            source_name: Some("fuchsia.echo2".to_string()),
3531                            target_path: Some("/svc/fuchsia.echo2".to_string()),
3532                            dependency_type: Some(fdecl::DependencyType::Strong),
3533                            availability: Some(fdecl::Availability::Required),
3534                            source_dictionary: None,
3535                            numbered_handle: None,
3536                            ..Default::default()
3537                        }),
3538                    ]),
3539                    ..Default::default()
3540                }
3541            },
3542            results = vec![
3543                Ok(()),
3544            ],
3545        },
3546    }
3547
3548    test_validate_values_data! {
3549        test_values_data_ok => {
3550            input = fdecl::ConfigValuesData {
3551                values: Some(vec![
3552                    fdecl::ConfigValueSpec {
3553                        value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(true))),
3554                        ..Default::default()
3555                    }
3556                ]),
3557                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3558                ..Default::default()
3559            },
3560            result = Ok(()),
3561        },
3562        test_values_data_no_checksum => {
3563            input = fdecl::ConfigValuesData {
3564                values: Some(vec![]),
3565                checksum: None,
3566                ..Default::default()
3567            },
3568            result = Err(ErrorList::new(vec![
3569                Error::missing_field(DeclType::ConfigValuesData, "checksum")
3570            ])),
3571        },
3572        test_values_data_unknown_checksum => {
3573            input = fdecl::ConfigValuesData {
3574                values: Some(vec![]),
3575                checksum: Some(fdecl::ConfigChecksum::unknown_variant_for_testing()),
3576                ..Default::default()
3577            },
3578            result = Err(ErrorList::new(vec![
3579                Error::invalid_field(DeclType::ConfigValuesData, "checksum")
3580            ])),
3581        },
3582        test_values_data_no_values => {
3583            input = fdecl::ConfigValuesData {
3584                values: None,
3585                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3586                ..Default::default()
3587            },
3588            result = Err(ErrorList::new(vec![
3589                Error::missing_field(DeclType::ConfigValuesData, "values")
3590            ])),
3591        },
3592        test_values_data_no_inner_value => {
3593            input = fdecl::ConfigValuesData {
3594                values: Some(vec![
3595                    fdecl::ConfigValueSpec::default()
3596                ]),
3597                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3598                ..Default::default()
3599            },
3600            result = Err(ErrorList::new(vec![
3601                Error::missing_field(DeclType::ConfigValueSpec, "value")
3602            ])),
3603        },
3604        test_values_data_unknown_inner_value => {
3605            input = fdecl::ConfigValuesData {
3606                values: Some(vec![
3607                    fdecl::ConfigValueSpec {
3608                        value: Some(fdecl::ConfigValue::unknown_variant_for_testing()),
3609                        ..Default::default()
3610                    }
3611                ]),
3612                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3613                ..Default::default()
3614            },
3615            result = Err(ErrorList::new(vec![
3616                Error::invalid_field(DeclType::ConfigValueSpec, "value")
3617            ])),
3618        },
3619        test_values_data_unknown_single_value => {
3620            input = fdecl::ConfigValuesData {
3621                values: Some(vec![
3622                    fdecl::ConfigValueSpec {
3623                        value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::unknown_variant_for_testing())),
3624                        ..Default::default()
3625                    }
3626                ]),
3627                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3628                ..Default::default()
3629            },
3630            result = Err(ErrorList::new(vec![
3631                Error::invalid_field(DeclType::ConfigValueSpec, "value")
3632            ])),
3633        },
3634        test_values_data_unknown_list_value => {
3635            input = fdecl::ConfigValuesData {
3636                values: Some(vec![
3637                    fdecl::ConfigValueSpec {
3638                        value: Some(fdecl::ConfigValue::Vector(fdecl::ConfigVectorValue::unknown_variant_for_testing())),
3639                        ..Default::default()
3640                    }
3641                ]),
3642                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3643                ..Default::default()
3644            },
3645            result = Err(ErrorList::new(vec![
3646                Error::invalid_field(DeclType::ConfigValueSpec, "value")
3647            ])),
3648        },
3649    }
3650
3651    test_validate! {
3652        // uses
3653        test_validate_uses_empty => {
3654            input = {
3655                let mut decl = new_component_decl();
3656                decl.program = Some(fdecl::Program {
3657                    runner: Some("elf".to_string()),
3658                    info: Some(fdata::Dictionary {
3659                        entries: None,
3660                        ..Default::default()
3661                    }),
3662                    ..Default::default()
3663                });
3664                decl.uses = Some(vec![
3665                    fdecl::Use::Service(fdecl::UseService {
3666                        source: None,
3667                        source_name: None,
3668                        target_path: None,
3669                        dependency_type: None,
3670                        ..Default::default()
3671                    }),
3672                    fdecl::Use::Protocol(fdecl::UseProtocol {
3673                        dependency_type: None,
3674                        source: None,
3675                        source_name: None,
3676                        target_path: None,
3677                        ..Default::default()
3678                    }),
3679                    fdecl::Use::Directory(fdecl::UseDirectory {
3680                        dependency_type: None,
3681                        source: None,
3682                        source_name: None,
3683                        target_path: None,
3684                        rights: None,
3685                        subdir: None,
3686                        ..Default::default()
3687                    }),
3688                    fdecl::Use::Storage(fdecl::UseStorage {
3689                        source_name: None,
3690                        target_path: None,
3691                        ..Default::default()
3692                    }),
3693                    fdecl::Use::EventStream(fdecl::UseEventStream {
3694                        source_name: None,
3695                        source: None,
3696                        target_path: None,
3697                        ..Default::default()
3698                    }),
3699                    fdecl::Use::Runner(fdecl::UseRunner {
3700                        source_name: None,
3701                        source: None,
3702                        ..Default::default()
3703                    }),
3704                ]);
3705                decl
3706            },
3707            result = Err(ErrorList::new(vec![
3708                Error::missing_field(DeclType::UseService, "source"),
3709                Error::missing_field(DeclType::UseService, "source_name"),
3710                Error::missing_field(DeclType::UseService, "target_path"),
3711                Error::missing_field(DeclType::UseService, "dependency_type"),
3712                Error::missing_field(DeclType::UseProtocol, "source"),
3713                Error::missing_field(DeclType::UseProtocol, "source_name"),
3714                Error::missing_field(DeclType::UseProtocol, "target_path"),
3715                Error::missing_field(DeclType::UseProtocol, "dependency_type"),
3716                Error::missing_field(DeclType::UseDirectory, "source"),
3717                Error::missing_field(DeclType::UseDirectory, "source_name"),
3718                Error::missing_field(DeclType::UseDirectory, "target_path"),
3719                Error::missing_field(DeclType::UseDirectory, "dependency_type"),
3720                Error::missing_field(DeclType::UseDirectory, "rights"),
3721                Error::missing_field(DeclType::UseStorage, "source_name"),
3722                Error::missing_field(DeclType::UseStorage, "target_path"),
3723                Error::missing_field(DeclType::UseEventStream, "source"),
3724                Error::missing_field(DeclType::UseEventStream, "source_name"),
3725                Error::missing_field(DeclType::UseEventStream, "target_path"),
3726                Error::missing_field(DeclType::UseRunner, "source"),
3727                Error::missing_field(DeclType::UseRunner, "source_name"),
3728            ])),
3729        },
3730        test_validate_missing_program_info => {
3731            input = {
3732                let mut decl = new_component_decl();
3733                decl.program = Some(fdecl::Program {
3734                    runner: Some("runner".to_string()),
3735                    info: None,
3736                    ..Default::default()
3737                });
3738                decl
3739            },
3740            result = Err(ErrorList::new(vec![
3741                Error::missing_field(DeclType::Program, "info")
3742            ])),
3743        },
3744        test_validate_uses_invalid_identifiers => {
3745            input = {
3746                let mut decl = new_component_decl();
3747                decl.uses = Some(vec![
3748                    fdecl::Use::Service(fdecl::UseService {
3749                        source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
3750                            name: "^bad".to_string(),
3751                        })),
3752                        source_name: Some("foo/".to_string()),
3753                        target_path: Some("a/foo".to_string()),
3754                        dependency_type: Some(fdecl::DependencyType::Strong),
3755                        ..Default::default()
3756                    }),
3757                    fdecl::Use::Protocol(fdecl::UseProtocol {
3758                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3759                            name: "^bad".to_string(),
3760                            collection: None,
3761                        })),
3762                        source_name: Some("foo/".to_string()),
3763                        target_path: Some("b/foo".to_string()),
3764                        dependency_type: Some(fdecl::DependencyType::Strong),
3765                        ..Default::default()
3766                    }),
3767                    fdecl::Use::Directory(fdecl::UseDirectory {
3768                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3769                            name: "^bad".to_string(),
3770                            collection: None,
3771                        })),
3772                        source_name: Some("foo/".to_string()),
3773                        target_path: Some("c".to_string()),
3774                        rights: Some(fio::Operations::CONNECT),
3775                        subdir: Some("/foo".to_string()),
3776                        dependency_type: Some(fdecl::DependencyType::Strong),
3777                        ..Default::default()
3778                    }),
3779                    fdecl::Use::Storage(fdecl::UseStorage {
3780                        source_name: Some("foo/".to_string()),
3781                        target_path: Some("d".to_string()),
3782                        ..Default::default()
3783                    }),
3784                    fdecl::Use::EventStream(fdecl::UseEventStream {
3785                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3786                            name: "^bad".to_string(),
3787                            collection: None,
3788                        })),
3789                        source_name: Some("foo/".to_string()),
3790                        target_path: Some("e".to_string()),
3791                        ..Default::default()
3792                    }),
3793                    fdecl::Use::Runner(fdecl::UseRunner {
3794                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3795                            name: "^bad".to_string(),
3796                            collection: None,
3797                        })),
3798                        source_name: Some("foo/".to_string()),
3799                        ..Default::default()
3800                    }),
3801                ]);
3802                decl
3803            },
3804            result = Err(ErrorList::new(vec![
3805                Error::invalid_field(DeclType::UseService, "source.capability.name"),
3806                Error::invalid_field(DeclType::UseService, "source_name"),
3807                Error::invalid_field(DeclType::UseService, "target_path"),
3808                Error::invalid_field(DeclType::UseProtocol, "source.child.name"),
3809                Error::invalid_field(DeclType::UseProtocol, "source_name"),
3810                Error::invalid_field(DeclType::UseProtocol, "target_path"),
3811                Error::invalid_field(DeclType::UseDirectory, "source.child.name"),
3812                Error::invalid_field(DeclType::UseDirectory, "source_name"),
3813                Error::invalid_field(DeclType::UseDirectory, "target_path"),
3814                Error::invalid_field(DeclType::UseDirectory, "subdir"),
3815                Error::invalid_field(DeclType::UseStorage, "source_name"),
3816                Error::invalid_field(DeclType::UseStorage, "target_path"),
3817                Error::invalid_field(DeclType::UseEventStream, "source.child.name"),
3818                Error::invalid_field(DeclType::UseEventStream, "source_name"),
3819                Error::invalid_field(DeclType::UseEventStream, "target_path"),
3820                Error::invalid_field(DeclType::UseRunner, "source.child.name"),
3821                Error::invalid_field(DeclType::UseRunner, "source_name"),
3822            ])),
3823        },
3824        test_validate_uses_missing_source => {
3825            input = {
3826                fdecl::Component {
3827                    uses: Some(vec![
3828                        fdecl::Use::Protocol(fdecl::UseProtocol {
3829                            dependency_type: Some(fdecl::DependencyType::Strong),
3830                            source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
3831                                name: "this-storage-doesnt-exist".to_string(),
3832                            })),
3833                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3834                            target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3835                            ..Default::default()
3836                        })
3837                    ]),
3838                    ..new_component_decl()
3839                }
3840            },
3841            result = Err(ErrorList::new(vec![
3842                Error::invalid_capability(DeclType::UseProtocol, "source", "this-storage-doesnt-exist"),
3843            ])),
3844        },
3845        test_validate_uses_invalid_child => {
3846            input = {
3847                fdecl::Component {
3848                    uses: Some(vec![
3849                        fdecl::Use::Protocol(fdecl::UseProtocol {
3850                            dependency_type: Some(fdecl::DependencyType::Strong),
3851                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3852                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3853                            target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3854                            ..Default::default()
3855                        }),
3856                        fdecl::Use::Service(fdecl::UseService {
3857                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3858                            source_name: Some("service_name".to_string()),
3859                            target_path: Some("/svc/service_name".to_string()),
3860                            dependency_type: Some(fdecl::DependencyType::Strong),
3861                            ..Default::default()
3862                        }),
3863                        fdecl::Use::Directory(fdecl::UseDirectory {
3864                            dependency_type: Some(fdecl::DependencyType::Strong),
3865                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3866                            source_name: Some("DirectoryName".to_string()),
3867                            target_path: Some("/data/DirectoryName".to_string()),
3868                            rights: Some(fio::Operations::CONNECT),
3869                            subdir: None,
3870                            ..Default::default()
3871                        }),
3872                        fdecl::Use::Runner(fdecl::UseRunner {
3873                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3874                            source_name: Some("RunnerName".to_string()),
3875                            ..Default::default()
3876                        }),
3877                    ]),
3878                    ..new_component_decl()
3879                }
3880            },
3881            result = Err(ErrorList::new(vec![
3882                Error::invalid_child(DeclType::UseProtocol, "source", "no-such-child"),
3883                Error::invalid_child(DeclType::UseService, "source", "no-such-child"),
3884                Error::invalid_child(DeclType::UseDirectory, "source", "no-such-child"),
3885                Error::invalid_child(DeclType::UseRunner, "source", "no-such-child"),
3886            ])),
3887        },
3888        test_validate_uses_invalid_capability_from_self => {
3889            input = {
3890                let mut decl = new_component_decl();
3891                decl.uses = Some(vec![
3892                    fdecl::Use::Service(fdecl::UseService {
3893                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3894                        source_name: Some("fuchsia.some.library.SomeService".into()),
3895                        target_path: Some("/svc/foo".into()),
3896                        dependency_type: Some(fdecl::DependencyType::Strong),
3897                        ..Default::default()
3898                    }),
3899                    fdecl::Use::Protocol(fdecl::UseProtocol {
3900                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3901                        source_name: Some("fuchsia.some.library.SomeProtocol".into()),
3902                        target_path: Some("/svc/bar".into()),
3903                        dependency_type: Some(fdecl::DependencyType::Strong),
3904                        ..Default::default()
3905                    }),
3906                    fdecl::Use::Directory(fdecl::UseDirectory {
3907                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3908                        source_name: Some("dir".into()),
3909                        target_path: Some("/assets".into()),
3910                        dependency_type: Some(fdecl::DependencyType::Strong),
3911                        rights: Some(fio::Operations::CONNECT),
3912                        ..Default::default()
3913                    }),
3914                    fdecl::Use::Runner(fdecl::UseRunner {
3915                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3916                        source_name: Some("source_elf".into()),
3917                        ..Default::default()
3918                    }),
3919                    fdecl::Use::Config(fdecl::UseConfiguration {
3920                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3921                        source_name: Some("source_config".into()),
3922                        target_name: Some("config".into()),
3923                        type_: Some(fdecl::ConfigType {
3924                            layout: fdecl::ConfigTypeLayout::Bool,
3925                            parameters: Some(Vec::new()),
3926                            constraints: Vec::new(),
3927                        }),
3928                        ..Default::default()
3929                    }),
3930                    fdecl::Use::Protocol(fdecl::UseProtocol {
3931                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3932                        source_name: Some("fuchsia.some.library.SomeProtocol".into()),
3933                        source_dictionary: Some("dict/inner".into()),
3934                        target_path: Some("/svc/baz".into()),
3935                        dependency_type: Some(fdecl::DependencyType::Strong),
3936                        ..Default::default()
3937                    }),
3938                ]);
3939                decl
3940            },
3941            result = Err(ErrorList::new(vec![
3942                Error::invalid_capability(
3943                    DeclType::UseService,
3944                    "source",
3945                    "fuchsia.some.library.SomeService"),
3946                Error::invalid_capability(
3947                    DeclType::UseProtocol,
3948                    "source",
3949                    "fuchsia.some.library.SomeProtocol"),
3950                Error::invalid_capability(DeclType::UseDirectory, "source", "dir"),
3951                Error::invalid_capability(DeclType::UseRunner, "source", "source_elf"),
3952                Error::invalid_capability(DeclType::UseConfiguration, "source", "source_config"),
3953                Error::invalid_capability(DeclType::UseProtocol, "source", "dict"),
3954            ])),
3955        },
3956        test_validate_use_numbered_handle => {
3957            input = {
3958                fdecl::Component {
3959                    uses: Some(vec![
3960                        // Valid
3961                        fdecl::Use::Protocol(fdecl::UseProtocol {
3962                            dependency_type: Some(fdecl::DependencyType::Strong),
3963                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
3964                            source_name: Some("fuchsia.logger.LogSink".into()),
3965                            numbered_handle: Some(0xab),
3966                            ..Default::default()
3967                        }),
3968                        // Invalid
3969                        fdecl::Use::Protocol(fdecl::UseProtocol {
3970                            dependency_type: Some(fdecl::DependencyType::Strong),
3971                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
3972                            source_name: Some("fuchsia.logger.LogSink".into()),
3973                            target_path: Some("/svc/fuchsia.logger.LogSink".into()),
3974                            numbered_handle: Some(0xab),
3975                            ..Default::default()
3976                        }),
3977                    ]),
3978                    ..new_component_decl()
3979                }
3980            },
3981            result = Err(ErrorList::new(vec![
3982                Error::extraneous_field(DeclType::UseProtocol, "numbered_handle"),
3983            ])),
3984        },
3985        test_validate_use_from_child_offer_to_child_strong_cycle => {
3986            input = {
3987                fdecl::Component {
3988                    capabilities: Some(vec![
3989                        fdecl::Capability::Service(fdecl::Service {
3990                            name: Some("a".to_string()),
3991                            source_path: Some("/a".to_string()),
3992                            ..Default::default()
3993                        })]),
3994                    uses: Some(vec![
3995                        fdecl::Use::Protocol(fdecl::UseProtocol {
3996                            dependency_type: Some(fdecl::DependencyType::Strong),
3997                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3998                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3999                            target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
4000                            ..Default::default()
4001                        }),
4002                        fdecl::Use::Service(fdecl::UseService {
4003                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4004                            source_name: Some("service_name".to_string()),
4005                            target_path: Some("/svc/service_name".to_string()),
4006                            dependency_type: Some(fdecl::DependencyType::Strong),
4007                            ..Default::default()
4008                        }),
4009                        fdecl::Use::Directory(fdecl::UseDirectory {
4010                            dependency_type: Some(fdecl::DependencyType::Strong),
4011                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4012                            source_name: Some("DirectoryName".to_string()),
4013                            target_path: Some("/data/DirectoryName".to_string()),
4014                            rights: Some(fio::Operations::CONNECT),
4015                            subdir: None,
4016                            ..Default::default()
4017                        }),
4018                    ]),
4019                    offers: Some(vec![
4020                        fdecl::Offer::Service(fdecl::OfferService {
4021                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4022                            source_name: Some("a".to_string()),
4023                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4024                            target_name: Some("a".to_string()),
4025                            ..Default::default()
4026                        })
4027                    ]),
4028                    children: Some(vec![
4029                        fdecl::Child {
4030                            name: Some("child".to_string()),
4031                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4032                            startup: Some(fdecl::StartupMode::Lazy),
4033                            on_terminate: None,
4034                            ..Default::default()
4035                        }
4036                    ]),
4037                    ..new_component_decl()
4038                }
4039            },
4040            result = Err(ErrorList::new(vec![
4041                Error::dependency_cycle("{{self -> child child -> self}}"),
4042            ])),
4043        },
4044        test_validate_use_from_child_storage_no_cycle => {
4045            input = {
4046                fdecl::Component {
4047                    capabilities: Some(vec![
4048                        fdecl::Capability::Storage(fdecl::Storage {
4049                            name: Some("cdata".to_string()),
4050                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None } )),
4051                            backing_dir: Some("minfs".to_string()),
4052                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4053                            ..Default::default()
4054                        }),
4055                        fdecl::Capability::Storage(fdecl::Storage {
4056                            name: Some("pdata".to_string()),
4057                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4058                            backing_dir: Some("minfs".to_string()),
4059                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4060                            ..Default::default()
4061                        }),
4062                    ]),
4063                    uses: Some(vec![
4064                        fdecl::Use::Protocol(fdecl::UseProtocol {
4065                            dependency_type: Some(fdecl::DependencyType::Strong),
4066                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child1".to_string(), collection: None})),
4067                            source_name: Some("a".to_string()),
4068                            target_path: Some("/svc/a".to_string()),
4069                            ..Default::default()
4070                        }),
4071                    ]),
4072                    offers: Some(vec![
4073                        fdecl::Offer::Storage(fdecl::OfferStorage {
4074                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4075                            source_name: Some("cdata".to_string()),
4076                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4077                            target_name: Some("cdata".to_string()),
4078                            ..Default::default()
4079                        }),
4080                        fdecl::Offer::Storage(fdecl::OfferStorage {
4081                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4082                            source_name: Some("pdata".to_string()),
4083                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4084                            target_name: Some("pdata".to_string()),
4085                            ..Default::default()
4086                        }),
4087                    ]),
4088                    children: Some(vec![
4089                        fdecl::Child {
4090                            name: Some("child1".to_string()),
4091                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4092                            startup: Some(fdecl::StartupMode::Lazy),
4093                            on_terminate: None,
4094                            ..Default::default()
4095                        },
4096                        fdecl::Child {
4097                            name: Some("child2".to_string()),
4098                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4099                            startup: Some(fdecl::StartupMode::Lazy),
4100                            on_terminate: None,
4101                            ..Default::default()
4102                        }
4103                    ]),
4104                    ..new_component_decl()
4105                }
4106            },
4107            result = Ok(()),
4108        },
4109        test_validate_use_from_child_storage_cycle => {
4110            input = {
4111                fdecl::Component {
4112                    capabilities: Some(vec![
4113                        fdecl::Capability::Storage(fdecl::Storage {
4114                            name: Some("data".to_string()),
4115                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4116                            backing_dir: Some("minfs".to_string()),
4117                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4118                            ..Default::default()
4119                        }),
4120                    ]),
4121                    uses: Some(vec![
4122                        fdecl::Use::Protocol(fdecl::UseProtocol {
4123                            dependency_type: Some(fdecl::DependencyType::Strong),
4124                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4125                            source_name: Some("a".to_string()),
4126                            target_path: Some("/svc/a".to_string()),
4127                            ..Default::default()
4128                        }),
4129                    ]),
4130                    offers: Some(vec![
4131                        fdecl::Offer::Storage(fdecl::OfferStorage {
4132                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4133                            source_name: Some("data".to_string()),
4134                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4135                            target_name: Some("data".to_string()),
4136                            ..Default::default()
4137                        }),
4138                    ]),
4139                    children: Some(vec![
4140                        fdecl::Child {
4141                            name: Some("child".to_string()),
4142                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4143                            startup: Some(fdecl::StartupMode::Lazy),
4144                            on_terminate: None,
4145                            ..Default::default()
4146                        },
4147                    ]),
4148                    ..new_component_decl()
4149                }
4150            },
4151            result = Err(ErrorList::new(vec![
4152                Error::dependency_cycle("{{self -> capability data -> child child -> self}}"),
4153            ])),
4154        },
4155        test_validate_storage_strong_cycle_between_children => {
4156            input = {
4157                fdecl::Component {
4158                    capabilities: Some(vec![
4159                        fdecl::Capability::Storage(fdecl::Storage {
4160                            name: Some("data".to_string()),
4161                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None } )),
4162                            backing_dir: Some("minfs".to_string()),
4163                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4164                            ..Default::default()
4165                        })
4166                    ]),
4167                    offers: Some(vec![
4168                        fdecl::Offer::Storage(fdecl::OfferStorage {
4169                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4170                            source_name: Some("data".to_string()),
4171                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4172                            target_name: Some("data".to_string()),
4173                            ..Default::default()
4174                        }),
4175                        fdecl::Offer::Service(fdecl::OfferService {
4176                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4177                            source_name: Some("a".to_string()),
4178                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4179                            target_name: Some("a".to_string()),
4180                            ..Default::default()
4181                        }),
4182                    ]),
4183                    children: Some(vec![
4184                        fdecl::Child {
4185                            name: Some("child1".to_string()),
4186                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4187                            startup: Some(fdecl::StartupMode::Lazy),
4188                            on_terminate: None,
4189                            ..Default::default()
4190                        },
4191                        fdecl::Child {
4192                            name: Some("child2".to_string()),
4193                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4194                            startup: Some(fdecl::StartupMode::Lazy),
4195                            on_terminate: None,
4196                            ..Default::default()
4197                        }
4198                    ]),
4199                    ..new_component_decl()
4200                }
4201            },
4202            result = Err(ErrorList::new(vec![
4203                Error::dependency_cycle("{{child child1 -> capability data -> child child2 -> child child1}}"),
4204            ])),
4205        },
4206        test_validate_strong_cycle_between_children_through_environment_debug => {
4207            input = {
4208                fdecl::Component {
4209                    environments: Some(vec![
4210                        fdecl::Environment {
4211                            name: Some("env".to_string()),
4212                            extends: Some(fdecl::EnvironmentExtends::Realm),
4213                            debug_capabilities: Some(vec![
4214                                fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
4215                                    source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4216                                    source_name: Some("fuchsia.foo.Bar".to_string()),
4217                                    target_name: Some("fuchsia.foo.Bar".to_string()),
4218                                    ..Default::default()
4219                                }),
4220                            ]),
4221                            ..Default::default()
4222                        },
4223                    ]),
4224                    offers: Some(vec![
4225                        fdecl::Offer::Service(fdecl::OfferService {
4226                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4227                            source_name: Some("a".to_string()),
4228                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4229                            target_name: Some("a".to_string()),
4230                            ..Default::default()
4231                        }),
4232                    ]),
4233                    children: Some(vec![
4234                        fdecl::Child {
4235                            name: Some("child1".to_string()),
4236                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4237                            startup: Some(fdecl::StartupMode::Lazy),
4238                            on_terminate: None,
4239                            ..Default::default()
4240                        },
4241                        fdecl::Child {
4242                            name: Some("child2".to_string()),
4243                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4244                            startup: Some(fdecl::StartupMode::Lazy),
4245                            environment: Some("env".to_string()),
4246                            on_terminate: None,
4247                            ..Default::default()
4248                        }
4249                    ]),
4250                    ..new_component_decl()
4251                }
4252            },
4253            result = Err(ErrorList::new(vec![
4254                Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}"),
4255            ])),
4256        },
4257        test_validate_strong_cycle_between_children_through_environment_runner => {
4258            input = {
4259                fdecl::Component {
4260                    environments: Some(vec![
4261                        fdecl::Environment {
4262                            name: Some("env".to_string()),
4263                            extends: Some(fdecl::EnvironmentExtends::Realm),
4264                            runners: Some(vec![
4265                                fdecl::RunnerRegistration {
4266                                    source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4267                                    source_name: Some("coff".to_string()),
4268                                    target_name: Some("coff".to_string()),
4269                                    ..Default::default()
4270                                }
4271                            ]),
4272                            ..Default::default()
4273                        },
4274                    ]),
4275                    offers: Some(vec![
4276                        fdecl::Offer::Service(fdecl::OfferService {
4277                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4278                            source_name: Some("a".to_string()),
4279                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4280                            target_name: Some("a".to_string()),
4281                            ..Default::default()
4282                        }),
4283                    ]),
4284                    children: Some(vec![
4285                        fdecl::Child {
4286                            name: Some("child1".to_string()),
4287                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4288                            startup: Some(fdecl::StartupMode::Lazy),
4289                            on_terminate: None,
4290                            ..Default::default()
4291                        },
4292                        fdecl::Child {
4293                            name: Some("child2".to_string()),
4294                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4295                            startup: Some(fdecl::StartupMode::Lazy),
4296                            environment: Some("env".to_string()),
4297                            on_terminate: None,
4298                            ..Default::default()
4299                        }
4300                    ]),
4301                    ..new_component_decl()
4302                }
4303            },
4304            result = Err(ErrorList::new(vec![
4305                Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}"),
4306            ])),
4307        },
4308        test_validate_strong_cycle_between_children_through_environment_resolver => {
4309            input = {
4310                fdecl::Component {
4311                    environments: Some(vec![
4312                        fdecl::Environment {
4313                            name: Some("env".to_string()),
4314                            extends: Some(fdecl::EnvironmentExtends::Realm),
4315                            resolvers: Some(vec![
4316                                fdecl::ResolverRegistration {
4317                                    resolver: Some("gopher".to_string()),
4318                                    source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4319                                    scheme: Some("gopher".to_string()),
4320                                    ..Default::default()
4321                                }
4322                            ]),
4323                            ..Default::default()
4324                        },
4325                    ]),
4326                    offers: Some(vec![
4327                        fdecl::Offer::Service(fdecl::OfferService {
4328                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4329                            source_name: Some("a".to_string()),
4330                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4331                            target_name: Some("a".to_string()),
4332                            ..Default::default()
4333                        }),
4334                    ]),
4335                    children: Some(vec![
4336                        fdecl::Child {
4337                            name: Some("child1".to_string()),
4338                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4339                            startup: Some(fdecl::StartupMode::Lazy),
4340                            on_terminate: None,
4341                            ..Default::default()
4342                        },
4343                        fdecl::Child {
4344                            name: Some("child2".to_string()),
4345                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4346                            startup: Some(fdecl::StartupMode::Lazy),
4347                            environment: Some("env".to_string()),
4348                            on_terminate: None,
4349                            ..Default::default()
4350                        }
4351                    ]),
4352                    ..new_component_decl()
4353                }
4354            },
4355            result = Err(ErrorList::new(vec![
4356                Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}"),
4357            ])),
4358        },
4359        test_validate_strong_cycle_between_self_and_two_children => {
4360            input = {
4361                fdecl::Component {
4362                    capabilities: Some(vec![
4363                        fdecl::Capability::Protocol(fdecl::Protocol {
4364                            name: Some("fuchsia.foo.Bar".to_string()),
4365                            source_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4366                            ..Default::default()
4367                        })
4368                    ]),
4369                    offers: Some(vec![
4370                        fdecl::Offer::Protocol(fdecl::OfferProtocol {
4371                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4372                            source_name: Some("fuchsia.foo.Bar".to_string()),
4373                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4374                            target_name: Some("fuchsia.foo.Bar".to_string()),
4375                            dependency_type: Some(fdecl::DependencyType::Strong),
4376                            ..Default::default()
4377                        }),
4378                        fdecl::Offer::Protocol(fdecl::OfferProtocol {
4379                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4380                            source_name: Some("fuchsia.bar.Baz".to_string()),
4381                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4382                            target_name: Some("fuchsia.bar.Baz".to_string()),
4383                            dependency_type: Some(fdecl::DependencyType::Strong),
4384                            ..Default::default()
4385                        }),
4386                    ]),
4387                    uses: Some(vec![
4388                        fdecl::Use::Protocol(fdecl::UseProtocol {
4389                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child2".to_string(), collection: None})),
4390                            source_name: Some("fuchsia.baz.Foo".to_string()),
4391                            target_path: Some("/svc/fuchsia.baz.Foo".to_string()),
4392                            dependency_type: Some(fdecl::DependencyType::Strong),
4393                            ..Default::default()
4394                        }),
4395                    ]),
4396                    children: Some(vec![
4397                        fdecl::Child {
4398                            name: Some("child1".to_string()),
4399                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4400                            startup: Some(fdecl::StartupMode::Lazy),
4401                            on_terminate: None,
4402                            ..Default::default()
4403                        },
4404                        fdecl::Child {
4405                            name: Some("child2".to_string()),
4406                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4407                            startup: Some(fdecl::StartupMode::Lazy),
4408                            on_terminate: None,
4409                            ..Default::default()
4410                        }
4411                    ]),
4412                    ..new_component_decl()
4413                }
4414            },
4415            result = Err(ErrorList::new(vec![
4416                Error::dependency_cycle("{{self -> child child1 -> child child2 -> self}}"),
4417            ])),
4418        },
4419        test_validate_strong_cycle_with_self_storage => {
4420            input = {
4421                fdecl::Component {
4422                    capabilities: Some(vec![
4423                        fdecl::Capability::Storage(fdecl::Storage {
4424                            name: Some("data".to_string()),
4425                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4426                            backing_dir: Some("minfs".to_string()),
4427                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4428                            ..Default::default()
4429                        }),
4430                        fdecl::Capability::Directory(fdecl::Directory {
4431                            name: Some("minfs".to_string()),
4432                            source_path: Some("/minfs".to_string()),
4433                            rights: Some(fio::RW_STAR_DIR),
4434                            ..Default::default()
4435                        }),
4436                    ]),
4437                    offers: Some(vec![
4438                        fdecl::Offer::Storage(fdecl::OfferStorage {
4439                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4440                            source_name: Some("data".to_string()),
4441                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4442                            target_name: Some("data".to_string()),
4443                            ..Default::default()
4444                        }),
4445                    ]),
4446                    uses: Some(vec![
4447                        fdecl::Use::Protocol(fdecl::UseProtocol {
4448                            dependency_type: Some(fdecl::DependencyType::Strong),
4449                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4450                            source_name: Some("fuchsia.foo.Bar".to_string()),
4451                            target_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4452                            ..Default::default()
4453                        }),
4454                    ]),
4455                    children: Some(vec![
4456                        fdecl::Child {
4457                            name: Some("child".to_string()),
4458                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4459                            startup: Some(fdecl::StartupMode::Lazy),
4460                            ..Default::default()
4461                        },
4462                    ]),
4463                    ..new_component_decl()
4464                }
4465            },
4466            result = Err(ErrorList::new(vec![
4467                Error::dependency_cycle("{{self -> capability data -> child child -> self}}"),
4468            ])),
4469        },
4470        test_validate_strong_cycle_with_self_storage_admin_protocol => {
4471            input = {
4472                fdecl::Component {
4473                    capabilities: Some(vec![
4474                        fdecl::Capability::Storage(fdecl::Storage {
4475                            name: Some("data".to_string()),
4476                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4477                            backing_dir: Some("minfs".to_string()),
4478                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4479                            ..Default::default()
4480                        }),
4481                        fdecl::Capability::Directory(fdecl::Directory {
4482                            name: Some("minfs".to_string()),
4483                            source_path: Some("/minfs".to_string()),
4484                            rights: Some(fio::RW_STAR_DIR),
4485                            ..Default::default()
4486                        }),
4487                    ]),
4488                    offers: Some(vec![
4489                        fdecl::Offer::Protocol(fdecl::OfferProtocol {
4490                            source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef { name: "data".to_string() })),
4491                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4492                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4493                            target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4494                            dependency_type: Some(fdecl::DependencyType::Strong),
4495                            ..Default::default()
4496                        }),
4497                    ]),
4498                    uses: Some(vec![
4499                        fdecl::Use::Protocol(fdecl::UseProtocol {
4500                            dependency_type: Some(fdecl::DependencyType::Strong),
4501                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4502                            source_name: Some("fuchsia.foo.Bar".to_string()),
4503                            target_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4504                            ..Default::default()
4505                        }),
4506                    ]),
4507                    children: Some(vec![
4508                        fdecl::Child {
4509                            name: Some("child".to_string()),
4510                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4511                            startup: Some(fdecl::StartupMode::Lazy),
4512                            ..Default::default()
4513                        },
4514                    ]),
4515                    ..new_component_decl()
4516                }
4517            },
4518            result = Err(ErrorList::new(vec![
4519                Error::dependency_cycle("{{self -> capability data -> child child -> self}}"),
4520            ])),
4521        },
4522        test_validate_strong_cycle_with_dictionary => {
4523            input = fdecl::Component {
4524                offers: Some(vec![
4525                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
4526                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4527                        source_name: Some("dict".into()),
4528                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4529                            name: "a".into(),
4530                            collection: None,
4531                        })),
4532                        target_name: Some("dict".into()),
4533                        dependency_type: Some(fdecl::DependencyType::Strong),
4534                        ..Default::default()
4535                    }),
4536                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4537                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4538                            name: "b".into(),
4539                            collection: None,
4540                        })),
4541                        source_name: Some("1".into()),
4542                        target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4543                            name: "dict".into(),
4544                        })),
4545                        target_name: Some("1".into()),
4546                        dependency_type: Some(fdecl::DependencyType::Strong),
4547                        ..Default::default()
4548                    }),
4549                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4550                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4551                            name: "a".into(),
4552                            collection: None,
4553                        })),
4554                        source_name: Some("2".into()),
4555                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4556                            name: "b".into(),
4557                            collection: None,
4558                        })),
4559                        target_name: Some("2".into()),
4560                        dependency_type: Some(fdecl::DependencyType::Strong),
4561                        ..Default::default()
4562                    }),
4563                ]),
4564                children: Some(vec![
4565                    fdecl::Child {
4566                        name: Some("a".into()),
4567                        url: Some("fuchsia-pkg://child".into()),
4568                        startup: Some(fdecl::StartupMode::Lazy),
4569                        ..Default::default()
4570                    },
4571                    fdecl::Child {
4572                        name: Some("b".into()),
4573                        url: Some("fuchsia-pkg://child".into()),
4574                        startup: Some(fdecl::StartupMode::Lazy),
4575                        ..Default::default()
4576                    },
4577                ]),
4578                capabilities: Some(vec![
4579                    fdecl::Capability::Dictionary(fdecl::Dictionary {
4580                        name: Some("dict".into()),
4581                        ..Default::default()
4582                    }),
4583                ]),
4584                ..Default::default()
4585            },
4586            result = Err(ErrorList::new(vec![
4587                Error::dependency_cycle("{{child a -> child b -> capability dict -> child a}}"),
4588            ])),
4589        },
4590        test_validate_strong_cycle_with_dictionary_indirect => {
4591            input = fdecl::Component {
4592                offers: Some(vec![
4593                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4594                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4595                        source_name: Some("3".into()),
4596                        source_dictionary: Some("dict".into()),
4597                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4598                            name: "a".into(),
4599                            collection: None,
4600                        })),
4601                        target_name: Some("3".into()),
4602                        dependency_type: Some(fdecl::DependencyType::Strong),
4603                        ..Default::default()
4604                    }),
4605                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4606                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4607                            name: "b".into(),
4608                            collection: None,
4609                        })),
4610                        source_name: Some("1".into()),
4611                        target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4612                            name: "dict".into(),
4613                        })),
4614                        target_name: Some("1".into()),
4615                        dependency_type: Some(fdecl::DependencyType::Strong),
4616                        ..Default::default()
4617                    }),
4618                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4619                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4620                            name: "a".into(),
4621                            collection: None,
4622                        })),
4623                        source_name: Some("2".into()),
4624                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4625                            name: "b".into(),
4626                            collection: None,
4627                        })),
4628                        target_name: Some("2".into()),
4629                        dependency_type: Some(fdecl::DependencyType::Strong),
4630                        ..Default::default()
4631                    }),
4632                ]),
4633                children: Some(vec![
4634                    fdecl::Child {
4635                        name: Some("a".into()),
4636                        url: Some("fuchsia-pkg://child".into()),
4637                        startup: Some(fdecl::StartupMode::Lazy),
4638                        ..Default::default()
4639                    },
4640                    fdecl::Child {
4641                        name: Some("b".into()),
4642                        url: Some("fuchsia-pkg://child".into()),
4643                        startup: Some(fdecl::StartupMode::Lazy),
4644                        ..Default::default()
4645                    },
4646                ]),
4647                capabilities: Some(vec![
4648                    fdecl::Capability::Dictionary(fdecl::Dictionary {
4649                        name: Some("dict".into()),
4650                        ..Default::default()
4651                    }),
4652                ]),
4653                ..Default::default()
4654            },
4655            result = Err(ErrorList::new(vec![
4656                Error::dependency_cycle("{{child a -> child b -> capability dict -> child a}}"),
4657            ])),
4658        },
4659        test_validate_use_dependency_cycle_with_dictionary => {
4660            input = fdecl::Component {
4661                offers: Some(vec![
4662                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
4663                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4664                        source_name: Some("dict".into()),
4665                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4666                            name: "a".into(),
4667                            collection: None,
4668                        })),
4669                        target_name: Some("dict".into()),
4670                        dependency_type: Some(fdecl::DependencyType::Strong),
4671                        ..Default::default()
4672                    }),
4673                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4674                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4675                        source_name: Some("1".into()),
4676                        target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4677                            name: "dict".into(),
4678                        })),
4679                        target_name: Some("1".into()),
4680                        dependency_type: Some(fdecl::DependencyType::Strong),
4681                        ..Default::default()
4682                    }),
4683                ]),
4684                children: Some(vec![
4685                    fdecl::Child {
4686                        name: Some("a".into()),
4687                        url: Some("fuchsia-pkg://child".into()),
4688                        startup: Some(fdecl::StartupMode::Lazy),
4689                        ..Default::default()
4690                    },
4691                ]),
4692                uses: Some(vec![
4693                        fdecl::Use::Protocol(fdecl::UseProtocol {
4694                            dependency_type: Some(fdecl::DependencyType::Strong),
4695                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "a".to_string(), collection: None})),
4696                            source_name: Some("2".to_string()),
4697                            target_path: Some("/svc/foo".into()),
4698                            ..Default::default()
4699                        }),
4700                ]),
4701                capabilities: Some(vec![
4702                    fdecl::Capability::Dictionary(fdecl::Dictionary {
4703                        name: Some("dict".into()),
4704                        ..Default::default()
4705                    }),
4706                    fdecl::Capability::Protocol(fdecl::Protocol {
4707                        name: Some("1".to_string()),
4708                        source_path: Some("/path".to_string()),
4709                        ..Default::default()
4710                    }),
4711                ]),
4712                ..Default::default()
4713            },
4714            result = Err(ErrorList::new(vec![
4715                Error::dependency_cycle("{{self -> capability dict -> child a -> self}}"),
4716            ])),
4717        },
4718        test_validate_use_from_child_offer_to_child_weak_cycle => {
4719            input = {
4720                fdecl::Component {
4721                    capabilities: Some(vec![
4722                        fdecl::Capability::Service(fdecl::Service {
4723                            name: Some("a".to_string()),
4724                            source_path: Some("/a".to_string()),
4725                            ..Default::default()
4726                        })]),
4727                    uses: Some(vec![
4728                        fdecl::Use::Protocol(fdecl::UseProtocol {
4729                            dependency_type: Some(fdecl::DependencyType::Weak),
4730                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4731                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4732                            target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
4733                            ..Default::default()
4734                        }),
4735                        fdecl::Use::Service(fdecl::UseService {
4736                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4737                            source_name: Some("service_name".to_string()),
4738                            target_path: Some("/svc/service_name".to_string()),
4739                            dependency_type: Some(fdecl::DependencyType::Weak),
4740                            ..Default::default()
4741                        }),
4742                        fdecl::Use::Directory(fdecl::UseDirectory {
4743                            dependency_type: Some(fdecl::DependencyType::Weak),
4744                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4745                            source_name: Some("DirectoryName".to_string()),
4746                            target_path: Some("/data/DirectoryName".to_string()),
4747                            rights: Some(fio::Operations::CONNECT),
4748                            subdir: None,
4749                            ..Default::default()
4750                        }),
4751                    ]),
4752                    offers: Some(vec![
4753                        fdecl::Offer::Service(fdecl::OfferService {
4754                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4755                            source_name: Some("a".to_string()),
4756                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4757                            target_name: Some("a".to_string()),
4758                            ..Default::default()
4759                        })
4760                    ]),
4761                    children: Some(vec![
4762                        fdecl::Child {
4763                            name: Some("child".to_string()),
4764                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4765                            startup: Some(fdecl::StartupMode::Lazy),
4766                            on_terminate: None,
4767                            ..Default::default()
4768                        }
4769                    ]),
4770                    ..new_component_decl()
4771                }
4772            },
4773            result = Ok(()),
4774        },
4775        test_validate_expose_from_self_to_framework_and_parent => {
4776            input = {
4777                fdecl::Component {
4778                    capabilities: Some(vec![
4779                        fdecl::Capability::Protocol(fdecl::Protocol {
4780                            name: Some("a".to_string()),
4781                            source_path: Some("/a".to_string()),
4782                            ..Default::default()
4783                        }),
4784                    ]),
4785                    exposes: Some(vec![
4786                        fdecl::Expose::Protocol(fdecl::ExposeProtocol {
4787                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4788                            source_name: Some("a".to_string()),
4789                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4790                            target_name: Some("a".to_string()),
4791                            ..Default::default()
4792                        }),
4793                        fdecl::Expose::Protocol(fdecl::ExposeProtocol {
4794                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4795                            source_name: Some("a".to_string()),
4796                            target: Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})),
4797                            target_name: Some("a".to_string()),
4798                            ..Default::default()
4799                        }),
4800                    ]),
4801                    ..new_component_decl()
4802                }
4803            },
4804            result = Ok(()),
4805        },
4806        test_validate_use_from_not_child_weak => {
4807            input = {
4808                fdecl::Component {
4809                    uses: Some(vec![
4810                        fdecl::Use::Protocol(fdecl::UseProtocol {
4811                            dependency_type: Some(fdecl::DependencyType::Weak),
4812                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4813                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4814                            target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
4815                            ..Default::default()
4816                        }),
4817                    ]),
4818                    ..new_component_decl()
4819                }
4820            },
4821            result = Err(ErrorList::new(vec![
4822                Error::invalid_field(DeclType::UseProtocol, "dependency_type"),
4823            ])),
4824        },
4825        test_validate_event_stream_offer_valid_decls => {
4826            input = {
4827                let mut decl = new_component_decl();
4828                decl.offers = Some(vec![
4829                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4830                        source_name: Some("stopped".to_string()),
4831                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4832                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4833                        target_name: Some("stopped".to_string()),
4834                        ..Default::default()
4835                    }),
4836                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4837                        source_name: Some("started".to_string()),
4838                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4839                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4840                        target_name: Some("started".to_string()),
4841                        ..Default::default()
4842                    }),
4843                ]);
4844                decl.children = Some(vec![fdecl::Child{
4845                    name: Some("test".to_string()),
4846                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4847                    startup: Some(fdecl::StartupMode::Lazy),
4848                    on_terminate: None,
4849                    environment: None,
4850                    ..Default::default()
4851                },
4852                fdecl::Child{
4853                    name: Some("test2".to_string()),
4854                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4855                    startup: Some(fdecl::StartupMode::Lazy),
4856                    on_terminate: None,
4857                    environment: None,
4858                    ..Default::default()
4859                }
4860                ]);
4861                decl
4862            },
4863            result = Ok(()),
4864        },
4865        test_validate_event_stream_offer_to_framework_invalid => {
4866            input = {
4867                let mut decl = new_component_decl();
4868                decl.offers = Some(vec![
4869                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4870                        source_name: Some("stopped".to_string()),
4871                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4872                        target: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
4873                        target_name: Some("stopped".to_string()),
4874                        ..Default::default()
4875                    }),
4876                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4877                        source_name: Some("started".to_string()),
4878                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4879                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4880                        target_name: Some("started".to_string()),
4881                        ..Default::default()
4882                    }),
4883                ]);
4884                decl.children = Some(vec![fdecl::Child{
4885                    name: Some("test".to_string()),
4886                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4887                    startup: Some(fdecl::StartupMode::Lazy),
4888                    on_terminate: None,
4889                    environment: None,
4890                    ..Default::default()
4891                },
4892                fdecl::Child{
4893                    name: Some("test2".to_string()),
4894                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4895                    startup: Some(fdecl::StartupMode::Lazy),
4896                    on_terminate: None,
4897                    environment: None,
4898                    ..Default::default()
4899                }
4900                ]);
4901                decl
4902            },
4903            result = Err(ErrorList::new(vec![
4904                Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "target".to_string() }),
4905                Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "target".to_string() }),
4906            ])),
4907        },
4908        test_validate_event_stream_offer_to_scope_zero_length_invalid => {
4909            input = {
4910                let mut decl = new_component_decl();
4911                decl.offers = Some(vec![
4912                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4913                        source_name: Some("started".to_string()),
4914                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4915                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4916                        scope: Some(vec![]),
4917                        target_name: Some("started".to_string()),
4918                        ..Default::default()
4919                    }),
4920                ]);
4921                decl.children = Some(vec![fdecl::Child{
4922                    name: Some("test".to_string()),
4923                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4924                    startup: Some(fdecl::StartupMode::Lazy),
4925                    on_terminate: None,
4926                    environment: None,
4927                    ..Default::default()
4928                },
4929                fdecl::Child{
4930                    name: Some("test2".to_string()),
4931                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4932                    startup: Some(fdecl::StartupMode::Lazy),
4933                    on_terminate: None,
4934                    environment: None,
4935                    ..Default::default()
4936                }
4937                ]);
4938                decl
4939            },
4940            result = Err(ErrorList::new(vec![
4941                Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "scope".to_string() }),
4942            ])),
4943        },
4944        test_validate_event_stream_offer_to_scope_framework_invalid => {
4945            input = {
4946                let mut decl = new_component_decl();
4947                decl.offers = Some(vec![
4948                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4949                        source_name: Some("started".to_string()),
4950                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4951                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4952                        scope: Some(vec![fdecl::Ref::Framework(fdecl::FrameworkRef{})]),
4953                        target_name: Some("started".to_string()),
4954                        ..Default::default()
4955                    }),
4956                ]);
4957                decl.children = Some(vec![fdecl::Child{
4958                    name: Some("test".to_string()),
4959                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4960                    startup: Some(fdecl::StartupMode::Lazy),
4961                    on_terminate: None,
4962                    environment: None,
4963                    ..Default::default()
4964                },
4965                fdecl::Child{
4966                    name: Some("test2".to_string()),
4967                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4968                    startup: Some(fdecl::StartupMode::Lazy),
4969                    on_terminate: None,
4970                    environment: None,
4971                    ..Default::default()
4972                }
4973                ]);
4974                decl
4975            },
4976            result = Err(ErrorList::new(vec![
4977                Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "scope".to_string() }),
4978            ])),
4979        },
4980        test_validate_event_stream_offer_to_scope_valid => {
4981            input = {
4982                let mut decl = new_component_decl();
4983                decl.offers = Some(vec![
4984                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4985                        source_name: Some("started".to_string()),
4986                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4987                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4988                        scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
4989                        target_name: Some("started".to_string()),
4990                        ..Default::default()
4991                    }),
4992                ]);
4993                decl.children = Some(vec![fdecl::Child{
4994                    name: Some("test".to_string()),
4995                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4996                    startup: Some(fdecl::StartupMode::Lazy),
4997                    on_terminate: None,
4998                    environment: None,
4999                    ..Default::default()
5000                },
5001                fdecl::Child{
5002                    name: Some("test2".to_string()),
5003                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
5004                    startup: Some(fdecl::StartupMode::Lazy),
5005                    on_terminate: None,
5006                    environment: None,
5007                    ..Default::default()
5008                }
5009                ]);
5010                decl
5011            },
5012            result = Ok(()),
5013        },
5014        test_validate_event_stream_offer_to_scope_with_capability_requested => {
5015            input = {
5016                let mut decl = new_component_decl();
5017                decl.offers = Some(vec![
5018                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
5019                        source_name: Some("capability_requested".to_string()),
5020                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5021                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5022                        scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
5023                        target_name: Some("started".to_string()),
5024                        ..Default::default()
5025                    }),
5026                ]);
5027                decl.children = Some(vec![fdecl::Child{
5028                    name: Some("test".to_string()),
5029                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5030                    startup: Some(fdecl::StartupMode::Lazy),
5031                    on_terminate: None,
5032                    environment: None,
5033                    ..Default::default()
5034                },
5035                fdecl::Child{
5036                    name: Some("test2".to_string()),
5037                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
5038                    startup: Some(fdecl::StartupMode::Lazy),
5039                    on_terminate: None,
5040                    environment: None,
5041                    ..Default::default()
5042                }
5043                ]);
5044                decl
5045            },
5046            result = Ok(()),
5047        },
5048        test_validate_event_stream_offer_with_no_source_name_invalid => {
5049            input = {
5050                let mut decl = new_component_decl();
5051                decl.offers = Some(vec![
5052                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
5053                        source_name: None,
5054                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5055                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5056                        scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
5057                        target_name: Some("started".to_string()),
5058                        ..Default::default()
5059                    }),
5060                ]);
5061                decl.children = Some(vec![fdecl::Child{
5062                    name: Some("test".to_string()),
5063                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5064                    startup: Some(fdecl::StartupMode::Lazy),
5065                    on_terminate: None,
5066                    environment: None,
5067                    ..Default::default()
5068                },
5069                fdecl::Child{
5070                    name: Some("test2".to_string()),
5071                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
5072                    startup: Some(fdecl::StartupMode::Lazy),
5073                    on_terminate: None,
5074                    environment: None,
5075                    ..Default::default()
5076                }
5077                ]);
5078                decl
5079            },
5080            result = Err(ErrorList::new(vec![
5081                Error::MissingField(DeclField { decl: DeclType::OfferEventStream, field: "source_name".to_string() }),
5082            ])),
5083        },
5084        test_validate_event_stream_offer_invalid_source => {
5085            input = {
5086                let mut decl = new_component_decl();
5087                decl.offers = Some(vec![
5088                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
5089                        source_name: Some("stopped".to_string()),
5090                        source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
5091                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5092                        target_name: Some("stopped".to_string()),
5093                        ..Default::default()
5094                    }),
5095                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
5096                        source_name: Some("started".to_string()),
5097                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5098                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5099                        target_name: Some("started".to_string()),
5100                        ..Default::default()
5101                    }),
5102                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
5103                        source_name: Some("capability_requested".to_string()),
5104                        source: Some(fdecl::Ref::Debug(fdecl::DebugRef{})),
5105                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5106                        target_name: Some("capability_requested".to_string()),
5107                        ..Default::default()
5108                    }),
5109                ]);
5110                decl.children = Some(vec![fdecl::Child{
5111                    name: Some("test".to_string()),
5112                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5113                    startup: Some(fdecl::StartupMode::Lazy),
5114                    on_terminate: None,
5115                    environment: None,
5116                    ..Default::default()
5117                },
5118                fdecl::Child{
5119                    name: Some("test2".to_string()),
5120                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
5121                    startup: Some(fdecl::StartupMode::Lazy),
5122                    on_terminate: None,
5123                    environment: None,
5124                    ..Default::default()
5125                }
5126                ]);
5127                decl
5128            },
5129            result = Err(ErrorList::new(vec![
5130                Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "source".to_string() }),
5131            ])),
5132        },
5133
5134        test_validate_event_stream_offer_missing_source => {
5135            input = {
5136                let mut decl = new_component_decl();
5137                decl.offers = Some(vec![
5138                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
5139                        source_name: Some("stopped".to_string()),
5140                        source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
5141                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5142                        target_name: Some("stopped".to_string()),
5143                        ..Default::default()
5144                    }),
5145                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
5146                        source_name: Some("started".to_string()),
5147                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5148                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5149                        target_name: Some("started".to_string()),
5150                        ..Default::default()
5151                    }),
5152                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
5153                        source_name: Some("capability_requested".to_string()),
5154                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5155                        target_name: Some("capability_requested".to_string()),
5156                        ..Default::default()
5157                    }),
5158                ]);
5159                decl.children = Some(vec![fdecl::Child{
5160                    name: Some("test".to_string()),
5161                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5162                    startup: Some(fdecl::StartupMode::Lazy),
5163                    on_terminate: None,
5164                    environment: None,
5165                    ..Default::default()
5166                },
5167                fdecl::Child{
5168                    name: Some("test2".to_string()),
5169                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
5170                    startup: Some(fdecl::StartupMode::Lazy),
5171                    on_terminate: None,
5172                    environment: None,
5173                    ..Default::default()
5174                }
5175                ]);
5176                decl
5177            },
5178            result = Err(ErrorList::new(vec![
5179                Error::MissingField(DeclField { decl: DeclType::OfferEventStream, field: "source".to_string() }),
5180            ])),
5181        },
5182        test_validate_event_stream_must_have_target_path => {
5183            input = {
5184                let mut decl = new_component_decl();
5185                decl.uses = Some(vec![
5186                    fdecl::Use::EventStream(fdecl::UseEventStream {
5187                        source_name: Some("bar".to_string()),
5188                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5189                        ..Default::default()
5190                    }),
5191                ]);
5192                decl
5193            },
5194            result = Err(ErrorList::new(vec![
5195                Error::MissingField(DeclField { decl: DeclType::UseEventStream, field: "target_path".to_string() })
5196            ])),
5197        },
5198        test_validate_event_stream_must_have_source_names => {
5199            input = {
5200                let mut decl = new_component_decl();
5201                decl.uses = Some(vec![
5202                    fdecl::Use::EventStream(fdecl::UseEventStream {
5203                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5204                        target_path: Some("/svc/something".to_string()),
5205                        ..Default::default()
5206                    }),
5207                ]);
5208                decl
5209            },
5210            result = Err(ErrorList::new(vec![
5211                Error::MissingField(DeclField { decl: DeclType::UseEventStream, field: "source_name".to_string() })
5212            ])),
5213        },
5214        test_validate_event_stream_scope_must_be_child_or_collection => {
5215            input = {
5216                let mut decl = new_component_decl();
5217                decl.uses = Some(vec![
5218                    fdecl::Use::EventStream(fdecl::UseEventStream {
5219                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5220                        target_path: Some("/svc/something".to_string()),
5221                        source_name: Some("some_source".to_string()),
5222                        scope: Some(vec![fdecl::Ref::Framework(fdecl::FrameworkRef{})]),
5223                        ..Default::default()
5224                    }),
5225                ]);
5226                decl
5227            },
5228            result = Err(ErrorList::new(vec![
5229                Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "scope".to_string() })
5230            ])),
5231        },
5232        test_validate_event_stream_source_must_be_parent_or_child => {
5233            input = {
5234                let mut decl = new_component_decl();
5235                decl.uses = Some(vec![
5236                    fdecl::Use::EventStream(fdecl::UseEventStream {
5237                        source: Some(fdecl::Ref::Debug(fdecl::DebugRef{})),
5238                        target_path: Some("/svc/something".to_string()),
5239                        source_name: Some("some_source".to_string()),
5240                        scope: Some(vec![]),
5241                        ..Default::default()
5242                    }),
5243                    fdecl::Use::EventStream(fdecl::UseEventStream {
5244                        source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
5245                        target_path: Some("/svc/something_else".to_string()),
5246                        source_name: Some("some_source".to_string()),
5247                        scope: Some(vec![]),
5248                        ..Default::default()
5249                    }),
5250                    fdecl::Use::EventStream(fdecl::UseEventStream {
5251                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5252                        target_path: Some("/svc/yet_something_else".to_string()),
5253                        source_name: Some("some_source".to_string()),
5254                        scope: Some(vec![]),
5255                        ..Default::default()
5256                    }),
5257                ]);
5258                decl
5259            },
5260            result = Err(ErrorList::new(vec![
5261                Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() }),
5262                Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() }),
5263                Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() })
5264            ])),
5265        },
5266        test_validate_no_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
5278            },
5279            result = Err(ErrorList::new(vec![
5280                Error::MissingRunner,
5281            ])),
5282        },
5283        test_validate_uses_runner => {
5284            input = {
5285                let mut decl = new_component_decl();
5286                decl.program = Some(fdecl::Program {
5287                    runner: None,
5288                    info: Some(fdata::Dictionary {
5289                        entries: None,
5290                        ..Default::default()
5291                    }),
5292                    ..Default::default()
5293                });
5294                decl.uses = Some(vec![
5295                    fdecl::Use::Runner(fdecl::UseRunner {
5296                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5297                        source_name: Some("runner".to_string()),
5298                        ..Default::default()
5299                    }),
5300                ]);
5301                decl
5302            },
5303            result = Ok(()),
5304        },
5305        test_validate_program_and_uses_runner_match => {
5306            input = {
5307                let mut decl = new_component_decl();
5308                decl.program = Some(fdecl::Program {
5309                    runner: Some("runner".to_string()),
5310                    info: Some(fdata::Dictionary {
5311                        entries: None,
5312                        ..Default::default()
5313                    }),
5314                    ..Default::default()
5315                });
5316                decl.uses = Some(vec![
5317                    fdecl::Use::Runner(fdecl::UseRunner {
5318                        source: Some(fdecl::Ref::Environment(fdecl::EnvironmentRef {})),
5319                        source_name: Some("runner".to_string()),
5320                        ..Default::default()
5321                    }),
5322                ]);
5323                decl
5324            },
5325            result = Ok(()),
5326        },
5327        test_validate_runner_names_conflict => {
5328            input = {
5329                let mut decl = new_component_decl();
5330                decl.program = Some(fdecl::Program {
5331                    runner: Some("runner".to_string()),
5332                    info: Some(fdata::Dictionary {
5333                        entries: None,
5334                        ..Default::default()
5335                    }),
5336                    ..Default::default()
5337                });
5338                decl.uses = Some(vec![
5339                    fdecl::Use::Runner(fdecl::UseRunner {
5340                        source: Some(fdecl::Ref::Environment(fdecl::EnvironmentRef {})),
5341                        source_name: Some("other.runner".to_string()),
5342                        ..Default::default()
5343                    }),
5344                ]);
5345                decl
5346            },
5347            result = Err(ErrorList::new(vec![
5348                Error::ConflictingRunners,
5349            ])),
5350        },
5351        test_validate_uses_runner_not_environement => {
5352            input = {
5353                let mut decl = new_component_decl();
5354                decl.program = Some(fdecl::Program {
5355                    runner: Some("runner".to_string()),
5356                    info: Some(fdata::Dictionary {
5357                        entries: None,
5358                        ..Default::default()
5359                    }),
5360                    ..Default::default()
5361                });
5362                decl.uses = Some(vec![
5363                    fdecl::Use::Runner(fdecl::UseRunner {
5364                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5365                        source_name: Some("runner".to_string()),
5366                        ..Default::default()
5367                    }),
5368                ]);
5369                decl
5370            },
5371            result = Err(ErrorList::new(vec![
5372                Error::ConflictingRunners,
5373            ])),
5374        },
5375        test_validate_uses_long_identifiers => {
5376            input = {
5377                let mut decl = new_component_decl();
5378                decl.program = Some(fdecl::Program {
5379                    runner: Some("elf".to_string()),
5380                    info: Some(fdata::Dictionary {
5381                        entries: None,
5382                        ..Default::default()
5383                    }),
5384                    ..Default::default()
5385                });
5386                decl.uses = Some(vec![
5387                    fdecl::Use::Service(fdecl::UseService {
5388                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5389                        source_name: Some(format!("{}", "a".repeat(256))),
5390                        target_path: Some("/a".repeat(2048)),
5391                        dependency_type: Some(fdecl::DependencyType::Strong),
5392                        ..Default::default()
5393                    }),
5394                    fdecl::Use::Protocol(fdecl::UseProtocol {
5395                        dependency_type: Some(fdecl::DependencyType::Strong),
5396                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5397                        source_name: Some(format!("{}", "a".repeat(256))),
5398                        target_path: Some("/b".repeat(2048)),
5399                        ..Default::default()
5400                    }),
5401                    fdecl::Use::Directory(fdecl::UseDirectory {
5402                        dependency_type: Some(fdecl::DependencyType::Strong),
5403                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5404                        source_name: Some(format!("{}", "a".repeat(256))),
5405                        target_path: Some("/c".repeat(2048)),
5406                        rights: Some(fio::Operations::CONNECT),
5407                        subdir: None,
5408                        ..Default::default()
5409                    }),
5410                    fdecl::Use::Storage(fdecl::UseStorage {
5411                        source_name: Some("cache".to_string()),
5412                        target_path: Some("/d".repeat(2048)),
5413                        ..Default::default()
5414                    }),
5415                ]);
5416                decl
5417            },
5418            result = Err(ErrorList::new(vec![
5419                Error::field_too_long(DeclType::UseService, "source_name"),
5420                Error::field_too_long(DeclType::UseService, "target_path"),
5421                Error::field_too_long(DeclType::UseProtocol, "source_name"),
5422                Error::field_too_long(DeclType::UseProtocol, "target_path"),
5423                Error::field_too_long(DeclType::UseDirectory, "source_name"),
5424                Error::field_too_long(DeclType::UseDirectory, "target_path"),
5425                Error::field_too_long(DeclType::UseStorage, "target_path"),
5426            ])),
5427        },
5428        test_validate_conflicting_paths => {
5429            input = {
5430                let mut decl = new_component_decl();
5431                decl.uses = Some(vec![
5432                    fdecl::Use::Service(fdecl::UseService {
5433                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5434                        source_name: Some("foo".to_string()),
5435                        target_path: Some("/bar".to_string()),
5436                        dependency_type: Some(fdecl::DependencyType::Strong),
5437                        ..Default::default()
5438                    }),
5439                    fdecl::Use::Protocol(fdecl::UseProtocol {
5440                        dependency_type: Some(fdecl::DependencyType::Strong),
5441                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5442                        source_name: Some("space".to_string()),
5443                        target_path: Some("/bar".to_string()),
5444                        ..Default::default()
5445                    }),
5446                    fdecl::Use::Directory(fdecl::UseDirectory {
5447                        dependency_type: Some(fdecl::DependencyType::Strong),
5448                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5449                        source_name: Some("crow".to_string()),
5450                        target_path: Some("/bar".to_string()),
5451                        rights: Some(fio::Operations::CONNECT),
5452                        subdir: None,
5453                        ..Default::default()
5454                    }),
5455                ]);
5456                decl
5457            },
5458            result = Err(ErrorList::new(vec![
5459                Error::duplicate_field(DeclType::UseDirectory, "target_path", "/bar"),
5460                Error::duplicate_field(DeclType::UseProtocol, "target_path", "/bar"),
5461            ])),
5462        },
5463        // exposes
5464        test_validate_exposes_empty => {
5465            input = {
5466                let mut decl = new_component_decl();
5467                decl.exposes = Some(vec![
5468                    fdecl::Expose::Service(fdecl::ExposeService {
5469                        source: None,
5470                        source_name: None,
5471                        target_name: None,
5472                        target: None,
5473                        ..Default::default()
5474                    }),
5475                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5476                        source: None,
5477                        source_name: None,
5478                        target_name: None,
5479                        target: None,
5480                        ..Default::default()
5481                    }),
5482                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5483                        source: None,
5484                        source_name: None,
5485                        target_name: None,
5486                        target: None,
5487                        rights: None,
5488                        subdir: None,
5489                        ..Default::default()
5490                    }),
5491                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5492                        source: None,
5493                        source_name: None,
5494                        target: None,
5495                        target_name: None,
5496                        ..Default::default()
5497                    }),
5498                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5499                        source: None,
5500                        source_name: None,
5501                        target: None,
5502                        target_name: None,
5503                        ..Default::default()
5504                    }),
5505                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5506                        ..Default::default()
5507                    }),
5508                ]);
5509                decl
5510            },
5511            result = Err(ErrorList::new(vec![
5512                Error::missing_field(DeclType::ExposeService, "source"),
5513                Error::missing_field(DeclType::ExposeService, "target"),
5514                Error::missing_field(DeclType::ExposeService, "source_name"),
5515                Error::missing_field(DeclType::ExposeService, "target_name"),
5516                Error::missing_field(DeclType::ExposeProtocol, "source"),
5517                Error::missing_field(DeclType::ExposeProtocol, "target"),
5518                Error::missing_field(DeclType::ExposeProtocol, "source_name"),
5519                Error::missing_field(DeclType::ExposeProtocol, "target_name"),
5520                Error::missing_field(DeclType::ExposeDirectory, "source"),
5521                Error::missing_field(DeclType::ExposeDirectory, "target"),
5522                Error::missing_field(DeclType::ExposeDirectory, "source_name"),
5523                Error::missing_field(DeclType::ExposeDirectory, "target_name"),
5524                Error::missing_field(DeclType::ExposeRunner, "source"),
5525                Error::missing_field(DeclType::ExposeRunner, "target"),
5526                Error::missing_field(DeclType::ExposeRunner, "source_name"),
5527                Error::missing_field(DeclType::ExposeRunner, "target_name"),
5528                Error::missing_field(DeclType::ExposeResolver, "source"),
5529                Error::missing_field(DeclType::ExposeResolver, "target"),
5530                Error::missing_field(DeclType::ExposeResolver, "source_name"),
5531                Error::missing_field(DeclType::ExposeResolver, "target_name"),
5532                Error::missing_field(DeclType::ExposeDictionary, "source"),
5533                Error::missing_field(DeclType::ExposeDictionary, "target"),
5534                Error::missing_field(DeclType::ExposeDictionary, "source_name"),
5535                Error::missing_field(DeclType::ExposeDictionary, "target_name"),
5536            ])),
5537        },
5538        test_validate_exposes_extraneous => {
5539            input = {
5540                let mut decl = new_component_decl();
5541                decl.exposes = Some(vec![
5542                    fdecl::Expose::Service(fdecl::ExposeService {
5543                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5544                            name: "logger".to_string(),
5545                            collection: Some("modular".to_string()),
5546                        })),
5547                        source_name: Some("logger".to_string()),
5548                        target_name: Some("logger".to_string()),
5549                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5550                        ..Default::default()
5551                    }),
5552                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5553                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5554                            name: "logger".to_string(),
5555                            collection: Some("modular".to_string()),
5556                        })),
5557                        source_name: Some("legacy_logger".to_string()),
5558                        target_name: Some("legacy_logger".to_string()),
5559                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5560                        ..Default::default()
5561                    }),
5562                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5563                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5564                            name: "netstack".to_string(),
5565                            collection: Some("modular".to_string()),
5566                        })),
5567                        source_name: Some("data".to_string()),
5568                        target_name: Some("data".to_string()),
5569                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5570                        rights: Some(fio::Operations::CONNECT),
5571                        subdir: None,
5572                        ..Default::default()
5573                    }),
5574                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5575                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5576                            name: "netstack".to_string(),
5577                            collection: Some("modular".to_string()),
5578                        })),
5579                        source_name: Some("elf".to_string()),
5580                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5581                        target_name: Some("elf".to_string()),
5582                        ..Default::default()
5583                    }),
5584                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5585                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5586                            name: "netstack".to_string(),
5587                            collection: Some("modular".to_string()),
5588                        })),
5589                        source_name: Some("pkg".to_string()),
5590                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5591                        target_name: Some("pkg".to_string()),
5592                        ..Default::default()
5593                    }),
5594                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5595                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5596                            name: "netstack".to_string(),
5597                            collection: Some("modular".to_string()),
5598                        })),
5599                        source_name: Some("dict".to_string()),
5600                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5601                        target_name: Some("dict".to_string()),
5602                        ..Default::default()
5603                    }),
5604                ]);
5605                decl
5606            },
5607            result = Err(ErrorList::new(vec![
5608                Error::extraneous_field(DeclType::ExposeService, "source.child.collection"),
5609                Error::extraneous_field(DeclType::ExposeProtocol, "source.child.collection"),
5610                Error::extraneous_field(DeclType::ExposeDirectory, "source.child.collection"),
5611                Error::extraneous_field(DeclType::ExposeRunner, "source.child.collection"),
5612                Error::extraneous_field(DeclType::ExposeResolver, "source.child.collection"),
5613                Error::extraneous_field(DeclType::ExposeDictionary, "source.child.collection"),
5614            ])),
5615        },
5616        test_validate_exposes_invalid_identifiers => {
5617            input = {
5618                let mut decl = new_component_decl();
5619                decl.exposes = Some(vec![
5620                    fdecl::Expose::Service(fdecl::ExposeService {
5621                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5622                            name: "^bad".to_string(),
5623                            collection: None,
5624                        })),
5625                        source_name: Some("foo/".to_string()),
5626                        target_name: Some("/".to_string()),
5627                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5628                        ..Default::default()
5629                    }),
5630                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5631                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5632                            name: "^bad".to_string(),
5633                            collection: None,
5634                        })),
5635                        source_name: Some("foo/".to_string()),
5636                        target_name: Some("/".to_string()),
5637                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5638                        ..Default::default()
5639                    }),
5640                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5641                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5642                            name: "^bad".to_string(),
5643                            collection: None,
5644                        })),
5645                        source_name: Some("foo/".to_string()),
5646                        target_name: Some("/".to_string()),
5647                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5648                        rights: Some(fio::Operations::CONNECT),
5649                        subdir: Some("/foo".to_string()),
5650                        ..Default::default()
5651                    }),
5652                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5653                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5654                            name: "^bad".to_string(),
5655                            collection: None,
5656                        })),
5657                        source_name: Some("/path".to_string()),
5658                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5659                        target_name: Some("elf!".to_string()),
5660                        ..Default::default()
5661                    }),
5662                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5663                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5664                            name: "^bad".to_string(),
5665                            collection: None,
5666                        })),
5667                        source_name: Some("/path".to_string()),
5668                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5669                        target_name: Some("pkg!".to_string()),
5670                        ..Default::default()
5671                    }),
5672                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5673                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5674                            name: "^bad".to_string(),
5675                            collection: None,
5676                        })),
5677                        source_name: Some("/path".to_string()),
5678                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5679                        target_name: Some("pkg!".to_string()),
5680                        ..Default::default()
5681                    }),
5682                ]);
5683                decl
5684            },
5685            result = Err(ErrorList::new(vec![
5686                Error::invalid_field(DeclType::ExposeService, "source.child.name"),
5687                Error::invalid_field(DeclType::ExposeService, "source_name"),
5688                Error::invalid_field(DeclType::ExposeService, "target_name"),
5689                Error::invalid_field(DeclType::ExposeProtocol, "source.child.name"),
5690                Error::invalid_field(DeclType::ExposeProtocol, "source_name"),
5691                Error::invalid_field(DeclType::ExposeProtocol, "target_name"),
5692                Error::invalid_field(DeclType::ExposeDirectory, "source.child.name"),
5693                Error::invalid_field(DeclType::ExposeDirectory, "source_name"),
5694                Error::invalid_field(DeclType::ExposeDirectory, "target_name"),
5695                Error::invalid_field(DeclType::ExposeDirectory, "subdir"),
5696                Error::invalid_field(DeclType::ExposeRunner, "source.child.name"),
5697                Error::invalid_field(DeclType::ExposeRunner, "source_name"),
5698                Error::invalid_field(DeclType::ExposeRunner, "target_name"),
5699                Error::invalid_field(DeclType::ExposeResolver, "source.child.name"),
5700                Error::invalid_field(DeclType::ExposeResolver, "source_name"),
5701                Error::invalid_field(DeclType::ExposeResolver, "target_name"),
5702                Error::invalid_field(DeclType::ExposeDictionary, "source.child.name"),
5703                Error::invalid_field(DeclType::ExposeDictionary, "source_name"),
5704                Error::invalid_field(DeclType::ExposeDictionary, "target_name"),
5705            ])),
5706        },
5707        test_validate_exposes_invalid_source_target => {
5708            input = {
5709                let mut decl = new_component_decl();
5710                decl.children = Some(vec![fdecl::Child{
5711                    name: Some("logger".to_string()),
5712                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5713                    startup: Some(fdecl::StartupMode::Lazy),
5714                    on_terminate: None,
5715                    environment: None,
5716                    ..Default::default()
5717                }]);
5718                decl.exposes = Some(vec![
5719                    fdecl::Expose::Service(fdecl::ExposeService {
5720                        source: None,
5721                        source_name: Some("a".to_string()),
5722                        target_name: Some("b".to_string()),
5723                        target: None,
5724                        ..Default::default()
5725                    }),
5726                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5727                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5728                        source_name: Some("c".to_string()),
5729                        target_name: Some("d".to_string()),
5730                        target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5731                        ..Default::default()
5732                    }),
5733                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5734                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "z".to_string()})),
5735                        source_name: Some("e".to_string()),
5736                        target_name: Some("f".to_string()),
5737                        target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "z".to_string()})),
5738                        rights: Some(fio::Operations::CONNECT),
5739                        subdir: None,
5740                        ..Default::default()
5741                    }),
5742                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5743                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5744                        source_name: Some("g".to_string()),
5745                        target_name: Some("h".to_string()),
5746                        target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5747                        rights: Some(fio::Operations::CONNECT),
5748                        subdir: None,
5749                        ..Default::default()
5750                    }),
5751                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5752                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5753                        source_name: Some("i".to_string()),
5754                        target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5755                        target_name: Some("j".to_string()),
5756                        ..Default::default()
5757                    }),
5758                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5759                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5760                        source_name: Some("k".to_string()),
5761                        target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5762                        target_name: Some("l".to_string()),
5763                        ..Default::default()
5764                    }),
5765                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5766                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5767                            name: "logger".to_string(),
5768                            collection: None,
5769                        })),
5770                        source_name: Some("m".to_string()),
5771                        target_name: Some("n".to_string()),
5772                        target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5773                        ..Default::default()
5774                    }),
5775                ]);
5776                decl
5777            },
5778            result = Err(ErrorList::new(vec![
5779                Error::missing_field(DeclType::ExposeService, "source"),
5780                Error::missing_field(DeclType::ExposeService, "target"),
5781                Error::invalid_field(DeclType::ExposeProtocol, "source"),
5782                Error::invalid_field(DeclType::ExposeProtocol, "target"),
5783                Error::invalid_field(DeclType::ExposeDirectory, "source"),
5784                Error::invalid_field(DeclType::ExposeDirectory, "target"),
5785                Error::invalid_field(DeclType::ExposeDirectory, "source"),
5786                Error::invalid_field(DeclType::ExposeDirectory, "target"),
5787                Error::invalid_field(DeclType::ExposeRunner, "source"),
5788                Error::invalid_field(DeclType::ExposeRunner, "target"),
5789                Error::invalid_field(DeclType::ExposeResolver, "source"),
5790                Error::invalid_field(DeclType::ExposeResolver, "target"),
5791                Error::invalid_field(DeclType::ExposeDictionary, "target"),
5792            ])),
5793        },
5794        test_validate_exposes_invalid_source_collection => {
5795            input = {
5796                let mut decl = new_component_decl();
5797                decl.collections = Some(vec![fdecl::Collection{
5798                    name: Some("col".to_string()),
5799                    durability: Some(fdecl::Durability::Transient),
5800                    allowed_offers: None,
5801                    allow_long_names: None,
5802                    ..Default::default()
5803                }]);
5804                decl.exposes = Some(vec![
5805                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5806                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
5807                        source_name: Some("a".to_string()),
5808                        target_name: Some("a".to_string()),
5809                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5810                        ..Default::default()
5811                    }),
5812                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5813                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5814                        source_name: Some("b".to_string()),
5815                        target_name: Some("b".to_string()),
5816                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5817                        rights: Some(fio::Operations::CONNECT),
5818                        subdir: None,
5819                        ..Default::default()
5820                    }),
5821                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5822                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5823                        source_name: Some("c".to_string()),
5824                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5825                        target_name: Some("c".to_string()),
5826                        ..Default::default()
5827                    }),
5828                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5829                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5830                        source_name: Some("d".to_string()),
5831                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5832                        target_name: Some("d".to_string()),
5833                        ..Default::default()
5834                    }),
5835                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5836                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5837                        source_name: Some("e".to_string()),
5838                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5839                        target_name: Some("e".to_string()),
5840                        ..Default::default()
5841                    }),
5842                ]);
5843                decl
5844            },
5845            result = Err(ErrorList::new(vec![
5846                Error::invalid_field(DeclType::ExposeProtocol, "source"),
5847                Error::invalid_field(DeclType::ExposeDirectory, "source"),
5848                Error::invalid_field(DeclType::ExposeRunner, "source"),
5849                Error::invalid_field(DeclType::ExposeResolver, "source"),
5850                Error::invalid_field(DeclType::ExposeDictionary, "source"),
5851            ])),
5852        },
5853        test_validate_exposes_sources_collection => {
5854            input = {
5855                let mut decl = new_component_decl();
5856                decl.collections = Some(vec![
5857                    fdecl::Collection {
5858                        name: Some("col".to_string()),
5859                        durability: Some(fdecl::Durability::Transient),
5860                        allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
5861                        allow_long_names: None,
5862                        ..Default::default()
5863                    }
5864                ]);
5865                decl.exposes = Some(vec![
5866                    fdecl::Expose::Service(fdecl::ExposeService {
5867                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
5868                        source_name: Some("a".to_string()),
5869                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5870                        target_name: Some("a".to_string()),
5871                        ..Default::default()
5872                    })
5873                ]);
5874                decl
5875            },
5876            result = Ok(()),
5877        },
5878        test_validate_exposes_long_identifiers => {
5879            input = {
5880                let mut decl = new_component_decl();
5881                decl.exposes = Some(vec![
5882                    fdecl::Expose::Service(fdecl::ExposeService {
5883                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5884                            name: "b".repeat(256),
5885                            collection: None,
5886                        })),
5887                        source_name: Some(format!("{}", "a".repeat(1025))),
5888                        target_name: Some(format!("{}", "b".repeat(1025))),
5889                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5890                        ..Default::default()
5891                    }),
5892                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5893                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5894                            name: "b".repeat(256),
5895                            collection: None,
5896                        })),
5897                        source_name: Some(format!("{}", "a".repeat(256))),
5898                        target_name: Some(format!("{}", "b".repeat(256))),
5899                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5900                        ..Default::default()
5901                    }),
5902                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5903                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5904                            name: "b".repeat(256),
5905                            collection: None,
5906                        })),
5907                        source_name: Some(format!("{}", "a".repeat(256))),
5908                        target_name: Some(format!("{}", "b".repeat(256))),
5909                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5910                        rights: Some(fio::Operations::CONNECT),
5911                        subdir: None,
5912                        ..Default::default()
5913                    }),
5914                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5915                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5916                            name: "b".repeat(256),
5917                            collection: None,
5918                        })),
5919                        source_name: Some("a".repeat(256)),
5920                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5921                        target_name: Some("b".repeat(256)),
5922                        ..Default::default()
5923                    }),
5924                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5925                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5926                            name: "b".repeat(256),
5927                            collection: None,
5928                        })),
5929                        source_name: Some("a".repeat(256)),
5930                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5931                        target_name: Some("b".repeat(256)),
5932                        ..Default::default()
5933                    }),
5934                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5935                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5936                            name: "b".repeat(256),
5937                            collection: None,
5938                        })),
5939                        source_name: Some("a".repeat(256)),
5940                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5941                        target_name: Some("b".repeat(256)),
5942                        ..Default::default()
5943                    }),
5944                ]);
5945                decl
5946            },
5947            result = Err(ErrorList::new(vec![
5948                Error::field_too_long(DeclType::ExposeService, "source.child.name"),
5949                Error::field_too_long(DeclType::ExposeService, "source_name"),
5950                Error::field_too_long(DeclType::ExposeService, "target_name"),
5951                Error::field_too_long(DeclType::ExposeProtocol, "source.child.name"),
5952                Error::field_too_long(DeclType::ExposeProtocol, "source_name"),
5953                Error::field_too_long(DeclType::ExposeProtocol, "target_name"),
5954                Error::field_too_long(DeclType::ExposeDirectory, "source.child.name"),
5955                Error::field_too_long(DeclType::ExposeDirectory, "source_name"),
5956                Error::field_too_long(DeclType::ExposeDirectory, "target_name"),
5957                Error::field_too_long(DeclType::ExposeRunner, "source.child.name"),
5958                Error::field_too_long(DeclType::ExposeRunner, "source_name"),
5959                Error::field_too_long(DeclType::ExposeRunner, "target_name"),
5960                Error::field_too_long(DeclType::ExposeResolver, "source.child.name"),
5961                Error::field_too_long(DeclType::ExposeResolver, "source_name"),
5962                Error::field_too_long(DeclType::ExposeResolver, "target_name"),
5963                Error::field_too_long(DeclType::ExposeDictionary, "source.child.name"),
5964                Error::field_too_long(DeclType::ExposeDictionary, "source_name"),
5965                Error::field_too_long(DeclType::ExposeDictionary, "target_name"),
5966            ])),
5967        },
5968        test_validate_exposes_invalid_child => {
5969            input = {
5970                let mut decl = new_component_decl();
5971                decl.exposes = Some(vec![
5972                    fdecl::Expose::Service(fdecl::ExposeService {
5973                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5974                            name: "netstack".to_string(),
5975                            collection: None,
5976                        })),
5977                        source_name: Some("fuchsia.logger.Log".to_string()),
5978                        target_name: Some("fuchsia.logger.Log".to_string()),
5979                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5980                        ..Default::default()
5981                    }),
5982                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5983                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5984                            name: "netstack".to_string(),
5985                            collection: None,
5986                        })),
5987                        source_name: Some("fuchsia.logger.LegacyLog".to_string()),
5988                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
5989                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5990                        ..Default::default()
5991                    }),
5992                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5993                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5994                            name: "netstack".to_string(),
5995                            collection: None,
5996                        })),
5997                        source_name: Some("data".to_string()),
5998                        target_name: Some("data".to_string()),
5999                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6000                        rights: Some(fio::Operations::CONNECT),
6001                        subdir: None,
6002                        ..Default::default()
6003                    }),
6004                    fdecl::Expose::Runner(fdecl::ExposeRunner {
6005                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6006                            name: "netstack".to_string(),
6007                            collection: None,
6008                        })),
6009                        source_name: Some("elf".to_string()),
6010                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6011                        target_name: Some("elf".to_string()),
6012                        ..Default::default()
6013                    }),
6014                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
6015                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6016                            name: "netstack".to_string(),
6017                            collection: None,
6018                        })),
6019                        source_name: Some("pkg".to_string()),
6020                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6021                        target_name: Some("pkg".to_string()),
6022                        ..Default::default()
6023                    }),
6024                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
6025                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6026                            name: "netstack".to_string(),
6027                            collection: None,
6028                        })),
6029                        source_name: Some("dict".to_string()),
6030                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6031                        target_name: Some("dict".to_string()),
6032                        ..Default::default()
6033                    }),
6034                ]);
6035                decl
6036            },
6037            result = Err(ErrorList::new(vec![
6038                Error::invalid_child(DeclType::ExposeService, "source", "netstack"),
6039                Error::invalid_child(DeclType::ExposeProtocol, "source", "netstack"),
6040                Error::invalid_child(DeclType::ExposeDirectory, "source", "netstack"),
6041                Error::invalid_child(DeclType::ExposeRunner, "source", "netstack"),
6042                Error::invalid_child(DeclType::ExposeResolver, "source", "netstack"),
6043                Error::invalid_child(DeclType::ExposeDictionary, "source", "netstack"),
6044            ])),
6045        },
6046        test_validate_exposes_invalid_source_capability => {
6047            input = {
6048                fdecl::Component {
6049                    exposes: Some(vec![
6050                        fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6051                            source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
6052                                name: "this-storage-doesnt-exist".to_string(),
6053                            })),
6054                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
6055                            target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
6056                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6057                            ..Default::default()
6058                        }),
6059                    ]),
6060                    ..new_component_decl()
6061                }
6062            },
6063            result = Err(ErrorList::new(vec![
6064                Error::invalid_capability(DeclType::ExposeProtocol, "source", "this-storage-doesnt-exist"),
6065            ])),
6066        },
6067        test_validate_exposes_duplicate_target => {
6068            input = {
6069                let mut decl = new_component_decl();
6070                decl.exposes = Some(vec![
6071                    fdecl::Expose::Service(fdecl::ExposeService {
6072                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6073                            name: "coll".into(),
6074                        })),
6075                        source_name: Some("netstack".to_string()),
6076                        target_name: Some("fuchsia.net.Stack".to_string()),
6077                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6078                        ..Default::default()
6079                    }),
6080                    fdecl::Expose::Service(fdecl::ExposeService {
6081                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6082                            name: "coll2".into(),
6083                        })),
6084                        source_name: Some("netstack2".to_string()),
6085                        target_name: Some("fuchsia.net.Stack".to_string()),
6086                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6087                        ..Default::default()
6088                    }),
6089                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6090                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6091                        source_name: Some("fonts".to_string()),
6092                        target_name: Some("fuchsia.fonts.Provider".to_string()),
6093                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6094                        ..Default::default()
6095                    }),
6096                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6097                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6098                        source_name: Some("fonts2".to_string()),
6099                        target_name: Some("fuchsia.fonts.Provider".to_string()),
6100                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6101                        ..Default::default()
6102                    }),
6103                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
6104                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6105                        source_name: Some("assets".to_string()),
6106                        target_name: Some("stuff".to_string()),
6107                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6108                        rights: None,
6109                        subdir: None,
6110                        ..Default::default()
6111                    }),
6112                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
6113                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6114                        source_name: Some("assets2".to_string()),
6115                        target_name: Some("stuff".to_string()),
6116                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6117                        rights: None,
6118                        subdir: None,
6119                        ..Default::default()
6120                    }),
6121                    fdecl::Expose::Runner(fdecl::ExposeRunner {
6122                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6123                        source_name: Some("source_elf".to_string()),
6124                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6125                        target_name: Some("elf".to_string()),
6126                        ..Default::default()
6127                    }),
6128                    fdecl::Expose::Runner(fdecl::ExposeRunner {
6129                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6130                        source_name: Some("source_elf".to_string()),
6131                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6132                        target_name: Some("elf".to_string()),
6133                        ..Default::default()
6134                    }),
6135                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
6136                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6137                        source_name: Some("source_pkg".to_string()),
6138                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6139                        target_name: Some("pkg".to_string()),
6140                        ..Default::default()
6141                    }),
6142                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
6143                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6144                        source_name: Some("source_pkg".to_string()),
6145                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6146                        target_name: Some("pkg".to_string()),
6147                        ..Default::default()
6148                    }),
6149                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
6150                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6151                        source_name: Some("source_dict".to_string()),
6152                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6153                        target_name: Some("dict".to_string()),
6154                        ..Default::default()
6155                    }),
6156                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
6157                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6158                        source_name: Some("source_dict".to_string()),
6159                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6160                        target_name: Some("dict".to_string()),
6161                        ..Default::default()
6162                    }),
6163                ]);
6164                decl.collections = Some(vec![
6165                    fdecl::Collection {
6166                        name: Some("coll".into()),
6167                        durability: Some(fdecl::Durability::Transient),
6168                        ..Default::default()
6169                    },
6170                    fdecl::Collection {
6171                        name: Some("coll2".into()),
6172                        durability: Some(fdecl::Durability::Transient),
6173                        ..Default::default()
6174                    },
6175                ]);
6176                decl.capabilities = Some(vec![
6177                    fdecl::Capability::Service(fdecl::Service {
6178                        name: Some("netstack".to_string()),
6179                        source_path: Some("/path".to_string()),
6180                        ..Default::default()
6181                    }),
6182                    fdecl::Capability::Service(fdecl::Service {
6183                        name: Some("netstack2".to_string()),
6184                        source_path: Some("/path".to_string()),
6185                        ..Default::default()
6186                    }),
6187                    fdecl::Capability::Protocol(fdecl::Protocol {
6188                        name: Some("fonts".to_string()),
6189                        source_path: Some("/path".to_string()),
6190                        ..Default::default()
6191                    }),
6192                    fdecl::Capability::Protocol(fdecl::Protocol {
6193                        name: Some("fonts2".to_string()),
6194                        source_path: Some("/path".to_string()),
6195                        ..Default::default()
6196                    }),
6197                    fdecl::Capability::Directory(fdecl::Directory {
6198                        name: Some("assets".to_string()),
6199                        source_path: Some("/path".to_string()),
6200                        rights: Some(fio::Operations::CONNECT),
6201                        ..Default::default()
6202                    }),
6203                    fdecl::Capability::Directory(fdecl::Directory {
6204                        name: Some("assets2".to_string()),
6205                        source_path: Some("/path".to_string()),
6206                        rights: Some(fio::Operations::CONNECT),
6207                        ..Default::default()
6208                    }),
6209                    fdecl::Capability::Runner(fdecl::Runner {
6210                        name: Some("source_elf".to_string()),
6211                        source_path: Some("/path".to_string()),
6212                        ..Default::default()
6213                    }),
6214                    fdecl::Capability::Resolver(fdecl::Resolver {
6215                        name: Some("source_pkg".to_string()),
6216                        source_path: Some("/path".to_string()),
6217                        ..Default::default()
6218                    }),
6219                    fdecl::Capability::Dictionary(fdecl::Dictionary {
6220                        name: Some("source_dict".to_string()),
6221                        ..Default::default()
6222                    }),
6223                ]);
6224                decl
6225            },
6226            result = Err(ErrorList::new(vec![
6227                // Duplicate services are allowed.
6228                Error::duplicate_field(DeclType::ExposeProtocol, "target_name",
6229                                    "fuchsia.fonts.Provider"),
6230                Error::duplicate_field(DeclType::ExposeDirectory, "target_name",
6231                                    "stuff"),
6232                Error::duplicate_field(DeclType::ExposeRunner, "target_name",
6233                                    "elf"),
6234                Error::duplicate_field(DeclType::ExposeResolver, "target_name", "pkg"),
6235                Error::duplicate_field(DeclType::ExposeDictionary, "target_name", "dict"),
6236            ])),
6237        },
6238        test_validate_exposes_invalid_capability_from_self => {
6239            input = {
6240                let mut decl = new_component_decl();
6241                decl.exposes = Some(vec![
6242                    fdecl::Expose::Service(fdecl::ExposeService {
6243                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6244                        source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
6245                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6246                        target_name: Some("foo".to_string()),
6247                        ..Default::default()
6248                    }),
6249                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6250                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6251                        source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
6252                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6253                        target_name: Some("bar".to_string()),
6254                        ..Default::default()
6255                    }),
6256                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
6257                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6258                        source_name: Some("dir".to_string()),
6259                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6260                        target_name: Some("assets".to_string()),
6261                        rights: None,
6262                        subdir: None,
6263                        ..Default::default()
6264                    }),
6265                    fdecl::Expose::Runner(fdecl::ExposeRunner {
6266                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6267                        source_name: Some("source_elf".to_string()),
6268                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6269                        target_name: Some("elf".to_string()),
6270                        ..Default::default()
6271                    }),
6272                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
6273                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6274                        source_name: Some("source_pkg".to_string()),
6275                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6276                        target_name: Some("pkg".to_string()),
6277                        ..Default::default()
6278                    }),
6279                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
6280                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6281                        source_name: Some("source_dict".to_string()),
6282                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6283                        target_name: Some("dict".to_string()),
6284                        ..Default::default()
6285                    }),
6286                    fdecl::Expose::Config(fdecl::ExposeConfiguration {
6287                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6288                        source_name: Some("source_config".to_string()),
6289                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6290                        target_name: Some("config".to_string()),
6291                        ..Default::default()
6292                    }),
6293                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6294                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6295                        source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
6296                        source_dictionary: Some("dict/inner".to_string()),
6297                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6298                        target_name: Some("baz".to_string()),
6299                        ..Default::default()
6300                    }),
6301                ]);
6302                decl
6303            },
6304            result = Err(ErrorList::new(vec![
6305                Error::invalid_capability(
6306                    DeclType::ExposeService,
6307                    "source",
6308                    "fuchsia.some.library.SomeProtocol"),
6309                Error::invalid_capability(
6310                    DeclType::ExposeProtocol,
6311                    "source",
6312                    "fuchsia.some.library.SomeProtocol"),
6313                Error::invalid_capability(DeclType::ExposeDirectory, "source", "dir"),
6314                Error::invalid_capability(DeclType::ExposeRunner, "source", "source_elf"),
6315                Error::invalid_capability(DeclType::ExposeResolver, "source", "source_pkg"),
6316                Error::invalid_capability(DeclType::ExposeDictionary, "source", "source_dict"),
6317                Error::invalid_capability(DeclType::ExposeConfig, "source", "source_config"),
6318                Error::invalid_capability(DeclType::ExposeProtocol, "source", "dict"),
6319            ])),
6320        },
6321
6322        test_validate_exposes_availability_service => {
6323            input = {
6324                let mut decl = generate_expose_different_source_and_availability_decl(
6325                    |source, availability, target_name|
6326                        fdecl::Expose::Service(fdecl::ExposeService {
6327                            source: Some(source),
6328                            source_name: Some("fuchsia.examples.Echo".to_string()),
6329                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6330                            target_name: Some(target_name.to_string()),
6331                            availability: Some(availability),
6332                            ..Default::default()
6333                        })
6334                );
6335                decl.capabilities = Some(vec![
6336                    fdecl::Capability::Service(fdecl::Service {
6337                        name: Some("fuchsia.examples.Echo".to_string()),
6338                        source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
6339                        ..Default::default()
6340                    }),
6341                ]);
6342                decl
6343            },
6344            result = {
6345                Err(ErrorList::new(vec![
6346                    Error::availability_must_be_optional(
6347                        DeclType::ExposeService,
6348                        "availability",
6349                        Some(&"fuchsia.examples.Echo".to_string()),
6350                    ),
6351                    Error::availability_must_be_optional(
6352                        DeclType::ExposeService,
6353                        "availability",
6354                        Some(&"fuchsia.examples.Echo".to_string()),
6355                    ),
6356                ]))
6357            },
6358        },
6359        test_validate_exposes_availability_protocol => {
6360            input = {
6361                let mut decl = generate_expose_different_source_and_availability_decl(
6362                    |source, availability, target_name|
6363                        fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6364                            source: Some(source),
6365                            source_name: Some("fuchsia.examples.Echo".to_string()),
6366                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6367                            target_name: Some(target_name.to_string()),
6368                            availability: Some(availability),
6369                            ..Default::default()
6370                        })
6371                );
6372                decl.capabilities = Some(vec![
6373                    fdecl::Capability::Protocol(fdecl::Protocol {
6374                        name: Some("fuchsia.examples.Echo".to_string()),
6375                        source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
6376                        ..Default::default()
6377                    }),
6378                ]);
6379                decl
6380            },
6381            result = {
6382                Err(ErrorList::new(vec![
6383                    Error::availability_must_be_optional(
6384                        DeclType::ExposeProtocol,
6385                        "availability",
6386                        Some(&"fuchsia.examples.Echo".to_string()),
6387                    ),
6388                    Error::availability_must_be_optional(
6389                        DeclType::ExposeProtocol,
6390                        "availability",
6391                        Some(&"fuchsia.examples.Echo".to_string()),
6392                    ),
6393                ]))
6394            },
6395        },
6396        test_validate_exposes_availability_directory => {
6397            input = {
6398                let mut decl = generate_expose_different_source_and_availability_decl(
6399                    |source, availability, target_name|
6400                        fdecl::Expose::Directory(fdecl::ExposeDirectory {
6401                            source: Some(source),
6402                            source_name: Some("fuchsia.examples.Echo".to_string()),
6403                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6404                            target_name: Some(target_name.to_string()),
6405                            availability: Some(availability),
6406                            ..Default::default()
6407                        })
6408                );
6409                decl.capabilities = Some(vec![
6410                    fdecl::Capability::Directory(fdecl::Directory {
6411                        name: Some("fuchsia.examples.Echo".to_string()),
6412                        source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
6413                        rights: Some(fio::Operations::READ_BYTES),
6414                        ..Default::default()
6415                    }),
6416                ]);
6417                decl
6418            },
6419            result = {
6420                Err(ErrorList::new(vec![
6421                    Error::availability_must_be_optional(
6422                        DeclType::ExposeDirectory,
6423                        "availability",
6424                        Some(&"fuchsia.examples.Echo".to_string()),
6425                    ),
6426                    Error::availability_must_be_optional(
6427                        DeclType::ExposeDirectory,
6428                        "availability",
6429                        Some(&"fuchsia.examples.Echo".to_string()),
6430                    ),
6431                ]))
6432            },
6433        },
6434
6435        // offers
6436        test_validate_offers_empty => {
6437            input = {
6438                let mut decl = new_component_decl();
6439                decl.offers = Some(vec![
6440                    fdecl::Offer::Service(fdecl::OfferService {
6441                        source: None,
6442                        source_name: None,
6443                        target: None,
6444                        target_name: None,
6445                        ..Default::default()
6446                    }),
6447                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6448                        source: None,
6449                        source_name: None,
6450                        target: None,
6451                        target_name: None,
6452                        dependency_type: None,
6453                        ..Default::default()
6454                    }),
6455                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6456                        source: None,
6457                        source_name: None,
6458                        target: None,
6459                        target_name: None,
6460                        rights: None,
6461                        subdir: None,
6462                        dependency_type: None,
6463                        ..Default::default()
6464                    }),
6465                    fdecl::Offer::Storage(fdecl::OfferStorage {
6466                        source_name: None,
6467                        source: None,
6468                        target: None,
6469                        target_name: None,
6470                        ..Default::default()
6471                    }),
6472                    fdecl::Offer::Runner(fdecl::OfferRunner {
6473                        source: None,
6474                        source_name: None,
6475                        target: None,
6476                        target_name: None,
6477                        ..Default::default()
6478                    }),
6479                    fdecl::Offer::Resolver(fdecl::OfferResolver {
6480                        ..Default::default()
6481                    }),
6482                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6483                        ..Default::default()
6484                    }),
6485                ]);
6486                decl
6487            },
6488            // TODO(dgonyeo): we need to handle the availability being unset until we've soft
6489            // migrated all manifests
6490            result = Err(ErrorList::new(vec![
6491                Error::missing_field(DeclType::OfferService, "source"),
6492                Error::missing_field(DeclType::OfferService, "source_name"),
6493                Error::missing_field(DeclType::OfferService, "target"),
6494                Error::missing_field(DeclType::OfferService, "target_name"),
6495                //Error::missing_field(DeclType::OfferService, "availability"),
6496                Error::missing_field(DeclType::OfferProtocol, "source"),
6497                Error::missing_field(DeclType::OfferProtocol, "source_name"),
6498                Error::missing_field(DeclType::OfferProtocol, "target"),
6499                Error::missing_field(DeclType::OfferProtocol, "target_name"),
6500                Error::missing_field(DeclType::OfferProtocol, "dependency_type"),
6501                //Error::missing_field(DeclType::OfferProtocol, "availability"),
6502                Error::missing_field(DeclType::OfferDirectory, "source"),
6503                Error::missing_field(DeclType::OfferDirectory, "source_name"),
6504                Error::missing_field(DeclType::OfferDirectory, "target"),
6505                Error::missing_field(DeclType::OfferDirectory, "target_name"),
6506                Error::missing_field(DeclType::OfferDirectory, "dependency_type"),
6507                //Error::missing_field(DeclType::OfferDirectory, "availability"),
6508                Error::missing_field(DeclType::OfferStorage, "source"),
6509                Error::missing_field(DeclType::OfferStorage, "source_name"),
6510                Error::missing_field(DeclType::OfferStorage, "target"),
6511                Error::missing_field(DeclType::OfferStorage, "target_name"),
6512                //Error::missing_field(DeclType::OfferStorage, "availability"),
6513                Error::missing_field(DeclType::OfferRunner, "source"),
6514                Error::missing_field(DeclType::OfferRunner, "source_name"),
6515                Error::missing_field(DeclType::OfferRunner, "target"),
6516                Error::missing_field(DeclType::OfferRunner, "target_name"),
6517                //Error::missing_field(DeclType::OfferRunner, "availability"),
6518                Error::missing_field(DeclType::OfferResolver, "source"),
6519                Error::missing_field(DeclType::OfferResolver, "source_name"),
6520                Error::missing_field(DeclType::OfferResolver, "target"),
6521                Error::missing_field(DeclType::OfferResolver, "target_name"),
6522                Error::missing_field(DeclType::OfferDictionary, "source"),
6523                Error::missing_field(DeclType::OfferDictionary, "source_name"),
6524                Error::missing_field(DeclType::OfferDictionary, "target"),
6525                Error::missing_field(DeclType::OfferDictionary, "target_name"),
6526                Error::missing_field(DeclType::OfferDictionary, "dependency_type"),
6527            ])),
6528        },
6529        test_validate_offers_long_identifiers => {
6530            input = {
6531                let mut decl = new_component_decl();
6532                decl.offers = Some(vec![
6533                    fdecl::Offer::Service(fdecl::OfferService {
6534                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6535                            name: "a".repeat(256),
6536                            collection: None,
6537                        })),
6538                        source_name: Some(format!("{}", "a".repeat(256))),
6539                        target: Some(fdecl::Ref::Child(
6540                        fdecl::ChildRef {
6541                            name: "b".repeat(256),
6542                            collection: None,
6543                        }
6544                        )),
6545                        target_name: Some(format!("{}", "b".repeat(256))),
6546                        ..Default::default()
6547                    }),
6548                    fdecl::Offer::Service(fdecl::OfferService {
6549                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6550                        source_name: Some("a".to_string()),
6551                        target: Some(fdecl::Ref::Collection(
6552                        fdecl::CollectionRef {
6553                            name: "b".repeat(256),
6554                        }
6555                        )),
6556                        target_name: Some(format!("{}", "b".repeat(256))),
6557                        ..Default::default()
6558                    }),
6559                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6560                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6561                            name: "a".repeat(256),
6562                            collection: None,
6563                        })),
6564                        source_name: Some(format!("{}", "a".repeat(256))),
6565                        target: Some(fdecl::Ref::Child(
6566                        fdecl::ChildRef {
6567                            name: "b".repeat(256),
6568                            collection: None,
6569                        }
6570                        )),
6571                        target_name: Some(format!("{}", "b".repeat(256))),
6572                        dependency_type: Some(fdecl::DependencyType::Strong),
6573                        ..Default::default()
6574                    }),
6575                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6576                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6577                        source_name: Some("a".to_string()),
6578                        target: Some(fdecl::Ref::Collection(
6579                        fdecl::CollectionRef {
6580                            name: "b".repeat(256),
6581                        }
6582                        )),
6583                        target_name: Some(format!("{}", "b".repeat(256))),
6584                        dependency_type: Some(fdecl::DependencyType::Weak),
6585                        ..Default::default()
6586                    }),
6587                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6588                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6589                            name: "a".repeat(256),
6590                            collection: None,
6591                        })),
6592                        source_name: Some(format!("{}", "a".repeat(256))),
6593                        target: Some(fdecl::Ref::Child(
6594                        fdecl::ChildRef {
6595                            name: "b".repeat(256),
6596                            collection: None,
6597                        }
6598                        )),
6599                        target_name: Some(format!("{}", "b".repeat(256))),
6600                        rights: Some(fio::Operations::CONNECT),
6601                        subdir: None,
6602                        dependency_type: Some(fdecl::DependencyType::Strong),
6603                        ..Default::default()
6604                    }),
6605                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6606                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6607                        source_name: Some("a".to_string()),
6608                        target: Some(fdecl::Ref::Collection(
6609                        fdecl::CollectionRef {
6610                            name: "b".repeat(256),
6611                        }
6612                        )),
6613                        target_name: Some(format!("{}", "b".repeat(256))),
6614                        rights: Some(fio::Operations::CONNECT),
6615                        subdir: None,
6616                        dependency_type: Some(fdecl::DependencyType::Weak),
6617                        ..Default::default()
6618                    }),
6619                    fdecl::Offer::Storage(fdecl::OfferStorage {
6620                        source_name: Some("data".to_string()),
6621                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6622                        target: Some(fdecl::Ref::Child(
6623                            fdecl::ChildRef {
6624                                name: "b".repeat(256),
6625                                collection: None,
6626                            }
6627                        )),
6628                        target_name: Some("data".to_string()),
6629                        ..Default::default()
6630                    }),
6631                    fdecl::Offer::Storage(fdecl::OfferStorage {
6632                        source_name: Some("data".to_string()),
6633                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6634                        target: Some(fdecl::Ref::Collection(
6635                            fdecl::CollectionRef { name: "b".repeat(256) }
6636                        )),
6637                        target_name: Some("data".to_string()),
6638                        ..Default::default()
6639                    }),
6640                    fdecl::Offer::Runner(fdecl::OfferRunner {
6641                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6642                            name: "a".repeat(256),
6643                            collection: None,
6644                        })),
6645                        source_name: Some("b".repeat(256)),
6646                        target: Some(fdecl::Ref::Collection(
6647                        fdecl::CollectionRef {
6648                            name: "c".repeat(256),
6649                        }
6650                        )),
6651                        target_name: Some("d".repeat(256)),
6652                        ..Default::default()
6653                    }),
6654                    fdecl::Offer::Resolver(fdecl::OfferResolver {
6655                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6656                            name: "a".repeat(256),
6657                            collection: None,
6658                        })),
6659                        source_name: Some("b".repeat(256)),
6660                        target: Some(fdecl::Ref::Collection(
6661                            fdecl::CollectionRef {
6662                                name: "c".repeat(256),
6663                            }
6664                        )),
6665                        target_name: Some("d".repeat(256)),
6666                        ..Default::default()
6667                    }),
6668                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6669                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6670                            name: "a".repeat(256),
6671                            collection: None,
6672                        })),
6673                        source_name: Some("b".repeat(256)),
6674                        target: Some(fdecl::Ref::Collection(
6675                            fdecl::CollectionRef {
6676                                name: "c".repeat(256),
6677                            }
6678                        )),
6679                        target_name: Some("d".repeat(256)),
6680                        dependency_type: Some(fdecl::DependencyType::Strong),
6681                        ..Default::default()
6682                    }),
6683                ]);
6684                decl
6685            },
6686            result = Err(ErrorList::new(vec![
6687                Error::field_too_long(DeclType::OfferService, "source.child.name"),
6688                Error::field_too_long(DeclType::OfferService, "source_name"),
6689                Error::field_too_long(DeclType::OfferService, "target.child.name"),
6690                Error::field_too_long(DeclType::OfferService, "target_name"),
6691                Error::field_too_long(DeclType::OfferService, "target.collection.name"),
6692                Error::field_too_long(DeclType::OfferService, "target_name"),
6693                Error::field_too_long(DeclType::OfferProtocol, "source.child.name"),
6694                Error::field_too_long(DeclType::OfferProtocol, "source_name"),
6695                Error::field_too_long(DeclType::OfferProtocol, "target.child.name"),
6696                Error::field_too_long(DeclType::OfferProtocol, "target_name"),
6697                Error::field_too_long(DeclType::OfferProtocol, "target.collection.name"),
6698                Error::field_too_long(DeclType::OfferProtocol, "target_name"),
6699                Error::field_too_long(DeclType::OfferDirectory, "source.child.name"),
6700                Error::field_too_long(DeclType::OfferDirectory, "source_name"),
6701                Error::field_too_long(DeclType::OfferDirectory, "target.child.name"),
6702                Error::field_too_long(DeclType::OfferDirectory, "target_name"),
6703                Error::field_too_long(DeclType::OfferDirectory, "target.collection.name"),
6704                Error::field_too_long(DeclType::OfferDirectory, "target_name"),
6705                Error::field_too_long(DeclType::OfferStorage, "target.child.name"),
6706                Error::field_too_long(DeclType::OfferStorage, "target.collection.name"),
6707                Error::field_too_long(DeclType::OfferRunner, "source.child.name"),
6708                Error::field_too_long(DeclType::OfferRunner, "source_name"),
6709                Error::field_too_long(DeclType::OfferRunner, "target.collection.name"),
6710                Error::field_too_long(DeclType::OfferRunner, "target_name"),
6711                Error::field_too_long(DeclType::OfferResolver, "source.child.name"),
6712                Error::field_too_long(DeclType::OfferResolver, "source_name"),
6713                Error::field_too_long(DeclType::OfferResolver, "target.collection.name"),
6714                Error::field_too_long(DeclType::OfferResolver, "target_name"),
6715                Error::field_too_long(DeclType::OfferDictionary, "source.child.name"),
6716                Error::field_too_long(DeclType::OfferDictionary, "source_name"),
6717                Error::field_too_long(DeclType::OfferDictionary, "target.collection.name"),
6718                Error::field_too_long(DeclType::OfferDictionary, "target_name"),
6719            ])),
6720        },
6721        test_validate_offers_extraneous => {
6722            input = {
6723                let mut decl = new_component_decl();
6724                decl.offers = Some(vec![
6725                    fdecl::Offer::Service(fdecl::OfferService {
6726                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6727                            name: "logger".to_string(),
6728                            collection: Some("modular".to_string()),
6729                        })),
6730                        source_name: Some("fuchsia.logger.Log".to_string()),
6731                        target: Some(fdecl::Ref::Child(
6732                            fdecl::ChildRef {
6733                                name: "netstack".to_string(),
6734                                collection: Some("modular".to_string()),
6735                            }
6736                        )),
6737                        target_name: Some("fuchsia.logger.Log".to_string()),
6738                        ..Default::default()
6739                    }),
6740                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6741                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6742                            name: "logger".to_string(),
6743                            collection: Some("modular".to_string()),
6744                        })),
6745                        source_name: Some("fuchsia.logger.Log".to_string()),
6746                        target: Some(fdecl::Ref::Child(
6747                            fdecl::ChildRef {
6748                                name: "netstack".to_string(),
6749                                collection: Some("modular".to_string()),
6750                            }
6751                        )),
6752                        target_name: Some("fuchsia.logger.Log".to_string()),
6753                        dependency_type: Some(fdecl::DependencyType::Strong),
6754                        ..Default::default()
6755                    }),
6756                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6757                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6758                            name: "logger".to_string(),
6759                            collection: Some("modular".to_string()),
6760                        })),
6761                        source_name: Some("assets".to_string()),
6762                        target: Some(fdecl::Ref::Child(
6763                            fdecl::ChildRef {
6764                                name: "netstack".to_string(),
6765                                collection: Some("modular".to_string()),
6766                            }
6767                        )),
6768                        target_name: Some("assets".to_string()),
6769                        rights: Some(fio::Operations::CONNECT),
6770                        subdir: None,
6771                        dependency_type: Some(fdecl::DependencyType::Weak),
6772                        ..Default::default()
6773                    }),
6774                    fdecl::Offer::Storage(fdecl::OfferStorage {
6775                        source_name: Some("data".to_string()),
6776                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{ })),
6777                        target: Some(fdecl::Ref::Child(
6778                            fdecl::ChildRef {
6779                                name: "netstack".to_string(),
6780                                collection: Some("modular".to_string()),
6781                            }
6782                        )),
6783                        target_name: Some("data".to_string()),
6784                        ..Default::default()
6785                    }),
6786                    fdecl::Offer::Runner(fdecl::OfferRunner {
6787                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6788                            name: "logger".to_string(),
6789                            collection: Some("modular".to_string()),
6790                        })),
6791                        source_name: Some("elf".to_string()),
6792                        target: Some(fdecl::Ref::Child(
6793                            fdecl::ChildRef {
6794                                name: "netstack".to_string(),
6795                                collection: Some("modular".to_string()),
6796                            }
6797                        )),
6798                        target_name: Some("elf".to_string()),
6799                        ..Default::default()
6800                    }),
6801                    fdecl::Offer::Resolver(fdecl::OfferResolver {
6802                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6803                            name: "logger".to_string(),
6804                            collection: Some("modular".to_string()),
6805                        })),
6806                        source_name: Some("pkg".to_string()),
6807                        target: Some(fdecl::Ref::Child(
6808                            fdecl::ChildRef {
6809                                name: "netstack".to_string(),
6810                                collection: Some("modular".to_string()),
6811                            }
6812                        )),
6813                        target_name: Some("pkg".to_string()),
6814                        ..Default::default()
6815                    }),
6816                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6817                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6818                            name: "logger".to_string(),
6819                            collection: Some("modular".to_string()),
6820                        })),
6821                        source_name: Some("dict".to_string()),
6822                        target: Some(fdecl::Ref::Child(
6823                            fdecl::ChildRef {
6824                                name: "netstack".to_string(),
6825                                collection: Some("modular".to_string()),
6826                            }
6827                        )),
6828                        target_name: Some("dict".to_string()),
6829                        dependency_type: Some(fdecl::DependencyType::Strong),
6830                        ..Default::default()
6831                    }),
6832                ]);
6833                decl.capabilities = Some(vec![
6834                    fdecl::Capability::Protocol(fdecl::Protocol {
6835                        name: Some("fuchsia.logger.Log".to_string()),
6836                        source_path: Some("/svc/logger".to_string()),
6837                        ..Default::default()
6838                    }),
6839                    fdecl::Capability::Directory(fdecl::Directory {
6840                        name: Some("assets".to_string()),
6841                        source_path: Some("/data/assets".to_string()),
6842                        rights: Some(fio::Operations::CONNECT),
6843                        ..Default::default()
6844                    }),
6845                ]);
6846                decl
6847            },
6848            result = Err(ErrorList::new(vec![
6849                Error::extraneous_field(DeclType::OfferService, "source.child.collection"),
6850                Error::extraneous_field(DeclType::OfferService, "target.child.collection"),
6851                Error::extraneous_field(DeclType::OfferProtocol, "source.child.collection"),
6852                Error::extraneous_field(DeclType::OfferProtocol, "target.child.collection"),
6853                Error::extraneous_field(DeclType::OfferDirectory, "source.child.collection"),
6854                Error::extraneous_field(DeclType::OfferDirectory, "target.child.collection"),
6855                Error::extraneous_field(DeclType::OfferStorage, "target.child.collection"),
6856                Error::extraneous_field(DeclType::OfferRunner, "source.child.collection"),
6857                Error::extraneous_field(DeclType::OfferRunner, "target.child.collection"),
6858                Error::extraneous_field(DeclType::OfferResolver, "source.child.collection"),
6859                Error::extraneous_field(DeclType::OfferResolver, "target.child.collection"),
6860                Error::extraneous_field(DeclType::OfferDictionary, "source.child.collection"),
6861                Error::extraneous_field(DeclType::OfferDictionary, "target.child.collection"),
6862            ])),
6863        },
6864        test_validate_offers_invalid_filtered_service_fields => {
6865            input = {
6866                let mut decl = new_component_decl();
6867                decl.offers = Some(vec![
6868                    fdecl::Offer::Service(fdecl::OfferService {
6869                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6870                        source_name: Some("fuchsia.logger.Log".to_string()),
6871                        target: Some(fdecl::Ref::Child(
6872                            fdecl::ChildRef {
6873                                name: "logger".to_string(),
6874                                collection: None,
6875                            }
6876                        )),
6877                        target_name: Some("fuchsia.logger.Log".to_string()),
6878                        source_instance_filter: Some(vec![]),
6879                        ..Default::default()
6880                    }),
6881                    fdecl::Offer::Service(fdecl::OfferService {
6882                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6883                        source_name: Some("fuchsia.logger.Log".to_string()),
6884                        target: Some(fdecl::Ref::Child(
6885                            fdecl::ChildRef {
6886                                name: "logger".to_string(),
6887                                collection: None,
6888                            }
6889                        )),
6890                        target_name: Some("fuchsia.logger.Log2".to_string()),
6891                        source_instance_filter: Some(vec!["^badname".to_string()]),
6892                        ..Default::default()
6893                    }),
6894                    fdecl::Offer::Service(fdecl::OfferService {
6895                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6896                        source_name: Some("fuchsia.logger.Log".to_string()),
6897                        target: Some(fdecl::Ref::Child(
6898                            fdecl::ChildRef {
6899                                name: "logger".to_string(),
6900                                collection: None,
6901                            }
6902                        )),
6903                        target_name: Some("fuchsia.logger.Log1".to_string()),
6904                        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()}]),
6905                        ..Default::default()
6906                    }),
6907                    fdecl::Offer::Service(fdecl::OfferService {
6908                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6909                        source_name: Some("fuchsia.logger.Log".to_string()),
6910                        target: Some(fdecl::Ref::Child(
6911                            fdecl::ChildRef {
6912                                name: "logger".to_string(),
6913                                collection: None,
6914                            }
6915                        )),
6916                        target_name: Some("fuchsia.logger.Log3".to_string()),
6917                        renamed_instances: Some(vec![
6918                            fdecl::NameMapping {
6919                                source_name: "^badname".to_string(),
6920                                target_name: "^badname".to_string(),
6921                            }
6922                        ]),
6923                        ..Default::default()
6924                    })
6925                ]);
6926                decl.children = Some(vec![
6927                    fdecl::Child {
6928                        name: Some("logger".to_string()),
6929                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
6930                        startup: Some(fdecl::StartupMode::Lazy),
6931                        on_terminate: None,
6932                        environment: None,
6933                        ..Default::default()
6934                    },
6935                ]);
6936                decl
6937            },
6938            result = Err(ErrorList::new(vec![
6939                Error::invalid_field(DeclType::OfferService, "source_instance_filter"),
6940                Error::invalid_field(DeclType::OfferService, "source_instance_filter"),
6941                Error::invalid_field(DeclType::OfferService, "renamed_instances"),
6942                Error::invalid_field(DeclType::OfferService, "renamed_instances.source_name"),
6943                Error::invalid_field(DeclType::OfferService, "renamed_instances.target_name"),
6944            ])),
6945        },
6946        test_validate_offers_invalid_identifiers => {
6947            input = {
6948                let mut decl = new_component_decl();
6949                decl.offers = Some(vec![
6950                    fdecl::Offer::Service(fdecl::OfferService {
6951                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6952                            name: "^bad".to_string(),
6953                            collection: None,
6954                        })),
6955                        source_name: Some("foo/".to_string()),
6956                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6957                            name: "%bad".to_string(),
6958                            collection: None,
6959                        })),
6960                        target_name: Some("/".to_string()),
6961                        ..Default::default()
6962                    }),
6963                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6964                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6965                            name: "^bad".to_string(),
6966                            collection: None,
6967                        })),
6968                        source_name: Some("foo/".to_string()),
6969                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6970                            name: "%bad".to_string(),
6971                            collection: None,
6972                        })),
6973                        target_name: Some("/".to_string()),
6974                        dependency_type: Some(fdecl::DependencyType::Strong),
6975                        ..Default::default()
6976                    }),
6977                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6978                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6979                            name: "^bad".to_string(),
6980                            collection: None,
6981                        })),
6982                        source_name: Some("foo/".to_string()),
6983                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6984                            name: "%bad".to_string(),
6985                            collection: None,
6986                        })),
6987                        target_name: Some("/".to_string()),
6988                        rights: Some(fio::Operations::CONNECT),
6989                        subdir: Some("/foo".to_string()),
6990                        dependency_type: Some(fdecl::DependencyType::Strong),
6991                        ..Default::default()
6992                    }),
6993                    fdecl::Offer::Runner(fdecl::OfferRunner {
6994                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6995                            name: "^bad".to_string(),
6996                            collection: None,
6997                        })),
6998                        source_name: Some("/path".to_string()),
6999                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7000                            name: "%bad".to_string(),
7001                            collection: None,
7002                        })),
7003                        target_name: Some("elf!".to_string()),
7004                        ..Default::default()
7005                    }),
7006                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7007                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7008                            name: "^bad".to_string(),
7009                            collection: None,
7010                        })),
7011                        source_name: Some("/path".to_string()),
7012                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7013                            name: "%bad".to_string(),
7014                            collection: None,
7015                        })),
7016                        target_name: Some("pkg!".to_string()),
7017                        ..Default::default()
7018                    }),
7019                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7020                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7021                            name: "^bad".to_string(),
7022                            collection: None,
7023                        })),
7024                        source_name: Some("/path".to_string()),
7025                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7026                            name: "%bad".to_string(),
7027                            collection: None,
7028                        })),
7029                        target_name: Some("pkg!".to_string()),
7030                        dependency_type: Some(fdecl::DependencyType::Strong),
7031                        ..Default::default()
7032                    }),
7033                ]);
7034                decl
7035            },
7036            result = Err(ErrorList::new(vec![
7037                Error::invalid_field(DeclType::OfferService, "source.child.name"),
7038                Error::invalid_field(DeclType::OfferService, "source_name"),
7039                Error::invalid_field(DeclType::OfferService, "target.child.name"),
7040                Error::invalid_field(DeclType::OfferService, "target_name"),
7041                Error::invalid_field(DeclType::OfferProtocol, "source.child.name"),
7042                Error::invalid_field(DeclType::OfferProtocol, "source_name"),
7043                Error::invalid_field(DeclType::OfferProtocol, "target.child.name"),
7044                Error::invalid_field(DeclType::OfferProtocol, "target_name"),
7045                Error::invalid_field(DeclType::OfferDirectory, "source.child.name"),
7046                Error::invalid_field(DeclType::OfferDirectory, "source_name"),
7047                Error::invalid_field(DeclType::OfferDirectory, "target.child.name"),
7048                Error::invalid_field(DeclType::OfferDirectory, "target_name"),
7049                Error::invalid_field(DeclType::OfferDirectory, "subdir"),
7050                Error::invalid_field(DeclType::OfferRunner, "source.child.name"),
7051                Error::invalid_field(DeclType::OfferRunner, "source_name"),
7052                Error::invalid_field(DeclType::OfferRunner, "target.child.name"),
7053                Error::invalid_field(DeclType::OfferRunner, "target_name"),
7054                Error::invalid_field(DeclType::OfferResolver, "source.child.name"),
7055                Error::invalid_field(DeclType::OfferResolver, "source_name"),
7056                Error::invalid_field(DeclType::OfferResolver, "target.child.name"),
7057                Error::invalid_field(DeclType::OfferResolver, "target_name"),
7058                Error::invalid_field(DeclType::OfferDictionary, "source.child.name"),
7059                Error::invalid_field(DeclType::OfferDictionary, "source_name"),
7060                Error::invalid_field(DeclType::OfferDictionary, "target.child.name"),
7061                Error::invalid_field(DeclType::OfferDictionary, "target_name"),
7062            ])),
7063        },
7064        test_validate_offers_target_equals_source => {
7065            input = {
7066                let mut decl = new_component_decl();
7067                decl.offers = Some(vec![
7068                    fdecl::Offer::Service(fdecl::OfferService {
7069                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7070                            name: "logger".to_string(),
7071                            collection: None,
7072                        })),
7073                        source_name: Some("logger".to_string()),
7074                        target: Some(fdecl::Ref::Child(
7075                        fdecl::ChildRef {
7076                            name: "logger".to_string(),
7077                            collection: None,
7078                        }
7079                        )),
7080                        target_name: Some("logger".to_string()),
7081                        ..Default::default()
7082                    }),
7083                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7084                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7085                            name: "logger".to_string(),
7086                            collection: None,
7087                        })),
7088                        source_name: Some("legacy_logger".to_string()),
7089                        target: Some(fdecl::Ref::Child(
7090                        fdecl::ChildRef {
7091                            name: "logger".to_string(),
7092                            collection: None,
7093                        }
7094                        )),
7095                        target_name: Some("weak_legacy_logger".to_string()),
7096                        dependency_type: Some(fdecl::DependencyType::Weak),
7097                        ..Default::default()
7098                    }),
7099                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7100                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7101                            name: "logger".to_string(),
7102                            collection: None,
7103                        })),
7104                        source_name: Some("legacy_logger".to_string()),
7105                        target: Some(fdecl::Ref::Child(
7106                        fdecl::ChildRef {
7107                            name: "logger".to_string(),
7108                            collection: None,
7109                        }
7110                        )),
7111                        target_name: Some("strong_legacy_logger".to_string()),
7112                        dependency_type: Some(fdecl::DependencyType::Strong),
7113                        ..Default::default()
7114                    }),
7115                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7116                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7117                            name: "logger".to_string(),
7118                            collection: None,
7119                        })),
7120                        source_name: Some("assets".to_string()),
7121                        target: Some(fdecl::Ref::Child(
7122                        fdecl::ChildRef {
7123                            name: "logger".to_string(),
7124                            collection: None,
7125                        }
7126                        )),
7127                        target_name: Some("assets".to_string()),
7128                        rights: Some(fio::Operations::CONNECT),
7129                        subdir: None,
7130                        dependency_type: Some(fdecl::DependencyType::Strong),
7131                        ..Default::default()
7132                    }),
7133                    fdecl::Offer::Runner(fdecl::OfferRunner {
7134                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7135                            name: "logger".to_string(),
7136                            collection: None,
7137                        })),
7138                        source_name: Some("web".to_string()),
7139                        target: Some(fdecl::Ref::Child(
7140                        fdecl::ChildRef {
7141                            name: "logger".to_string(),
7142                            collection: None,
7143                        }
7144                        )),
7145                        target_name: Some("web".to_string()),
7146                        ..Default::default()
7147                    }),
7148                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7149                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7150                            name: "logger".to_string(),
7151                            collection: None,
7152                        })),
7153                        source_name: Some("pkg".to_string()),
7154                        target: Some(fdecl::Ref::Child(
7155                        fdecl::ChildRef {
7156                            name: "logger".to_string(),
7157                            collection: None,
7158                        }
7159                        )),
7160                        target_name: Some("pkg".to_string()),
7161                        ..Default::default()
7162                    }),
7163                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7164                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7165                            name: "logger".to_string(),
7166                            collection: None,
7167                        })),
7168                        source_name: Some("dict".to_string()),
7169                        target: Some(fdecl::Ref::Child(
7170                        fdecl::ChildRef {
7171                            name: "logger".to_string(),
7172                            collection: None,
7173                        }
7174                        )),
7175                        target_name: Some("dict".to_string()),
7176                        dependency_type: Some(fdecl::DependencyType::Strong),
7177                        ..Default::default()
7178                    }),
7179                ]);
7180                decl.children = Some(vec![fdecl::Child{
7181                    name: Some("logger".to_string()),
7182                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
7183                    startup: Some(fdecl::StartupMode::Lazy),
7184                    on_terminate: None,
7185                    environment: None,
7186                    ..Default::default()
7187                }]);
7188                decl
7189            },
7190            result = Err(ErrorList::new(vec![
7191                Error::dependency_cycle("{{child logger -> child logger}}"),
7192            ])),
7193        },
7194        test_validate_offers_storage_target_equals_source => {
7195            input = fdecl::Component {
7196                offers: Some(vec![
7197                    fdecl::Offer::Storage(fdecl::OfferStorage {
7198                        source_name: Some("data".to_string()),
7199                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef { })),
7200                        target: Some(fdecl::Ref::Child(
7201                            fdecl::ChildRef {
7202                                name: "logger".to_string(),
7203                                collection: None,
7204                            }
7205                        )),
7206                        target_name: Some("data".to_string()),
7207                        ..Default::default()
7208                    })
7209                ]),
7210                capabilities: Some(vec![
7211                    fdecl::Capability::Storage(fdecl::Storage {
7212                        name: Some("data".to_string()),
7213                        backing_dir: Some("minfs".to_string()),
7214                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7215                            name: "logger".to_string(),
7216                            collection: None,
7217                        })),
7218                        subdir: None,
7219                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
7220                        ..Default::default()
7221                    }),
7222                ]),
7223                children: Some(vec![
7224                    fdecl::Child {
7225                        name: Some("logger".to_string()),
7226                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
7227                        startup: Some(fdecl::StartupMode::Lazy),
7228                        on_terminate: None,
7229                        environment: None,
7230                        ..Default::default()
7231                    },
7232                ]),
7233                ..new_component_decl()
7234            },
7235            result = Err(ErrorList::new(vec![
7236                Error::dependency_cycle("{{child logger -> capability data -> child logger}}"),
7237            ])),
7238        },
7239        test_validate_offers_invalid_child => {
7240            input = {
7241                let mut decl = new_component_decl();
7242                decl.offers = Some(vec![
7243                    fdecl::Offer::Service(fdecl::OfferService {
7244                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7245                            name: "logger".to_string(),
7246                            collection: None,
7247                        })),
7248                        source_name: Some("fuchsia.logger.Log".to_string()),
7249                        target: Some(fdecl::Ref::Child(
7250                        fdecl::ChildRef {
7251                            name: "netstack".to_string(),
7252                            collection: None,
7253                        }
7254                        )),
7255                        target_name: Some("fuchsia.logger.Log".to_string()),
7256                        ..Default::default()
7257                    }),
7258                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7259                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7260                            name: "logger".to_string(),
7261                            collection: None,
7262                        })),
7263                        source_name: Some("fuchsia.logger.LegacyLog".to_string()),
7264                        target: Some(fdecl::Ref::Child(
7265                        fdecl::ChildRef {
7266                            name: "netstack".to_string(),
7267                            collection: None,
7268                        }
7269                        )),
7270                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7271                        dependency_type: Some(fdecl::DependencyType::Strong),
7272                        ..Default::default()
7273                    }),
7274                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7275                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7276                            name: "logger".to_string(),
7277                            collection: None,
7278                        })),
7279                        source_name: Some("assets".to_string()),
7280                        target: Some(fdecl::Ref::Collection(
7281                        fdecl::CollectionRef { name: "modular".to_string() }
7282                        )),
7283                        target_name: Some("assets".to_string()),
7284                        rights: Some(fio::Operations::CONNECT),
7285                        subdir: None,
7286                        dependency_type: Some(fdecl::DependencyType::Weak),
7287                        ..Default::default()
7288                    }),
7289                ]);
7290                decl.capabilities = Some(vec![
7291                    fdecl::Capability::Storage(fdecl::Storage {
7292                        name: Some("memfs".to_string()),
7293                        backing_dir: Some("memfs".to_string()),
7294                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7295                            name: "logger".to_string(),
7296                            collection: None,
7297                        })),
7298                        subdir: None,
7299                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
7300                        ..Default::default()
7301                    }),
7302                ]);
7303                decl.children = Some(vec![
7304                    fdecl::Child {
7305                        name: Some("netstack".to_string()),
7306                        url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
7307                        startup: Some(fdecl::StartupMode::Lazy),
7308                        on_terminate: None,
7309                        environment: None,
7310                        ..Default::default()
7311                    },
7312                ]);
7313                decl.collections = Some(vec![
7314                    fdecl::Collection {
7315                        name: Some("modular".to_string()),
7316                        durability: Some(fdecl::Durability::Transient),
7317                        environment: None,
7318                        allowed_offers: Some(fdecl::AllowedOffers::StaticAndDynamic),
7319                        allow_long_names: None,
7320                        ..Default::default()
7321                    },
7322                ]);
7323                decl
7324            },
7325            result = Err(ErrorList::new(vec![
7326                Error::invalid_child(DeclType::Storage, "source", "logger"),
7327                Error::invalid_child(DeclType::OfferService, "source", "logger"),
7328                Error::invalid_child(DeclType::OfferProtocol, "source", "logger"),
7329                Error::invalid_child(DeclType::OfferDirectory, "source", "logger"),
7330            ])),
7331        },
7332        test_validate_offers_invalid_source_capability => {
7333            input = {
7334                fdecl::Component {
7335                    offers: Some(vec![
7336                        fdecl::Offer::Protocol(fdecl::OfferProtocol {
7337                            source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
7338                                name: "this-storage-doesnt-exist".to_string(),
7339                            })),
7340                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
7341                            target: Some(fdecl::Ref::Child(
7342                            fdecl::ChildRef {
7343                                name: "netstack".to_string(),
7344                                collection: None,
7345                            }
7346                            )),
7347                            target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
7348                            dependency_type: Some(fdecl::DependencyType::Strong),
7349                            ..Default::default()
7350                        }),
7351                    ]),
7352                    ..new_component_decl()
7353                }
7354            },
7355            result = Err(ErrorList::new(vec![
7356                Error::invalid_capability(DeclType::OfferProtocol, "source", "this-storage-doesnt-exist"),
7357                Error::invalid_child(DeclType::OfferProtocol, "target", "netstack"),
7358            ])),
7359        },
7360        test_validate_offers_target => {
7361            input = {
7362                let mut decl = new_component_decl();
7363                decl.offers = Some(vec![
7364                    fdecl::Offer::Service(fdecl::OfferService {
7365                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
7366                            name: "modular".into()
7367                        })),
7368                        source_name: Some("logger".to_string()),
7369                        target: Some(fdecl::Ref::Child(
7370                        fdecl::ChildRef {
7371                            name: "netstack".to_string(),
7372                            collection: None,
7373                        }
7374                        )),
7375                        target_name: Some("fuchsia.logger.Log".to_string()),
7376                        ..Default::default()
7377                    }),
7378                    fdecl::Offer::Service(fdecl::OfferService {
7379                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
7380                            name: "modular".into()
7381                        })),
7382                        source_name: Some("logger".to_string()),
7383                        target: Some(fdecl::Ref::Child(
7384                        fdecl::ChildRef {
7385                            name: "netstack".to_string(),
7386                            collection: None,
7387                        }
7388                        )),
7389                        target_name: Some("fuchsia.logger.Log".to_string()),
7390                        ..Default::default()
7391                    }),
7392                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7393                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7394                        source_name: Some("fuchsia.logger.LegacyLog".to_string()),
7395                        target: Some(fdecl::Ref::Child(
7396                        fdecl::ChildRef {
7397                            name: "netstack".to_string(),
7398                            collection: None,
7399                        }
7400                        )),
7401                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7402                        dependency_type: Some(fdecl::DependencyType::Strong),
7403                        ..Default::default()
7404                    }),
7405                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7406                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7407                        source_name: Some("fuchsia.logger.LegacyLog".to_string()),
7408                        target: Some(fdecl::Ref::Child(
7409                        fdecl::ChildRef {
7410                            name: "netstack".to_string(),
7411                            collection: None,
7412                        }
7413                        )),
7414                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7415                        dependency_type: Some(fdecl::DependencyType::Strong),
7416                        ..Default::default()
7417                    }),
7418                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7419                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7420                        source_name: Some("assets".to_string()),
7421                        target: Some(fdecl::Ref::Collection(
7422                        fdecl::CollectionRef { name: "modular".to_string() }
7423                        )),
7424                        target_name: Some("assets".to_string()),
7425                        rights: Some(fio::Operations::CONNECT),
7426                        subdir: None,
7427                        dependency_type: Some(fdecl::DependencyType::Strong),
7428                        ..Default::default()
7429                    }),
7430                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7431                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7432                        source_name: Some("assets".to_string()),
7433                        target: Some(fdecl::Ref::Collection(
7434                        fdecl::CollectionRef { name: "modular".to_string() }
7435                        )),
7436                        target_name: Some("assets".to_string()),
7437                        rights: Some(fio::Operations::CONNECT),
7438                        subdir: None,
7439                        dependency_type: Some(fdecl::DependencyType::Weak),
7440                        ..Default::default()
7441                    }),
7442                    fdecl::Offer::Storage(fdecl::OfferStorage {
7443                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7444                        source_name: Some("data".to_string()),
7445                        target: Some(fdecl::Ref::Collection(
7446                        fdecl::CollectionRef { name: "modular".to_string() }
7447                        )),
7448                        target_name: Some("data".to_string()),
7449                        ..Default::default()
7450                    }),
7451                    fdecl::Offer::Storage(fdecl::OfferStorage {
7452                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7453                        source_name: Some("data".to_string()),
7454                        target: Some(fdecl::Ref::Collection(
7455                        fdecl::CollectionRef { name: "modular".to_string() }
7456                        )),
7457                        target_name: Some("data".to_string()),
7458                        ..Default::default()
7459                    }),
7460                    fdecl::Offer::Runner(fdecl::OfferRunner {
7461                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7462                        source_name: Some("elf".to_string()),
7463                        target: Some(fdecl::Ref::Collection(
7464                        fdecl::CollectionRef { name: "modular".to_string() }
7465                        )),
7466                        target_name: Some("duplicated".to_string()),
7467                        ..Default::default()
7468                    }),
7469                    fdecl::Offer::Runner(fdecl::OfferRunner {
7470                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7471                        source_name: Some("elf".to_string()),
7472                        target: Some(fdecl::Ref::Collection(
7473                        fdecl::CollectionRef { name: "modular".to_string() }
7474                        )),
7475                        target_name: Some("duplicated".to_string()),
7476                        ..Default::default()
7477                    }),
7478                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7479                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7480                        source_name: Some("pkg".to_string()),
7481                        target: Some(fdecl::Ref::Collection(
7482                        fdecl::CollectionRef { name: "modular".to_string() }
7483                        )),
7484                        target_name: Some("duplicated".to_string()),
7485                        ..Default::default()
7486                    }),
7487                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
7488                        source_name: Some("started".to_string()),
7489                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7490                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "netstack".to_string(), collection: None})),
7491                        target_name: Some("started".to_string()),
7492                        ..Default::default()
7493                    }),
7494                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
7495                        source_name: Some("started".to_string()),
7496                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7497                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "netstack".to_string(), collection: None})),
7498                        target_name: Some("started".to_string()),
7499                        ..Default::default()
7500                    }),
7501                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7502                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7503                        source_name: Some("a".to_string()),
7504                        target: Some(fdecl::Ref::Collection(
7505                            fdecl::CollectionRef { name: "modular".to_string() }
7506                        )),
7507                        target_name: Some("dict".to_string()),
7508                        dependency_type: Some(fdecl::DependencyType::Strong),
7509                        ..Default::default()
7510                    }),
7511                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7512                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7513                        source_name: Some("b".to_string()),
7514                        target: Some(fdecl::Ref::Collection(
7515                            fdecl::CollectionRef { name: "modular".to_string() }
7516                        )),
7517                        target_name: Some("dict".to_string()),
7518                        dependency_type: Some(fdecl::DependencyType::Strong),
7519                        ..Default::default()
7520                    }),
7521                ]);
7522                decl.children = Some(vec![
7523                    fdecl::Child{
7524                        name: Some("netstack".to_string()),
7525                        url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
7526                        startup: Some(fdecl::StartupMode::Eager),
7527                        on_terminate: None,
7528                        environment: None,
7529                        ..Default::default()
7530                    },
7531                ]);
7532                decl.collections = Some(vec![
7533                    fdecl::Collection{
7534                        name: Some("modular".to_string()),
7535                        durability: Some(fdecl::Durability::Transient),
7536                        environment: None,
7537                        allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7538                        allow_long_names: None,
7539                        ..Default::default()
7540                    },
7541                ]);
7542                decl
7543            },
7544            result = Err(ErrorList::new(vec![
7545                // Duplicate services are allowed, for aggregation.
7546                Error::duplicate_field(DeclType::OfferProtocol, "target_name", "fuchsia.logger.LegacyLog"),
7547                Error::duplicate_field(DeclType::OfferDirectory, "target_name", "assets"),
7548                Error::duplicate_field(DeclType::OfferStorage, "target_name", "data"),
7549                Error::duplicate_field(DeclType::OfferRunner, "target_name", "duplicated"),
7550                Error::duplicate_field(DeclType::OfferResolver, "target_name", "duplicated"),
7551                Error::duplicate_field(DeclType::OfferEventStream, "target_name", "started"),
7552                Error::duplicate_field(DeclType::OfferDictionary, "target_name", "dict"),
7553            ])),
7554        },
7555        test_validate_offers_target_invalid => {
7556            input = {
7557                let mut decl = new_component_decl();
7558                decl.offers = Some(vec![
7559                    fdecl::Offer::Service(fdecl::OfferService {
7560                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7561                        source_name: Some("logger".to_string()),
7562                        target: Some(fdecl::Ref::Child(
7563                        fdecl::ChildRef {
7564                            name: "netstack".to_string(),
7565                            collection: None,
7566                        }
7567                        )),
7568                        target_name: Some("fuchsia.logger.Log".to_string()),
7569                        ..Default::default()
7570                    }),
7571                    fdecl::Offer::Service(fdecl::OfferService {
7572                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7573                        source_name: Some("logger".to_string()),
7574                        target: Some(fdecl::Ref::Collection(
7575                        fdecl::CollectionRef { name: "modular".to_string(), }
7576                        )),
7577                        target_name: Some("fuchsia.logger.Log".to_string()),
7578                        ..Default::default()
7579                    }),
7580                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7581                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7582                        source_name: Some("legacy_logger".to_string()),
7583                        target: Some(fdecl::Ref::Child(
7584                        fdecl::ChildRef {
7585                            name: "netstack".to_string(),
7586                            collection: None,
7587                        }
7588                        )),
7589                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7590                        dependency_type: Some(fdecl::DependencyType::Weak),
7591                        ..Default::default()
7592                    }),
7593                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7594                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7595                        source_name: Some("legacy_logger".to_string()),
7596                        target: Some(fdecl::Ref::Collection(
7597                        fdecl::CollectionRef { name: "modular".to_string(), }
7598                        )),
7599                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7600                        dependency_type: Some(fdecl::DependencyType::Strong),
7601                        ..Default::default()
7602                    }),
7603                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7604                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7605                        source_name: Some("assets".to_string()),
7606                        target: Some(fdecl::Ref::Child(
7607                        fdecl::ChildRef {
7608                            name: "netstack".to_string(),
7609                            collection: None,
7610                        }
7611                        )),
7612                        target_name: Some("data".to_string()),
7613                        rights: Some(fio::Operations::CONNECT),
7614                        subdir: None,
7615                        dependency_type: Some(fdecl::DependencyType::Strong),
7616                        ..Default::default()
7617                    }),
7618                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7619                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7620                        source_name: Some("assets".to_string()),
7621                        target: Some(fdecl::Ref::Collection(
7622                        fdecl::CollectionRef { name: "modular".to_string(), }
7623                        )),
7624                        target_name: Some("data".to_string()),
7625                        rights: Some(fio::Operations::CONNECT),
7626                        subdir: None,
7627                        dependency_type: Some(fdecl::DependencyType::Weak),
7628                        ..Default::default()
7629                    }),
7630                    fdecl::Offer::Storage(fdecl::OfferStorage {
7631                        source_name: Some("data".to_string()),
7632                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7633                        target: Some(fdecl::Ref::Child(
7634                            fdecl::ChildRef {
7635                                name: "netstack".to_string(),
7636                                collection: None,
7637                            }
7638                        )),
7639                        target_name: Some("data".to_string()),
7640                        ..Default::default()
7641                    }),
7642                    fdecl::Offer::Storage(fdecl::OfferStorage {
7643                        source_name: Some("data".to_string()),
7644                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7645                        target: Some(fdecl::Ref::Collection(
7646                            fdecl::CollectionRef { name: "modular".to_string(), }
7647                        )),
7648                        target_name: Some("data".to_string()),
7649                        ..Default::default()
7650                    }),
7651                    fdecl::Offer::Runner(fdecl::OfferRunner {
7652                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7653                        source_name: Some("elf".to_string()),
7654                        target: Some(fdecl::Ref::Child(
7655                            fdecl::ChildRef {
7656                                name: "netstack".to_string(),
7657                                collection: None,
7658                            }
7659                        )),
7660                        target_name: Some("elf".to_string()),
7661                        ..Default::default()
7662                    }),
7663                    fdecl::Offer::Runner(fdecl::OfferRunner {
7664                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7665                        source_name: Some("elf".to_string()),
7666                        target: Some(fdecl::Ref::Collection(
7667                        fdecl::CollectionRef { name: "modular".to_string(), }
7668                        )),
7669                        target_name: Some("elf".to_string()),
7670                        ..Default::default()
7671                    }),
7672                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7673                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7674                        source_name: Some("pkg".to_string()),
7675                        target: Some(fdecl::Ref::Child(
7676                            fdecl::ChildRef {
7677                                name: "netstack".to_string(),
7678                                collection: None,
7679                            }
7680                        )),
7681                        target_name: Some("pkg".to_string()),
7682                        ..Default::default()
7683                    }),
7684                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7685                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7686                        source_name: Some("pkg".to_string()),
7687                        target: Some(fdecl::Ref::Collection(
7688                        fdecl::CollectionRef { name: "modular".to_string(), }
7689                        )),
7690                        target_name: Some("pkg".to_string()),
7691                        ..Default::default()
7692                    }),
7693                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7694                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7695                        source_name: Some("pkg".to_string()),
7696                        target: Some(fdecl::Ref::Child(
7697                            fdecl::ChildRef {
7698                                name: "netstack".to_string(),
7699                                collection: None,
7700                            }
7701                        )),
7702                        target_name: Some("pkg".to_string()),
7703                        dependency_type: Some(fdecl::DependencyType::Strong),
7704                        ..Default::default()
7705                    }),
7706                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7707                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7708                        source_name: Some("pkg".to_string()),
7709                        target: Some(fdecl::Ref::Collection(
7710                        fdecl::CollectionRef { name: "modular".to_string(), }
7711                        )),
7712                        target_name: Some("pkg".to_string()),
7713                        dependency_type: Some(fdecl::DependencyType::Strong),
7714                        ..Default::default()
7715                    }),
7716                ]);
7717                decl
7718            },
7719            result = Err(ErrorList::new(vec![
7720                Error::invalid_child(DeclType::OfferService, "target", "netstack"),
7721                Error::invalid_collection(DeclType::OfferService, "target", "modular"),
7722                Error::invalid_child(DeclType::OfferProtocol, "target", "netstack"),
7723                Error::invalid_collection(DeclType::OfferProtocol, "target", "modular"),
7724                Error::invalid_child(DeclType::OfferDirectory, "target", "netstack"),
7725                Error::invalid_collection(DeclType::OfferDirectory, "target", "modular"),
7726                Error::invalid_child(DeclType::OfferStorage, "target", "netstack"),
7727                Error::invalid_collection(DeclType::OfferStorage, "target", "modular"),
7728                Error::invalid_child(DeclType::OfferRunner, "target", "netstack"),
7729                Error::invalid_collection(DeclType::OfferRunner, "target", "modular"),
7730                Error::invalid_child(DeclType::OfferResolver, "target", "netstack"),
7731                Error::invalid_collection(DeclType::OfferResolver, "target", "modular"),
7732                Error::invalid_child(DeclType::OfferDictionary, "target", "netstack"),
7733                Error::invalid_collection(DeclType::OfferDictionary, "target", "modular"),
7734            ])),
7735        },
7736        test_validate_offers_target_dictionary => {
7737            input = fdecl::Component {
7738                offers: Some(vec![
7739                    // Offer to static dictionary is ok
7740                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7741                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7742                        source_name: Some("p".to_string()),
7743                        target: Some(fdecl::Ref::Capability(
7744                            fdecl::CapabilityRef {
7745                                name: "dict".into(),
7746                            },
7747                        )),
7748                        target_name: Some("p".into()),
7749                        dependency_type: Some(fdecl::DependencyType::Strong),
7750                        ..Default::default()
7751                    }),
7752                    // Offer to dynamic dictionary is forbidden
7753                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7754                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7755                        source_name: Some("p".to_string()),
7756                        target: Some(fdecl::Ref::Capability(
7757                            fdecl::CapabilityRef {
7758                                name: "dynamic".into(),
7759                            },
7760                        )),
7761                        target_name: Some("p".into()),
7762                        dependency_type: Some(fdecl::DependencyType::Strong),
7763                        ..Default::default()
7764                    }),
7765                ]),
7766                capabilities: Some(vec![
7767                    fdecl::Capability::Dictionary(fdecl::Dictionary {
7768                        name: Some("dict".into()),
7769                        ..Default::default()
7770                    }),
7771                    fdecl::Capability::Dictionary(fdecl::Dictionary {
7772                        name: Some("dynamic".into()),
7773                        source_path: Some("/out/dir".into()),
7774                        ..Default::default()
7775                    }),
7776                ]),
7777                ..Default::default()
7778            },
7779            result = Err(ErrorList::new(vec![
7780                Error::invalid_field(DeclType::OfferProtocol, "target"),
7781            ])),
7782        },
7783        test_validate_offers_invalid_source_collection => {
7784            input = {
7785                let mut decl = new_component_decl();
7786                decl.collections = Some(vec![
7787                    fdecl::Collection {
7788                        name: Some("col".to_string()),
7789                        durability: Some(fdecl::Durability::Transient),
7790                        allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7791                        allow_long_names: None,
7792                        ..Default::default()
7793                    }
7794                ]);
7795                decl.children = Some(vec![
7796                    fdecl::Child {
7797                        name: Some("child".to_string()),
7798                        url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7799                        startup: Some(fdecl::StartupMode::Lazy),
7800                        on_terminate: None,
7801                        ..Default::default()
7802                    }
7803                ]);
7804                decl.offers = Some(vec![
7805                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7806                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7807                        source_name: Some("a".to_string()),
7808                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7809                        target_name: Some("a".to_string()),
7810                        dependency_type: Some(fdecl::DependencyType::Strong),
7811                        ..Default::default()
7812                    }),
7813                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7814                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7815                        source_name: Some("b".to_string()),
7816                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7817                        target_name: Some("b".to_string()),
7818                        rights: Some(fio::Operations::CONNECT),
7819                        subdir: None,
7820                        dependency_type: Some(fdecl::DependencyType::Strong),
7821                        ..Default::default()
7822                    }),
7823                    fdecl::Offer::Storage(fdecl::OfferStorage {
7824                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7825                        source_name: Some("c".to_string()),
7826                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7827                        target_name: Some("c".to_string()),
7828                        ..Default::default()
7829                    }),
7830                    fdecl::Offer::Runner(fdecl::OfferRunner {
7831                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7832                        source_name: Some("d".to_string()),
7833                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7834                        target_name: Some("d".to_string()),
7835                        ..Default::default()
7836                    }),
7837                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7838                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7839                        source_name: Some("e".to_string()),
7840                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7841                        target_name: Some("e".to_string()),
7842                        ..Default::default()
7843                    }),
7844                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7845                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7846                        source_name: Some("f".to_string()),
7847                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7848                        target_name: Some("f".to_string()),
7849                        dependency_type: Some(fdecl::DependencyType::Strong),
7850                        ..Default::default()
7851                    }),
7852                ]);
7853                decl
7854            },
7855            result = Err(ErrorList::new(vec![
7856                Error::invalid_field(DeclType::OfferProtocol, "source"),
7857                Error::invalid_field(DeclType::OfferDirectory, "source"),
7858                Error::invalid_field(DeclType::OfferStorage, "source"),
7859                Error::invalid_field(DeclType::OfferRunner, "source"),
7860                Error::invalid_field(DeclType::OfferResolver, "source"),
7861                Error::invalid_field(DeclType::OfferDictionary, "source"),
7862            ])),
7863        },
7864        test_validate_offers_source_collection => {
7865            input = {
7866                let mut decl = new_component_decl();
7867                decl.collections = Some(vec![
7868                    fdecl::Collection {
7869                        name: Some("col".to_string()),
7870                        durability: Some(fdecl::Durability::Transient),
7871                        allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7872                        allow_long_names: None,
7873                        ..Default::default()
7874                    }
7875                ]);
7876                decl.children = Some(vec![
7877                    fdecl::Child {
7878                        name: Some("child".to_string()),
7879                        url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7880                        startup: Some(fdecl::StartupMode::Lazy),
7881                        on_terminate: None,
7882                        ..Default::default()
7883                    }
7884                ]);
7885                decl.offers = Some(vec![
7886                    fdecl::Offer::Service(fdecl::OfferService {
7887                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7888                        source_name: Some("a".to_string()),
7889                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7890                        target_name: Some("a".to_string()),
7891                        ..Default::default()
7892                    })
7893                ]);
7894                decl
7895            },
7896            result = Ok(()),
7897        },
7898        test_validate_offers_invalid_capability_from_self => {
7899            input = {
7900                let mut decl = new_component_decl();
7901                decl.children = Some(vec![
7902                    fdecl::Child {
7903                        name: Some("child".to_string()),
7904                        url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7905                        startup: Some(fdecl::StartupMode::Lazy),
7906                        ..Default::default()
7907                    }
7908                ]);
7909                decl.offers = Some(vec![
7910                    fdecl::Offer::Service(fdecl::OfferService {
7911                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7912                        source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7913                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7914                            name: "child".into(),
7915                            collection: None
7916                        })),
7917                        target_name: Some("foo".into()),
7918                        ..Default::default()
7919                    }),
7920                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7921                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7922                        source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7923                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7924                            name: "child".into(),
7925                            collection: None
7926                        })),
7927                        target_name: Some("bar".into()),
7928                        dependency_type: Some(fdecl::DependencyType::Strong),
7929                        ..Default::default()
7930                    }),
7931                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7932                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7933                        source_name: Some("dir".into()),
7934                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7935                            name: "child".into(),
7936                            collection: None
7937                        })),
7938                        target_name: Some("assets".into()),
7939                        dependency_type: Some(fdecl::DependencyType::Strong),
7940                        ..Default::default()
7941                    }),
7942                    fdecl::Offer::Runner(fdecl::OfferRunner {
7943                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7944                        source_name: Some("source_elf".into()),
7945                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7946                            name: "child".into(),
7947                            collection: None
7948                        })),
7949                        target_name: Some("elf".into()),
7950                        ..Default::default()
7951                    }),
7952                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7953                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7954                        source_name: Some("source_pkg".into()),
7955                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7956                            name: "child".into(),
7957                            collection: None
7958                        })),
7959                        target_name: Some("pkg".into()),
7960                        ..Default::default()
7961                    }),
7962                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7963                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7964                        source_name: Some("source_dict".into()),
7965                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7966                            name: "child".into(),
7967                            collection: None
7968                        })),
7969                        target_name: Some("dict".into()),
7970                        dependency_type: Some(fdecl::DependencyType::Strong),
7971                        ..Default::default()
7972                    }),
7973                    fdecl::Offer::Storage(fdecl::OfferStorage {
7974                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7975                        source_name: Some("source_storage".into()),
7976                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7977                            name: "child".into(),
7978                            collection: None
7979                        })),
7980                        target_name: Some("storage".into()),
7981                        ..Default::default()
7982                    }),
7983                    fdecl::Offer::Config(fdecl::OfferConfiguration {
7984                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7985                        source_name: Some("source_config".into()),
7986                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7987                            name: "child".into(),
7988                            collection: None
7989                        })),
7990                        target_name: Some("config".into()),
7991                        ..Default::default()
7992                    }),
7993                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7994                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7995                        source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7996                        source_dictionary: Some("dict/inner".into()),
7997                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7998                            name: "child".into(),
7999                            collection: None
8000                        })),
8001                        target_name: Some("baz".into()),
8002                        dependency_type: Some(fdecl::DependencyType::Strong),
8003                        ..Default::default()
8004                    }),
8005                ]);
8006                decl
8007            },
8008            result = Err(ErrorList::new(vec![
8009                Error::invalid_capability(
8010                    DeclType::OfferService,
8011                    "source",
8012                    "fuchsia.some.library.SomeProtocol"),
8013                Error::invalid_capability(
8014                    DeclType::OfferProtocol,
8015                    "source",
8016                    "fuchsia.some.library.SomeProtocol"),
8017                Error::invalid_capability(DeclType::OfferDirectory, "source", "dir"),
8018                Error::invalid_capability(DeclType::OfferRunner, "source", "source_elf"),
8019                Error::invalid_capability(DeclType::OfferResolver, "source", "source_pkg"),
8020                Error::invalid_capability(DeclType::OfferDictionary, "source", "source_dict"),
8021                Error::invalid_capability(DeclType::OfferStorage, "source", "source_storage"),
8022                Error::invalid_capability(DeclType::OfferConfig, "source", "source_config"),
8023                Error::invalid_capability(DeclType::OfferProtocol, "source", "dict"),
8024            ])),
8025        },
8026        test_validate_offers_long_dependency_cycle => {
8027            input = {
8028                let mut decl = new_component_decl();
8029                let dependencies = vec![
8030                    ("d", "b"),
8031                    ("a", "b"),
8032                    ("b", "c"),
8033                    ("b", "d"),
8034                    ("c", "a"),
8035                ];
8036                let offers = dependencies.into_iter().map(|(from,to)|
8037                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
8038                        source: Some(fdecl::Ref::Child(
8039                        fdecl::ChildRef { name: from.to_string(), collection: None },
8040                        )),
8041                        source_name: Some(format!("thing_{}", from)),
8042                        target: Some(fdecl::Ref::Child(
8043                        fdecl::ChildRef { name: to.to_string(), collection: None },
8044                        )),
8045                        target_name: Some(format!("thing_{}", from)),
8046                        dependency_type: Some(fdecl::DependencyType::Strong),
8047                        ..Default::default()
8048                    })).collect();
8049                let children = ["a", "b", "c", "d"].iter().map(|name| {
8050                    fdecl::Child {
8051                        name: Some(name.to_string()),
8052                        url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
8053                        startup: Some(fdecl::StartupMode::Lazy),
8054                        on_terminate: None,
8055                        environment: None,
8056                        ..Default::default()
8057                    }
8058                }).collect();
8059                decl.offers = Some(offers);
8060                decl.children = Some(children);
8061                decl
8062            },
8063            result = Err(ErrorList::new(vec![
8064                Error::dependency_cycle("{{child a -> child b -> child c -> child a}, {child b -> child d -> child b}}")
8065            ])),
8066        },
8067        test_validate_offers_not_required_invalid_source_service => {
8068            input = {
8069                let mut decl = generate_offer_different_source_and_availability_decl(
8070                    |source, availability, target_name|
8071                        fdecl::Offer::Service(fdecl::OfferService {
8072                            source: Some(source),
8073                            source_name: Some("fuchsia.examples.Echo".to_string()),
8074                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8075                                name: "sink".to_string(),
8076                                collection: None,
8077                            })),
8078                            target_name: Some(target_name.into()),
8079                            availability: Some(availability),
8080                            ..Default::default()
8081                        })
8082                );
8083                decl.capabilities = Some(vec![
8084                    fdecl::Capability::Service(fdecl::Service {
8085                        name: Some("fuchsia.examples.Echo".to_string()),
8086                        source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
8087                        ..Default::default()
8088                    }),
8089                ]);
8090                decl
8091            },
8092            result = {
8093                Err(ErrorList::new(vec![
8094                    Error::availability_must_be_optional(
8095                        DeclType::OfferService,
8096                        "availability",
8097                        Some(&"fuchsia.examples.Echo".to_string()),
8098                    ),
8099                    Error::availability_must_be_optional(
8100                        DeclType::OfferService,
8101                        "availability",
8102                        Some(&"fuchsia.examples.Echo".to_string()),
8103                    ),
8104                ]))
8105            },
8106        },
8107        test_validate_offers_not_required_invalid_source_protocol => {
8108            input = {
8109                let mut decl = generate_offer_different_source_and_availability_decl(
8110                    |source, availability, target_name|
8111                        fdecl::Offer::Protocol(fdecl::OfferProtocol {
8112                            source: Some(source),
8113                            source_name: Some("fuchsia.examples.Echo".to_string()),
8114                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8115                                name: "sink".to_string(),
8116                                collection: None,
8117                            })),
8118                            target_name: Some(target_name.into()),
8119                            dependency_type: Some(fdecl::DependencyType::Strong),
8120                            availability: Some(availability),
8121                            ..Default::default()
8122                        })
8123                );
8124                decl.capabilities = Some(vec![
8125                    fdecl::Capability::Protocol(fdecl::Protocol {
8126                        name: Some("fuchsia.examples.Echo".to_string()),
8127                        source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
8128                        ..Default::default()
8129                    }),
8130                ]);
8131                decl
8132            },
8133            result = {
8134                Err(ErrorList::new(vec![
8135                    Error::availability_must_be_optional(
8136                        DeclType::OfferProtocol,
8137                        "availability",
8138                        Some(&"fuchsia.examples.Echo".to_string()),
8139                    ),
8140                    Error::availability_must_be_optional(
8141                        DeclType::OfferProtocol,
8142                        "availability",
8143                        Some(&"fuchsia.examples.Echo".to_string()),
8144                    ),
8145                ]))
8146            },
8147        },
8148        test_validate_offers_not_required_invalid_source_directory => {
8149            input = {
8150                let mut decl = generate_offer_different_source_and_availability_decl(
8151                    |source, availability, target_name|
8152                        fdecl::Offer::Directory(fdecl::OfferDirectory {
8153                            source: Some(source),
8154                            source_name: Some("assets".to_string()),
8155                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8156                                name: "sink".to_string(),
8157                                collection: None,
8158                            })),
8159                            target_name: Some(target_name.into()),
8160                            rights: Some(fio::Operations::CONNECT),
8161                            subdir: None,
8162                            dependency_type: Some(fdecl::DependencyType::Weak),
8163                            availability: Some(availability),
8164                            ..Default::default()
8165                        })
8166                );
8167                decl.capabilities = Some(vec![
8168                    fdecl::Capability::Directory(fdecl::Directory {
8169                        name: Some("assets".to_string()),
8170                        source_path: Some("/assets".to_string()),
8171                        rights: Some(fio::Operations::CONNECT),
8172                        ..Default::default()
8173                    }),
8174                ]);
8175                decl
8176            },
8177            result = {
8178                Err(ErrorList::new(vec![
8179                    Error::availability_must_be_optional(
8180                        DeclType::OfferDirectory,
8181                        "availability",
8182                        Some(&"assets".to_string()),
8183                    ),
8184                    Error::availability_must_be_optional(
8185                        DeclType::OfferDirectory,
8186                        "availability",
8187                        Some(&"assets".to_string()),
8188                    ),
8189                ]))
8190            },
8191        },
8192        test_validate_offers_not_required_invalid_source_storage => {
8193            input = {
8194                let mut decl = new_component_decl();
8195                decl.children = Some(vec![
8196                    fdecl::Child {
8197                        name: Some("sink".to_string()),
8198                        url: Some("fuchsia-pkg://fuchsia.com/sink#meta/sink.cm".to_string()),
8199                        startup: Some(fdecl::StartupMode::Lazy),
8200                        on_terminate: None,
8201                        environment: None,
8202                        ..Default::default()
8203                    },
8204                ]);
8205                decl.capabilities = Some(vec![
8206                    fdecl::Capability::Storage(fdecl::Storage {
8207                        name: Some("data".to_string()),
8208                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8209                        backing_dir: Some("minfs".to_string()),
8210                        subdir: None,
8211                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
8212                        ..Default::default()
8213                    }),
8214                ]);
8215                let new_offer = |source: fdecl::Ref, availability: fdecl::Availability,
8216                                        target_name: &str|
8217                {
8218                    fdecl::Offer::Storage(fdecl::OfferStorage {
8219                        source: Some(source),
8220                        source_name: Some("data".to_string()),
8221                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8222                            name: "sink".to_string(),
8223                            collection: None,
8224                        })),
8225                        target_name: Some(target_name.into()),
8226                        availability: Some(availability),
8227                        ..Default::default()
8228                    })
8229                };
8230                decl.offers = Some(vec![
8231                    // These offers are fine, offers with a source of parent or void can be
8232                    // optional.
8233                    new_offer(
8234                        fdecl::Ref::Parent(fdecl::ParentRef {}),
8235                        fdecl::Availability::Required,
8236                        "data0",
8237                    ),
8238                    new_offer(
8239                        fdecl::Ref::Parent(fdecl::ParentRef {}),
8240                        fdecl::Availability::Optional,
8241                        "data1",
8242                    ),
8243                    new_offer(
8244                        fdecl::Ref::Parent(fdecl::ParentRef {}),
8245                        fdecl::Availability::SameAsTarget,
8246                        "data2",
8247                    ),
8248                    new_offer(
8249                        fdecl::Ref::VoidType(fdecl::VoidRef {}),
8250                        fdecl::Availability::Optional,
8251                        "data3",
8252                    ),
8253                    // These offers are not fine, offers with a source other than parent or void
8254                    // must be required.
8255                    new_offer(
8256                        fdecl::Ref::Self_(fdecl::SelfRef {}),
8257                        fdecl::Availability::Optional,
8258                        "data4",
8259                    ),
8260                    new_offer(
8261                        fdecl::Ref::Self_(fdecl::SelfRef {}),
8262                        fdecl::Availability::SameAsTarget,
8263                        "data5",
8264                    ),
8265                    // These offers are also not fine, offers with a source of void must be optional
8266                    new_offer(
8267                        fdecl::Ref::VoidType(fdecl::VoidRef {}),
8268                        fdecl::Availability::Required,
8269                        "data6",
8270                    ),
8271                    new_offer(
8272                        fdecl::Ref::VoidType(fdecl::VoidRef {}),
8273                        fdecl::Availability::SameAsTarget,
8274                        "data7",
8275                    ),
8276                ]);
8277                decl
8278            },
8279            result = {
8280                Err(ErrorList::new(vec![
8281                    Error::availability_must_be_optional(
8282                        DeclType::OfferStorage,
8283                        "availability",
8284                        Some(&"data".to_string()),
8285                    ),
8286                    Error::availability_must_be_optional(
8287                        DeclType::OfferStorage,
8288                        "availability",
8289                        Some(&"data".to_string()),
8290                    ),
8291                ]))
8292            },
8293        },
8294
8295        test_validate_offers_valid_service_aggregation => {
8296            input = {
8297                let mut decl = new_component_decl();
8298                decl.offers = Some(vec![
8299                    fdecl::Offer::Service(fdecl::OfferService {
8300                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
8301                            name: "coll_a".to_string()
8302                        })),
8303                        source_name: Some("fuchsia.logger.Log".to_string()),
8304                        target: Some(fdecl::Ref::Child(
8305                            fdecl::ChildRef {
8306                                name: "child_c".to_string(),
8307                                collection: None,
8308                            }
8309                        )),
8310                        target_name: Some("fuchsia.logger.Log".to_string()),
8311                        source_instance_filter: None,
8312                        ..Default::default()
8313                    }),
8314                    fdecl::Offer::Service(fdecl::OfferService {
8315                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
8316                            name: "coll_b".to_string()
8317                        })),
8318                        source_name: Some("fuchsia.logger.Log".to_string()),
8319                        target: Some(fdecl::Ref::Child(
8320                            fdecl::ChildRef {
8321                                name: "child_c".to_string(),
8322                                collection: None,
8323                            }
8324                        )),
8325                        target_name: Some("fuchsia.logger.Log".to_string()),
8326                        source_instance_filter: Some(vec!["a_different_default".to_string()]),
8327                        ..Default::default()
8328                    })
8329                ]);
8330                decl.children = Some(vec![
8331                    fdecl::Child {
8332                        name: Some("child_c".to_string()),
8333                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
8334                        startup: Some(fdecl::StartupMode::Lazy),
8335                        ..Default::default()
8336                    },
8337                ]);
8338                decl.collections = Some(vec![
8339                    fdecl::Collection {
8340                        name: Some("coll_a".into()),
8341                        durability: Some(fdecl::Durability::Transient),
8342                        ..Default::default()
8343                    },
8344                    fdecl::Collection {
8345                        name: Some("coll_b".into()),
8346                        durability: Some(fdecl::Durability::Transient),
8347                        ..Default::default()
8348                    },
8349                ]);
8350                decl
8351            },
8352            result = Ok(()),
8353        },
8354
8355        // dictionaries
8356        test_validate_source_dictionary => {
8357            input = fdecl::Component {
8358                program: Some(fdecl::Program {
8359                    runner: Some("elf".into()),
8360                    info: Some(fdata::Dictionary {
8361                        entries: None,
8362                        ..Default::default()
8363                    }),
8364                    ..Default::default()
8365                }),
8366                uses: Some(vec![
8367                    fdecl::Use::Protocol(fdecl::UseProtocol {
8368                        dependency_type: Some(fdecl::DependencyType::Strong),
8369                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8370                        source_dictionary: Some("bad//".into()),
8371                        source_name: Some("foo".into()),
8372                        target_path: Some("/svc/foo".into()),
8373                        ..Default::default()
8374                    }),
8375                ]),
8376                exposes: Some(vec![
8377                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
8378                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8379                            name: "missing".into(),
8380                            collection: None,
8381                        })),
8382                        source_dictionary: Some("in/dict".into()),
8383                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8384                        source_name: Some("foo".into()),
8385                        target_name: Some("bar".into()),
8386                        ..Default::default()
8387                    }),
8388                ]),
8389                offers: Some(vec![
8390                    fdecl::Offer::Service(fdecl::OfferService {
8391                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8392                        source_dictionary: Some("bad//".into()),
8393                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8394                            name: "child".into(),
8395                            collection: None,
8396                        })),
8397                        source_name: Some("foo".into()),
8398                        target_name: Some("bar".into()),
8399                        ..Default::default()
8400                    }),
8401                ]),
8402                children: Some(vec![
8403                    fdecl::Child {
8404                        name: Some("child".into()),
8405                        url: Some("fuchsia-pkg://child".into()),
8406                        startup: Some(fdecl::StartupMode::Lazy),
8407                        ..Default::default()
8408                    },
8409                ]),
8410                ..Default::default()
8411            },
8412            result = Err(ErrorList::new(vec![
8413                Error::invalid_field(DeclType::UseProtocol, "source_dictionary"),
8414                Error::invalid_child(DeclType::ExposeDirectory, "source", "missing"),
8415                Error::invalid_field(DeclType::OfferService, "source_dictionary"),
8416            ])),
8417        },
8418        test_validate_dictionary_too_long => {
8419            input = fdecl::Component {
8420                program: Some(fdecl::Program {
8421                    runner: Some("elf".into()),
8422                    info: Some(fdata::Dictionary {
8423                        entries: None,
8424                        ..Default::default()
8425                    }),
8426                    ..Default::default()
8427                }),
8428                uses: Some(vec![
8429                    fdecl::Use::Protocol(fdecl::UseProtocol {
8430                        dependency_type: Some(fdecl::DependencyType::Strong),
8431                        source: Some(fdecl::Ref::Parent( fdecl::ParentRef {} )),
8432                        source_dictionary: Some("a".repeat(4096)),
8433                        source_name: Some("foo".into()),
8434                        target_path: Some("/svc/foo".into()),
8435                        ..Default::default()
8436                    }),
8437                ]),
8438                ..Default::default()
8439            },
8440            result = Err(ErrorList::new(vec![
8441                Error::field_too_long(DeclType::UseProtocol, "source_dictionary"),
8442            ])),
8443        },
8444
8445        // environments
8446        test_validate_environment_empty => {
8447            input = {
8448                let mut decl = new_component_decl();
8449                decl.environments = Some(vec![fdecl::Environment {
8450                    name: None,
8451                    extends: None,
8452                    runners: None,
8453                    resolvers: None,
8454                    stop_timeout_ms: None,
8455                    debug_capabilities: None,
8456                    ..Default::default()
8457                }]);
8458                decl
8459            },
8460            result = Err(ErrorList::new(vec![
8461                Error::missing_field(DeclType::Environment, "name"),
8462                Error::missing_field(DeclType::Environment, "extends"),
8463            ])),
8464        },
8465
8466        test_validate_environment_no_stop_timeout => {
8467            input = {
8468                let mut decl = new_component_decl();
8469                decl.environments = Some(vec![fdecl::Environment {
8470                    name: Some("env".to_string()),
8471                    extends: Some(fdecl::EnvironmentExtends::None),
8472                    runners: None,
8473                    resolvers: None,
8474                    stop_timeout_ms: None,
8475                    ..Default::default()
8476                }]);
8477                decl
8478            },
8479            result = Err(ErrorList::new(vec![Error::missing_field(DeclType::Environment, "stop_timeout_ms")])),
8480        },
8481
8482        test_validate_environment_extends_stop_timeout => {
8483            input = {  let mut decl = new_component_decl();
8484                decl.environments = Some(vec![fdecl::Environment {
8485                    name: Some("env".to_string()),
8486                    extends: Some(fdecl::EnvironmentExtends::Realm),
8487                    runners: None,
8488                    resolvers: None,
8489                    stop_timeout_ms: None,
8490                    ..Default::default()
8491                }]);
8492                decl
8493            },
8494            result = Ok(()),
8495        },
8496        test_validate_environment_long_identifiers => {
8497            input = {
8498                let mut decl = new_component_decl();
8499                decl.environments = Some(vec![fdecl::Environment {
8500                    name: Some("a".repeat(256)),
8501                    extends: Some(fdecl::EnvironmentExtends::None),
8502                    runners: Some(vec![
8503                        fdecl::RunnerRegistration {
8504                            source_name: Some("a".repeat(256)),
8505                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8506                            target_name: Some("a".repeat(256)),
8507                            ..Default::default()
8508                        },
8509                    ]),
8510                    resolvers: Some(vec![
8511                        fdecl::ResolverRegistration {
8512                            resolver: Some("a".repeat(256)),
8513                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8514                            scheme: Some("a".repeat(256)),
8515                            ..Default::default()
8516                        },
8517                    ]),
8518                    stop_timeout_ms: Some(1234),
8519                    ..Default::default()
8520                }]);
8521                decl
8522            },
8523            result = Err(ErrorList::new(vec![
8524                Error::field_too_long(DeclType::Environment, "name"),
8525                Error::field_too_long(DeclType::RunnerRegistration, "source_name"),
8526                Error::field_too_long(DeclType::RunnerRegistration, "target_name"),
8527                Error::field_too_long(DeclType::ResolverRegistration, "resolver"),
8528                Error::field_too_long(DeclType::ResolverRegistration, "scheme"),
8529            ])),
8530        },
8531        test_validate_environment_empty_runner_resolver_fields => {
8532            input = {
8533                let mut decl = new_component_decl();
8534                decl.environments = Some(vec![fdecl::Environment {
8535                    name: Some("a".to_string()),
8536                    extends: Some(fdecl::EnvironmentExtends::None),
8537                    runners: Some(vec![
8538                        fdecl::RunnerRegistration {
8539                            source_name: None,
8540                            source: None,
8541                            target_name: None,
8542                            ..Default::default()
8543                        },
8544                    ]),
8545                    resolvers: Some(vec![
8546                        fdecl::ResolverRegistration {
8547                            resolver: None,
8548                            source: None,
8549                            scheme: None,
8550                            ..Default::default()
8551                        },
8552                    ]),
8553                    stop_timeout_ms: Some(1234),
8554                    ..Default::default()
8555                }]);
8556                decl
8557            },
8558            result = Err(ErrorList::new(vec![
8559                Error::missing_field(DeclType::RunnerRegistration, "source_name"),
8560                Error::missing_field(DeclType::RunnerRegistration, "source"),
8561                Error::missing_field(DeclType::RunnerRegistration, "target_name"),
8562                Error::missing_field(DeclType::ResolverRegistration, "resolver"),
8563                Error::missing_field(DeclType::ResolverRegistration, "source"),
8564                Error::missing_field(DeclType::ResolverRegistration, "scheme"),
8565            ])),
8566        },
8567        test_validate_environment_invalid_fields => {
8568            input = {
8569                let mut decl = new_component_decl();
8570                decl.environments = Some(vec![fdecl::Environment {
8571                    name: Some("a".to_string()),
8572                    extends: Some(fdecl::EnvironmentExtends::None),
8573                    runners: Some(vec![
8574                        fdecl::RunnerRegistration {
8575                            source_name: Some("^a".to_string()),
8576                            source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
8577                            target_name: Some("%a".to_string()),
8578                            ..Default::default()
8579                        },
8580                    ]),
8581                    resolvers: Some(vec![
8582                        fdecl::ResolverRegistration {
8583                            resolver: Some("^a".to_string()),
8584                            source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
8585                            scheme: Some("9scheme".to_string()),
8586                            ..Default::default()
8587                        },
8588                    ]),
8589                    stop_timeout_ms: Some(1234),
8590                    ..Default::default()
8591                }]);
8592                decl
8593            },
8594            result = Err(ErrorList::new(vec![
8595                Error::invalid_field(DeclType::RunnerRegistration, "source_name"),
8596                Error::invalid_field(DeclType::RunnerRegistration, "source"),
8597                Error::invalid_field(DeclType::RunnerRegistration, "target_name"),
8598                Error::invalid_field(DeclType::ResolverRegistration, "resolver"),
8599                Error::invalid_field(DeclType::ResolverRegistration, "source"),
8600                Error::invalid_field(DeclType::ResolverRegistration, "scheme"),
8601            ])),
8602        },
8603        test_validate_environment_missing_runner => {
8604            input = {
8605                let mut decl = new_component_decl();
8606                decl.environments = Some(vec![fdecl::Environment {
8607                    name: Some("a".to_string()),
8608                    extends: Some(fdecl::EnvironmentExtends::None),
8609                    runners: Some(vec![
8610                        fdecl::RunnerRegistration {
8611                            source_name: Some("dart".to_string()),
8612                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
8613                            target_name: Some("dart".to_string()),
8614                            ..Default::default()
8615                        },
8616                    ]),
8617                    resolvers: None,
8618                    stop_timeout_ms: Some(1234),
8619                    ..Default::default()
8620                }]);
8621                decl
8622            },
8623            result = Err(ErrorList::new(vec![
8624                Error::invalid_runner(DeclType::RunnerRegistration, "source_name", "dart"),
8625            ])),
8626        },
8627        test_validate_environment_duplicate_registrations => {
8628            input = {
8629                let mut decl = new_component_decl();
8630                decl.environments = Some(vec![fdecl::Environment {
8631                    name: Some("a".to_string()),
8632                    extends: Some(fdecl::EnvironmentExtends::None),
8633                    runners: Some(vec![
8634                        fdecl::RunnerRegistration {
8635                            source_name: Some("dart".to_string()),
8636                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8637                            target_name: Some("dart".to_string()),
8638                            ..Default::default()
8639                        },
8640                        fdecl::RunnerRegistration {
8641                            source_name: Some("other-dart".to_string()),
8642                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8643                            target_name: Some("dart".to_string()),
8644                            ..Default::default()
8645                        },
8646                    ]),
8647                    resolvers: Some(vec![
8648                        fdecl::ResolverRegistration {
8649                            resolver: Some("pkg_resolver".to_string()),
8650                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8651                            scheme: Some("fuchsia-pkg".to_string()),
8652                            ..Default::default()
8653                        },
8654                        fdecl::ResolverRegistration {
8655                            resolver: Some("base_resolver".to_string()),
8656                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8657                            scheme: Some("fuchsia-pkg".to_string()),
8658                            ..Default::default()
8659                        },
8660                    ]),
8661                    stop_timeout_ms: Some(1234),
8662                    ..Default::default()
8663                }]);
8664                decl
8665            },
8666            result = Err(ErrorList::new(vec![
8667                Error::duplicate_field(DeclType::RunnerRegistration, "target_name", "dart"),
8668                Error::duplicate_field(DeclType::ResolverRegistration, "scheme", "fuchsia-pkg"),
8669            ])),
8670        },
8671        test_validate_environment_from_missing_child => {
8672            input = {
8673                let mut decl = new_component_decl();
8674                decl.environments = Some(vec![fdecl::Environment {
8675                    name: Some("a".to_string()),
8676                    extends: Some(fdecl::EnvironmentExtends::None),
8677                    runners: Some(vec![
8678                        fdecl::RunnerRegistration {
8679                            source_name: Some("elf".to_string()),
8680                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8681                                name: "missing".to_string(),
8682                                collection: None,
8683                            })),
8684                            target_name: Some("elf".to_string()),
8685                            ..Default::default()
8686                        },
8687                    ]),
8688                    resolvers: Some(vec![
8689                        fdecl::ResolverRegistration {
8690                            resolver: Some("pkg_resolver".to_string()),
8691                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8692                                name: "missing".to_string(),
8693                                collection: None,
8694                            })),
8695                            scheme: Some("fuchsia-pkg".to_string()),
8696                            ..Default::default()
8697                        },
8698                    ]),
8699                    stop_timeout_ms: Some(1234),
8700                    ..Default::default()
8701                }]);
8702                decl
8703            },
8704            result = Err(ErrorList::new(vec![
8705                Error::invalid_child(DeclType::RunnerRegistration, "source", "missing"),
8706                Error::invalid_child(DeclType::ResolverRegistration, "source", "missing"),
8707            ])),
8708        },
8709        test_validate_environment_runner_child_cycle => {
8710            input = {
8711                let mut decl = new_component_decl();
8712                decl.environments = Some(vec![fdecl::Environment {
8713                    name: Some("env".to_string()),
8714                    extends: Some(fdecl::EnvironmentExtends::None),
8715                    runners: Some(vec![
8716                        fdecl::RunnerRegistration {
8717                            source_name: Some("elf".to_string()),
8718                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8719                                name: "child".to_string(),
8720                                collection: None,
8721                            })),
8722                            target_name: Some("elf".to_string()),
8723                            ..Default::default()
8724                        },
8725                    ]),
8726                    resolvers: None,
8727                    stop_timeout_ms: Some(1234),
8728                    ..Default::default()
8729                }]);
8730                decl.children = Some(vec![fdecl::Child {
8731                    name: Some("child".to_string()),
8732                    startup: Some(fdecl::StartupMode::Lazy),
8733                    on_terminate: None,
8734                    url: Some("fuchsia-pkg://child".to_string()),
8735                    environment: Some("env".to_string()),
8736                    ..Default::default()
8737                }]);
8738                decl
8739            },
8740            result = Err(ErrorList::new(vec![
8741                Error::dependency_cycle(
8742                    "{{child child -> environment env -> child child}}"
8743                ),
8744            ])),
8745        },
8746        test_validate_environment_resolver_child_cycle => {
8747            input = {
8748                let mut decl = new_component_decl();
8749                decl.environments = Some(vec![fdecl::Environment {
8750                    name: Some("env".to_string()),
8751                    extends: Some(fdecl::EnvironmentExtends::None),
8752                    runners: None,
8753                    resolvers: Some(vec![
8754                        fdecl::ResolverRegistration {
8755                            resolver: Some("pkg_resolver".to_string()),
8756                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8757                                name: "child".to_string(),
8758                                collection: None,
8759                            })),
8760                            scheme: Some("fuchsia-pkg".to_string()),
8761                            ..Default::default()
8762                        },
8763                    ]),
8764                    stop_timeout_ms: Some(1234),
8765                    ..Default::default()
8766                }]);
8767                decl.children = Some(vec![fdecl::Child {
8768                    name: Some("child".to_string()),
8769                    startup: Some(fdecl::StartupMode::Lazy),
8770                    on_terminate: None,
8771                    url: Some("fuchsia-pkg://child".to_string()),
8772                    environment: Some("env".to_string()),
8773                    ..Default::default()
8774                }]);
8775                decl
8776            },
8777            result = Err(ErrorList::new(vec![
8778                Error::dependency_cycle(
8779                    "{{child child -> environment env -> child child}}"
8780                ),
8781            ])),
8782        },
8783        test_validate_environment_resolver_multiple_children_cycle => {
8784            input = {
8785                let mut decl = new_component_decl();
8786                decl.environments = Some(vec![fdecl::Environment {
8787                    name: Some("env".to_string()),
8788                    extends: Some(fdecl::EnvironmentExtends::None),
8789                    runners: None,
8790                    resolvers: Some(vec![
8791                        fdecl::ResolverRegistration {
8792                            resolver: Some("pkg_resolver".to_string()),
8793                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8794                                name: "a".to_string(),
8795                                collection: None,
8796                            })),
8797                            scheme: Some("fuchsia-pkg".to_string()),
8798                            ..Default::default()
8799                        },
8800                    ]),
8801                    stop_timeout_ms: Some(1234),
8802                    ..Default::default()
8803                }]);
8804                decl.children = Some(vec![
8805                    fdecl::Child {
8806                        name: Some("a".to_string()),
8807                        startup: Some(fdecl::StartupMode::Lazy),
8808                        on_terminate: None,
8809                        url: Some("fuchsia-pkg://child-a".to_string()),
8810                        environment: None,
8811                        ..Default::default()
8812                    },
8813                    fdecl::Child {
8814                        name: Some("b".to_string()),
8815                        startup: Some(fdecl::StartupMode::Lazy),
8816                        on_terminate: None,
8817                        url: Some("fuchsia-pkg://child-b".to_string()),
8818                        environment: Some("env".to_string()),
8819                        ..Default::default()
8820                    },
8821                ]);
8822                decl.offers = Some(vec![fdecl::Offer::Service(fdecl::OfferService {
8823                    source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8824                        name: "b".to_string(),
8825                        collection: None,
8826                    })),
8827                    source_name: Some("thing".to_string()),
8828                    target: Some(fdecl::Ref::Child(
8829                    fdecl::ChildRef {
8830                        name: "a".to_string(),
8831                        collection: None,
8832                    }
8833                    )),
8834                    target_name: Some("thing".to_string()),
8835                    ..Default::default()
8836                })]);
8837                decl
8838            },
8839            result = Err(ErrorList::new(vec![
8840                Error::dependency_cycle(
8841                    "{{child a -> environment env -> child b -> child a}}"
8842                ),
8843            ])),
8844        },
8845        test_validate_environment_debug_empty => {
8846            input = {
8847                let mut decl = new_component_decl();
8848                decl.environments = Some(vec![
8849                    fdecl::Environment {
8850                        name: Some("a".to_string()),
8851                        extends: Some(fdecl::EnvironmentExtends::None),
8852                        stop_timeout_ms: Some(2),
8853                        debug_capabilities:Some(vec![
8854                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8855                                source: None,
8856                                source_name: None,
8857                                target_name: None,
8858                                ..Default::default()
8859                            }),
8860                    ]),
8861                    ..Default::default()
8862                }]);
8863                decl
8864            },
8865            result = Err(ErrorList::new(vec![
8866                Error::missing_field(DeclType::DebugProtocolRegistration, "source"),
8867                Error::missing_field(DeclType::DebugProtocolRegistration, "source_name"),
8868                Error::missing_field(DeclType::DebugProtocolRegistration, "target_name"),
8869            ])),
8870        },
8871        test_validate_environment_debug_log_identifier => {
8872            input = {
8873                let mut decl = new_component_decl();
8874                decl.environments = Some(vec![
8875                    fdecl::Environment {
8876                        name: Some("a".to_string()),
8877                        extends: Some(fdecl::EnvironmentExtends::None),
8878                        stop_timeout_ms: Some(2),
8879                        debug_capabilities:Some(vec![
8880                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8881                                source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8882                                    name: "a".repeat(256),
8883                                    collection: None,
8884                                })),
8885                                source_name: Some(format!("{}", "a".repeat(256))),
8886                                target_name: Some(format!("{}", "b".repeat(256))),
8887                                ..Default::default()
8888                            }),
8889                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8890                                source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8891                                source_name: Some("a".to_string()),
8892                                target_name: Some(format!("{}", "b".repeat(256))),
8893                                ..Default::default()
8894                            }),
8895                    ]),
8896                    ..Default::default()
8897                }]);
8898                decl
8899            },
8900            result = Err(ErrorList::new(vec![
8901                Error::field_too_long(DeclType::DebugProtocolRegistration, "source.child.name"),
8902                Error::field_too_long(DeclType::DebugProtocolRegistration, "source_name"),
8903                Error::field_too_long(DeclType::DebugProtocolRegistration, "target_name"),
8904                Error::field_too_long(DeclType::DebugProtocolRegistration, "target_name"),
8905            ])),
8906        },
8907        test_validate_environment_debug_log_extraneous => {
8908            input = {
8909                let mut decl = new_component_decl();
8910                decl.environments = Some(vec![
8911                    fdecl::Environment {
8912                        name: Some("a".to_string()),
8913                        extends: Some(fdecl::EnvironmentExtends::None),
8914                        stop_timeout_ms: Some(2),
8915                        debug_capabilities:Some(vec![
8916                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8917                                source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8918                                    name: "logger".to_string(),
8919                                    collection: Some("modular".to_string()),
8920                                })),
8921                                source_name: Some("fuchsia.logger.Log".to_string()),
8922                                target_name: Some("fuchsia.logger.Log".to_string()),
8923                                ..Default::default()
8924                            }),
8925                    ]),
8926                    ..Default::default()
8927                }]);
8928                decl
8929            },
8930            result = Err(ErrorList::new(vec![
8931                Error::extraneous_field(DeclType::DebugProtocolRegistration, "source.child.collection"),
8932            ])),
8933        },
8934        test_validate_environment_debug_log_invalid_identifiers => {
8935            input = {
8936                let mut decl = new_component_decl();
8937                decl.environments = Some(vec![
8938                    fdecl::Environment {
8939                        name: Some("a".to_string()),
8940                        extends: Some(fdecl::EnvironmentExtends::None),
8941                        stop_timeout_ms: Some(2),
8942                        debug_capabilities:Some(vec![
8943                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8944                                source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8945                                    name: "^bad".to_string(),
8946                                    collection: None,
8947                                })),
8948                                source_name: Some("foo/".to_string()),
8949                                target_name: Some("/".to_string()),
8950                                ..Default::default()
8951                            }),
8952                    ]),
8953                    ..Default::default()
8954                }]);
8955                decl
8956            },
8957            result = Err(ErrorList::new(vec![
8958                Error::invalid_field(DeclType::DebugProtocolRegistration, "source.child.name"),
8959                Error::invalid_field(DeclType::DebugProtocolRegistration, "source_name"),
8960                Error::invalid_field(DeclType::DebugProtocolRegistration, "target_name"),
8961            ])),
8962        },
8963        test_validate_environment_debug_log_invalid_child => {
8964            input = {
8965                let mut decl = new_component_decl();
8966                decl.environments = Some(vec![
8967                    fdecl::Environment {
8968                        name: Some("a".to_string()),
8969                        extends: Some(fdecl::EnvironmentExtends::None),
8970                        stop_timeout_ms: Some(2),
8971                        debug_capabilities:Some(vec![
8972                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8973                                source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8974                                    name: "logger".to_string(),
8975                                    collection: None,
8976                                })),
8977                                source_name: Some("fuchsia.logger.LegacyLog".to_string()),
8978                                target_name: Some("fuchsia.logger.LegacyLog".to_string()),
8979                                ..Default::default()
8980                            }),
8981                    ]),
8982                    ..Default::default()
8983                }]);
8984                decl.children = Some(vec![
8985                    fdecl::Child {
8986                        name: Some("netstack".to_string()),
8987                        url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
8988                        startup: Some(fdecl::StartupMode::Lazy),
8989                        on_terminate: None,
8990                        environment: None,
8991                        ..Default::default()
8992                    },
8993                ]);
8994                decl
8995            },
8996            result = Err(ErrorList::new(vec![
8997                Error::invalid_child(DeclType::DebugProtocolRegistration, "source", "logger"),
8998
8999            ])),
9000        },
9001        test_validate_environment_debug_source_capability => {
9002            input = {
9003                let mut decl = new_component_decl();
9004                decl.environments = Some(vec![
9005                    fdecl::Environment {
9006                        name: Some("a".to_string()),
9007                        extends: Some(fdecl::EnvironmentExtends::None),
9008                        stop_timeout_ms: Some(2),
9009                        debug_capabilities:Some(vec![
9010                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
9011                                source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
9012                                    name: "storage".to_string(),
9013                                })),
9014                                source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
9015                                target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
9016                                ..Default::default()
9017                            }),
9018                    ]),
9019                    ..Default::default()
9020                }]);
9021                decl
9022            },
9023            result = Err(ErrorList::new(vec![
9024                Error::invalid_field(DeclType::DebugProtocolRegistration, "source"),
9025            ])),
9026        },
9027
9028        // children
9029        test_validate_children_empty => {
9030            input = {
9031                let mut decl = new_component_decl();
9032                decl.children = Some(vec![fdecl::Child{
9033                    name: None,
9034                    url: None,
9035                    startup: None,
9036                    on_terminate: None,
9037                    environment: None,
9038                    ..Default::default()
9039                }]);
9040                decl
9041            },
9042            result = Err(ErrorList::new(vec![
9043                Error::missing_field(DeclType::Child, "name"),
9044                Error::missing_field(DeclType::Child, "url"),
9045                Error::missing_field(DeclType::Child, "startup"),
9046                // `on_terminate` is allowed to be None
9047            ])),
9048        },
9049        test_validate_children_invalid_identifiers => {
9050            input = {
9051                let mut decl = new_component_decl();
9052                decl.children = Some(vec![fdecl::Child{
9053                    name: Some("^bad".to_string()),
9054                    url: Some("scheme://invalid-port:99999999/path#frag".to_string()),
9055                    startup: Some(fdecl::StartupMode::Lazy),
9056                    on_terminate: None,
9057                    environment: None,
9058                    ..Default::default()
9059                }]);
9060                decl
9061            },
9062            result = Err(ErrorList::new(vec![
9063                Error::invalid_field(DeclType::Child, "name"),
9064                Error::invalid_url(DeclType::Child, "url", "\"scheme://invalid-port:99999999/path#frag\": Malformed URL: InvalidPort."),
9065            ])),
9066        },
9067        test_validate_children_long_identifiers => {
9068            input = {
9069                let mut decl = new_component_decl();
9070                decl.children = Some(vec![fdecl::Child{
9071                    name: Some("a".repeat(1025)),
9072                    url: Some(format!("fuchsia-pkg://{}", "a".repeat(4083))),
9073                    startup: Some(fdecl::StartupMode::Lazy),
9074                    on_terminate: None,
9075                    environment: Some("a".repeat(1025)),
9076                    ..Default::default()
9077                }]);
9078                decl
9079            },
9080            result = Err(ErrorList::new(vec![
9081                Error::field_too_long(DeclType::Child, "name"),
9082                Error::field_too_long(DeclType::Child, "url"),
9083                Error::field_too_long(DeclType::Child, "environment"),
9084                Error::invalid_environment(DeclType::Child, "environment", "a".repeat(1025)),
9085            ])),
9086        },
9087        test_validate_child_references_unknown_env => {
9088            input = {
9089                let mut decl = new_component_decl();
9090                decl.children = Some(vec![fdecl::Child{
9091                    name: Some("foo".to_string()),
9092                    url: Some("fuchsia-pkg://foo".to_string()),
9093                    startup: Some(fdecl::StartupMode::Lazy),
9094                    on_terminate: None,
9095                    environment: Some("test_env".to_string()),
9096                    ..Default::default()
9097                }]);
9098                decl
9099            },
9100            result = Err(ErrorList::new(vec![
9101                Error::invalid_environment(DeclType::Child, "environment", "test_env"),
9102            ])),
9103        },
9104
9105        // collections
9106        test_validate_collections_empty => {
9107            input = {
9108                let mut decl = new_component_decl();
9109                decl.collections = Some(vec![fdecl::Collection{
9110                    name: None,
9111                    durability: None,
9112                    environment: None,
9113                    allowed_offers: None,
9114                    allow_long_names: None,
9115                    ..Default::default()
9116                }]);
9117                decl
9118            },
9119            result = Err(ErrorList::new(vec![
9120                Error::missing_field(DeclType::Collection, "name"),
9121                Error::missing_field(DeclType::Collection, "durability"),
9122            ])),
9123        },
9124        test_validate_collections_invalid_identifiers => {
9125            input = {
9126                let mut decl = new_component_decl();
9127                decl.collections = Some(vec![fdecl::Collection{
9128                    name: Some("^bad".to_string()),
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::invalid_field(DeclType::Collection, "name"),
9139            ])),
9140        },
9141        test_validate_collections_long_identifiers => {
9142            input = {
9143                let mut decl = new_component_decl();
9144                decl.collections = Some(vec![fdecl::Collection{
9145                    name: Some("a".repeat(1025)),
9146                    durability: Some(fdecl::Durability::Transient),
9147                    environment: None,
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::field_too_long(DeclType::Collection, "name"),
9156            ])),
9157        },
9158        test_validate_collection_references_unknown_env => {
9159            input = {
9160                let mut decl = new_component_decl();
9161                decl.collections = Some(vec![fdecl::Collection {
9162                    name: Some("foo".to_string()),
9163                    durability: Some(fdecl::Durability::Transient),
9164                    environment: Some("test_env".to_string()),
9165                    allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
9166                    allow_long_names: None,
9167                    ..Default::default()
9168                }]);
9169                decl
9170            },
9171            result = Err(ErrorList::new(vec![
9172                Error::invalid_environment(DeclType::Collection, "environment", "test_env"),
9173            ])),
9174        },
9175
9176        // capabilities
9177        test_validate_capabilities_empty => {
9178            input = {
9179                let mut decl = new_component_decl();
9180                decl.capabilities = Some(vec![
9181                    fdecl::Capability::Service(fdecl::Service {
9182                        name: None,
9183                        source_path: None,
9184                        ..Default::default()
9185                    }),
9186                    fdecl::Capability::Protocol(fdecl::Protocol {
9187                        name: None,
9188                        source_path: None,
9189                        ..Default::default()
9190                    }),
9191                    fdecl::Capability::Directory(fdecl::Directory {
9192                        name: None,
9193                        source_path: None,
9194                        rights: None,
9195                        ..Default::default()
9196                    }),
9197                    fdecl::Capability::Storage(fdecl::Storage {
9198                        name: None,
9199                        source: None,
9200                        backing_dir: None,
9201                        subdir: None,
9202                        storage_id: None,
9203                        ..Default::default()
9204                    }),
9205                    fdecl::Capability::Runner(fdecl::Runner {
9206                        name: None,
9207                        source_path: None,
9208                        ..Default::default()
9209                    }),
9210                    fdecl::Capability::Resolver(fdecl::Resolver {
9211                        name: None,
9212                        source_path: None,
9213                        ..Default::default()
9214                    }),
9215                    fdecl::Capability::Dictionary(fdecl::Dictionary {
9216                        ..Default::default()
9217                    }),
9218                ]);
9219                decl
9220            },
9221            result = Err(ErrorList::new(vec![
9222                Error::missing_field(DeclType::Dictionary, "name"),
9223                Error::missing_field(DeclType::Service, "name"),
9224                Error::missing_field(DeclType::Service, "source_path"),
9225                Error::missing_field(DeclType::Protocol, "name"),
9226                Error::missing_field(DeclType::Protocol, "source_path"),
9227                Error::missing_field(DeclType::Directory, "name"),
9228                Error::missing_field(DeclType::Directory, "source_path"),
9229                Error::missing_field(DeclType::Directory, "rights"),
9230                Error::missing_field(DeclType::Storage, "source"),
9231                Error::missing_field(DeclType::Storage, "name"),
9232                Error::missing_field(DeclType::Storage, "storage_id"),
9233                Error::missing_field(DeclType::Storage, "backing_dir"),
9234                Error::missing_field(DeclType::Runner, "name"),
9235                Error::missing_field(DeclType::Runner, "source_path"),
9236                Error::missing_field(DeclType::Resolver, "name"),
9237                Error::missing_field(DeclType::Resolver, "source_path"),
9238            ])),
9239        },
9240        test_validate_capabilities_invalid_identifiers => {
9241            input = {
9242                let mut decl = new_component_decl();
9243                decl.capabilities = Some(vec![
9244                    fdecl::Capability::Service(fdecl::Service {
9245                        name: Some("^bad".to_string()),
9246                        source_path: Some("&bad".to_string()),
9247                        ..Default::default()
9248                    }),
9249                    fdecl::Capability::Protocol(fdecl::Protocol {
9250                        name: Some("^bad".to_string()),
9251                        source_path: Some("&bad".to_string()),
9252                        ..Default::default()
9253                    }),
9254                    fdecl::Capability::Directory(fdecl::Directory {
9255                        name: Some("^bad".to_string()),
9256                        source_path: Some("&bad".to_string()),
9257                        rights: Some(fio::Operations::CONNECT),
9258                        ..Default::default()
9259                    }),
9260                    fdecl::Capability::Storage(fdecl::Storage {
9261                        name: Some("^bad".to_string()),
9262                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9263                            name: "/bad".to_string()
9264                        })),
9265                        backing_dir: Some("&bad".to_string()),
9266                        subdir: None,
9267                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9268                        ..Default::default()
9269                    }),
9270                    fdecl::Capability::Runner(fdecl::Runner {
9271                        name: Some("^bad".to_string()),
9272                        source_path: Some("&bad".to_string()),
9273                        ..Default::default()
9274                    }),
9275                    fdecl::Capability::Resolver(fdecl::Resolver {
9276                        name: Some("^bad".to_string()),
9277                        source_path: Some("&bad".to_string()),
9278                        ..Default::default()
9279                    }),
9280                    fdecl::Capability::Dictionary(fdecl::Dictionary {
9281                        name: Some("^bad".to_string()),
9282                        ..Default::default()
9283                    }),
9284                ]);
9285                decl
9286            },
9287            result = Err(ErrorList::new(vec![
9288                Error::invalid_field(DeclType::Dictionary, "name"),
9289                Error::invalid_field(DeclType::Service, "name"),
9290                Error::invalid_field(DeclType::Service, "source_path"),
9291                Error::invalid_field(DeclType::Protocol, "name"),
9292                Error::invalid_field(DeclType::Protocol, "source_path"),
9293                Error::invalid_field(DeclType::Directory, "name"),
9294                Error::invalid_field(DeclType::Directory, "source_path"),
9295                Error::invalid_field(DeclType::Storage, "source"),
9296                Error::invalid_field(DeclType::Storage, "name"),
9297                Error::invalid_field(DeclType::Storage, "backing_dir"),
9298                Error::invalid_field(DeclType::Runner, "name"),
9299                Error::invalid_field(DeclType::Runner, "source_path"),
9300                Error::invalid_field(DeclType::Resolver, "name"),
9301                Error::invalid_field(DeclType::Resolver, "source_path"),
9302            ])),
9303        },
9304        test_validate_capabilities_invalid_child => {
9305            input = {
9306                let mut decl = new_component_decl();
9307                decl.capabilities = Some(vec![
9308                    fdecl::Capability::Storage(fdecl::Storage {
9309                        name: Some("foo".to_string()),
9310                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9311                            name: "invalid".to_string(),
9312                        })),
9313                        backing_dir: Some("foo".to_string()),
9314                        subdir: None,
9315                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9316                        ..Default::default()
9317                    }),
9318                ]);
9319                decl
9320            },
9321            result = Err(ErrorList::new(vec![
9322                Error::invalid_field(DeclType::Storage, "source"),
9323            ])),
9324        },
9325        test_validate_capabilities_long_identifiers => {
9326            input = {
9327                let mut decl = new_component_decl();
9328                decl.capabilities = Some(vec![
9329                    fdecl::Capability::Service(fdecl::Service {
9330                        name: Some("a".repeat(256)),
9331                        source_path: Some("/c".repeat(2048)),
9332                        ..Default::default()
9333                    }),
9334                    fdecl::Capability::Protocol(fdecl::Protocol {
9335                        name: Some("a".repeat(256)),
9336                        source_path: Some("/c".repeat(2048)),
9337                        ..Default::default()
9338                    }),
9339                    fdecl::Capability::Directory(fdecl::Directory {
9340                        name: Some("a".repeat(256)),
9341                        source_path: Some("/c".repeat(2048)),
9342                        rights: Some(fio::Operations::CONNECT),
9343                        ..Default::default()
9344                    }),
9345                    fdecl::Capability::Storage(fdecl::Storage {
9346                        name: Some("a".repeat(256)),
9347                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
9348                            name: "b".repeat(256),
9349                            collection: None,
9350                        })),
9351                        backing_dir: Some(format!("{}", "c".repeat(256))),
9352                        subdir: None,
9353                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9354                        ..Default::default()
9355                    }),
9356                    fdecl::Capability::Runner(fdecl::Runner {
9357                        name: Some("a".repeat(256)),
9358                        source_path: Some("/c".repeat(2048)),
9359                        ..Default::default()
9360                    }),
9361                    fdecl::Capability::Resolver(fdecl::Resolver {
9362                        name: Some("a".repeat(256)),
9363                        source_path: Some("/c".repeat(2048)),
9364                        ..Default::default()
9365                    }),
9366                    fdecl::Capability::Dictionary(fdecl::Dictionary {
9367                        name: Some("a".repeat(256)),
9368                        ..Default::default()
9369                    }),
9370                ]);
9371                decl
9372            },
9373            result = Err(ErrorList::new(vec![
9374                Error::field_too_long(DeclType::Dictionary, "name"),
9375                Error::field_too_long(DeclType::Service, "name"),
9376                Error::field_too_long(DeclType::Service, "source_path"),
9377                Error::field_too_long(DeclType::Protocol, "name"),
9378                Error::field_too_long(DeclType::Protocol, "source_path"),
9379                Error::field_too_long(DeclType::Directory, "name"),
9380                Error::field_too_long(DeclType::Directory, "source_path"),
9381                Error::field_too_long(DeclType::Storage, "source.child.name"),
9382                Error::field_too_long(DeclType::Storage, "name"),
9383                Error::field_too_long(DeclType::Storage, "backing_dir"),
9384                Error::field_too_long(DeclType::Runner, "name"),
9385                Error::field_too_long(DeclType::Runner, "source_path"),
9386                Error::field_too_long(DeclType::Resolver, "name"),
9387                Error::field_too_long(DeclType::Resolver, "source_path"),
9388            ])),
9389        },
9390        test_validate_capabilities_duplicate_name => {
9391            input = {
9392                let mut decl = new_component_decl();
9393                decl.capabilities = Some(vec![
9394                    fdecl::Capability::Service(fdecl::Service {
9395                        name: Some("service".to_string()),
9396                        source_path: Some("/service".to_string()),
9397                        ..Default::default()
9398                    }),
9399                    fdecl::Capability::Service(fdecl::Service {
9400                        name: Some("service".to_string()),
9401                        source_path: Some("/service".to_string()),
9402                        ..Default::default()
9403                    }),
9404                    fdecl::Capability::Protocol(fdecl::Protocol {
9405                        name: Some("protocol".to_string()),
9406                        source_path: Some("/protocol".to_string()),
9407                        ..Default::default()
9408                    }),
9409                    fdecl::Capability::Protocol(fdecl::Protocol {
9410                        name: Some("protocol".to_string()),
9411                        source_path: Some("/protocol".to_string()),
9412                        ..Default::default()
9413                    }),
9414                    fdecl::Capability::Directory(fdecl::Directory {
9415                        name: Some("directory".to_string()),
9416                        source_path: Some("/directory".to_string()),
9417                        rights: Some(fio::Operations::CONNECT),
9418                        ..Default::default()
9419                    }),
9420                    fdecl::Capability::Directory(fdecl::Directory {
9421                        name: Some("directory".to_string()),
9422                        source_path: Some("/directory".to_string()),
9423                        rights: Some(fio::Operations::CONNECT),
9424                        ..Default::default()
9425                    }),
9426                    fdecl::Capability::Storage(fdecl::Storage {
9427                        name: Some("storage".to_string()),
9428                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
9429                        backing_dir: Some("directory".to_string()),
9430                        subdir: None,
9431                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9432                        ..Default::default()
9433                    }),
9434                    fdecl::Capability::Storage(fdecl::Storage {
9435                        name: Some("storage".to_string()),
9436                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
9437                        backing_dir: Some("directory".to_string()),
9438                        subdir: None,
9439                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9440                        ..Default::default()
9441                    }),
9442                    fdecl::Capability::Runner(fdecl::Runner {
9443                        name: Some("runner".to_string()),
9444                        source_path: Some("/runner".to_string()),
9445                        ..Default::default()
9446                    }),
9447                    fdecl::Capability::Runner(fdecl::Runner {
9448                        name: Some("runner".to_string()),
9449                        source_path: Some("/runner".to_string()),
9450                        ..Default::default()
9451                    }),
9452                    fdecl::Capability::Resolver(fdecl::Resolver {
9453                        name: Some("resolver".to_string()),
9454                        source_path: Some("/resolver".to_string()),
9455                        ..Default::default()
9456                    }),
9457                    fdecl::Capability::Resolver(fdecl::Resolver {
9458                        name: Some("resolver".to_string()),
9459                        source_path: Some("/resolver".to_string()),
9460                        ..Default::default()
9461                    }),
9462                    fdecl::Capability::Dictionary(fdecl::Dictionary {
9463                        name: Some("dictionary".to_string()),
9464                        ..Default::default()
9465                    }),
9466                    fdecl::Capability::Dictionary(fdecl::Dictionary {
9467                        name: Some("dictionary".to_string()),
9468                        ..Default::default()
9469                    }),
9470                ]);
9471                decl
9472            },
9473            result = Err(ErrorList::new(vec![
9474                Error::duplicate_field(DeclType::Dictionary, "name", "dictionary"),
9475                Error::duplicate_field(DeclType::Service, "name", "service"),
9476                Error::duplicate_field(DeclType::Protocol, "name", "protocol"),
9477                Error::duplicate_field(DeclType::Directory, "name", "directory"),
9478                Error::duplicate_field(DeclType::Storage, "name", "storage"),
9479                Error::duplicate_field(DeclType::Runner, "name", "runner"),
9480                Error::duplicate_field(DeclType::Resolver, "name", "resolver"),
9481            ])),
9482        },
9483        test_validate_invalid_service_aggregation_conflicting_filter => {
9484            input = {
9485                let mut decl = new_component_decl();
9486                decl.offers = Some(vec![
9487                    fdecl::Offer::Service(fdecl::OfferService {
9488                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9489                            name: "coll_a".to_string()
9490                        })),
9491                        source_name: Some("fuchsia.logger.Log".to_string()),
9492                        target: Some(fdecl::Ref::Child(
9493                            fdecl::ChildRef {
9494                                name: "child_c".to_string(),
9495                                collection: None,
9496                            }
9497                        )),
9498                        target_name: Some("fuchsia.logger.Log1".to_string()),
9499                        source_instance_filter: Some(vec!["default".to_string()]),
9500                        ..Default::default()
9501                    }),
9502                    fdecl::Offer::Service(fdecl::OfferService {
9503                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9504                            name: "coll_b".to_string()
9505                        })),
9506                        source_name: Some("fuchsia.logger.Log".to_string()),
9507                        target: Some(fdecl::Ref::Child(
9508                            fdecl::ChildRef {
9509                                name: "child_c".to_string(),
9510                                collection: None,
9511                            }
9512                        )),
9513                        target_name: Some("fuchsia.logger.Log1".to_string()),
9514                        source_instance_filter: Some(vec!["default".to_string()]),
9515                        ..Default::default()
9516                    }),
9517                ]);
9518                decl.collections = Some(vec![
9519                    fdecl::Collection {
9520                        name: Some("coll_a".to_string()),
9521                        durability: Some(fdecl::Durability::Transient),
9522                        ..Default::default()
9523                    },
9524                    fdecl::Collection {
9525                        name: Some("coll_b".to_string()),
9526                        durability: Some(fdecl::Durability::Transient),
9527                        ..Default::default()
9528                    },
9529                ]);
9530                decl.children = Some(vec![
9531                    fdecl::Child {
9532                        name: Some("child_c".to_string()),
9533                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9534                        startup: Some(fdecl::StartupMode::Lazy),
9535                        ..Default::default()
9536                    },
9537                ]);
9538                decl
9539            },
9540            result = Err(ErrorList::new(vec![
9541                Error::invalid_aggregate_offer("Conflicting source_instance_filter in aggregate \
9542                   service offer, instance_name 'default' seen in filter lists multiple times"),
9543            ])),
9544        },
9545
9546        test_validate_invalid_service_aggregation_conflicting_source_name => {
9547            input = {
9548                let mut decl = new_component_decl();
9549                decl.offers = Some(vec![
9550                    fdecl::Offer::Service(fdecl::OfferService {
9551                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9552                            name: "coll_a".into()
9553                        })),
9554                        source_name: Some("fuchsia.logger.Log".to_string()),
9555                        target: Some(fdecl::Ref::Child(
9556                            fdecl::ChildRef {
9557                                name: "child_c".to_string(),
9558                                collection: None,
9559                            }
9560                        )),
9561                        target_name: Some("fuchsia.logger.Log2".to_string()),
9562                        source_instance_filter: Some(vec!["default2".to_string()]),
9563                        ..Default::default()
9564                    }),
9565                    fdecl::Offer::Service(fdecl::OfferService {
9566                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9567                            name: "coll_b".into()
9568                        })),
9569                        source_name: Some("fuchsia.logger.LogAlt".to_string()),
9570                        target: Some(fdecl::Ref::Child(
9571                            fdecl::ChildRef {
9572                                name: "child_c".to_string(),
9573                                collection: None,
9574                            }
9575                        )),
9576                        target_name: Some("fuchsia.logger.Log2".to_string()),
9577                        source_instance_filter: Some(vec!["default".to_string()]),
9578                        ..Default::default()
9579                    })
9580                ]);
9581                decl.collections = Some(vec![
9582                    fdecl::Collection {
9583                        name: Some("coll_a".to_string()),
9584                        durability: Some(fdecl::Durability::Transient),
9585                        ..Default::default()
9586                    },
9587                    fdecl::Collection {
9588                        name: Some("coll_b".to_string()),
9589                        durability: Some(fdecl::Durability::Transient),
9590                        ..Default::default()
9591                    },
9592                ]);
9593                decl.children = Some(vec![
9594                    fdecl::Child {
9595                        name: Some("child_c".to_string()),
9596                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9597                        startup: Some(fdecl::StartupMode::Lazy),
9598                        on_terminate: None,
9599                        environment: None,
9600                        ..Default::default()
9601                    },
9602                ]);
9603                decl
9604            },
9605            result = Err(ErrorList::new(vec![
9606                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."),
9607            ])),
9608        },
9609
9610        test_validate_resolvers_missing_from_offer => {
9611            input = {
9612                let mut decl = new_component_decl();
9613                decl.offers = Some(vec![fdecl::Offer::Resolver(fdecl::OfferResolver {
9614                    source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
9615                    source_name: Some("a".to_string()),
9616                    target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
9617                    target_name: Some("a".to_string()),
9618                    ..Default::default()
9619                })]);
9620                decl.children = Some(vec![fdecl::Child {
9621                    name: Some("child".to_string()),
9622                    url: Some("test:///child".to_string()),
9623                    startup: Some(fdecl::StartupMode::Eager),
9624                    on_terminate: None,
9625                    environment: None,
9626                    ..Default::default()
9627                }]);
9628                decl
9629            },
9630            result = Err(ErrorList::new(vec![
9631                Error::invalid_capability(DeclType::OfferResolver, "source", "a"),
9632            ])),
9633        },
9634        test_validate_resolvers_missing_from_expose => {
9635            input = {
9636                let mut decl = new_component_decl();
9637                decl.exposes = Some(vec![fdecl::Expose::Resolver(fdecl::ExposeResolver {
9638                    source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
9639                    source_name: Some("a".to_string()),
9640                    target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
9641                    target_name: Some("a".to_string()),
9642                    ..Default::default()
9643                })]);
9644                decl
9645            },
9646            result = Err(ErrorList::new(vec![
9647                Error::invalid_capability(DeclType::ExposeResolver, "source", "a"),
9648            ])),
9649        },
9650
9651        test_validate_config_missing_config => {
9652            input = {
9653                let mut decl = new_component_decl();
9654                decl.config = Some(fdecl::ConfigSchema{
9655                    fields: None,
9656                    checksum: None,
9657                    value_source: None,
9658                    ..Default::default()
9659                });
9660                decl
9661            },
9662            result = Err(ErrorList::new(vec![
9663                Error::missing_field(DeclType::ConfigSchema, "fields"),
9664                Error::missing_field(DeclType::ConfigSchema, "checksum"),
9665                Error::missing_field(DeclType::ConfigSchema, "value_source"),
9666            ])),
9667        },
9668
9669        test_validate_config_missing_config_field => {
9670            input = {
9671                let mut decl = new_component_decl();
9672                decl.config = Some(fdecl::ConfigSchema{
9673                    fields: Some(vec![
9674                        fdecl::ConfigField {
9675                            key: None,
9676                            type_: None,
9677                            ..Default::default()
9678                        }
9679                    ]),
9680                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9681                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9682                    ..Default::default()
9683                });
9684                decl
9685            },
9686            result = Err(ErrorList::new(vec![
9687                Error::missing_field(DeclType::ConfigField, "key"),
9688                Error::missing_field(DeclType::ConfigField, "value_type"),
9689            ])),
9690        },
9691
9692        test_validate_config_bool => {
9693            input = {
9694                let mut decl = new_component_decl();
9695                decl.config = Some(fdecl::ConfigSchema{
9696                    fields: Some(vec![
9697                        fdecl::ConfigField {
9698                            key: Some("test".to_string()),
9699                            type_: Some(fdecl::ConfigType {
9700                                layout: fdecl::ConfigTypeLayout::Bool,
9701                                parameters: Some(vec![]),
9702                                constraints: vec![]
9703                            }),
9704                            ..Default::default()
9705                        }
9706                    ]),
9707                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9708                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9709                    ..Default::default()
9710                });
9711                decl
9712            },
9713            result = Ok(()),
9714        },
9715
9716        test_validate_config_bool_extra_constraint => {
9717            input = {
9718                let mut decl = new_component_decl();
9719                decl.config = Some(fdecl::ConfigSchema{
9720                    fields: Some(vec![
9721                        fdecl::ConfigField {
9722                            key: Some("test".to_string()),
9723                            type_: Some(fdecl::ConfigType {
9724                                layout: fdecl::ConfigTypeLayout::Bool,
9725                                parameters: Some(vec![]),
9726                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9727                            }),
9728                            ..Default::default()
9729                        }
9730                    ]),
9731                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9732                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9733                    ..Default::default()
9734                });
9735                decl
9736            },
9737            result = Err(ErrorList::new(vec![
9738                Error::extraneous_field(DeclType::ConfigType, "constraints")
9739            ])),
9740        },
9741
9742        test_validate_config_bool_missing_parameters => {
9743            input = {
9744                let mut decl = new_component_decl();
9745                decl.config = Some(fdecl::ConfigSchema{
9746                    fields: Some(vec![
9747                        fdecl::ConfigField {
9748                            key: Some("test".to_string()),
9749                            type_: Some(fdecl::ConfigType {
9750                                layout: fdecl::ConfigTypeLayout::Bool,
9751                                parameters: None,
9752                                constraints: vec![]
9753                            }),
9754                            ..Default::default()
9755                        }
9756                    ]),
9757                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9758                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9759                    ..Default::default()
9760                });
9761                decl
9762            },
9763            result = Err(ErrorList::new(vec![
9764                Error::missing_field(DeclType::ConfigType, "parameters")
9765            ])),
9766        },
9767
9768        test_validate_config_string => {
9769            input = {
9770                let mut decl = new_component_decl();
9771                decl.config = Some(fdecl::ConfigSchema{
9772                    fields: Some(vec![
9773                        fdecl::ConfigField {
9774                            key: Some("test".to_string()),
9775                            type_: Some(fdecl::ConfigType {
9776                                layout: fdecl::ConfigTypeLayout::String,
9777                                parameters: Some(vec![]),
9778                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9779                            }),
9780                            ..Default::default()
9781                        }
9782                    ]),
9783                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9784                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9785                    ..Default::default()
9786                });
9787                decl
9788            },
9789            result = Ok(()),
9790        },
9791
9792        test_validate_config_string_missing_parameter => {
9793            input = {
9794                let mut decl = new_component_decl();
9795                decl.config = Some(fdecl::ConfigSchema{
9796                    fields: Some(vec![
9797                        fdecl::ConfigField {
9798                            key: Some("test".to_string()),
9799                            type_: Some(fdecl::ConfigType {
9800                                layout: fdecl::ConfigTypeLayout::String,
9801                                parameters: None,
9802                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9803                            }),
9804                            ..Default::default()
9805                        }
9806                    ]),
9807                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9808                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9809                    ..Default::default()
9810                });
9811                decl
9812            },
9813            result = Err(ErrorList::new(vec![
9814                Error::missing_field(DeclType::ConfigType, "parameters")
9815            ])),
9816        },
9817
9818        test_validate_config_string_missing_constraint => {
9819            input = {
9820                let mut decl = new_component_decl();
9821                decl.config = Some(fdecl::ConfigSchema{
9822                    fields: Some(vec![
9823                        fdecl::ConfigField {
9824                            key: Some("test".to_string()),
9825                            type_: Some(fdecl::ConfigType {
9826                                layout: fdecl::ConfigTypeLayout::String,
9827                                parameters: Some(vec![]),
9828                                constraints: vec![]
9829                            }),
9830                            ..Default::default()
9831                        }
9832                    ]),
9833                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9834                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9835                    ..Default::default()
9836                });
9837                decl
9838            },
9839            result = Err(ErrorList::new(vec![
9840                Error::missing_field(DeclType::ConfigType, "constraints")
9841            ])),
9842        },
9843
9844        test_validate_config_string_extra_constraint => {
9845            input = {
9846                let mut decl = new_component_decl();
9847                decl.config = Some(fdecl::ConfigSchema{
9848                    fields: Some(vec![
9849                        fdecl::ConfigField {
9850                            key: Some("test".to_string()),
9851                            type_: Some(fdecl::ConfigType {
9852                                layout: fdecl::ConfigTypeLayout::String,
9853                                parameters: Some(vec![]),
9854                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10), fdecl::LayoutConstraint::MaxSize(10)]
9855                            }),
9856                            ..Default::default()
9857                        }
9858                    ]),
9859                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9860                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9861                    ..Default::default()
9862                });
9863                decl
9864            },
9865            result = Err(ErrorList::new(vec![
9866                Error::extraneous_field(DeclType::ConfigType, "constraints")
9867            ])),
9868        },
9869
9870        test_validate_config_vector_bool => {
9871            input = {
9872                let mut decl = new_component_decl();
9873                decl.config = Some(fdecl::ConfigSchema{
9874                    fields: Some(vec![
9875                        fdecl::ConfigField {
9876                            key: Some("test".to_string()),
9877                            type_: Some(fdecl::ConfigType {
9878                                layout: fdecl::ConfigTypeLayout::Vector,
9879                                parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9880                                    layout: fdecl::ConfigTypeLayout::Bool,
9881                                    parameters: Some(vec![]),
9882                                    constraints: vec![],
9883                                })]),
9884                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9885                            }),
9886                            ..Default::default()
9887                        }
9888                    ]),
9889                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9890                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9891                    ..Default::default()
9892                });
9893                decl
9894            },
9895            result = Ok(()),
9896        },
9897
9898        test_validate_config_vector_extra_parameter => {
9899            input = {
9900                let mut decl = new_component_decl();
9901                decl.config = Some(fdecl::ConfigSchema{
9902                    fields: Some(vec![
9903                        fdecl::ConfigField {
9904                            key: Some("test".to_string()),
9905                            type_: Some(fdecl::ConfigType {
9906                                layout: fdecl::ConfigTypeLayout::Vector,
9907                                parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9908                                    layout: fdecl::ConfigTypeLayout::Bool,
9909                                    parameters: Some(vec![]),
9910                                    constraints: vec![],
9911                                }), fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9912                                    layout: fdecl::ConfigTypeLayout::Uint8,
9913                                    parameters: Some(vec![]),
9914                                    constraints: vec![],
9915                                })]),
9916                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9917                            }),
9918                            ..Default::default()
9919                        }
9920                    ]),
9921                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9922                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9923                    ..Default::default()
9924                });
9925                decl
9926            },
9927            result = Err(ErrorList::new(vec![
9928                Error::extraneous_field(DeclType::ConfigType, "parameters")
9929            ])),
9930        },
9931
9932        test_validate_config_vector_missing_parameter => {
9933            input = {
9934                let mut decl = new_component_decl();
9935                decl.config = Some(fdecl::ConfigSchema{
9936                    fields: Some(vec![
9937                        fdecl::ConfigField {
9938                            key: Some("test".to_string()),
9939                            type_: Some(fdecl::ConfigType {
9940                                layout: fdecl::ConfigTypeLayout::Vector,
9941                                parameters: Some(vec![]),
9942                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9943                            }),
9944                            ..Default::default()
9945                        }
9946                    ]),
9947                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9948                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9949                    ..Default::default()
9950                });
9951                decl
9952            },
9953            result = Err(ErrorList::new(vec![
9954                Error::missing_field(DeclType::ConfigType, "parameters")
9955            ])),
9956        },
9957
9958        test_validate_config_vector_string => {
9959            input = {
9960                let mut decl = new_component_decl();
9961                decl.config = Some(fdecl::ConfigSchema{
9962                    fields: Some(vec![
9963                        fdecl::ConfigField {
9964                            key: Some("test".to_string()),
9965                            type_: Some(fdecl::ConfigType {
9966                                layout: fdecl::ConfigTypeLayout::Vector,
9967                                parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9968                                    layout: fdecl::ConfigTypeLayout::String,
9969                                    parameters: Some(vec![]),
9970                                    constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9971                                })]),
9972                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9973                            }),
9974                            ..Default::default()
9975                        }
9976                    ]),
9977                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9978                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9979                    ..Default::default()
9980                });
9981                decl
9982            },
9983            result = Ok(()),
9984        },
9985
9986        test_validate_config_vector_vector => {
9987            input = {
9988                let mut decl = new_component_decl();
9989                decl.config = Some(fdecl::ConfigSchema{
9990                    fields: Some(vec![
9991                        fdecl::ConfigField {
9992                            key: Some("test".to_string()),
9993                            type_: Some(fdecl::ConfigType {
9994                                layout: fdecl::ConfigTypeLayout::Vector,
9995                                parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9996                                    layout: fdecl::ConfigTypeLayout::Vector,
9997                                    parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9998                                        layout: fdecl::ConfigTypeLayout::String,
9999                                        parameters: Some(vec![]),
10000                                        constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
10001                                    })]),
10002                                    constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
10003                                })]),
10004                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
10005                            }),
10006                            ..Default::default()
10007                        }
10008                    ]),
10009                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
10010                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
10011                    ..Default::default()
10012                });
10013                decl
10014            },
10015            result = Err(ErrorList::new(vec![
10016                Error::nested_vector()
10017            ])),
10018        },
10019
10020        test_validate_exposes_invalid_aggregation_different_availability => {
10021            input = {
10022                let mut decl = new_component_decl();
10023                decl.exposes = Some(vec![
10024                    fdecl::Expose::Service(fdecl::ExposeService {
10025                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10026                            name: "coll_a".into()
10027                        })),
10028                        source_name: Some("fuchsia.logger.Log".to_string()),
10029                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
10030                        target_name: Some("fuchsia.logger.Log".to_string()),
10031                        availability: Some(fdecl::Availability::Required),
10032                        ..Default::default()
10033                    }),
10034                    fdecl::Expose::Service(fdecl::ExposeService {
10035                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10036                            name: "coll_b".into()
10037                        })),
10038                        source_name: Some("fuchsia.logger.Log".to_string()),
10039                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
10040                        target_name: Some("fuchsia.logger.Log".to_string()),
10041                        availability: Some(fdecl::Availability::Optional),
10042                        ..Default::default()
10043                    })
10044                ]);
10045                decl.collections = Some(vec![
10046                    fdecl::Collection {
10047                        name: Some("coll_a".to_string()),
10048                        durability: Some(fdecl::Durability::Transient),
10049                        ..Default::default()
10050                    },
10051                    fdecl::Collection {
10052                        name: Some("coll_b".to_string()),
10053                        durability: Some(fdecl::Durability::Transient),
10054                        ..Default::default()
10055                    },
10056                ]);
10057                decl
10058            },
10059            result = Err(ErrorList::new(vec![
10060                Error::DifferentAvailabilityInAggregation(AvailabilityList(vec![
10061                    fdecl::Availability::Required,
10062                    fdecl::Availability::Optional,
10063                ]))
10064            ])),
10065        },
10066
10067        test_validate_offers_invalid_aggregation_different_availability => {
10068            input = {
10069                let mut decl = new_component_decl();
10070                decl.offers = Some(vec![
10071                    fdecl::Offer::Service(fdecl::OfferService {
10072                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10073                            name: "coll_a".into()
10074                        })),
10075                        source_name: Some("fuchsia.logger.Log".to_string()),
10076                        target: Some(fdecl::Ref::Child(
10077                            fdecl::ChildRef {
10078                                name: "child_c".to_string(),
10079                                collection: None,
10080                            }
10081                        )),
10082                        target_name: Some("fuchsia.logger.Log".to_string()),
10083                        source_instance_filter: Some(vec!["default".to_string()]),
10084                        availability: Some(fdecl::Availability::Required),
10085                        ..Default::default()
10086                    }),
10087                    fdecl::Offer::Service(fdecl::OfferService {
10088                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10089                            name: "coll_b".into()
10090                        })),
10091                        source_name: Some("fuchsia.logger.Log".to_string()),
10092                        target: Some(fdecl::Ref::Child(
10093                            fdecl::ChildRef {
10094                                name: "child_c".to_string(),
10095                                collection: None,
10096                            }
10097                        )),
10098                        target_name: Some("fuchsia.logger.Log".to_string()),
10099                        source_instance_filter: Some(vec!["a_different_default".to_string()]),
10100                        availability: Some(fdecl::Availability::Optional),
10101                        ..Default::default()
10102                    })
10103                ]);
10104                decl.collections = Some(vec![
10105                    fdecl::Collection {
10106                        name: Some("coll_a".to_string()),
10107                        durability: Some(fdecl::Durability::Transient),
10108                        ..Default::default()
10109                    },
10110                    fdecl::Collection {
10111                        name: Some("coll_b".to_string()),
10112                        durability: Some(fdecl::Durability::Transient),
10113                        ..Default::default()
10114                    },
10115                ]);
10116                decl.children = Some(vec![
10117                    fdecl::Child {
10118                        name: Some("child_c".to_string()),
10119                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
10120                        startup: Some(fdecl::StartupMode::Lazy),
10121                        on_terminate: None,
10122                        environment: None,
10123                        ..Default::default()
10124                    },
10125                ]);
10126                decl
10127            },
10128            result = Err(ErrorList::new(vec![
10129                Error::DifferentAvailabilityInAggregation(AvailabilityList(vec![
10130                    fdecl::Availability::Required,
10131                    fdecl::Availability::Optional,
10132                ]))
10133            ])),
10134        },
10135    }
10136
10137    test_validate_capabilities! {
10138        test_validate_capabilities_individually_ok => {
10139            input = vec![
10140                fdecl::Capability::Protocol(fdecl::Protocol {
10141                    name: Some("foo_svc".into()),
10142                    source_path: Some("/svc/foo".into()),
10143                    ..Default::default()
10144                }),
10145                fdecl::Capability::Directory(fdecl::Directory {
10146                    name: Some("foo_dir".into()),
10147                    source_path: Some("/foo".into()),
10148                    rights: Some(fio::Operations::CONNECT),
10149                    ..Default::default()
10150                }),
10151            ],
10152            as_builtin = false,
10153            result = Ok(()),
10154        },
10155        test_validate_capabilities_individually_err => {
10156            input = vec![
10157                fdecl::Capability::Protocol(fdecl::Protocol {
10158                    name: None,
10159                    source_path: None,
10160                    ..Default::default()
10161                }),
10162                fdecl::Capability::Directory(fdecl::Directory {
10163                    name: None,
10164                    source_path: None,
10165                    rights: None,
10166                    ..Default::default()
10167                }),
10168            ],
10169            as_builtin = false,
10170            result = Err(ErrorList::new(vec![
10171                Error::missing_field(DeclType::Protocol, "name"),
10172                Error::missing_field(DeclType::Protocol, "source_path"),
10173                Error::missing_field(DeclType::Directory, "name"),
10174                Error::missing_field(DeclType::Directory, "source_path"),
10175                Error::missing_field(DeclType::Directory, "rights"),
10176            ])),
10177        },
10178        test_validate_builtin_capabilities_individually_ok => {
10179            input = vec![
10180                fdecl::Capability::Protocol(fdecl::Protocol {
10181                    name: Some("foo_protocol".into()),
10182                    source_path: None,
10183                    ..Default::default()
10184                }),
10185                fdecl::Capability::Directory(fdecl::Directory {
10186                    name: Some("foo_dir".into()),
10187                    source_path: None,
10188                    rights: Some(fio::Operations::CONNECT),
10189                    ..Default::default()
10190                }),
10191                fdecl::Capability::Service(fdecl::Service {
10192                    name: Some("foo_svc".into()),
10193                    source_path: None,
10194                    ..Default::default()
10195                }),
10196                fdecl::Capability::Runner(fdecl::Runner {
10197                    name: Some("foo_runner".into()),
10198                    source_path: None,
10199                    ..Default::default()
10200                }),
10201                fdecl::Capability::Resolver(fdecl::Resolver {
10202                    name: Some("foo_resolver".into()),
10203                    source_path: None,
10204                    ..Default::default()
10205                }),
10206            ],
10207            as_builtin = true,
10208            result = Ok(()),
10209        },
10210        test_validate_builtin_capabilities_individually_err => {
10211            input = vec![
10212                fdecl::Capability::Protocol(fdecl::Protocol {
10213                    name: None,
10214                    source_path: Some("/svc/foo".into()),
10215                    ..Default::default()
10216                }),
10217                fdecl::Capability::Directory(fdecl::Directory {
10218                    name: None,
10219                    source_path: Some("/foo".into()),
10220                    rights: None,
10221                    ..Default::default()
10222                }),
10223                fdecl::Capability::Service(fdecl::Service {
10224                    name: None,
10225                    source_path: Some("/svc/foo".into()),
10226                    ..Default::default()
10227                }),
10228                fdecl::Capability::Runner(fdecl::Runner {
10229                    name: None,
10230                    source_path:  Some("/foo".into()),
10231                    ..Default::default()
10232                }),
10233                fdecl::Capability::Resolver(fdecl::Resolver {
10234                    name: None,
10235                    source_path:  Some("/foo".into()),
10236                    ..Default::default()
10237                }),
10238                fdecl::Capability::Storage(fdecl::Storage {
10239                    name: None,
10240                    ..Default::default()
10241                }),
10242            ],
10243            as_builtin = true,
10244            result = Err(ErrorList::new(vec![
10245                Error::missing_field(DeclType::Protocol, "name"),
10246                Error::extraneous_source_path(DeclType::Protocol, "/svc/foo"),
10247                Error::missing_field(DeclType::Directory, "name"),
10248                Error::extraneous_source_path(DeclType::Directory, "/foo"),
10249                Error::missing_field(DeclType::Directory, "rights"),
10250                Error::missing_field(DeclType::Service, "name"),
10251                Error::extraneous_source_path(DeclType::Service, "/svc/foo"),
10252                Error::missing_field(DeclType::Runner, "name"),
10253                Error::extraneous_source_path(DeclType::Runner, "/foo"),
10254                Error::missing_field(DeclType::Resolver, "name"),
10255                Error::extraneous_source_path(DeclType::Resolver, "/foo"),
10256                Error::CapabilityCannotBeBuiltin(DeclType::Storage),
10257            ])),
10258        },
10259        test_validate_delivery_type_ok => {
10260            input = vec![
10261                fdecl::Capability::Protocol(fdecl::Protocol {
10262                    name: Some("foo_svc1".into()),
10263                    source_path: Some("/svc/foo1".into()),
10264                    ..Default::default()
10265                }),
10266                fdecl::Capability::Protocol(fdecl::Protocol {
10267                    name: Some("foo_svc2".into()),
10268                    source_path: Some("/svc/foo2".into()),
10269                    delivery: Some(fdecl::DeliveryType::Immediate),
10270                    ..Default::default()
10271                }),
10272                fdecl::Capability::Protocol(fdecl::Protocol {
10273                    name: Some("foo_svc3".into()),
10274                    source_path: Some("/svc/foo3".into()),
10275                    delivery: Some(fdecl::DeliveryType::OnReadable),
10276                    ..Default::default()
10277                }),
10278            ],
10279            as_builtin = false,
10280            result = Ok(()),
10281        },
10282        test_validate_delivery_type_err => {
10283            input = vec![
10284                fdecl::Capability::Protocol(fdecl::Protocol {
10285                    name: Some("foo_svc".into()),
10286                    source_path: Some("/svc/foo".into()),
10287                    delivery: Some(fdecl::DeliveryType::unknown()),
10288                    ..Default::default()
10289                }),
10290            ],
10291            as_builtin = false,
10292            result = Err(ErrorList::new(vec![
10293                Error::invalid_field(DeclType::Protocol, "delivery"),
10294            ])),
10295        },
10296    }
10297
10298    /// Passes different source and availability options to `new_expose` to
10299    /// generate a component declaration.
10300    fn generate_expose_different_source_and_availability_decl(
10301        new_expose: impl Fn(fdecl::Ref, fdecl::Availability, &str) -> fdecl::Expose,
10302    ) -> fdecl::Component {
10303        let mut decl = new_component_decl();
10304        let child = "child";
10305        decl.children = Some(vec![fdecl::Child {
10306            name: Some(child.to_string()),
10307            url: Some("fuchsia-pkg://fuchsia.com/source#meta/source.cm".to_string()),
10308            startup: Some(fdecl::StartupMode::Lazy),
10309            ..Default::default()
10310        }]);
10311        decl.exposes = Some(vec![
10312            // Optional expose from self is okay.
10313            new_expose(
10314                fdecl::Ref::Self_(fdecl::SelfRef {}),
10315                fdecl::Availability::Optional,
10316                "fuchsia.examples.Echo1",
10317            ),
10318            // Optional expose from child is okay.
10319            new_expose(
10320                fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
10321                fdecl::Availability::Optional,
10322                "fuchsia.examples.Echo2",
10323            ),
10324            // Optional expose from void is okay.
10325            new_expose(
10326                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10327                fdecl::Availability::Optional,
10328                "fuchsia.examples.Echo3",
10329            ),
10330            // Optional expose from framework is okay.
10331            new_expose(
10332                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10333                fdecl::Availability::Optional,
10334                "fuchsia.examples.Echo4",
10335            ),
10336            // Transitional expose from self is okay.
10337            new_expose(
10338                fdecl::Ref::Self_(fdecl::SelfRef {}),
10339                fdecl::Availability::Transitional,
10340                "fuchsia.examples.Echo5",
10341            ),
10342            // Transitional expose from child is okay.
10343            new_expose(
10344                fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
10345                fdecl::Availability::Transitional,
10346                "fuchsia.examples.Echo6",
10347            ),
10348            // Transitional expose from void is okay.
10349            new_expose(
10350                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10351                fdecl::Availability::Transitional,
10352                "fuchsia.examples.Echo7",
10353            ),
10354            // Transitional expose from framework is okay.
10355            new_expose(
10356                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10357                fdecl::Availability::Transitional,
10358                "fuchsia.examples.Echo8",
10359            ),
10360            // Same-as-target expose from self is okay.
10361            new_expose(
10362                fdecl::Ref::Self_(fdecl::SelfRef {}),
10363                fdecl::Availability::SameAsTarget,
10364                "fuchsia.examples.Echo9",
10365            ),
10366            // Same-as-target expose from child is okay.
10367            new_expose(
10368                fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
10369                fdecl::Availability::SameAsTarget,
10370                "fuchsia.examples.Echo10",
10371            ),
10372            // Same-as-target expose from framework is okay.
10373            new_expose(
10374                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10375                fdecl::Availability::SameAsTarget,
10376                "fuchsia.examples.Echo11",
10377            ),
10378            // Required expose from void is an error.
10379            new_expose(
10380                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10381                fdecl::Availability::Required,
10382                "fuchsia.examples.Echo12",
10383            ),
10384            // Same-as-target expose from void is an error.
10385            new_expose(
10386                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10387                fdecl::Availability::SameAsTarget,
10388                "fuchsia.examples.Echo13",
10389            ),
10390        ]);
10391        decl
10392    }
10393
10394    /// Passes different source and availability options to `new_offer` to
10395    /// generate a component declaration.
10396    fn generate_offer_different_source_and_availability_decl(
10397        new_offer: impl Fn(fdecl::Ref, fdecl::Availability, &str) -> fdecl::Offer,
10398    ) -> fdecl::Component {
10399        let mut decl = new_component_decl();
10400        decl.children = Some(vec![
10401            fdecl::Child {
10402                name: Some("source".to_string()),
10403                url: Some("fuchsia-pkg://fuchsia.com/source#meta/source.cm".to_string()),
10404                startup: Some(fdecl::StartupMode::Lazy),
10405                on_terminate: None,
10406                environment: None,
10407                ..Default::default()
10408            },
10409            fdecl::Child {
10410                name: Some("sink".to_string()),
10411                url: Some("fuchsia-pkg://fuchsia.com/sink#meta/sink.cm".to_string()),
10412                startup: Some(fdecl::StartupMode::Lazy),
10413                on_terminate: None,
10414                environment: None,
10415                ..Default::default()
10416            },
10417        ]);
10418        decl.offers = Some(vec![
10419            // These offers are fine, offers with a source of parent or void can be
10420            // optional.
10421            new_offer(
10422                fdecl::Ref::Parent(fdecl::ParentRef {}),
10423                fdecl::Availability::Required,
10424                "fuchsia.examples.Echo0",
10425            ),
10426            new_offer(
10427                fdecl::Ref::Parent(fdecl::ParentRef {}),
10428                fdecl::Availability::Optional,
10429                "fuchsia.examples.Echo1",
10430            ),
10431            new_offer(
10432                fdecl::Ref::Parent(fdecl::ParentRef {}),
10433                fdecl::Availability::SameAsTarget,
10434                "fuchsia.examples.Echo2",
10435            ),
10436            new_offer(
10437                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10438                fdecl::Availability::Optional,
10439                "fuchsia.examples.Echo3",
10440            ),
10441            // These offers are fine, offers with a source other than parent or void
10442            // can also be optional.
10443            new_offer(
10444                fdecl::Ref::Self_(fdecl::SelfRef {}),
10445                fdecl::Availability::Optional,
10446                "fuchsia.examples.Echo4",
10447            ),
10448            new_offer(
10449                fdecl::Ref::Self_(fdecl::SelfRef {}),
10450                fdecl::Availability::SameAsTarget,
10451                "fuchsia.examples.Echo5",
10452            ),
10453            new_offer(
10454                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10455                fdecl::Availability::Optional,
10456                "fuchsia.examples.Echo6",
10457            ),
10458            new_offer(
10459                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10460                fdecl::Availability::SameAsTarget,
10461                "fuchsia.examples.Echo7",
10462            ),
10463            new_offer(
10464                fdecl::Ref::Child(fdecl::ChildRef { name: "source".to_string(), collection: None }),
10465                fdecl::Availability::Optional,
10466                "fuchsia.examples.Echo8",
10467            ),
10468            new_offer(
10469                fdecl::Ref::Child(fdecl::ChildRef { name: "source".to_string(), collection: None }),
10470                fdecl::Availability::SameAsTarget,
10471                "fuchsia.examples.Echo9",
10472            ),
10473            // These offers are also not fine, offers with a source of void must be optional
10474            new_offer(
10475                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10476                fdecl::Availability::Required,
10477                "fuchsia.examples.Echo10",
10478            ),
10479            new_offer(
10480                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10481                fdecl::Availability::SameAsTarget,
10482                "fuchsia.examples.Echo11",
10483            ),
10484        ]);
10485        decl
10486    }
10487
10488    #[test]
10489    fn test_validate_dynamic_offers_empty() {
10490        assert_eq!(
10491            validate_dynamic_offers(vec![], [], [], &[], &[], &mut DirectedGraph::new(), &vec![]),
10492            Ok(())
10493        );
10494    }
10495
10496    #[test]
10497    fn test_validate_dynamic_offers_okay() {
10498        assert_eq!(
10499            validate_dynamic_offers(
10500                vec![],
10501                [],
10502                [],
10503                &[fdecl::Capability::Protocol(fdecl::Protocol {
10504                    name: Some("thing".to_string()),
10505                    source_path: Some("/svc/foo".into()),
10506                    ..Default::default()
10507                })],
10508                &[],
10509                &mut DirectedGraph::new(),
10510                &vec![
10511                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
10512                        dependency_type: Some(fdecl::DependencyType::Strong),
10513                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef)),
10514                        source_name: Some("thing".to_string()),
10515                        target_name: Some("thing".repeat(26)),
10516                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10517                            name: "foo".to_string(),
10518                            collection: Some("foo".to_string()),
10519                        })),
10520                        ..Default::default()
10521                    }),
10522                    fdecl::Offer::Service(fdecl::OfferService {
10523                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10524                        source_name: Some("thang".repeat(26)),
10525                        target_name: Some("thang".repeat(26)),
10526                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10527                            name: "foo".to_string(),
10528                            collection: Some("foo".to_string()),
10529                        })),
10530                        ..Default::default()
10531                    }),
10532                    fdecl::Offer::Directory(fdecl::OfferDirectory {
10533                        dependency_type: Some(fdecl::DependencyType::Strong),
10534                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10535                        source_name: Some("thung1".repeat(26)),
10536                        target_name: Some("thung1".repeat(26)),
10537                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10538                            name: "foo".to_string(),
10539                            collection: Some("foo".to_string()),
10540                        })),
10541                        ..Default::default()
10542                    }),
10543                    fdecl::Offer::Storage(fdecl::OfferStorage {
10544                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10545                        source_name: Some("thung2".repeat(26)),
10546                        target_name: Some("thung2".repeat(26)),
10547                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10548                            name: "foo".to_string(),
10549                            collection: Some("foo".to_string()),
10550                        })),
10551                        ..Default::default()
10552                    }),
10553                    fdecl::Offer::Runner(fdecl::OfferRunner {
10554                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10555                        source_name: Some("thung3".repeat(26)),
10556                        target_name: Some("thung3".repeat(26)),
10557                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10558                            name: "foo".to_string(),
10559                            collection: Some("foo".to_string()),
10560                        })),
10561                        ..Default::default()
10562                    }),
10563                    fdecl::Offer::Resolver(fdecl::OfferResolver {
10564                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10565                        source_name: Some("thung4".repeat(26)),
10566                        target_name: Some("thung4".repeat(26)),
10567                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10568                            name: "foo".to_string(),
10569                            collection: Some("foo".to_string()),
10570                        })),
10571                        ..Default::default()
10572                    }),
10573                ],
10574            ),
10575            Ok(())
10576        );
10577    }
10578
10579    #[test]
10580    fn test_validate_dynamic_offers_valid_service_aggregation() {
10581        assert_eq!(
10582            validate_dynamic_offers(
10583                vec![],
10584                ["child_a", "child_b", "child_c"],
10585                [],
10586                &[],
10587                &[],
10588                &mut DirectedGraph::new(),
10589                &vec![
10590                    fdecl::Offer::Service(fdecl::OfferService {
10591                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10592                            name: "child_a".to_string(),
10593                            collection: None
10594                        })),
10595                        source_name: Some("fuchsia.logger.Log".to_string()),
10596                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10597                            name: "child_c".to_string(),
10598                            collection: None,
10599                        })),
10600                        target_name: Some("fuchsia.logger.Log".to_string()),
10601                        source_instance_filter: Some(vec!["default".to_string()]),
10602                        ..Default::default()
10603                    }),
10604                    fdecl::Offer::Service(fdecl::OfferService {
10605                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10606                            name: "child_b".to_string(),
10607                            collection: None
10608                        })),
10609                        source_name: Some("fuchsia.logger.Log".to_string()),
10610                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10611                            name: "child_c".to_string(),
10612                            collection: None,
10613                        })),
10614                        target_name: Some("fuchsia.logger.Log".to_string()),
10615                        source_instance_filter: Some(vec!["a_different_default".to_string()]),
10616                        ..Default::default()
10617                    })
10618                ],
10619            ),
10620            Ok(())
10621        );
10622    }
10623
10624    #[test]
10625    fn test_validate_dynamic_service_aggregation_missing_filter() {
10626        assert_eq!(
10627            validate_dynamic_offers(
10628                vec![],
10629                ["child_a", "child_b", "child_c"],
10630                [],
10631                &[],
10632                &[],
10633                &mut DirectedGraph::new(),
10634                &vec![
10635                    fdecl::Offer::Service(fdecl::OfferService {
10636                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10637                            name: "child_a".to_string(),
10638                            collection: None
10639                        })),
10640                        source_name: Some("fuchsia.logger.Log".to_string()),
10641                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10642                            name: "child_c".to_string(),
10643                            collection: None,
10644                        })),
10645                        target_name: Some("fuchsia.logger.Log".to_string()),
10646                        source_instance_filter: Some(vec!["default".to_string()]),
10647                        ..Default::default()
10648                    }),
10649                    fdecl::Offer::Service(fdecl::OfferService {
10650                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10651                            name: "child_b".to_string(),
10652                            collection: None
10653                        })),
10654                        source_name: Some("fuchsia.logger.Log".to_string()),
10655                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10656                            name: "child_c".to_string(),
10657                            collection: None,
10658                        })),
10659                        target_name: Some("fuchsia.logger.Log".to_string()),
10660                        source_instance_filter: None,
10661                        ..Default::default()
10662                    }),
10663                ],
10664            ),
10665            Err(ErrorList::new(vec![Error::invalid_aggregate_offer(
10666                "source_instance_filter must be set for dynamic aggregate service offers"
10667            ),]))
10668        );
10669    }
10670
10671    #[test]
10672    fn test_validate_dynamic_offers_omit_target() {
10673        assert_eq!(
10674            validate_dynamic_offers(
10675                vec![],
10676                [],
10677                [],
10678                &[fdecl::Capability::Protocol(fdecl::Protocol {
10679                    name: Some("thing".to_string()),
10680                    source_path: Some("/svc/foo".into()),
10681                    ..Default::default()
10682                })],
10683                &[],
10684                &mut DirectedGraph::new(),
10685                &vec![
10686                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
10687                        dependency_type: Some(fdecl::DependencyType::Strong),
10688                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef)),
10689                        source_name: Some("thing".to_string()),
10690                        target_name: Some("thing".to_string()),
10691                        ..Default::default()
10692                    }),
10693                    fdecl::Offer::Service(fdecl::OfferService {
10694                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10695                        source_name: Some("thang".to_string()),
10696                        target_name: Some("thang".to_string()),
10697                        ..Default::default()
10698                    }),
10699                    fdecl::Offer::Directory(fdecl::OfferDirectory {
10700                        dependency_type: Some(fdecl::DependencyType::Strong),
10701                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10702                        source_name: Some("thung1".to_string()),
10703                        target_name: Some("thung1".to_string()),
10704                        ..Default::default()
10705                    }),
10706                    fdecl::Offer::Storage(fdecl::OfferStorage {
10707                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10708                        source_name: Some("thung2".to_string()),
10709                        target_name: Some("thung2".to_string()),
10710                        ..Default::default()
10711                    }),
10712                    fdecl::Offer::Runner(fdecl::OfferRunner {
10713                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10714                        source_name: Some("thung3".to_string()),
10715                        target_name: Some("thung3".to_string()),
10716                        ..Default::default()
10717                    }),
10718                    fdecl::Offer::Resolver(fdecl::OfferResolver {
10719                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10720                        source_name: Some("thung4".to_string()),
10721                        target_name: Some("thung4".to_string()),
10722                        ..Default::default()
10723                    }),
10724                ],
10725            ),
10726            Err(ErrorList::new(vec![
10727                Error::missing_field(DeclType::OfferProtocol, "target"),
10728                Error::missing_field(DeclType::OfferService, "target"),
10729                Error::missing_field(DeclType::OfferDirectory, "target"),
10730                Error::missing_field(DeclType::OfferStorage, "target"),
10731                Error::missing_field(DeclType::OfferRunner, "target"),
10732                Error::missing_field(DeclType::OfferResolver, "target"),
10733            ]))
10734        );
10735    }
10736
10737    #[test]
10738    fn test_validate_dynamic_offers_collection_collision() {
10739        assert_eq!(
10740            validate_dynamic_offers(
10741                vec![],
10742                [],
10743                ["coll"],
10744                &[],
10745                &[fdecl::Offer::Protocol(fdecl::OfferProtocol {
10746                    dependency_type: Some(fdecl::DependencyType::Strong),
10747                    source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10748                    source_name: Some("thing".to_string()),
10749                    target_name: Some("thing".to_string()),
10750                    target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10751                        name: "coll".into()
10752                    })),
10753                    ..Default::default()
10754                })],
10755                &mut DirectedGraph::new(),
10756                &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10757                    dependency_type: Some(fdecl::DependencyType::Strong),
10758                    source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10759                    source_name: Some("thing".to_string()),
10760                    target_name: Some("thing".to_string()),
10761                    target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10762                        name: "child".to_string(),
10763                        collection: Some("coll".to_string()),
10764                    })),
10765                    ..Default::default()
10766                }),],
10767            ),
10768            Err(ErrorList::new(vec![Error::duplicate_field(
10769                DeclType::OfferProtocol,
10770                "target_name",
10771                "thing"
10772            ),]))
10773        );
10774    }
10775
10776    #[test]
10777    fn test_validate_dynamic_offers_cycle_collection_to_static_child() {
10778        assert_eq!(
10779            validate_dynamic_offers(
10780                vec![("dyn", "coll")],
10781                ["static_child"],
10782                ["coll"],
10783                &[],
10784                &[fdecl::Offer::Service(fdecl::OfferService {
10785                    source_name: Some("foo".to_string()),
10786                    target_name: Some("foo".to_string()),
10787                    source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10788                        name: "coll".into(),
10789                    })),
10790                    target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10791                        name: "static_child".into(),
10792                        collection: None,
10793                    })),
10794                    ..Default::default()
10795                })],
10796                &mut DirectedGraph::new(),
10797                &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10798                    source_name: Some("bar".to_string()),
10799                    target_name: Some("bar".to_string()),
10800                    source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10801                        name: "static_child".into(),
10802                        collection: None,
10803                    })),
10804                    target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10805                        name: "dyn".to_string(),
10806                        collection: Some("coll".to_string()),
10807                    })),
10808                    dependency_type: Some(fdecl::DependencyType::Strong),
10809                    ..Default::default()
10810                }),],
10811            ),
10812            Err(ErrorList::new(vec![Error::dependency_cycle(
10813                "{{child coll:dyn -> collection coll -> child static_child -> child coll:dyn}}"
10814            )]))
10815        );
10816    }
10817
10818    #[test]
10819    fn test_validate_dynamic_offers_cycle_collection_to_dynamic_child() {
10820        assert_eq!(
10821            validate_dynamic_offers(
10822                vec![("dyn", "coll1"), ("dyn", "coll2")],
10823                [],
10824                ["coll1", "coll2"],
10825                &[],
10826                &[],
10827                &mut DirectedGraph::new(),
10828                &vec![
10829                    fdecl::Offer::Service(fdecl::OfferService {
10830                        source_name: Some("foo".to_string()),
10831                        target_name: Some("foo".to_string()),
10832                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10833                            name: "coll2".into(),
10834                        })),
10835                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10836                            name: "dyn".into(),
10837                            collection: Some("coll1".into()),
10838                        })),
10839                        ..Default::default()
10840                    }),
10841                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
10842                        source_name: Some("bar".to_string()),
10843                        target_name: Some("bar".to_string()),
10844                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10845                            name: "dyn".into(),
10846                            collection: Some("coll1".into()),
10847                        })),
10848                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10849                            name: "dyn".to_string(),
10850                            collection: Some("coll2".to_string()),
10851                        })),
10852                        dependency_type: Some(fdecl::DependencyType::Strong),
10853                        ..Default::default()
10854                    }),
10855                ],
10856            ),
10857            Err(ErrorList::new(vec![Error::dependency_cycle(
10858                "{{child coll1:dyn -> child coll2:dyn -> collection coll2 -> child coll1:dyn}}"
10859            )]))
10860        );
10861    }
10862
10863    #[test]
10864    fn test_validate_dynamic_offers_cycle_collection_to_dynamic_child_between_new_and_existing() {
10865        assert_eq!(
10866            validate_dynamic_offers(
10867                vec![("dyn", "coll1"), ("dyn", "coll2")],
10868                [],
10869                ["coll1", "coll2"],
10870                &[],
10871                &[],
10872                &mut DirectedGraph::from([(
10873                    DependencyNode::Child("dyn".into(), Some("coll1".into())),
10874                    DependencyNode::Child("dyn".into(), Some("coll2".into())),
10875                )]),
10876                &vec![fdecl::Offer::Service(fdecl::OfferService {
10877                    source_name: Some("foo".to_string()),
10878                    target_name: Some("foo".to_string()),
10879                    source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10880                        name: "coll2".into(),
10881                    })),
10882                    target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10883                        name: "dyn".into(),
10884                        collection: Some("coll1".into()),
10885                    })),
10886                    ..Default::default()
10887                }),],
10888            ),
10889            Err(ErrorList::new(vec![Error::dependency_cycle(
10890                "{{child coll1:dyn -> child coll2:dyn -> collection coll2 -> child coll1:dyn}}"
10891            )]))
10892        );
10893    }
10894
10895    #[test]
10896    fn test_validate_dynamic_offers_cycle_collection_to_collection() {
10897        assert_eq!(
10898            validate_dynamic_offers(
10899                vec![("dyn", "coll1"), ("dyn", "coll2")],
10900                [],
10901                ["coll1", "coll2"],
10902                &[],
10903                &[fdecl::Offer::Service(fdecl::OfferService {
10904                    source_name: Some("foo".to_string()),
10905                    target_name: Some("foo".to_string()),
10906                    source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10907                        name: "coll1".into(),
10908                    })),
10909                    target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10910                        name: "coll2".into(),
10911                    })),
10912                    ..Default::default()
10913                })],
10914                &mut DirectedGraph::new(),
10915                &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10916                    source_name: Some("bar".to_string()),
10917                    target_name: Some("bar".to_string()),
10918                    source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10919                        name: "dyn".into(),
10920                        collection: Some("coll2".parse().unwrap()),
10921                    })),
10922                    target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10923                        name: "dyn".into(),
10924                        collection: Some("coll1".parse().unwrap()),
10925                    })),
10926                    dependency_type: Some(fdecl::DependencyType::Strong),
10927                    ..Default::default()
10928                }),],
10929            ),
10930            Err(ErrorList::new(vec![Error::dependency_cycle(
10931                "{{child coll1:dyn -> collection coll1 -> child coll2:dyn -> child coll1:dyn}}",
10932            )]))
10933        );
10934    }
10935
10936    #[test]
10937    fn test_validate_dynamic_child() {
10938        assert_eq!(
10939            Ok(()),
10940            validate_dynamic_child(&fdecl::Child {
10941                name: Some("a".repeat(MAX_LONG_NAME_LENGTH).to_string()),
10942                url: Some("test:///child".to_string()),
10943                startup: Some(fdecl::StartupMode::Lazy),
10944                on_terminate: None,
10945                environment: None,
10946                ..Default::default()
10947            })
10948        );
10949    }
10950
10951    #[test]
10952    fn test_validate_dynamic_child_environment_is_invalid() {
10953        assert_eq!(
10954            validate_dynamic_child(&fdecl::Child {
10955                name: Some("a".repeat(MAX_LONG_NAME_LENGTH).to_string()),
10956                url: Some("test:///child".to_string()),
10957                startup: Some(fdecl::StartupMode::Lazy),
10958                on_terminate: None,
10959                environment: Some("env".to_string()),
10960                ..Default::default()
10961            }),
10962            Err(ErrorList::new(vec![Error::DynamicChildWithEnvironment]))
10963        );
10964    }
10965
10966    #[test]
10967    fn test_validate_dynamic_offers_missing_stuff() {
10968        assert_eq!(
10969            validate_dynamic_offers(
10970                vec![],
10971                [],
10972                [],
10973                &[],
10974                &[],
10975                &mut DirectedGraph::new(),
10976                &vec![
10977                    fdecl::Offer::Protocol(fdecl::OfferProtocol::default()),
10978                    fdecl::Offer::Service(fdecl::OfferService::default()),
10979                    fdecl::Offer::Directory(fdecl::OfferDirectory::default()),
10980                    fdecl::Offer::Storage(fdecl::OfferStorage::default()),
10981                    fdecl::Offer::Runner(fdecl::OfferRunner::default()),
10982                    fdecl::Offer::Resolver(fdecl::OfferResolver::default()),
10983                ],
10984            ),
10985            Err(ErrorList::new(vec![
10986                Error::missing_field(DeclType::OfferProtocol, "source"),
10987                Error::missing_field(DeclType::OfferProtocol, "source_name"),
10988                Error::missing_field(DeclType::OfferProtocol, "target"),
10989                Error::missing_field(DeclType::OfferProtocol, "target_name"),
10990                Error::missing_field(DeclType::OfferProtocol, "dependency_type"),
10991                Error::missing_field(DeclType::OfferService, "source"),
10992                Error::missing_field(DeclType::OfferService, "source_name"),
10993                Error::missing_field(DeclType::OfferService, "target"),
10994                Error::missing_field(DeclType::OfferService, "target_name"),
10995                Error::missing_field(DeclType::OfferDirectory, "source"),
10996                Error::missing_field(DeclType::OfferDirectory, "source_name"),
10997                Error::missing_field(DeclType::OfferDirectory, "target"),
10998                Error::missing_field(DeclType::OfferDirectory, "target_name"),
10999                Error::missing_field(DeclType::OfferDirectory, "dependency_type"),
11000                Error::missing_field(DeclType::OfferStorage, "source"),
11001                Error::missing_field(DeclType::OfferStorage, "source_name"),
11002                Error::missing_field(DeclType::OfferStorage, "target"),
11003                Error::missing_field(DeclType::OfferStorage, "target_name"),
11004                Error::missing_field(DeclType::OfferRunner, "source"),
11005                Error::missing_field(DeclType::OfferRunner, "source_name"),
11006                Error::missing_field(DeclType::OfferRunner, "target"),
11007                Error::missing_field(DeclType::OfferRunner, "target_name"),
11008                Error::missing_field(DeclType::OfferResolver, "source"),
11009                Error::missing_field(DeclType::OfferResolver, "source_name"),
11010                Error::missing_field(DeclType::OfferResolver, "target"),
11011                Error::missing_field(DeclType::OfferResolver, "target_name"),
11012            ]))
11013        );
11014    }
11015
11016    test_dependency! {
11017        (test_validate_offers_protocol_dependency_cycle) => {
11018            ty = fdecl::Offer::Protocol,
11019            offer_decl = fdecl::OfferProtocol {
11020                source: None,  // Filled by macro
11021                target: None,  // Filled by macro
11022                source_name: Some(format!("thing")),
11023                target_name: Some(format!("thing")),
11024                dependency_type: Some(fdecl::DependencyType::Strong),
11025                ..Default::default()
11026            },
11027        },
11028        (test_validate_offers_directory_dependency_cycle) => {
11029            ty = fdecl::Offer::Directory,
11030            offer_decl = fdecl::OfferDirectory {
11031                source: None,  // Filled by macro
11032                target: None,  // Filled by macro
11033                source_name: Some(format!("thing")),
11034                target_name: Some(format!("thing")),
11035                rights: Some(fio::Operations::CONNECT),
11036                subdir: None,
11037                dependency_type: Some(fdecl::DependencyType::Strong),
11038                ..Default::default()
11039            },
11040        },
11041        (test_validate_offers_service_dependency_cycle) => {
11042            ty = fdecl::Offer::Service,
11043            offer_decl = fdecl::OfferService {
11044                source: None,  // Filled by macro
11045                target: None,  // Filled by macro
11046                source_name: Some(format!("thing")),
11047                target_name: Some(format!("thing")),
11048                ..Default::default()
11049            },
11050        },
11051        (test_validate_offers_runner_dependency_cycle) => {
11052            ty = fdecl::Offer::Runner,
11053            offer_decl = fdecl::OfferRunner {
11054                source: None,  // Filled by macro
11055                target: None,  // Filled by macro
11056                source_name: Some(format!("thing")),
11057                target_name: Some(format!("thing")),
11058                ..Default::default()
11059            },
11060        },
11061        (test_validate_offers_resolver_dependency_cycle) => {
11062            ty = fdecl::Offer::Resolver,
11063            offer_decl = fdecl::OfferResolver {
11064                source: None,  // Filled by macro
11065                target: None,  // Filled by macro
11066                source_name: Some(format!("thing")),
11067                target_name: Some(format!("thing")),
11068                ..Default::default()
11069            },
11070        },
11071    }
11072    test_weak_dependency! {
11073        (test_validate_offers_protocol_weak_dependency_cycle) => {
11074            ty = fdecl::Offer::Protocol,
11075            offer_decl = fdecl::OfferProtocol {
11076                source: None,  // Filled by macro
11077                target: None,  // Filled by macro
11078                source_name: Some(format!("thing")),
11079                target_name: Some(format!("thing")),
11080                dependency_type: None, // Filled by macro
11081                ..Default::default()
11082            },
11083        },
11084        (test_validate_offers_directory_weak_dependency_cycle) => {
11085            ty = fdecl::Offer::Directory,
11086            offer_decl = fdecl::OfferDirectory {
11087                source: None,  // Filled by macro
11088                target: None,  // Filled by macro
11089                source_name: Some(format!("thing")),
11090                target_name: Some(format!("thing")),
11091                rights: Some(fio::Operations::CONNECT),
11092                subdir: None,
11093                dependency_type: None,  // Filled by macro
11094                ..Default::default()
11095            },
11096        },
11097        (test_validate_offers_service_weak_dependency_cycle) => {
11098            ty = fdecl::Offer::Service,
11099            offer_decl = fdecl::OfferService {
11100                source: None,  // Filled by macro
11101                target: None,  // Filled by macro
11102                source_name: Some(format!("thing")),
11103                target_name: Some(format!("thing")),
11104                dependency_type: None, // Filled by macro
11105                ..Default::default()
11106            },
11107        },
11108    }
11109}