cm_fidl_validator/
lib.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5pub(crate) mod util;
6
7pub mod error;
8
9pub use crate::util::check_url;
10
11use crate::error::*;
12use crate::util::*;
13use cm_graph::DependencyNode;
14use cm_types::IterablePath;
15use directed_graph::DirectedGraph;
16use fidl_fuchsia_component_decl as fdecl;
17use itertools::Itertools;
18use std::collections::{BTreeSet, HashMap, HashSet};
19use std::path::Path;
20
21trait HasAvailability {
22    fn availability(&self) -> fdecl::Availability;
23}
24
25impl HasAvailability for fdecl::ExposeService {
26    fn availability(&self) -> fdecl::Availability {
27        return self.availability.unwrap_or(fdecl::Availability::Required);
28    }
29}
30
31impl HasAvailability for fdecl::OfferService {
32    fn availability(&self) -> fdecl::Availability {
33        return self.availability.unwrap_or(fdecl::Availability::Required);
34    }
35}
36
37/// Validates Configuration Value Spec.
38///
39/// For now, this simply verifies that all semantically required fields are present.
40pub fn validate_value_spec(spec: &fdecl::ConfigValueSpec) -> Result<(), ErrorList> {
41    let mut errors = vec![];
42    if let Some(value) = &spec.value {
43        match value {
44            fdecl::ConfigValue::Single(s) => match s {
45                fdecl::ConfigSingleValue::Bool(_)
46                | fdecl::ConfigSingleValue::Uint8(_)
47                | fdecl::ConfigSingleValue::Uint16(_)
48                | fdecl::ConfigSingleValue::Uint32(_)
49                | fdecl::ConfigSingleValue::Uint64(_)
50                | fdecl::ConfigSingleValue::Int8(_)
51                | fdecl::ConfigSingleValue::Int16(_)
52                | fdecl::ConfigSingleValue::Int32(_)
53                | fdecl::ConfigSingleValue::Int64(_)
54                | fdecl::ConfigSingleValue::String(_) => {}
55                fdecl::ConfigSingleValueUnknown!() => {
56                    errors.push(Error::invalid_field(DeclType::ConfigValueSpec, "value"));
57                }
58            },
59            fdecl::ConfigValue::Vector(l) => match l {
60                fdecl::ConfigVectorValue::BoolVector(_)
61                | fdecl::ConfigVectorValue::Uint8Vector(_)
62                | fdecl::ConfigVectorValue::Uint16Vector(_)
63                | fdecl::ConfigVectorValue::Uint32Vector(_)
64                | fdecl::ConfigVectorValue::Uint64Vector(_)
65                | fdecl::ConfigVectorValue::Int8Vector(_)
66                | fdecl::ConfigVectorValue::Int16Vector(_)
67                | fdecl::ConfigVectorValue::Int32Vector(_)
68                | fdecl::ConfigVectorValue::Int64Vector(_)
69                | fdecl::ConfigVectorValue::StringVector(_) => {}
70                fdecl::ConfigVectorValueUnknown!() => {
71                    errors.push(Error::invalid_field(DeclType::ConfigValueSpec, "value"));
72                }
73            },
74            fdecl::ConfigValueUnknown!() => {
75                errors.push(Error::invalid_field(DeclType::ConfigValueSpec, "value"));
76            }
77        }
78    } else {
79        errors.push(Error::missing_field(DeclType::ConfigValueSpec, "value"));
80    }
81
82    if errors.is_empty() { Ok(()) } else { Err(ErrorList::new(errors)) }
83}
84
85/// Validates Configuration Values Data.
86///
87/// The Value Data may ultimately originate from a CVF file, or be directly constructed by the
88/// caller. Either way, Value Data should always be validated before it's used. For now, this
89/// simply verifies that all semantically required fields are present.
90///
91/// This method does not validate value data against a configuration schema.
92pub fn validate_values_data(data: &fdecl::ConfigValuesData) -> Result<(), ErrorList> {
93    let mut errors = vec![];
94    if let Some(values) = &data.values {
95        for spec in values {
96            if let Err(mut e) = validate_value_spec(spec) {
97                errors.append(&mut e.errs);
98            }
99        }
100    } else {
101        errors.push(Error::missing_field(DeclType::ConfigValuesData, "values"));
102    }
103
104    if let Some(checksum) = &data.checksum {
105        match checksum {
106            fdecl::ConfigChecksum::Sha256(_) => {}
107            fdecl::ConfigChecksumUnknown!() => {
108                errors.push(Error::invalid_field(DeclType::ConfigValuesData, "checksum"));
109            }
110        }
111    } else {
112        errors.push(Error::missing_field(DeclType::ConfigValuesData, "checksum"));
113    }
114
115    if errors.is_empty() { Ok(()) } else { Err(ErrorList::new(errors)) }
116}
117
118// `fdecl::Ref` is not hashable, so define this equivalent type for use in maps
119#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
120enum RefKey<'a> {
121    Parent,
122    Self_,
123    Child(&'a str),
124    Collection(&'a str),
125    Framework,
126    Capability,
127    Debug,
128}
129
130/// Validates a Component.
131///
132/// The Component may ultimately originate from a CM file, or be directly constructed by the
133/// caller. Either way, a Component should always be validated before it's used. Examples
134/// of what is validated (which may evolve in the future):
135///
136/// - That all semantically required fields are present
137/// - That a child_name referenced in a source actually exists in the list of children
138/// - That there are no duplicate target paths.
139/// - That only weak-dependency capabilities may be offered back to the
140///   component that exposed them.
141///
142/// `dependencies` is an output parameter that captures the capability dependency graph derived
143/// from `decl`. Callers may use this to traverse the capability graph in topological order.
144///
145/// All checks are local to this Component.
146pub fn validate<'a>(
147    decl: &'a fdecl::Component,
148    dependencies: &'a mut DirectedGraph<DependencyNode>,
149) -> Result<(), ErrorList> {
150    let ctx = ValidationContext::new(dependencies);
151    ctx.validate(decl, &[]).map_err(|errs| ErrorList::new(errs))
152}
153
154/// Validates a list of namespace or builtin Capabilities.
155fn validate_capabilities(
156    capabilities: &[fdecl::Capability],
157    as_builtin: bool,
158) -> Result<(), ErrorList> {
159    let mut deps = DirectedGraph::new();
160    let mut ctx = ValidationContext::new(&mut deps);
161
162    ctx.load_dictionary_names(capabilities.iter().filter_map(|capability| match capability {
163        fdecl::Capability::Dictionary(dictionary_decl) => Some(dictionary_decl),
164        _ => None,
165    }));
166
167    ctx.validate_capability_decls(capabilities, as_builtin);
168    if ctx.errors.is_empty() { Ok(()) } else { Err(ErrorList::new(ctx.errors)) }
169}
170
171// Validate builtin capabilities.
172pub fn validate_builtin_capabilities(
173    capabilities: &Vec<fdecl::Capability>,
174) -> Result<(), ErrorList> {
175    validate_capabilities(capabilities, true)
176}
177
178// Validate namespace capabilities.
179pub fn validate_namespace_capabilities(
180    capabilities: &Vec<fdecl::Capability>,
181) -> Result<(), ErrorList> {
182    validate_capabilities(capabilities, false)
183}
184
185/// An interface to call into either `check_dynamic_name()` or `check_name()`, depending on the context
186/// of the caller.
187type CheckChildNameFn = fn(Option<&String>, DeclType, &str, &mut Vec<Error>) -> bool;
188
189pub fn validate_dynamic_child(child: &fdecl::Child) -> Result<(), ErrorList> {
190    let mut errors = vec![];
191
192    if let Err(mut error_list) = validate_child(child, check_dynamic_name) {
193        errors.append(&mut error_list.errs);
194    }
195
196    if child.environment.is_some() {
197        errors.push(Error::DynamicChildWithEnvironment);
198    }
199
200    if errors.is_empty() { Ok(()) } else { Err(ErrorList { errs: errors }) }
201}
202
203/// Validates an independent Child. Performs the same validation on it as `validate`. A
204/// `check_name_fn` is passed into specify the function used to validate the child name.
205fn validate_child(
206    child: &fdecl::Child,
207    check_child_name: CheckChildNameFn,
208) -> Result<(), ErrorList> {
209    let mut errors = vec![];
210    check_child_name(child.name.as_ref(), DeclType::Child, "name", &mut errors);
211    check_url(child.url.as_ref(), DeclType::Child, "url", &mut errors);
212    if child.startup.is_none() {
213        errors.push(Error::missing_field(DeclType::Child, "startup"));
214    }
215    // Allow `on_terminate` to be unset since the default is almost always desired.
216    if child.environment.is_some() {
217        check_name(child.environment.as_ref(), DeclType::Child, "environment", &mut errors);
218    }
219    if errors.is_empty() { Ok(()) } else { Err(ErrorList { errs: errors }) }
220}
221
222/// Validates a collection of dynamic offers. Dynamic offers differ from static
223/// offers, in that
224///
225/// 1. a dynamic offer's `target` field must be omitted;
226/// 2. a dynamic offer's `source` _may_ be a dynamic child;
227/// 3. since this crate isn't really designed to handle dynamic children, we
228///    disable the checks that ensure that the source/target exist, and that the
229///    offers don't introduce any cycles.
230///
231/// `dependencies` is an output parameter that captures new dependencies from `new_dynamic_offers`.
232/// Any existing dependency edges are preserved. The input may be non empty and is normally the
233/// output of the previous [`validate`] or [`validate_dynamic_offers`].
234pub fn validate_dynamic_offers<'a>(
235    dynamic_children: Vec<(&'a str, &'a str)>,
236    dependencies: &mut DirectedGraph<DependencyNode>,
237    new_dynamic_offers: &'a [fdecl::Offer],
238    decl: &'a fdecl::Component,
239) -> Result<(), ErrorList> {
240    let mut ctx = ValidationContext::new(dependencies);
241    ctx.dynamic_children = dynamic_children;
242    ctx.validate(decl, new_dynamic_offers).map_err(|errs| ErrorList::new(errs))
243}
244
245fn check_offer_name(
246    prop: Option<&String>,
247    decl: DeclType,
248    keyword: &str,
249    offer_type: OfferType,
250    errors: &mut Vec<Error>,
251) -> bool {
252    if offer_type == OfferType::Dynamic {
253        check_dynamic_name(prop, decl, keyword, errors)
254    } else {
255        check_name(prop, decl, keyword, errors)
256    }
257}
258
259struct ValidationContext<'a> {
260    all_children: HashMap<&'a str, &'a fdecl::Child>,
261    all_collections: HashSet<&'a str>,
262    all_capability_ids: HashSet<&'a str>,
263    all_storages: HashMap<&'a str, Option<&'a fdecl::Ref>>,
264    all_services: HashSet<&'a str>,
265    all_protocols: HashSet<&'a str>,
266    all_directories: HashSet<&'a str>,
267    all_runners: HashSet<&'a str>,
268    all_resolvers: HashSet<&'a str>,
269    all_dictionaries: HashMap<&'a str, &'a fdecl::Dictionary>,
270
271    #[cfg(fuchsia_api_level_at_least = "HEAD")]
272    all_configs: HashSet<&'a str>,
273
274    all_environment_names: HashSet<&'a str>,
275    dynamic_children: Vec<(&'a str, &'a str)>,
276    strong_dependencies: &'a mut DirectedGraph<DependencyNode>,
277    target_ids: IdMap<'a>,
278    errors: Vec<Error>,
279}
280
281/// [Container] provides a capability type agnostic trait to check for the existence of a
282/// capability definition of a particular type. This is useful for writing common validation
283/// functions.
284trait Container {
285    fn contains(&self, key: &str) -> bool;
286}
287
288impl<'a> Container for HashSet<&'a str> {
289    fn contains(&self, key: &str) -> bool {
290        self.contains(key)
291    }
292}
293
294impl<'a, T> Container for HashMap<&'a str, T> {
295    fn contains(&self, key: &str) -> bool {
296        self.contains_key(key)
297    }
298}
299
300impl<'a> ValidationContext<'a> {
301    fn new(strong_dependencies: &'a mut DirectedGraph<DependencyNode>) -> Self {
302        Self {
303            strong_dependencies,
304            all_children: Default::default(),
305            all_collections: Default::default(),
306            all_capability_ids: Default::default(),
307            all_storages: Default::default(),
308            all_services: Default::default(),
309            all_protocols: Default::default(),
310            all_directories: Default::default(),
311            all_runners: Default::default(),
312            all_resolvers: Default::default(),
313            all_dictionaries: Default::default(),
314
315            #[cfg(fuchsia_api_level_at_least = "HEAD")]
316            all_configs: Default::default(),
317
318            all_environment_names: Default::default(),
319            dynamic_children: Default::default(),
320            target_ids: Default::default(),
321            errors: Default::default(),
322        }
323    }
324
325    fn validate(
326        mut self,
327        decl: &'a fdecl::Component,
328        new_dynamic_offers: &'a [fdecl::Offer],
329    ) -> Result<(), Vec<Error>> {
330        // Collect all environment names first, so that references to them can be checked.
331        if let Some(envs) = &decl.environments {
332            self.collect_environment_names(&envs);
333        }
334
335        // Validate "children" and build the set of all children.
336        if let Some(children) = decl.children.as_ref() {
337            for child in children {
338                self.validate_child_decl(&child);
339            }
340        }
341
342        // Validate "collections" and build the set of all collections.
343        if let Some(collections) = decl.collections.as_ref() {
344            for collection in collections {
345                self.validate_collection_decl(&collection);
346            }
347        }
348
349        // Validate "capabilities" and build the set of all capabilities.
350        if let Some(capabilities) = decl.capabilities.as_ref() {
351            self.load_dictionary_names(capabilities.iter().filter_map(
352                |capability| match capability {
353                    fdecl::Capability::Dictionary(dictionary_decl) => Some(dictionary_decl),
354                    _ => None,
355                },
356            ));
357            self.validate_capability_decls(capabilities, false);
358        }
359
360        // Validate "uses".
361        let mut use_runner_name = None;
362        let mut use_runner_source = None;
363        if let Some(uses) = decl.uses.as_ref() {
364            (use_runner_name, use_runner_source) = self.validate_use_decls(uses);
365        }
366
367        // Validate "program".
368        if let Some(program) = decl.program.as_ref() {
369            self.validate_program(program, use_runner_name, use_runner_source);
370        }
371
372        // Validate "exposes".
373        if let Some(exposes) = decl.exposes.as_ref() {
374            let mut expose_to_parent_ids = HashMap::new();
375            let mut expose_to_framework_ids = HashMap::new();
376            for expose in exposes.iter() {
377                self.validate_expose_decl(
378                    &expose,
379                    &mut expose_to_parent_ids,
380                    &mut expose_to_framework_ids,
381                );
382            }
383            self.validate_expose_group(&exposes);
384        }
385
386        // Validate "offers".
387        if let Some(offers) = decl.offers.as_ref() {
388            for offer in offers.iter() {
389                self.validate_offer_decl(&offer, OfferType::Static);
390            }
391            self.validate_offer_group(&offers, OfferType::Static);
392        }
393
394        for dynamic_offer in new_dynamic_offers {
395            self.validate_offer_decl(dynamic_offer, OfferType::Dynamic);
396            cm_graph::add_dependencies_from_offer(
397                &mut self.strong_dependencies,
398                dynamic_offer,
399                &self.dynamic_children,
400            );
401        }
402        self.validate_offer_group(new_dynamic_offers, OfferType::Dynamic);
403
404        // Validate "environments" after all other declarations are processed.
405        if let Some(environment) = decl.environments.as_ref() {
406            for environment in environment {
407                self.validate_environment_decl(&environment);
408            }
409        }
410
411        // Validate "config"
412        self.validate_config(decl.config.as_ref(), decl.uses.as_ref());
413
414        // Check that there are no strong cyclical dependencies
415        cm_graph::generate_dependency_graph(
416            &mut self.strong_dependencies,
417            &decl,
418            &self.dynamic_children,
419        );
420        if let Err(e) = self.strong_dependencies.topological_sort() {
421            self.errors.push(Error::dependency_cycle(e.format_cycle()));
422        }
423
424        if self.errors.is_empty() { Ok(()) } else { Err(self.errors) }
425    }
426
427    /// Collects all the environment names, watching for duplicates.
428    fn collect_environment_names(&mut self, envs: &'a [fdecl::Environment]) {
429        for env in envs {
430            if let Some(name) = env.name.as_ref() {
431                if !self.all_environment_names.insert(name) {
432                    self.errors.push(Error::duplicate_field(DeclType::Environment, "name", name));
433                }
434            }
435        }
436    }
437
438    // Validates a config schema. Checks that each field's layout matches the expected constraints
439    // and properties.
440    fn validate_config(
441        &mut self,
442        config: Option<&fdecl::ConfigSchema>,
443        uses: Option<&Vec<fdecl::Use>>,
444    ) {
445        use std::collections::BTreeMap;
446
447        // Get all of the `use` configs that are optional without a default.
448        let optional_use_keys: BTreeMap<String, fdecl::ConfigType> =
449            uses.map_or(BTreeMap::new(), |u| {
450                u.iter()
451                    .map(|u| {
452                        let fdecl::Use::Config(config) = u else {
453                            return None;
454                        };
455                        if config.availability == Some(fdecl::Availability::Required)
456                            || config.availability == None
457                        {
458                            return None;
459                        }
460                        if let Some(_) = config.default.as_ref() {
461                            return None;
462                        }
463                        let Some(key) = config.target_name.clone() else {
464                            return None;
465                        };
466                        let Some(value) = config.type_.clone() else {
467                            return None;
468                        };
469                        Some((key, value))
470                    })
471                    .flatten()
472                    .collect()
473            });
474
475        // Validate default values in use configs.
476        for u in uses.iter().flat_map(|x| x.iter()) {
477            let fdecl::Use::Config(config) = u else { continue };
478            let Some(default) = config.default.as_ref() else { continue };
479            validate_value_spec(&fdecl::ConfigValueSpec {
480                value: Some(default.clone()),
481                ..Default::default()
482            })
483            .map_err(|mut e| self.errors.append(&mut e.errs))
484            .ok();
485        }
486
487        let Some(config) = config else {
488            if !optional_use_keys.is_empty() {
489                self.errors.push(Error::missing_field(DeclType::ConfigField, "config"))
490            }
491            return;
492        };
493
494        if let Some(fields) = &config.fields {
495            for field in fields {
496                if field.key.is_none() {
497                    self.errors.push(Error::missing_field(DeclType::ConfigField, "key"));
498                }
499                if let Some(type_) = &field.type_ {
500                    self.validate_config_type(type_, true);
501                } else {
502                    self.errors.push(Error::missing_field(DeclType::ConfigField, "value_type"));
503                }
504            }
505        } else {
506            self.errors.push(Error::missing_field(DeclType::ConfigSchema, "fields"));
507        }
508
509        if let Some(checksum) = &config.checksum {
510            match checksum {
511                fdecl::ConfigChecksum::Sha256(_) => {}
512                fdecl::ConfigChecksumUnknown!() => {
513                    self.errors.push(Error::invalid_field(DeclType::ConfigSchema, "checksum"));
514                }
515            }
516        } else {
517            self.errors.push(Error::missing_field(DeclType::ConfigSchema, "checksum"));
518        }
519
520        'outer: for (key, value) in optional_use_keys.iter() {
521            for field in config.fields.iter().flatten() {
522                if field.key.as_ref() == Some(key) {
523                    if field.type_.as_ref() != Some(value) {
524                        self.errors.push(Error::invalid_field(DeclType::ConfigField, key.clone()));
525                    }
526                    continue 'outer;
527                }
528            }
529            self.errors.push(Error::missing_field(DeclType::ConfigField, key.clone()));
530        }
531
532        match config.value_source {
533            None => self.errors.push(Error::missing_field(DeclType::ConfigSchema, "value_source")),
534            #[cfg(fuchsia_api_level_at_least = "HEAD")]
535            Some(fdecl::ConfigValueSource::Capabilities(_)) => {
536                if !optional_use_keys.is_empty() {
537                    self.errors
538                        .push(Error::invalid_field(DeclType::ConfigValueSource, "ValueSource"))
539                }
540            }
541            Some(fdecl::ConfigValueSource::__SourceBreaking { .. }) => {
542                self.errors.push(Error::invalid_field(DeclType::ConfigValueSource, "ValueSource"))
543            }
544            _ => (),
545        };
546    }
547
548    fn validate_config_type(&mut self, type_: &fdecl::ConfigType, accept_vectors: bool) {
549        match &type_.layout {
550            fdecl::ConfigTypeLayout::Bool
551            | fdecl::ConfigTypeLayout::Uint8
552            | fdecl::ConfigTypeLayout::Uint16
553            | fdecl::ConfigTypeLayout::Uint32
554            | fdecl::ConfigTypeLayout::Uint64
555            | fdecl::ConfigTypeLayout::Int8
556            | fdecl::ConfigTypeLayout::Int16
557            | fdecl::ConfigTypeLayout::Int32
558            | fdecl::ConfigTypeLayout::Int64 => {
559                // These layouts have no parameters or constraints
560                if let Some(parameters) = &type_.parameters {
561                    if !parameters.is_empty() {
562                        self.errors
563                            .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
564                    }
565                } else {
566                    self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"));
567                }
568
569                if !type_.constraints.is_empty() {
570                    self.errors.push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
571                }
572            }
573            fdecl::ConfigTypeLayout::String => {
574                // String has exactly one constraint and no parameter
575                if let Some(parameters) = &type_.parameters {
576                    if !parameters.is_empty() {
577                        self.errors
578                            .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
579                    }
580                } else {
581                    self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"));
582                }
583
584                if type_.constraints.is_empty() {
585                    self.errors.push(Error::missing_field(DeclType::ConfigType, "constraints"));
586                } else if type_.constraints.len() > 1 {
587                    self.errors.push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
588                } else if let fdecl::LayoutConstraint::MaxSize(_) = &type_.constraints[0] {
589                } else {
590                    self.errors.push(Error::invalid_field(DeclType::ConfigType, "constraints"));
591                }
592            }
593            fdecl::ConfigTypeLayout::Vector => {
594                if accept_vectors {
595                    // Vector has exactly one constraint and one parameter
596                    if let Some(parameters) = &type_.parameters {
597                        if parameters.is_empty() {
598                            self.errors
599                                .push(Error::missing_field(DeclType::ConfigType, "parameters"));
600                        } else if parameters.len() > 1 {
601                            self.errors
602                                .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
603                        } else if let fdecl::LayoutParameter::NestedType(nested_type) =
604                            &parameters[0]
605                        {
606                            self.validate_config_type(nested_type, false);
607                        } else {
608                            self.errors
609                                .push(Error::invalid_field(DeclType::ConfigType, "parameters"));
610                        }
611                    } else {
612                        self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"))
613                    }
614
615                    if type_.constraints.is_empty() {
616                        self.errors.push(Error::missing_field(DeclType::ConfigType, "constraints"));
617                    } else if type_.constraints.len() > 1 {
618                        self.errors
619                            .push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
620                    } else if let fdecl::LayoutConstraint::MaxSize(_) = &type_.constraints[0] {
621                    } else {
622                        self.errors.push(Error::invalid_field(DeclType::ConfigType, "constraints"));
623                    }
624                } else {
625                    self.errors.push(Error::nested_vector());
626                }
627            }
628            _ => self.errors.push(Error::invalid_field(DeclType::ConfigType, "layout")),
629        }
630    }
631
632    fn validate_capability_decls(
633        &mut self,
634        capabilities: &'a [fdecl::Capability],
635        as_builtin: bool,
636    ) {
637        for capability in capabilities {
638            self.validate_capability_decl(capability, as_builtin);
639        }
640    }
641
642    /// Validates an individual capability declaration as either a built-in capability or (if
643    /// `as_builtin = false`) as a component or namespace capability.
644    // Storage capabilities are not currently allowed as built-ins, but there's no deep reason for this.
645    // Update this method to allow built-in storage capabilities as needed.
646    fn validate_capability_decl(&mut self, capability: &'a fdecl::Capability, as_builtin: bool) {
647        match capability {
648            fdecl::Capability::Service(service) => self.validate_service_decl(&service, as_builtin),
649            fdecl::Capability::Protocol(protocol) => {
650                self.validate_protocol_decl(&protocol, as_builtin)
651            }
652            fdecl::Capability::Directory(directory) => {
653                self.validate_directory_decl(&directory, as_builtin)
654            }
655            fdecl::Capability::Storage(storage) => {
656                if as_builtin {
657                    self.errors.push(Error::CapabilityCannotBeBuiltin(DeclType::Storage))
658                } else {
659                    self.validate_storage_decl(&storage)
660                }
661            }
662            fdecl::Capability::Runner(runner) => self.validate_runner_decl(&runner, as_builtin),
663            fdecl::Capability::Resolver(resolver) => {
664                self.validate_resolver_decl(&resolver, as_builtin)
665            }
666            fdecl::Capability::EventStream(event) => {
667                if as_builtin {
668                    self.validate_event_stream_decl(&event)
669                } else {
670                    self.errors.push(Error::CapabilityMustBeBuiltin(DeclType::EventStream))
671                }
672            }
673            fdecl::Capability::Dictionary(dictionary) => {
674                self.validate_dictionary_decl(&dictionary);
675            }
676            #[cfg(fuchsia_api_level_at_least = "HEAD")]
677            fdecl::Capability::Config(config) => {
678                self.validate_configuration_decl(&config);
679            }
680            fdecl::CapabilityUnknown!() => self.errors.push(Error::UnknownCapability),
681        }
682    }
683
684    /// Returns the `source_name` and `source` of the runner in `uses`, if present.
685    fn validate_use_decls(
686        &mut self,
687        uses: &'a [fdecl::Use],
688    ) -> (Option<&'a String>, Option<&'a fdecl::Ref>) {
689        // Validate individual fields.
690        for use_ in uses.iter() {
691            self.validate_use_decl(&use_);
692        }
693        self.validate_use_paths(&uses);
694
695        #[cfg(fuchsia_api_level_at_least = "HEAD")]
696        {
697            let mut use_runner_name = None;
698            let mut use_runner_source = None;
699            for use_ in uses.iter() {
700                if let fdecl::Use::Runner(use_runner) = use_ {
701                    if use_runner_name.is_some() {
702                        self.errors.push(Error::MultipleRunnersUsed);
703                    }
704
705                    use_runner_name = use_runner.source_name.as_ref();
706                    use_runner_source = use_runner.source.as_ref();
707                }
708            }
709            return (use_runner_name, use_runner_source);
710        }
711        #[cfg(fuchsia_api_level_less_than = "HEAD")]
712        return (None, None);
713    }
714
715    fn validate_use_decl(&mut self, use_: &'a fdecl::Use) {
716        match use_ {
717            fdecl::Use::Service(u) => {
718                let decl = DeclType::UseService;
719                self.validate_use_fields(
720                    decl,
721                    Self::service_checker,
722                    u.source.as_ref(),
723                    u.source_name.as_ref(),
724                    u.source_dictionary.as_ref(),
725                    u.target_path.as_ref(),
726                    u.dependency_type.as_ref(),
727                    u.availability.as_ref(),
728                );
729                if u.dependency_type.is_none() {
730                    self.errors.push(Error::missing_field(decl, "dependency_type"));
731                }
732            }
733            fdecl::Use::Protocol(u) => {
734                let decl = DeclType::UseProtocol;
735                self.validate_use_fields(
736                    decl,
737                    Self::protocol_checker,
738                    u.source.as_ref(),
739                    u.source_name.as_ref(),
740                    u.source_dictionary.as_ref(),
741                    u.target_path.as_ref(),
742                    u.dependency_type.as_ref(),
743                    u.availability.as_ref(),
744                );
745                #[cfg(not(fuchsia_api_level_at_least = "29"))]
746                let has_numbered_handle = false;
747                #[cfg(fuchsia_api_level_at_least = "29")]
748                let has_numbered_handle = u.numbered_handle.is_some();
749                if u.target_path.is_none() && !has_numbered_handle {
750                    self.errors.push(Error::missing_field(decl, "target_path"));
751                }
752                if has_numbered_handle && u.target_path.is_some() {
753                    self.errors.push(Error::extraneous_field(decl, "numbered_handle"));
754                }
755                if u.dependency_type.is_none() {
756                    self.errors.push(Error::missing_field(decl, "dependency_type"));
757                }
758            }
759            fdecl::Use::Directory(u) => {
760                let decl = DeclType::UseDirectory;
761                self.validate_use_fields(
762                    decl,
763                    Self::directory_checker,
764                    u.source.as_ref(),
765                    u.source_name.as_ref(),
766                    u.source_dictionary.as_ref(),
767                    u.target_path.as_ref(),
768                    u.dependency_type.as_ref(),
769                    u.availability.as_ref(),
770                );
771                if u.dependency_type.is_none() {
772                    self.errors.push(Error::missing_field(decl, "dependency_type"));
773                }
774                if u.rights.is_none() {
775                    self.errors.push(Error::missing_field(DeclType::UseDirectory, "rights"));
776                }
777                if let Some(subdir) = u.subdir.as_ref() {
778                    check_relative_path(
779                        Some(subdir),
780                        DeclType::UseDirectory,
781                        "subdir",
782                        &mut self.errors,
783                    );
784                }
785            }
786            fdecl::Use::Storage(u) => {
787                const SOURCE: Option<fdecl::Ref> = Some(fdecl::Ref::Parent(fdecl::ParentRef {}));
788                const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
789                    Some(fdecl::DependencyType::Strong);
790                self.validate_use_fields(
791                    DeclType::UseStorage,
792                    Self::storage_checker,
793                    SOURCE.as_ref(),
794                    u.source_name.as_ref(),
795                    None,
796                    u.target_path.as_ref(),
797                    DEPENDENCY_TYPE.as_ref(),
798                    u.availability.as_ref(),
799                );
800            }
801            fdecl::Use::EventStream(u) => {
802                const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
803                    Some(fdecl::DependencyType::Strong);
804                let decl = DeclType::UseEventStream;
805                self.validate_use_fields(
806                    decl,
807                    Self::event_stream_checker,
808                    u.source.as_ref(),
809                    u.source_name.as_ref(),
810                    None,
811                    u.target_path.as_ref(),
812                    DEPENDENCY_TYPE.as_ref(),
813                    u.availability.as_ref(),
814                );
815                // Additional validation.
816                match u.source {
817                    Some(fdecl::Ref::Child(_)) | Some(fdecl::Ref::Parent(_)) => {
818                        // Allowed.
819                    }
820                    Some(fdecl::Ref::Framework(_))
821                    | Some(fdecl::Ref::Self_(_))
822                    | Some(fdecl::Ref::Debug(_)) => {
823                        // Allowed in general but not for event streams, add an error.
824                        self.errors.push(Error::invalid_field(decl, "source"));
825                    }
826                    Some(fdecl::Ref::Collection(_)) | Some(fdecl::RefUnknown!()) | None => {
827                        // Already handled by validate_use_fields.
828                    }
829                }
830                if let Some(scope) = &u.scope {
831                    for reference in scope {
832                        if !matches!(reference, fdecl::Ref::Child(_) | fdecl::Ref::Collection(_)) {
833                            self.errors.push(Error::invalid_field(decl, "scope"));
834                        }
835                    }
836                }
837            }
838            #[cfg(fuchsia_api_level_at_least = "HEAD")]
839            fdecl::Use::Runner(u) => {
840                const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
841                    Some(fdecl::DependencyType::Strong);
842                const AVAILABILITY: Option<fdecl::Availability> =
843                    Some(fdecl::Availability::Required);
844                let decl = DeclType::UseRunner;
845                self.validate_use_fields(
846                    decl,
847                    Self::runner_checker,
848                    u.source.as_ref(),
849                    u.source_name.as_ref(),
850                    u.source_dictionary.as_ref(),
851                    None,
852                    DEPENDENCY_TYPE.as_ref(),
853                    AVAILABILITY.as_ref(),
854                );
855            }
856            #[cfg(fuchsia_api_level_at_least = "HEAD")]
857            fdecl::Use::Config(u) => {
858                const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
859                    Some(fdecl::DependencyType::Strong);
860                let decl = DeclType::UseConfiguration;
861                self.validate_use_fields(
862                    decl,
863                    Self::config_checker,
864                    u.source.as_ref(),
865                    u.source_name.as_ref(),
866                    None,
867                    None,
868                    DEPENDENCY_TYPE.as_ref(),
869                    u.availability.as_ref(),
870                );
871            }
872            #[cfg(fuchsia_api_level_at_least = "29")]
873            fdecl::Use::Dictionary(u) => {
874                let decl = DeclType::UseDictionary;
875                self.validate_use_fields(
876                    decl,
877                    Self::dictionary_checker,
878                    u.source.as_ref(),
879                    u.source_name.as_ref(),
880                    u.source_dictionary.as_ref(),
881                    u.target_path.as_ref(),
882                    u.dependency_type.as_ref(),
883                    u.availability.as_ref(),
884                );
885                if u.dependency_type.is_none() {
886                    self.errors.push(Error::missing_field(decl, "dependency_type"));
887                }
888            }
889            fdecl::UseUnknown!() => {
890                self.errors.push(Error::invalid_field(DeclType::Component, "use"));
891            }
892        }
893    }
894
895    /// Validates the "program" declaration. This does not check runner-specific properties
896    /// since those are checked by the runner.
897    fn validate_program(
898        &mut self,
899        program: &fdecl::Program,
900        use_runner_name: Option<&String>,
901        _use_runner_source: Option<&fdecl::Ref>,
902    ) {
903        match &program.runner {
904            Some(_) =>
905            {
906                #[cfg(fuchsia_api_level_at_least = "HEAD")]
907                if use_runner_name.is_some() {
908                    if use_runner_name != program.runner.as_ref()
909                        || _use_runner_source
910                            != Some(&fdecl::Ref::Environment(fdecl::EnvironmentRef))
911                    {
912                        self.errors.push(Error::ConflictingRunners);
913                    }
914                }
915            }
916            None => {
917                if use_runner_name.is_none() {
918                    self.errors.push(Error::MissingRunner);
919                }
920            }
921        }
922
923        if program.info.is_none() {
924            self.errors.push(Error::missing_field(DeclType::Program, "info"));
925        }
926    }
927
928    /// Validates that paths-based capabilities (service, directory, protocol, etc.)
929    /// are different, are not prefixes of each other, and do not collide "/pkg".
930    fn validate_use_paths(&mut self, uses: &[fdecl::Use]) {
931        fn use_to_path<'a>(use_: &'a fdecl::Use) -> Option<&'a Path> {
932            match use_ {
933                #[cfg(fuchsia_api_level_at_least = "29")]
934                fdecl::Use::Dictionary(fdecl::UseDictionary {
935                    target_path: Some(path), ..
936                }) => Some(Path::new(path)),
937                fdecl::Use::Directory(fdecl::UseDirectory { target_path: Some(path), .. }) => {
938                    Some(Path::new(path))
939                }
940                fdecl::Use::Protocol(fdecl::UseProtocol { target_path: Some(path), .. }) => {
941                    Some(Path::new(path))
942                }
943                fdecl::Use::Service(fdecl::UseService { target_path: Some(path), .. }) => {
944                    Some(Path::new(path))
945                }
946                fdecl::Use::Storage(fdecl::UseStorage { target_path: Some(path), .. }) => {
947                    Some(Path::new(path))
948                }
949                _ => None,
950            }
951        }
952        enum NodeType {
953            Service,
954            Directory,
955            // This variant is never constructed if we're at an API version before "use dictionary"
956            // was added.
957            #[allow(unused)]
958            Dictionary,
959        }
960        fn use_to_type(use_: &fdecl::Use) -> Option<NodeType> {
961            match use_ {
962                fdecl::Use::Config(_) => None,
963                #[cfg(fuchsia_api_level_at_least = "29")]
964                fdecl::Use::Dictionary(_) => Some(NodeType::Dictionary),
965                fdecl::Use::Directory(_) => Some(NodeType::Directory),
966                fdecl::Use::EventStream(_) => Some(NodeType::Service),
967                fdecl::Use::Protocol(_) => Some(NodeType::Service),
968                #[cfg(fuchsia_api_level_at_least = "HEAD")]
969                fdecl::Use::Runner(_) => None,
970                fdecl::Use::Service(_) => Some(NodeType::Directory),
971                fdecl::Use::Storage(_) => Some(NodeType::Directory),
972                _ => None,
973            }
974        }
975        let mut paths_and_uses =
976            uses.iter().filter_map(|use_| use_to_path(use_).map(|x| (x, use_))).collect::<Vec<_>>();
977        let mut duplicate_fields = false;
978        while let Some((path, use_)) = paths_and_uses.pop() {
979            if paths_and_uses.iter().any(|(path_from_iter, use_from_iter)| {
980                path == *path_from_iter
981                    && !(DeclType::from(*use_from_iter) == DeclType::UseDictionary
982                        && DeclType::from(use_) == DeclType::UseDictionary)
983            }) {
984                self.errors.push(Error::duplicate_field(
985                    use_.into(),
986                    "target_path",
987                    path.to_str().unwrap(),
988                ));
989                duplicate_fields = true;
990            }
991        }
992        if duplicate_fields {
993            // When paths are identical instead of merely prefixes of each other, then the above is
994            // a more ergonomic and easy to understand error. The logic below will also fire for
995            return;
996        }
997        for (use_a, use_b) in uses.iter().tuple_combinations() {
998            let Some(path_a) = use_to_path(use_a) else {
999                continue;
1000            };
1001            let Some(path_b) = use_to_path(use_b) else {
1002                continue;
1003            };
1004            let Some(type_a) = use_to_type(use_a) else {
1005                continue;
1006            };
1007            let Some(type_b) = use_to_type(use_b) else {
1008                continue;
1009            };
1010            let mut conflicts = false;
1011            match (type_a, type_b) {
1012                (NodeType::Service, NodeType::Service)
1013                | (NodeType::Directory, NodeType::Service)
1014                | (NodeType::Service, NodeType::Directory)
1015                | (NodeType::Directory, NodeType::Directory) => {
1016                    if path_a.starts_with(path_b) || path_b.starts_with(path_a) {
1017                        conflicts = true;
1018                    }
1019                }
1020                (NodeType::Dictionary, NodeType::Service)
1021                | (NodeType::Dictionary, NodeType::Directory) => {
1022                    if path_a.starts_with(path_b) {
1023                        conflicts = true;
1024                    }
1025                }
1026                (NodeType::Service, NodeType::Dictionary)
1027                | (NodeType::Directory, NodeType::Dictionary) => {
1028                    if path_b.starts_with(path_a) {
1029                        conflicts = true;
1030                    }
1031                }
1032                (NodeType::Dictionary, NodeType::Dictionary) => {
1033                    // All combinations of two dictionaries are valid.
1034                }
1035            }
1036            if conflicts {
1037                self.errors.push(Error::invalid_path_overlap(
1038                    use_a.into(),
1039                    path_a.to_str().unwrap(),
1040                    use_b.into(),
1041                    path_b.to_str().unwrap(),
1042                ));
1043            }
1044        }
1045        for use_ in uses {
1046            let Some(used_path) = use_to_path(use_) else {
1047                continue;
1048            };
1049            if used_path.starts_with(Path::new("/pkg")) {
1050                self.errors.push(Error::pkg_path_overlap(use_.into(), used_path.to_str().unwrap()));
1051            }
1052        }
1053    }
1054
1055    fn validate_use_fields(
1056        &mut self,
1057        decl: DeclType,
1058        // This takes a callback that returns a [Container], instead of the &[Container] directly,
1059        // to avoid a borrow checker error that would occur from a simultaneous borrow on
1060        // &mut self.
1061        capability_checker: impl Fn(&Self) -> &dyn Container,
1062        source: Option<&'a fdecl::Ref>,
1063        source_name: Option<&'a String>,
1064        source_dictionary: Option<&'a String>,
1065        target_path: Option<&'a String>,
1066        dependency_type: Option<&fdecl::DependencyType>,
1067        availability: Option<&'a fdecl::Availability>,
1068    ) {
1069        self.validate_use_source(decl, source, source_dictionary);
1070
1071        check_name(source_name, decl, "source_name", &mut self.errors);
1072        if source_dictionary.is_some() {
1073            check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
1074        }
1075        match decl {
1076            DeclType::UseRunner | DeclType::UseConfiguration => {}
1077            #[cfg(fuchsia_api_level_at_least = "HEAD")]
1078            DeclType::UseProtocol => {
1079                if target_path.is_some() {
1080                    check_path(target_path, decl, "target_path", &mut self.errors);
1081                } else {
1082                    // Expect this use to contain numbered_handle, verified by the caller.
1083                }
1084            }
1085            _ => {
1086                check_path(target_path, decl, "target_path", &mut self.errors);
1087            }
1088        }
1089        check_use_availability(decl, availability, &mut self.errors);
1090
1091        // Only allow `weak` dependency with `use from child`.
1092        let is_use_from_child = match source {
1093            Some(fdecl::Ref::Child(_)) => true,
1094            _ => false,
1095        };
1096        match (is_use_from_child, dependency_type) {
1097            (false, Some(fdecl::DependencyType::Weak)) => {
1098                self.errors.push(Error::invalid_field(decl, "dependency_type"));
1099            }
1100            _ => {}
1101        }
1102
1103        self.validate_route_from_self(
1104            decl,
1105            source,
1106            source_name,
1107            source_dictionary,
1108            capability_checker,
1109        );
1110    }
1111
1112    fn validate_use_source(
1113        &mut self,
1114        decl: DeclType,
1115        source: Option<&'a fdecl::Ref>,
1116        source_dictionary: Option<&'a String>,
1117    ) {
1118        match (source, source_dictionary) {
1119            // These sources support source_dictionary.
1120            (Some(fdecl::Ref::Parent(_)), _) => {}
1121            (Some(fdecl::Ref::Self_(_)), _) => {}
1122            (Some(fdecl::Ref::Child(child)), _) => {
1123                self.validate_child_ref(decl, "source", &child, OfferType::Static);
1124                return;
1125            }
1126            // These sources don't.
1127            (Some(fdecl::Ref::Framework(_)), None) => {}
1128            (Some(fdecl::Ref::Debug(_)), None) => {}
1129            (Some(fdecl::Ref::Capability(c)), None) => {
1130                self.validate_source_capability(&c, decl, "source");
1131                return;
1132            }
1133            #[cfg(fuchsia_api_level_at_least = "HEAD")]
1134            (Some(fdecl::Ref::Environment(_)), None) => {}
1135            (Some(fdecl::Ref::Collection(collection)), None) if decl == DeclType::UseService => {
1136                self.validate_collection_ref(decl, "source", &collection);
1137                return;
1138            }
1139            // `source` is required.
1140            (None, _) => self.errors.push(Error::missing_field(decl, "source")),
1141            // Any combination that was not recognized above must be invalid.
1142            (_, _) => self.errors.push(Error::invalid_field(decl, "source")),
1143        }
1144    }
1145
1146    fn validate_child_decl(&mut self, child: &'a fdecl::Child) {
1147        if let Err(mut e) = validate_child(child, check_name) {
1148            self.errors.append(&mut e.errs);
1149        }
1150        if let Some(name) = child.name.as_ref() {
1151            let name: &str = name;
1152            if self.all_children.insert(name, child).is_some() {
1153                self.errors.push(Error::duplicate_field(DeclType::Child, "name", name));
1154            }
1155        }
1156        if let Some(environment) = child.environment.as_ref() {
1157            if !self.all_environment_names.contains(environment.as_str()) {
1158                self.errors.push(Error::invalid_environment(
1159                    DeclType::Child,
1160                    "environment",
1161                    environment,
1162                ));
1163            }
1164        }
1165    }
1166
1167    fn validate_collection_decl(&mut self, collection: &'a fdecl::Collection) {
1168        let name = collection.name.as_ref();
1169        if check_name(name, DeclType::Collection, "name", &mut self.errors) {
1170            let name: &str = name.unwrap();
1171            if !self.all_collections.insert(name) {
1172                self.errors.push(Error::duplicate_field(DeclType::Collection, "name", name));
1173            }
1174        }
1175        if collection.durability.is_none() {
1176            self.errors.push(Error::missing_field(DeclType::Collection, "durability"));
1177        }
1178        if let Some(environment) = collection.environment.as_ref() {
1179            if !self.all_environment_names.contains(environment.as_str()) {
1180                self.errors.push(Error::invalid_environment(
1181                    DeclType::Collection,
1182                    "environment",
1183                    environment,
1184                ));
1185            }
1186        }
1187        // Allow `allowed_offers` & `allow_long_names` to be unset/unvalidated, for backwards compatibility.
1188    }
1189
1190    fn validate_environment_decl(&mut self, environment: &'a fdecl::Environment) {
1191        let name = environment.name.as_ref();
1192        check_name(name, DeclType::Environment, "name", &mut self.errors);
1193        if environment.extends.is_none() {
1194            self.errors.push(Error::missing_field(DeclType::Environment, "extends"));
1195        }
1196        if let Some(runners) = environment.runners.as_ref() {
1197            let mut registered_runners = HashSet::new();
1198            for runner in runners {
1199                self.validate_runner_registration(runner, &mut registered_runners);
1200            }
1201        }
1202        if let Some(resolvers) = environment.resolvers.as_ref() {
1203            let mut registered_schemes = HashSet::new();
1204            for resolver in resolvers {
1205                self.validate_resolver_registration(resolver, &mut registered_schemes);
1206            }
1207        }
1208
1209        match environment.extends.as_ref() {
1210            Some(fdecl::EnvironmentExtends::None) => {
1211                if environment.stop_timeout_ms.is_none() {
1212                    self.errors
1213                        .push(Error::missing_field(DeclType::Environment, "stop_timeout_ms"));
1214                }
1215            }
1216            None | Some(fdecl::EnvironmentExtends::Realm) => {}
1217        }
1218
1219        if let Some(debugs) = environment.debug_capabilities.as_ref() {
1220            for debug in debugs {
1221                self.validate_environment_debug_registration(debug);
1222            }
1223        }
1224    }
1225
1226    fn validate_runner_registration(
1227        &mut self,
1228        runner_registration: &'a fdecl::RunnerRegistration,
1229        runner_names: &mut HashSet<&'a str>,
1230    ) {
1231        check_name(
1232            runner_registration.source_name.as_ref(),
1233            DeclType::RunnerRegistration,
1234            "source_name",
1235            &mut self.errors,
1236        );
1237        self.validate_registration_source(
1238            runner_registration.source.as_ref(),
1239            DeclType::RunnerRegistration,
1240        );
1241        // If the source is `self`, ensure we have a corresponding Runner.
1242        if let (Some(fdecl::Ref::Self_(_)), Some(name)) =
1243            (&runner_registration.source, runner_registration.source_name.as_ref())
1244        {
1245            if !self.all_runners.contains(name as &str) {
1246                self.errors.push(Error::invalid_runner(
1247                    DeclType::RunnerRegistration,
1248                    "source_name",
1249                    name,
1250                ));
1251            }
1252        }
1253
1254        check_name(
1255            runner_registration.target_name.as_ref(),
1256            DeclType::RunnerRegistration,
1257            "target_name",
1258            &mut self.errors,
1259        );
1260        if let Some(name) = runner_registration.target_name.as_ref() {
1261            if !runner_names.insert(name.as_str()) {
1262                self.errors.push(Error::duplicate_field(
1263                    DeclType::RunnerRegistration,
1264                    "target_name",
1265                    name,
1266                ));
1267            }
1268        }
1269    }
1270
1271    fn validate_resolver_registration(
1272        &mut self,
1273        resolver_registration: &'a fdecl::ResolverRegistration,
1274        schemes: &mut HashSet<&'a str>,
1275    ) {
1276        check_name(
1277            resolver_registration.resolver.as_ref(),
1278            DeclType::ResolverRegistration,
1279            "resolver",
1280            &mut self.errors,
1281        );
1282        self.validate_registration_source(
1283            resolver_registration.source.as_ref(),
1284            DeclType::ResolverRegistration,
1285        );
1286        check_url_scheme(
1287            resolver_registration.scheme.as_ref(),
1288            DeclType::ResolverRegistration,
1289            "scheme",
1290            &mut self.errors,
1291        );
1292        if let Some(scheme) = resolver_registration.scheme.as_ref() {
1293            if !schemes.insert(scheme.as_str()) {
1294                self.errors.push(Error::duplicate_field(
1295                    DeclType::ResolverRegistration,
1296                    "scheme",
1297                    scheme,
1298                ));
1299            }
1300        }
1301    }
1302
1303    fn validate_registration_source(&mut self, source: Option<&'a fdecl::Ref>, ty: DeclType) {
1304        match source {
1305            Some(fdecl::Ref::Parent(_)) => {}
1306            Some(fdecl::Ref::Self_(_)) => {}
1307            Some(fdecl::Ref::Child(child_ref)) => {
1308                // Make sure the child is valid.
1309                self.validate_child_ref(ty, "source", &child_ref, OfferType::Static);
1310            }
1311            Some(_) => {
1312                self.errors.push(Error::invalid_field(ty, "source"));
1313            }
1314            None => {
1315                self.errors.push(Error::missing_field(ty, "source"));
1316            }
1317        }
1318    }
1319
1320    fn validate_service_decl(&mut self, service: &'a fdecl::Service, as_builtin: bool) {
1321        if check_name(service.name.as_ref(), DeclType::Service, "name", &mut self.errors) {
1322            let name = service.name.as_ref().unwrap();
1323            if !self.all_capability_ids.insert(name) {
1324                self.errors.push(Error::duplicate_field(DeclType::Service, "name", name.as_str()));
1325            }
1326            self.all_services.insert(name);
1327        }
1328        match as_builtin {
1329            true => {
1330                if let Some(path) = service.source_path.as_ref() {
1331                    self.errors.push(Error::extraneous_source_path(DeclType::Service, path))
1332                }
1333            }
1334            false => {
1335                check_path(
1336                    service.source_path.as_ref(),
1337                    DeclType::Service,
1338                    "source_path",
1339                    &mut self.errors,
1340                );
1341            }
1342        }
1343    }
1344
1345    fn validate_protocol_decl(&mut self, protocol: &'a fdecl::Protocol, as_builtin: bool) {
1346        if check_name(protocol.name.as_ref(), DeclType::Protocol, "name", &mut self.errors) {
1347            let name = protocol.name.as_ref().unwrap();
1348            if !self.all_capability_ids.insert(name) {
1349                self.errors.push(Error::duplicate_field(DeclType::Protocol, "name", name.as_str()));
1350            }
1351            self.all_protocols.insert(name);
1352        }
1353        match as_builtin {
1354            true => {
1355                if let Some(path) = protocol.source_path.as_ref() {
1356                    self.errors.push(Error::extraneous_source_path(DeclType::Protocol, path))
1357                }
1358            }
1359            false => {
1360                check_path(
1361                    protocol.source_path.as_ref(),
1362                    DeclType::Protocol,
1363                    "source_path",
1364                    &mut self.errors,
1365                );
1366            }
1367        }
1368
1369        #[cfg(fuchsia_api_level_at_least = "HEAD")]
1370        match protocol.delivery {
1371            Some(delivery) => match cm_types::DeliveryType::try_from(delivery) {
1372                Ok(_) => {}
1373                Err(_) => self.errors.push(Error::invalid_field(DeclType::Protocol, "delivery")),
1374            },
1375            None => {}
1376        }
1377    }
1378
1379    fn validate_directory_decl(&mut self, directory: &'a fdecl::Directory, as_builtin: bool) {
1380        if check_name(directory.name.as_ref(), DeclType::Directory, "name", &mut self.errors) {
1381            let name = directory.name.as_ref().unwrap();
1382            if !self.all_capability_ids.insert(name) {
1383                self.errors.push(Error::duplicate_field(
1384                    DeclType::Directory,
1385                    "name",
1386                    name.as_str(),
1387                ));
1388            }
1389            self.all_directories.insert(name);
1390        }
1391        match as_builtin {
1392            true => {
1393                if let Some(path) = directory.source_path.as_ref() {
1394                    self.errors.push(Error::extraneous_source_path(DeclType::Directory, path))
1395                }
1396            }
1397            false => {
1398                check_path(
1399                    directory.source_path.as_ref(),
1400                    DeclType::Directory,
1401                    "source_path",
1402                    &mut self.errors,
1403                );
1404            }
1405        }
1406        if directory.rights.is_none() {
1407            self.errors.push(Error::missing_field(DeclType::Directory, "rights"));
1408        }
1409    }
1410
1411    fn validate_storage_decl(&mut self, storage: &'a fdecl::Storage) {
1412        match storage.source.as_ref() {
1413            Some(fdecl::Ref::Parent(_)) => {}
1414            Some(fdecl::Ref::Self_(_)) => {}
1415            Some(fdecl::Ref::Child(child)) => {
1416                let _ =
1417                    self.validate_child_ref(DeclType::Storage, "source", &child, OfferType::Static);
1418            }
1419            Some(_) => {
1420                self.errors.push(Error::invalid_field(DeclType::Storage, "source"));
1421            }
1422            None => {
1423                self.errors.push(Error::missing_field(DeclType::Storage, "source"));
1424            }
1425        };
1426        if check_name(storage.name.as_ref(), DeclType::Storage, "name", &mut self.errors) {
1427            let name = storage.name.as_ref().unwrap();
1428            if !self.all_capability_ids.insert(name) {
1429                self.errors.push(Error::duplicate_field(DeclType::Storage, "name", name.as_str()));
1430            }
1431            self.all_storages.insert(name, storage.source.as_ref());
1432        }
1433        if storage.storage_id.is_none() {
1434            self.errors.push(Error::missing_field(DeclType::Storage, "storage_id"));
1435        }
1436        check_name(
1437            storage.backing_dir.as_ref(),
1438            DeclType::Storage,
1439            "backing_dir",
1440            &mut self.errors,
1441        );
1442    }
1443
1444    fn validate_runner_decl(&mut self, runner: &'a fdecl::Runner, as_builtin: bool) {
1445        if check_name(runner.name.as_ref(), DeclType::Runner, "name", &mut self.errors) {
1446            let name = runner.name.as_ref().unwrap();
1447            if !self.all_capability_ids.insert(name) {
1448                self.errors.push(Error::duplicate_field(DeclType::Runner, "name", name.as_str()));
1449            }
1450            self.all_runners.insert(name);
1451        }
1452        match as_builtin {
1453            true => {
1454                if let Some(path) = runner.source_path.as_ref() {
1455                    self.errors.push(Error::extraneous_source_path(DeclType::Runner, path))
1456                }
1457            }
1458            false => {
1459                check_path(
1460                    runner.source_path.as_ref(),
1461                    DeclType::Runner,
1462                    "source_path",
1463                    &mut self.errors,
1464                );
1465            }
1466        }
1467    }
1468
1469    fn validate_resolver_decl(&mut self, resolver: &'a fdecl::Resolver, as_builtin: bool) {
1470        if check_name(resolver.name.as_ref(), DeclType::Resolver, "name", &mut self.errors) {
1471            let name = resolver.name.as_ref().unwrap();
1472            if !self.all_capability_ids.insert(name) {
1473                self.errors.push(Error::duplicate_field(DeclType::Resolver, "name", name.as_str()));
1474            }
1475            self.all_resolvers.insert(name);
1476        }
1477        match as_builtin {
1478            true => {
1479                if let Some(path) = resolver.source_path.as_ref() {
1480                    self.errors.push(Error::extraneous_source_path(DeclType::Resolver, path))
1481                }
1482            }
1483            false => {
1484                check_path(
1485                    resolver.source_path.as_ref(),
1486                    DeclType::Resolver,
1487                    "source_path",
1488                    &mut self.errors,
1489                );
1490            }
1491        }
1492    }
1493
1494    // Dictionaries can reference other dictionaries in the same manifest, so before processing any
1495    // dictionary declarations this function should be called to do a first pass to pre-populate
1496    // the dictionary map.
1497    fn load_dictionary_names(&mut self, dictionaries: impl Iterator<Item = &'a fdecl::Dictionary>) {
1498        for dictionary in dictionaries {
1499            let decl = DeclType::Dictionary;
1500            if check_name(dictionary.name.as_ref(), decl, "name", &mut self.errors) {
1501                let name = dictionary.name.as_ref().unwrap();
1502                if !self.all_capability_ids.insert(name) {
1503                    self.errors.push(Error::duplicate_field(decl, "name", name.as_str()));
1504                }
1505                self.all_dictionaries.insert(name, &dictionary);
1506            }
1507        }
1508    }
1509
1510    fn validate_dictionary_decl(&mut self, dictionary: &'a fdecl::Dictionary) {
1511        let decl = DeclType::Dictionary;
1512        if let Some(path) = dictionary.source_path.as_ref() {
1513            if dictionary.source.is_some() {
1514                self.errors.push(Error::extraneous_field(decl, "source"));
1515            }
1516            check_path(Some(path), DeclType::Dictionary, "source_path", &mut self.errors);
1517        }
1518    }
1519
1520    #[cfg(fuchsia_api_level_at_least = "HEAD")]
1521    fn validate_configuration_decl(&mut self, config: &'a fdecl::Configuration) {
1522        let decl = DeclType::Configuration;
1523        if check_name(config.name.as_ref(), decl, "name", &mut self.errors) {
1524            let name = config.name.as_ref().unwrap();
1525            if !self.all_capability_ids.insert(name) {
1526                self.errors.push(Error::duplicate_field(decl, "name", name.as_str()));
1527            }
1528            self.all_configs.insert(name);
1529        }
1530    }
1531
1532    fn validate_environment_debug_registration(&mut self, debug: &'a fdecl::DebugRegistration) {
1533        match debug {
1534            fdecl::DebugRegistration::Protocol(o) => {
1535                let decl = DeclType::DebugProtocolRegistration;
1536                self.validate_environment_debug_fields(
1537                    decl,
1538                    o.source.as_ref(),
1539                    o.source_name.as_ref(),
1540                    o.target_name.as_ref(),
1541                );
1542
1543                if let (Some(fdecl::Ref::Self_(_)), Some(name)) =
1544                    (&o.source, o.source_name.as_ref())
1545                {
1546                    if !self.all_protocols.contains(&name as &str) {
1547                        self.errors.push(Error::invalid_field(decl, "source"));
1548                    }
1549                }
1550            }
1551            _ => {
1552                self.errors.push(Error::invalid_field(DeclType::Environment, "debug"));
1553            }
1554        }
1555    }
1556
1557    fn validate_environment_debug_fields(
1558        &mut self,
1559        decl: DeclType,
1560        source: Option<&fdecl::Ref>,
1561        source_name: Option<&String>,
1562        target_name: Option<&'a String>,
1563    ) {
1564        // We don't support "source" from "capability" for now.
1565        match source {
1566            Some(fdecl::Ref::Parent(_)) => {}
1567            Some(fdecl::Ref::Self_(_)) => {}
1568            Some(fdecl::Ref::Framework(_)) => {}
1569            Some(fdecl::Ref::Child(child)) => {
1570                let _ = self.validate_child_ref(decl, "source", &child, OfferType::Static);
1571            }
1572            Some(_) => self.errors.push(Error::invalid_field(decl, "source")),
1573            None => self.errors.push(Error::missing_field(decl, "source")),
1574        }
1575        check_name(source_name, decl, "source_name", &mut self.errors);
1576        check_name(target_name, decl, "target_name", &mut self.errors);
1577    }
1578
1579    fn validate_event_stream_decl(&mut self, event: &'a fdecl::EventStream) {
1580        if check_name(event.name.as_ref(), DeclType::EventStream, "name", &mut self.errors) {
1581            let name = event.name.as_ref().unwrap();
1582            if !self.all_capability_ids.insert(name) {
1583                self.errors.push(Error::duplicate_field(
1584                    DeclType::EventStream,
1585                    "name",
1586                    name.as_str(),
1587                ));
1588            }
1589        }
1590    }
1591
1592    fn validate_source_collection(
1593        &mut self,
1594        collection: &fdecl::CollectionRef,
1595        decl_type: DeclType,
1596    ) -> bool {
1597        let num_errors = self.errors.len();
1598        if check_name(Some(&collection.name), decl_type, "source.collection.name", &mut self.errors)
1599            && !self.all_collections.contains(&collection.name as &str)
1600        {
1601            self.errors.push(Error::invalid_collection(
1602                decl_type,
1603                "source",
1604                &collection.name as &str,
1605            ));
1606        }
1607        num_errors == self.errors.len()
1608    }
1609
1610    fn validate_filtered_service_fields(
1611        &mut self,
1612        decl_type: DeclType,
1613        source_instance_filter: Option<&Vec<String>>,
1614        renamed_instances: Option<&Vec<fdecl::NameMapping>>,
1615    ) {
1616        if let Some(source_instance_filter) = source_instance_filter {
1617            if source_instance_filter.is_empty() {
1618                // if the  source_instance_filter is empty the offered service will have 0 instances,
1619                // which means the offer shouldn't have been created at all.
1620                self.errors.push(Error::invalid_field(decl_type, "source_instance_filter"));
1621            }
1622            for name in source_instance_filter {
1623                check_name(Some(name), decl_type, "source_instance_filter", &mut self.errors);
1624            }
1625        }
1626        if let Some(renamed_instances) = renamed_instances {
1627            // Multiple sources shouldn't map to the same target name
1628            let mut seen_target_names = HashSet::<String>::new();
1629            for mapping in renamed_instances {
1630                check_name(
1631                    Some(&mapping.source_name),
1632                    decl_type,
1633                    "renamed_instances.source_name",
1634                    &mut self.errors,
1635                );
1636                check_name(
1637                    Some(&mapping.target_name),
1638                    decl_type,
1639                    "renamed_instances.target_name",
1640                    &mut self.errors,
1641                );
1642                if !seen_target_names.insert(mapping.target_name.clone()) {
1643                    self.errors.push(Error::invalid_field(decl_type, "renamed_instances"));
1644                    break;
1645                }
1646            }
1647        }
1648    }
1649
1650    fn validate_source_capability(
1651        &mut self,
1652        capability: &fdecl::CapabilityRef,
1653        decl_type: DeclType,
1654        field: &str,
1655    ) -> bool {
1656        let num_errors = self.errors.len();
1657        if check_name(Some(&capability.name), decl_type, "source.capability.name", &mut self.errors)
1658            && !self.all_capability_ids.contains(capability.name.as_str())
1659        {
1660            self.errors.push(Error::invalid_capability(decl_type, field, &capability.name));
1661        }
1662        num_errors == self.errors.len()
1663    }
1664
1665    /// Return a key that can be used in `HashMap` to group aggregate declarations.
1666    ///
1667    /// Returns `None` if the input resembles an invalid declaration.
1668    fn make_group_key(
1669        target_name: Option<&'a String>,
1670        target: Option<&'a fdecl::Ref>,
1671    ) -> Option<(&'a str, RefKey<'a>)> {
1672        if target_name.is_none() {
1673            return None;
1674        }
1675        let target_name = target_name.unwrap().as_str();
1676        if target.is_none() {
1677            return None;
1678        }
1679        let target = match target.unwrap() {
1680            fdecl::Ref::Parent(_) => RefKey::Parent,
1681            fdecl::Ref::Self_(_) => RefKey::Self_,
1682            fdecl::Ref::Child(r) => RefKey::Child(r.name.as_str()),
1683            fdecl::Ref::Collection(r) => RefKey::Collection(r.name.as_str()),
1684            fdecl::Ref::Framework(_) => RefKey::Framework,
1685            fdecl::Ref::Capability(_) => RefKey::Capability,
1686            fdecl::Ref::Debug(_) => RefKey::Debug,
1687            fdecl::RefUnknown!() => {
1688                return None;
1689            }
1690        };
1691        Some((target_name, target))
1692    }
1693
1694    fn validate_aggregation_has_same_availability(
1695        &mut self,
1696        route_group: &Vec<impl HasAvailability>,
1697    ) {
1698        // Use `BtreeSet` for stable ordering of items in error message.
1699        let availability_of_sources: BTreeSet<_> =
1700            route_group.iter().map(|r| r.availability()).collect();
1701
1702        // All sources that feed into an aggregation operation should have the same availability.
1703        if availability_of_sources.len() > 1 {
1704            self.errors.push(Error::different_availability_in_aggregation(
1705                availability_of_sources.into_iter().collect(),
1706            ));
1707        }
1708    }
1709
1710    // Checks a group of expose decls to confirm that any duplicate exposes are
1711    // valid aggregate expose declarations.
1712    fn validate_expose_group(&mut self, exposes: &'a [fdecl::Expose]) {
1713        let mut expose_groups: HashMap<_, Vec<fdecl::ExposeService>> = HashMap::new();
1714        let service_exposes = exposes
1715            .into_iter()
1716            .filter_map(|o| if let fdecl::Expose::Service(s) = o { Some(s) } else { None });
1717        for expose in service_exposes {
1718            let key = Self::make_group_key(expose.target_name.as_ref(), expose.target.as_ref());
1719            if let Some(key) = key {
1720                expose_groups.entry(key).or_insert_with(|| vec![]).push(expose.clone());
1721            }
1722        }
1723        for expose_group in expose_groups.into_values() {
1724            if expose_group.len() == 1 {
1725                // If there are not multiple exposes for a (target_name, target) pair then there are
1726                // no aggregation conditions to check.
1727                continue;
1728            }
1729
1730            self.validate_aggregation_has_same_availability(&expose_group);
1731        }
1732    }
1733
1734    fn validate_expose_decl(
1735        &mut self,
1736        expose: &'a fdecl::Expose,
1737        expose_to_parent_ids: &mut HashMap<&'a str, AllowableIds>,
1738        expose_to_framework_ids: &mut HashMap<&'a str, AllowableIds>,
1739    ) {
1740        match expose {
1741            fdecl::Expose::Service(e) => {
1742                let decl = DeclType::ExposeService;
1743                self.validate_expose_fields(
1744                    decl,
1745                    AllowableIds::Many,
1746                    CollectionSource::Allow,
1747                    Self::service_checker,
1748                    e.source.as_ref(),
1749                    e.source_name.as_ref(),
1750                    e.source_dictionary.as_ref(),
1751                    e.target.as_ref(),
1752                    e.target_name.as_ref(),
1753                    e.availability.as_ref(),
1754                    expose_to_parent_ids,
1755                    expose_to_framework_ids,
1756                );
1757            }
1758            fdecl::Expose::Protocol(e) => {
1759                let decl = DeclType::ExposeProtocol;
1760                self.validate_expose_fields(
1761                    decl,
1762                    AllowableIds::One,
1763                    CollectionSource::Deny,
1764                    Self::protocol_checker,
1765                    e.source.as_ref(),
1766                    e.source_name.as_ref(),
1767                    e.source_dictionary.as_ref(),
1768                    e.target.as_ref(),
1769                    e.target_name.as_ref(),
1770                    e.availability.as_ref(),
1771                    expose_to_parent_ids,
1772                    expose_to_framework_ids,
1773                );
1774            }
1775            fdecl::Expose::Directory(e) => {
1776                let decl = DeclType::ExposeDirectory;
1777                self.validate_expose_fields(
1778                    decl,
1779                    AllowableIds::One,
1780                    CollectionSource::Deny,
1781                    Self::directory_checker,
1782                    e.source.as_ref(),
1783                    e.source_name.as_ref(),
1784                    e.source_dictionary.as_ref(),
1785                    e.target.as_ref(),
1786                    e.target_name.as_ref(),
1787                    e.availability.as_ref(),
1788                    expose_to_parent_ids,
1789                    expose_to_framework_ids,
1790                );
1791
1792                // Subdir makes sense when routing, but when exposing to framework the subdirectory
1793                // can be exposed directly.
1794                match e.target.as_ref() {
1795                    Some(fdecl::Ref::Framework(_)) => {
1796                        if e.subdir.is_some() {
1797                            self.errors.push(Error::invalid_field(decl, "subdir"));
1798                        }
1799                    }
1800                    _ => {}
1801                }
1802
1803                if let Some(subdir) = e.subdir.as_ref() {
1804                    check_relative_path(Some(subdir), decl, "subdir", &mut self.errors);
1805                }
1806            }
1807            fdecl::Expose::Runner(e) => {
1808                let decl = DeclType::ExposeRunner;
1809                self.validate_expose_fields(
1810                    decl,
1811                    AllowableIds::One,
1812                    CollectionSource::Deny,
1813                    Self::runner_checker,
1814                    e.source.as_ref(),
1815                    e.source_name.as_ref(),
1816                    e.source_dictionary.as_ref(),
1817                    e.target.as_ref(),
1818                    e.target_name.as_ref(),
1819                    Some(&fdecl::Availability::Required),
1820                    expose_to_parent_ids,
1821                    expose_to_framework_ids,
1822                );
1823            }
1824            fdecl::Expose::Resolver(e) => {
1825                let decl = DeclType::ExposeResolver;
1826                self.validate_expose_fields(
1827                    decl,
1828                    AllowableIds::One,
1829                    CollectionSource::Deny,
1830                    Self::resolver_checker,
1831                    e.source.as_ref(),
1832                    e.source_name.as_ref(),
1833                    e.source_dictionary.as_ref(),
1834                    e.target.as_ref(),
1835                    e.target_name.as_ref(),
1836                    Some(&fdecl::Availability::Required),
1837                    expose_to_parent_ids,
1838                    expose_to_framework_ids,
1839                );
1840            }
1841            fdecl::Expose::Dictionary(e) => {
1842                let decl = DeclType::ExposeDictionary;
1843                self.validate_expose_fields(
1844                    decl,
1845                    AllowableIds::One,
1846                    CollectionSource::Deny,
1847                    Self::dictionary_checker,
1848                    e.source.as_ref(),
1849                    e.source_name.as_ref(),
1850                    e.source_dictionary.as_ref(),
1851                    e.target.as_ref(),
1852                    e.target_name.as_ref(),
1853                    e.availability.as_ref(),
1854                    expose_to_parent_ids,
1855                    expose_to_framework_ids,
1856                );
1857            }
1858            #[cfg(fuchsia_api_level_at_least = "HEAD")]
1859            fdecl::Expose::Config(e) => {
1860                let decl = DeclType::ExposeConfig;
1861                self.validate_expose_fields(
1862                    decl,
1863                    AllowableIds::One,
1864                    CollectionSource::Deny,
1865                    Self::config_checker,
1866                    e.source.as_ref(),
1867                    e.source_name.as_ref(),
1868                    None,
1869                    e.target.as_ref(),
1870                    e.target_name.as_ref(),
1871                    e.availability.as_ref(),
1872                    expose_to_parent_ids,
1873                    expose_to_framework_ids,
1874                );
1875            }
1876            _ => {
1877                self.errors.push(Error::invalid_field(DeclType::Component, "expose"));
1878            }
1879        }
1880    }
1881
1882    fn validate_expose_fields(
1883        &mut self,
1884        decl: DeclType,
1885        allowable_ids: AllowableIds,
1886        collection_source: CollectionSource,
1887        // This takes a callback that returns a [Container], instead of the &[Container] directly,
1888        // to avoid a borrow checker error that would occur from a simultaneous borrow on
1889        // &mut self.
1890        capability_checker: impl Fn(&Self) -> &dyn Container,
1891        source: Option<&fdecl::Ref>,
1892        source_name: Option<&String>,
1893        source_dictionary: Option<&String>,
1894        target: Option<&fdecl::Ref>,
1895        target_name: Option<&'a String>,
1896        availability: Option<&fdecl::Availability>,
1897        expose_to_parent_ids: &mut HashMap<&'a str, AllowableIds>,
1898        expose_to_framework_ids: &mut HashMap<&'a str, AllowableIds>,
1899    ) {
1900        self.validate_expose_source(decl, collection_source, source, source_dictionary);
1901        check_route_availability(decl, availability, source, source_name, &mut self.errors);
1902        match target {
1903            Some(r) => match r {
1904                fdecl::Ref::Parent(_) => {}
1905                fdecl::Ref::Framework(_) => {}
1906                _ => {
1907                    self.errors.push(Error::invalid_field(decl, "target"));
1908                }
1909            },
1910            None => {
1911                self.errors.push(Error::missing_field(decl, "target"));
1912            }
1913        }
1914        check_name(source_name, decl, "source_name", &mut self.errors);
1915        if source_dictionary.is_some() {
1916            check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
1917        }
1918        if check_name(target_name, decl, "target_name", &mut self.errors) {
1919            let maybe_ids_set = match target {
1920                Some(fdecl::Ref::Parent(_)) => Some(expose_to_parent_ids),
1921                Some(fdecl::Ref::Framework(_)) => Some(expose_to_framework_ids),
1922                _ => None,
1923            };
1924            if let Some(ids_set) = maybe_ids_set {
1925                let target_name = target_name.unwrap();
1926                if let Some(prev_state) = ids_set.insert(target_name, allowable_ids) {
1927                    if prev_state == AllowableIds::One || prev_state != allowable_ids {
1928                        self.errors.push(Error::duplicate_field(decl, "target_name", target_name));
1929                    }
1930                }
1931            }
1932        }
1933
1934        self.validate_route_from_self(
1935            decl,
1936            source,
1937            source_name,
1938            source_dictionary,
1939            capability_checker,
1940        );
1941    }
1942
1943    fn validate_expose_source(
1944        &mut self,
1945        decl: DeclType,
1946        collection_source: CollectionSource,
1947        source: Option<&fdecl::Ref>,
1948        source_dictionary: Option<&String>,
1949    ) {
1950        match (source, source_dictionary) {
1951            // These sources support source_dictionary.
1952            (Some(fdecl::Ref::Self_(_)), _) => {}
1953            (Some(fdecl::Ref::Child(child)), _) => {
1954                let _ = self.validate_child_ref(decl, "source", &child, OfferType::Static);
1955            }
1956            // These sources don't.
1957            (Some(fdecl::Ref::VoidType(_)), None) => {}
1958            (Some(fdecl::Ref::Framework(_)), None) => {}
1959            (Some(fdecl::Ref::Capability(c)), None) => {
1960                self.validate_source_capability(c, decl, "source");
1961            }
1962            (Some(fdecl::Ref::Collection(c)), None)
1963                if collection_source == CollectionSource::Allow =>
1964            {
1965                self.validate_source_collection(c, decl);
1966            }
1967            // `source` is required.
1968            (None, _) => {
1969                self.errors.push(Error::missing_field(decl, "source"));
1970            }
1971            // Any combination that was not recognized above must be invalid.
1972            (_, _) => {
1973                self.errors.push(Error::invalid_field(decl, "source"));
1974            }
1975        }
1976    }
1977
1978    // Checks a group of offer decls to confirm that any duplicate offers are
1979    // valid aggregate offer declarations.
1980    fn validate_offer_group(&mut self, offers: &'a [fdecl::Offer], offer_type: OfferType) {
1981        let mut offer_groups: HashMap<_, Vec<fdecl::OfferService>> = HashMap::new();
1982        let service_offers = offers
1983            .into_iter()
1984            .filter_map(|o| if let fdecl::Offer::Service(s) = o { Some(s) } else { None });
1985        for offer in service_offers {
1986            let key = Self::make_group_key(offer.target_name.as_ref(), offer.target.as_ref());
1987            if let Some(key) = key {
1988                offer_groups.entry(key).or_insert_with(|| vec![]).push(offer.clone());
1989            }
1990        }
1991        for offer_group in offer_groups.into_values() {
1992            if offer_group.len() == 1 {
1993                // If there are not multiple offers for a (target_name, target) pair then there are
1994                // no aggregation conditions to check.
1995                continue;
1996            }
1997
1998            self.validate_aggregation_has_same_availability(&offer_group);
1999
2000            let mut source_instance_filter_entries: HashSet<String> = HashSet::new();
2001            let mut service_source_names: HashSet<String> = HashSet::new();
2002            for o in offer_group {
2003                // Currently only service capabilities can be aggregated
2004                match (o.source_instance_filter, offer_type) {
2005                    (Some(source_instance_filter), _) => {
2006                        for instance_name in source_instance_filter {
2007                            if !source_instance_filter_entries.insert(instance_name.clone()) {
2008                                // If the source instance in the filter has been seen before this
2009                                // means there is a conflicting aggregate service offer.
2010                                self.errors.push(Error::invalid_aggregate_offer(format!(
2011                                    "Conflicting source_instance_filter in aggregate service \
2012                                    offer, instance_name '{}' seen in filter lists multiple times",
2013                                    instance_name,
2014                                )));
2015                            }
2016                        }
2017                    }
2018                    (None, OfferType::Static) => {}
2019                    (None, OfferType::Dynamic) => {
2020                        // Dynamic offers must include a filter.
2021                        self.errors.push(Error::invalid_aggregate_offer(
2022                            "source_instance_filter must be set for dynamic aggregate service \
2023                            offers",
2024                        ));
2025                    }
2026                }
2027                service_source_names.insert(
2028                    o.source_name
2029                        .expect("Offer Service declarations must always contain source_name"),
2030                );
2031            }
2032
2033            if service_source_names.len() > 1 {
2034                self.errors.push(Error::invalid_aggregate_offer(format!(
2035                    "All aggregate service offers must have the same source_name, saw {}. Use \
2036                    renamed_instances to rename instance names to avoid conflict.",
2037                    service_source_names.into_iter().sorted().collect::<Vec<String>>().join(", ")
2038                )));
2039            }
2040        }
2041    }
2042
2043    fn validate_offer_decl(&mut self, offer: &'a fdecl::Offer, offer_type: OfferType) {
2044        match offer {
2045            fdecl::Offer::Service(o) => {
2046                let decl = DeclType::OfferService;
2047                self.validate_offer_fields(
2048                    decl,
2049                    AllowableIds::Many,
2050                    CollectionSource::Allow,
2051                    Self::service_checker,
2052                    o.source.as_ref(),
2053                    o.source_name.as_ref(),
2054                    o.source_dictionary.as_ref(),
2055                    o.target.as_ref(),
2056                    o.target_name.as_ref(),
2057                    #[cfg(fuchsia_api_level_at_least = "HEAD")]
2058                    Some(o.dependency_type.as_ref().unwrap_or(&fdecl::DependencyType::Strong)),
2059                    #[cfg(fuchsia_api_level_less_than = "HEAD")]
2060                    Some(&fdecl::DependencyType::Strong),
2061                    o.availability.as_ref(),
2062                    offer_type,
2063                );
2064                self.validate_filtered_service_fields(
2065                    decl,
2066                    o.source_instance_filter.as_ref(),
2067                    o.renamed_instances.as_ref(),
2068                );
2069            }
2070            fdecl::Offer::Protocol(o) => {
2071                let decl = DeclType::OfferProtocol;
2072                self.validate_offer_fields(
2073                    decl,
2074                    AllowableIds::One,
2075                    CollectionSource::Deny,
2076                    Self::protocol_checker,
2077                    o.source.as_ref(),
2078                    o.source_name.as_ref(),
2079                    o.source_dictionary.as_ref(),
2080                    o.target.as_ref(),
2081                    o.target_name.as_ref(),
2082                    o.dependency_type.as_ref(),
2083                    o.availability.as_ref(),
2084                    offer_type,
2085                );
2086            }
2087            fdecl::Offer::Directory(o) => {
2088                let decl = DeclType::OfferDirectory;
2089                self.validate_offer_fields(
2090                    decl,
2091                    AllowableIds::One,
2092                    CollectionSource::Deny,
2093                    Self::directory_checker,
2094                    o.source.as_ref(),
2095                    o.source_name.as_ref(),
2096                    o.source_dictionary.as_ref(),
2097                    o.target.as_ref(),
2098                    o.target_name.as_ref(),
2099                    o.dependency_type.as_ref(),
2100                    o.availability.as_ref(),
2101                    offer_type,
2102                );
2103                if let Some(subdir) = o.subdir.as_ref() {
2104                    check_relative_path(
2105                        Some(subdir),
2106                        DeclType::OfferDirectory,
2107                        "subdir",
2108                        &mut self.errors,
2109                    );
2110                }
2111            }
2112            fdecl::Offer::Storage(o) => {
2113                let decl = DeclType::OfferStorage;
2114                self.validate_storage_offer_fields(
2115                    decl,
2116                    Self::storage_checker,
2117                    o.source.as_ref(),
2118                    o.source_name.as_ref(),
2119                    o.target.as_ref(),
2120                    o.target_name.as_ref(),
2121                    o.availability.as_ref(),
2122                    offer_type,
2123                );
2124            }
2125            fdecl::Offer::Runner(o) => {
2126                let decl = DeclType::OfferRunner;
2127                self.validate_offer_fields(
2128                    decl,
2129                    AllowableIds::One,
2130                    CollectionSource::Deny,
2131                    Self::runner_checker,
2132                    o.source.as_ref(),
2133                    o.source_name.as_ref(),
2134                    o.source_dictionary.as_ref(),
2135                    o.target.as_ref(),
2136                    o.target_name.as_ref(),
2137                    Some(&fdecl::DependencyType::Strong),
2138                    Some(&fdecl::Availability::Required),
2139                    offer_type,
2140                );
2141            }
2142            fdecl::Offer::Resolver(o) => {
2143                let decl = DeclType::OfferResolver;
2144                self.validate_offer_fields(
2145                    decl,
2146                    AllowableIds::One,
2147                    CollectionSource::Deny,
2148                    Self::resolver_checker,
2149                    o.source.as_ref(),
2150                    o.source_name.as_ref(),
2151                    o.source_dictionary.as_ref(),
2152                    o.target.as_ref(),
2153                    o.target_name.as_ref(),
2154                    Some(&fdecl::DependencyType::Strong),
2155                    Some(&fdecl::Availability::Required),
2156                    offer_type,
2157                );
2158            }
2159            fdecl::Offer::EventStream(e) => {
2160                self.validate_event_stream_offer_fields(e, offer_type);
2161            }
2162            fdecl::Offer::Dictionary(o) => {
2163                let decl = DeclType::OfferDictionary;
2164                self.validate_offer_fields(
2165                    decl,
2166                    AllowableIds::One,
2167                    CollectionSource::Deny,
2168                    Self::dictionary_checker,
2169                    o.source.as_ref(),
2170                    o.source_name.as_ref(),
2171                    o.source_dictionary.as_ref(),
2172                    o.target.as_ref(),
2173                    o.target_name.as_ref(),
2174                    o.dependency_type.as_ref(),
2175                    o.availability.as_ref(),
2176                    offer_type,
2177                );
2178            }
2179            #[cfg(fuchsia_api_level_at_least = "HEAD")]
2180            fdecl::Offer::Config(o) => {
2181                let decl = DeclType::OfferConfig;
2182                self.validate_offer_fields(
2183                    decl,
2184                    AllowableIds::One,
2185                    CollectionSource::Deny,
2186                    Self::config_checker,
2187                    o.source.as_ref(),
2188                    o.source_name.as_ref(),
2189                    None,
2190                    o.target.as_ref(),
2191                    o.target_name.as_ref(),
2192                    Some(&fdecl::DependencyType::Strong),
2193                    o.availability.as_ref(),
2194                    offer_type,
2195                );
2196            }
2197            fdecl::OfferUnknown!() => {
2198                self.errors.push(Error::invalid_field(DeclType::Component, "offer"));
2199            }
2200        }
2201    }
2202
2203    fn validate_offer_fields(
2204        &mut self,
2205        decl: DeclType,
2206        allowable_names: AllowableIds,
2207        collection_source: CollectionSource,
2208        capability_checker: impl Fn(&Self) -> &dyn Container,
2209        source: Option<&'a fdecl::Ref>,
2210        source_name: Option<&'a String>,
2211        source_dictionary: Option<&'a String>,
2212        target: Option<&'a fdecl::Ref>,
2213        target_name: Option<&'a String>,
2214        dependency_type: Option<&'a fdecl::DependencyType>,
2215        availability: Option<&'a fdecl::Availability>,
2216        offer_type: OfferType,
2217    ) {
2218        self.validate_offer_source(decl, collection_source, source, source_dictionary, offer_type);
2219        check_route_availability(decl, availability, source, source_name, &mut self.errors);
2220        check_offer_name(source_name, decl, "source_name", offer_type, &mut self.errors);
2221        if source_dictionary.is_some() {
2222            check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
2223        }
2224        self.validate_offer_target(decl, allowable_names, target, target_name, offer_type);
2225        check_offer_name(target_name, decl, "target_name", offer_type, &mut self.errors);
2226
2227        if dependency_type.is_none() {
2228            self.errors.push(Error::missing_field(decl, "dependency_type"));
2229        }
2230
2231        self.validate_route_from_self(
2232            decl,
2233            source,
2234            source_name,
2235            source_dictionary,
2236            capability_checker,
2237        );
2238    }
2239
2240    fn validate_offer_source(
2241        &mut self,
2242        decl: DeclType,
2243        collection_source: CollectionSource,
2244        source: Option<&'a fdecl::Ref>,
2245        source_dictionary: Option<&'a String>,
2246        offer_type: OfferType,
2247    ) {
2248        match (source, source_dictionary) {
2249            // These sources support source_dictionary.
2250            (Some(fdecl::Ref::Parent(_)), _) => {}
2251            (Some(fdecl::Ref::Self_(_)), _) => {}
2252            (Some(fdecl::Ref::Child(child)), _) => {
2253                self.validate_child_ref(decl, "source", &child, offer_type);
2254            }
2255            // These sources don't.
2256            (Some(fdecl::Ref::VoidType(_)), None) => {}
2257            (Some(fdecl::Ref::Framework(_)), None) => {}
2258            (Some(fdecl::Ref::Capability(c)), None) => {
2259                self.validate_source_capability(c, decl, "source");
2260            }
2261            (Some(fdecl::Ref::Collection(c)), None)
2262                if collection_source == CollectionSource::Allow =>
2263            {
2264                self.validate_source_collection(c, decl);
2265            }
2266            // `source` is required.
2267            (None, _) => self.errors.push(Error::missing_field(decl, "source")),
2268            // Any combination that was not recognized above must be invalid.
2269            (_, _) => self.errors.push(Error::invalid_field(decl, "source")),
2270        }
2271    }
2272
2273    fn validate_storage_offer_fields(
2274        &mut self,
2275        decl: DeclType,
2276        // This takes a callback that returns a [Container], instead of the &[Container] directly,
2277        // to avoid a borrow checker error that would occur from a simultaneous borrow on
2278        // &mut self.
2279        capability_checker: impl Fn(&Self) -> &dyn Container,
2280        source: Option<&'a fdecl::Ref>,
2281        source_name: Option<&'a String>,
2282        target: Option<&'a fdecl::Ref>,
2283        target_name: Option<&'a String>,
2284        availability: Option<&fdecl::Availability>,
2285        offer_type: OfferType,
2286    ) {
2287        match source {
2288            Some(fdecl::Ref::Parent(_) | fdecl::Ref::VoidType(_) | fdecl::Ref::Self_(_)) => {}
2289            Some(_) => {
2290                self.errors.push(Error::invalid_field(decl, "source"));
2291            }
2292            None => {
2293                self.errors.push(Error::missing_field(decl, "source"));
2294            }
2295        }
2296        check_route_availability(decl, availability, source, source_name, &mut self.errors);
2297        check_offer_name(source_name, decl, "source_name", offer_type, &mut self.errors);
2298        self.validate_offer_target(decl, AllowableIds::One, target, target_name, offer_type);
2299        check_offer_name(target_name, decl, "target_name", offer_type, &mut self.errors);
2300
2301        if let (Some(fdecl::Ref::Self_(_)), Some(name)) = (source, source_name) {
2302            if !(capability_checker)(self).contains(name) {
2303                self.errors.push(Error::invalid_capability(decl, "source", name));
2304            }
2305        }
2306    }
2307
2308    fn validate_event_stream_offer_fields(
2309        &mut self,
2310        event_stream: &'a fdecl::OfferEventStream,
2311        offer_type: OfferType,
2312    ) {
2313        let decl = DeclType::OfferEventStream;
2314        check_name(event_stream.source_name.as_ref(), decl, "source_name", &mut self.errors);
2315        if event_stream.target == Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})) {
2316            // Expose to framework from framework is never valid.
2317            self.errors.push(Error::invalid_field(DeclType::OfferEventStream, "target"));
2318        }
2319        if let Some(scope) = &event_stream.scope {
2320            if scope.is_empty() {
2321                self.errors.push(Error::invalid_field(decl, "scope"));
2322            }
2323            for value in scope {
2324                match value {
2325                    fdecl::Ref::Child(child) => {
2326                        self.validate_child_ref(
2327                            DeclType::OfferEventStream,
2328                            "scope",
2329                            &child,
2330                            offer_type,
2331                        );
2332                    }
2333                    fdecl::Ref::Collection(collection) => {
2334                        self.validate_collection_ref(
2335                            DeclType::OfferEventStream,
2336                            "scope",
2337                            &collection,
2338                        );
2339                    }
2340                    _ => {
2341                        self.errors.push(Error::invalid_field(DeclType::OfferEventStream, "scope"));
2342                    }
2343                }
2344            }
2345        }
2346        // Only parent, framework, child, and void are valid.
2347        match event_stream.source {
2348            Some(
2349                fdecl::Ref::Parent(_)
2350                | fdecl::Ref::Framework(_)
2351                | fdecl::Ref::Child(_)
2352                | fdecl::Ref::VoidType(_),
2353            ) => {}
2354            Some(_) => {
2355                self.errors.push(Error::invalid_field(decl, "source"));
2356            }
2357            None => {
2358                self.errors.push(Error::missing_field(decl, "source"));
2359            }
2360        };
2361
2362        check_route_availability(
2363            decl,
2364            event_stream.availability.as_ref(),
2365            event_stream.source.as_ref(),
2366            event_stream.source_name.as_ref(),
2367            &mut self.errors,
2368        );
2369
2370        self.validate_offer_target(
2371            decl,
2372            AllowableIds::One,
2373            event_stream.target.as_ref(),
2374            event_stream.target_name.as_ref(),
2375            offer_type,
2376        );
2377        check_name(event_stream.target_name.as_ref(), decl, "target_name", &mut self.errors);
2378    }
2379
2380    /// Check a `ChildRef` contains a valid child that exists.
2381    fn validate_child_ref(
2382        &mut self,
2383        decl: DeclType,
2384        field_name: &str,
2385        child: &fdecl::ChildRef,
2386        offer_type: OfferType,
2387    ) -> bool {
2388        if offer_type == OfferType::Dynamic && child.collection.is_some() {
2389            return self.validate_dynamic_child_ref(decl, field_name, child);
2390        }
2391        // Ensure the name is valid, and the reference refers to a static child.
2392        //
2393        // We attempt to list all errors if possible.
2394        let mut valid = true;
2395        if !check_name(
2396            Some(&child.name),
2397            decl,
2398            &format!("{}.child.name", field_name),
2399            &mut self.errors,
2400        ) {
2401            valid = false;
2402        }
2403        if child.collection.is_some() {
2404            self.errors
2405                .push(Error::extraneous_field(decl, format!("{}.child.collection", field_name)));
2406            valid = false;
2407        }
2408        if !valid {
2409            return false;
2410        }
2411
2412        // Ensure the child exists.
2413        let name: &str = &child.name;
2414        if !self.all_children.contains_key(name) {
2415            self.errors.push(Error::invalid_child(decl, field_name, name));
2416            return false;
2417        }
2418
2419        true
2420    }
2421
2422    /// Check a `ChildRef` contains a valid dynamic child.
2423    ///
2424    /// The manifest we're validating doesn't contain dynamic children so we can't check if the dynamic
2425    /// child actually exists, but we can confirm things like the name is valid.
2426    fn validate_dynamic_child_ref(
2427        &mut self,
2428        decl: DeclType,
2429        field_name: &str,
2430        child: &fdecl::ChildRef,
2431    ) -> bool {
2432        // Ensure the name is valid.
2433        //
2434        // We attempt to list all errors if possible.
2435        let mut valid = true;
2436        if !check_dynamic_name(
2437            Some(&child.name),
2438            decl,
2439            &format!("{}.child.name", field_name),
2440            &mut self.errors,
2441        ) {
2442            valid = false;
2443        }
2444        if !check_name(
2445            child.collection.as_ref(),
2446            decl,
2447            &format!("{}.child.collection", field_name),
2448            &mut self.errors,
2449        ) {
2450            valid = false;
2451        }
2452        valid
2453    }
2454
2455    /// Check a `CollectionRef` is valid and refers to an existing collection.
2456    fn validate_collection_ref(
2457        &mut self,
2458        decl: DeclType,
2459        field_name: &str,
2460        collection: &fdecl::CollectionRef,
2461    ) -> bool {
2462        // Ensure the name is valid.
2463        if !check_name(
2464            Some(&collection.name),
2465            decl,
2466            &format!("{}.collection.name", field_name),
2467            &mut self.errors,
2468        ) {
2469            return false;
2470        }
2471
2472        // Ensure the collection exists.
2473        if !self.all_collections.contains(&collection.name as &str) {
2474            self.errors.push(Error::invalid_collection(decl, field_name, &collection.name as &str));
2475            return false;
2476        }
2477
2478        true
2479    }
2480
2481    fn validate_offer_target(
2482        &mut self,
2483        decl: DeclType,
2484        allowable_names: AllowableIds,
2485        target: Option<&'a fdecl::Ref>,
2486        target_name: Option<&'a String>,
2487        offer_type: OfferType,
2488    ) {
2489        match target {
2490            Some(fdecl::Ref::Child(c)) => {
2491                self.validate_target_child(decl, allowable_names, c, target_name, offer_type);
2492            }
2493            Some(fdecl::Ref::Collection(c)) => {
2494                self.validate_target_collection(decl, allowable_names, c, target_name);
2495            }
2496            Some(fdecl::Ref::Capability(c)) => {
2497                // Only offers to dictionary capabilities are valid.
2498                if let Some(d) = self.all_dictionaries.get(&c.name.as_str()) {
2499                    if d.source_path.is_some() {
2500                        // If `source_path` is present that means this is an offer into a
2501                        // dynamic dictionary, which is not allowed.
2502                        self.errors.push(Error::invalid_field(decl, "target"));
2503                    }
2504                } else {
2505                    self.errors.push(Error::invalid_field(decl, "target"));
2506                }
2507            }
2508            Some(_) => {
2509                self.errors.push(Error::invalid_field(decl, "target"));
2510            }
2511            None => {
2512                self.errors.push(Error::missing_field(decl, "target"));
2513            }
2514        }
2515    }
2516
2517    fn validate_target_child(
2518        &mut self,
2519        decl: DeclType,
2520        allowable_names: AllowableIds,
2521        child: &'a fdecl::ChildRef,
2522        target_name: Option<&'a String>,
2523        offer_type: OfferType,
2524    ) {
2525        if !self.validate_child_ref(decl, "target", child, offer_type) {
2526            return;
2527        }
2528        if let Some(target_name) = target_name {
2529            let names_for_target = self
2530                .target_ids
2531                .entry(TargetId::Component(
2532                    &child.name,
2533                    child.collection.as_ref().map(|s| s.as_str()),
2534                ))
2535                .or_insert(HashMap::new());
2536            if let Some(prev_state) = names_for_target.insert(target_name, allowable_names) {
2537                if prev_state == AllowableIds::One || prev_state != allowable_names {
2538                    self.errors.push(Error::duplicate_field(
2539                        decl,
2540                        "target_name",
2541                        target_name as &str,
2542                    ));
2543                }
2544            }
2545            if let Some(collection) = child.collection.as_ref() {
2546                if let Some(names_for_target) =
2547                    self.target_ids.get(&TargetId::Collection(&collection))
2548                {
2549                    if names_for_target.contains_key(&target_name.as_str()) {
2550                        // This dynamic offer conflicts with a static offer to the same collection.
2551                        self.errors.push(Error::duplicate_field(
2552                            decl,
2553                            "target_name",
2554                            target_name as &str,
2555                        ));
2556                    }
2557                }
2558            }
2559        }
2560    }
2561
2562    fn validate_target_collection(
2563        &mut self,
2564        decl: DeclType,
2565        allowable_names: AllowableIds,
2566        collection: &'a fdecl::CollectionRef,
2567        target_name: Option<&'a String>,
2568    ) {
2569        if !self.validate_collection_ref(decl, "target", &collection) {
2570            return;
2571        }
2572        if let Some(target_name) = target_name {
2573            let names_for_target = self
2574                .target_ids
2575                .entry(TargetId::Collection(&collection.name))
2576                .or_insert(HashMap::new());
2577            if let Some(prev_state) = names_for_target.insert(target_name, allowable_names) {
2578                if prev_state == AllowableIds::One || prev_state != allowable_names {
2579                    self.errors.push(Error::duplicate_field(
2580                        decl,
2581                        "target_name",
2582                        target_name as &str,
2583                    ));
2584                }
2585            }
2586        }
2587    }
2588
2589    fn validate_route_from_self(
2590        &mut self,
2591        decl: DeclType,
2592        source: Option<&fdecl::Ref>,
2593        source_name: Option<&String>,
2594        source_dictionary: Option<&String>,
2595        capability_checker: impl Fn(&Self) -> &dyn Container,
2596    ) {
2597        let (Some(fdecl::Ref::Self_(_)), Some(name)) = (source, source_name) else {
2598            return;
2599        };
2600        match source_dictionary {
2601            Some(source_dictionary) => {
2602                if let Ok(path) = cm_types::RelativePath::new(source_dictionary) {
2603                    if let Some(first_segment) = path.iter_segments().next().map(|s| s.as_str()) {
2604                        if !self.all_dictionaries.contains_key(first_segment) {
2605                            self.errors.push(Error::invalid_capability(
2606                                decl,
2607                                "source",
2608                                first_segment,
2609                            ));
2610                        }
2611                    }
2612                }
2613            }
2614            None => {
2615                if !(capability_checker)(self).contains(name) {
2616                    self.errors.push(Error::invalid_capability(decl, "source", name));
2617                }
2618            }
2619        }
2620    }
2621
2622    // The following functions can be used to convert a type-specific collection of capabilities
2623    // into [Container].
2624    fn service_checker(&self) -> &dyn Container {
2625        &self.all_services
2626    }
2627    fn protocol_checker(&self) -> &dyn Container {
2628        &self.all_protocols
2629    }
2630    fn directory_checker(&self) -> &dyn Container {
2631        &self.all_directories
2632    }
2633    fn runner_checker(&self) -> &dyn Container {
2634        &self.all_runners
2635    }
2636    fn resolver_checker(&self) -> &dyn Container {
2637        &self.all_resolvers
2638    }
2639
2640    fn dictionary_checker(&self) -> &dyn Container {
2641        &self.all_dictionaries
2642    }
2643
2644    #[cfg(fuchsia_api_level_at_least = "HEAD")]
2645    fn config_checker(&self) -> &dyn Container {
2646        &self.all_configs
2647    }
2648    fn storage_checker(&self) -> &dyn Container {
2649        &self.all_storages
2650    }
2651    fn event_stream_checker(&self) -> &dyn Container {
2652        // Components can't define their own event streams. If someone tries to route an event
2653        // stream from Self it should generate some other error. So just return `true` to bypass
2654        // the logic.
2655        struct AlwaysTrueContainer {}
2656        impl Container for AlwaysTrueContainer {
2657            fn contains(&self, _key: &str) -> bool {
2658                true
2659            }
2660        }
2661        static CONTAINER: AlwaysTrueContainer = AlwaysTrueContainer {};
2662        &CONTAINER
2663    }
2664}
2665
2666#[cfg(test)]
2667mod tests {
2668    use super::*;
2669    use cm_types::MAX_LONG_NAME_LENGTH;
2670    use test_case::test_case;
2671    use {
2672        fidl_fuchsia_component_decl as fdecl, fidl_fuchsia_data as fdata, fidl_fuchsia_io as fio,
2673    };
2674
2675    macro_rules! test_validate {
2676        (
2677            $(
2678                $test_name:ident => {
2679                    input = $input:expr,
2680                    result = $result:expr,
2681                },
2682            )+
2683        ) => {
2684            $(
2685                #[test]
2686                fn $test_name() {
2687                    validate_test($input, $result);
2688                }
2689            )+
2690        }
2691    }
2692
2693    macro_rules! test_validate_any_result {
2694        (
2695            $(
2696                $test_name:ident => {
2697                    input = $input:expr,
2698                    results = $results:expr,
2699                },
2700            )+
2701        ) => {
2702            $(
2703                #[test]
2704                fn $test_name() {
2705                    validate_test_any_result($input, $results);
2706                }
2707            )+
2708        }
2709    }
2710
2711    macro_rules! test_validate_values_data {
2712        (
2713            $(
2714                $test_name:ident => {
2715                    input = $input:expr,
2716                    result = $result:expr,
2717                },
2718            )+
2719        ) => {
2720            $(
2721                #[test]
2722                fn $test_name() {
2723                    validate_values_data_test($input, $result);
2724                }
2725            )+
2726        }
2727    }
2728
2729    macro_rules! test_validate_capabilities {
2730        (
2731            $(
2732                $test_name:ident => {
2733                    input = $input:expr,
2734                    as_builtin = $as_builtin:expr,
2735                    result = $result:expr,
2736                },
2737            )+
2738        ) => {
2739            $(
2740                #[test]
2741                fn $test_name() {
2742                    validate_capabilities_test($input, $as_builtin, $result);
2743                }
2744            )+
2745        }
2746    }
2747
2748    macro_rules! test_dependency {
2749        (
2750            $(
2751                ($test_name:ident) => {
2752                    ty = $ty:expr,
2753                    offer_decl = $offer_decl:expr,
2754                },
2755            )+
2756        ) => {
2757            $(
2758                #[test]
2759                fn $test_name() {
2760                    let mut decl = new_component_decl();
2761                    let dependencies = vec![
2762                        ("a", "b"),
2763                        ("b", "a"),
2764                    ];
2765                    let offers = dependencies.into_iter().map(|(from,to)| {
2766                        let mut offer_decl = $offer_decl;
2767                        offer_decl.source = Some(fdecl::Ref::Child(
2768                           fdecl::ChildRef { name: from.to_string(), collection: None },
2769                        ));
2770                        offer_decl.target = Some(fdecl::Ref::Child(
2771                           fdecl::ChildRef { name: to.to_string(), collection: None },
2772                        ));
2773                        $ty(offer_decl)
2774                    }).collect();
2775                    let children = ["a", "b"].iter().map(|name| {
2776                        fdecl::Child {
2777                            name: Some(name.to_string()),
2778                            url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
2779                            startup: Some(fdecl::StartupMode::Lazy),
2780                            on_terminate: None,
2781                            environment: None,
2782                            ..Default::default()
2783                        }
2784                    }).collect();
2785                    decl.offers = Some(offers);
2786                    decl.children = Some(children);
2787                    let result = Err(ErrorList::new(vec![
2788                        Error::dependency_cycle("{{child a -> child b -> child a}}")
2789                    ]));
2790                    validate_test(decl, result);
2791                }
2792            )+
2793        }
2794    }
2795
2796    macro_rules! test_weak_dependency {
2797        (
2798            $(
2799                ($test_name:ident) => {
2800                    ty = $ty:expr,
2801                    offer_decl = $offer_decl:expr,
2802                },
2803            )+
2804        ) => {
2805            $(
2806                #[test_case(fdecl::DependencyType::Weak)]
2807                fn $test_name(weak_dep: fdecl::DependencyType) {
2808                    let mut decl = new_component_decl();
2809                    let offers = vec![
2810                        {
2811                            let mut offer_decl = $offer_decl;
2812                            offer_decl.source = Some(fdecl::Ref::Child(
2813                               fdecl::ChildRef { name: "a".to_string(), collection: None },
2814                            ));
2815                            offer_decl.target = Some(fdecl::Ref::Child(
2816                               fdecl::ChildRef { name: "b".to_string(), collection: None },
2817                            ));
2818                            offer_decl.dependency_type = Some(fdecl::DependencyType::Strong);
2819                            $ty(offer_decl)
2820                        },
2821                        {
2822                            let mut offer_decl = $offer_decl;
2823                            offer_decl.source = Some(fdecl::Ref::Child(
2824                               fdecl::ChildRef { name: "b".to_string(), collection: None },
2825                            ));
2826                            offer_decl.target = Some(fdecl::Ref::Child(
2827                               fdecl::ChildRef { name: "a".to_string(), collection: None },
2828                            ));
2829                            offer_decl.dependency_type = Some(weak_dep);
2830                            $ty(offer_decl)
2831                        },
2832                    ];
2833                    let children = ["a", "b"].iter().map(|name| {
2834                        fdecl::Child {
2835                            name: Some(name.to_string()),
2836                            url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
2837                            startup: Some(fdecl::StartupMode::Lazy),
2838                            on_terminate: None,
2839                            environment: None,
2840                            ..Default::default()
2841                        }
2842                    }).collect();
2843                    decl.offers = Some(offers);
2844                    decl.children = Some(children);
2845                    let result = Ok(());
2846                    validate_test(decl, result);
2847                }
2848            )+
2849        }
2850    }
2851
2852    #[track_caller]
2853    fn validate_test(input: fdecl::Component, expected_res: Result<(), ErrorList>) {
2854        let res = validate(&input, &mut DirectedGraph::new());
2855        assert_eq!(res, expected_res);
2856    }
2857
2858    #[track_caller]
2859    fn validate_test_any_result(input: fdecl::Component, expected_res: Vec<Result<(), ErrorList>>) {
2860        let res = format!("{:?}", validate(&input, &mut DirectedGraph::new()));
2861        let expected_res_debug = format!("{:?}", expected_res);
2862
2863        let matched_exp =
2864            expected_res.into_iter().find(|expected| res == format!("{:?}", expected));
2865
2866        assert!(
2867            matched_exp.is_some(),
2868            "assertion failed: Expected one of:\n{:?}\nActual:\n{:?}",
2869            expected_res_debug,
2870            res
2871        );
2872    }
2873
2874    #[track_caller]
2875    fn validate_values_data_test(
2876        input: fdecl::ConfigValuesData,
2877        expected_res: Result<(), ErrorList>,
2878    ) {
2879        let res = validate_values_data(&input);
2880        assert_eq!(res, expected_res);
2881    }
2882
2883    #[track_caller]
2884    fn validate_capabilities_test(
2885        input: Vec<fdecl::Capability>,
2886        as_builtin: bool,
2887        expected_res: Result<(), ErrorList>,
2888    ) {
2889        let res = validate_capabilities(&input, as_builtin);
2890        assert_eq!(res, expected_res);
2891    }
2892
2893    fn new_component_decl() -> fdecl::Component {
2894        fdecl::Component {
2895            program: None,
2896            uses: None,
2897            exposes: None,
2898            offers: None,
2899            facets: None,
2900            capabilities: None,
2901            children: None,
2902            collections: None,
2903            environments: None,
2904            ..Default::default()
2905        }
2906    }
2907
2908    test_validate_any_result! {
2909        test_validate_use_disallows_nested_dirs => {
2910            input = {
2911                let mut decl = new_component_decl();
2912                decl.uses = Some(vec![
2913                    fdecl::Use::Directory(fdecl::UseDirectory {
2914                        dependency_type: Some(fdecl::DependencyType::Strong),
2915                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2916                        source_name: Some("abc".to_string()),
2917                        target_path: Some("/foo/bar".to_string()),
2918                        rights: Some(fio::Operations::CONNECT),
2919                        subdir: None,
2920                        ..Default::default()
2921                    }),
2922                    fdecl::Use::Directory(fdecl::UseDirectory {
2923                        dependency_type: Some(fdecl::DependencyType::Strong),
2924                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2925                        source_name: Some("abc".to_string()),
2926                        target_path: Some("/foo/bar/baz".to_string()),
2927                        rights: Some(fio::Operations::CONNECT),
2928                        subdir: None,
2929                        ..Default::default()
2930                    }),
2931                ]);
2932                decl
2933            },
2934            results = vec![
2935                Err(ErrorList::new(vec![
2936                    Error::invalid_path_overlap(
2937                        DeclType::UseDirectory, "/foo/bar/baz", DeclType::UseDirectory, "/foo/bar"),
2938                ])),
2939                Err(ErrorList::new(vec![
2940                    Error::invalid_path_overlap(
2941                        DeclType::UseDirectory, "/foo/bar", DeclType::UseDirectory, "/foo/bar/baz"),
2942                ])),
2943            ],
2944        },
2945        test_validate_use_disallows_nested_dirs_storage => {
2946            input = {
2947                let mut decl = new_component_decl();
2948                decl.uses = Some(vec![
2949                    fdecl::Use::Storage(fdecl::UseStorage {
2950                        source_name: Some("abc".to_string()),
2951                        target_path: Some("/foo/bar".to_string()),
2952                        ..Default::default()
2953                    }),
2954                    fdecl::Use::Storage(fdecl::UseStorage {
2955                        source_name: Some("abc".to_string()),
2956                        target_path: Some("/foo/bar/baz".to_string()),
2957                        ..Default::default()
2958                    }),
2959                ]);
2960                decl
2961            },
2962            results = vec![
2963                Err(ErrorList::new(vec![
2964                    Error::invalid_path_overlap(
2965                        DeclType::UseStorage, "/foo/bar/baz", DeclType::UseStorage, "/foo/bar"),
2966                ])),
2967                Err(ErrorList::new(vec![
2968                    Error::invalid_path_overlap(
2969                        DeclType::UseStorage, "/foo/bar", DeclType::UseStorage, "/foo/bar/baz"),
2970                ])),
2971            ],
2972        },
2973        test_validate_use_disallows_nested_dirs_directory_and_storage => {
2974            input = {
2975                let mut decl = new_component_decl();
2976                decl.uses = Some(vec![
2977                    fdecl::Use::Directory(fdecl::UseDirectory {
2978                        dependency_type: Some(fdecl::DependencyType::Strong),
2979                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2980                        source_name: Some("abc".to_string()),
2981                        target_path: Some("/foo/bar".to_string()),
2982                        rights: Some(fio::Operations::CONNECT),
2983                        subdir: None,
2984                        ..Default::default()
2985                    }),
2986                    fdecl::Use::Storage(fdecl::UseStorage {
2987                        source_name: Some("abc".to_string()),
2988                        target_path: Some("/foo/bar/baz".to_string()),
2989                        ..Default::default()
2990                    }),
2991                ]);
2992                decl
2993            },
2994            results = vec![
2995                Err(ErrorList::new(vec![
2996                    Error::invalid_path_overlap(
2997                        DeclType::UseStorage, "/foo/bar/baz", DeclType::UseDirectory, "/foo/bar"),
2998                ])),
2999                Err(ErrorList::new(vec![
3000                    Error::invalid_path_overlap(
3001                        DeclType::UseDirectory, "/foo/bar", DeclType::UseStorage, "/foo/bar/baz"),
3002                ])),
3003            ],
3004        },
3005        test_validate_use_disallows_common_prefixes_protocol => {
3006            input = {
3007                let mut decl = new_component_decl();
3008                decl.uses = Some(vec![
3009                    fdecl::Use::Directory(fdecl::UseDirectory {
3010                        dependency_type: Some(fdecl::DependencyType::Strong),
3011                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3012                        source_name: Some("abc".to_string()),
3013                        target_path: Some("/foo/bar".to_string()),
3014                        rights: Some(fio::Operations::CONNECT),
3015                        subdir: None,
3016                        ..Default::default()
3017                    }),
3018                    fdecl::Use::Protocol(fdecl::UseProtocol {
3019                        dependency_type: Some(fdecl::DependencyType::Strong),
3020                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3021                        source_name: Some("crow".to_string()),
3022                        target_path: Some("/foo/bar/fuchsia.2".to_string()),
3023                        ..Default::default()
3024                    }),
3025                ]);
3026                decl
3027            },
3028            results = vec![
3029                Err(ErrorList::new(vec![
3030                    Error::invalid_path_overlap(
3031                        DeclType::UseProtocol, "/foo/bar/fuchsia.2", DeclType::UseDirectory, "/foo/bar"),
3032                ])),
3033                Err(ErrorList::new(vec![
3034                    Error::invalid_path_overlap(
3035                        DeclType::UseDirectory, "/foo/bar", DeclType::UseProtocol, "/foo/bar/fuchsia.2"),
3036                ])),
3037            ],
3038        },
3039        test_validate_use_disallows_common_prefixes_service => {
3040            input = {
3041                let mut decl = new_component_decl();
3042                decl.uses = Some(vec![
3043                    fdecl::Use::Directory(fdecl::UseDirectory {
3044                        dependency_type: Some(fdecl::DependencyType::Strong),
3045                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3046                        source_name: Some("abc".to_string()),
3047                        target_path: Some("/foo/bar".to_string()),
3048                        rights: Some(fio::Operations::CONNECT),
3049                        subdir: None,
3050                        ..Default::default()
3051                    }),
3052                    fdecl::Use::Service(fdecl::UseService {
3053                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3054                        source_name: Some("space".to_string()),
3055                        target_path: Some("/foo/bar/baz/fuchsia.logger.Log".to_string()),
3056                        dependency_type: Some(fdecl::DependencyType::Strong),
3057                        ..Default::default()
3058                    }),
3059                ]);
3060                decl
3061            },
3062            results = vec![
3063                Err(ErrorList::new(vec![
3064                    Error::invalid_path_overlap(
3065                        DeclType::UseService, "/foo/bar/baz/fuchsia.logger.Log", DeclType::UseDirectory, "/foo/bar"),
3066                ])),
3067                Err(ErrorList::new(vec![
3068                    Error::invalid_path_overlap(
3069                        DeclType::UseDirectory, "/foo/bar", DeclType::UseService, "/foo/bar/baz/fuchsia.logger.Log"),
3070                ])),
3071            ],
3072        },
3073        test_validate_use_disallows_pkg => {
3074            input = {
3075                let mut decl = new_component_decl();
3076                decl.uses = Some(vec![
3077                    fdecl::Use::Directory(fdecl::UseDirectory {
3078                        dependency_type: Some(fdecl::DependencyType::Strong),
3079                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3080                        source_name: Some("abc".to_string()),
3081                        target_path: Some("/pkg".to_string()),
3082                        rights: Some(fio::Operations::CONNECT),
3083                        subdir: None,
3084                        ..Default::default()
3085                    }),
3086                ]);
3087                decl
3088            },
3089            results = vec![
3090                Err(ErrorList::new(vec![
3091                    Error::pkg_path_overlap(DeclType::UseDirectory, "/pkg"),
3092                ])),
3093            ],
3094        },
3095        test_validate_use_disallows_pkg_overlap => {
3096            input = {
3097                let mut decl = new_component_decl();
3098                decl.uses = Some(vec![
3099                    fdecl::Use::Directory(fdecl::UseDirectory {
3100                        dependency_type: Some(fdecl::DependencyType::Strong),
3101                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3102                        source_name: Some("abc".to_string()),
3103                        target_path: Some("/pkg/foo".to_string()),
3104                        rights: Some(fio::Operations::CONNECT),
3105                        subdir: None,
3106                        ..Default::default()
3107                    }),
3108                ]);
3109                decl
3110            },
3111            results = vec![
3112                Err(ErrorList::new(vec![
3113                    Error::pkg_path_overlap(DeclType::UseDirectory, "/pkg/foo"),
3114                ])),
3115            ],
3116        },
3117        test_validate_use_optional_config_correct => {
3118            input = {
3119                let mut decl = new_component_decl();
3120                decl.uses = Some(vec![
3121                    fdecl::Use::Config(fdecl::UseConfiguration {
3122                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3123                        source_name: Some("abc".to_string()),
3124                        target_name: Some("foo".to_string()),
3125                        availability: Some(fdecl::Availability::Optional),
3126                        type_: Some(fdecl::ConfigType {
3127                            layout: fdecl::ConfigTypeLayout::Bool,
3128                            parameters: Some(Vec::new()),
3129                            constraints: Vec::new(),
3130                        }),
3131                        ..Default::default()
3132                    }),
3133                ]);
3134                decl.config = Some(fdecl::ConfigSchema {
3135                    fields: Some(vec![fdecl::ConfigField {
3136                        key: Some("foo".into()),
3137                        type_: Some(fdecl::ConfigType {
3138                            layout: fdecl::ConfigTypeLayout::Bool,
3139                            parameters: Some(Vec::new()),
3140                            constraints: Vec::new(),
3141                        }),
3142                        mutability: None,
3143                        ..Default::default()}]),
3144                    checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3145                    value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3146                    ..Default::default()
3147                     });
3148                decl
3149            },
3150            results = vec![Ok(())],
3151        },
3152        test_validate_use_optional_config_no_config_schema => {
3153            input = {
3154                let mut decl = new_component_decl();
3155                decl.uses = Some(vec![
3156                    fdecl::Use::Config(fdecl::UseConfiguration {
3157                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3158                        source_name: Some("abc".to_string()),
3159                        target_name: Some("foo".to_string()),
3160                        availability: Some(fdecl::Availability::Optional),
3161                        type_: Some(fdecl::ConfigType {
3162                            layout: fdecl::ConfigTypeLayout::Bool,
3163                            parameters: None,
3164                            constraints: Vec::new(),
3165                        }),
3166                        ..Default::default()
3167                    }),
3168                ]);
3169                decl
3170            },
3171            results = vec![
3172                Err(ErrorList::new(vec![
3173                    Error::missing_field(DeclType::ConfigField, "config"),
3174                ])),
3175            ],
3176        },
3177        test_validate_use_optional_config_no_config_field => {
3178            input = {
3179                let mut decl = new_component_decl();
3180                decl.uses = Some(vec![
3181                    fdecl::Use::Config(fdecl::UseConfiguration {
3182                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3183                        source_name: Some("abc".to_string()),
3184                        target_name: Some("foo".to_string()),
3185                        availability: Some(fdecl::Availability::Optional),
3186                        type_: Some(fdecl::ConfigType {
3187                            layout: fdecl::ConfigTypeLayout::Bool,
3188                            parameters: None,
3189                            constraints: Vec::new(),
3190                        }),
3191                        ..Default::default()
3192                    }),
3193                ]);
3194                decl.config = Some(fdecl::ConfigSchema {
3195                    fields: Some(vec![]),
3196                    checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3197                    value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3198                    ..Default::default()
3199                     });
3200                decl
3201            },
3202            results = vec![
3203                Err(ErrorList::new(vec![
3204                    Error::missing_field(DeclType::ConfigField, "foo"),
3205                ])),
3206            ],
3207        },
3208        test_validate_use_optional_config_bad_type => {
3209            input = {
3210                let mut decl = new_component_decl();
3211                decl.uses = Some(vec![
3212                    fdecl::Use::Config(fdecl::UseConfiguration {
3213                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3214                        source_name: Some("abc".to_string()),
3215                        target_name: Some("foo".to_string()),
3216                        availability: Some(fdecl::Availability::Optional),
3217                        type_: Some(fdecl::ConfigType {
3218                            layout: fdecl::ConfigTypeLayout::Bool,
3219                            parameters: None,
3220                            constraints: Vec::new(),
3221                        }),
3222                        ..Default::default()
3223                    }),
3224                ]);
3225                decl.config = Some(fdecl::ConfigSchema {
3226                    fields: Some(vec![fdecl::ConfigField {
3227                        key: Some("foo".into()),
3228                        type_: Some(fdecl::ConfigType {
3229                            layout: fdecl::ConfigTypeLayout::Int16,
3230                            parameters: Some(Vec::new()),
3231                            constraints: Vec::new(),
3232                        }),
3233                        mutability: None,
3234                        ..Default::default()}]),
3235                    checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3236                    value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3237                    ..Default::default()
3238                     });
3239                decl
3240            },
3241            results = vec![
3242                Err(ErrorList::new(vec![
3243                    Error::invalid_field(DeclType::ConfigField, "foo"),
3244                ])),
3245            ],
3246        },
3247    }
3248
3249    #[cfg(fuchsia_api_level_at_least = "29")]
3250    test_validate_any_result! {
3251        test_validate_use_dictionary => {
3252            input = {
3253                let mut decl = new_component_decl();
3254                decl.uses = Some(vec![
3255                    fdecl::Use::Dictionary(fdecl::UseDictionary {
3256                        dependency_type: Some(fdecl::DependencyType::Strong),
3257                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3258                        source_name: Some("toolbox".to_string()),
3259                        target_path: Some("/svc".to_string()),
3260                        availability: Some(fdecl::Availability::Required),
3261                        ..Default::default()
3262                    }),
3263                ]);
3264                decl
3265            },
3266            results = vec![
3267                Ok(()),
3268            ],
3269        },
3270        test_validate_use_dictionary_invalid_name => {
3271            input = {
3272                let mut decl = new_component_decl();
3273                decl.uses = Some(vec![
3274                    fdecl::Use::Dictionary(fdecl::UseDictionary {
3275                        dependency_type: Some(fdecl::DependencyType::Strong),
3276                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3277                        source_name: Some("toolbox@".to_string()),
3278                        target_path: Some("/svc".to_string()),
3279                        availability: Some(fdecl::Availability::Required),
3280                        ..Default::default()
3281                    }),
3282                ]);
3283                decl
3284            },
3285            results = vec![
3286                Err(ErrorList::new(vec![Error::invalid_field(DeclType::UseDictionary, "source_name")])),
3287            ],
3288        },
3289        test_validate_use_dictionary_invalid_path => {
3290            input = {
3291                let mut decl = new_component_decl();
3292                decl.uses = Some(vec![
3293                    fdecl::Use::Dictionary(fdecl::UseDictionary {
3294                        dependency_type: Some(fdecl::DependencyType::Strong),
3295                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3296                        source_name: Some("toolbox".to_string()),
3297                        target_path: Some("/svc@".to_string()),
3298                        availability: Some(fdecl::Availability::Required),
3299                        ..Default::default()
3300                    }),
3301                ]);
3302                decl
3303            },
3304            results = vec![
3305                Err(ErrorList::new(vec![Error::field_invalid_segment(DeclType::UseDictionary, "target_path")])),
3306            ],
3307        },
3308        test_validate_use_dictionary_disallows_pkg_overlap => {
3309            input = {
3310                let mut decl = new_component_decl();
3311                decl.uses = Some(vec![
3312                    fdecl::Use::Dictionary(fdecl::UseDictionary {
3313                        dependency_type: Some(fdecl::DependencyType::Strong),
3314                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3315                        source_name: Some("toolbox".to_string()),
3316                        target_path: Some("/pkg/toolbox".to_string()),
3317                        availability: Some(fdecl::Availability::Required),
3318                        ..Default::default()
3319                    }),
3320                ]);
3321                decl
3322            },
3323            results = vec![
3324                Err(ErrorList::new(vec![
3325                    Error::pkg_path_overlap(DeclType::UseDictionary, "/pkg/toolbox"),
3326                ])),
3327            ],
3328        },
3329        test_validate_use_dictionary_path_overlap_directory => {
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("/foo/bar".to_string()),
3338                        availability: Some(fdecl::Availability::Required),
3339                        ..Default::default()
3340                    }),
3341                    fdecl::Use::Directory(fdecl::UseDirectory {
3342                        dependency_type: Some(fdecl::DependencyType::Strong),
3343                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3344                        source_name: Some("data".to_string()),
3345                        target_path: Some("/foo".to_string()),
3346                        rights: Some(fio::Operations::CONNECT),
3347                        subdir: None,
3348                        ..Default::default()
3349                    }),
3350                ]);
3351                decl
3352            },
3353            results = vec![
3354                Err(ErrorList::new(vec![
3355                    Error::invalid_path_overlap(
3356                        DeclType::UseDirectory,
3357                        "/foo",
3358                        DeclType::UseDictionary,
3359                        "/foo/bar",
3360                    ),
3361                ])),
3362                Err(ErrorList::new(vec![
3363                    Error::invalid_path_overlap(
3364                        DeclType::UseDictionary,
3365                        "/foo/bar",
3366                        DeclType::UseDirectory,
3367                        "/foo",
3368                    ),
3369                ])),
3370            ],
3371        },
3372        test_validate_use_dictionary_path_overlap_protocol => {
3373            input = {
3374                let mut decl = new_component_decl();
3375                decl.uses = Some(vec![
3376                    fdecl::Use::Dictionary(fdecl::UseDictionary {
3377                        dependency_type: Some(fdecl::DependencyType::Strong),
3378                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3379                        source_name: Some("toolbox".to_string()),
3380                        target_path: Some("/svc/toolbox".to_string()),
3381                        availability: Some(fdecl::Availability::Required),
3382                        ..Default::default()
3383                    }),
3384                    fdecl::Use::Protocol(fdecl::UseProtocol {
3385                        dependency_type: Some(fdecl::DependencyType::Strong),
3386                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3387                        source_name: Some("fuchsia.examples.Echo".to_string()),
3388                        target_path: Some("/svc/fuchsia.examples.Echo".to_string()),
3389                        ..Default::default()
3390                    }),
3391                ]);
3392                decl
3393            },
3394            results = vec![
3395                Ok(()),
3396            ],
3397        },
3398        test_validate_use_dictionary_path_overlap_protocol_2 => {
3399            input = {
3400                let mut decl = new_component_decl();
3401                decl.uses = Some(vec![
3402                    fdecl::Use::Dictionary(fdecl::UseDictionary {
3403                        dependency_type: Some(fdecl::DependencyType::Strong),
3404                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3405                        source_name: Some("toolbox".to_string()),
3406                        target_path: Some("/svc".to_string()),
3407                        availability: Some(fdecl::Availability::Required),
3408                        ..Default::default()
3409                    }),
3410                    fdecl::Use::Protocol(fdecl::UseProtocol {
3411                        dependency_type: Some(fdecl::DependencyType::Strong),
3412                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3413                        source_name: Some("fuchsia.examples.Echo".to_string()),
3414                        target_path: Some("/svc/fuchsia.examples.Echo".to_string()),
3415                        ..Default::default()
3416                    }),
3417                ]);
3418                decl
3419            },
3420            results = vec![
3421                Ok(()),
3422            ],
3423        },
3424        test_validate_use_dictionary_path_identical_dictionary => {
3425            input = {
3426                let mut decl = new_component_decl();
3427                decl.uses = Some(vec![
3428                    fdecl::Use::Dictionary(fdecl::UseDictionary {
3429                        dependency_type: Some(fdecl::DependencyType::Strong),
3430                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3431                        source_name: Some("toolbox-1".to_string()),
3432                        target_path: Some("/svc".to_string()),
3433                        availability: Some(fdecl::Availability::Required),
3434                        ..Default::default()
3435                    }),
3436                    fdecl::Use::Dictionary(fdecl::UseDictionary {
3437                        dependency_type: Some(fdecl::DependencyType::Strong),
3438                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3439                        source_name: Some("toolbox-2".to_string()),
3440                        target_path: Some("/svc".to_string()),
3441                        availability: Some(fdecl::Availability::Required),
3442                        ..Default::default()
3443                    }),
3444                    fdecl::Use::Protocol(fdecl::UseProtocol {
3445                        dependency_type: Some(fdecl::DependencyType::Strong),
3446                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3447                        source_name: Some("fuchsia.examples.Echo".to_string()),
3448                        target_path: Some("/svc/fuchsia.examples.Echo".to_string()),
3449                        ..Default::default()
3450                    }),
3451                ]);
3452                decl
3453            },
3454            results = vec![
3455                Ok(()),
3456            ],
3457        },
3458        test_validate_use_two_dictionaires_and_a_protocol_at_the_same_path => {
3459            input = {
3460                fdecl::Component {
3461                    program: Some(fdecl::Program {
3462                        runner: Some("realm_builder".to_string()),
3463                        info: Some(fdata::Dictionary {
3464                            entries: Some(vec![ ]),
3465                            ..Default::default()
3466                        }),
3467                        ..Default::default()
3468                    }),
3469                    uses: Some(vec![
3470                        fdecl::Use::Dictionary(fdecl::UseDictionary {
3471                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3472                            source_name: Some("my_dictionary".to_string()),
3473                            target_path: Some("/svc".to_string()),
3474                            dependency_type: Some(fdecl::DependencyType::Strong),
3475                            availability: Some(fdecl::Availability::Required),
3476                            source_dictionary: None,
3477                            ..Default::default()
3478                        }),
3479                        fdecl::Use::Dictionary(fdecl::UseDictionary {
3480                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
3481                            source_name: Some("my_dictionary2".to_string()),
3482                            target_path: Some("/svc".to_string()),
3483                            dependency_type: Some(fdecl::DependencyType::Strong),
3484                            availability: Some(fdecl::Availability::Required),
3485                            source_dictionary: None,
3486                            ..Default::default()
3487                        }),
3488                        fdecl::Use::Protocol(fdecl::UseProtocol {
3489                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
3490                            source_name: Some("fuchsia.echo2".to_string()),
3491                            target_path: Some("/svc/fuchsia.echo2".to_string()),
3492                            dependency_type: Some(fdecl::DependencyType::Strong),
3493                            availability: Some(fdecl::Availability::Required),
3494                            source_dictionary: None,
3495                            numbered_handle: None,
3496                            ..Default::default()
3497                        }),
3498                    ]),
3499                    ..Default::default()
3500                }
3501            },
3502            results = vec![
3503                Ok(()),
3504            ],
3505        },
3506    }
3507
3508    test_validate_values_data! {
3509        test_values_data_ok => {
3510            input = fdecl::ConfigValuesData {
3511                values: Some(vec![
3512                    fdecl::ConfigValueSpec {
3513                        value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(true))),
3514                        ..Default::default()
3515                    }
3516                ]),
3517                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3518                ..Default::default()
3519            },
3520            result = Ok(()),
3521        },
3522        test_values_data_no_checksum => {
3523            input = fdecl::ConfigValuesData {
3524                values: Some(vec![]),
3525                checksum: None,
3526                ..Default::default()
3527            },
3528            result = Err(ErrorList::new(vec![
3529                Error::missing_field(DeclType::ConfigValuesData, "checksum")
3530            ])),
3531        },
3532        test_values_data_unknown_checksum => {
3533            input = fdecl::ConfigValuesData {
3534                values: Some(vec![]),
3535                checksum: Some(fdecl::ConfigChecksum::unknown_variant_for_testing()),
3536                ..Default::default()
3537            },
3538            result = Err(ErrorList::new(vec![
3539                Error::invalid_field(DeclType::ConfigValuesData, "checksum")
3540            ])),
3541        },
3542        test_values_data_no_values => {
3543            input = fdecl::ConfigValuesData {
3544                values: None,
3545                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3546                ..Default::default()
3547            },
3548            result = Err(ErrorList::new(vec![
3549                Error::missing_field(DeclType::ConfigValuesData, "values")
3550            ])),
3551        },
3552        test_values_data_no_inner_value => {
3553            input = fdecl::ConfigValuesData {
3554                values: Some(vec![
3555                    fdecl::ConfigValueSpec::default()
3556                ]),
3557                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3558                ..Default::default()
3559            },
3560            result = Err(ErrorList::new(vec![
3561                Error::missing_field(DeclType::ConfigValueSpec, "value")
3562            ])),
3563        },
3564        test_values_data_unknown_inner_value => {
3565            input = fdecl::ConfigValuesData {
3566                values: Some(vec![
3567                    fdecl::ConfigValueSpec {
3568                        value: Some(fdecl::ConfigValue::unknown_variant_for_testing()),
3569                        ..Default::default()
3570                    }
3571                ]),
3572                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3573                ..Default::default()
3574            },
3575            result = Err(ErrorList::new(vec![
3576                Error::invalid_field(DeclType::ConfigValueSpec, "value")
3577            ])),
3578        },
3579        test_values_data_unknown_single_value => {
3580            input = fdecl::ConfigValuesData {
3581                values: Some(vec![
3582                    fdecl::ConfigValueSpec {
3583                        value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::unknown_variant_for_testing())),
3584                        ..Default::default()
3585                    }
3586                ]),
3587                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3588                ..Default::default()
3589            },
3590            result = Err(ErrorList::new(vec![
3591                Error::invalid_field(DeclType::ConfigValueSpec, "value")
3592            ])),
3593        },
3594        test_values_data_unknown_list_value => {
3595            input = fdecl::ConfigValuesData {
3596                values: Some(vec![
3597                    fdecl::ConfigValueSpec {
3598                        value: Some(fdecl::ConfigValue::Vector(fdecl::ConfigVectorValue::unknown_variant_for_testing())),
3599                        ..Default::default()
3600                    }
3601                ]),
3602                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3603                ..Default::default()
3604            },
3605            result = Err(ErrorList::new(vec![
3606                Error::invalid_field(DeclType::ConfigValueSpec, "value")
3607            ])),
3608        },
3609    }
3610
3611    test_validate! {
3612        // uses
3613        test_validate_uses_empty => {
3614            input = {
3615                let mut decl = new_component_decl();
3616                decl.program = Some(fdecl::Program {
3617                    runner: Some("elf".to_string()),
3618                    info: Some(fdata::Dictionary {
3619                        entries: None,
3620                        ..Default::default()
3621                    }),
3622                    ..Default::default()
3623                });
3624                decl.uses = Some(vec![
3625                    fdecl::Use::Service(fdecl::UseService {
3626                        source: None,
3627                        source_name: None,
3628                        target_path: None,
3629                        dependency_type: None,
3630                        ..Default::default()
3631                    }),
3632                    fdecl::Use::Protocol(fdecl::UseProtocol {
3633                        dependency_type: None,
3634                        source: None,
3635                        source_name: None,
3636                        target_path: None,
3637                        ..Default::default()
3638                    }),
3639                    fdecl::Use::Directory(fdecl::UseDirectory {
3640                        dependency_type: None,
3641                        source: None,
3642                        source_name: None,
3643                        target_path: None,
3644                        rights: None,
3645                        subdir: None,
3646                        ..Default::default()
3647                    }),
3648                    fdecl::Use::Storage(fdecl::UseStorage {
3649                        source_name: None,
3650                        target_path: None,
3651                        ..Default::default()
3652                    }),
3653                    fdecl::Use::EventStream(fdecl::UseEventStream {
3654                        source_name: None,
3655                        source: None,
3656                        target_path: None,
3657                        ..Default::default()
3658                    }),
3659                    fdecl::Use::Runner(fdecl::UseRunner {
3660                        source_name: None,
3661                        source: None,
3662                        ..Default::default()
3663                    }),
3664                ]);
3665                decl
3666            },
3667            result = Err(ErrorList::new(vec![
3668                Error::missing_field(DeclType::UseService, "source"),
3669                Error::missing_field(DeclType::UseService, "source_name"),
3670                Error::missing_field(DeclType::UseService, "target_path"),
3671                Error::missing_field(DeclType::UseService, "dependency_type"),
3672                Error::missing_field(DeclType::UseProtocol, "source"),
3673                Error::missing_field(DeclType::UseProtocol, "source_name"),
3674                Error::missing_field(DeclType::UseProtocol, "target_path"),
3675                Error::missing_field(DeclType::UseProtocol, "dependency_type"),
3676                Error::missing_field(DeclType::UseDirectory, "source"),
3677                Error::missing_field(DeclType::UseDirectory, "source_name"),
3678                Error::missing_field(DeclType::UseDirectory, "target_path"),
3679                Error::missing_field(DeclType::UseDirectory, "dependency_type"),
3680                Error::missing_field(DeclType::UseDirectory, "rights"),
3681                Error::missing_field(DeclType::UseStorage, "source_name"),
3682                Error::missing_field(DeclType::UseStorage, "target_path"),
3683                Error::missing_field(DeclType::UseEventStream, "source"),
3684                Error::missing_field(DeclType::UseEventStream, "source_name"),
3685                Error::missing_field(DeclType::UseEventStream, "target_path"),
3686                Error::missing_field(DeclType::UseRunner, "source"),
3687                Error::missing_field(DeclType::UseRunner, "source_name"),
3688            ])),
3689        },
3690        test_validate_missing_program_info => {
3691            input = {
3692                let mut decl = new_component_decl();
3693                decl.program = Some(fdecl::Program {
3694                    runner: Some("runner".to_string()),
3695                    info: None,
3696                    ..Default::default()
3697                });
3698                decl
3699            },
3700            result = Err(ErrorList::new(vec![
3701                Error::missing_field(DeclType::Program, "info")
3702            ])),
3703        },
3704        test_validate_uses_invalid_identifiers => {
3705            input = {
3706                let mut decl = new_component_decl();
3707                decl.uses = Some(vec![
3708                    fdecl::Use::Service(fdecl::UseService {
3709                        source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
3710                            name: "^bad".to_string(),
3711                        })),
3712                        source_name: Some("foo/".to_string()),
3713                        target_path: Some("a/foo".to_string()),
3714                        dependency_type: Some(fdecl::DependencyType::Strong),
3715                        ..Default::default()
3716                    }),
3717                    fdecl::Use::Protocol(fdecl::UseProtocol {
3718                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3719                            name: "^bad".to_string(),
3720                            collection: None,
3721                        })),
3722                        source_name: Some("foo/".to_string()),
3723                        target_path: Some("b/foo".to_string()),
3724                        dependency_type: Some(fdecl::DependencyType::Strong),
3725                        ..Default::default()
3726                    }),
3727                    fdecl::Use::Directory(fdecl::UseDirectory {
3728                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3729                            name: "^bad".to_string(),
3730                            collection: None,
3731                        })),
3732                        source_name: Some("foo/".to_string()),
3733                        target_path: Some("c".to_string()),
3734                        rights: Some(fio::Operations::CONNECT),
3735                        subdir: Some("/foo".to_string()),
3736                        dependency_type: Some(fdecl::DependencyType::Strong),
3737                        ..Default::default()
3738                    }),
3739                    fdecl::Use::Storage(fdecl::UseStorage {
3740                        source_name: Some("foo/".to_string()),
3741                        target_path: Some("d".to_string()),
3742                        ..Default::default()
3743                    }),
3744                    fdecl::Use::EventStream(fdecl::UseEventStream {
3745                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3746                            name: "^bad".to_string(),
3747                            collection: None,
3748                        })),
3749                        source_name: Some("foo/".to_string()),
3750                        target_path: Some("e".to_string()),
3751                        ..Default::default()
3752                    }),
3753                    fdecl::Use::Runner(fdecl::UseRunner {
3754                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3755                            name: "^bad".to_string(),
3756                            collection: None,
3757                        })),
3758                        source_name: Some("foo/".to_string()),
3759                        ..Default::default()
3760                    }),
3761                ]);
3762                decl
3763            },
3764            result = Err(ErrorList::new(vec![
3765                Error::invalid_field(DeclType::UseService, "source.capability.name"),
3766                Error::invalid_field(DeclType::UseService, "source_name"),
3767                Error::invalid_field(DeclType::UseService, "target_path"),
3768                Error::invalid_field(DeclType::UseProtocol, "source.child.name"),
3769                Error::invalid_field(DeclType::UseProtocol, "source_name"),
3770                Error::invalid_field(DeclType::UseProtocol, "target_path"),
3771                Error::invalid_field(DeclType::UseDirectory, "source.child.name"),
3772                Error::invalid_field(DeclType::UseDirectory, "source_name"),
3773                Error::invalid_field(DeclType::UseDirectory, "target_path"),
3774                Error::invalid_field(DeclType::UseDirectory, "subdir"),
3775                Error::invalid_field(DeclType::UseStorage, "source_name"),
3776                Error::invalid_field(DeclType::UseStorage, "target_path"),
3777                Error::invalid_field(DeclType::UseEventStream, "source.child.name"),
3778                Error::invalid_field(DeclType::UseEventStream, "source_name"),
3779                Error::invalid_field(DeclType::UseEventStream, "target_path"),
3780                Error::invalid_field(DeclType::UseRunner, "source.child.name"),
3781                Error::invalid_field(DeclType::UseRunner, "source_name"),
3782            ])),
3783        },
3784        test_validate_uses_missing_source => {
3785            input = {
3786                fdecl::Component {
3787                    uses: Some(vec![
3788                        fdecl::Use::Protocol(fdecl::UseProtocol {
3789                            dependency_type: Some(fdecl::DependencyType::Strong),
3790                            source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
3791                                name: "this-storage-doesnt-exist".to_string(),
3792                            })),
3793                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3794                            target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3795                            ..Default::default()
3796                        })
3797                    ]),
3798                    ..new_component_decl()
3799                }
3800            },
3801            result = Err(ErrorList::new(vec![
3802                Error::invalid_capability(DeclType::UseProtocol, "source", "this-storage-doesnt-exist"),
3803            ])),
3804        },
3805        test_validate_uses_invalid_child => {
3806            input = {
3807                fdecl::Component {
3808                    uses: Some(vec![
3809                        fdecl::Use::Protocol(fdecl::UseProtocol {
3810                            dependency_type: Some(fdecl::DependencyType::Strong),
3811                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3812                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3813                            target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3814                            ..Default::default()
3815                        }),
3816                        fdecl::Use::Service(fdecl::UseService {
3817                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3818                            source_name: Some("service_name".to_string()),
3819                            target_path: Some("/svc/service_name".to_string()),
3820                            dependency_type: Some(fdecl::DependencyType::Strong),
3821                            ..Default::default()
3822                        }),
3823                        fdecl::Use::Directory(fdecl::UseDirectory {
3824                            dependency_type: Some(fdecl::DependencyType::Strong),
3825                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3826                            source_name: Some("DirectoryName".to_string()),
3827                            target_path: Some("/data/DirectoryName".to_string()),
3828                            rights: Some(fio::Operations::CONNECT),
3829                            subdir: None,
3830                            ..Default::default()
3831                        }),
3832                        fdecl::Use::Runner(fdecl::UseRunner {
3833                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3834                            source_name: Some("RunnerName".to_string()),
3835                            ..Default::default()
3836                        }),
3837                    ]),
3838                    ..new_component_decl()
3839                }
3840            },
3841            result = Err(ErrorList::new(vec![
3842                Error::invalid_child(DeclType::UseProtocol, "source", "no-such-child"),
3843                Error::invalid_child(DeclType::UseService, "source", "no-such-child"),
3844                Error::invalid_child(DeclType::UseDirectory, "source", "no-such-child"),
3845                Error::invalid_child(DeclType::UseRunner, "source", "no-such-child"),
3846            ])),
3847        },
3848        test_validate_uses_invalid_capability_from_self => {
3849            input = {
3850                let mut decl = new_component_decl();
3851                decl.uses = Some(vec![
3852                    fdecl::Use::Service(fdecl::UseService {
3853                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3854                        source_name: Some("fuchsia.some.library.SomeService".into()),
3855                        target_path: Some("/svc/foo".into()),
3856                        dependency_type: Some(fdecl::DependencyType::Strong),
3857                        ..Default::default()
3858                    }),
3859                    fdecl::Use::Protocol(fdecl::UseProtocol {
3860                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3861                        source_name: Some("fuchsia.some.library.SomeProtocol".into()),
3862                        target_path: Some("/svc/bar".into()),
3863                        dependency_type: Some(fdecl::DependencyType::Strong),
3864                        ..Default::default()
3865                    }),
3866                    fdecl::Use::Directory(fdecl::UseDirectory {
3867                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3868                        source_name: Some("dir".into()),
3869                        target_path: Some("/assets".into()),
3870                        dependency_type: Some(fdecl::DependencyType::Strong),
3871                        rights: Some(fio::Operations::CONNECT),
3872                        ..Default::default()
3873                    }),
3874                    fdecl::Use::Runner(fdecl::UseRunner {
3875                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3876                        source_name: Some("source_elf".into()),
3877                        ..Default::default()
3878                    }),
3879                    fdecl::Use::Config(fdecl::UseConfiguration {
3880                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3881                        source_name: Some("source_config".into()),
3882                        target_name: Some("config".into()),
3883                        type_: Some(fdecl::ConfigType {
3884                            layout: fdecl::ConfigTypeLayout::Bool,
3885                            parameters: Some(Vec::new()),
3886                            constraints: Vec::new(),
3887                        }),
3888                        ..Default::default()
3889                    }),
3890                    fdecl::Use::Protocol(fdecl::UseProtocol {
3891                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3892                        source_name: Some("fuchsia.some.library.SomeProtocol".into()),
3893                        source_dictionary: Some("dict/inner".into()),
3894                        target_path: Some("/svc/baz".into()),
3895                        dependency_type: Some(fdecl::DependencyType::Strong),
3896                        ..Default::default()
3897                    }),
3898                ]);
3899                decl
3900            },
3901            result = Err(ErrorList::new(vec![
3902                Error::invalid_capability(
3903                    DeclType::UseService,
3904                    "source",
3905                    "fuchsia.some.library.SomeService"),
3906                Error::invalid_capability(
3907                    DeclType::UseProtocol,
3908                    "source",
3909                    "fuchsia.some.library.SomeProtocol"),
3910                Error::invalid_capability(DeclType::UseDirectory, "source", "dir"),
3911                Error::invalid_capability(DeclType::UseRunner, "source", "source_elf"),
3912                Error::invalid_capability(DeclType::UseConfiguration, "source", "source_config"),
3913                Error::invalid_capability(DeclType::UseProtocol, "source", "dict"),
3914            ])),
3915        },
3916        test_validate_use_numbered_handle => {
3917            input = {
3918                fdecl::Component {
3919                    uses: Some(vec![
3920                        // Valid
3921                        fdecl::Use::Protocol(fdecl::UseProtocol {
3922                            dependency_type: Some(fdecl::DependencyType::Strong),
3923                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
3924                            source_name: Some("fuchsia.logger.LogSink".into()),
3925                            numbered_handle: Some(0xab),
3926                            ..Default::default()
3927                        }),
3928                        // Invalid
3929                        fdecl::Use::Protocol(fdecl::UseProtocol {
3930                            dependency_type: Some(fdecl::DependencyType::Strong),
3931                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
3932                            source_name: Some("fuchsia.logger.LogSink".into()),
3933                            target_path: Some("/svc/fuchsia.logger.LogSink".into()),
3934                            numbered_handle: Some(0xab),
3935                            ..Default::default()
3936                        }),
3937                    ]),
3938                    ..new_component_decl()
3939                }
3940            },
3941            result = Err(ErrorList::new(vec![
3942                Error::extraneous_field(DeclType::UseProtocol, "numbered_handle"),
3943            ])),
3944        },
3945        test_validate_use_from_child_offer_to_child_strong_cycle => {
3946            input = {
3947                fdecl::Component {
3948                    capabilities: Some(vec![
3949                        fdecl::Capability::Service(fdecl::Service {
3950                            name: Some("a".to_string()),
3951                            source_path: Some("/a".to_string()),
3952                            ..Default::default()
3953                        })]),
3954                    uses: Some(vec![
3955                        fdecl::Use::Protocol(fdecl::UseProtocol {
3956                            dependency_type: Some(fdecl::DependencyType::Strong),
3957                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3958                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3959                            target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3960                            ..Default::default()
3961                        }),
3962                        fdecl::Use::Service(fdecl::UseService {
3963                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3964                            source_name: Some("service_name".to_string()),
3965                            target_path: Some("/svc/service_name".to_string()),
3966                            dependency_type: Some(fdecl::DependencyType::Strong),
3967                            ..Default::default()
3968                        }),
3969                        fdecl::Use::Directory(fdecl::UseDirectory {
3970                            dependency_type: Some(fdecl::DependencyType::Strong),
3971                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3972                            source_name: Some("DirectoryName".to_string()),
3973                            target_path: Some("/data/DirectoryName".to_string()),
3974                            rights: Some(fio::Operations::CONNECT),
3975                            subdir: None,
3976                            ..Default::default()
3977                        }),
3978                    ]),
3979                    offers: Some(vec![
3980                        fdecl::Offer::Service(fdecl::OfferService {
3981                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3982                            source_name: Some("a".to_string()),
3983                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
3984                            target_name: Some("a".to_string()),
3985                            ..Default::default()
3986                        })
3987                    ]),
3988                    children: Some(vec![
3989                        fdecl::Child {
3990                            name: Some("child".to_string()),
3991                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3992                            startup: Some(fdecl::StartupMode::Lazy),
3993                            on_terminate: None,
3994                            ..Default::default()
3995                        }
3996                    ]),
3997                    ..new_component_decl()
3998                }
3999            },
4000            result = Err(ErrorList::new(vec![
4001                Error::dependency_cycle("{{self -> child child -> self}}"),
4002            ])),
4003        },
4004        test_validate_use_from_child_storage_no_cycle => {
4005            input = {
4006                fdecl::Component {
4007                    capabilities: Some(vec![
4008                        fdecl::Capability::Storage(fdecl::Storage {
4009                            name: Some("cdata".to_string()),
4010                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None } )),
4011                            backing_dir: Some("minfs".to_string()),
4012                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4013                            ..Default::default()
4014                        }),
4015                        fdecl::Capability::Storage(fdecl::Storage {
4016                            name: Some("pdata".to_string()),
4017                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4018                            backing_dir: Some("minfs".to_string()),
4019                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4020                            ..Default::default()
4021                        }),
4022                    ]),
4023                    uses: Some(vec![
4024                        fdecl::Use::Protocol(fdecl::UseProtocol {
4025                            dependency_type: Some(fdecl::DependencyType::Strong),
4026                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child1".to_string(), collection: None})),
4027                            source_name: Some("a".to_string()),
4028                            target_path: Some("/svc/a".to_string()),
4029                            ..Default::default()
4030                        }),
4031                    ]),
4032                    offers: Some(vec![
4033                        fdecl::Offer::Storage(fdecl::OfferStorage {
4034                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4035                            source_name: Some("cdata".to_string()),
4036                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4037                            target_name: Some("cdata".to_string()),
4038                            ..Default::default()
4039                        }),
4040                        fdecl::Offer::Storage(fdecl::OfferStorage {
4041                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4042                            source_name: Some("pdata".to_string()),
4043                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4044                            target_name: Some("pdata".to_string()),
4045                            ..Default::default()
4046                        }),
4047                    ]),
4048                    children: Some(vec![
4049                        fdecl::Child {
4050                            name: Some("child1".to_string()),
4051                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4052                            startup: Some(fdecl::StartupMode::Lazy),
4053                            on_terminate: None,
4054                            ..Default::default()
4055                        },
4056                        fdecl::Child {
4057                            name: Some("child2".to_string()),
4058                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4059                            startup: Some(fdecl::StartupMode::Lazy),
4060                            on_terminate: None,
4061                            ..Default::default()
4062                        }
4063                    ]),
4064                    ..new_component_decl()
4065                }
4066            },
4067            result = Ok(()),
4068        },
4069        test_validate_use_from_child_storage_cycle => {
4070            input = {
4071                fdecl::Component {
4072                    capabilities: Some(vec![
4073                        fdecl::Capability::Storage(fdecl::Storage {
4074                            name: Some("data".to_string()),
4075                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4076                            backing_dir: Some("minfs".to_string()),
4077                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4078                            ..Default::default()
4079                        }),
4080                    ]),
4081                    uses: Some(vec![
4082                        fdecl::Use::Protocol(fdecl::UseProtocol {
4083                            dependency_type: Some(fdecl::DependencyType::Strong),
4084                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4085                            source_name: Some("a".to_string()),
4086                            target_path: Some("/svc/a".to_string()),
4087                            ..Default::default()
4088                        }),
4089                    ]),
4090                    offers: Some(vec![
4091                        fdecl::Offer::Storage(fdecl::OfferStorage {
4092                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4093                            source_name: Some("data".to_string()),
4094                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4095                            target_name: Some("data".to_string()),
4096                            ..Default::default()
4097                        }),
4098                    ]),
4099                    children: Some(vec![
4100                        fdecl::Child {
4101                            name: Some("child".to_string()),
4102                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4103                            startup: Some(fdecl::StartupMode::Lazy),
4104                            on_terminate: None,
4105                            ..Default::default()
4106                        },
4107                    ]),
4108                    ..new_component_decl()
4109                }
4110            },
4111            result = Err(ErrorList::new(vec![
4112                Error::dependency_cycle("{{self -> capability data -> child child -> self}}"),
4113            ])),
4114        },
4115        test_validate_storage_strong_cycle_between_children => {
4116            input = {
4117                fdecl::Component {
4118                    capabilities: Some(vec![
4119                        fdecl::Capability::Storage(fdecl::Storage {
4120                            name: Some("data".to_string()),
4121                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None } )),
4122                            backing_dir: Some("minfs".to_string()),
4123                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4124                            ..Default::default()
4125                        })
4126                    ]),
4127                    offers: Some(vec![
4128                        fdecl::Offer::Storage(fdecl::OfferStorage {
4129                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4130                            source_name: Some("data".to_string()),
4131                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4132                            target_name: Some("data".to_string()),
4133                            ..Default::default()
4134                        }),
4135                        fdecl::Offer::Service(fdecl::OfferService {
4136                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4137                            source_name: Some("a".to_string()),
4138                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4139                            target_name: Some("a".to_string()),
4140                            ..Default::default()
4141                        }),
4142                    ]),
4143                    children: Some(vec![
4144                        fdecl::Child {
4145                            name: Some("child1".to_string()),
4146                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4147                            startup: Some(fdecl::StartupMode::Lazy),
4148                            on_terminate: None,
4149                            ..Default::default()
4150                        },
4151                        fdecl::Child {
4152                            name: Some("child2".to_string()),
4153                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4154                            startup: Some(fdecl::StartupMode::Lazy),
4155                            on_terminate: None,
4156                            ..Default::default()
4157                        }
4158                    ]),
4159                    ..new_component_decl()
4160                }
4161            },
4162            result = Err(ErrorList::new(vec![
4163                Error::dependency_cycle("{{child child1 -> capability data -> child child2 -> child child1}}"),
4164            ])),
4165        },
4166        test_validate_strong_cycle_between_children_through_environment_debug => {
4167            input = {
4168                fdecl::Component {
4169                    environments: Some(vec![
4170                        fdecl::Environment {
4171                            name: Some("env".to_string()),
4172                            extends: Some(fdecl::EnvironmentExtends::Realm),
4173                            debug_capabilities: Some(vec![
4174                                fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
4175                                    source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4176                                    source_name: Some("fuchsia.foo.Bar".to_string()),
4177                                    target_name: Some("fuchsia.foo.Bar".to_string()),
4178                                    ..Default::default()
4179                                }),
4180                            ]),
4181                            ..Default::default()
4182                        },
4183                    ]),
4184                    offers: Some(vec![
4185                        fdecl::Offer::Service(fdecl::OfferService {
4186                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4187                            source_name: Some("a".to_string()),
4188                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4189                            target_name: Some("a".to_string()),
4190                            ..Default::default()
4191                        }),
4192                    ]),
4193                    children: Some(vec![
4194                        fdecl::Child {
4195                            name: Some("child1".to_string()),
4196                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4197                            startup: Some(fdecl::StartupMode::Lazy),
4198                            on_terminate: None,
4199                            ..Default::default()
4200                        },
4201                        fdecl::Child {
4202                            name: Some("child2".to_string()),
4203                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4204                            startup: Some(fdecl::StartupMode::Lazy),
4205                            environment: Some("env".to_string()),
4206                            on_terminate: None,
4207                            ..Default::default()
4208                        }
4209                    ]),
4210                    ..new_component_decl()
4211                }
4212            },
4213            result = Err(ErrorList::new(vec![
4214                Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}"),
4215            ])),
4216        },
4217        test_validate_strong_cycle_between_children_through_environment_runner => {
4218            input = {
4219                fdecl::Component {
4220                    environments: Some(vec![
4221                        fdecl::Environment {
4222                            name: Some("env".to_string()),
4223                            extends: Some(fdecl::EnvironmentExtends::Realm),
4224                            runners: Some(vec![
4225                                fdecl::RunnerRegistration {
4226                                    source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4227                                    source_name: Some("coff".to_string()),
4228                                    target_name: Some("coff".to_string()),
4229                                    ..Default::default()
4230                                }
4231                            ]),
4232                            ..Default::default()
4233                        },
4234                    ]),
4235                    offers: Some(vec![
4236                        fdecl::Offer::Service(fdecl::OfferService {
4237                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4238                            source_name: Some("a".to_string()),
4239                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4240                            target_name: Some("a".to_string()),
4241                            ..Default::default()
4242                        }),
4243                    ]),
4244                    children: Some(vec![
4245                        fdecl::Child {
4246                            name: Some("child1".to_string()),
4247                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4248                            startup: Some(fdecl::StartupMode::Lazy),
4249                            on_terminate: None,
4250                            ..Default::default()
4251                        },
4252                        fdecl::Child {
4253                            name: Some("child2".to_string()),
4254                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4255                            startup: Some(fdecl::StartupMode::Lazy),
4256                            environment: Some("env".to_string()),
4257                            on_terminate: None,
4258                            ..Default::default()
4259                        }
4260                    ]),
4261                    ..new_component_decl()
4262                }
4263            },
4264            result = Err(ErrorList::new(vec![
4265                Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}"),
4266            ])),
4267        },
4268        test_validate_strong_cycle_between_children_through_environment_resolver => {
4269            input = {
4270                fdecl::Component {
4271                    environments: Some(vec![
4272                        fdecl::Environment {
4273                            name: Some("env".to_string()),
4274                            extends: Some(fdecl::EnvironmentExtends::Realm),
4275                            resolvers: Some(vec![
4276                                fdecl::ResolverRegistration {
4277                                    resolver: Some("gopher".to_string()),
4278                                    source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4279                                    scheme: Some("gopher".to_string()),
4280                                    ..Default::default()
4281                                }
4282                            ]),
4283                            ..Default::default()
4284                        },
4285                    ]),
4286                    offers: Some(vec![
4287                        fdecl::Offer::Service(fdecl::OfferService {
4288                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4289                            source_name: Some("a".to_string()),
4290                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4291                            target_name: Some("a".to_string()),
4292                            ..Default::default()
4293                        }),
4294                    ]),
4295                    children: Some(vec![
4296                        fdecl::Child {
4297                            name: Some("child1".to_string()),
4298                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4299                            startup: Some(fdecl::StartupMode::Lazy),
4300                            on_terminate: None,
4301                            ..Default::default()
4302                        },
4303                        fdecl::Child {
4304                            name: Some("child2".to_string()),
4305                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4306                            startup: Some(fdecl::StartupMode::Lazy),
4307                            environment: Some("env".to_string()),
4308                            on_terminate: None,
4309                            ..Default::default()
4310                        }
4311                    ]),
4312                    ..new_component_decl()
4313                }
4314            },
4315            result = Err(ErrorList::new(vec![
4316                Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}"),
4317            ])),
4318        },
4319        test_validate_strong_cycle_between_self_and_two_children => {
4320            input = {
4321                fdecl::Component {
4322                    capabilities: Some(vec![
4323                        fdecl::Capability::Protocol(fdecl::Protocol {
4324                            name: Some("fuchsia.foo.Bar".to_string()),
4325                            source_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4326                            ..Default::default()
4327                        })
4328                    ]),
4329                    offers: Some(vec![
4330                        fdecl::Offer::Protocol(fdecl::OfferProtocol {
4331                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4332                            source_name: Some("fuchsia.foo.Bar".to_string()),
4333                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4334                            target_name: Some("fuchsia.foo.Bar".to_string()),
4335                            dependency_type: Some(fdecl::DependencyType::Strong),
4336                            ..Default::default()
4337                        }),
4338                        fdecl::Offer::Protocol(fdecl::OfferProtocol {
4339                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4340                            source_name: Some("fuchsia.bar.Baz".to_string()),
4341                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4342                            target_name: Some("fuchsia.bar.Baz".to_string()),
4343                            dependency_type: Some(fdecl::DependencyType::Strong),
4344                            ..Default::default()
4345                        }),
4346                    ]),
4347                    uses: Some(vec![
4348                        fdecl::Use::Protocol(fdecl::UseProtocol {
4349                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child2".to_string(), collection: None})),
4350                            source_name: Some("fuchsia.baz.Foo".to_string()),
4351                            target_path: Some("/svc/fuchsia.baz.Foo".to_string()),
4352                            dependency_type: Some(fdecl::DependencyType::Strong),
4353                            ..Default::default()
4354                        }),
4355                    ]),
4356                    children: Some(vec![
4357                        fdecl::Child {
4358                            name: Some("child1".to_string()),
4359                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4360                            startup: Some(fdecl::StartupMode::Lazy),
4361                            on_terminate: None,
4362                            ..Default::default()
4363                        },
4364                        fdecl::Child {
4365                            name: Some("child2".to_string()),
4366                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4367                            startup: Some(fdecl::StartupMode::Lazy),
4368                            on_terminate: None,
4369                            ..Default::default()
4370                        }
4371                    ]),
4372                    ..new_component_decl()
4373                }
4374            },
4375            result = Err(ErrorList::new(vec![
4376                Error::dependency_cycle("{{self -> child child1 -> child child2 -> self}}"),
4377            ])),
4378        },
4379        test_validate_strong_cycle_with_self_storage => {
4380            input = {
4381                fdecl::Component {
4382                    capabilities: Some(vec![
4383                        fdecl::Capability::Storage(fdecl::Storage {
4384                            name: Some("data".to_string()),
4385                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4386                            backing_dir: Some("minfs".to_string()),
4387                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4388                            ..Default::default()
4389                        }),
4390                        fdecl::Capability::Directory(fdecl::Directory {
4391                            name: Some("minfs".to_string()),
4392                            source_path: Some("/minfs".to_string()),
4393                            rights: Some(fio::RW_STAR_DIR),
4394                            ..Default::default()
4395                        }),
4396                    ]),
4397                    offers: Some(vec![
4398                        fdecl::Offer::Storage(fdecl::OfferStorage {
4399                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4400                            source_name: Some("data".to_string()),
4401                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4402                            target_name: Some("data".to_string()),
4403                            ..Default::default()
4404                        }),
4405                    ]),
4406                    uses: Some(vec![
4407                        fdecl::Use::Protocol(fdecl::UseProtocol {
4408                            dependency_type: Some(fdecl::DependencyType::Strong),
4409                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4410                            source_name: Some("fuchsia.foo.Bar".to_string()),
4411                            target_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4412                            ..Default::default()
4413                        }),
4414                    ]),
4415                    children: Some(vec![
4416                        fdecl::Child {
4417                            name: Some("child".to_string()),
4418                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4419                            startup: Some(fdecl::StartupMode::Lazy),
4420                            ..Default::default()
4421                        },
4422                    ]),
4423                    ..new_component_decl()
4424                }
4425            },
4426            result = Err(ErrorList::new(vec![
4427                Error::dependency_cycle("{{self -> capability data -> child child -> self}}"),
4428            ])),
4429        },
4430        test_validate_strong_cycle_with_self_storage_admin_protocol => {
4431            input = {
4432                fdecl::Component {
4433                    capabilities: Some(vec![
4434                        fdecl::Capability::Storage(fdecl::Storage {
4435                            name: Some("data".to_string()),
4436                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4437                            backing_dir: Some("minfs".to_string()),
4438                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4439                            ..Default::default()
4440                        }),
4441                        fdecl::Capability::Directory(fdecl::Directory {
4442                            name: Some("minfs".to_string()),
4443                            source_path: Some("/minfs".to_string()),
4444                            rights: Some(fio::RW_STAR_DIR),
4445                            ..Default::default()
4446                        }),
4447                    ]),
4448                    offers: Some(vec![
4449                        fdecl::Offer::Protocol(fdecl::OfferProtocol {
4450                            source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef { name: "data".to_string() })),
4451                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4452                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4453                            target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4454                            dependency_type: Some(fdecl::DependencyType::Strong),
4455                            ..Default::default()
4456                        }),
4457                    ]),
4458                    uses: Some(vec![
4459                        fdecl::Use::Protocol(fdecl::UseProtocol {
4460                            dependency_type: Some(fdecl::DependencyType::Strong),
4461                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4462                            source_name: Some("fuchsia.foo.Bar".to_string()),
4463                            target_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4464                            ..Default::default()
4465                        }),
4466                    ]),
4467                    children: Some(vec![
4468                        fdecl::Child {
4469                            name: Some("child".to_string()),
4470                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4471                            startup: Some(fdecl::StartupMode::Lazy),
4472                            ..Default::default()
4473                        },
4474                    ]),
4475                    ..new_component_decl()
4476                }
4477            },
4478            result = Err(ErrorList::new(vec![
4479                Error::dependency_cycle("{{self -> capability data -> child child -> self}}"),
4480            ])),
4481        },
4482        test_validate_strong_cycle_with_dictionary => {
4483            input = fdecl::Component {
4484                offers: Some(vec![
4485                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
4486                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4487                        source_name: Some("dict".into()),
4488                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4489                            name: "a".into(),
4490                            collection: None,
4491                        })),
4492                        target_name: Some("dict".into()),
4493                        dependency_type: Some(fdecl::DependencyType::Strong),
4494                        ..Default::default()
4495                    }),
4496                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4497                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4498                            name: "b".into(),
4499                            collection: None,
4500                        })),
4501                        source_name: Some("1".into()),
4502                        target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4503                            name: "dict".into(),
4504                        })),
4505                        target_name: Some("1".into()),
4506                        dependency_type: Some(fdecl::DependencyType::Strong),
4507                        ..Default::default()
4508                    }),
4509                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4510                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4511                            name: "a".into(),
4512                            collection: None,
4513                        })),
4514                        source_name: Some("2".into()),
4515                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4516                            name: "b".into(),
4517                            collection: None,
4518                        })),
4519                        target_name: Some("2".into()),
4520                        dependency_type: Some(fdecl::DependencyType::Strong),
4521                        ..Default::default()
4522                    }),
4523                ]),
4524                children: Some(vec![
4525                    fdecl::Child {
4526                        name: Some("a".into()),
4527                        url: Some("fuchsia-pkg://child".into()),
4528                        startup: Some(fdecl::StartupMode::Lazy),
4529                        ..Default::default()
4530                    },
4531                    fdecl::Child {
4532                        name: Some("b".into()),
4533                        url: Some("fuchsia-pkg://child".into()),
4534                        startup: Some(fdecl::StartupMode::Lazy),
4535                        ..Default::default()
4536                    },
4537                ]),
4538                capabilities: Some(vec![
4539                    fdecl::Capability::Dictionary(fdecl::Dictionary {
4540                        name: Some("dict".into()),
4541                        ..Default::default()
4542                    }),
4543                ]),
4544                ..Default::default()
4545            },
4546            result = Err(ErrorList::new(vec![
4547                Error::dependency_cycle("{{child a -> child b -> capability dict -> child a}}"),
4548            ])),
4549        },
4550        test_validate_strong_cycle_with_dictionary_indirect => {
4551            input = fdecl::Component {
4552                offers: Some(vec![
4553                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4554                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4555                        source_name: Some("3".into()),
4556                        source_dictionary: Some("dict".into()),
4557                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4558                            name: "a".into(),
4559                            collection: None,
4560                        })),
4561                        target_name: Some("3".into()),
4562                        dependency_type: Some(fdecl::DependencyType::Strong),
4563                        ..Default::default()
4564                    }),
4565                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4566                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4567                            name: "b".into(),
4568                            collection: None,
4569                        })),
4570                        source_name: Some("1".into()),
4571                        target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4572                            name: "dict".into(),
4573                        })),
4574                        target_name: Some("1".into()),
4575                        dependency_type: Some(fdecl::DependencyType::Strong),
4576                        ..Default::default()
4577                    }),
4578                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4579                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4580                            name: "a".into(),
4581                            collection: None,
4582                        })),
4583                        source_name: Some("2".into()),
4584                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4585                            name: "b".into(),
4586                            collection: None,
4587                        })),
4588                        target_name: Some("2".into()),
4589                        dependency_type: Some(fdecl::DependencyType::Strong),
4590                        ..Default::default()
4591                    }),
4592                ]),
4593                children: Some(vec![
4594                    fdecl::Child {
4595                        name: Some("a".into()),
4596                        url: Some("fuchsia-pkg://child".into()),
4597                        startup: Some(fdecl::StartupMode::Lazy),
4598                        ..Default::default()
4599                    },
4600                    fdecl::Child {
4601                        name: Some("b".into()),
4602                        url: Some("fuchsia-pkg://child".into()),
4603                        startup: Some(fdecl::StartupMode::Lazy),
4604                        ..Default::default()
4605                    },
4606                ]),
4607                capabilities: Some(vec![
4608                    fdecl::Capability::Dictionary(fdecl::Dictionary {
4609                        name: Some("dict".into()),
4610                        ..Default::default()
4611                    }),
4612                ]),
4613                ..Default::default()
4614            },
4615            result = Err(ErrorList::new(vec![
4616                Error::dependency_cycle("{{child a -> child b -> capability dict -> child a}}"),
4617            ])),
4618        },
4619        test_validate_use_dependency_cycle_with_dictionary => {
4620            input = fdecl::Component {
4621                offers: Some(vec![
4622                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
4623                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4624                        source_name: Some("dict".into()),
4625                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4626                            name: "a".into(),
4627                            collection: None,
4628                        })),
4629                        target_name: Some("dict".into()),
4630                        dependency_type: Some(fdecl::DependencyType::Strong),
4631                        ..Default::default()
4632                    }),
4633                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4634                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4635                        source_name: Some("1".into()),
4636                        target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4637                            name: "dict".into(),
4638                        })),
4639                        target_name: Some("1".into()),
4640                        dependency_type: Some(fdecl::DependencyType::Strong),
4641                        ..Default::default()
4642                    }),
4643                ]),
4644                children: Some(vec![
4645                    fdecl::Child {
4646                        name: Some("a".into()),
4647                        url: Some("fuchsia-pkg://child".into()),
4648                        startup: Some(fdecl::StartupMode::Lazy),
4649                        ..Default::default()
4650                    },
4651                ]),
4652                uses: Some(vec![
4653                        fdecl::Use::Protocol(fdecl::UseProtocol {
4654                            dependency_type: Some(fdecl::DependencyType::Strong),
4655                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "a".to_string(), collection: None})),
4656                            source_name: Some("2".to_string()),
4657                            target_path: Some("/svc/foo".into()),
4658                            ..Default::default()
4659                        }),
4660                ]),
4661                capabilities: Some(vec![
4662                    fdecl::Capability::Dictionary(fdecl::Dictionary {
4663                        name: Some("dict".into()),
4664                        ..Default::default()
4665                    }),
4666                    fdecl::Capability::Protocol(fdecl::Protocol {
4667                        name: Some("1".to_string()),
4668                        source_path: Some("/path".to_string()),
4669                        ..Default::default()
4670                    }),
4671                ]),
4672                ..Default::default()
4673            },
4674            result = Err(ErrorList::new(vec![
4675                Error::dependency_cycle("{{self -> capability dict -> child a -> self}}"),
4676            ])),
4677        },
4678        test_validate_use_from_child_offer_to_child_weak_cycle => {
4679            input = {
4680                fdecl::Component {
4681                    capabilities: Some(vec![
4682                        fdecl::Capability::Service(fdecl::Service {
4683                            name: Some("a".to_string()),
4684                            source_path: Some("/a".to_string()),
4685                            ..Default::default()
4686                        })]),
4687                    uses: Some(vec![
4688                        fdecl::Use::Protocol(fdecl::UseProtocol {
4689                            dependency_type: Some(fdecl::DependencyType::Weak),
4690                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4691                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4692                            target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
4693                            ..Default::default()
4694                        }),
4695                        fdecl::Use::Service(fdecl::UseService {
4696                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4697                            source_name: Some("service_name".to_string()),
4698                            target_path: Some("/svc/service_name".to_string()),
4699                            dependency_type: Some(fdecl::DependencyType::Weak),
4700                            ..Default::default()
4701                        }),
4702                        fdecl::Use::Directory(fdecl::UseDirectory {
4703                            dependency_type: Some(fdecl::DependencyType::Weak),
4704                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4705                            source_name: Some("DirectoryName".to_string()),
4706                            target_path: Some("/data/DirectoryName".to_string()),
4707                            rights: Some(fio::Operations::CONNECT),
4708                            subdir: None,
4709                            ..Default::default()
4710                        }),
4711                    ]),
4712                    offers: Some(vec![
4713                        fdecl::Offer::Service(fdecl::OfferService {
4714                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4715                            source_name: Some("a".to_string()),
4716                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4717                            target_name: Some("a".to_string()),
4718                            ..Default::default()
4719                        })
4720                    ]),
4721                    children: Some(vec![
4722                        fdecl::Child {
4723                            name: Some("child".to_string()),
4724                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4725                            startup: Some(fdecl::StartupMode::Lazy),
4726                            on_terminate: None,
4727                            ..Default::default()
4728                        }
4729                    ]),
4730                    ..new_component_decl()
4731                }
4732            },
4733            result = Ok(()),
4734        },
4735        test_validate_expose_from_self_to_framework_and_parent => {
4736            input = {
4737                fdecl::Component {
4738                    capabilities: Some(vec![
4739                        fdecl::Capability::Protocol(fdecl::Protocol {
4740                            name: Some("a".to_string()),
4741                            source_path: Some("/a".to_string()),
4742                            ..Default::default()
4743                        }),
4744                    ]),
4745                    exposes: Some(vec![
4746                        fdecl::Expose::Protocol(fdecl::ExposeProtocol {
4747                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4748                            source_name: Some("a".to_string()),
4749                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4750                            target_name: Some("a".to_string()),
4751                            ..Default::default()
4752                        }),
4753                        fdecl::Expose::Protocol(fdecl::ExposeProtocol {
4754                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4755                            source_name: Some("a".to_string()),
4756                            target: Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})),
4757                            target_name: Some("a".to_string()),
4758                            ..Default::default()
4759                        }),
4760                    ]),
4761                    ..new_component_decl()
4762                }
4763            },
4764            result = Ok(()),
4765        },
4766        test_validate_use_from_not_child_weak => {
4767            input = {
4768                fdecl::Component {
4769                    uses: Some(vec![
4770                        fdecl::Use::Protocol(fdecl::UseProtocol {
4771                            dependency_type: Some(fdecl::DependencyType::Weak),
4772                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4773                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4774                            target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
4775                            ..Default::default()
4776                        }),
4777                    ]),
4778                    ..new_component_decl()
4779                }
4780            },
4781            result = Err(ErrorList::new(vec![
4782                Error::invalid_field(DeclType::UseProtocol, "dependency_type"),
4783            ])),
4784        },
4785        test_validate_event_stream_offer_valid_decls => {
4786            input = {
4787                let mut decl = new_component_decl();
4788                decl.offers = Some(vec![
4789                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4790                        source_name: Some("stopped".to_string()),
4791                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4792                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4793                        target_name: Some("stopped".to_string()),
4794                        ..Default::default()
4795                    }),
4796                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4797                        source_name: Some("started".to_string()),
4798                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4799                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4800                        target_name: Some("started".to_string()),
4801                        ..Default::default()
4802                    }),
4803                ]);
4804                decl.children = Some(vec![fdecl::Child{
4805                    name: Some("test".to_string()),
4806                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4807                    startup: Some(fdecl::StartupMode::Lazy),
4808                    on_terminate: None,
4809                    environment: None,
4810                    ..Default::default()
4811                },
4812                fdecl::Child{
4813                    name: Some("test2".to_string()),
4814                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4815                    startup: Some(fdecl::StartupMode::Lazy),
4816                    on_terminate: None,
4817                    environment: None,
4818                    ..Default::default()
4819                }
4820                ]);
4821                decl
4822            },
4823            result = Ok(()),
4824        },
4825        test_validate_event_stream_offer_to_framework_invalid => {
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::Framework(fdecl::FrameworkRef{})),
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 = Err(ErrorList::new(vec![
4864                Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "target".to_string() }),
4865                Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "target".to_string() }),
4866            ])),
4867        },
4868        test_validate_event_stream_offer_to_scope_zero_length_invalid => {
4869            input = {
4870                let mut decl = new_component_decl();
4871                decl.offers = Some(vec![
4872                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4873                        source_name: Some("started".to_string()),
4874                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4875                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4876                        scope: Some(vec![]),
4877                        target_name: Some("started".to_string()),
4878                        ..Default::default()
4879                    }),
4880                ]);
4881                decl.children = Some(vec![fdecl::Child{
4882                    name: Some("test".to_string()),
4883                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4884                    startup: Some(fdecl::StartupMode::Lazy),
4885                    on_terminate: None,
4886                    environment: None,
4887                    ..Default::default()
4888                },
4889                fdecl::Child{
4890                    name: Some("test2".to_string()),
4891                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4892                    startup: Some(fdecl::StartupMode::Lazy),
4893                    on_terminate: None,
4894                    environment: None,
4895                    ..Default::default()
4896                }
4897                ]);
4898                decl
4899            },
4900            result = Err(ErrorList::new(vec![
4901                Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "scope".to_string() }),
4902            ])),
4903        },
4904        test_validate_event_stream_offer_to_scope_framework_invalid => {
4905            input = {
4906                let mut decl = new_component_decl();
4907                decl.offers = Some(vec![
4908                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4909                        source_name: Some("started".to_string()),
4910                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4911                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4912                        scope: Some(vec![fdecl::Ref::Framework(fdecl::FrameworkRef{})]),
4913                        target_name: Some("started".to_string()),
4914                        ..Default::default()
4915                    }),
4916                ]);
4917                decl.children = Some(vec![fdecl::Child{
4918                    name: Some("test".to_string()),
4919                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4920                    startup: Some(fdecl::StartupMode::Lazy),
4921                    on_terminate: None,
4922                    environment: None,
4923                    ..Default::default()
4924                },
4925                fdecl::Child{
4926                    name: Some("test2".to_string()),
4927                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4928                    startup: Some(fdecl::StartupMode::Lazy),
4929                    on_terminate: None,
4930                    environment: None,
4931                    ..Default::default()
4932                }
4933                ]);
4934                decl
4935            },
4936            result = Err(ErrorList::new(vec![
4937                Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "scope".to_string() }),
4938            ])),
4939        },
4940        test_validate_event_stream_offer_to_scope_valid => {
4941            input = {
4942                let mut decl = new_component_decl();
4943                decl.offers = Some(vec![
4944                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4945                        source_name: Some("started".to_string()),
4946                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4947                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4948                        scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
4949                        target_name: Some("started".to_string()),
4950                        ..Default::default()
4951                    }),
4952                ]);
4953                decl.children = Some(vec![fdecl::Child{
4954                    name: Some("test".to_string()),
4955                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4956                    startup: Some(fdecl::StartupMode::Lazy),
4957                    on_terminate: None,
4958                    environment: None,
4959                    ..Default::default()
4960                },
4961                fdecl::Child{
4962                    name: Some("test2".to_string()),
4963                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4964                    startup: Some(fdecl::StartupMode::Lazy),
4965                    on_terminate: None,
4966                    environment: None,
4967                    ..Default::default()
4968                }
4969                ]);
4970                decl
4971            },
4972            result = Ok(()),
4973        },
4974        test_validate_event_stream_offer_to_scope_with_capability_requested => {
4975            input = {
4976                let mut decl = new_component_decl();
4977                decl.offers = Some(vec![
4978                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4979                        source_name: Some("capability_requested".to_string()),
4980                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4981                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4982                        scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
4983                        target_name: Some("started".to_string()),
4984                        ..Default::default()
4985                    }),
4986                ]);
4987                decl.children = Some(vec![fdecl::Child{
4988                    name: Some("test".to_string()),
4989                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4990                    startup: Some(fdecl::StartupMode::Lazy),
4991                    on_terminate: None,
4992                    environment: None,
4993                    ..Default::default()
4994                },
4995                fdecl::Child{
4996                    name: Some("test2".to_string()),
4997                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4998                    startup: Some(fdecl::StartupMode::Lazy),
4999                    on_terminate: None,
5000                    environment: None,
5001                    ..Default::default()
5002                }
5003                ]);
5004                decl
5005            },
5006            result = Ok(()),
5007        },
5008        test_validate_event_stream_offer_with_no_source_name_invalid => {
5009            input = {
5010                let mut decl = new_component_decl();
5011                decl.offers = Some(vec![
5012                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
5013                        source_name: None,
5014                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5015                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5016                        scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
5017                        target_name: Some("started".to_string()),
5018                        ..Default::default()
5019                    }),
5020                ]);
5021                decl.children = Some(vec![fdecl::Child{
5022                    name: Some("test".to_string()),
5023                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5024                    startup: Some(fdecl::StartupMode::Lazy),
5025                    on_terminate: None,
5026                    environment: None,
5027                    ..Default::default()
5028                },
5029                fdecl::Child{
5030                    name: Some("test2".to_string()),
5031                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
5032                    startup: Some(fdecl::StartupMode::Lazy),
5033                    on_terminate: None,
5034                    environment: None,
5035                    ..Default::default()
5036                }
5037                ]);
5038                decl
5039            },
5040            result = Err(ErrorList::new(vec![
5041                Error::MissingField(DeclField { decl: DeclType::OfferEventStream, field: "source_name".to_string() }),
5042            ])),
5043        },
5044        test_validate_event_stream_offer_invalid_source => {
5045            input = {
5046                let mut decl = new_component_decl();
5047                decl.offers = Some(vec![
5048                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
5049                        source_name: Some("stopped".to_string()),
5050                        source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
5051                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5052                        target_name: Some("stopped".to_string()),
5053                        ..Default::default()
5054                    }),
5055                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
5056                        source_name: Some("started".to_string()),
5057                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5058                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5059                        target_name: Some("started".to_string()),
5060                        ..Default::default()
5061                    }),
5062                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
5063                        source_name: Some("capability_requested".to_string()),
5064                        source: Some(fdecl::Ref::Debug(fdecl::DebugRef{})),
5065                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5066                        target_name: Some("capability_requested".to_string()),
5067                        ..Default::default()
5068                    }),
5069                ]);
5070                decl.children = Some(vec![fdecl::Child{
5071                    name: Some("test".to_string()),
5072                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5073                    startup: Some(fdecl::StartupMode::Lazy),
5074                    on_terminate: None,
5075                    environment: None,
5076                    ..Default::default()
5077                },
5078                fdecl::Child{
5079                    name: Some("test2".to_string()),
5080                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
5081                    startup: Some(fdecl::StartupMode::Lazy),
5082                    on_terminate: None,
5083                    environment: None,
5084                    ..Default::default()
5085                }
5086                ]);
5087                decl
5088            },
5089            result = Err(ErrorList::new(vec![
5090                Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "source".to_string() }),
5091            ])),
5092        },
5093
5094        test_validate_event_stream_offer_missing_source => {
5095            input = {
5096                let mut decl = new_component_decl();
5097                decl.offers = Some(vec![
5098                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
5099                        source_name: Some("stopped".to_string()),
5100                        source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
5101                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5102                        target_name: Some("stopped".to_string()),
5103                        ..Default::default()
5104                    }),
5105                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
5106                        source_name: Some("started".to_string()),
5107                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5108                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5109                        target_name: Some("started".to_string()),
5110                        ..Default::default()
5111                    }),
5112                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
5113                        source_name: Some("capability_requested".to_string()),
5114                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
5115                        target_name: Some("capability_requested".to_string()),
5116                        ..Default::default()
5117                    }),
5118                ]);
5119                decl.children = Some(vec![fdecl::Child{
5120                    name: Some("test".to_string()),
5121                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5122                    startup: Some(fdecl::StartupMode::Lazy),
5123                    on_terminate: None,
5124                    environment: None,
5125                    ..Default::default()
5126                },
5127                fdecl::Child{
5128                    name: Some("test2".to_string()),
5129                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
5130                    startup: Some(fdecl::StartupMode::Lazy),
5131                    on_terminate: None,
5132                    environment: None,
5133                    ..Default::default()
5134                }
5135                ]);
5136                decl
5137            },
5138            result = Err(ErrorList::new(vec![
5139                Error::MissingField(DeclField { decl: DeclType::OfferEventStream, field: "source".to_string() }),
5140            ])),
5141        },
5142        test_validate_event_stream_must_have_target_path => {
5143            input = {
5144                let mut decl = new_component_decl();
5145                decl.uses = Some(vec![
5146                    fdecl::Use::EventStream(fdecl::UseEventStream {
5147                        source_name: Some("bar".to_string()),
5148                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5149                        ..Default::default()
5150                    }),
5151                ]);
5152                decl
5153            },
5154            result = Err(ErrorList::new(vec![
5155                Error::MissingField(DeclField { decl: DeclType::UseEventStream, field: "target_path".to_string() })
5156            ])),
5157        },
5158        test_validate_event_stream_must_have_source_names => {
5159            input = {
5160                let mut decl = new_component_decl();
5161                decl.uses = Some(vec![
5162                    fdecl::Use::EventStream(fdecl::UseEventStream {
5163                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5164                        target_path: Some("/svc/something".to_string()),
5165                        ..Default::default()
5166                    }),
5167                ]);
5168                decl
5169            },
5170            result = Err(ErrorList::new(vec![
5171                Error::MissingField(DeclField { decl: DeclType::UseEventStream, field: "source_name".to_string() })
5172            ])),
5173        },
5174        test_validate_event_stream_scope_must_be_child_or_collection => {
5175            input = {
5176                let mut decl = new_component_decl();
5177                decl.uses = Some(vec![
5178                    fdecl::Use::EventStream(fdecl::UseEventStream {
5179                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5180                        target_path: Some("/svc/something".to_string()),
5181                        source_name: Some("some_source".to_string()),
5182                        scope: Some(vec![fdecl::Ref::Framework(fdecl::FrameworkRef{})]),
5183                        ..Default::default()
5184                    }),
5185                ]);
5186                decl
5187            },
5188            result = Err(ErrorList::new(vec![
5189                Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "scope".to_string() })
5190            ])),
5191        },
5192        test_validate_event_stream_source_must_be_parent_or_child => {
5193            input = {
5194                let mut decl = new_component_decl();
5195                decl.uses = Some(vec![
5196                    fdecl::Use::EventStream(fdecl::UseEventStream {
5197                        source: Some(fdecl::Ref::Debug(fdecl::DebugRef{})),
5198                        target_path: Some("/svc/something".to_string()),
5199                        source_name: Some("some_source".to_string()),
5200                        scope: Some(vec![]),
5201                        ..Default::default()
5202                    }),
5203                    fdecl::Use::EventStream(fdecl::UseEventStream {
5204                        source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
5205                        target_path: Some("/svc/something_else".to_string()),
5206                        source_name: Some("some_source".to_string()),
5207                        scope: Some(vec![]),
5208                        ..Default::default()
5209                    }),
5210                    fdecl::Use::EventStream(fdecl::UseEventStream {
5211                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5212                        target_path: Some("/svc/yet_something_else".to_string()),
5213                        source_name: Some("some_source".to_string()),
5214                        scope: Some(vec![]),
5215                        ..Default::default()
5216                    }),
5217                ]);
5218                decl
5219            },
5220            result = Err(ErrorList::new(vec![
5221                Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() }),
5222                Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() }),
5223                Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() })
5224            ])),
5225        },
5226        test_validate_no_runner => {
5227            input = {
5228                let mut decl = new_component_decl();
5229                decl.program = Some(fdecl::Program {
5230                    runner: None,
5231                    info: Some(fdata::Dictionary {
5232                        entries: None,
5233                        ..Default::default()
5234                    }),
5235                    ..Default::default()
5236                });
5237                decl
5238            },
5239            result = Err(ErrorList::new(vec![
5240                Error::MissingRunner,
5241            ])),
5242        },
5243        test_validate_uses_runner => {
5244            input = {
5245                let mut decl = new_component_decl();
5246                decl.program = Some(fdecl::Program {
5247                    runner: None,
5248                    info: Some(fdata::Dictionary {
5249                        entries: None,
5250                        ..Default::default()
5251                    }),
5252                    ..Default::default()
5253                });
5254                decl.uses = Some(vec![
5255                    fdecl::Use::Runner(fdecl::UseRunner {
5256                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5257                        source_name: Some("runner".to_string()),
5258                        ..Default::default()
5259                    }),
5260                ]);
5261                decl
5262            },
5263            result = Ok(()),
5264        },
5265        test_validate_program_and_uses_runner_match => {
5266            input = {
5267                let mut decl = new_component_decl();
5268                decl.program = Some(fdecl::Program {
5269                    runner: Some("runner".to_string()),
5270                    info: Some(fdata::Dictionary {
5271                        entries: None,
5272                        ..Default::default()
5273                    }),
5274                    ..Default::default()
5275                });
5276                decl.uses = Some(vec![
5277                    fdecl::Use::Runner(fdecl::UseRunner {
5278                        source: Some(fdecl::Ref::Environment(fdecl::EnvironmentRef {})),
5279                        source_name: Some("runner".to_string()),
5280                        ..Default::default()
5281                    }),
5282                ]);
5283                decl
5284            },
5285            result = Ok(()),
5286        },
5287        test_validate_runner_names_conflict => {
5288            input = {
5289                let mut decl = new_component_decl();
5290                decl.program = Some(fdecl::Program {
5291                    runner: Some("runner".to_string()),
5292                    info: Some(fdata::Dictionary {
5293                        entries: None,
5294                        ..Default::default()
5295                    }),
5296                    ..Default::default()
5297                });
5298                decl.uses = Some(vec![
5299                    fdecl::Use::Runner(fdecl::UseRunner {
5300                        source: Some(fdecl::Ref::Environment(fdecl::EnvironmentRef {})),
5301                        source_name: Some("other.runner".to_string()),
5302                        ..Default::default()
5303                    }),
5304                ]);
5305                decl
5306            },
5307            result = Err(ErrorList::new(vec![
5308                Error::ConflictingRunners,
5309            ])),
5310        },
5311        test_validate_uses_runner_not_environement => {
5312            input = {
5313                let mut decl = new_component_decl();
5314                decl.program = Some(fdecl::Program {
5315                    runner: Some("runner".to_string()),
5316                    info: Some(fdata::Dictionary {
5317                        entries: None,
5318                        ..Default::default()
5319                    }),
5320                    ..Default::default()
5321                });
5322                decl.uses = Some(vec![
5323                    fdecl::Use::Runner(fdecl::UseRunner {
5324                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5325                        source_name: Some("runner".to_string()),
5326                        ..Default::default()
5327                    }),
5328                ]);
5329                decl
5330            },
5331            result = Err(ErrorList::new(vec![
5332                Error::ConflictingRunners,
5333            ])),
5334        },
5335        test_validate_uses_long_identifiers => {
5336            input = {
5337                let mut decl = new_component_decl();
5338                decl.program = Some(fdecl::Program {
5339                    runner: Some("elf".to_string()),
5340                    info: Some(fdata::Dictionary {
5341                        entries: None,
5342                        ..Default::default()
5343                    }),
5344                    ..Default::default()
5345                });
5346                decl.uses = Some(vec![
5347                    fdecl::Use::Service(fdecl::UseService {
5348                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5349                        source_name: Some(format!("{}", "a".repeat(256))),
5350                        target_path: Some("/a".repeat(2048)),
5351                        dependency_type: Some(fdecl::DependencyType::Strong),
5352                        ..Default::default()
5353                    }),
5354                    fdecl::Use::Protocol(fdecl::UseProtocol {
5355                        dependency_type: Some(fdecl::DependencyType::Strong),
5356                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5357                        source_name: Some(format!("{}", "a".repeat(256))),
5358                        target_path: Some("/b".repeat(2048)),
5359                        ..Default::default()
5360                    }),
5361                    fdecl::Use::Directory(fdecl::UseDirectory {
5362                        dependency_type: Some(fdecl::DependencyType::Strong),
5363                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5364                        source_name: Some(format!("{}", "a".repeat(256))),
5365                        target_path: Some("/c".repeat(2048)),
5366                        rights: Some(fio::Operations::CONNECT),
5367                        subdir: None,
5368                        ..Default::default()
5369                    }),
5370                    fdecl::Use::Storage(fdecl::UseStorage {
5371                        source_name: Some("cache".to_string()),
5372                        target_path: Some("/d".repeat(2048)),
5373                        ..Default::default()
5374                    }),
5375                ]);
5376                decl
5377            },
5378            result = Err(ErrorList::new(vec![
5379                Error::field_too_long(DeclType::UseService, "source_name"),
5380                Error::field_too_long(DeclType::UseService, "target_path"),
5381                Error::field_too_long(DeclType::UseProtocol, "source_name"),
5382                Error::field_too_long(DeclType::UseProtocol, "target_path"),
5383                Error::field_too_long(DeclType::UseDirectory, "source_name"),
5384                Error::field_too_long(DeclType::UseDirectory, "target_path"),
5385                Error::field_too_long(DeclType::UseStorage, "target_path"),
5386            ])),
5387        },
5388        test_validate_conflicting_paths => {
5389            input = {
5390                let mut decl = new_component_decl();
5391                decl.uses = Some(vec![
5392                    fdecl::Use::Service(fdecl::UseService {
5393                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5394                        source_name: Some("foo".to_string()),
5395                        target_path: Some("/bar".to_string()),
5396                        dependency_type: Some(fdecl::DependencyType::Strong),
5397                        ..Default::default()
5398                    }),
5399                    fdecl::Use::Protocol(fdecl::UseProtocol {
5400                        dependency_type: Some(fdecl::DependencyType::Strong),
5401                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5402                        source_name: Some("space".to_string()),
5403                        target_path: Some("/bar".to_string()),
5404                        ..Default::default()
5405                    }),
5406                    fdecl::Use::Directory(fdecl::UseDirectory {
5407                        dependency_type: Some(fdecl::DependencyType::Strong),
5408                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5409                        source_name: Some("crow".to_string()),
5410                        target_path: Some("/bar".to_string()),
5411                        rights: Some(fio::Operations::CONNECT),
5412                        subdir: None,
5413                        ..Default::default()
5414                    }),
5415                ]);
5416                decl
5417            },
5418            result = Err(ErrorList::new(vec![
5419                Error::duplicate_field(DeclType::UseDirectory, "target_path", "/bar"),
5420                Error::duplicate_field(DeclType::UseProtocol, "target_path", "/bar"),
5421            ])),
5422        },
5423        // exposes
5424        test_validate_exposes_empty => {
5425            input = {
5426                let mut decl = new_component_decl();
5427                decl.exposes = Some(vec![
5428                    fdecl::Expose::Service(fdecl::ExposeService {
5429                        source: None,
5430                        source_name: None,
5431                        target_name: None,
5432                        target: None,
5433                        ..Default::default()
5434                    }),
5435                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5436                        source: None,
5437                        source_name: None,
5438                        target_name: None,
5439                        target: None,
5440                        ..Default::default()
5441                    }),
5442                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5443                        source: None,
5444                        source_name: None,
5445                        target_name: None,
5446                        target: None,
5447                        rights: None,
5448                        subdir: None,
5449                        ..Default::default()
5450                    }),
5451                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5452                        source: None,
5453                        source_name: None,
5454                        target: None,
5455                        target_name: None,
5456                        ..Default::default()
5457                    }),
5458                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5459                        source: None,
5460                        source_name: None,
5461                        target: None,
5462                        target_name: None,
5463                        ..Default::default()
5464                    }),
5465                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5466                        ..Default::default()
5467                    }),
5468                ]);
5469                decl
5470            },
5471            result = Err(ErrorList::new(vec![
5472                Error::missing_field(DeclType::ExposeService, "source"),
5473                Error::missing_field(DeclType::ExposeService, "target"),
5474                Error::missing_field(DeclType::ExposeService, "source_name"),
5475                Error::missing_field(DeclType::ExposeService, "target_name"),
5476                Error::missing_field(DeclType::ExposeProtocol, "source"),
5477                Error::missing_field(DeclType::ExposeProtocol, "target"),
5478                Error::missing_field(DeclType::ExposeProtocol, "source_name"),
5479                Error::missing_field(DeclType::ExposeProtocol, "target_name"),
5480                Error::missing_field(DeclType::ExposeDirectory, "source"),
5481                Error::missing_field(DeclType::ExposeDirectory, "target"),
5482                Error::missing_field(DeclType::ExposeDirectory, "source_name"),
5483                Error::missing_field(DeclType::ExposeDirectory, "target_name"),
5484                Error::missing_field(DeclType::ExposeRunner, "source"),
5485                Error::missing_field(DeclType::ExposeRunner, "target"),
5486                Error::missing_field(DeclType::ExposeRunner, "source_name"),
5487                Error::missing_field(DeclType::ExposeRunner, "target_name"),
5488                Error::missing_field(DeclType::ExposeResolver, "source"),
5489                Error::missing_field(DeclType::ExposeResolver, "target"),
5490                Error::missing_field(DeclType::ExposeResolver, "source_name"),
5491                Error::missing_field(DeclType::ExposeResolver, "target_name"),
5492                Error::missing_field(DeclType::ExposeDictionary, "source"),
5493                Error::missing_field(DeclType::ExposeDictionary, "target"),
5494                Error::missing_field(DeclType::ExposeDictionary, "source_name"),
5495                Error::missing_field(DeclType::ExposeDictionary, "target_name"),
5496            ])),
5497        },
5498        test_validate_exposes_extraneous => {
5499            input = {
5500                let mut decl = new_component_decl();
5501                decl.exposes = Some(vec![
5502                    fdecl::Expose::Service(fdecl::ExposeService {
5503                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5504                            name: "logger".to_string(),
5505                            collection: Some("modular".to_string()),
5506                        })),
5507                        source_name: Some("logger".to_string()),
5508                        target_name: Some("logger".to_string()),
5509                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5510                        ..Default::default()
5511                    }),
5512                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5513                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5514                            name: "logger".to_string(),
5515                            collection: Some("modular".to_string()),
5516                        })),
5517                        source_name: Some("legacy_logger".to_string()),
5518                        target_name: Some("legacy_logger".to_string()),
5519                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5520                        ..Default::default()
5521                    }),
5522                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5523                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5524                            name: "netstack".to_string(),
5525                            collection: Some("modular".to_string()),
5526                        })),
5527                        source_name: Some("data".to_string()),
5528                        target_name: Some("data".to_string()),
5529                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5530                        rights: Some(fio::Operations::CONNECT),
5531                        subdir: None,
5532                        ..Default::default()
5533                    }),
5534                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5535                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5536                            name: "netstack".to_string(),
5537                            collection: Some("modular".to_string()),
5538                        })),
5539                        source_name: Some("elf".to_string()),
5540                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5541                        target_name: Some("elf".to_string()),
5542                        ..Default::default()
5543                    }),
5544                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5545                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5546                            name: "netstack".to_string(),
5547                            collection: Some("modular".to_string()),
5548                        })),
5549                        source_name: Some("pkg".to_string()),
5550                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5551                        target_name: Some("pkg".to_string()),
5552                        ..Default::default()
5553                    }),
5554                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5555                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5556                            name: "netstack".to_string(),
5557                            collection: Some("modular".to_string()),
5558                        })),
5559                        source_name: Some("dict".to_string()),
5560                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5561                        target_name: Some("dict".to_string()),
5562                        ..Default::default()
5563                    }),
5564                ]);
5565                decl
5566            },
5567            result = Err(ErrorList::new(vec![
5568                Error::extraneous_field(DeclType::ExposeService, "source.child.collection"),
5569                Error::extraneous_field(DeclType::ExposeProtocol, "source.child.collection"),
5570                Error::extraneous_field(DeclType::ExposeDirectory, "source.child.collection"),
5571                Error::extraneous_field(DeclType::ExposeRunner, "source.child.collection"),
5572                Error::extraneous_field(DeclType::ExposeResolver, "source.child.collection"),
5573                Error::extraneous_field(DeclType::ExposeDictionary, "source.child.collection"),
5574            ])),
5575        },
5576        test_validate_exposes_invalid_identifiers => {
5577            input = {
5578                let mut decl = new_component_decl();
5579                decl.exposes = Some(vec![
5580                    fdecl::Expose::Service(fdecl::ExposeService {
5581                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5582                            name: "^bad".to_string(),
5583                            collection: None,
5584                        })),
5585                        source_name: Some("foo/".to_string()),
5586                        target_name: Some("/".to_string()),
5587                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5588                        ..Default::default()
5589                    }),
5590                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5591                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5592                            name: "^bad".to_string(),
5593                            collection: None,
5594                        })),
5595                        source_name: Some("foo/".to_string()),
5596                        target_name: Some("/".to_string()),
5597                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5598                        ..Default::default()
5599                    }),
5600                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5601                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5602                            name: "^bad".to_string(),
5603                            collection: None,
5604                        })),
5605                        source_name: Some("foo/".to_string()),
5606                        target_name: Some("/".to_string()),
5607                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5608                        rights: Some(fio::Operations::CONNECT),
5609                        subdir: Some("/foo".to_string()),
5610                        ..Default::default()
5611                    }),
5612                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5613                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5614                            name: "^bad".to_string(),
5615                            collection: None,
5616                        })),
5617                        source_name: Some("/path".to_string()),
5618                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5619                        target_name: Some("elf!".to_string()),
5620                        ..Default::default()
5621                    }),
5622                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5623                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5624                            name: "^bad".to_string(),
5625                            collection: None,
5626                        })),
5627                        source_name: Some("/path".to_string()),
5628                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5629                        target_name: Some("pkg!".to_string()),
5630                        ..Default::default()
5631                    }),
5632                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5633                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5634                            name: "^bad".to_string(),
5635                            collection: None,
5636                        })),
5637                        source_name: Some("/path".to_string()),
5638                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5639                        target_name: Some("pkg!".to_string()),
5640                        ..Default::default()
5641                    }),
5642                ]);
5643                decl
5644            },
5645            result = Err(ErrorList::new(vec![
5646                Error::invalid_field(DeclType::ExposeService, "source.child.name"),
5647                Error::invalid_field(DeclType::ExposeService, "source_name"),
5648                Error::invalid_field(DeclType::ExposeService, "target_name"),
5649                Error::invalid_field(DeclType::ExposeProtocol, "source.child.name"),
5650                Error::invalid_field(DeclType::ExposeProtocol, "source_name"),
5651                Error::invalid_field(DeclType::ExposeProtocol, "target_name"),
5652                Error::invalid_field(DeclType::ExposeDirectory, "source.child.name"),
5653                Error::invalid_field(DeclType::ExposeDirectory, "source_name"),
5654                Error::invalid_field(DeclType::ExposeDirectory, "target_name"),
5655                Error::invalid_field(DeclType::ExposeDirectory, "subdir"),
5656                Error::invalid_field(DeclType::ExposeRunner, "source.child.name"),
5657                Error::invalid_field(DeclType::ExposeRunner, "source_name"),
5658                Error::invalid_field(DeclType::ExposeRunner, "target_name"),
5659                Error::invalid_field(DeclType::ExposeResolver, "source.child.name"),
5660                Error::invalid_field(DeclType::ExposeResolver, "source_name"),
5661                Error::invalid_field(DeclType::ExposeResolver, "target_name"),
5662                Error::invalid_field(DeclType::ExposeDictionary, "source.child.name"),
5663                Error::invalid_field(DeclType::ExposeDictionary, "source_name"),
5664                Error::invalid_field(DeclType::ExposeDictionary, "target_name"),
5665            ])),
5666        },
5667        test_validate_exposes_invalid_source_target => {
5668            input = {
5669                let mut decl = new_component_decl();
5670                decl.children = Some(vec![fdecl::Child{
5671                    name: Some("logger".to_string()),
5672                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5673                    startup: Some(fdecl::StartupMode::Lazy),
5674                    on_terminate: None,
5675                    environment: None,
5676                    ..Default::default()
5677                }]);
5678                decl.exposes = Some(vec![
5679                    fdecl::Expose::Service(fdecl::ExposeService {
5680                        source: None,
5681                        source_name: Some("a".to_string()),
5682                        target_name: Some("b".to_string()),
5683                        target: None,
5684                        ..Default::default()
5685                    }),
5686                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5687                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5688                        source_name: Some("c".to_string()),
5689                        target_name: Some("d".to_string()),
5690                        target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5691                        ..Default::default()
5692                    }),
5693                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5694                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "z".to_string()})),
5695                        source_name: Some("e".to_string()),
5696                        target_name: Some("f".to_string()),
5697                        target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "z".to_string()})),
5698                        rights: Some(fio::Operations::CONNECT),
5699                        subdir: None,
5700                        ..Default::default()
5701                    }),
5702                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5703                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5704                        source_name: Some("g".to_string()),
5705                        target_name: Some("h".to_string()),
5706                        target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5707                        rights: Some(fio::Operations::CONNECT),
5708                        subdir: None,
5709                        ..Default::default()
5710                    }),
5711                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5712                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5713                        source_name: Some("i".to_string()),
5714                        target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5715                        target_name: Some("j".to_string()),
5716                        ..Default::default()
5717                    }),
5718                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5719                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5720                        source_name: Some("k".to_string()),
5721                        target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5722                        target_name: Some("l".to_string()),
5723                        ..Default::default()
5724                    }),
5725                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5726                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5727                            name: "logger".to_string(),
5728                            collection: None,
5729                        })),
5730                        source_name: Some("m".to_string()),
5731                        target_name: Some("n".to_string()),
5732                        target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5733                        ..Default::default()
5734                    }),
5735                ]);
5736                decl
5737            },
5738            result = Err(ErrorList::new(vec![
5739                Error::missing_field(DeclType::ExposeService, "source"),
5740                Error::missing_field(DeclType::ExposeService, "target"),
5741                Error::invalid_field(DeclType::ExposeProtocol, "source"),
5742                Error::invalid_field(DeclType::ExposeProtocol, "target"),
5743                Error::invalid_field(DeclType::ExposeDirectory, "source"),
5744                Error::invalid_field(DeclType::ExposeDirectory, "target"),
5745                Error::invalid_field(DeclType::ExposeDirectory, "source"),
5746                Error::invalid_field(DeclType::ExposeDirectory, "target"),
5747                Error::invalid_field(DeclType::ExposeRunner, "source"),
5748                Error::invalid_field(DeclType::ExposeRunner, "target"),
5749                Error::invalid_field(DeclType::ExposeResolver, "source"),
5750                Error::invalid_field(DeclType::ExposeResolver, "target"),
5751                Error::invalid_field(DeclType::ExposeDictionary, "target"),
5752            ])),
5753        },
5754        test_validate_exposes_invalid_source_collection => {
5755            input = {
5756                let mut decl = new_component_decl();
5757                decl.collections = Some(vec![fdecl::Collection{
5758                    name: Some("col".to_string()),
5759                    durability: Some(fdecl::Durability::Transient),
5760                    allowed_offers: None,
5761                    allow_long_names: None,
5762                    ..Default::default()
5763                }]);
5764                decl.exposes = Some(vec![
5765                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5766                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
5767                        source_name: Some("a".to_string()),
5768                        target_name: Some("a".to_string()),
5769                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5770                        ..Default::default()
5771                    }),
5772                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5773                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5774                        source_name: Some("b".to_string()),
5775                        target_name: Some("b".to_string()),
5776                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5777                        rights: Some(fio::Operations::CONNECT),
5778                        subdir: None,
5779                        ..Default::default()
5780                    }),
5781                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5782                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5783                        source_name: Some("c".to_string()),
5784                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5785                        target_name: Some("c".to_string()),
5786                        ..Default::default()
5787                    }),
5788                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5789                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5790                        source_name: Some("d".to_string()),
5791                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5792                        target_name: Some("d".to_string()),
5793                        ..Default::default()
5794                    }),
5795                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5796                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5797                        source_name: Some("e".to_string()),
5798                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5799                        target_name: Some("e".to_string()),
5800                        ..Default::default()
5801                    }),
5802                ]);
5803                decl
5804            },
5805            result = Err(ErrorList::new(vec![
5806                Error::invalid_field(DeclType::ExposeProtocol, "source"),
5807                Error::invalid_field(DeclType::ExposeDirectory, "source"),
5808                Error::invalid_field(DeclType::ExposeRunner, "source"),
5809                Error::invalid_field(DeclType::ExposeResolver, "source"),
5810                Error::invalid_field(DeclType::ExposeDictionary, "source"),
5811            ])),
5812        },
5813        test_validate_exposes_sources_collection => {
5814            input = {
5815                let mut decl = new_component_decl();
5816                decl.collections = Some(vec![
5817                    fdecl::Collection {
5818                        name: Some("col".to_string()),
5819                        durability: Some(fdecl::Durability::Transient),
5820                        allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
5821                        allow_long_names: None,
5822                        ..Default::default()
5823                    }
5824                ]);
5825                decl.exposes = Some(vec![
5826                    fdecl::Expose::Service(fdecl::ExposeService {
5827                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
5828                        source_name: Some("a".to_string()),
5829                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5830                        target_name: Some("a".to_string()),
5831                        ..Default::default()
5832                    })
5833                ]);
5834                decl
5835            },
5836            result = Ok(()),
5837        },
5838        test_validate_exposes_long_identifiers => {
5839            input = {
5840                let mut decl = new_component_decl();
5841                decl.exposes = Some(vec![
5842                    fdecl::Expose::Service(fdecl::ExposeService {
5843                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5844                            name: "b".repeat(256),
5845                            collection: None,
5846                        })),
5847                        source_name: Some(format!("{}", "a".repeat(1025))),
5848                        target_name: Some(format!("{}", "b".repeat(1025))),
5849                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5850                        ..Default::default()
5851                    }),
5852                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5853                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5854                            name: "b".repeat(256),
5855                            collection: None,
5856                        })),
5857                        source_name: Some(format!("{}", "a".repeat(256))),
5858                        target_name: Some(format!("{}", "b".repeat(256))),
5859                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5860                        ..Default::default()
5861                    }),
5862                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5863                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5864                            name: "b".repeat(256),
5865                            collection: None,
5866                        })),
5867                        source_name: Some(format!("{}", "a".repeat(256))),
5868                        target_name: Some(format!("{}", "b".repeat(256))),
5869                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5870                        rights: Some(fio::Operations::CONNECT),
5871                        subdir: None,
5872                        ..Default::default()
5873                    }),
5874                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5875                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5876                            name: "b".repeat(256),
5877                            collection: None,
5878                        })),
5879                        source_name: Some("a".repeat(256)),
5880                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5881                        target_name: Some("b".repeat(256)),
5882                        ..Default::default()
5883                    }),
5884                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5885                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5886                            name: "b".repeat(256),
5887                            collection: None,
5888                        })),
5889                        source_name: Some("a".repeat(256)),
5890                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5891                        target_name: Some("b".repeat(256)),
5892                        ..Default::default()
5893                    }),
5894                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5895                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5896                            name: "b".repeat(256),
5897                            collection: None,
5898                        })),
5899                        source_name: Some("a".repeat(256)),
5900                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5901                        target_name: Some("b".repeat(256)),
5902                        ..Default::default()
5903                    }),
5904                ]);
5905                decl
5906            },
5907            result = Err(ErrorList::new(vec![
5908                Error::field_too_long(DeclType::ExposeService, "source.child.name"),
5909                Error::field_too_long(DeclType::ExposeService, "source_name"),
5910                Error::field_too_long(DeclType::ExposeService, "target_name"),
5911                Error::field_too_long(DeclType::ExposeProtocol, "source.child.name"),
5912                Error::field_too_long(DeclType::ExposeProtocol, "source_name"),
5913                Error::field_too_long(DeclType::ExposeProtocol, "target_name"),
5914                Error::field_too_long(DeclType::ExposeDirectory, "source.child.name"),
5915                Error::field_too_long(DeclType::ExposeDirectory, "source_name"),
5916                Error::field_too_long(DeclType::ExposeDirectory, "target_name"),
5917                Error::field_too_long(DeclType::ExposeRunner, "source.child.name"),
5918                Error::field_too_long(DeclType::ExposeRunner, "source_name"),
5919                Error::field_too_long(DeclType::ExposeRunner, "target_name"),
5920                Error::field_too_long(DeclType::ExposeResolver, "source.child.name"),
5921                Error::field_too_long(DeclType::ExposeResolver, "source_name"),
5922                Error::field_too_long(DeclType::ExposeResolver, "target_name"),
5923                Error::field_too_long(DeclType::ExposeDictionary, "source.child.name"),
5924                Error::field_too_long(DeclType::ExposeDictionary, "source_name"),
5925                Error::field_too_long(DeclType::ExposeDictionary, "target_name"),
5926            ])),
5927        },
5928        test_validate_exposes_invalid_child => {
5929            input = {
5930                let mut decl = new_component_decl();
5931                decl.exposes = Some(vec![
5932                    fdecl::Expose::Service(fdecl::ExposeService {
5933                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5934                            name: "netstack".to_string(),
5935                            collection: None,
5936                        })),
5937                        source_name: Some("fuchsia.logger.Log".to_string()),
5938                        target_name: Some("fuchsia.logger.Log".to_string()),
5939                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5940                        ..Default::default()
5941                    }),
5942                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5943                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5944                            name: "netstack".to_string(),
5945                            collection: None,
5946                        })),
5947                        source_name: Some("fuchsia.logger.LegacyLog".to_string()),
5948                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
5949                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5950                        ..Default::default()
5951                    }),
5952                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5953                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5954                            name: "netstack".to_string(),
5955                            collection: None,
5956                        })),
5957                        source_name: Some("data".to_string()),
5958                        target_name: Some("data".to_string()),
5959                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5960                        rights: Some(fio::Operations::CONNECT),
5961                        subdir: None,
5962                        ..Default::default()
5963                    }),
5964                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5965                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5966                            name: "netstack".to_string(),
5967                            collection: None,
5968                        })),
5969                        source_name: Some("elf".to_string()),
5970                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5971                        target_name: Some("elf".to_string()),
5972                        ..Default::default()
5973                    }),
5974                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5975                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5976                            name: "netstack".to_string(),
5977                            collection: None,
5978                        })),
5979                        source_name: Some("pkg".to_string()),
5980                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5981                        target_name: Some("pkg".to_string()),
5982                        ..Default::default()
5983                    }),
5984                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5985                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5986                            name: "netstack".to_string(),
5987                            collection: None,
5988                        })),
5989                        source_name: Some("dict".to_string()),
5990                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5991                        target_name: Some("dict".to_string()),
5992                        ..Default::default()
5993                    }),
5994                ]);
5995                decl
5996            },
5997            result = Err(ErrorList::new(vec![
5998                Error::invalid_child(DeclType::ExposeService, "source", "netstack"),
5999                Error::invalid_child(DeclType::ExposeProtocol, "source", "netstack"),
6000                Error::invalid_child(DeclType::ExposeDirectory, "source", "netstack"),
6001                Error::invalid_child(DeclType::ExposeRunner, "source", "netstack"),
6002                Error::invalid_child(DeclType::ExposeResolver, "source", "netstack"),
6003                Error::invalid_child(DeclType::ExposeDictionary, "source", "netstack"),
6004            ])),
6005        },
6006        test_validate_exposes_invalid_source_capability => {
6007            input = {
6008                fdecl::Component {
6009                    exposes: Some(vec![
6010                        fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6011                            source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
6012                                name: "this-storage-doesnt-exist".to_string(),
6013                            })),
6014                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
6015                            target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
6016                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6017                            ..Default::default()
6018                        }),
6019                    ]),
6020                    ..new_component_decl()
6021                }
6022            },
6023            result = Err(ErrorList::new(vec![
6024                Error::invalid_capability(DeclType::ExposeProtocol, "source", "this-storage-doesnt-exist"),
6025            ])),
6026        },
6027        test_validate_exposes_duplicate_target => {
6028            input = {
6029                let mut decl = new_component_decl();
6030                decl.exposes = Some(vec![
6031                    fdecl::Expose::Service(fdecl::ExposeService {
6032                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6033                            name: "coll".into(),
6034                        })),
6035                        source_name: Some("netstack".to_string()),
6036                        target_name: Some("fuchsia.net.Stack".to_string()),
6037                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6038                        ..Default::default()
6039                    }),
6040                    fdecl::Expose::Service(fdecl::ExposeService {
6041                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6042                            name: "coll2".into(),
6043                        })),
6044                        source_name: Some("netstack2".to_string()),
6045                        target_name: Some("fuchsia.net.Stack".to_string()),
6046                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6047                        ..Default::default()
6048                    }),
6049                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6050                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6051                        source_name: Some("fonts".to_string()),
6052                        target_name: Some("fuchsia.fonts.Provider".to_string()),
6053                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6054                        ..Default::default()
6055                    }),
6056                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6057                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6058                        source_name: Some("fonts2".to_string()),
6059                        target_name: Some("fuchsia.fonts.Provider".to_string()),
6060                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6061                        ..Default::default()
6062                    }),
6063                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
6064                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6065                        source_name: Some("assets".to_string()),
6066                        target_name: Some("stuff".to_string()),
6067                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6068                        rights: None,
6069                        subdir: None,
6070                        ..Default::default()
6071                    }),
6072                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
6073                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6074                        source_name: Some("assets2".to_string()),
6075                        target_name: Some("stuff".to_string()),
6076                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6077                        rights: None,
6078                        subdir: None,
6079                        ..Default::default()
6080                    }),
6081                    fdecl::Expose::Runner(fdecl::ExposeRunner {
6082                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6083                        source_name: Some("source_elf".to_string()),
6084                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6085                        target_name: Some("elf".to_string()),
6086                        ..Default::default()
6087                    }),
6088                    fdecl::Expose::Runner(fdecl::ExposeRunner {
6089                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6090                        source_name: Some("source_elf".to_string()),
6091                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6092                        target_name: Some("elf".to_string()),
6093                        ..Default::default()
6094                    }),
6095                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
6096                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6097                        source_name: Some("source_pkg".to_string()),
6098                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6099                        target_name: Some("pkg".to_string()),
6100                        ..Default::default()
6101                    }),
6102                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
6103                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6104                        source_name: Some("source_pkg".to_string()),
6105                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6106                        target_name: Some("pkg".to_string()),
6107                        ..Default::default()
6108                    }),
6109                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
6110                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6111                        source_name: Some("source_dict".to_string()),
6112                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6113                        target_name: Some("dict".to_string()),
6114                        ..Default::default()
6115                    }),
6116                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
6117                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6118                        source_name: Some("source_dict".to_string()),
6119                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6120                        target_name: Some("dict".to_string()),
6121                        ..Default::default()
6122                    }),
6123                ]);
6124                decl.collections = Some(vec![
6125                    fdecl::Collection {
6126                        name: Some("coll".into()),
6127                        durability: Some(fdecl::Durability::Transient),
6128                        ..Default::default()
6129                    },
6130                    fdecl::Collection {
6131                        name: Some("coll2".into()),
6132                        durability: Some(fdecl::Durability::Transient),
6133                        ..Default::default()
6134                    },
6135                ]);
6136                decl.capabilities = Some(vec![
6137                    fdecl::Capability::Service(fdecl::Service {
6138                        name: Some("netstack".to_string()),
6139                        source_path: Some("/path".to_string()),
6140                        ..Default::default()
6141                    }),
6142                    fdecl::Capability::Service(fdecl::Service {
6143                        name: Some("netstack2".to_string()),
6144                        source_path: Some("/path".to_string()),
6145                        ..Default::default()
6146                    }),
6147                    fdecl::Capability::Protocol(fdecl::Protocol {
6148                        name: Some("fonts".to_string()),
6149                        source_path: Some("/path".to_string()),
6150                        ..Default::default()
6151                    }),
6152                    fdecl::Capability::Protocol(fdecl::Protocol {
6153                        name: Some("fonts2".to_string()),
6154                        source_path: Some("/path".to_string()),
6155                        ..Default::default()
6156                    }),
6157                    fdecl::Capability::Directory(fdecl::Directory {
6158                        name: Some("assets".to_string()),
6159                        source_path: Some("/path".to_string()),
6160                        rights: Some(fio::Operations::CONNECT),
6161                        ..Default::default()
6162                    }),
6163                    fdecl::Capability::Directory(fdecl::Directory {
6164                        name: Some("assets2".to_string()),
6165                        source_path: Some("/path".to_string()),
6166                        rights: Some(fio::Operations::CONNECT),
6167                        ..Default::default()
6168                    }),
6169                    fdecl::Capability::Runner(fdecl::Runner {
6170                        name: Some("source_elf".to_string()),
6171                        source_path: Some("/path".to_string()),
6172                        ..Default::default()
6173                    }),
6174                    fdecl::Capability::Resolver(fdecl::Resolver {
6175                        name: Some("source_pkg".to_string()),
6176                        source_path: Some("/path".to_string()),
6177                        ..Default::default()
6178                    }),
6179                    fdecl::Capability::Dictionary(fdecl::Dictionary {
6180                        name: Some("source_dict".to_string()),
6181                        ..Default::default()
6182                    }),
6183                ]);
6184                decl
6185            },
6186            result = Err(ErrorList::new(vec![
6187                // Duplicate services are allowed.
6188                Error::duplicate_field(DeclType::ExposeProtocol, "target_name",
6189                                    "fuchsia.fonts.Provider"),
6190                Error::duplicate_field(DeclType::ExposeDirectory, "target_name",
6191                                    "stuff"),
6192                Error::duplicate_field(DeclType::ExposeRunner, "target_name",
6193                                    "elf"),
6194                Error::duplicate_field(DeclType::ExposeResolver, "target_name", "pkg"),
6195                Error::duplicate_field(DeclType::ExposeDictionary, "target_name", "dict"),
6196            ])),
6197        },
6198        test_validate_exposes_invalid_capability_from_self => {
6199            input = {
6200                let mut decl = new_component_decl();
6201                decl.exposes = Some(vec![
6202                    fdecl::Expose::Service(fdecl::ExposeService {
6203                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6204                        source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
6205                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6206                        target_name: Some("foo".to_string()),
6207                        ..Default::default()
6208                    }),
6209                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6210                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6211                        source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
6212                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6213                        target_name: Some("bar".to_string()),
6214                        ..Default::default()
6215                    }),
6216                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
6217                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6218                        source_name: Some("dir".to_string()),
6219                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6220                        target_name: Some("assets".to_string()),
6221                        rights: None,
6222                        subdir: None,
6223                        ..Default::default()
6224                    }),
6225                    fdecl::Expose::Runner(fdecl::ExposeRunner {
6226                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6227                        source_name: Some("source_elf".to_string()),
6228                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6229                        target_name: Some("elf".to_string()),
6230                        ..Default::default()
6231                    }),
6232                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
6233                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6234                        source_name: Some("source_pkg".to_string()),
6235                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6236                        target_name: Some("pkg".to_string()),
6237                        ..Default::default()
6238                    }),
6239                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
6240                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6241                        source_name: Some("source_dict".to_string()),
6242                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6243                        target_name: Some("dict".to_string()),
6244                        ..Default::default()
6245                    }),
6246                    fdecl::Expose::Config(fdecl::ExposeConfiguration {
6247                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6248                        source_name: Some("source_config".to_string()),
6249                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6250                        target_name: Some("config".to_string()),
6251                        ..Default::default()
6252                    }),
6253                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6254                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
6255                        source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
6256                        source_dictionary: Some("dict/inner".to_string()),
6257                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6258                        target_name: Some("baz".to_string()),
6259                        ..Default::default()
6260                    }),
6261                ]);
6262                decl
6263            },
6264            result = Err(ErrorList::new(vec![
6265                Error::invalid_capability(
6266                    DeclType::ExposeService,
6267                    "source",
6268                    "fuchsia.some.library.SomeProtocol"),
6269                Error::invalid_capability(
6270                    DeclType::ExposeProtocol,
6271                    "source",
6272                    "fuchsia.some.library.SomeProtocol"),
6273                Error::invalid_capability(DeclType::ExposeDirectory, "source", "dir"),
6274                Error::invalid_capability(DeclType::ExposeRunner, "source", "source_elf"),
6275                Error::invalid_capability(DeclType::ExposeResolver, "source", "source_pkg"),
6276                Error::invalid_capability(DeclType::ExposeDictionary, "source", "source_dict"),
6277                Error::invalid_capability(DeclType::ExposeConfig, "source", "source_config"),
6278                Error::invalid_capability(DeclType::ExposeProtocol, "source", "dict"),
6279            ])),
6280        },
6281
6282        test_validate_exposes_availability_service => {
6283            input = {
6284                let mut decl = generate_expose_different_source_and_availability_decl(
6285                    |source, availability, target_name|
6286                        fdecl::Expose::Service(fdecl::ExposeService {
6287                            source: Some(source),
6288                            source_name: Some("fuchsia.examples.Echo".to_string()),
6289                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6290                            target_name: Some(target_name.to_string()),
6291                            availability: Some(availability),
6292                            ..Default::default()
6293                        })
6294                );
6295                decl.capabilities = Some(vec![
6296                    fdecl::Capability::Service(fdecl::Service {
6297                        name: Some("fuchsia.examples.Echo".to_string()),
6298                        source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
6299                        ..Default::default()
6300                    }),
6301                ]);
6302                decl
6303            },
6304            result = {
6305                Err(ErrorList::new(vec![
6306                    Error::availability_must_be_optional(
6307                        DeclType::ExposeService,
6308                        "availability",
6309                        Some(&"fuchsia.examples.Echo".to_string()),
6310                    ),
6311                    Error::availability_must_be_optional(
6312                        DeclType::ExposeService,
6313                        "availability",
6314                        Some(&"fuchsia.examples.Echo".to_string()),
6315                    ),
6316                ]))
6317            },
6318        },
6319        test_validate_exposes_availability_protocol => {
6320            input = {
6321                let mut decl = generate_expose_different_source_and_availability_decl(
6322                    |source, availability, target_name|
6323                        fdecl::Expose::Protocol(fdecl::ExposeProtocol {
6324                            source: Some(source),
6325                            source_name: Some("fuchsia.examples.Echo".to_string()),
6326                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6327                            target_name: Some(target_name.to_string()),
6328                            availability: Some(availability),
6329                            ..Default::default()
6330                        })
6331                );
6332                decl.capabilities = Some(vec![
6333                    fdecl::Capability::Protocol(fdecl::Protocol {
6334                        name: Some("fuchsia.examples.Echo".to_string()),
6335                        source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
6336                        ..Default::default()
6337                    }),
6338                ]);
6339                decl
6340            },
6341            result = {
6342                Err(ErrorList::new(vec![
6343                    Error::availability_must_be_optional(
6344                        DeclType::ExposeProtocol,
6345                        "availability",
6346                        Some(&"fuchsia.examples.Echo".to_string()),
6347                    ),
6348                    Error::availability_must_be_optional(
6349                        DeclType::ExposeProtocol,
6350                        "availability",
6351                        Some(&"fuchsia.examples.Echo".to_string()),
6352                    ),
6353                ]))
6354            },
6355        },
6356        test_validate_exposes_availability_directory => {
6357            input = {
6358                let mut decl = generate_expose_different_source_and_availability_decl(
6359                    |source, availability, target_name|
6360                        fdecl::Expose::Directory(fdecl::ExposeDirectory {
6361                            source: Some(source),
6362                            source_name: Some("fuchsia.examples.Echo".to_string()),
6363                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6364                            target_name: Some(target_name.to_string()),
6365                            availability: Some(availability),
6366                            ..Default::default()
6367                        })
6368                );
6369                decl.capabilities = Some(vec![
6370                    fdecl::Capability::Directory(fdecl::Directory {
6371                        name: Some("fuchsia.examples.Echo".to_string()),
6372                        source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
6373                        rights: Some(fio::Operations::READ_BYTES),
6374                        ..Default::default()
6375                    }),
6376                ]);
6377                decl
6378            },
6379            result = {
6380                Err(ErrorList::new(vec![
6381                    Error::availability_must_be_optional(
6382                        DeclType::ExposeDirectory,
6383                        "availability",
6384                        Some(&"fuchsia.examples.Echo".to_string()),
6385                    ),
6386                    Error::availability_must_be_optional(
6387                        DeclType::ExposeDirectory,
6388                        "availability",
6389                        Some(&"fuchsia.examples.Echo".to_string()),
6390                    ),
6391                ]))
6392            },
6393        },
6394
6395        // offers
6396        test_validate_offers_empty => {
6397            input = {
6398                let mut decl = new_component_decl();
6399                decl.offers = Some(vec![
6400                    fdecl::Offer::Service(fdecl::OfferService {
6401                        source: None,
6402                        source_name: None,
6403                        target: None,
6404                        target_name: None,
6405                        ..Default::default()
6406                    }),
6407                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6408                        source: None,
6409                        source_name: None,
6410                        target: None,
6411                        target_name: None,
6412                        dependency_type: None,
6413                        ..Default::default()
6414                    }),
6415                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6416                        source: None,
6417                        source_name: None,
6418                        target: None,
6419                        target_name: None,
6420                        rights: None,
6421                        subdir: None,
6422                        dependency_type: None,
6423                        ..Default::default()
6424                    }),
6425                    fdecl::Offer::Storage(fdecl::OfferStorage {
6426                        source_name: None,
6427                        source: None,
6428                        target: None,
6429                        target_name: None,
6430                        ..Default::default()
6431                    }),
6432                    fdecl::Offer::Runner(fdecl::OfferRunner {
6433                        source: None,
6434                        source_name: None,
6435                        target: None,
6436                        target_name: None,
6437                        ..Default::default()
6438                    }),
6439                    fdecl::Offer::Resolver(fdecl::OfferResolver {
6440                        ..Default::default()
6441                    }),
6442                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6443                        ..Default::default()
6444                    }),
6445                ]);
6446                decl
6447            },
6448            // TODO(dgonyeo): we need to handle the availability being unset until we've soft
6449            // migrated all manifests
6450            result = Err(ErrorList::new(vec![
6451                Error::missing_field(DeclType::OfferService, "source"),
6452                Error::missing_field(DeclType::OfferService, "source_name"),
6453                Error::missing_field(DeclType::OfferService, "target"),
6454                Error::missing_field(DeclType::OfferService, "target_name"),
6455                //Error::missing_field(DeclType::OfferService, "availability"),
6456                Error::missing_field(DeclType::OfferProtocol, "source"),
6457                Error::missing_field(DeclType::OfferProtocol, "source_name"),
6458                Error::missing_field(DeclType::OfferProtocol, "target"),
6459                Error::missing_field(DeclType::OfferProtocol, "target_name"),
6460                Error::missing_field(DeclType::OfferProtocol, "dependency_type"),
6461                //Error::missing_field(DeclType::OfferProtocol, "availability"),
6462                Error::missing_field(DeclType::OfferDirectory, "source"),
6463                Error::missing_field(DeclType::OfferDirectory, "source_name"),
6464                Error::missing_field(DeclType::OfferDirectory, "target"),
6465                Error::missing_field(DeclType::OfferDirectory, "target_name"),
6466                Error::missing_field(DeclType::OfferDirectory, "dependency_type"),
6467                //Error::missing_field(DeclType::OfferDirectory, "availability"),
6468                Error::missing_field(DeclType::OfferStorage, "source"),
6469                Error::missing_field(DeclType::OfferStorage, "source_name"),
6470                Error::missing_field(DeclType::OfferStorage, "target"),
6471                Error::missing_field(DeclType::OfferStorage, "target_name"),
6472                //Error::missing_field(DeclType::OfferStorage, "availability"),
6473                Error::missing_field(DeclType::OfferRunner, "source"),
6474                Error::missing_field(DeclType::OfferRunner, "source_name"),
6475                Error::missing_field(DeclType::OfferRunner, "target"),
6476                Error::missing_field(DeclType::OfferRunner, "target_name"),
6477                //Error::missing_field(DeclType::OfferRunner, "availability"),
6478                Error::missing_field(DeclType::OfferResolver, "source"),
6479                Error::missing_field(DeclType::OfferResolver, "source_name"),
6480                Error::missing_field(DeclType::OfferResolver, "target"),
6481                Error::missing_field(DeclType::OfferResolver, "target_name"),
6482                Error::missing_field(DeclType::OfferDictionary, "source"),
6483                Error::missing_field(DeclType::OfferDictionary, "source_name"),
6484                Error::missing_field(DeclType::OfferDictionary, "target"),
6485                Error::missing_field(DeclType::OfferDictionary, "target_name"),
6486                Error::missing_field(DeclType::OfferDictionary, "dependency_type"),
6487            ])),
6488        },
6489        test_validate_offers_long_identifiers => {
6490            input = {
6491                let mut decl = new_component_decl();
6492                decl.offers = Some(vec![
6493                    fdecl::Offer::Service(fdecl::OfferService {
6494                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6495                            name: "a".repeat(256),
6496                            collection: None,
6497                        })),
6498                        source_name: Some(format!("{}", "a".repeat(256))),
6499                        target: Some(fdecl::Ref::Child(
6500                        fdecl::ChildRef {
6501                            name: "b".repeat(256),
6502                            collection: None,
6503                        }
6504                        )),
6505                        target_name: Some(format!("{}", "b".repeat(256))),
6506                        ..Default::default()
6507                    }),
6508                    fdecl::Offer::Service(fdecl::OfferService {
6509                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6510                        source_name: Some("a".to_string()),
6511                        target: Some(fdecl::Ref::Collection(
6512                        fdecl::CollectionRef {
6513                            name: "b".repeat(256),
6514                        }
6515                        )),
6516                        target_name: Some(format!("{}", "b".repeat(256))),
6517                        ..Default::default()
6518                    }),
6519                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6520                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6521                            name: "a".repeat(256),
6522                            collection: None,
6523                        })),
6524                        source_name: Some(format!("{}", "a".repeat(256))),
6525                        target: Some(fdecl::Ref::Child(
6526                        fdecl::ChildRef {
6527                            name: "b".repeat(256),
6528                            collection: None,
6529                        }
6530                        )),
6531                        target_name: Some(format!("{}", "b".repeat(256))),
6532                        dependency_type: Some(fdecl::DependencyType::Strong),
6533                        ..Default::default()
6534                    }),
6535                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6536                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6537                        source_name: Some("a".to_string()),
6538                        target: Some(fdecl::Ref::Collection(
6539                        fdecl::CollectionRef {
6540                            name: "b".repeat(256),
6541                        }
6542                        )),
6543                        target_name: Some(format!("{}", "b".repeat(256))),
6544                        dependency_type: Some(fdecl::DependencyType::Weak),
6545                        ..Default::default()
6546                    }),
6547                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6548                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6549                            name: "a".repeat(256),
6550                            collection: None,
6551                        })),
6552                        source_name: Some(format!("{}", "a".repeat(256))),
6553                        target: Some(fdecl::Ref::Child(
6554                        fdecl::ChildRef {
6555                            name: "b".repeat(256),
6556                            collection: None,
6557                        }
6558                        )),
6559                        target_name: Some(format!("{}", "b".repeat(256))),
6560                        rights: Some(fio::Operations::CONNECT),
6561                        subdir: None,
6562                        dependency_type: Some(fdecl::DependencyType::Strong),
6563                        ..Default::default()
6564                    }),
6565                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6566                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6567                        source_name: Some("a".to_string()),
6568                        target: Some(fdecl::Ref::Collection(
6569                        fdecl::CollectionRef {
6570                            name: "b".repeat(256),
6571                        }
6572                        )),
6573                        target_name: Some(format!("{}", "b".repeat(256))),
6574                        rights: Some(fio::Operations::CONNECT),
6575                        subdir: None,
6576                        dependency_type: Some(fdecl::DependencyType::Weak),
6577                        ..Default::default()
6578                    }),
6579                    fdecl::Offer::Storage(fdecl::OfferStorage {
6580                        source_name: Some("data".to_string()),
6581                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6582                        target: Some(fdecl::Ref::Child(
6583                            fdecl::ChildRef {
6584                                name: "b".repeat(256),
6585                                collection: None,
6586                            }
6587                        )),
6588                        target_name: Some("data".to_string()),
6589                        ..Default::default()
6590                    }),
6591                    fdecl::Offer::Storage(fdecl::OfferStorage {
6592                        source_name: Some("data".to_string()),
6593                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6594                        target: Some(fdecl::Ref::Collection(
6595                            fdecl::CollectionRef { name: "b".repeat(256) }
6596                        )),
6597                        target_name: Some("data".to_string()),
6598                        ..Default::default()
6599                    }),
6600                    fdecl::Offer::Runner(fdecl::OfferRunner {
6601                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6602                            name: "a".repeat(256),
6603                            collection: None,
6604                        })),
6605                        source_name: Some("b".repeat(256)),
6606                        target: Some(fdecl::Ref::Collection(
6607                        fdecl::CollectionRef {
6608                            name: "c".repeat(256),
6609                        }
6610                        )),
6611                        target_name: Some("d".repeat(256)),
6612                        ..Default::default()
6613                    }),
6614                    fdecl::Offer::Resolver(fdecl::OfferResolver {
6615                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6616                            name: "a".repeat(256),
6617                            collection: None,
6618                        })),
6619                        source_name: Some("b".repeat(256)),
6620                        target: Some(fdecl::Ref::Collection(
6621                            fdecl::CollectionRef {
6622                                name: "c".repeat(256),
6623                            }
6624                        )),
6625                        target_name: Some("d".repeat(256)),
6626                        ..Default::default()
6627                    }),
6628                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6629                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6630                            name: "a".repeat(256),
6631                            collection: None,
6632                        })),
6633                        source_name: Some("b".repeat(256)),
6634                        target: Some(fdecl::Ref::Collection(
6635                            fdecl::CollectionRef {
6636                                name: "c".repeat(256),
6637                            }
6638                        )),
6639                        target_name: Some("d".repeat(256)),
6640                        dependency_type: Some(fdecl::DependencyType::Strong),
6641                        ..Default::default()
6642                    }),
6643                ]);
6644                decl
6645            },
6646            result = Err(ErrorList::new(vec![
6647                Error::field_too_long(DeclType::OfferService, "source.child.name"),
6648                Error::field_too_long(DeclType::OfferService, "source_name"),
6649                Error::field_too_long(DeclType::OfferService, "target.child.name"),
6650                Error::field_too_long(DeclType::OfferService, "target_name"),
6651                Error::field_too_long(DeclType::OfferService, "target.collection.name"),
6652                Error::field_too_long(DeclType::OfferService, "target_name"),
6653                Error::field_too_long(DeclType::OfferProtocol, "source.child.name"),
6654                Error::field_too_long(DeclType::OfferProtocol, "source_name"),
6655                Error::field_too_long(DeclType::OfferProtocol, "target.child.name"),
6656                Error::field_too_long(DeclType::OfferProtocol, "target_name"),
6657                Error::field_too_long(DeclType::OfferProtocol, "target.collection.name"),
6658                Error::field_too_long(DeclType::OfferProtocol, "target_name"),
6659                Error::field_too_long(DeclType::OfferDirectory, "source.child.name"),
6660                Error::field_too_long(DeclType::OfferDirectory, "source_name"),
6661                Error::field_too_long(DeclType::OfferDirectory, "target.child.name"),
6662                Error::field_too_long(DeclType::OfferDirectory, "target_name"),
6663                Error::field_too_long(DeclType::OfferDirectory, "target.collection.name"),
6664                Error::field_too_long(DeclType::OfferDirectory, "target_name"),
6665                Error::field_too_long(DeclType::OfferStorage, "target.child.name"),
6666                Error::field_too_long(DeclType::OfferStorage, "target.collection.name"),
6667                Error::field_too_long(DeclType::OfferRunner, "source.child.name"),
6668                Error::field_too_long(DeclType::OfferRunner, "source_name"),
6669                Error::field_too_long(DeclType::OfferRunner, "target.collection.name"),
6670                Error::field_too_long(DeclType::OfferRunner, "target_name"),
6671                Error::field_too_long(DeclType::OfferResolver, "source.child.name"),
6672                Error::field_too_long(DeclType::OfferResolver, "source_name"),
6673                Error::field_too_long(DeclType::OfferResolver, "target.collection.name"),
6674                Error::field_too_long(DeclType::OfferResolver, "target_name"),
6675                Error::field_too_long(DeclType::OfferDictionary, "source.child.name"),
6676                Error::field_too_long(DeclType::OfferDictionary, "source_name"),
6677                Error::field_too_long(DeclType::OfferDictionary, "target.collection.name"),
6678                Error::field_too_long(DeclType::OfferDictionary, "target_name"),
6679            ])),
6680        },
6681        test_validate_offers_extraneous => {
6682            input = {
6683                let mut decl = new_component_decl();
6684                decl.offers = Some(vec![
6685                    fdecl::Offer::Service(fdecl::OfferService {
6686                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6687                            name: "logger".to_string(),
6688                            collection: Some("modular".to_string()),
6689                        })),
6690                        source_name: Some("fuchsia.logger.Log".to_string()),
6691                        target: Some(fdecl::Ref::Child(
6692                            fdecl::ChildRef {
6693                                name: "netstack".to_string(),
6694                                collection: Some("modular".to_string()),
6695                            }
6696                        )),
6697                        target_name: Some("fuchsia.logger.Log".to_string()),
6698                        ..Default::default()
6699                    }),
6700                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6701                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6702                            name: "logger".to_string(),
6703                            collection: Some("modular".to_string()),
6704                        })),
6705                        source_name: Some("fuchsia.logger.Log".to_string()),
6706                        target: Some(fdecl::Ref::Child(
6707                            fdecl::ChildRef {
6708                                name: "netstack".to_string(),
6709                                collection: Some("modular".to_string()),
6710                            }
6711                        )),
6712                        target_name: Some("fuchsia.logger.Log".to_string()),
6713                        dependency_type: Some(fdecl::DependencyType::Strong),
6714                        ..Default::default()
6715                    }),
6716                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6717                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6718                            name: "logger".to_string(),
6719                            collection: Some("modular".to_string()),
6720                        })),
6721                        source_name: Some("assets".to_string()),
6722                        target: Some(fdecl::Ref::Child(
6723                            fdecl::ChildRef {
6724                                name: "netstack".to_string(),
6725                                collection: Some("modular".to_string()),
6726                            }
6727                        )),
6728                        target_name: Some("assets".to_string()),
6729                        rights: Some(fio::Operations::CONNECT),
6730                        subdir: None,
6731                        dependency_type: Some(fdecl::DependencyType::Weak),
6732                        ..Default::default()
6733                    }),
6734                    fdecl::Offer::Storage(fdecl::OfferStorage {
6735                        source_name: Some("data".to_string()),
6736                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{ })),
6737                        target: Some(fdecl::Ref::Child(
6738                            fdecl::ChildRef {
6739                                name: "netstack".to_string(),
6740                                collection: Some("modular".to_string()),
6741                            }
6742                        )),
6743                        target_name: Some("data".to_string()),
6744                        ..Default::default()
6745                    }),
6746                    fdecl::Offer::Runner(fdecl::OfferRunner {
6747                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6748                            name: "logger".to_string(),
6749                            collection: Some("modular".to_string()),
6750                        })),
6751                        source_name: Some("elf".to_string()),
6752                        target: Some(fdecl::Ref::Child(
6753                            fdecl::ChildRef {
6754                                name: "netstack".to_string(),
6755                                collection: Some("modular".to_string()),
6756                            }
6757                        )),
6758                        target_name: Some("elf".to_string()),
6759                        ..Default::default()
6760                    }),
6761                    fdecl::Offer::Resolver(fdecl::OfferResolver {
6762                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6763                            name: "logger".to_string(),
6764                            collection: Some("modular".to_string()),
6765                        })),
6766                        source_name: Some("pkg".to_string()),
6767                        target: Some(fdecl::Ref::Child(
6768                            fdecl::ChildRef {
6769                                name: "netstack".to_string(),
6770                                collection: Some("modular".to_string()),
6771                            }
6772                        )),
6773                        target_name: Some("pkg".to_string()),
6774                        ..Default::default()
6775                    }),
6776                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6777                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6778                            name: "logger".to_string(),
6779                            collection: Some("modular".to_string()),
6780                        })),
6781                        source_name: Some("dict".to_string()),
6782                        target: Some(fdecl::Ref::Child(
6783                            fdecl::ChildRef {
6784                                name: "netstack".to_string(),
6785                                collection: Some("modular".to_string()),
6786                            }
6787                        )),
6788                        target_name: Some("dict".to_string()),
6789                        dependency_type: Some(fdecl::DependencyType::Strong),
6790                        ..Default::default()
6791                    }),
6792                ]);
6793                decl.capabilities = Some(vec![
6794                    fdecl::Capability::Protocol(fdecl::Protocol {
6795                        name: Some("fuchsia.logger.Log".to_string()),
6796                        source_path: Some("/svc/logger".to_string()),
6797                        ..Default::default()
6798                    }),
6799                    fdecl::Capability::Directory(fdecl::Directory {
6800                        name: Some("assets".to_string()),
6801                        source_path: Some("/data/assets".to_string()),
6802                        rights: Some(fio::Operations::CONNECT),
6803                        ..Default::default()
6804                    }),
6805                ]);
6806                decl
6807            },
6808            result = Err(ErrorList::new(vec![
6809                Error::extraneous_field(DeclType::OfferService, "source.child.collection"),
6810                Error::extraneous_field(DeclType::OfferService, "target.child.collection"),
6811                Error::extraneous_field(DeclType::OfferProtocol, "source.child.collection"),
6812                Error::extraneous_field(DeclType::OfferProtocol, "target.child.collection"),
6813                Error::extraneous_field(DeclType::OfferDirectory, "source.child.collection"),
6814                Error::extraneous_field(DeclType::OfferDirectory, "target.child.collection"),
6815                Error::extraneous_field(DeclType::OfferStorage, "target.child.collection"),
6816                Error::extraneous_field(DeclType::OfferRunner, "source.child.collection"),
6817                Error::extraneous_field(DeclType::OfferRunner, "target.child.collection"),
6818                Error::extraneous_field(DeclType::OfferResolver, "source.child.collection"),
6819                Error::extraneous_field(DeclType::OfferResolver, "target.child.collection"),
6820                Error::extraneous_field(DeclType::OfferDictionary, "source.child.collection"),
6821                Error::extraneous_field(DeclType::OfferDictionary, "target.child.collection"),
6822            ])),
6823        },
6824        test_validate_offers_invalid_filtered_service_fields => {
6825            input = {
6826                let mut decl = new_component_decl();
6827                decl.offers = Some(vec![
6828                    fdecl::Offer::Service(fdecl::OfferService {
6829                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6830                        source_name: Some("fuchsia.logger.Log".to_string()),
6831                        target: Some(fdecl::Ref::Child(
6832                            fdecl::ChildRef {
6833                                name: "logger".to_string(),
6834                                collection: None,
6835                            }
6836                        )),
6837                        target_name: Some("fuchsia.logger.Log".to_string()),
6838                        source_instance_filter: Some(vec![]),
6839                        ..Default::default()
6840                    }),
6841                    fdecl::Offer::Service(fdecl::OfferService {
6842                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6843                        source_name: Some("fuchsia.logger.Log".to_string()),
6844                        target: Some(fdecl::Ref::Child(
6845                            fdecl::ChildRef {
6846                                name: "logger".to_string(),
6847                                collection: None,
6848                            }
6849                        )),
6850                        target_name: Some("fuchsia.logger.Log2".to_string()),
6851                        source_instance_filter: Some(vec!["^badname".to_string()]),
6852                        ..Default::default()
6853                    }),
6854                    fdecl::Offer::Service(fdecl::OfferService {
6855                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6856                        source_name: Some("fuchsia.logger.Log".to_string()),
6857                        target: Some(fdecl::Ref::Child(
6858                            fdecl::ChildRef {
6859                                name: "logger".to_string(),
6860                                collection: None,
6861                            }
6862                        )),
6863                        target_name: Some("fuchsia.logger.Log1".to_string()),
6864                        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()}]),
6865                        ..Default::default()
6866                    }),
6867                    fdecl::Offer::Service(fdecl::OfferService {
6868                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6869                        source_name: Some("fuchsia.logger.Log".to_string()),
6870                        target: Some(fdecl::Ref::Child(
6871                            fdecl::ChildRef {
6872                                name: "logger".to_string(),
6873                                collection: None,
6874                            }
6875                        )),
6876                        target_name: Some("fuchsia.logger.Log3".to_string()),
6877                        renamed_instances: Some(vec![
6878                            fdecl::NameMapping {
6879                                source_name: "^badname".to_string(),
6880                                target_name: "^badname".to_string(),
6881                            }
6882                        ]),
6883                        ..Default::default()
6884                    })
6885                ]);
6886                decl.children = Some(vec![
6887                    fdecl::Child {
6888                        name: Some("logger".to_string()),
6889                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
6890                        startup: Some(fdecl::StartupMode::Lazy),
6891                        on_terminate: None,
6892                        environment: None,
6893                        ..Default::default()
6894                    },
6895                ]);
6896                decl
6897            },
6898            result = Err(ErrorList::new(vec![
6899                Error::invalid_field(DeclType::OfferService, "source_instance_filter"),
6900                Error::invalid_field(DeclType::OfferService, "source_instance_filter"),
6901                Error::invalid_field(DeclType::OfferService, "renamed_instances"),
6902                Error::invalid_field(DeclType::OfferService, "renamed_instances.source_name"),
6903                Error::invalid_field(DeclType::OfferService, "renamed_instances.target_name"),
6904            ])),
6905        },
6906        test_validate_offers_invalid_identifiers => {
6907            input = {
6908                let mut decl = new_component_decl();
6909                decl.offers = Some(vec![
6910                    fdecl::Offer::Service(fdecl::OfferService {
6911                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6912                            name: "^bad".to_string(),
6913                            collection: None,
6914                        })),
6915                        source_name: Some("foo/".to_string()),
6916                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6917                            name: "%bad".to_string(),
6918                            collection: None,
6919                        })),
6920                        target_name: Some("/".to_string()),
6921                        ..Default::default()
6922                    }),
6923                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6924                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6925                            name: "^bad".to_string(),
6926                            collection: None,
6927                        })),
6928                        source_name: Some("foo/".to_string()),
6929                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6930                            name: "%bad".to_string(),
6931                            collection: None,
6932                        })),
6933                        target_name: Some("/".to_string()),
6934                        dependency_type: Some(fdecl::DependencyType::Strong),
6935                        ..Default::default()
6936                    }),
6937                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6938                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6939                            name: "^bad".to_string(),
6940                            collection: None,
6941                        })),
6942                        source_name: Some("foo/".to_string()),
6943                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6944                            name: "%bad".to_string(),
6945                            collection: None,
6946                        })),
6947                        target_name: Some("/".to_string()),
6948                        rights: Some(fio::Operations::CONNECT),
6949                        subdir: Some("/foo".to_string()),
6950                        dependency_type: Some(fdecl::DependencyType::Strong),
6951                        ..Default::default()
6952                    }),
6953                    fdecl::Offer::Runner(fdecl::OfferRunner {
6954                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6955                            name: "^bad".to_string(),
6956                            collection: None,
6957                        })),
6958                        source_name: Some("/path".to_string()),
6959                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6960                            name: "%bad".to_string(),
6961                            collection: None,
6962                        })),
6963                        target_name: Some("elf!".to_string()),
6964                        ..Default::default()
6965                    }),
6966                    fdecl::Offer::Resolver(fdecl::OfferResolver {
6967                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6968                            name: "^bad".to_string(),
6969                            collection: None,
6970                        })),
6971                        source_name: Some("/path".to_string()),
6972                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6973                            name: "%bad".to_string(),
6974                            collection: None,
6975                        })),
6976                        target_name: Some("pkg!".to_string()),
6977                        ..Default::default()
6978                    }),
6979                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6980                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6981                            name: "^bad".to_string(),
6982                            collection: None,
6983                        })),
6984                        source_name: Some("/path".to_string()),
6985                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6986                            name: "%bad".to_string(),
6987                            collection: None,
6988                        })),
6989                        target_name: Some("pkg!".to_string()),
6990                        dependency_type: Some(fdecl::DependencyType::Strong),
6991                        ..Default::default()
6992                    }),
6993                ]);
6994                decl
6995            },
6996            result = Err(ErrorList::new(vec![
6997                Error::invalid_field(DeclType::OfferService, "source.child.name"),
6998                Error::invalid_field(DeclType::OfferService, "source_name"),
6999                Error::invalid_field(DeclType::OfferService, "target.child.name"),
7000                Error::invalid_field(DeclType::OfferService, "target_name"),
7001                Error::invalid_field(DeclType::OfferProtocol, "source.child.name"),
7002                Error::invalid_field(DeclType::OfferProtocol, "source_name"),
7003                Error::invalid_field(DeclType::OfferProtocol, "target.child.name"),
7004                Error::invalid_field(DeclType::OfferProtocol, "target_name"),
7005                Error::invalid_field(DeclType::OfferDirectory, "source.child.name"),
7006                Error::invalid_field(DeclType::OfferDirectory, "source_name"),
7007                Error::invalid_field(DeclType::OfferDirectory, "target.child.name"),
7008                Error::invalid_field(DeclType::OfferDirectory, "target_name"),
7009                Error::invalid_field(DeclType::OfferDirectory, "subdir"),
7010                Error::invalid_field(DeclType::OfferRunner, "source.child.name"),
7011                Error::invalid_field(DeclType::OfferRunner, "source_name"),
7012                Error::invalid_field(DeclType::OfferRunner, "target.child.name"),
7013                Error::invalid_field(DeclType::OfferRunner, "target_name"),
7014                Error::invalid_field(DeclType::OfferResolver, "source.child.name"),
7015                Error::invalid_field(DeclType::OfferResolver, "source_name"),
7016                Error::invalid_field(DeclType::OfferResolver, "target.child.name"),
7017                Error::invalid_field(DeclType::OfferResolver, "target_name"),
7018                Error::invalid_field(DeclType::OfferDictionary, "source.child.name"),
7019                Error::invalid_field(DeclType::OfferDictionary, "source_name"),
7020                Error::invalid_field(DeclType::OfferDictionary, "target.child.name"),
7021                Error::invalid_field(DeclType::OfferDictionary, "target_name"),
7022            ])),
7023        },
7024        test_validate_offers_target_equals_source => {
7025            input = {
7026                let mut decl = new_component_decl();
7027                decl.offers = Some(vec![
7028                    fdecl::Offer::Service(fdecl::OfferService {
7029                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7030                            name: "logger".to_string(),
7031                            collection: None,
7032                        })),
7033                        source_name: Some("logger".to_string()),
7034                        target: Some(fdecl::Ref::Child(
7035                        fdecl::ChildRef {
7036                            name: "logger".to_string(),
7037                            collection: None,
7038                        }
7039                        )),
7040                        target_name: Some("logger".to_string()),
7041                        ..Default::default()
7042                    }),
7043                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7044                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7045                            name: "logger".to_string(),
7046                            collection: None,
7047                        })),
7048                        source_name: Some("legacy_logger".to_string()),
7049                        target: Some(fdecl::Ref::Child(
7050                        fdecl::ChildRef {
7051                            name: "logger".to_string(),
7052                            collection: None,
7053                        }
7054                        )),
7055                        target_name: Some("weak_legacy_logger".to_string()),
7056                        dependency_type: Some(fdecl::DependencyType::Weak),
7057                        ..Default::default()
7058                    }),
7059                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7060                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7061                            name: "logger".to_string(),
7062                            collection: None,
7063                        })),
7064                        source_name: Some("legacy_logger".to_string()),
7065                        target: Some(fdecl::Ref::Child(
7066                        fdecl::ChildRef {
7067                            name: "logger".to_string(),
7068                            collection: None,
7069                        }
7070                        )),
7071                        target_name: Some("strong_legacy_logger".to_string()),
7072                        dependency_type: Some(fdecl::DependencyType::Strong),
7073                        ..Default::default()
7074                    }),
7075                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7076                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7077                            name: "logger".to_string(),
7078                            collection: None,
7079                        })),
7080                        source_name: Some("assets".to_string()),
7081                        target: Some(fdecl::Ref::Child(
7082                        fdecl::ChildRef {
7083                            name: "logger".to_string(),
7084                            collection: None,
7085                        }
7086                        )),
7087                        target_name: Some("assets".to_string()),
7088                        rights: Some(fio::Operations::CONNECT),
7089                        subdir: None,
7090                        dependency_type: Some(fdecl::DependencyType::Strong),
7091                        ..Default::default()
7092                    }),
7093                    fdecl::Offer::Runner(fdecl::OfferRunner {
7094                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7095                            name: "logger".to_string(),
7096                            collection: None,
7097                        })),
7098                        source_name: Some("web".to_string()),
7099                        target: Some(fdecl::Ref::Child(
7100                        fdecl::ChildRef {
7101                            name: "logger".to_string(),
7102                            collection: None,
7103                        }
7104                        )),
7105                        target_name: Some("web".to_string()),
7106                        ..Default::default()
7107                    }),
7108                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7109                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7110                            name: "logger".to_string(),
7111                            collection: None,
7112                        })),
7113                        source_name: Some("pkg".to_string()),
7114                        target: Some(fdecl::Ref::Child(
7115                        fdecl::ChildRef {
7116                            name: "logger".to_string(),
7117                            collection: None,
7118                        }
7119                        )),
7120                        target_name: Some("pkg".to_string()),
7121                        ..Default::default()
7122                    }),
7123                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7124                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7125                            name: "logger".to_string(),
7126                            collection: None,
7127                        })),
7128                        source_name: Some("dict".to_string()),
7129                        target: Some(fdecl::Ref::Child(
7130                        fdecl::ChildRef {
7131                            name: "logger".to_string(),
7132                            collection: None,
7133                        }
7134                        )),
7135                        target_name: Some("dict".to_string()),
7136                        dependency_type: Some(fdecl::DependencyType::Strong),
7137                        ..Default::default()
7138                    }),
7139                ]);
7140                decl.children = Some(vec![fdecl::Child{
7141                    name: Some("logger".to_string()),
7142                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
7143                    startup: Some(fdecl::StartupMode::Lazy),
7144                    on_terminate: None,
7145                    environment: None,
7146                    ..Default::default()
7147                }]);
7148                decl
7149            },
7150            result = Err(ErrorList::new(vec![
7151                Error::dependency_cycle("{{child logger -> child logger}}"),
7152            ])),
7153        },
7154        test_validate_offers_storage_target_equals_source => {
7155            input = fdecl::Component {
7156                offers: Some(vec![
7157                    fdecl::Offer::Storage(fdecl::OfferStorage {
7158                        source_name: Some("data".to_string()),
7159                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef { })),
7160                        target: Some(fdecl::Ref::Child(
7161                            fdecl::ChildRef {
7162                                name: "logger".to_string(),
7163                                collection: None,
7164                            }
7165                        )),
7166                        target_name: Some("data".to_string()),
7167                        ..Default::default()
7168                    })
7169                ]),
7170                capabilities: Some(vec![
7171                    fdecl::Capability::Storage(fdecl::Storage {
7172                        name: Some("data".to_string()),
7173                        backing_dir: Some("minfs".to_string()),
7174                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7175                            name: "logger".to_string(),
7176                            collection: None,
7177                        })),
7178                        subdir: None,
7179                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
7180                        ..Default::default()
7181                    }),
7182                ]),
7183                children: Some(vec![
7184                    fdecl::Child {
7185                        name: Some("logger".to_string()),
7186                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
7187                        startup: Some(fdecl::StartupMode::Lazy),
7188                        on_terminate: None,
7189                        environment: None,
7190                        ..Default::default()
7191                    },
7192                ]),
7193                ..new_component_decl()
7194            },
7195            result = Err(ErrorList::new(vec![
7196                Error::dependency_cycle("{{child logger -> capability data -> child logger}}"),
7197            ])),
7198        },
7199        test_validate_offers_invalid_child => {
7200            input = {
7201                let mut decl = new_component_decl();
7202                decl.offers = Some(vec![
7203                    fdecl::Offer::Service(fdecl::OfferService {
7204                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7205                            name: "logger".to_string(),
7206                            collection: None,
7207                        })),
7208                        source_name: Some("fuchsia.logger.Log".to_string()),
7209                        target: Some(fdecl::Ref::Child(
7210                        fdecl::ChildRef {
7211                            name: "netstack".to_string(),
7212                            collection: None,
7213                        }
7214                        )),
7215                        target_name: Some("fuchsia.logger.Log".to_string()),
7216                        ..Default::default()
7217                    }),
7218                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7219                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7220                            name: "logger".to_string(),
7221                            collection: None,
7222                        })),
7223                        source_name: Some("fuchsia.logger.LegacyLog".to_string()),
7224                        target: Some(fdecl::Ref::Child(
7225                        fdecl::ChildRef {
7226                            name: "netstack".to_string(),
7227                            collection: None,
7228                        }
7229                        )),
7230                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7231                        dependency_type: Some(fdecl::DependencyType::Strong),
7232                        ..Default::default()
7233                    }),
7234                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7235                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7236                            name: "logger".to_string(),
7237                            collection: None,
7238                        })),
7239                        source_name: Some("assets".to_string()),
7240                        target: Some(fdecl::Ref::Collection(
7241                        fdecl::CollectionRef { name: "modular".to_string() }
7242                        )),
7243                        target_name: Some("assets".to_string()),
7244                        rights: Some(fio::Operations::CONNECT),
7245                        subdir: None,
7246                        dependency_type: Some(fdecl::DependencyType::Weak),
7247                        ..Default::default()
7248                    }),
7249                ]);
7250                decl.capabilities = Some(vec![
7251                    fdecl::Capability::Storage(fdecl::Storage {
7252                        name: Some("memfs".to_string()),
7253                        backing_dir: Some("memfs".to_string()),
7254                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7255                            name: "logger".to_string(),
7256                            collection: None,
7257                        })),
7258                        subdir: None,
7259                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
7260                        ..Default::default()
7261                    }),
7262                ]);
7263                decl.children = Some(vec![
7264                    fdecl::Child {
7265                        name: Some("netstack".to_string()),
7266                        url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
7267                        startup: Some(fdecl::StartupMode::Lazy),
7268                        on_terminate: None,
7269                        environment: None,
7270                        ..Default::default()
7271                    },
7272                ]);
7273                decl.collections = Some(vec![
7274                    fdecl::Collection {
7275                        name: Some("modular".to_string()),
7276                        durability: Some(fdecl::Durability::Transient),
7277                        environment: None,
7278                        allowed_offers: Some(fdecl::AllowedOffers::StaticAndDynamic),
7279                        allow_long_names: None,
7280                        ..Default::default()
7281                    },
7282                ]);
7283                decl
7284            },
7285            result = Err(ErrorList::new(vec![
7286                Error::invalid_child(DeclType::Storage, "source", "logger"),
7287                Error::invalid_child(DeclType::OfferService, "source", "logger"),
7288                Error::invalid_child(DeclType::OfferProtocol, "source", "logger"),
7289                Error::invalid_child(DeclType::OfferDirectory, "source", "logger"),
7290            ])),
7291        },
7292        test_validate_offers_invalid_source_capability => {
7293            input = {
7294                fdecl::Component {
7295                    offers: Some(vec![
7296                        fdecl::Offer::Protocol(fdecl::OfferProtocol {
7297                            source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
7298                                name: "this-storage-doesnt-exist".to_string(),
7299                            })),
7300                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
7301                            target: Some(fdecl::Ref::Child(
7302                            fdecl::ChildRef {
7303                                name: "netstack".to_string(),
7304                                collection: None,
7305                            }
7306                            )),
7307                            target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
7308                            dependency_type: Some(fdecl::DependencyType::Strong),
7309                            ..Default::default()
7310                        }),
7311                    ]),
7312                    ..new_component_decl()
7313                }
7314            },
7315            result = Err(ErrorList::new(vec![
7316                Error::invalid_capability(DeclType::OfferProtocol, "source", "this-storage-doesnt-exist"),
7317                Error::invalid_child(DeclType::OfferProtocol, "target", "netstack"),
7318            ])),
7319        },
7320        test_validate_offers_target => {
7321            input = {
7322                let mut decl = new_component_decl();
7323                decl.offers = Some(vec![
7324                    fdecl::Offer::Service(fdecl::OfferService {
7325                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
7326                            name: "modular".into()
7327                        })),
7328                        source_name: Some("logger".to_string()),
7329                        target: Some(fdecl::Ref::Child(
7330                        fdecl::ChildRef {
7331                            name: "netstack".to_string(),
7332                            collection: None,
7333                        }
7334                        )),
7335                        target_name: Some("fuchsia.logger.Log".to_string()),
7336                        ..Default::default()
7337                    }),
7338                    fdecl::Offer::Service(fdecl::OfferService {
7339                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
7340                            name: "modular".into()
7341                        })),
7342                        source_name: Some("logger".to_string()),
7343                        target: Some(fdecl::Ref::Child(
7344                        fdecl::ChildRef {
7345                            name: "netstack".to_string(),
7346                            collection: None,
7347                        }
7348                        )),
7349                        target_name: Some("fuchsia.logger.Log".to_string()),
7350                        ..Default::default()
7351                    }),
7352                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7353                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7354                        source_name: Some("fuchsia.logger.LegacyLog".to_string()),
7355                        target: Some(fdecl::Ref::Child(
7356                        fdecl::ChildRef {
7357                            name: "netstack".to_string(),
7358                            collection: None,
7359                        }
7360                        )),
7361                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7362                        dependency_type: Some(fdecl::DependencyType::Strong),
7363                        ..Default::default()
7364                    }),
7365                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7366                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7367                        source_name: Some("fuchsia.logger.LegacyLog".to_string()),
7368                        target: Some(fdecl::Ref::Child(
7369                        fdecl::ChildRef {
7370                            name: "netstack".to_string(),
7371                            collection: None,
7372                        }
7373                        )),
7374                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7375                        dependency_type: Some(fdecl::DependencyType::Strong),
7376                        ..Default::default()
7377                    }),
7378                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7379                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7380                        source_name: Some("assets".to_string()),
7381                        target: Some(fdecl::Ref::Collection(
7382                        fdecl::CollectionRef { name: "modular".to_string() }
7383                        )),
7384                        target_name: Some("assets".to_string()),
7385                        rights: Some(fio::Operations::CONNECT),
7386                        subdir: None,
7387                        dependency_type: Some(fdecl::DependencyType::Strong),
7388                        ..Default::default()
7389                    }),
7390                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7391                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7392                        source_name: Some("assets".to_string()),
7393                        target: Some(fdecl::Ref::Collection(
7394                        fdecl::CollectionRef { name: "modular".to_string() }
7395                        )),
7396                        target_name: Some("assets".to_string()),
7397                        rights: Some(fio::Operations::CONNECT),
7398                        subdir: None,
7399                        dependency_type: Some(fdecl::DependencyType::Weak),
7400                        ..Default::default()
7401                    }),
7402                    fdecl::Offer::Storage(fdecl::OfferStorage {
7403                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7404                        source_name: Some("data".to_string()),
7405                        target: Some(fdecl::Ref::Collection(
7406                        fdecl::CollectionRef { name: "modular".to_string() }
7407                        )),
7408                        target_name: Some("data".to_string()),
7409                        ..Default::default()
7410                    }),
7411                    fdecl::Offer::Storage(fdecl::OfferStorage {
7412                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7413                        source_name: Some("data".to_string()),
7414                        target: Some(fdecl::Ref::Collection(
7415                        fdecl::CollectionRef { name: "modular".to_string() }
7416                        )),
7417                        target_name: Some("data".to_string()),
7418                        ..Default::default()
7419                    }),
7420                    fdecl::Offer::Runner(fdecl::OfferRunner {
7421                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7422                        source_name: Some("elf".to_string()),
7423                        target: Some(fdecl::Ref::Collection(
7424                        fdecl::CollectionRef { name: "modular".to_string() }
7425                        )),
7426                        target_name: Some("duplicated".to_string()),
7427                        ..Default::default()
7428                    }),
7429                    fdecl::Offer::Runner(fdecl::OfferRunner {
7430                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7431                        source_name: Some("elf".to_string()),
7432                        target: Some(fdecl::Ref::Collection(
7433                        fdecl::CollectionRef { name: "modular".to_string() }
7434                        )),
7435                        target_name: Some("duplicated".to_string()),
7436                        ..Default::default()
7437                    }),
7438                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7439                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7440                        source_name: Some("pkg".to_string()),
7441                        target: Some(fdecl::Ref::Collection(
7442                        fdecl::CollectionRef { name: "modular".to_string() }
7443                        )),
7444                        target_name: Some("duplicated".to_string()),
7445                        ..Default::default()
7446                    }),
7447                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
7448                        source_name: Some("started".to_string()),
7449                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7450                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "netstack".to_string(), collection: None})),
7451                        target_name: Some("started".to_string()),
7452                        ..Default::default()
7453                    }),
7454                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
7455                        source_name: Some("started".to_string()),
7456                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7457                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "netstack".to_string(), collection: None})),
7458                        target_name: Some("started".to_string()),
7459                        ..Default::default()
7460                    }),
7461                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7462                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7463                        source_name: Some("a".to_string()),
7464                        target: Some(fdecl::Ref::Collection(
7465                            fdecl::CollectionRef { name: "modular".to_string() }
7466                        )),
7467                        target_name: Some("dict".to_string()),
7468                        dependency_type: Some(fdecl::DependencyType::Strong),
7469                        ..Default::default()
7470                    }),
7471                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7472                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7473                        source_name: Some("b".to_string()),
7474                        target: Some(fdecl::Ref::Collection(
7475                            fdecl::CollectionRef { name: "modular".to_string() }
7476                        )),
7477                        target_name: Some("dict".to_string()),
7478                        dependency_type: Some(fdecl::DependencyType::Strong),
7479                        ..Default::default()
7480                    }),
7481                ]);
7482                decl.children = Some(vec![
7483                    fdecl::Child{
7484                        name: Some("netstack".to_string()),
7485                        url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
7486                        startup: Some(fdecl::StartupMode::Eager),
7487                        on_terminate: None,
7488                        environment: None,
7489                        ..Default::default()
7490                    },
7491                ]);
7492                decl.collections = Some(vec![
7493                    fdecl::Collection{
7494                        name: Some("modular".to_string()),
7495                        durability: Some(fdecl::Durability::Transient),
7496                        environment: None,
7497                        allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7498                        allow_long_names: None,
7499                        ..Default::default()
7500                    },
7501                ]);
7502                decl
7503            },
7504            result = Err(ErrorList::new(vec![
7505                // Duplicate services are allowed, for aggregation.
7506                Error::duplicate_field(DeclType::OfferProtocol, "target_name", "fuchsia.logger.LegacyLog"),
7507                Error::duplicate_field(DeclType::OfferDirectory, "target_name", "assets"),
7508                Error::duplicate_field(DeclType::OfferStorage, "target_name", "data"),
7509                Error::duplicate_field(DeclType::OfferRunner, "target_name", "duplicated"),
7510                Error::duplicate_field(DeclType::OfferResolver, "target_name", "duplicated"),
7511                Error::duplicate_field(DeclType::OfferEventStream, "target_name", "started"),
7512                Error::duplicate_field(DeclType::OfferDictionary, "target_name", "dict"),
7513            ])),
7514        },
7515        test_validate_offers_target_invalid => {
7516            input = {
7517                let mut decl = new_component_decl();
7518                decl.offers = Some(vec![
7519                    fdecl::Offer::Service(fdecl::OfferService {
7520                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7521                        source_name: Some("logger".to_string()),
7522                        target: Some(fdecl::Ref::Child(
7523                        fdecl::ChildRef {
7524                            name: "netstack".to_string(),
7525                            collection: None,
7526                        }
7527                        )),
7528                        target_name: Some("fuchsia.logger.Log".to_string()),
7529                        ..Default::default()
7530                    }),
7531                    fdecl::Offer::Service(fdecl::OfferService {
7532                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7533                        source_name: Some("logger".to_string()),
7534                        target: Some(fdecl::Ref::Collection(
7535                        fdecl::CollectionRef { name: "modular".to_string(), }
7536                        )),
7537                        target_name: Some("fuchsia.logger.Log".to_string()),
7538                        ..Default::default()
7539                    }),
7540                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7541                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7542                        source_name: Some("legacy_logger".to_string()),
7543                        target: Some(fdecl::Ref::Child(
7544                        fdecl::ChildRef {
7545                            name: "netstack".to_string(),
7546                            collection: None,
7547                        }
7548                        )),
7549                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7550                        dependency_type: Some(fdecl::DependencyType::Weak),
7551                        ..Default::default()
7552                    }),
7553                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7554                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7555                        source_name: Some("legacy_logger".to_string()),
7556                        target: Some(fdecl::Ref::Collection(
7557                        fdecl::CollectionRef { name: "modular".to_string(), }
7558                        )),
7559                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7560                        dependency_type: Some(fdecl::DependencyType::Strong),
7561                        ..Default::default()
7562                    }),
7563                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7564                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7565                        source_name: Some("assets".to_string()),
7566                        target: Some(fdecl::Ref::Child(
7567                        fdecl::ChildRef {
7568                            name: "netstack".to_string(),
7569                            collection: None,
7570                        }
7571                        )),
7572                        target_name: Some("data".to_string()),
7573                        rights: Some(fio::Operations::CONNECT),
7574                        subdir: None,
7575                        dependency_type: Some(fdecl::DependencyType::Strong),
7576                        ..Default::default()
7577                    }),
7578                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7579                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7580                        source_name: Some("assets".to_string()),
7581                        target: Some(fdecl::Ref::Collection(
7582                        fdecl::CollectionRef { name: "modular".to_string(), }
7583                        )),
7584                        target_name: Some("data".to_string()),
7585                        rights: Some(fio::Operations::CONNECT),
7586                        subdir: None,
7587                        dependency_type: Some(fdecl::DependencyType::Weak),
7588                        ..Default::default()
7589                    }),
7590                    fdecl::Offer::Storage(fdecl::OfferStorage {
7591                        source_name: Some("data".to_string()),
7592                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7593                        target: Some(fdecl::Ref::Child(
7594                            fdecl::ChildRef {
7595                                name: "netstack".to_string(),
7596                                collection: None,
7597                            }
7598                        )),
7599                        target_name: Some("data".to_string()),
7600                        ..Default::default()
7601                    }),
7602                    fdecl::Offer::Storage(fdecl::OfferStorage {
7603                        source_name: Some("data".to_string()),
7604                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7605                        target: Some(fdecl::Ref::Collection(
7606                            fdecl::CollectionRef { name: "modular".to_string(), }
7607                        )),
7608                        target_name: Some("data".to_string()),
7609                        ..Default::default()
7610                    }),
7611                    fdecl::Offer::Runner(fdecl::OfferRunner {
7612                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7613                        source_name: Some("elf".to_string()),
7614                        target: Some(fdecl::Ref::Child(
7615                            fdecl::ChildRef {
7616                                name: "netstack".to_string(),
7617                                collection: None,
7618                            }
7619                        )),
7620                        target_name: Some("elf".to_string()),
7621                        ..Default::default()
7622                    }),
7623                    fdecl::Offer::Runner(fdecl::OfferRunner {
7624                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7625                        source_name: Some("elf".to_string()),
7626                        target: Some(fdecl::Ref::Collection(
7627                        fdecl::CollectionRef { name: "modular".to_string(), }
7628                        )),
7629                        target_name: Some("elf".to_string()),
7630                        ..Default::default()
7631                    }),
7632                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7633                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7634                        source_name: Some("pkg".to_string()),
7635                        target: Some(fdecl::Ref::Child(
7636                            fdecl::ChildRef {
7637                                name: "netstack".to_string(),
7638                                collection: None,
7639                            }
7640                        )),
7641                        target_name: Some("pkg".to_string()),
7642                        ..Default::default()
7643                    }),
7644                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7645                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7646                        source_name: Some("pkg".to_string()),
7647                        target: Some(fdecl::Ref::Collection(
7648                        fdecl::CollectionRef { name: "modular".to_string(), }
7649                        )),
7650                        target_name: Some("pkg".to_string()),
7651                        ..Default::default()
7652                    }),
7653                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7654                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7655                        source_name: Some("pkg".to_string()),
7656                        target: Some(fdecl::Ref::Child(
7657                            fdecl::ChildRef {
7658                                name: "netstack".to_string(),
7659                                collection: None,
7660                            }
7661                        )),
7662                        target_name: Some("pkg".to_string()),
7663                        dependency_type: Some(fdecl::DependencyType::Strong),
7664                        ..Default::default()
7665                    }),
7666                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7667                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7668                        source_name: Some("pkg".to_string()),
7669                        target: Some(fdecl::Ref::Collection(
7670                        fdecl::CollectionRef { name: "modular".to_string(), }
7671                        )),
7672                        target_name: Some("pkg".to_string()),
7673                        dependency_type: Some(fdecl::DependencyType::Strong),
7674                        ..Default::default()
7675                    }),
7676                ]);
7677                decl
7678            },
7679            result = Err(ErrorList::new(vec![
7680                Error::invalid_child(DeclType::OfferService, "target", "netstack"),
7681                Error::invalid_collection(DeclType::OfferService, "target", "modular"),
7682                Error::invalid_child(DeclType::OfferProtocol, "target", "netstack"),
7683                Error::invalid_collection(DeclType::OfferProtocol, "target", "modular"),
7684                Error::invalid_child(DeclType::OfferDirectory, "target", "netstack"),
7685                Error::invalid_collection(DeclType::OfferDirectory, "target", "modular"),
7686                Error::invalid_child(DeclType::OfferStorage, "target", "netstack"),
7687                Error::invalid_collection(DeclType::OfferStorage, "target", "modular"),
7688                Error::invalid_child(DeclType::OfferRunner, "target", "netstack"),
7689                Error::invalid_collection(DeclType::OfferRunner, "target", "modular"),
7690                Error::invalid_child(DeclType::OfferResolver, "target", "netstack"),
7691                Error::invalid_collection(DeclType::OfferResolver, "target", "modular"),
7692                Error::invalid_child(DeclType::OfferDictionary, "target", "netstack"),
7693                Error::invalid_collection(DeclType::OfferDictionary, "target", "modular"),
7694            ])),
7695        },
7696        test_validate_offers_target_dictionary => {
7697            input = fdecl::Component {
7698                offers: Some(vec![
7699                    // Offer to static dictionary is ok
7700                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7701                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7702                        source_name: Some("p".to_string()),
7703                        target: Some(fdecl::Ref::Capability(
7704                            fdecl::CapabilityRef {
7705                                name: "dict".into(),
7706                            },
7707                        )),
7708                        target_name: Some("p".into()),
7709                        dependency_type: Some(fdecl::DependencyType::Strong),
7710                        ..Default::default()
7711                    }),
7712                    // Offer to dynamic dictionary is forbidden
7713                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7714                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7715                        source_name: Some("p".to_string()),
7716                        target: Some(fdecl::Ref::Capability(
7717                            fdecl::CapabilityRef {
7718                                name: "dynamic".into(),
7719                            },
7720                        )),
7721                        target_name: Some("p".into()),
7722                        dependency_type: Some(fdecl::DependencyType::Strong),
7723                        ..Default::default()
7724                    }),
7725                ]),
7726                capabilities: Some(vec![
7727                    fdecl::Capability::Dictionary(fdecl::Dictionary {
7728                        name: Some("dict".into()),
7729                        ..Default::default()
7730                    }),
7731                    fdecl::Capability::Dictionary(fdecl::Dictionary {
7732                        name: Some("dynamic".into()),
7733                        source_path: Some("/out/dir".into()),
7734                        ..Default::default()
7735                    }),
7736                ]),
7737                ..Default::default()
7738            },
7739            result = Err(ErrorList::new(vec![
7740                Error::invalid_field(DeclType::OfferProtocol, "target"),
7741            ])),
7742        },
7743        test_validate_offers_invalid_source_collection => {
7744            input = {
7745                let mut decl = new_component_decl();
7746                decl.collections = Some(vec![
7747                    fdecl::Collection {
7748                        name: Some("col".to_string()),
7749                        durability: Some(fdecl::Durability::Transient),
7750                        allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7751                        allow_long_names: None,
7752                        ..Default::default()
7753                    }
7754                ]);
7755                decl.children = Some(vec![
7756                    fdecl::Child {
7757                        name: Some("child".to_string()),
7758                        url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7759                        startup: Some(fdecl::StartupMode::Lazy),
7760                        on_terminate: None,
7761                        ..Default::default()
7762                    }
7763                ]);
7764                decl.offers = Some(vec![
7765                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7766                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7767                        source_name: Some("a".to_string()),
7768                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7769                        target_name: Some("a".to_string()),
7770                        dependency_type: Some(fdecl::DependencyType::Strong),
7771                        ..Default::default()
7772                    }),
7773                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7774                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7775                        source_name: Some("b".to_string()),
7776                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7777                        target_name: Some("b".to_string()),
7778                        rights: Some(fio::Operations::CONNECT),
7779                        subdir: None,
7780                        dependency_type: Some(fdecl::DependencyType::Strong),
7781                        ..Default::default()
7782                    }),
7783                    fdecl::Offer::Storage(fdecl::OfferStorage {
7784                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7785                        source_name: Some("c".to_string()),
7786                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7787                        target_name: Some("c".to_string()),
7788                        ..Default::default()
7789                    }),
7790                    fdecl::Offer::Runner(fdecl::OfferRunner {
7791                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7792                        source_name: Some("d".to_string()),
7793                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7794                        target_name: Some("d".to_string()),
7795                        ..Default::default()
7796                    }),
7797                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7798                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7799                        source_name: Some("e".to_string()),
7800                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7801                        target_name: Some("e".to_string()),
7802                        ..Default::default()
7803                    }),
7804                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7805                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7806                        source_name: Some("f".to_string()),
7807                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7808                        target_name: Some("f".to_string()),
7809                        dependency_type: Some(fdecl::DependencyType::Strong),
7810                        ..Default::default()
7811                    }),
7812                ]);
7813                decl
7814            },
7815            result = Err(ErrorList::new(vec![
7816                Error::invalid_field(DeclType::OfferProtocol, "source"),
7817                Error::invalid_field(DeclType::OfferDirectory, "source"),
7818                Error::invalid_field(DeclType::OfferStorage, "source"),
7819                Error::invalid_field(DeclType::OfferRunner, "source"),
7820                Error::invalid_field(DeclType::OfferResolver, "source"),
7821                Error::invalid_field(DeclType::OfferDictionary, "source"),
7822            ])),
7823        },
7824        test_validate_offers_source_collection => {
7825            input = {
7826                let mut decl = new_component_decl();
7827                decl.collections = Some(vec![
7828                    fdecl::Collection {
7829                        name: Some("col".to_string()),
7830                        durability: Some(fdecl::Durability::Transient),
7831                        allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7832                        allow_long_names: None,
7833                        ..Default::default()
7834                    }
7835                ]);
7836                decl.children = Some(vec![
7837                    fdecl::Child {
7838                        name: Some("child".to_string()),
7839                        url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7840                        startup: Some(fdecl::StartupMode::Lazy),
7841                        on_terminate: None,
7842                        ..Default::default()
7843                    }
7844                ]);
7845                decl.offers = Some(vec![
7846                    fdecl::Offer::Service(fdecl::OfferService {
7847                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7848                        source_name: Some("a".to_string()),
7849                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7850                        target_name: Some("a".to_string()),
7851                        ..Default::default()
7852                    })
7853                ]);
7854                decl
7855            },
7856            result = Ok(()),
7857        },
7858        test_validate_offers_invalid_capability_from_self => {
7859            input = {
7860                let mut decl = new_component_decl();
7861                decl.children = Some(vec![
7862                    fdecl::Child {
7863                        name: Some("child".to_string()),
7864                        url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7865                        startup: Some(fdecl::StartupMode::Lazy),
7866                        ..Default::default()
7867                    }
7868                ]);
7869                decl.offers = Some(vec![
7870                    fdecl::Offer::Service(fdecl::OfferService {
7871                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7872                        source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7873                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7874                            name: "child".into(),
7875                            collection: None
7876                        })),
7877                        target_name: Some("foo".into()),
7878                        ..Default::default()
7879                    }),
7880                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7881                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7882                        source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7883                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7884                            name: "child".into(),
7885                            collection: None
7886                        })),
7887                        target_name: Some("bar".into()),
7888                        dependency_type: Some(fdecl::DependencyType::Strong),
7889                        ..Default::default()
7890                    }),
7891                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7892                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7893                        source_name: Some("dir".into()),
7894                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7895                            name: "child".into(),
7896                            collection: None
7897                        })),
7898                        target_name: Some("assets".into()),
7899                        dependency_type: Some(fdecl::DependencyType::Strong),
7900                        ..Default::default()
7901                    }),
7902                    fdecl::Offer::Runner(fdecl::OfferRunner {
7903                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7904                        source_name: Some("source_elf".into()),
7905                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7906                            name: "child".into(),
7907                            collection: None
7908                        })),
7909                        target_name: Some("elf".into()),
7910                        ..Default::default()
7911                    }),
7912                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7913                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7914                        source_name: Some("source_pkg".into()),
7915                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7916                            name: "child".into(),
7917                            collection: None
7918                        })),
7919                        target_name: Some("pkg".into()),
7920                        ..Default::default()
7921                    }),
7922                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7923                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7924                        source_name: Some("source_dict".into()),
7925                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7926                            name: "child".into(),
7927                            collection: None
7928                        })),
7929                        target_name: Some("dict".into()),
7930                        dependency_type: Some(fdecl::DependencyType::Strong),
7931                        ..Default::default()
7932                    }),
7933                    fdecl::Offer::Storage(fdecl::OfferStorage {
7934                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7935                        source_name: Some("source_storage".into()),
7936                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7937                            name: "child".into(),
7938                            collection: None
7939                        })),
7940                        target_name: Some("storage".into()),
7941                        ..Default::default()
7942                    }),
7943                    fdecl::Offer::Config(fdecl::OfferConfiguration {
7944                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7945                        source_name: Some("source_config".into()),
7946                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7947                            name: "child".into(),
7948                            collection: None
7949                        })),
7950                        target_name: Some("config".into()),
7951                        ..Default::default()
7952                    }),
7953                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7954                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7955                        source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7956                        source_dictionary: Some("dict/inner".into()),
7957                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7958                            name: "child".into(),
7959                            collection: None
7960                        })),
7961                        target_name: Some("baz".into()),
7962                        dependency_type: Some(fdecl::DependencyType::Strong),
7963                        ..Default::default()
7964                    }),
7965                ]);
7966                decl
7967            },
7968            result = Err(ErrorList::new(vec![
7969                Error::invalid_capability(
7970                    DeclType::OfferService,
7971                    "source",
7972                    "fuchsia.some.library.SomeProtocol"),
7973                Error::invalid_capability(
7974                    DeclType::OfferProtocol,
7975                    "source",
7976                    "fuchsia.some.library.SomeProtocol"),
7977                Error::invalid_capability(DeclType::OfferDirectory, "source", "dir"),
7978                Error::invalid_capability(DeclType::OfferRunner, "source", "source_elf"),
7979                Error::invalid_capability(DeclType::OfferResolver, "source", "source_pkg"),
7980                Error::invalid_capability(DeclType::OfferDictionary, "source", "source_dict"),
7981                Error::invalid_capability(DeclType::OfferStorage, "source", "source_storage"),
7982                Error::invalid_capability(DeclType::OfferConfig, "source", "source_config"),
7983                Error::invalid_capability(DeclType::OfferProtocol, "source", "dict"),
7984            ])),
7985        },
7986        test_validate_offers_long_dependency_cycle => {
7987            input = {
7988                let mut decl = new_component_decl();
7989                let dependencies = vec![
7990                    ("d", "b"),
7991                    ("a", "b"),
7992                    ("b", "c"),
7993                    ("b", "d"),
7994                    ("c", "a"),
7995                ];
7996                let offers = dependencies.into_iter().map(|(from,to)|
7997                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7998                        source: Some(fdecl::Ref::Child(
7999                        fdecl::ChildRef { name: from.to_string(), collection: None },
8000                        )),
8001                        source_name: Some(format!("thing_{}", from)),
8002                        target: Some(fdecl::Ref::Child(
8003                        fdecl::ChildRef { name: to.to_string(), collection: None },
8004                        )),
8005                        target_name: Some(format!("thing_{}", from)),
8006                        dependency_type: Some(fdecl::DependencyType::Strong),
8007                        ..Default::default()
8008                    })).collect();
8009                let children = ["a", "b", "c", "d"].iter().map(|name| {
8010                    fdecl::Child {
8011                        name: Some(name.to_string()),
8012                        url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
8013                        startup: Some(fdecl::StartupMode::Lazy),
8014                        on_terminate: None,
8015                        environment: None,
8016                        ..Default::default()
8017                    }
8018                }).collect();
8019                decl.offers = Some(offers);
8020                decl.children = Some(children);
8021                decl
8022            },
8023            result = Err(ErrorList::new(vec![
8024                Error::dependency_cycle("{{child a -> child b -> child c -> child a}, {child b -> child d -> child b}}")
8025            ])),
8026        },
8027        test_validate_offers_not_required_invalid_source_service => {
8028            input = {
8029                let mut decl = generate_offer_different_source_and_availability_decl(
8030                    |source, availability, target_name|
8031                        fdecl::Offer::Service(fdecl::OfferService {
8032                            source: Some(source),
8033                            source_name: Some("fuchsia.examples.Echo".to_string()),
8034                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8035                                name: "sink".to_string(),
8036                                collection: None,
8037                            })),
8038                            target_name: Some(target_name.into()),
8039                            availability: Some(availability),
8040                            ..Default::default()
8041                        })
8042                );
8043                decl.capabilities = Some(vec![
8044                    fdecl::Capability::Service(fdecl::Service {
8045                        name: Some("fuchsia.examples.Echo".to_string()),
8046                        source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
8047                        ..Default::default()
8048                    }),
8049                ]);
8050                decl
8051            },
8052            result = {
8053                Err(ErrorList::new(vec![
8054                    Error::availability_must_be_optional(
8055                        DeclType::OfferService,
8056                        "availability",
8057                        Some(&"fuchsia.examples.Echo".to_string()),
8058                    ),
8059                    Error::availability_must_be_optional(
8060                        DeclType::OfferService,
8061                        "availability",
8062                        Some(&"fuchsia.examples.Echo".to_string()),
8063                    ),
8064                ]))
8065            },
8066        },
8067        test_validate_offers_not_required_invalid_source_protocol => {
8068            input = {
8069                let mut decl = generate_offer_different_source_and_availability_decl(
8070                    |source, availability, target_name|
8071                        fdecl::Offer::Protocol(fdecl::OfferProtocol {
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                            dependency_type: Some(fdecl::DependencyType::Strong),
8080                            availability: Some(availability),
8081                            ..Default::default()
8082                        })
8083                );
8084                decl.capabilities = Some(vec![
8085                    fdecl::Capability::Protocol(fdecl::Protocol {
8086                        name: Some("fuchsia.examples.Echo".to_string()),
8087                        source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
8088                        ..Default::default()
8089                    }),
8090                ]);
8091                decl
8092            },
8093            result = {
8094                Err(ErrorList::new(vec![
8095                    Error::availability_must_be_optional(
8096                        DeclType::OfferProtocol,
8097                        "availability",
8098                        Some(&"fuchsia.examples.Echo".to_string()),
8099                    ),
8100                    Error::availability_must_be_optional(
8101                        DeclType::OfferProtocol,
8102                        "availability",
8103                        Some(&"fuchsia.examples.Echo".to_string()),
8104                    ),
8105                ]))
8106            },
8107        },
8108        test_validate_offers_not_required_invalid_source_directory => {
8109            input = {
8110                let mut decl = generate_offer_different_source_and_availability_decl(
8111                    |source, availability, target_name|
8112                        fdecl::Offer::Directory(fdecl::OfferDirectory {
8113                            source: Some(source),
8114                            source_name: Some("assets".to_string()),
8115                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8116                                name: "sink".to_string(),
8117                                collection: None,
8118                            })),
8119                            target_name: Some(target_name.into()),
8120                            rights: Some(fio::Operations::CONNECT),
8121                            subdir: None,
8122                            dependency_type: Some(fdecl::DependencyType::Weak),
8123                            availability: Some(availability),
8124                            ..Default::default()
8125                        })
8126                );
8127                decl.capabilities = Some(vec![
8128                    fdecl::Capability::Directory(fdecl::Directory {
8129                        name: Some("assets".to_string()),
8130                        source_path: Some("/assets".to_string()),
8131                        rights: Some(fio::Operations::CONNECT),
8132                        ..Default::default()
8133                    }),
8134                ]);
8135                decl
8136            },
8137            result = {
8138                Err(ErrorList::new(vec![
8139                    Error::availability_must_be_optional(
8140                        DeclType::OfferDirectory,
8141                        "availability",
8142                        Some(&"assets".to_string()),
8143                    ),
8144                    Error::availability_must_be_optional(
8145                        DeclType::OfferDirectory,
8146                        "availability",
8147                        Some(&"assets".to_string()),
8148                    ),
8149                ]))
8150            },
8151        },
8152        test_validate_offers_not_required_invalid_source_storage => {
8153            input = {
8154                let mut decl = new_component_decl();
8155                decl.children = Some(vec![
8156                    fdecl::Child {
8157                        name: Some("sink".to_string()),
8158                        url: Some("fuchsia-pkg://fuchsia.com/sink#meta/sink.cm".to_string()),
8159                        startup: Some(fdecl::StartupMode::Lazy),
8160                        on_terminate: None,
8161                        environment: None,
8162                        ..Default::default()
8163                    },
8164                ]);
8165                decl.capabilities = Some(vec![
8166                    fdecl::Capability::Storage(fdecl::Storage {
8167                        name: Some("data".to_string()),
8168                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8169                        backing_dir: Some("minfs".to_string()),
8170                        subdir: None,
8171                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
8172                        ..Default::default()
8173                    }),
8174                ]);
8175                let new_offer = |source: fdecl::Ref, availability: fdecl::Availability,
8176                                        target_name: &str|
8177                {
8178                    fdecl::Offer::Storage(fdecl::OfferStorage {
8179                        source: Some(source),
8180                        source_name: Some("data".to_string()),
8181                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8182                            name: "sink".to_string(),
8183                            collection: None,
8184                        })),
8185                        target_name: Some(target_name.into()),
8186                        availability: Some(availability),
8187                        ..Default::default()
8188                    })
8189                };
8190                decl.offers = Some(vec![
8191                    // These offers are fine, offers with a source of parent or void can be
8192                    // optional.
8193                    new_offer(
8194                        fdecl::Ref::Parent(fdecl::ParentRef {}),
8195                        fdecl::Availability::Required,
8196                        "data0",
8197                    ),
8198                    new_offer(
8199                        fdecl::Ref::Parent(fdecl::ParentRef {}),
8200                        fdecl::Availability::Optional,
8201                        "data1",
8202                    ),
8203                    new_offer(
8204                        fdecl::Ref::Parent(fdecl::ParentRef {}),
8205                        fdecl::Availability::SameAsTarget,
8206                        "data2",
8207                    ),
8208                    new_offer(
8209                        fdecl::Ref::VoidType(fdecl::VoidRef {}),
8210                        fdecl::Availability::Optional,
8211                        "data3",
8212                    ),
8213                    // These offers are not fine, offers with a source other than parent or void
8214                    // must be required.
8215                    new_offer(
8216                        fdecl::Ref::Self_(fdecl::SelfRef {}),
8217                        fdecl::Availability::Optional,
8218                        "data4",
8219                    ),
8220                    new_offer(
8221                        fdecl::Ref::Self_(fdecl::SelfRef {}),
8222                        fdecl::Availability::SameAsTarget,
8223                        "data5",
8224                    ),
8225                    // These offers are also not fine, offers with a source of void must be optional
8226                    new_offer(
8227                        fdecl::Ref::VoidType(fdecl::VoidRef {}),
8228                        fdecl::Availability::Required,
8229                        "data6",
8230                    ),
8231                    new_offer(
8232                        fdecl::Ref::VoidType(fdecl::VoidRef {}),
8233                        fdecl::Availability::SameAsTarget,
8234                        "data7",
8235                    ),
8236                ]);
8237                decl
8238            },
8239            result = {
8240                Err(ErrorList::new(vec![
8241                    Error::availability_must_be_optional(
8242                        DeclType::OfferStorage,
8243                        "availability",
8244                        Some(&"data".to_string()),
8245                    ),
8246                    Error::availability_must_be_optional(
8247                        DeclType::OfferStorage,
8248                        "availability",
8249                        Some(&"data".to_string()),
8250                    ),
8251                ]))
8252            },
8253        },
8254
8255        test_validate_offers_valid_service_aggregation => {
8256            input = {
8257                let mut decl = new_component_decl();
8258                decl.offers = Some(vec![
8259                    fdecl::Offer::Service(fdecl::OfferService {
8260                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
8261                            name: "coll_a".to_string()
8262                        })),
8263                        source_name: Some("fuchsia.logger.Log".to_string()),
8264                        target: Some(fdecl::Ref::Child(
8265                            fdecl::ChildRef {
8266                                name: "child_c".to_string(),
8267                                collection: None,
8268                            }
8269                        )),
8270                        target_name: Some("fuchsia.logger.Log".to_string()),
8271                        source_instance_filter: None,
8272                        ..Default::default()
8273                    }),
8274                    fdecl::Offer::Service(fdecl::OfferService {
8275                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
8276                            name: "coll_b".to_string()
8277                        })),
8278                        source_name: Some("fuchsia.logger.Log".to_string()),
8279                        target: Some(fdecl::Ref::Child(
8280                            fdecl::ChildRef {
8281                                name: "child_c".to_string(),
8282                                collection: None,
8283                            }
8284                        )),
8285                        target_name: Some("fuchsia.logger.Log".to_string()),
8286                        source_instance_filter: Some(vec!["a_different_default".to_string()]),
8287                        ..Default::default()
8288                    })
8289                ]);
8290                decl.children = Some(vec![
8291                    fdecl::Child {
8292                        name: Some("child_c".to_string()),
8293                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
8294                        startup: Some(fdecl::StartupMode::Lazy),
8295                        ..Default::default()
8296                    },
8297                ]);
8298                decl.collections = Some(vec![
8299                    fdecl::Collection {
8300                        name: Some("coll_a".into()),
8301                        durability: Some(fdecl::Durability::Transient),
8302                        ..Default::default()
8303                    },
8304                    fdecl::Collection {
8305                        name: Some("coll_b".into()),
8306                        durability: Some(fdecl::Durability::Transient),
8307                        ..Default::default()
8308                    },
8309                ]);
8310                decl
8311            },
8312            result = Ok(()),
8313        },
8314
8315        // dictionaries
8316        test_validate_source_dictionary => {
8317            input = fdecl::Component {
8318                program: Some(fdecl::Program {
8319                    runner: Some("elf".into()),
8320                    info: Some(fdata::Dictionary {
8321                        entries: None,
8322                        ..Default::default()
8323                    }),
8324                    ..Default::default()
8325                }),
8326                uses: Some(vec![
8327                    fdecl::Use::Protocol(fdecl::UseProtocol {
8328                        dependency_type: Some(fdecl::DependencyType::Strong),
8329                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8330                        source_dictionary: Some("bad//".into()),
8331                        source_name: Some("foo".into()),
8332                        target_path: Some("/svc/foo".into()),
8333                        ..Default::default()
8334                    }),
8335                ]),
8336                exposes: Some(vec![
8337                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
8338                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8339                            name: "missing".into(),
8340                            collection: None,
8341                        })),
8342                        source_dictionary: Some("in/dict".into()),
8343                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8344                        source_name: Some("foo".into()),
8345                        target_name: Some("bar".into()),
8346                        ..Default::default()
8347                    }),
8348                ]),
8349                offers: Some(vec![
8350                    fdecl::Offer::Service(fdecl::OfferService {
8351                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8352                        source_dictionary: Some("bad//".into()),
8353                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
8354                            name: "child".into(),
8355                            collection: None,
8356                        })),
8357                        source_name: Some("foo".into()),
8358                        target_name: Some("bar".into()),
8359                        ..Default::default()
8360                    }),
8361                ]),
8362                children: Some(vec![
8363                    fdecl::Child {
8364                        name: Some("child".into()),
8365                        url: Some("fuchsia-pkg://child".into()),
8366                        startup: Some(fdecl::StartupMode::Lazy),
8367                        ..Default::default()
8368                    },
8369                ]),
8370                ..Default::default()
8371            },
8372            result = Err(ErrorList::new(vec![
8373                Error::invalid_field(DeclType::UseProtocol, "source_dictionary"),
8374                Error::invalid_child(DeclType::ExposeDirectory, "source", "missing"),
8375                Error::invalid_field(DeclType::OfferService, "source_dictionary"),
8376            ])),
8377        },
8378        test_validate_dictionary_too_long => {
8379            input = fdecl::Component {
8380                program: Some(fdecl::Program {
8381                    runner: Some("elf".into()),
8382                    info: Some(fdata::Dictionary {
8383                        entries: None,
8384                        ..Default::default()
8385                    }),
8386                    ..Default::default()
8387                }),
8388                uses: Some(vec![
8389                    fdecl::Use::Protocol(fdecl::UseProtocol {
8390                        dependency_type: Some(fdecl::DependencyType::Strong),
8391                        source: Some(fdecl::Ref::Parent( fdecl::ParentRef {} )),
8392                        source_dictionary: Some("a".repeat(4096)),
8393                        source_name: Some("foo".into()),
8394                        target_path: Some("/svc/foo".into()),
8395                        ..Default::default()
8396                    }),
8397                ]),
8398                ..Default::default()
8399            },
8400            result = Err(ErrorList::new(vec![
8401                Error::field_too_long(DeclType::UseProtocol, "source_dictionary"),
8402            ])),
8403        },
8404
8405        // environments
8406        test_validate_environment_empty => {
8407            input = {
8408                let mut decl = new_component_decl();
8409                decl.environments = Some(vec![fdecl::Environment {
8410                    name: None,
8411                    extends: None,
8412                    runners: None,
8413                    resolvers: None,
8414                    stop_timeout_ms: None,
8415                    debug_capabilities: None,
8416                    ..Default::default()
8417                }]);
8418                decl
8419            },
8420            result = Err(ErrorList::new(vec![
8421                Error::missing_field(DeclType::Environment, "name"),
8422                Error::missing_field(DeclType::Environment, "extends"),
8423            ])),
8424        },
8425
8426        test_validate_environment_no_stop_timeout => {
8427            input = {
8428                let mut decl = new_component_decl();
8429                decl.environments = Some(vec![fdecl::Environment {
8430                    name: Some("env".to_string()),
8431                    extends: Some(fdecl::EnvironmentExtends::None),
8432                    runners: None,
8433                    resolvers: None,
8434                    stop_timeout_ms: None,
8435                    ..Default::default()
8436                }]);
8437                decl
8438            },
8439            result = Err(ErrorList::new(vec![Error::missing_field(DeclType::Environment, "stop_timeout_ms")])),
8440        },
8441
8442        test_validate_environment_extends_stop_timeout => {
8443            input = {  let mut decl = new_component_decl();
8444                decl.environments = Some(vec![fdecl::Environment {
8445                    name: Some("env".to_string()),
8446                    extends: Some(fdecl::EnvironmentExtends::Realm),
8447                    runners: None,
8448                    resolvers: None,
8449                    stop_timeout_ms: None,
8450                    ..Default::default()
8451                }]);
8452                decl
8453            },
8454            result = Ok(()),
8455        },
8456        test_validate_environment_long_identifiers => {
8457            input = {
8458                let mut decl = new_component_decl();
8459                decl.environments = Some(vec![fdecl::Environment {
8460                    name: Some("a".repeat(256)),
8461                    extends: Some(fdecl::EnvironmentExtends::None),
8462                    runners: Some(vec![
8463                        fdecl::RunnerRegistration {
8464                            source_name: Some("a".repeat(256)),
8465                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8466                            target_name: Some("a".repeat(256)),
8467                            ..Default::default()
8468                        },
8469                    ]),
8470                    resolvers: Some(vec![
8471                        fdecl::ResolverRegistration {
8472                            resolver: Some("a".repeat(256)),
8473                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8474                            scheme: Some("a".repeat(256)),
8475                            ..Default::default()
8476                        },
8477                    ]),
8478                    stop_timeout_ms: Some(1234),
8479                    ..Default::default()
8480                }]);
8481                decl
8482            },
8483            result = Err(ErrorList::new(vec![
8484                Error::field_too_long(DeclType::Environment, "name"),
8485                Error::field_too_long(DeclType::RunnerRegistration, "source_name"),
8486                Error::field_too_long(DeclType::RunnerRegistration, "target_name"),
8487                Error::field_too_long(DeclType::ResolverRegistration, "resolver"),
8488                Error::field_too_long(DeclType::ResolverRegistration, "scheme"),
8489            ])),
8490        },
8491        test_validate_environment_empty_runner_resolver_fields => {
8492            input = {
8493                let mut decl = new_component_decl();
8494                decl.environments = Some(vec![fdecl::Environment {
8495                    name: Some("a".to_string()),
8496                    extends: Some(fdecl::EnvironmentExtends::None),
8497                    runners: Some(vec![
8498                        fdecl::RunnerRegistration {
8499                            source_name: None,
8500                            source: None,
8501                            target_name: None,
8502                            ..Default::default()
8503                        },
8504                    ]),
8505                    resolvers: Some(vec![
8506                        fdecl::ResolverRegistration {
8507                            resolver: None,
8508                            source: None,
8509                            scheme: None,
8510                            ..Default::default()
8511                        },
8512                    ]),
8513                    stop_timeout_ms: Some(1234),
8514                    ..Default::default()
8515                }]);
8516                decl
8517            },
8518            result = Err(ErrorList::new(vec![
8519                Error::missing_field(DeclType::RunnerRegistration, "source_name"),
8520                Error::missing_field(DeclType::RunnerRegistration, "source"),
8521                Error::missing_field(DeclType::RunnerRegistration, "target_name"),
8522                Error::missing_field(DeclType::ResolverRegistration, "resolver"),
8523                Error::missing_field(DeclType::ResolverRegistration, "source"),
8524                Error::missing_field(DeclType::ResolverRegistration, "scheme"),
8525            ])),
8526        },
8527        test_validate_environment_invalid_fields => {
8528            input = {
8529                let mut decl = new_component_decl();
8530                decl.environments = Some(vec![fdecl::Environment {
8531                    name: Some("a".to_string()),
8532                    extends: Some(fdecl::EnvironmentExtends::None),
8533                    runners: Some(vec![
8534                        fdecl::RunnerRegistration {
8535                            source_name: Some("^a".to_string()),
8536                            source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
8537                            target_name: Some("%a".to_string()),
8538                            ..Default::default()
8539                        },
8540                    ]),
8541                    resolvers: Some(vec![
8542                        fdecl::ResolverRegistration {
8543                            resolver: Some("^a".to_string()),
8544                            source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
8545                            scheme: Some("9scheme".to_string()),
8546                            ..Default::default()
8547                        },
8548                    ]),
8549                    stop_timeout_ms: Some(1234),
8550                    ..Default::default()
8551                }]);
8552                decl
8553            },
8554            result = Err(ErrorList::new(vec![
8555                Error::invalid_field(DeclType::RunnerRegistration, "source_name"),
8556                Error::invalid_field(DeclType::RunnerRegistration, "source"),
8557                Error::invalid_field(DeclType::RunnerRegistration, "target_name"),
8558                Error::invalid_field(DeclType::ResolverRegistration, "resolver"),
8559                Error::invalid_field(DeclType::ResolverRegistration, "source"),
8560                Error::invalid_field(DeclType::ResolverRegistration, "scheme"),
8561            ])),
8562        },
8563        test_validate_environment_missing_runner => {
8564            input = {
8565                let mut decl = new_component_decl();
8566                decl.environments = Some(vec![fdecl::Environment {
8567                    name: Some("a".to_string()),
8568                    extends: Some(fdecl::EnvironmentExtends::None),
8569                    runners: Some(vec![
8570                        fdecl::RunnerRegistration {
8571                            source_name: Some("dart".to_string()),
8572                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
8573                            target_name: Some("dart".to_string()),
8574                            ..Default::default()
8575                        },
8576                    ]),
8577                    resolvers: None,
8578                    stop_timeout_ms: Some(1234),
8579                    ..Default::default()
8580                }]);
8581                decl
8582            },
8583            result = Err(ErrorList::new(vec![
8584                Error::invalid_runner(DeclType::RunnerRegistration, "source_name", "dart"),
8585            ])),
8586        },
8587        test_validate_environment_duplicate_registrations => {
8588            input = {
8589                let mut decl = new_component_decl();
8590                decl.environments = Some(vec![fdecl::Environment {
8591                    name: Some("a".to_string()),
8592                    extends: Some(fdecl::EnvironmentExtends::None),
8593                    runners: Some(vec![
8594                        fdecl::RunnerRegistration {
8595                            source_name: Some("dart".to_string()),
8596                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8597                            target_name: Some("dart".to_string()),
8598                            ..Default::default()
8599                        },
8600                        fdecl::RunnerRegistration {
8601                            source_name: Some("other-dart".to_string()),
8602                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8603                            target_name: Some("dart".to_string()),
8604                            ..Default::default()
8605                        },
8606                    ]),
8607                    resolvers: Some(vec![
8608                        fdecl::ResolverRegistration {
8609                            resolver: Some("pkg_resolver".to_string()),
8610                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8611                            scheme: Some("fuchsia-pkg".to_string()),
8612                            ..Default::default()
8613                        },
8614                        fdecl::ResolverRegistration {
8615                            resolver: Some("base_resolver".to_string()),
8616                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8617                            scheme: Some("fuchsia-pkg".to_string()),
8618                            ..Default::default()
8619                        },
8620                    ]),
8621                    stop_timeout_ms: Some(1234),
8622                    ..Default::default()
8623                }]);
8624                decl
8625            },
8626            result = Err(ErrorList::new(vec![
8627                Error::duplicate_field(DeclType::RunnerRegistration, "target_name", "dart"),
8628                Error::duplicate_field(DeclType::ResolverRegistration, "scheme", "fuchsia-pkg"),
8629            ])),
8630        },
8631        test_validate_environment_from_missing_child => {
8632            input = {
8633                let mut decl = new_component_decl();
8634                decl.environments = Some(vec![fdecl::Environment {
8635                    name: Some("a".to_string()),
8636                    extends: Some(fdecl::EnvironmentExtends::None),
8637                    runners: Some(vec![
8638                        fdecl::RunnerRegistration {
8639                            source_name: Some("elf".to_string()),
8640                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8641                                name: "missing".to_string(),
8642                                collection: None,
8643                            })),
8644                            target_name: Some("elf".to_string()),
8645                            ..Default::default()
8646                        },
8647                    ]),
8648                    resolvers: Some(vec![
8649                        fdecl::ResolverRegistration {
8650                            resolver: Some("pkg_resolver".to_string()),
8651                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8652                                name: "missing".to_string(),
8653                                collection: None,
8654                            })),
8655                            scheme: Some("fuchsia-pkg".to_string()),
8656                            ..Default::default()
8657                        },
8658                    ]),
8659                    stop_timeout_ms: Some(1234),
8660                    ..Default::default()
8661                }]);
8662                decl
8663            },
8664            result = Err(ErrorList::new(vec![
8665                Error::invalid_child(DeclType::RunnerRegistration, "source", "missing"),
8666                Error::invalid_child(DeclType::ResolverRegistration, "source", "missing"),
8667            ])),
8668        },
8669        test_validate_environment_runner_child_cycle => {
8670            input = {
8671                let mut decl = new_component_decl();
8672                decl.environments = Some(vec![fdecl::Environment {
8673                    name: Some("env".to_string()),
8674                    extends: Some(fdecl::EnvironmentExtends::None),
8675                    runners: Some(vec![
8676                        fdecl::RunnerRegistration {
8677                            source_name: Some("elf".to_string()),
8678                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8679                                name: "child".to_string(),
8680                                collection: None,
8681                            })),
8682                            target_name: Some("elf".to_string()),
8683                            ..Default::default()
8684                        },
8685                    ]),
8686                    resolvers: None,
8687                    stop_timeout_ms: Some(1234),
8688                    ..Default::default()
8689                }]);
8690                decl.children = Some(vec![fdecl::Child {
8691                    name: Some("child".to_string()),
8692                    startup: Some(fdecl::StartupMode::Lazy),
8693                    on_terminate: None,
8694                    url: Some("fuchsia-pkg://child".to_string()),
8695                    environment: Some("env".to_string()),
8696                    ..Default::default()
8697                }]);
8698                decl
8699            },
8700            result = Err(ErrorList::new(vec![
8701                Error::dependency_cycle(
8702                    "{{child child -> environment env -> child child}}"
8703                ),
8704            ])),
8705        },
8706        test_validate_environment_resolver_child_cycle => {
8707            input = {
8708                let mut decl = new_component_decl();
8709                decl.environments = Some(vec![fdecl::Environment {
8710                    name: Some("env".to_string()),
8711                    extends: Some(fdecl::EnvironmentExtends::None),
8712                    runners: None,
8713                    resolvers: Some(vec![
8714                        fdecl::ResolverRegistration {
8715                            resolver: Some("pkg_resolver".to_string()),
8716                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8717                                name: "child".to_string(),
8718                                collection: None,
8719                            })),
8720                            scheme: Some("fuchsia-pkg".to_string()),
8721                            ..Default::default()
8722                        },
8723                    ]),
8724                    stop_timeout_ms: Some(1234),
8725                    ..Default::default()
8726                }]);
8727                decl.children = Some(vec![fdecl::Child {
8728                    name: Some("child".to_string()),
8729                    startup: Some(fdecl::StartupMode::Lazy),
8730                    on_terminate: None,
8731                    url: Some("fuchsia-pkg://child".to_string()),
8732                    environment: Some("env".to_string()),
8733                    ..Default::default()
8734                }]);
8735                decl
8736            },
8737            result = Err(ErrorList::new(vec![
8738                Error::dependency_cycle(
8739                    "{{child child -> environment env -> child child}}"
8740                ),
8741            ])),
8742        },
8743        test_validate_environment_resolver_multiple_children_cycle => {
8744            input = {
8745                let mut decl = new_component_decl();
8746                decl.environments = Some(vec![fdecl::Environment {
8747                    name: Some("env".to_string()),
8748                    extends: Some(fdecl::EnvironmentExtends::None),
8749                    runners: None,
8750                    resolvers: Some(vec![
8751                        fdecl::ResolverRegistration {
8752                            resolver: Some("pkg_resolver".to_string()),
8753                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8754                                name: "a".to_string(),
8755                                collection: None,
8756                            })),
8757                            scheme: Some("fuchsia-pkg".to_string()),
8758                            ..Default::default()
8759                        },
8760                    ]),
8761                    stop_timeout_ms: Some(1234),
8762                    ..Default::default()
8763                }]);
8764                decl.children = Some(vec![
8765                    fdecl::Child {
8766                        name: Some("a".to_string()),
8767                        startup: Some(fdecl::StartupMode::Lazy),
8768                        on_terminate: None,
8769                        url: Some("fuchsia-pkg://child-a".to_string()),
8770                        environment: None,
8771                        ..Default::default()
8772                    },
8773                    fdecl::Child {
8774                        name: Some("b".to_string()),
8775                        startup: Some(fdecl::StartupMode::Lazy),
8776                        on_terminate: None,
8777                        url: Some("fuchsia-pkg://child-b".to_string()),
8778                        environment: Some("env".to_string()),
8779                        ..Default::default()
8780                    },
8781                ]);
8782                decl.offers = Some(vec![fdecl::Offer::Service(fdecl::OfferService {
8783                    source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8784                        name: "b".to_string(),
8785                        collection: None,
8786                    })),
8787                    source_name: Some("thing".to_string()),
8788                    target: Some(fdecl::Ref::Child(
8789                    fdecl::ChildRef {
8790                        name: "a".to_string(),
8791                        collection: None,
8792                    }
8793                    )),
8794                    target_name: Some("thing".to_string()),
8795                    ..Default::default()
8796                })]);
8797                decl
8798            },
8799            result = Err(ErrorList::new(vec![
8800                Error::dependency_cycle(
8801                    "{{child a -> environment env -> child b -> child a}}"
8802                ),
8803            ])),
8804        },
8805        test_validate_environment_debug_empty => {
8806            input = {
8807                let mut decl = new_component_decl();
8808                decl.environments = Some(vec![
8809                    fdecl::Environment {
8810                        name: Some("a".to_string()),
8811                        extends: Some(fdecl::EnvironmentExtends::None),
8812                        stop_timeout_ms: Some(2),
8813                        debug_capabilities:Some(vec![
8814                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8815                                source: None,
8816                                source_name: None,
8817                                target_name: None,
8818                                ..Default::default()
8819                            }),
8820                    ]),
8821                    ..Default::default()
8822                }]);
8823                decl
8824            },
8825            result = Err(ErrorList::new(vec![
8826                Error::missing_field(DeclType::DebugProtocolRegistration, "source"),
8827                Error::missing_field(DeclType::DebugProtocolRegistration, "source_name"),
8828                Error::missing_field(DeclType::DebugProtocolRegistration, "target_name"),
8829            ])),
8830        },
8831        test_validate_environment_debug_log_identifier => {
8832            input = {
8833                let mut decl = new_component_decl();
8834                decl.environments = Some(vec![
8835                    fdecl::Environment {
8836                        name: Some("a".to_string()),
8837                        extends: Some(fdecl::EnvironmentExtends::None),
8838                        stop_timeout_ms: Some(2),
8839                        debug_capabilities:Some(vec![
8840                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8841                                source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8842                                    name: "a".repeat(256),
8843                                    collection: None,
8844                                })),
8845                                source_name: Some(format!("{}", "a".repeat(256))),
8846                                target_name: Some(format!("{}", "b".repeat(256))),
8847                                ..Default::default()
8848                            }),
8849                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8850                                source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8851                                source_name: Some("a".to_string()),
8852                                target_name: Some(format!("{}", "b".repeat(256))),
8853                                ..Default::default()
8854                            }),
8855                    ]),
8856                    ..Default::default()
8857                }]);
8858                decl
8859            },
8860            result = Err(ErrorList::new(vec![
8861                Error::field_too_long(DeclType::DebugProtocolRegistration, "source.child.name"),
8862                Error::field_too_long(DeclType::DebugProtocolRegistration, "source_name"),
8863                Error::field_too_long(DeclType::DebugProtocolRegistration, "target_name"),
8864                Error::field_too_long(DeclType::DebugProtocolRegistration, "target_name"),
8865            ])),
8866        },
8867        test_validate_environment_debug_log_extraneous => {
8868            input = {
8869                let mut decl = new_component_decl();
8870                decl.environments = Some(vec![
8871                    fdecl::Environment {
8872                        name: Some("a".to_string()),
8873                        extends: Some(fdecl::EnvironmentExtends::None),
8874                        stop_timeout_ms: Some(2),
8875                        debug_capabilities:Some(vec![
8876                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8877                                source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8878                                    name: "logger".to_string(),
8879                                    collection: Some("modular".to_string()),
8880                                })),
8881                                source_name: Some("fuchsia.logger.Log".to_string()),
8882                                target_name: Some("fuchsia.logger.Log".to_string()),
8883                                ..Default::default()
8884                            }),
8885                    ]),
8886                    ..Default::default()
8887                }]);
8888                decl
8889            },
8890            result = Err(ErrorList::new(vec![
8891                Error::extraneous_field(DeclType::DebugProtocolRegistration, "source.child.collection"),
8892            ])),
8893        },
8894        test_validate_environment_debug_log_invalid_identifiers => {
8895            input = {
8896                let mut decl = new_component_decl();
8897                decl.environments = Some(vec![
8898                    fdecl::Environment {
8899                        name: Some("a".to_string()),
8900                        extends: Some(fdecl::EnvironmentExtends::None),
8901                        stop_timeout_ms: Some(2),
8902                        debug_capabilities:Some(vec![
8903                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8904                                source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8905                                    name: "^bad".to_string(),
8906                                    collection: None,
8907                                })),
8908                                source_name: Some("foo/".to_string()),
8909                                target_name: Some("/".to_string()),
8910                                ..Default::default()
8911                            }),
8912                    ]),
8913                    ..Default::default()
8914                }]);
8915                decl
8916            },
8917            result = Err(ErrorList::new(vec![
8918                Error::invalid_field(DeclType::DebugProtocolRegistration, "source.child.name"),
8919                Error::invalid_field(DeclType::DebugProtocolRegistration, "source_name"),
8920                Error::invalid_field(DeclType::DebugProtocolRegistration, "target_name"),
8921            ])),
8922        },
8923        test_validate_environment_debug_log_invalid_child => {
8924            input = {
8925                let mut decl = new_component_decl();
8926                decl.environments = Some(vec![
8927                    fdecl::Environment {
8928                        name: Some("a".to_string()),
8929                        extends: Some(fdecl::EnvironmentExtends::None),
8930                        stop_timeout_ms: Some(2),
8931                        debug_capabilities:Some(vec![
8932                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8933                                source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8934                                    name: "logger".to_string(),
8935                                    collection: None,
8936                                })),
8937                                source_name: Some("fuchsia.logger.LegacyLog".to_string()),
8938                                target_name: Some("fuchsia.logger.LegacyLog".to_string()),
8939                                ..Default::default()
8940                            }),
8941                    ]),
8942                    ..Default::default()
8943                }]);
8944                decl.children = Some(vec![
8945                    fdecl::Child {
8946                        name: Some("netstack".to_string()),
8947                        url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
8948                        startup: Some(fdecl::StartupMode::Lazy),
8949                        on_terminate: None,
8950                        environment: None,
8951                        ..Default::default()
8952                    },
8953                ]);
8954                decl
8955            },
8956            result = Err(ErrorList::new(vec![
8957                Error::invalid_child(DeclType::DebugProtocolRegistration, "source", "logger"),
8958
8959            ])),
8960        },
8961        test_validate_environment_debug_source_capability => {
8962            input = {
8963                let mut decl = new_component_decl();
8964                decl.environments = Some(vec![
8965                    fdecl::Environment {
8966                        name: Some("a".to_string()),
8967                        extends: Some(fdecl::EnvironmentExtends::None),
8968                        stop_timeout_ms: Some(2),
8969                        debug_capabilities:Some(vec![
8970                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8971                                source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
8972                                    name: "storage".to_string(),
8973                                })),
8974                                source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
8975                                target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
8976                                ..Default::default()
8977                            }),
8978                    ]),
8979                    ..Default::default()
8980                }]);
8981                decl
8982            },
8983            result = Err(ErrorList::new(vec![
8984                Error::invalid_field(DeclType::DebugProtocolRegistration, "source"),
8985            ])),
8986        },
8987
8988        // children
8989        test_validate_children_empty => {
8990            input = {
8991                let mut decl = new_component_decl();
8992                decl.children = Some(vec![fdecl::Child{
8993                    name: None,
8994                    url: None,
8995                    startup: None,
8996                    on_terminate: None,
8997                    environment: None,
8998                    ..Default::default()
8999                }]);
9000                decl
9001            },
9002            result = Err(ErrorList::new(vec![
9003                Error::missing_field(DeclType::Child, "name"),
9004                Error::missing_field(DeclType::Child, "url"),
9005                Error::missing_field(DeclType::Child, "startup"),
9006                // `on_terminate` is allowed to be None
9007            ])),
9008        },
9009        test_validate_children_invalid_identifiers => {
9010            input = {
9011                let mut decl = new_component_decl();
9012                decl.children = Some(vec![fdecl::Child{
9013                    name: Some("^bad".to_string()),
9014                    url: Some("scheme://invalid-port:99999999/path#frag".to_string()),
9015                    startup: Some(fdecl::StartupMode::Lazy),
9016                    on_terminate: None,
9017                    environment: None,
9018                    ..Default::default()
9019                }]);
9020                decl
9021            },
9022            result = Err(ErrorList::new(vec![
9023                Error::invalid_field(DeclType::Child, "name"),
9024                Error::invalid_url(DeclType::Child, "url", "\"scheme://invalid-port:99999999/path#frag\": Malformed URL: InvalidPort."),
9025            ])),
9026        },
9027        test_validate_children_long_identifiers => {
9028            input = {
9029                let mut decl = new_component_decl();
9030                decl.children = Some(vec![fdecl::Child{
9031                    name: Some("a".repeat(1025)),
9032                    url: Some(format!("fuchsia-pkg://{}", "a".repeat(4083))),
9033                    startup: Some(fdecl::StartupMode::Lazy),
9034                    on_terminate: None,
9035                    environment: Some("a".repeat(1025)),
9036                    ..Default::default()
9037                }]);
9038                decl
9039            },
9040            result = Err(ErrorList::new(vec![
9041                Error::field_too_long(DeclType::Child, "name"),
9042                Error::field_too_long(DeclType::Child, "url"),
9043                Error::field_too_long(DeclType::Child, "environment"),
9044                Error::invalid_environment(DeclType::Child, "environment", "a".repeat(1025)),
9045            ])),
9046        },
9047        test_validate_child_references_unknown_env => {
9048            input = {
9049                let mut decl = new_component_decl();
9050                decl.children = Some(vec![fdecl::Child{
9051                    name: Some("foo".to_string()),
9052                    url: Some("fuchsia-pkg://foo".to_string()),
9053                    startup: Some(fdecl::StartupMode::Lazy),
9054                    on_terminate: None,
9055                    environment: Some("test_env".to_string()),
9056                    ..Default::default()
9057                }]);
9058                decl
9059            },
9060            result = Err(ErrorList::new(vec![
9061                Error::invalid_environment(DeclType::Child, "environment", "test_env"),
9062            ])),
9063        },
9064
9065        // collections
9066        test_validate_collections_empty => {
9067            input = {
9068                let mut decl = new_component_decl();
9069                decl.collections = Some(vec![fdecl::Collection{
9070                    name: None,
9071                    durability: None,
9072                    environment: None,
9073                    allowed_offers: None,
9074                    allow_long_names: None,
9075                    ..Default::default()
9076                }]);
9077                decl
9078            },
9079            result = Err(ErrorList::new(vec![
9080                Error::missing_field(DeclType::Collection, "name"),
9081                Error::missing_field(DeclType::Collection, "durability"),
9082            ])),
9083        },
9084        test_validate_collections_invalid_identifiers => {
9085            input = {
9086                let mut decl = new_component_decl();
9087                decl.collections = Some(vec![fdecl::Collection{
9088                    name: Some("^bad".to_string()),
9089                    durability: Some(fdecl::Durability::Transient),
9090                    environment: None,
9091                    allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
9092                    allow_long_names: None,
9093                    ..Default::default()
9094                }]);
9095                decl
9096            },
9097            result = Err(ErrorList::new(vec![
9098                Error::invalid_field(DeclType::Collection, "name"),
9099            ])),
9100        },
9101        test_validate_collections_long_identifiers => {
9102            input = {
9103                let mut decl = new_component_decl();
9104                decl.collections = Some(vec![fdecl::Collection{
9105                    name: Some("a".repeat(1025)),
9106                    durability: Some(fdecl::Durability::Transient),
9107                    environment: None,
9108                    allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
9109                    allow_long_names: None,
9110                    ..Default::default()
9111                }]);
9112                decl
9113            },
9114            result = Err(ErrorList::new(vec![
9115                Error::field_too_long(DeclType::Collection, "name"),
9116            ])),
9117        },
9118        test_validate_collection_references_unknown_env => {
9119            input = {
9120                let mut decl = new_component_decl();
9121                decl.collections = Some(vec![fdecl::Collection {
9122                    name: Some("foo".to_string()),
9123                    durability: Some(fdecl::Durability::Transient),
9124                    environment: Some("test_env".to_string()),
9125                    allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
9126                    allow_long_names: None,
9127                    ..Default::default()
9128                }]);
9129                decl
9130            },
9131            result = Err(ErrorList::new(vec![
9132                Error::invalid_environment(DeclType::Collection, "environment", "test_env"),
9133            ])),
9134        },
9135
9136        // capabilities
9137        test_validate_capabilities_empty => {
9138            input = {
9139                let mut decl = new_component_decl();
9140                decl.capabilities = Some(vec![
9141                    fdecl::Capability::Service(fdecl::Service {
9142                        name: None,
9143                        source_path: None,
9144                        ..Default::default()
9145                    }),
9146                    fdecl::Capability::Protocol(fdecl::Protocol {
9147                        name: None,
9148                        source_path: None,
9149                        ..Default::default()
9150                    }),
9151                    fdecl::Capability::Directory(fdecl::Directory {
9152                        name: None,
9153                        source_path: None,
9154                        rights: None,
9155                        ..Default::default()
9156                    }),
9157                    fdecl::Capability::Storage(fdecl::Storage {
9158                        name: None,
9159                        source: None,
9160                        backing_dir: None,
9161                        subdir: None,
9162                        storage_id: None,
9163                        ..Default::default()
9164                    }),
9165                    fdecl::Capability::Runner(fdecl::Runner {
9166                        name: None,
9167                        source_path: None,
9168                        ..Default::default()
9169                    }),
9170                    fdecl::Capability::Resolver(fdecl::Resolver {
9171                        name: None,
9172                        source_path: None,
9173                        ..Default::default()
9174                    }),
9175                    fdecl::Capability::Dictionary(fdecl::Dictionary {
9176                        ..Default::default()
9177                    }),
9178                ]);
9179                decl
9180            },
9181            result = Err(ErrorList::new(vec![
9182                Error::missing_field(DeclType::Dictionary, "name"),
9183                Error::missing_field(DeclType::Service, "name"),
9184                Error::missing_field(DeclType::Service, "source_path"),
9185                Error::missing_field(DeclType::Protocol, "name"),
9186                Error::missing_field(DeclType::Protocol, "source_path"),
9187                Error::missing_field(DeclType::Directory, "name"),
9188                Error::missing_field(DeclType::Directory, "source_path"),
9189                Error::missing_field(DeclType::Directory, "rights"),
9190                Error::missing_field(DeclType::Storage, "source"),
9191                Error::missing_field(DeclType::Storage, "name"),
9192                Error::missing_field(DeclType::Storage, "storage_id"),
9193                Error::missing_field(DeclType::Storage, "backing_dir"),
9194                Error::missing_field(DeclType::Runner, "name"),
9195                Error::missing_field(DeclType::Runner, "source_path"),
9196                Error::missing_field(DeclType::Resolver, "name"),
9197                Error::missing_field(DeclType::Resolver, "source_path"),
9198            ])),
9199        },
9200        test_validate_capabilities_invalid_identifiers => {
9201            input = {
9202                let mut decl = new_component_decl();
9203                decl.capabilities = Some(vec![
9204                    fdecl::Capability::Service(fdecl::Service {
9205                        name: Some("^bad".to_string()),
9206                        source_path: Some("&bad".to_string()),
9207                        ..Default::default()
9208                    }),
9209                    fdecl::Capability::Protocol(fdecl::Protocol {
9210                        name: Some("^bad".to_string()),
9211                        source_path: Some("&bad".to_string()),
9212                        ..Default::default()
9213                    }),
9214                    fdecl::Capability::Directory(fdecl::Directory {
9215                        name: Some("^bad".to_string()),
9216                        source_path: Some("&bad".to_string()),
9217                        rights: Some(fio::Operations::CONNECT),
9218                        ..Default::default()
9219                    }),
9220                    fdecl::Capability::Storage(fdecl::Storage {
9221                        name: Some("^bad".to_string()),
9222                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9223                            name: "/bad".to_string()
9224                        })),
9225                        backing_dir: Some("&bad".to_string()),
9226                        subdir: None,
9227                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9228                        ..Default::default()
9229                    }),
9230                    fdecl::Capability::Runner(fdecl::Runner {
9231                        name: Some("^bad".to_string()),
9232                        source_path: Some("&bad".to_string()),
9233                        ..Default::default()
9234                    }),
9235                    fdecl::Capability::Resolver(fdecl::Resolver {
9236                        name: Some("^bad".to_string()),
9237                        source_path: Some("&bad".to_string()),
9238                        ..Default::default()
9239                    }),
9240                    fdecl::Capability::Dictionary(fdecl::Dictionary {
9241                        name: Some("^bad".to_string()),
9242                        ..Default::default()
9243                    }),
9244                ]);
9245                decl
9246            },
9247            result = Err(ErrorList::new(vec![
9248                Error::invalid_field(DeclType::Dictionary, "name"),
9249                Error::invalid_field(DeclType::Service, "name"),
9250                Error::invalid_field(DeclType::Service, "source_path"),
9251                Error::invalid_field(DeclType::Protocol, "name"),
9252                Error::invalid_field(DeclType::Protocol, "source_path"),
9253                Error::invalid_field(DeclType::Directory, "name"),
9254                Error::invalid_field(DeclType::Directory, "source_path"),
9255                Error::invalid_field(DeclType::Storage, "source"),
9256                Error::invalid_field(DeclType::Storage, "name"),
9257                Error::invalid_field(DeclType::Storage, "backing_dir"),
9258                Error::invalid_field(DeclType::Runner, "name"),
9259                Error::invalid_field(DeclType::Runner, "source_path"),
9260                Error::invalid_field(DeclType::Resolver, "name"),
9261                Error::invalid_field(DeclType::Resolver, "source_path"),
9262            ])),
9263        },
9264        test_validate_capabilities_invalid_child => {
9265            input = {
9266                let mut decl = new_component_decl();
9267                decl.capabilities = Some(vec![
9268                    fdecl::Capability::Storage(fdecl::Storage {
9269                        name: Some("foo".to_string()),
9270                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9271                            name: "invalid".to_string(),
9272                        })),
9273                        backing_dir: Some("foo".to_string()),
9274                        subdir: None,
9275                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9276                        ..Default::default()
9277                    }),
9278                ]);
9279                decl
9280            },
9281            result = Err(ErrorList::new(vec![
9282                Error::invalid_field(DeclType::Storage, "source"),
9283            ])),
9284        },
9285        test_validate_capabilities_long_identifiers => {
9286            input = {
9287                let mut decl = new_component_decl();
9288                decl.capabilities = Some(vec![
9289                    fdecl::Capability::Service(fdecl::Service {
9290                        name: Some("a".repeat(256)),
9291                        source_path: Some("/c".repeat(2048)),
9292                        ..Default::default()
9293                    }),
9294                    fdecl::Capability::Protocol(fdecl::Protocol {
9295                        name: Some("a".repeat(256)),
9296                        source_path: Some("/c".repeat(2048)),
9297                        ..Default::default()
9298                    }),
9299                    fdecl::Capability::Directory(fdecl::Directory {
9300                        name: Some("a".repeat(256)),
9301                        source_path: Some("/c".repeat(2048)),
9302                        rights: Some(fio::Operations::CONNECT),
9303                        ..Default::default()
9304                    }),
9305                    fdecl::Capability::Storage(fdecl::Storage {
9306                        name: Some("a".repeat(256)),
9307                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
9308                            name: "b".repeat(256),
9309                            collection: None,
9310                        })),
9311                        backing_dir: Some(format!("{}", "c".repeat(256))),
9312                        subdir: None,
9313                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9314                        ..Default::default()
9315                    }),
9316                    fdecl::Capability::Runner(fdecl::Runner {
9317                        name: Some("a".repeat(256)),
9318                        source_path: Some("/c".repeat(2048)),
9319                        ..Default::default()
9320                    }),
9321                    fdecl::Capability::Resolver(fdecl::Resolver {
9322                        name: Some("a".repeat(256)),
9323                        source_path: Some("/c".repeat(2048)),
9324                        ..Default::default()
9325                    }),
9326                    fdecl::Capability::Dictionary(fdecl::Dictionary {
9327                        name: Some("a".repeat(256)),
9328                        ..Default::default()
9329                    }),
9330                ]);
9331                decl
9332            },
9333            result = Err(ErrorList::new(vec![
9334                Error::field_too_long(DeclType::Dictionary, "name"),
9335                Error::field_too_long(DeclType::Service, "name"),
9336                Error::field_too_long(DeclType::Service, "source_path"),
9337                Error::field_too_long(DeclType::Protocol, "name"),
9338                Error::field_too_long(DeclType::Protocol, "source_path"),
9339                Error::field_too_long(DeclType::Directory, "name"),
9340                Error::field_too_long(DeclType::Directory, "source_path"),
9341                Error::field_too_long(DeclType::Storage, "source.child.name"),
9342                Error::field_too_long(DeclType::Storage, "name"),
9343                Error::field_too_long(DeclType::Storage, "backing_dir"),
9344                Error::field_too_long(DeclType::Runner, "name"),
9345                Error::field_too_long(DeclType::Runner, "source_path"),
9346                Error::field_too_long(DeclType::Resolver, "name"),
9347                Error::field_too_long(DeclType::Resolver, "source_path"),
9348            ])),
9349        },
9350        test_validate_capabilities_duplicate_name => {
9351            input = {
9352                let mut decl = new_component_decl();
9353                decl.capabilities = Some(vec![
9354                    fdecl::Capability::Service(fdecl::Service {
9355                        name: Some("service".to_string()),
9356                        source_path: Some("/service".to_string()),
9357                        ..Default::default()
9358                    }),
9359                    fdecl::Capability::Service(fdecl::Service {
9360                        name: Some("service".to_string()),
9361                        source_path: Some("/service".to_string()),
9362                        ..Default::default()
9363                    }),
9364                    fdecl::Capability::Protocol(fdecl::Protocol {
9365                        name: Some("protocol".to_string()),
9366                        source_path: Some("/protocol".to_string()),
9367                        ..Default::default()
9368                    }),
9369                    fdecl::Capability::Protocol(fdecl::Protocol {
9370                        name: Some("protocol".to_string()),
9371                        source_path: Some("/protocol".to_string()),
9372                        ..Default::default()
9373                    }),
9374                    fdecl::Capability::Directory(fdecl::Directory {
9375                        name: Some("directory".to_string()),
9376                        source_path: Some("/directory".to_string()),
9377                        rights: Some(fio::Operations::CONNECT),
9378                        ..Default::default()
9379                    }),
9380                    fdecl::Capability::Directory(fdecl::Directory {
9381                        name: Some("directory".to_string()),
9382                        source_path: Some("/directory".to_string()),
9383                        rights: Some(fio::Operations::CONNECT),
9384                        ..Default::default()
9385                    }),
9386                    fdecl::Capability::Storage(fdecl::Storage {
9387                        name: Some("storage".to_string()),
9388                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
9389                        backing_dir: Some("directory".to_string()),
9390                        subdir: None,
9391                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9392                        ..Default::default()
9393                    }),
9394                    fdecl::Capability::Storage(fdecl::Storage {
9395                        name: Some("storage".to_string()),
9396                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
9397                        backing_dir: Some("directory".to_string()),
9398                        subdir: None,
9399                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9400                        ..Default::default()
9401                    }),
9402                    fdecl::Capability::Runner(fdecl::Runner {
9403                        name: Some("runner".to_string()),
9404                        source_path: Some("/runner".to_string()),
9405                        ..Default::default()
9406                    }),
9407                    fdecl::Capability::Runner(fdecl::Runner {
9408                        name: Some("runner".to_string()),
9409                        source_path: Some("/runner".to_string()),
9410                        ..Default::default()
9411                    }),
9412                    fdecl::Capability::Resolver(fdecl::Resolver {
9413                        name: Some("resolver".to_string()),
9414                        source_path: Some("/resolver".to_string()),
9415                        ..Default::default()
9416                    }),
9417                    fdecl::Capability::Resolver(fdecl::Resolver {
9418                        name: Some("resolver".to_string()),
9419                        source_path: Some("/resolver".to_string()),
9420                        ..Default::default()
9421                    }),
9422                    fdecl::Capability::Dictionary(fdecl::Dictionary {
9423                        name: Some("dictionary".to_string()),
9424                        ..Default::default()
9425                    }),
9426                    fdecl::Capability::Dictionary(fdecl::Dictionary {
9427                        name: Some("dictionary".to_string()),
9428                        ..Default::default()
9429                    }),
9430                ]);
9431                decl
9432            },
9433            result = Err(ErrorList::new(vec![
9434                Error::duplicate_field(DeclType::Dictionary, "name", "dictionary"),
9435                Error::duplicate_field(DeclType::Service, "name", "service"),
9436                Error::duplicate_field(DeclType::Protocol, "name", "protocol"),
9437                Error::duplicate_field(DeclType::Directory, "name", "directory"),
9438                Error::duplicate_field(DeclType::Storage, "name", "storage"),
9439                Error::duplicate_field(DeclType::Runner, "name", "runner"),
9440                Error::duplicate_field(DeclType::Resolver, "name", "resolver"),
9441            ])),
9442        },
9443        test_validate_invalid_service_aggregation_conflicting_filter => {
9444            input = {
9445                let mut decl = new_component_decl();
9446                decl.offers = Some(vec![
9447                    fdecl::Offer::Service(fdecl::OfferService {
9448                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9449                            name: "coll_a".to_string()
9450                        })),
9451                        source_name: Some("fuchsia.logger.Log".to_string()),
9452                        target: Some(fdecl::Ref::Child(
9453                            fdecl::ChildRef {
9454                                name: "child_c".to_string(),
9455                                collection: None,
9456                            }
9457                        )),
9458                        target_name: Some("fuchsia.logger.Log1".to_string()),
9459                        source_instance_filter: Some(vec!["default".to_string()]),
9460                        ..Default::default()
9461                    }),
9462                    fdecl::Offer::Service(fdecl::OfferService {
9463                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9464                            name: "coll_b".to_string()
9465                        })),
9466                        source_name: Some("fuchsia.logger.Log".to_string()),
9467                        target: Some(fdecl::Ref::Child(
9468                            fdecl::ChildRef {
9469                                name: "child_c".to_string(),
9470                                collection: None,
9471                            }
9472                        )),
9473                        target_name: Some("fuchsia.logger.Log1".to_string()),
9474                        source_instance_filter: Some(vec!["default".to_string()]),
9475                        ..Default::default()
9476                    }),
9477                ]);
9478                decl.collections = Some(vec![
9479                    fdecl::Collection {
9480                        name: Some("coll_a".to_string()),
9481                        durability: Some(fdecl::Durability::Transient),
9482                        ..Default::default()
9483                    },
9484                    fdecl::Collection {
9485                        name: Some("coll_b".to_string()),
9486                        durability: Some(fdecl::Durability::Transient),
9487                        ..Default::default()
9488                    },
9489                ]);
9490                decl.children = Some(vec![
9491                    fdecl::Child {
9492                        name: Some("child_c".to_string()),
9493                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9494                        startup: Some(fdecl::StartupMode::Lazy),
9495                        ..Default::default()
9496                    },
9497                ]);
9498                decl
9499            },
9500            result = Err(ErrorList::new(vec![
9501                Error::invalid_aggregate_offer("Conflicting source_instance_filter in aggregate \
9502                   service offer, instance_name 'default' seen in filter lists multiple times"),
9503            ])),
9504        },
9505
9506        test_validate_invalid_service_aggregation_conflicting_source_name => {
9507            input = {
9508                let mut decl = new_component_decl();
9509                decl.offers = Some(vec![
9510                    fdecl::Offer::Service(fdecl::OfferService {
9511                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9512                            name: "coll_a".into()
9513                        })),
9514                        source_name: Some("fuchsia.logger.Log".to_string()),
9515                        target: Some(fdecl::Ref::Child(
9516                            fdecl::ChildRef {
9517                                name: "child_c".to_string(),
9518                                collection: None,
9519                            }
9520                        )),
9521                        target_name: Some("fuchsia.logger.Log2".to_string()),
9522                        source_instance_filter: Some(vec!["default2".to_string()]),
9523                        ..Default::default()
9524                    }),
9525                    fdecl::Offer::Service(fdecl::OfferService {
9526                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9527                            name: "coll_b".into()
9528                        })),
9529                        source_name: Some("fuchsia.logger.LogAlt".to_string()),
9530                        target: Some(fdecl::Ref::Child(
9531                            fdecl::ChildRef {
9532                                name: "child_c".to_string(),
9533                                collection: None,
9534                            }
9535                        )),
9536                        target_name: Some("fuchsia.logger.Log2".to_string()),
9537                        source_instance_filter: Some(vec!["default".to_string()]),
9538                        ..Default::default()
9539                    })
9540                ]);
9541                decl.collections = Some(vec![
9542                    fdecl::Collection {
9543                        name: Some("coll_a".to_string()),
9544                        durability: Some(fdecl::Durability::Transient),
9545                        ..Default::default()
9546                    },
9547                    fdecl::Collection {
9548                        name: Some("coll_b".to_string()),
9549                        durability: Some(fdecl::Durability::Transient),
9550                        ..Default::default()
9551                    },
9552                ]);
9553                decl.children = Some(vec![
9554                    fdecl::Child {
9555                        name: Some("child_c".to_string()),
9556                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9557                        startup: Some(fdecl::StartupMode::Lazy),
9558                        on_terminate: None,
9559                        environment: None,
9560                        ..Default::default()
9561                    },
9562                ]);
9563                decl
9564            },
9565            result = Err(ErrorList::new(vec![
9566                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."),
9567            ])),
9568        },
9569
9570        test_validate_resolvers_missing_from_offer => {
9571            input = {
9572                let mut decl = new_component_decl();
9573                decl.offers = Some(vec![fdecl::Offer::Resolver(fdecl::OfferResolver {
9574                    source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
9575                    source_name: Some("a".to_string()),
9576                    target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
9577                    target_name: Some("a".to_string()),
9578                    ..Default::default()
9579                })]);
9580                decl.children = Some(vec![fdecl::Child {
9581                    name: Some("child".to_string()),
9582                    url: Some("test:///child".to_string()),
9583                    startup: Some(fdecl::StartupMode::Eager),
9584                    on_terminate: None,
9585                    environment: None,
9586                    ..Default::default()
9587                }]);
9588                decl
9589            },
9590            result = Err(ErrorList::new(vec![
9591                Error::invalid_capability(DeclType::OfferResolver, "source", "a"),
9592            ])),
9593        },
9594        test_validate_resolvers_missing_from_expose => {
9595            input = {
9596                let mut decl = new_component_decl();
9597                decl.exposes = Some(vec![fdecl::Expose::Resolver(fdecl::ExposeResolver {
9598                    source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
9599                    source_name: Some("a".to_string()),
9600                    target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
9601                    target_name: Some("a".to_string()),
9602                    ..Default::default()
9603                })]);
9604                decl
9605            },
9606            result = Err(ErrorList::new(vec![
9607                Error::invalid_capability(DeclType::ExposeResolver, "source", "a"),
9608            ])),
9609        },
9610
9611        test_validate_config_missing_config => {
9612            input = {
9613                let mut decl = new_component_decl();
9614                decl.config = Some(fdecl::ConfigSchema{
9615                    fields: None,
9616                    checksum: None,
9617                    value_source: None,
9618                    ..Default::default()
9619                });
9620                decl
9621            },
9622            result = Err(ErrorList::new(vec![
9623                Error::missing_field(DeclType::ConfigSchema, "fields"),
9624                Error::missing_field(DeclType::ConfigSchema, "checksum"),
9625                Error::missing_field(DeclType::ConfigSchema, "value_source"),
9626            ])),
9627        },
9628
9629        test_validate_config_missing_config_field => {
9630            input = {
9631                let mut decl = new_component_decl();
9632                decl.config = Some(fdecl::ConfigSchema{
9633                    fields: Some(vec![
9634                        fdecl::ConfigField {
9635                            key: None,
9636                            type_: None,
9637                            ..Default::default()
9638                        }
9639                    ]),
9640                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9641                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9642                    ..Default::default()
9643                });
9644                decl
9645            },
9646            result = Err(ErrorList::new(vec![
9647                Error::missing_field(DeclType::ConfigField, "key"),
9648                Error::missing_field(DeclType::ConfigField, "value_type"),
9649            ])),
9650        },
9651
9652        test_validate_config_bool => {
9653            input = {
9654                let mut decl = new_component_decl();
9655                decl.config = Some(fdecl::ConfigSchema{
9656                    fields: Some(vec![
9657                        fdecl::ConfigField {
9658                            key: Some("test".to_string()),
9659                            type_: Some(fdecl::ConfigType {
9660                                layout: fdecl::ConfigTypeLayout::Bool,
9661                                parameters: Some(vec![]),
9662                                constraints: vec![]
9663                            }),
9664                            ..Default::default()
9665                        }
9666                    ]),
9667                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9668                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9669                    ..Default::default()
9670                });
9671                decl
9672            },
9673            result = Ok(()),
9674        },
9675
9676        test_validate_config_bool_extra_constraint => {
9677            input = {
9678                let mut decl = new_component_decl();
9679                decl.config = Some(fdecl::ConfigSchema{
9680                    fields: Some(vec![
9681                        fdecl::ConfigField {
9682                            key: Some("test".to_string()),
9683                            type_: Some(fdecl::ConfigType {
9684                                layout: fdecl::ConfigTypeLayout::Bool,
9685                                parameters: Some(vec![]),
9686                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9687                            }),
9688                            ..Default::default()
9689                        }
9690                    ]),
9691                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9692                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9693                    ..Default::default()
9694                });
9695                decl
9696            },
9697            result = Err(ErrorList::new(vec![
9698                Error::extraneous_field(DeclType::ConfigType, "constraints")
9699            ])),
9700        },
9701
9702        test_validate_config_bool_missing_parameters => {
9703            input = {
9704                let mut decl = new_component_decl();
9705                decl.config = Some(fdecl::ConfigSchema{
9706                    fields: Some(vec![
9707                        fdecl::ConfigField {
9708                            key: Some("test".to_string()),
9709                            type_: Some(fdecl::ConfigType {
9710                                layout: fdecl::ConfigTypeLayout::Bool,
9711                                parameters: None,
9712                                constraints: vec![]
9713                            }),
9714                            ..Default::default()
9715                        }
9716                    ]),
9717                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9718                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9719                    ..Default::default()
9720                });
9721                decl
9722            },
9723            result = Err(ErrorList::new(vec![
9724                Error::missing_field(DeclType::ConfigType, "parameters")
9725            ])),
9726        },
9727
9728        test_validate_config_string => {
9729            input = {
9730                let mut decl = new_component_decl();
9731                decl.config = Some(fdecl::ConfigSchema{
9732                    fields: Some(vec![
9733                        fdecl::ConfigField {
9734                            key: Some("test".to_string()),
9735                            type_: Some(fdecl::ConfigType {
9736                                layout: fdecl::ConfigTypeLayout::String,
9737                                parameters: Some(vec![]),
9738                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9739                            }),
9740                            ..Default::default()
9741                        }
9742                    ]),
9743                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9744                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9745                    ..Default::default()
9746                });
9747                decl
9748            },
9749            result = Ok(()),
9750        },
9751
9752        test_validate_config_string_missing_parameter => {
9753            input = {
9754                let mut decl = new_component_decl();
9755                decl.config = Some(fdecl::ConfigSchema{
9756                    fields: Some(vec![
9757                        fdecl::ConfigField {
9758                            key: Some("test".to_string()),
9759                            type_: Some(fdecl::ConfigType {
9760                                layout: fdecl::ConfigTypeLayout::String,
9761                                parameters: None,
9762                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9763                            }),
9764                            ..Default::default()
9765                        }
9766                    ]),
9767                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9768                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9769                    ..Default::default()
9770                });
9771                decl
9772            },
9773            result = Err(ErrorList::new(vec![
9774                Error::missing_field(DeclType::ConfigType, "parameters")
9775            ])),
9776        },
9777
9778        test_validate_config_string_missing_constraint => {
9779            input = {
9780                let mut decl = new_component_decl();
9781                decl.config = Some(fdecl::ConfigSchema{
9782                    fields: Some(vec![
9783                        fdecl::ConfigField {
9784                            key: Some("test".to_string()),
9785                            type_: Some(fdecl::ConfigType {
9786                                layout: fdecl::ConfigTypeLayout::String,
9787                                parameters: Some(vec![]),
9788                                constraints: vec![]
9789                            }),
9790                            ..Default::default()
9791                        }
9792                    ]),
9793                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9794                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9795                    ..Default::default()
9796                });
9797                decl
9798            },
9799            result = Err(ErrorList::new(vec![
9800                Error::missing_field(DeclType::ConfigType, "constraints")
9801            ])),
9802        },
9803
9804        test_validate_config_string_extra_constraint => {
9805            input = {
9806                let mut decl = new_component_decl();
9807                decl.config = Some(fdecl::ConfigSchema{
9808                    fields: Some(vec![
9809                        fdecl::ConfigField {
9810                            key: Some("test".to_string()),
9811                            type_: Some(fdecl::ConfigType {
9812                                layout: fdecl::ConfigTypeLayout::String,
9813                                parameters: Some(vec![]),
9814                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10), fdecl::LayoutConstraint::MaxSize(10)]
9815                            }),
9816                            ..Default::default()
9817                        }
9818                    ]),
9819                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9820                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9821                    ..Default::default()
9822                });
9823                decl
9824            },
9825            result = Err(ErrorList::new(vec![
9826                Error::extraneous_field(DeclType::ConfigType, "constraints")
9827            ])),
9828        },
9829
9830        test_validate_config_vector_bool => {
9831            input = {
9832                let mut decl = new_component_decl();
9833                decl.config = Some(fdecl::ConfigSchema{
9834                    fields: Some(vec![
9835                        fdecl::ConfigField {
9836                            key: Some("test".to_string()),
9837                            type_: Some(fdecl::ConfigType {
9838                                layout: fdecl::ConfigTypeLayout::Vector,
9839                                parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9840                                    layout: fdecl::ConfigTypeLayout::Bool,
9841                                    parameters: Some(vec![]),
9842                                    constraints: vec![],
9843                                })]),
9844                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9845                            }),
9846                            ..Default::default()
9847                        }
9848                    ]),
9849                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9850                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9851                    ..Default::default()
9852                });
9853                decl
9854            },
9855            result = Ok(()),
9856        },
9857
9858        test_validate_config_vector_extra_parameter => {
9859            input = {
9860                let mut decl = new_component_decl();
9861                decl.config = Some(fdecl::ConfigSchema{
9862                    fields: Some(vec![
9863                        fdecl::ConfigField {
9864                            key: Some("test".to_string()),
9865                            type_: Some(fdecl::ConfigType {
9866                                layout: fdecl::ConfigTypeLayout::Vector,
9867                                parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9868                                    layout: fdecl::ConfigTypeLayout::Bool,
9869                                    parameters: Some(vec![]),
9870                                    constraints: vec![],
9871                                }), fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9872                                    layout: fdecl::ConfigTypeLayout::Uint8,
9873                                    parameters: Some(vec![]),
9874                                    constraints: vec![],
9875                                })]),
9876                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9877                            }),
9878                            ..Default::default()
9879                        }
9880                    ]),
9881                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9882                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9883                    ..Default::default()
9884                });
9885                decl
9886            },
9887            result = Err(ErrorList::new(vec![
9888                Error::extraneous_field(DeclType::ConfigType, "parameters")
9889            ])),
9890        },
9891
9892        test_validate_config_vector_missing_parameter => {
9893            input = {
9894                let mut decl = new_component_decl();
9895                decl.config = Some(fdecl::ConfigSchema{
9896                    fields: Some(vec![
9897                        fdecl::ConfigField {
9898                            key: Some("test".to_string()),
9899                            type_: Some(fdecl::ConfigType {
9900                                layout: fdecl::ConfigTypeLayout::Vector,
9901                                parameters: Some(vec![]),
9902                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9903                            }),
9904                            ..Default::default()
9905                        }
9906                    ]),
9907                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9908                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9909                    ..Default::default()
9910                });
9911                decl
9912            },
9913            result = Err(ErrorList::new(vec![
9914                Error::missing_field(DeclType::ConfigType, "parameters")
9915            ])),
9916        },
9917
9918        test_validate_config_vector_string => {
9919            input = {
9920                let mut decl = new_component_decl();
9921                decl.config = Some(fdecl::ConfigSchema{
9922                    fields: Some(vec![
9923                        fdecl::ConfigField {
9924                            key: Some("test".to_string()),
9925                            type_: Some(fdecl::ConfigType {
9926                                layout: fdecl::ConfigTypeLayout::Vector,
9927                                parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9928                                    layout: fdecl::ConfigTypeLayout::String,
9929                                    parameters: Some(vec![]),
9930                                    constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9931                                })]),
9932                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9933                            }),
9934                            ..Default::default()
9935                        }
9936                    ]),
9937                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9938                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9939                    ..Default::default()
9940                });
9941                decl
9942            },
9943            result = Ok(()),
9944        },
9945
9946        test_validate_config_vector_vector => {
9947            input = {
9948                let mut decl = new_component_decl();
9949                decl.config = Some(fdecl::ConfigSchema{
9950                    fields: Some(vec![
9951                        fdecl::ConfigField {
9952                            key: Some("test".to_string()),
9953                            type_: Some(fdecl::ConfigType {
9954                                layout: fdecl::ConfigTypeLayout::Vector,
9955                                parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9956                                    layout: fdecl::ConfigTypeLayout::Vector,
9957                                    parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9958                                        layout: fdecl::ConfigTypeLayout::String,
9959                                        parameters: Some(vec![]),
9960                                        constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9961                                    })]),
9962                                    constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9963                                })]),
9964                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9965                            }),
9966                            ..Default::default()
9967                        }
9968                    ]),
9969                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9970                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9971                    ..Default::default()
9972                });
9973                decl
9974            },
9975            result = Err(ErrorList::new(vec![
9976                Error::nested_vector()
9977            ])),
9978        },
9979
9980        test_validate_exposes_invalid_aggregation_different_availability => {
9981            input = {
9982                let mut decl = new_component_decl();
9983                decl.exposes = Some(vec![
9984                    fdecl::Expose::Service(fdecl::ExposeService {
9985                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9986                            name: "coll_a".into()
9987                        })),
9988                        source_name: Some("fuchsia.logger.Log".to_string()),
9989                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
9990                        target_name: Some("fuchsia.logger.Log".to_string()),
9991                        availability: Some(fdecl::Availability::Required),
9992                        ..Default::default()
9993                    }),
9994                    fdecl::Expose::Service(fdecl::ExposeService {
9995                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9996                            name: "coll_b".into()
9997                        })),
9998                        source_name: Some("fuchsia.logger.Log".to_string()),
9999                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
10000                        target_name: Some("fuchsia.logger.Log".to_string()),
10001                        availability: Some(fdecl::Availability::Optional),
10002                        ..Default::default()
10003                    })
10004                ]);
10005                decl.collections = Some(vec![
10006                    fdecl::Collection {
10007                        name: Some("coll_a".to_string()),
10008                        durability: Some(fdecl::Durability::Transient),
10009                        ..Default::default()
10010                    },
10011                    fdecl::Collection {
10012                        name: Some("coll_b".to_string()),
10013                        durability: Some(fdecl::Durability::Transient),
10014                        ..Default::default()
10015                    },
10016                ]);
10017                decl
10018            },
10019            result = Err(ErrorList::new(vec![
10020                Error::DifferentAvailabilityInAggregation(AvailabilityList(vec![
10021                    fdecl::Availability::Required,
10022                    fdecl::Availability::Optional,
10023                ]))
10024            ])),
10025        },
10026
10027        test_validate_offers_invalid_aggregation_different_availability => {
10028            input = {
10029                let mut decl = new_component_decl();
10030                decl.offers = Some(vec![
10031                    fdecl::Offer::Service(fdecl::OfferService {
10032                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10033                            name: "coll_a".into()
10034                        })),
10035                        source_name: Some("fuchsia.logger.Log".to_string()),
10036                        target: Some(fdecl::Ref::Child(
10037                            fdecl::ChildRef {
10038                                name: "child_c".to_string(),
10039                                collection: None,
10040                            }
10041                        )),
10042                        target_name: Some("fuchsia.logger.Log".to_string()),
10043                        source_instance_filter: Some(vec!["default".to_string()]),
10044                        availability: Some(fdecl::Availability::Required),
10045                        ..Default::default()
10046                    }),
10047                    fdecl::Offer::Service(fdecl::OfferService {
10048                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10049                            name: "coll_b".into()
10050                        })),
10051                        source_name: Some("fuchsia.logger.Log".to_string()),
10052                        target: Some(fdecl::Ref::Child(
10053                            fdecl::ChildRef {
10054                                name: "child_c".to_string(),
10055                                collection: None,
10056                            }
10057                        )),
10058                        target_name: Some("fuchsia.logger.Log".to_string()),
10059                        source_instance_filter: Some(vec!["a_different_default".to_string()]),
10060                        availability: Some(fdecl::Availability::Optional),
10061                        ..Default::default()
10062                    })
10063                ]);
10064                decl.collections = Some(vec![
10065                    fdecl::Collection {
10066                        name: Some("coll_a".to_string()),
10067                        durability: Some(fdecl::Durability::Transient),
10068                        ..Default::default()
10069                    },
10070                    fdecl::Collection {
10071                        name: Some("coll_b".to_string()),
10072                        durability: Some(fdecl::Durability::Transient),
10073                        ..Default::default()
10074                    },
10075                ]);
10076                decl.children = Some(vec![
10077                    fdecl::Child {
10078                        name: Some("child_c".to_string()),
10079                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
10080                        startup: Some(fdecl::StartupMode::Lazy),
10081                        on_terminate: None,
10082                        environment: None,
10083                        ..Default::default()
10084                    },
10085                ]);
10086                decl
10087            },
10088            result = Err(ErrorList::new(vec![
10089                Error::DifferentAvailabilityInAggregation(AvailabilityList(vec![
10090                    fdecl::Availability::Required,
10091                    fdecl::Availability::Optional,
10092                ]))
10093            ])),
10094        },
10095    }
10096
10097    test_validate_capabilities! {
10098        test_validate_capabilities_individually_ok => {
10099            input = vec![
10100                fdecl::Capability::Protocol(fdecl::Protocol {
10101                    name: Some("foo_svc".into()),
10102                    source_path: Some("/svc/foo".into()),
10103                    ..Default::default()
10104                }),
10105                fdecl::Capability::Directory(fdecl::Directory {
10106                    name: Some("foo_dir".into()),
10107                    source_path: Some("/foo".into()),
10108                    rights: Some(fio::Operations::CONNECT),
10109                    ..Default::default()
10110                }),
10111            ],
10112            as_builtin = false,
10113            result = Ok(()),
10114        },
10115        test_validate_capabilities_individually_err => {
10116            input = vec![
10117                fdecl::Capability::Protocol(fdecl::Protocol {
10118                    name: None,
10119                    source_path: None,
10120                    ..Default::default()
10121                }),
10122                fdecl::Capability::Directory(fdecl::Directory {
10123                    name: None,
10124                    source_path: None,
10125                    rights: None,
10126                    ..Default::default()
10127                }),
10128            ],
10129            as_builtin = false,
10130            result = Err(ErrorList::new(vec![
10131                Error::missing_field(DeclType::Protocol, "name"),
10132                Error::missing_field(DeclType::Protocol, "source_path"),
10133                Error::missing_field(DeclType::Directory, "name"),
10134                Error::missing_field(DeclType::Directory, "source_path"),
10135                Error::missing_field(DeclType::Directory, "rights"),
10136            ])),
10137        },
10138        test_validate_builtin_capabilities_individually_ok => {
10139            input = vec![
10140                fdecl::Capability::Protocol(fdecl::Protocol {
10141                    name: Some("foo_protocol".into()),
10142                    source_path: None,
10143                    ..Default::default()
10144                }),
10145                fdecl::Capability::Directory(fdecl::Directory {
10146                    name: Some("foo_dir".into()),
10147                    source_path: None,
10148                    rights: Some(fio::Operations::CONNECT),
10149                    ..Default::default()
10150                }),
10151                fdecl::Capability::Service(fdecl::Service {
10152                    name: Some("foo_svc".into()),
10153                    source_path: None,
10154                    ..Default::default()
10155                }),
10156                fdecl::Capability::Runner(fdecl::Runner {
10157                    name: Some("foo_runner".into()),
10158                    source_path: None,
10159                    ..Default::default()
10160                }),
10161                fdecl::Capability::Resolver(fdecl::Resolver {
10162                    name: Some("foo_resolver".into()),
10163                    source_path: None,
10164                    ..Default::default()
10165                }),
10166            ],
10167            as_builtin = true,
10168            result = Ok(()),
10169        },
10170        test_validate_builtin_capabilities_individually_err => {
10171            input = vec![
10172                fdecl::Capability::Protocol(fdecl::Protocol {
10173                    name: None,
10174                    source_path: Some("/svc/foo".into()),
10175                    ..Default::default()
10176                }),
10177                fdecl::Capability::Directory(fdecl::Directory {
10178                    name: None,
10179                    source_path: Some("/foo".into()),
10180                    rights: None,
10181                    ..Default::default()
10182                }),
10183                fdecl::Capability::Service(fdecl::Service {
10184                    name: None,
10185                    source_path: Some("/svc/foo".into()),
10186                    ..Default::default()
10187                }),
10188                fdecl::Capability::Runner(fdecl::Runner {
10189                    name: None,
10190                    source_path:  Some("/foo".into()),
10191                    ..Default::default()
10192                }),
10193                fdecl::Capability::Resolver(fdecl::Resolver {
10194                    name: None,
10195                    source_path:  Some("/foo".into()),
10196                    ..Default::default()
10197                }),
10198                fdecl::Capability::Storage(fdecl::Storage {
10199                    name: None,
10200                    ..Default::default()
10201                }),
10202            ],
10203            as_builtin = true,
10204            result = Err(ErrorList::new(vec![
10205                Error::missing_field(DeclType::Protocol, "name"),
10206                Error::extraneous_source_path(DeclType::Protocol, "/svc/foo"),
10207                Error::missing_field(DeclType::Directory, "name"),
10208                Error::extraneous_source_path(DeclType::Directory, "/foo"),
10209                Error::missing_field(DeclType::Directory, "rights"),
10210                Error::missing_field(DeclType::Service, "name"),
10211                Error::extraneous_source_path(DeclType::Service, "/svc/foo"),
10212                Error::missing_field(DeclType::Runner, "name"),
10213                Error::extraneous_source_path(DeclType::Runner, "/foo"),
10214                Error::missing_field(DeclType::Resolver, "name"),
10215                Error::extraneous_source_path(DeclType::Resolver, "/foo"),
10216                Error::CapabilityCannotBeBuiltin(DeclType::Storage),
10217            ])),
10218        },
10219        test_validate_delivery_type_ok => {
10220            input = vec![
10221                fdecl::Capability::Protocol(fdecl::Protocol {
10222                    name: Some("foo_svc1".into()),
10223                    source_path: Some("/svc/foo1".into()),
10224                    ..Default::default()
10225                }),
10226                fdecl::Capability::Protocol(fdecl::Protocol {
10227                    name: Some("foo_svc2".into()),
10228                    source_path: Some("/svc/foo2".into()),
10229                    delivery: Some(fdecl::DeliveryType::Immediate),
10230                    ..Default::default()
10231                }),
10232                fdecl::Capability::Protocol(fdecl::Protocol {
10233                    name: Some("foo_svc3".into()),
10234                    source_path: Some("/svc/foo3".into()),
10235                    delivery: Some(fdecl::DeliveryType::OnReadable),
10236                    ..Default::default()
10237                }),
10238            ],
10239            as_builtin = false,
10240            result = Ok(()),
10241        },
10242        test_validate_delivery_type_err => {
10243            input = vec![
10244                fdecl::Capability::Protocol(fdecl::Protocol {
10245                    name: Some("foo_svc".into()),
10246                    source_path: Some("/svc/foo".into()),
10247                    delivery: Some(fdecl::DeliveryType::unknown()),
10248                    ..Default::default()
10249                }),
10250            ],
10251            as_builtin = false,
10252            result = Err(ErrorList::new(vec![
10253                Error::invalid_field(DeclType::Protocol, "delivery"),
10254            ])),
10255        },
10256    }
10257
10258    /// Passes different source and availability options to `new_expose` to
10259    /// generate a component declaration.
10260    fn generate_expose_different_source_and_availability_decl(
10261        new_expose: impl Fn(fdecl::Ref, fdecl::Availability, &str) -> fdecl::Expose,
10262    ) -> fdecl::Component {
10263        let mut decl = new_component_decl();
10264        let child = "child";
10265        decl.children = Some(vec![fdecl::Child {
10266            name: Some(child.to_string()),
10267            url: Some("fuchsia-pkg://fuchsia.com/source#meta/source.cm".to_string()),
10268            startup: Some(fdecl::StartupMode::Lazy),
10269            ..Default::default()
10270        }]);
10271        decl.exposes = Some(vec![
10272            // Optional expose from self is okay.
10273            new_expose(
10274                fdecl::Ref::Self_(fdecl::SelfRef {}),
10275                fdecl::Availability::Optional,
10276                "fuchsia.examples.Echo1",
10277            ),
10278            // Optional expose from child is okay.
10279            new_expose(
10280                fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
10281                fdecl::Availability::Optional,
10282                "fuchsia.examples.Echo2",
10283            ),
10284            // Optional expose from void is okay.
10285            new_expose(
10286                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10287                fdecl::Availability::Optional,
10288                "fuchsia.examples.Echo3",
10289            ),
10290            // Optional expose from framework is okay.
10291            new_expose(
10292                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10293                fdecl::Availability::Optional,
10294                "fuchsia.examples.Echo4",
10295            ),
10296            // Transitional expose from self is okay.
10297            new_expose(
10298                fdecl::Ref::Self_(fdecl::SelfRef {}),
10299                fdecl::Availability::Transitional,
10300                "fuchsia.examples.Echo5",
10301            ),
10302            // Transitional expose from child is okay.
10303            new_expose(
10304                fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
10305                fdecl::Availability::Transitional,
10306                "fuchsia.examples.Echo6",
10307            ),
10308            // Transitional expose from void is okay.
10309            new_expose(
10310                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10311                fdecl::Availability::Transitional,
10312                "fuchsia.examples.Echo7",
10313            ),
10314            // Transitional expose from framework is okay.
10315            new_expose(
10316                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10317                fdecl::Availability::Transitional,
10318                "fuchsia.examples.Echo8",
10319            ),
10320            // Same-as-target expose from self is okay.
10321            new_expose(
10322                fdecl::Ref::Self_(fdecl::SelfRef {}),
10323                fdecl::Availability::SameAsTarget,
10324                "fuchsia.examples.Echo9",
10325            ),
10326            // Same-as-target expose from child is okay.
10327            new_expose(
10328                fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
10329                fdecl::Availability::SameAsTarget,
10330                "fuchsia.examples.Echo10",
10331            ),
10332            // Same-as-target expose from framework is okay.
10333            new_expose(
10334                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10335                fdecl::Availability::SameAsTarget,
10336                "fuchsia.examples.Echo11",
10337            ),
10338            // Required expose from void is an error.
10339            new_expose(
10340                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10341                fdecl::Availability::Required,
10342                "fuchsia.examples.Echo12",
10343            ),
10344            // Same-as-target expose from void is an error.
10345            new_expose(
10346                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10347                fdecl::Availability::SameAsTarget,
10348                "fuchsia.examples.Echo13",
10349            ),
10350        ]);
10351        decl
10352    }
10353
10354    /// Passes different source and availability options to `new_offer` to
10355    /// generate a component declaration.
10356    fn generate_offer_different_source_and_availability_decl(
10357        new_offer: impl Fn(fdecl::Ref, fdecl::Availability, &str) -> fdecl::Offer,
10358    ) -> fdecl::Component {
10359        let mut decl = new_component_decl();
10360        decl.children = Some(vec![
10361            fdecl::Child {
10362                name: Some("source".to_string()),
10363                url: Some("fuchsia-pkg://fuchsia.com/source#meta/source.cm".to_string()),
10364                startup: Some(fdecl::StartupMode::Lazy),
10365                on_terminate: None,
10366                environment: None,
10367                ..Default::default()
10368            },
10369            fdecl::Child {
10370                name: Some("sink".to_string()),
10371                url: Some("fuchsia-pkg://fuchsia.com/sink#meta/sink.cm".to_string()),
10372                startup: Some(fdecl::StartupMode::Lazy),
10373                on_terminate: None,
10374                environment: None,
10375                ..Default::default()
10376            },
10377        ]);
10378        decl.offers = Some(vec![
10379            // These offers are fine, offers with a source of parent or void can be
10380            // optional.
10381            new_offer(
10382                fdecl::Ref::Parent(fdecl::ParentRef {}),
10383                fdecl::Availability::Required,
10384                "fuchsia.examples.Echo0",
10385            ),
10386            new_offer(
10387                fdecl::Ref::Parent(fdecl::ParentRef {}),
10388                fdecl::Availability::Optional,
10389                "fuchsia.examples.Echo1",
10390            ),
10391            new_offer(
10392                fdecl::Ref::Parent(fdecl::ParentRef {}),
10393                fdecl::Availability::SameAsTarget,
10394                "fuchsia.examples.Echo2",
10395            ),
10396            new_offer(
10397                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10398                fdecl::Availability::Optional,
10399                "fuchsia.examples.Echo3",
10400            ),
10401            // These offers are fine, offers with a source other than parent or void
10402            // can also be optional.
10403            new_offer(
10404                fdecl::Ref::Self_(fdecl::SelfRef {}),
10405                fdecl::Availability::Optional,
10406                "fuchsia.examples.Echo4",
10407            ),
10408            new_offer(
10409                fdecl::Ref::Self_(fdecl::SelfRef {}),
10410                fdecl::Availability::SameAsTarget,
10411                "fuchsia.examples.Echo5",
10412            ),
10413            new_offer(
10414                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10415                fdecl::Availability::Optional,
10416                "fuchsia.examples.Echo6",
10417            ),
10418            new_offer(
10419                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10420                fdecl::Availability::SameAsTarget,
10421                "fuchsia.examples.Echo7",
10422            ),
10423            new_offer(
10424                fdecl::Ref::Child(fdecl::ChildRef { name: "source".to_string(), collection: None }),
10425                fdecl::Availability::Optional,
10426                "fuchsia.examples.Echo8",
10427            ),
10428            new_offer(
10429                fdecl::Ref::Child(fdecl::ChildRef { name: "source".to_string(), collection: None }),
10430                fdecl::Availability::SameAsTarget,
10431                "fuchsia.examples.Echo9",
10432            ),
10433            // These offers are also not fine, offers with a source of void must be optional
10434            new_offer(
10435                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10436                fdecl::Availability::Required,
10437                "fuchsia.examples.Echo10",
10438            ),
10439            new_offer(
10440                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10441                fdecl::Availability::SameAsTarget,
10442                "fuchsia.examples.Echo11",
10443            ),
10444        ]);
10445        decl
10446    }
10447
10448    #[test]
10449    fn test_validate_dynamic_offers_empty() {
10450        assert_eq!(
10451            validate_dynamic_offers(
10452                vec![],
10453                &mut DirectedGraph::new(),
10454                &vec![],
10455                &fdecl::Component::default()
10456            ),
10457            Ok(())
10458        );
10459    }
10460
10461    #[test]
10462    fn test_validate_dynamic_offers_okay() {
10463        assert_eq!(
10464            validate_dynamic_offers(
10465                vec![],
10466                &mut DirectedGraph::new(),
10467                &vec![
10468                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
10469                        dependency_type: Some(fdecl::DependencyType::Strong),
10470                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef)),
10471                        source_name: Some("thing".to_string()),
10472                        target_name: Some("thing".repeat(26)),
10473                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10474                            name: "foo".to_string(),
10475                            collection: Some("foo".to_string()),
10476                        })),
10477                        ..Default::default()
10478                    }),
10479                    fdecl::Offer::Service(fdecl::OfferService {
10480                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10481                        source_name: Some("thang".repeat(26)),
10482                        target_name: Some("thang".repeat(26)),
10483                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10484                            name: "foo".to_string(),
10485                            collection: Some("foo".to_string()),
10486                        })),
10487                        ..Default::default()
10488                    }),
10489                    fdecl::Offer::Directory(fdecl::OfferDirectory {
10490                        dependency_type: Some(fdecl::DependencyType::Strong),
10491                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10492                        source_name: Some("thung1".repeat(26)),
10493                        target_name: Some("thung1".repeat(26)),
10494                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10495                            name: "foo".to_string(),
10496                            collection: Some("foo".to_string()),
10497                        })),
10498                        ..Default::default()
10499                    }),
10500                    fdecl::Offer::Storage(fdecl::OfferStorage {
10501                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10502                        source_name: Some("thung2".repeat(26)),
10503                        target_name: Some("thung2".repeat(26)),
10504                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10505                            name: "foo".to_string(),
10506                            collection: Some("foo".to_string()),
10507                        })),
10508                        ..Default::default()
10509                    }),
10510                    fdecl::Offer::Runner(fdecl::OfferRunner {
10511                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10512                        source_name: Some("thung3".repeat(26)),
10513                        target_name: Some("thung3".repeat(26)),
10514                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10515                            name: "foo".to_string(),
10516                            collection: Some("foo".to_string()),
10517                        })),
10518                        ..Default::default()
10519                    }),
10520                    fdecl::Offer::Resolver(fdecl::OfferResolver {
10521                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10522                        source_name: Some("thung4".repeat(26)),
10523                        target_name: Some("thung4".repeat(26)),
10524                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10525                            name: "foo".to_string(),
10526                            collection: Some("foo".to_string()),
10527                        })),
10528                        ..Default::default()
10529                    }),
10530                ],
10531                &fdecl::Component {
10532                    capabilities: Some(vec![fdecl::Capability::Protocol(fdecl::Protocol {
10533                        name: Some("thing".to_string()),
10534                        source_path: Some("/svc/foo".into()),
10535                        ..Default::default()
10536                    }),]),
10537                    ..Default::default()
10538                }
10539            ),
10540            Ok(())
10541        );
10542    }
10543
10544    #[test]
10545    fn test_validate_dynamic_offers_valid_service_aggregation() {
10546        assert_eq!(
10547            validate_dynamic_offers(
10548                vec![],
10549                &mut DirectedGraph::new(),
10550                &vec![
10551                    fdecl::Offer::Service(fdecl::OfferService {
10552                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10553                            name: "child_a".to_string(),
10554                            collection: None
10555                        })),
10556                        source_name: Some("fuchsia.logger.Log".to_string()),
10557                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10558                            name: "child_c".to_string(),
10559                            collection: None,
10560                        })),
10561                        target_name: Some("fuchsia.logger.Log".to_string()),
10562                        source_instance_filter: Some(vec!["default".to_string()]),
10563                        ..Default::default()
10564                    }),
10565                    fdecl::Offer::Service(fdecl::OfferService {
10566                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10567                            name: "child_b".to_string(),
10568                            collection: None
10569                        })),
10570                        source_name: Some("fuchsia.logger.Log".to_string()),
10571                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10572                            name: "child_c".to_string(),
10573                            collection: None,
10574                        })),
10575                        target_name: Some("fuchsia.logger.Log".to_string()),
10576                        source_instance_filter: Some(vec!["a_different_default".to_string()]),
10577                        ..Default::default()
10578                    })
10579                ],
10580                &fdecl::Component {
10581                    children: Some(vec![
10582                        fdecl::Child {
10583                            name: Some("child_a".to_string()),
10584                            url: Some(
10585                                "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10586                                    .to_string()
10587                            ),
10588                            startup: Some(fdecl::StartupMode::Lazy),
10589                            on_terminate: None,
10590                            environment: None,
10591                            ..Default::default()
10592                        },
10593                        fdecl::Child {
10594                            name: Some("child_b".to_string()),
10595                            url: Some(
10596                                "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10597                                    .to_string()
10598                            ),
10599                            startup: Some(fdecl::StartupMode::Lazy),
10600                            on_terminate: None,
10601                            environment: None,
10602                            ..Default::default()
10603                        },
10604                        fdecl::Child {
10605                            name: Some("child_c".to_string()),
10606                            url: Some(
10607                                "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10608                                    .to_string()
10609                            ),
10610                            startup: Some(fdecl::StartupMode::Lazy),
10611                            on_terminate: None,
10612                            environment: None,
10613                            ..Default::default()
10614                        },
10615                    ]),
10616                    ..Default::default()
10617                }
10618            ),
10619            Ok(())
10620        );
10621    }
10622
10623    #[test]
10624    fn test_validate_dynamic_service_aggregation_missing_filter() {
10625        assert_eq!(
10626            validate_dynamic_offers(
10627                vec![],
10628                &mut DirectedGraph::new(),
10629                &vec![
10630                    fdecl::Offer::Service(fdecl::OfferService {
10631                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10632                            name: "child_a".to_string(),
10633                            collection: None
10634                        })),
10635                        source_name: Some("fuchsia.logger.Log".to_string()),
10636                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10637                            name: "child_c".to_string(),
10638                            collection: None,
10639                        })),
10640                        target_name: Some("fuchsia.logger.Log".to_string()),
10641                        source_instance_filter: Some(vec!["default".to_string()]),
10642                        ..Default::default()
10643                    }),
10644                    fdecl::Offer::Service(fdecl::OfferService {
10645                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10646                            name: "child_b".to_string(),
10647                            collection: None
10648                        })),
10649                        source_name: Some("fuchsia.logger.Log".to_string()),
10650                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10651                            name: "child_c".to_string(),
10652                            collection: None,
10653                        })),
10654                        target_name: Some("fuchsia.logger.Log".to_string()),
10655                        source_instance_filter: None,
10656                        ..Default::default()
10657                    }),
10658                ],
10659                &fdecl::Component {
10660                    children: Some(vec![
10661                        fdecl::Child {
10662                            name: Some("child_a".to_string()),
10663                            url: Some(
10664                                "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10665                                    .to_string()
10666                            ),
10667                            startup: Some(fdecl::StartupMode::Lazy),
10668                            ..Default::default()
10669                        },
10670                        fdecl::Child {
10671                            name: Some("child_b".to_string()),
10672                            url: Some(
10673                                "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10674                                    .to_string()
10675                            ),
10676                            startup: Some(fdecl::StartupMode::Lazy),
10677                            ..Default::default()
10678                        },
10679                        fdecl::Child {
10680                            name: Some("child_c".to_string()),
10681                            url: Some(
10682                                "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10683                                    .to_string()
10684                            ),
10685                            startup: Some(fdecl::StartupMode::Lazy),
10686                            ..Default::default()
10687                        },
10688                    ]),
10689                    ..Default::default()
10690                },
10691            ),
10692            Err(ErrorList::new(vec![Error::invalid_aggregate_offer(
10693                "source_instance_filter must be set for dynamic aggregate service offers"
10694            ),]))
10695        );
10696    }
10697
10698    #[test]
10699    fn test_validate_dynamic_offers_omit_target() {
10700        assert_eq!(
10701            validate_dynamic_offers(
10702                vec![],
10703                &mut DirectedGraph::new(),
10704                &vec![
10705                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
10706                        dependency_type: Some(fdecl::DependencyType::Strong),
10707                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef)),
10708                        source_name: Some("thing".to_string()),
10709                        target_name: Some("thing".to_string()),
10710                        ..Default::default()
10711                    }),
10712                    fdecl::Offer::Service(fdecl::OfferService {
10713                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10714                        source_name: Some("thang".to_string()),
10715                        target_name: Some("thang".to_string()),
10716                        ..Default::default()
10717                    }),
10718                    fdecl::Offer::Directory(fdecl::OfferDirectory {
10719                        dependency_type: Some(fdecl::DependencyType::Strong),
10720                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10721                        source_name: Some("thung1".to_string()),
10722                        target_name: Some("thung1".to_string()),
10723                        ..Default::default()
10724                    }),
10725                    fdecl::Offer::Storage(fdecl::OfferStorage {
10726                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10727                        source_name: Some("thung2".to_string()),
10728                        target_name: Some("thung2".to_string()),
10729                        ..Default::default()
10730                    }),
10731                    fdecl::Offer::Runner(fdecl::OfferRunner {
10732                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10733                        source_name: Some("thung3".to_string()),
10734                        target_name: Some("thung3".to_string()),
10735                        ..Default::default()
10736                    }),
10737                    fdecl::Offer::Resolver(fdecl::OfferResolver {
10738                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10739                        source_name: Some("thung4".to_string()),
10740                        target_name: Some("thung4".to_string()),
10741                        ..Default::default()
10742                    }),
10743                ],
10744                &fdecl::Component {
10745                    capabilities: Some(vec![fdecl::Capability::Protocol(fdecl::Protocol {
10746                        name: Some("thing".to_string()),
10747                        source_path: Some("/svc/foo".into()),
10748                        ..Default::default()
10749                    }),]),
10750                    ..Default::default()
10751                }
10752            ),
10753            Err(ErrorList::new(vec![
10754                Error::missing_field(DeclType::OfferProtocol, "target"),
10755                Error::missing_field(DeclType::OfferService, "target"),
10756                Error::missing_field(DeclType::OfferDirectory, "target"),
10757                Error::missing_field(DeclType::OfferStorage, "target"),
10758                Error::missing_field(DeclType::OfferRunner, "target"),
10759                Error::missing_field(DeclType::OfferResolver, "target"),
10760            ]))
10761        );
10762    }
10763
10764    #[test]
10765    fn test_validate_dynamic_offers_collection_collision() {
10766        assert_eq!(
10767            validate_dynamic_offers(
10768                vec![],
10769                &mut DirectedGraph::new(),
10770                &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10771                    dependency_type: Some(fdecl::DependencyType::Strong),
10772                    source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10773                    source_name: Some("thing".to_string()),
10774                    target_name: Some("thing".to_string()),
10775                    target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10776                        name: "child".to_string(),
10777                        collection: Some("coll".to_string()),
10778                    })),
10779                    ..Default::default()
10780                }),],
10781                &fdecl::Component {
10782                    offers: Some(vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10783                        dependency_type: Some(fdecl::DependencyType::Strong),
10784                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10785                        source_name: Some("thing".to_string()),
10786                        target_name: Some("thing".to_string()),
10787                        target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10788                            name: "coll".into()
10789                        })),
10790                        ..Default::default()
10791                    }),]),
10792                    collections: Some(vec![fdecl::Collection {
10793                        name: Some("coll".to_string()),
10794                        durability: Some(fdecl::Durability::Transient),
10795                        ..Default::default()
10796                    },]),
10797                    ..Default::default()
10798                }
10799            ),
10800            Err(ErrorList::new(vec![Error::duplicate_field(
10801                DeclType::OfferProtocol,
10802                "target_name",
10803                "thing"
10804            ),]))
10805        );
10806    }
10807
10808    #[test]
10809    fn test_validate_dynamic_offers_cycle_collection_to_static_child() {
10810        assert_eq!(
10811            validate_dynamic_offers(
10812                vec![("dyn", "coll")],
10813                &mut DirectedGraph::new(),
10814                &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10815                    source_name: Some("bar".to_string()),
10816                    target_name: Some("bar".to_string()),
10817                    source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10818                        name: "static_child".into(),
10819                        collection: None,
10820                    })),
10821                    target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10822                        name: "dyn".to_string(),
10823                        collection: Some("coll".to_string()),
10824                    })),
10825                    dependency_type: Some(fdecl::DependencyType::Strong),
10826                    ..Default::default()
10827                }),],
10828                &fdecl::Component {
10829                    offers: Some(vec![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: "coll".into(),
10834                        })),
10835                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10836                            name: "static_child".into(),
10837                            collection: None,
10838                        })),
10839                        ..Default::default()
10840                    })]),
10841                    children: Some(vec![fdecl::Child {
10842                        name: Some("static_child".into()),
10843                        url: Some("url#child.cm".into()),
10844                        startup: Some(fdecl::StartupMode::Lazy),
10845                        ..Default::default()
10846                    }]),
10847                    collections: Some(vec![fdecl::Collection {
10848                        name: Some("coll".into()),
10849                        durability: Some(fdecl::Durability::Transient),
10850                        ..Default::default()
10851                    }]),
10852                    ..Default::default()
10853                }
10854            ),
10855            Err(ErrorList::new(vec![Error::dependency_cycle(
10856                "{{child coll:dyn -> collection coll -> child static_child -> child coll:dyn}}"
10857            )]))
10858        );
10859    }
10860
10861    #[test]
10862    fn test_validate_dynamic_offers_cycle_collection_to_dynamic_child() {
10863        assert_eq!(
10864            validate_dynamic_offers(
10865                vec![("dyn", "coll1"), ("dyn", "coll2")],
10866                &mut DirectedGraph::new(),
10867                &vec![
10868                    fdecl::Offer::Service(fdecl::OfferService {
10869                        source_name: Some("foo".to_string()),
10870                        target_name: Some("foo".to_string()),
10871                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10872                            name: "coll2".into(),
10873                        })),
10874                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10875                            name: "dyn".into(),
10876                            collection: Some("coll1".into()),
10877                        })),
10878                        ..Default::default()
10879                    }),
10880                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
10881                        source_name: Some("bar".to_string()),
10882                        target_name: Some("bar".to_string()),
10883                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10884                            name: "dyn".into(),
10885                            collection: Some("coll1".into()),
10886                        })),
10887                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10888                            name: "dyn".to_string(),
10889                            collection: Some("coll2".to_string()),
10890                        })),
10891                        dependency_type: Some(fdecl::DependencyType::Strong),
10892                        ..Default::default()
10893                    }),
10894                ],
10895                &fdecl::Component {
10896                    collections: Some(vec![
10897                        fdecl::Collection {
10898                            name: Some("coll1".into()),
10899                            durability: Some(fdecl::Durability::Transient),
10900                            ..Default::default()
10901                        },
10902                        fdecl::Collection {
10903                            name: Some("coll2".into()),
10904                            durability: Some(fdecl::Durability::Transient),
10905                            ..Default::default()
10906                        },
10907                    ]),
10908                    ..Default::default()
10909                }
10910            ),
10911            Err(ErrorList::new(vec![Error::dependency_cycle(
10912                "{{child coll1:dyn -> child coll2:dyn -> collection coll2 -> child coll1:dyn}}"
10913            )]))
10914        );
10915    }
10916
10917    #[test]
10918    fn test_validate_dynamic_offers_cycle_collection_to_dynamic_child_between_new_and_existing() {
10919        assert_eq!(
10920            validate_dynamic_offers(
10921                vec![("dyn", "coll1"), ("dyn", "coll2")],
10922                &mut DirectedGraph::from([(
10923                    DependencyNode::Child("dyn".into(), Some("coll1".into())),
10924                    DependencyNode::Child("dyn".into(), Some("coll2".into())),
10925                )]),
10926                &vec![fdecl::Offer::Service(fdecl::OfferService {
10927                    source_name: Some("foo".to_string()),
10928                    target_name: Some("foo".to_string()),
10929                    source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10930                        name: "coll2".into(),
10931                    })),
10932                    target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10933                        name: "dyn".into(),
10934                        collection: Some("coll1".into()),
10935                    })),
10936                    ..Default::default()
10937                }),],
10938                &fdecl::Component {
10939                    collections: Some(vec![
10940                        fdecl::Collection {
10941                            name: Some("coll1".into()),
10942                            durability: Some(fdecl::Durability::Transient),
10943                            ..Default::default()
10944                        },
10945                        fdecl::Collection {
10946                            name: Some("coll2".into()),
10947                            durability: Some(fdecl::Durability::Transient),
10948                            ..Default::default()
10949                        },
10950                    ]),
10951                    ..Default::default()
10952                }
10953            ),
10954            Err(ErrorList::new(vec![Error::dependency_cycle(
10955                "{{child coll1:dyn -> child coll2:dyn -> collection coll2 -> child coll1:dyn}}"
10956            )]))
10957        );
10958    }
10959
10960    #[test]
10961    fn test_validate_dynamic_offers_cycle_collection_to_collection() {
10962        assert_eq!(
10963            validate_dynamic_offers(
10964                vec![("dyn", "coll1"), ("dyn", "coll2")],
10965                &mut DirectedGraph::new(),
10966                &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10967                    source_name: Some("bar".to_string()),
10968                    target_name: Some("bar".to_string()),
10969                    source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10970                        name: "dyn".into(),
10971                        collection: Some("coll2".parse().unwrap()),
10972                    })),
10973                    target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10974                        name: "dyn".into(),
10975                        collection: Some("coll1".parse().unwrap()),
10976                    })),
10977                    dependency_type: Some(fdecl::DependencyType::Strong),
10978                    ..Default::default()
10979                }),],
10980                &fdecl::Component {
10981                    offers: Some(vec![fdecl::Offer::Service(fdecl::OfferService {
10982                        source_name: Some("foo".to_string()),
10983                        target_name: Some("foo".to_string()),
10984                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10985                            name: "coll1".into(),
10986                        })),
10987                        target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10988                            name: "coll2".into(),
10989                        })),
10990                        ..Default::default()
10991                    })]),
10992                    collections: Some(vec![
10993                        fdecl::Collection {
10994                            name: Some("coll1".into()),
10995                            durability: Some(fdecl::Durability::Transient),
10996                            ..Default::default()
10997                        },
10998                        fdecl::Collection {
10999                            name: Some("coll2".into()),
11000                            durability: Some(fdecl::Durability::Transient),
11001                            ..Default::default()
11002                        },
11003                    ]),
11004                    ..Default::default()
11005                }
11006            ),
11007            Err(ErrorList::new(vec![Error::dependency_cycle(
11008                "{{child coll1:dyn -> collection coll1 -> child coll2:dyn -> child coll1:dyn}}",
11009            )]))
11010        );
11011    }
11012
11013    #[test]
11014    fn test_validate_dynamic_child() {
11015        assert_eq!(
11016            Ok(()),
11017            validate_dynamic_child(&fdecl::Child {
11018                name: Some("a".repeat(MAX_LONG_NAME_LENGTH).to_string()),
11019                url: Some("test:///child".to_string()),
11020                startup: Some(fdecl::StartupMode::Lazy),
11021                on_terminate: None,
11022                environment: None,
11023                ..Default::default()
11024            })
11025        );
11026    }
11027
11028    #[test]
11029    fn test_validate_dynamic_child_environment_is_invalid() {
11030        assert_eq!(
11031            validate_dynamic_child(&fdecl::Child {
11032                name: Some("a".repeat(MAX_LONG_NAME_LENGTH).to_string()),
11033                url: Some("test:///child".to_string()),
11034                startup: Some(fdecl::StartupMode::Lazy),
11035                on_terminate: None,
11036                environment: Some("env".to_string()),
11037                ..Default::default()
11038            }),
11039            Err(ErrorList::new(vec![Error::DynamicChildWithEnvironment]))
11040        );
11041    }
11042
11043    #[test]
11044    fn test_validate_dynamic_offers_missing_stuff() {
11045        assert_eq!(
11046            validate_dynamic_offers(
11047                vec![],
11048                &mut DirectedGraph::new(),
11049                &vec![
11050                    fdecl::Offer::Protocol(fdecl::OfferProtocol::default()),
11051                    fdecl::Offer::Service(fdecl::OfferService::default()),
11052                    fdecl::Offer::Directory(fdecl::OfferDirectory::default()),
11053                    fdecl::Offer::Storage(fdecl::OfferStorage::default()),
11054                    fdecl::Offer::Runner(fdecl::OfferRunner::default()),
11055                    fdecl::Offer::Resolver(fdecl::OfferResolver::default()),
11056                ],
11057                &fdecl::Component::default()
11058            ),
11059            Err(ErrorList::new(vec![
11060                Error::missing_field(DeclType::OfferProtocol, "source"),
11061                Error::missing_field(DeclType::OfferProtocol, "source_name"),
11062                Error::missing_field(DeclType::OfferProtocol, "target"),
11063                Error::missing_field(DeclType::OfferProtocol, "target_name"),
11064                Error::missing_field(DeclType::OfferProtocol, "dependency_type"),
11065                Error::missing_field(DeclType::OfferService, "source"),
11066                Error::missing_field(DeclType::OfferService, "source_name"),
11067                Error::missing_field(DeclType::OfferService, "target"),
11068                Error::missing_field(DeclType::OfferService, "target_name"),
11069                Error::missing_field(DeclType::OfferDirectory, "source"),
11070                Error::missing_field(DeclType::OfferDirectory, "source_name"),
11071                Error::missing_field(DeclType::OfferDirectory, "target"),
11072                Error::missing_field(DeclType::OfferDirectory, "target_name"),
11073                Error::missing_field(DeclType::OfferDirectory, "dependency_type"),
11074                Error::missing_field(DeclType::OfferStorage, "source"),
11075                Error::missing_field(DeclType::OfferStorage, "source_name"),
11076                Error::missing_field(DeclType::OfferStorage, "target"),
11077                Error::missing_field(DeclType::OfferStorage, "target_name"),
11078                Error::missing_field(DeclType::OfferRunner, "source"),
11079                Error::missing_field(DeclType::OfferRunner, "source_name"),
11080                Error::missing_field(DeclType::OfferRunner, "target"),
11081                Error::missing_field(DeclType::OfferRunner, "target_name"),
11082                Error::missing_field(DeclType::OfferResolver, "source"),
11083                Error::missing_field(DeclType::OfferResolver, "source_name"),
11084                Error::missing_field(DeclType::OfferResolver, "target"),
11085                Error::missing_field(DeclType::OfferResolver, "target_name"),
11086            ]))
11087        );
11088    }
11089
11090    test_dependency! {
11091        (test_validate_offers_protocol_dependency_cycle) => {
11092            ty = fdecl::Offer::Protocol,
11093            offer_decl = fdecl::OfferProtocol {
11094                source: None,  // Filled by macro
11095                target: None,  // Filled by macro
11096                source_name: Some(format!("thing")),
11097                target_name: Some(format!("thing")),
11098                dependency_type: Some(fdecl::DependencyType::Strong),
11099                ..Default::default()
11100            },
11101        },
11102        (test_validate_offers_directory_dependency_cycle) => {
11103            ty = fdecl::Offer::Directory,
11104            offer_decl = fdecl::OfferDirectory {
11105                source: None,  // Filled by macro
11106                target: None,  // Filled by macro
11107                source_name: Some(format!("thing")),
11108                target_name: Some(format!("thing")),
11109                rights: Some(fio::Operations::CONNECT),
11110                subdir: None,
11111                dependency_type: Some(fdecl::DependencyType::Strong),
11112                ..Default::default()
11113            },
11114        },
11115        (test_validate_offers_service_dependency_cycle) => {
11116            ty = fdecl::Offer::Service,
11117            offer_decl = fdecl::OfferService {
11118                source: None,  // Filled by macro
11119                target: None,  // Filled by macro
11120                source_name: Some(format!("thing")),
11121                target_name: Some(format!("thing")),
11122                ..Default::default()
11123            },
11124        },
11125        (test_validate_offers_runner_dependency_cycle) => {
11126            ty = fdecl::Offer::Runner,
11127            offer_decl = fdecl::OfferRunner {
11128                source: None,  // Filled by macro
11129                target: None,  // Filled by macro
11130                source_name: Some(format!("thing")),
11131                target_name: Some(format!("thing")),
11132                ..Default::default()
11133            },
11134        },
11135        (test_validate_offers_resolver_dependency_cycle) => {
11136            ty = fdecl::Offer::Resolver,
11137            offer_decl = fdecl::OfferResolver {
11138                source: None,  // Filled by macro
11139                target: None,  // Filled by macro
11140                source_name: Some(format!("thing")),
11141                target_name: Some(format!("thing")),
11142                ..Default::default()
11143            },
11144        },
11145    }
11146    test_weak_dependency! {
11147        (test_validate_offers_protocol_weak_dependency_cycle) => {
11148            ty = fdecl::Offer::Protocol,
11149            offer_decl = fdecl::OfferProtocol {
11150                source: None,  // Filled by macro
11151                target: None,  // Filled by macro
11152                source_name: Some(format!("thing")),
11153                target_name: Some(format!("thing")),
11154                dependency_type: None, // Filled by macro
11155                ..Default::default()
11156            },
11157        },
11158        (test_validate_offers_directory_weak_dependency_cycle) => {
11159            ty = fdecl::Offer::Directory,
11160            offer_decl = fdecl::OfferDirectory {
11161                source: None,  // Filled by macro
11162                target: None,  // Filled by macro
11163                source_name: Some(format!("thing")),
11164                target_name: Some(format!("thing")),
11165                rights: Some(fio::Operations::CONNECT),
11166                subdir: None,
11167                dependency_type: None,  // Filled by macro
11168                ..Default::default()
11169            },
11170        },
11171        (test_validate_offers_service_weak_dependency_cycle) => {
11172            ty = fdecl::Offer::Service,
11173            offer_decl = fdecl::OfferService {
11174                source: None,  // Filled by macro
11175                target: None,  // Filled by macro
11176                source_name: Some(format!("thing")),
11177                target_name: Some(format!("thing")),
11178                dependency_type: None, // Filled by macro
11179                ..Default::default()
11180            },
11181        },
11182    }
11183}