cm_fidl_validator/
lib.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5pub(crate) mod util;
6
7pub mod error;
8
9pub use crate::util::check_url;
10
11use crate::error::*;
12use crate::util::*;
13use directed_graph::DirectedGraph;
14use fidl_fuchsia_component_decl as fdecl;
15use itertools::Itertools;
16use std::collections::{BTreeSet, HashMap, HashSet};
17use std::path::Path;
18
19trait HasAvailability {
20    fn availability(&self) -> fdecl::Availability;
21}
22
23impl HasAvailability for fdecl::ExposeService {
24    fn availability(&self) -> fdecl::Availability {
25        return self.availability.unwrap_or(fdecl::Availability::Required);
26    }
27}
28
29impl HasAvailability for fdecl::OfferService {
30    fn availability(&self) -> fdecl::Availability {
31        return self.availability.unwrap_or(fdecl::Availability::Required);
32    }
33}
34
35#[cfg(fuchsia_api_level_at_least = "25")]
36macro_rules! get_source_dictionary {
37    ($decl:ident) => {
38        $decl.source_dictionary.as_ref()
39    };
40}
41#[cfg(fuchsia_api_level_less_than = "25")]
42macro_rules! get_source_dictionary {
43    ($decl:ident) => {
44        None
45    };
46}
47
48/// Validates Configuration Value Spec.
49///
50/// For now, this simply verifies that all semantically required fields are present.
51pub fn validate_value_spec(spec: &fdecl::ConfigValueSpec) -> Result<(), ErrorList> {
52    let mut errors = vec![];
53    if let Some(value) = &spec.value {
54        match value {
55            fdecl::ConfigValue::Single(s) => match s {
56                fdecl::ConfigSingleValue::Bool(_)
57                | fdecl::ConfigSingleValue::Uint8(_)
58                | fdecl::ConfigSingleValue::Uint16(_)
59                | fdecl::ConfigSingleValue::Uint32(_)
60                | fdecl::ConfigSingleValue::Uint64(_)
61                | fdecl::ConfigSingleValue::Int8(_)
62                | fdecl::ConfigSingleValue::Int16(_)
63                | fdecl::ConfigSingleValue::Int32(_)
64                | fdecl::ConfigSingleValue::Int64(_)
65                | fdecl::ConfigSingleValue::String(_) => {}
66                fdecl::ConfigSingleValueUnknown!() => {
67                    errors.push(Error::invalid_field(DeclType::ConfigValueSpec, "value"));
68                }
69            },
70            fdecl::ConfigValue::Vector(l) => match l {
71                fdecl::ConfigVectorValue::BoolVector(_)
72                | fdecl::ConfigVectorValue::Uint8Vector(_)
73                | fdecl::ConfigVectorValue::Uint16Vector(_)
74                | fdecl::ConfigVectorValue::Uint32Vector(_)
75                | fdecl::ConfigVectorValue::Uint64Vector(_)
76                | fdecl::ConfigVectorValue::Int8Vector(_)
77                | fdecl::ConfigVectorValue::Int16Vector(_)
78                | fdecl::ConfigVectorValue::Int32Vector(_)
79                | fdecl::ConfigVectorValue::Int64Vector(_)
80                | fdecl::ConfigVectorValue::StringVector(_) => {}
81                fdecl::ConfigVectorValueUnknown!() => {
82                    errors.push(Error::invalid_field(DeclType::ConfigValueSpec, "value"));
83                }
84            },
85            fdecl::ConfigValueUnknown!() => {
86                errors.push(Error::invalid_field(DeclType::ConfigValueSpec, "value"));
87            }
88        }
89    } else {
90        errors.push(Error::missing_field(DeclType::ConfigValueSpec, "value"));
91    }
92
93    if errors.is_empty() {
94        Ok(())
95    } else {
96        Err(ErrorList::new(errors))
97    }
98}
99
100/// Validates Configuration Values Data.
101///
102/// The Value Data may ultimately originate from a CVF file, or be directly constructed by the
103/// caller. Either way, Value Data should always be validated before it's used. For now, this
104/// simply verifies that all semantically required fields are present.
105///
106/// This method does not validate value data against a configuration schema.
107pub fn validate_values_data(data: &fdecl::ConfigValuesData) -> Result<(), ErrorList> {
108    let mut errors = vec![];
109    if let Some(values) = &data.values {
110        for spec in values {
111            if let Err(mut e) = validate_value_spec(spec) {
112                errors.append(&mut e.errs);
113            }
114        }
115    } else {
116        errors.push(Error::missing_field(DeclType::ConfigValuesData, "values"));
117    }
118
119    if let Some(checksum) = &data.checksum {
120        match checksum {
121            fdecl::ConfigChecksum::Sha256(_) => {}
122            fdecl::ConfigChecksumUnknown!() => {
123                errors.push(Error::invalid_field(DeclType::ConfigValuesData, "checksum"));
124            }
125        }
126    } else {
127        errors.push(Error::missing_field(DeclType::ConfigValuesData, "checksum"));
128    }
129
130    if errors.is_empty() {
131        Ok(())
132    } else {
133        Err(ErrorList::new(errors))
134    }
135}
136
137// `fdecl::Ref` is not hashable, so define this equivalent type for use in maps
138#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
139enum RefKey<'a> {
140    Parent,
141    Self_,
142    Child(&'a str),
143    Collection(&'a str),
144    Framework,
145    Capability,
146    Debug,
147}
148
149/// Validates a Component.
150///
151/// The Component may ultimately originate from a CM file, or be directly constructed by the
152/// caller. Either way, a Component should always be validated before it's used. Examples
153/// of what is validated (which may evolve in the future):
154///
155/// - That all semantically required fields are present
156/// - That a child_name referenced in a source actually exists in the list of children
157/// - That there are no duplicate target paths.
158/// - That only weak-dependency capabilities may be offered back to the
159///   component that exposed them.
160///
161/// All checks are local to this Component.
162pub fn validate(decl: &fdecl::Component) -> Result<(), ErrorList> {
163    let ctx = ValidationContext::default();
164    ctx.validate(decl, None).map_err(|errs| ErrorList::new(errs))
165}
166
167/// Validates a list of namespace or builtin Capabilities.
168fn validate_capabilities(
169    capabilities: &[fdecl::Capability],
170    as_builtin: bool,
171) -> Result<(), ErrorList> {
172    let mut ctx = ValidationContext::default();
173
174    #[cfg(fuchsia_api_level_at_least = "25")]
175    ctx.load_dictionary_names(capabilities.iter().filter_map(|capability| match capability {
176        fdecl::Capability::Dictionary(dictionary_decl) => Some(dictionary_decl),
177        _ => None,
178    }));
179
180    ctx.validate_capability_decls(capabilities, as_builtin);
181    if ctx.errors.is_empty() {
182        Ok(())
183    } else {
184        Err(ErrorList::new(ctx.errors))
185    }
186}
187
188// Validate builtin capabilities.
189pub fn validate_builtin_capabilities(
190    capabilities: &Vec<fdecl::Capability>,
191) -> Result<(), ErrorList> {
192    validate_capabilities(capabilities, true)
193}
194
195// Validate namespace capabilities.
196pub fn validate_namespace_capabilities(
197    capabilities: &Vec<fdecl::Capability>,
198) -> Result<(), ErrorList> {
199    validate_capabilities(capabilities, false)
200}
201
202/// An interface to call into either `check_dynamic_name()` or `check_name()`, depending on the context
203/// of the caller.
204type CheckChildNameFn = fn(Option<&String>, DeclType, &str, &mut Vec<Error>) -> bool;
205
206pub fn validate_dynamic_child(child: &fdecl::Child) -> Result<(), ErrorList> {
207    let mut errors = vec![];
208
209    if let Err(mut error_list) = validate_child(child, check_dynamic_name) {
210        errors.append(&mut error_list.errs);
211    }
212
213    if child.environment.is_some() {
214        errors.push(Error::DynamicChildWithEnvironment);
215    }
216
217    if errors.is_empty() {
218        Ok(())
219    } else {
220        Err(ErrorList { errs: errors })
221    }
222}
223
224/// Validates an independent Child. Performs the same validation on it as `validate`. A
225/// `check_name_fn` is passed into specify the function used to validate the child name.
226fn validate_child(
227    child: &fdecl::Child,
228    check_child_name: CheckChildNameFn,
229) -> Result<(), ErrorList> {
230    let mut errors = vec![];
231    check_child_name(child.name.as_ref(), DeclType::Child, "name", &mut errors);
232    check_url(child.url.as_ref(), DeclType::Child, "url", &mut errors);
233    if child.startup.is_none() {
234        errors.push(Error::missing_field(DeclType::Child, "startup"));
235    }
236    // Allow `on_terminate` to be unset since the default is almost always desired.
237    if child.environment.is_some() {
238        check_name(child.environment.as_ref(), DeclType::Child, "environment", &mut errors);
239    }
240    if errors.is_empty() {
241        Ok(())
242    } else {
243        Err(ErrorList { errs: errors })
244    }
245}
246
247/// Validates a collection of dynamic offers. Dynamic offers differ from static
248/// offers, in that
249///
250/// 1. a dynamic offer's `target` field must be omitted;
251/// 2. a dynamic offer's `source` _may_ be a dynamic child;
252/// 3. since this crate isn't really designed to handle dynamic children, we
253///    disable the checks that ensure that the source/target exist, and that the
254///    offers don't introduce any cycles.
255pub fn validate_dynamic_offers<'a>(
256    dynamic_children: Vec<(&'a str, &'a str)>,
257    offers: &'a Vec<fdecl::Offer>,
258    decl: &'a fdecl::Component,
259) -> Result<(), ErrorList> {
260    let mut ctx = ValidationContext::default();
261    ctx.dynamic_children = dynamic_children;
262    ctx.validate(decl, Some(offers)).map_err(|errs| ErrorList::new(errs))
263}
264
265fn check_offer_name(
266    prop: Option<&String>,
267    decl: DeclType,
268    keyword: &str,
269    offer_type: OfferType,
270    errors: &mut Vec<Error>,
271) -> bool {
272    if offer_type == OfferType::Dynamic {
273        check_dynamic_name(prop, decl, keyword, errors)
274    } else {
275        check_name(prop, decl, keyword, errors)
276    }
277}
278
279#[derive(Default)]
280struct ValidationContext<'a> {
281    all_children: HashMap<&'a str, &'a fdecl::Child>,
282    all_collections: HashSet<&'a str>,
283    all_capability_ids: HashSet<&'a str>,
284    all_storages: HashMap<&'a str, Option<&'a fdecl::Ref>>,
285    all_services: HashSet<&'a str>,
286    all_protocols: HashSet<&'a str>,
287    all_directories: HashSet<&'a str>,
288    all_runners: HashSet<&'a str>,
289    all_resolvers: HashSet<&'a str>,
290    #[cfg(fuchsia_api_level_at_least = "25")]
291    all_dictionaries: HashMap<&'a str, &'a fdecl::Dictionary>,
292
293    #[cfg(fuchsia_api_level_at_least = "HEAD")]
294    all_configs: HashSet<&'a str>,
295
296    all_environment_names: HashSet<&'a str>,
297    dynamic_children: Vec<(&'a str, &'a str)>,
298    strong_dependencies: DirectedGraph<cm_graph::DependencyNode<'a>>,
299    target_ids: IdMap<'a>,
300    errors: Vec<Error>,
301}
302
303/// [Container] provides a capability type agnostic trait to check for the existence of a
304/// capability definition of a particular type. This is useful for writing common validation
305/// functions.
306trait Container {
307    fn contains(&self, key: &str) -> bool;
308}
309
310impl<'a> Container for HashSet<&'a str> {
311    fn contains(&self, key: &str) -> bool {
312        self.contains(key)
313    }
314}
315
316impl<'a, T> Container for HashMap<&'a str, T> {
317    fn contains(&self, key: &str) -> bool {
318        self.contains_key(key)
319    }
320}
321
322impl<'a> ValidationContext<'a> {
323    fn validate(
324        mut self,
325        decl: &'a fdecl::Component,
326        dynamic_offers: Option<&'a Vec<fdecl::Offer>>,
327    ) -> Result<(), Vec<Error>> {
328        // Collect all environment names first, so that references to them can be checked.
329        if let Some(envs) = &decl.environments {
330            self.collect_environment_names(&envs);
331        }
332
333        // Validate "children" and build the set of all children.
334        if let Some(children) = decl.children.as_ref() {
335            for child in children {
336                self.validate_child_decl(&child);
337            }
338        }
339
340        // Validate "collections" and build the set of all collections.
341        if let Some(collections) = decl.collections.as_ref() {
342            for collection in collections {
343                self.validate_collection_decl(&collection);
344            }
345        }
346
347        // Validate "capabilities" and build the set of all capabilities.
348        if let Some(capabilities) = decl.capabilities.as_ref() {
349            #[cfg(fuchsia_api_level_at_least = "25")]
350            self.load_dictionary_names(capabilities.iter().filter_map(
351                |capability| match capability {
352                    fdecl::Capability::Dictionary(dictionary_decl) => Some(dictionary_decl),
353                    _ => None,
354                },
355            ));
356            self.validate_capability_decls(capabilities, false);
357        }
358
359        // Validate "uses".
360        let mut use_runner_name = None;
361        let mut use_runner_source = None;
362        if let Some(uses) = decl.uses.as_ref() {
363            (use_runner_name, use_runner_source) = self.validate_use_decls(uses);
364        }
365
366        // Validate "program".
367        if let Some(program) = decl.program.as_ref() {
368            self.validate_program(program, use_runner_name, use_runner_source);
369        }
370
371        // Validate "exposes".
372        if let Some(exposes) = decl.exposes.as_ref() {
373            let mut expose_to_parent_ids = HashMap::new();
374            let mut expose_to_framework_ids = HashMap::new();
375            for expose in exposes.iter() {
376                self.validate_expose_decl(
377                    &expose,
378                    &mut expose_to_parent_ids,
379                    &mut expose_to_framework_ids,
380                );
381            }
382            self.validate_expose_group(&exposes);
383        }
384
385        // Validate "offers".
386        if let Some(offers) = decl.offers.as_ref() {
387            for offer in offers.iter() {
388                self.validate_offer_decl(&offer, OfferType::Static);
389            }
390            self.validate_offer_group(&offers, OfferType::Static);
391        }
392
393        if let Some(dynamic_offers) = dynamic_offers.as_ref() {
394            for dynamic_offer in dynamic_offers.iter() {
395                self.validate_offer_decl(&dynamic_offer, OfferType::Dynamic);
396            }
397            self.validate_offer_group(&dynamic_offers, OfferType::Dynamic);
398        }
399
400        // Validate "environments" after all other declarations are processed.
401        if let Some(environment) = decl.environments.as_ref() {
402            for environment in environment {
403                self.validate_environment_decl(&environment);
404            }
405        }
406
407        // Validate "config"
408        #[cfg(fuchsia_api_level_at_least = "20")]
409        self.validate_config(decl.config.as_ref(), decl.uses.as_ref());
410
411        // Check that there are no strong cyclical dependencies
412        cm_graph::generate_dependency_graph(
413            &mut self.strong_dependencies,
414            &decl,
415            &self.dynamic_children,
416            dynamic_offers,
417        );
418        if let Err(e) = self.strong_dependencies.topological_sort() {
419            self.errors.push(Error::dependency_cycle(e.format_cycle()));
420        }
421
422        if self.errors.is_empty() {
423            Ok(())
424        } else {
425            Err(self.errors)
426        }
427    }
428
429    /// Collects all the environment names, watching for duplicates.
430    fn collect_environment_names(&mut self, envs: &'a [fdecl::Environment]) {
431        for env in envs {
432            if let Some(name) = env.name.as_ref() {
433                if !self.all_environment_names.insert(name) {
434                    self.errors.push(Error::duplicate_field(DeclType::Environment, "name", name));
435                }
436            }
437        }
438    }
439
440    // Validates a config schema. Checks that each field's layout matches the expected constraints
441    // and properties.
442    #[cfg(fuchsia_api_level_at_least = "20")]
443    fn validate_config(
444        &mut self,
445        config: Option<&fdecl::ConfigSchema>,
446        uses: Option<&Vec<fdecl::Use>>,
447    ) {
448        use std::collections::BTreeMap;
449
450        // Get all of the `use` configs that are optional without a default.
451        let optional_use_keys: BTreeMap<String, fdecl::ConfigType> =
452            uses.map_or(BTreeMap::new(), |u| {
453                u.iter()
454                    .map(|u| {
455                        let fdecl::Use::Config(config) = u else {
456                            return None;
457                        };
458                        if config.availability == Some(fdecl::Availability::Required)
459                            || config.availability == None
460                        {
461                            return None;
462                        }
463                        if let Some(_) = config.default.as_ref() {
464                            return None;
465                        }
466                        let Some(key) = config.target_name.clone() else {
467                            return None;
468                        };
469                        let Some(value) = config.type_.clone() else {
470                            return None;
471                        };
472                        Some((key, value))
473                    })
474                    .flatten()
475                    .collect()
476            });
477
478        // Validate default values in use configs.
479        for u in uses.iter().flat_map(|x| x.iter()) {
480            let fdecl::Use::Config(config) = u else { continue };
481            let Some(default) = config.default.as_ref() else { continue };
482            validate_value_spec(&fdecl::ConfigValueSpec {
483                value: Some(default.clone()),
484                ..Default::default()
485            })
486            .map_err(|mut e| self.errors.append(&mut e.errs))
487            .ok();
488        }
489
490        let Some(config) = config else {
491            if !optional_use_keys.is_empty() {
492                self.errors.push(Error::missing_field(DeclType::ConfigField, "config"))
493            }
494            return;
495        };
496
497        if let Some(fields) = &config.fields {
498            for field in fields {
499                if field.key.is_none() {
500                    self.errors.push(Error::missing_field(DeclType::ConfigField, "key"));
501                }
502                if let Some(type_) = &field.type_ {
503                    self.validate_config_type(type_, true);
504                } else {
505                    self.errors.push(Error::missing_field(DeclType::ConfigField, "value_type"));
506                }
507            }
508        } else {
509            self.errors.push(Error::missing_field(DeclType::ConfigSchema, "fields"));
510        }
511
512        if let Some(checksum) = &config.checksum {
513            match checksum {
514                fdecl::ConfigChecksum::Sha256(_) => {}
515                fdecl::ConfigChecksumUnknown!() => {
516                    self.errors.push(Error::invalid_field(DeclType::ConfigSchema, "checksum"));
517                }
518            }
519        } else {
520            self.errors.push(Error::missing_field(DeclType::ConfigSchema, "checksum"));
521        }
522
523        'outer: for (key, value) in optional_use_keys.iter() {
524            for field in config.fields.iter().flatten() {
525                if field.key.as_ref() == Some(key) {
526                    if field.type_.as_ref() != Some(value) {
527                        self.errors.push(Error::invalid_field(DeclType::ConfigField, key.clone()));
528                    }
529                    continue 'outer;
530                }
531            }
532            self.errors.push(Error::missing_field(DeclType::ConfigField, key.clone()));
533        }
534
535        match config.value_source {
536            None => self.errors.push(Error::missing_field(DeclType::ConfigSchema, "value_source")),
537            #[cfg(fuchsia_api_level_at_least = "HEAD")]
538            Some(fdecl::ConfigValueSource::Capabilities(_)) => {
539                if !optional_use_keys.is_empty() {
540                    self.errors
541                        .push(Error::invalid_field(DeclType::ConfigValueSource, "ValueSource"))
542                }
543            }
544            Some(fdecl::ConfigValueSource::__SourceBreaking { .. }) => {
545                self.errors.push(Error::invalid_field(DeclType::ConfigValueSource, "ValueSource"))
546            }
547            _ => (),
548        };
549    }
550
551    #[cfg(fuchsia_api_level_at_least = "20")]
552    fn validate_config_type(&mut self, type_: &fdecl::ConfigType, accept_vectors: bool) {
553        match &type_.layout {
554            fdecl::ConfigTypeLayout::Bool
555            | fdecl::ConfigTypeLayout::Uint8
556            | fdecl::ConfigTypeLayout::Uint16
557            | fdecl::ConfigTypeLayout::Uint32
558            | fdecl::ConfigTypeLayout::Uint64
559            | fdecl::ConfigTypeLayout::Int8
560            | fdecl::ConfigTypeLayout::Int16
561            | fdecl::ConfigTypeLayout::Int32
562            | fdecl::ConfigTypeLayout::Int64 => {
563                // These layouts have no parameters or constraints
564                if let Some(parameters) = &type_.parameters {
565                    if !parameters.is_empty() {
566                        self.errors
567                            .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
568                    }
569                } else {
570                    self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"));
571                }
572
573                if !type_.constraints.is_empty() {
574                    self.errors.push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
575                }
576            }
577            fdecl::ConfigTypeLayout::String => {
578                // String has exactly one constraint and no parameter
579                if let Some(parameters) = &type_.parameters {
580                    if !parameters.is_empty() {
581                        self.errors
582                            .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
583                    }
584                } else {
585                    self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"));
586                }
587
588                if type_.constraints.is_empty() {
589                    self.errors.push(Error::missing_field(DeclType::ConfigType, "constraints"));
590                } else if type_.constraints.len() > 1 {
591                    self.errors.push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
592                } else if let fdecl::LayoutConstraint::MaxSize(_) = &type_.constraints[0] {
593                } else {
594                    self.errors.push(Error::invalid_field(DeclType::ConfigType, "constraints"));
595                }
596            }
597            fdecl::ConfigTypeLayout::Vector => {
598                if accept_vectors {
599                    // Vector has exactly one constraint and one parameter
600                    if let Some(parameters) = &type_.parameters {
601                        if parameters.is_empty() {
602                            self.errors
603                                .push(Error::missing_field(DeclType::ConfigType, "parameters"));
604                        } else if parameters.len() > 1 {
605                            self.errors
606                                .push(Error::extraneous_field(DeclType::ConfigType, "parameters"));
607                        } else if let fdecl::LayoutParameter::NestedType(nested_type) =
608                            &parameters[0]
609                        {
610                            self.validate_config_type(nested_type, false);
611                        } else {
612                            self.errors
613                                .push(Error::invalid_field(DeclType::ConfigType, "parameters"));
614                        }
615                    } else {
616                        self.errors.push(Error::missing_field(DeclType::ConfigType, "parameters"))
617                    }
618
619                    if type_.constraints.is_empty() {
620                        self.errors.push(Error::missing_field(DeclType::ConfigType, "constraints"));
621                    } else if type_.constraints.len() > 1 {
622                        self.errors
623                            .push(Error::extraneous_field(DeclType::ConfigType, "constraints"));
624                    } else if let fdecl::LayoutConstraint::MaxSize(_) = &type_.constraints[0] {
625                    } else {
626                        self.errors.push(Error::invalid_field(DeclType::ConfigType, "constraints"));
627                    }
628                } else {
629                    self.errors.push(Error::nested_vector());
630                }
631            }
632            _ => self.errors.push(Error::invalid_field(DeclType::ConfigType, "layout")),
633        }
634    }
635
636    fn validate_capability_decls(
637        &mut self,
638        capabilities: &'a [fdecl::Capability],
639        as_builtin: bool,
640    ) {
641        for capability in capabilities {
642            self.validate_capability_decl(capability, as_builtin);
643        }
644    }
645
646    /// Validates an individual capability declaration as either a built-in capability or (if
647    /// `as_builtin = false`) as a component or namespace capability.
648    // Storage capabilities are not currently allowed as built-ins, but there's no deep reason for this.
649    // Update this method to allow built-in storage capabilities as needed.
650    fn validate_capability_decl(&mut self, capability: &'a fdecl::Capability, as_builtin: bool) {
651        match capability {
652            fdecl::Capability::Service(service) => self.validate_service_decl(&service, as_builtin),
653            fdecl::Capability::Protocol(protocol) => {
654                self.validate_protocol_decl(&protocol, as_builtin)
655            }
656            fdecl::Capability::Directory(directory) => {
657                self.validate_directory_decl(&directory, as_builtin)
658            }
659            fdecl::Capability::Storage(storage) => {
660                if as_builtin {
661                    self.errors.push(Error::CapabilityCannotBeBuiltin(DeclType::Storage))
662                } else {
663                    self.validate_storage_decl(&storage)
664                }
665            }
666            fdecl::Capability::Runner(runner) => self.validate_runner_decl(&runner, as_builtin),
667            fdecl::Capability::Resolver(resolver) => {
668                self.validate_resolver_decl(&resolver, as_builtin)
669            }
670            fdecl::Capability::EventStream(event) => {
671                if as_builtin {
672                    self.validate_event_stream_decl(&event)
673                } else {
674                    self.errors.push(Error::CapabilityMustBeBuiltin(DeclType::EventStream))
675                }
676            }
677            #[cfg(fuchsia_api_level_at_least = "25")]
678            fdecl::Capability::Dictionary(dictionary) => {
679                self.validate_dictionary_decl(&dictionary);
680            }
681            #[cfg(fuchsia_api_level_at_least = "HEAD")]
682            fdecl::Capability::Config(config) => {
683                self.validate_configuration_decl(&config);
684            }
685            fdecl::CapabilityUnknown!() => self.errors.push(Error::UnknownCapability),
686        }
687    }
688
689    /// Returns the `source_name` and `source` of the runner in `uses`, if present.
690    fn validate_use_decls(
691        &mut self,
692        uses: &'a [fdecl::Use],
693    ) -> (Option<&'a String>, Option<&'a fdecl::Ref>) {
694        // Validate individual fields.
695        for use_ in uses.iter() {
696            self.validate_use_decl(&use_);
697        }
698        self.validate_use_paths(&uses);
699
700        #[cfg(fuchsia_api_level_at_least = "HEAD")]
701        {
702            let mut use_runner_name = None;
703            let mut use_runner_source = None;
704            for use_ in uses.iter() {
705                if let fdecl::Use::Runner(use_runner) = use_ {
706                    if use_runner_name.is_some() {
707                        self.errors.push(Error::MultipleRunnersUsed);
708                    }
709
710                    use_runner_name = use_runner.source_name.as_ref();
711                    use_runner_source = use_runner.source.as_ref();
712                }
713            }
714            return (use_runner_name, use_runner_source);
715        }
716        #[cfg(fuchsia_api_level_less_than = "HEAD")]
717        return (None, None);
718    }
719
720    fn validate_use_decl(&mut self, use_: &'a fdecl::Use) {
721        match use_ {
722            fdecl::Use::Service(u) => {
723                let decl = DeclType::UseService;
724                self.validate_use_fields(
725                    decl,
726                    Self::service_checker,
727                    u.source.as_ref(),
728                    u.source_name.as_ref(),
729                    get_source_dictionary!(u),
730                    u.target_path.as_ref(),
731                    u.dependency_type.as_ref(),
732                    u.availability.as_ref(),
733                );
734                if u.dependency_type.is_none() {
735                    self.errors.push(Error::missing_field(decl, "dependency_type"));
736                }
737            }
738            fdecl::Use::Protocol(u) => {
739                let decl = DeclType::UseProtocol;
740                self.validate_use_fields(
741                    decl,
742                    Self::protocol_checker,
743                    u.source.as_ref(),
744                    u.source_name.as_ref(),
745                    get_source_dictionary!(u),
746                    u.target_path.as_ref(),
747                    u.dependency_type.as_ref(),
748                    u.availability.as_ref(),
749                );
750                if u.dependency_type.is_none() {
751                    self.errors.push(Error::missing_field(decl, "dependency_type"));
752                }
753            }
754            fdecl::Use::Directory(u) => {
755                let decl = DeclType::UseDirectory;
756                self.validate_use_fields(
757                    decl,
758                    Self::directory_checker,
759                    u.source.as_ref(),
760                    u.source_name.as_ref(),
761                    get_source_dictionary!(u),
762                    u.target_path.as_ref(),
763                    u.dependency_type.as_ref(),
764                    u.availability.as_ref(),
765                );
766                if u.dependency_type.is_none() {
767                    self.errors.push(Error::missing_field(decl, "dependency_type"));
768                }
769                if u.rights.is_none() {
770                    self.errors.push(Error::missing_field(DeclType::UseDirectory, "rights"));
771                }
772                if let Some(subdir) = u.subdir.as_ref() {
773                    check_relative_path(
774                        Some(subdir),
775                        DeclType::UseDirectory,
776                        "subdir",
777                        &mut self.errors,
778                    );
779                }
780            }
781            fdecl::Use::Storage(u) => {
782                const SOURCE: Option<fdecl::Ref> = Some(fdecl::Ref::Parent(fdecl::ParentRef {}));
783                const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
784                    Some(fdecl::DependencyType::Strong);
785                self.validate_use_fields(
786                    DeclType::UseStorage,
787                    Self::storage_checker,
788                    SOURCE.as_ref(),
789                    u.source_name.as_ref(),
790                    None,
791                    u.target_path.as_ref(),
792                    DEPENDENCY_TYPE.as_ref(),
793                    u.availability.as_ref(),
794                );
795            }
796            fdecl::Use::EventStream(u) => {
797                const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
798                    Some(fdecl::DependencyType::Strong);
799                let decl = DeclType::UseEventStream;
800                self.validate_use_fields(
801                    decl,
802                    Self::event_stream_checker,
803                    u.source.as_ref(),
804                    u.source_name.as_ref(),
805                    None,
806                    u.target_path.as_ref(),
807                    DEPENDENCY_TYPE.as_ref(),
808                    u.availability.as_ref(),
809                );
810                // Additional validation.
811                match u.source {
812                    Some(fdecl::Ref::Child(_)) | Some(fdecl::Ref::Parent(_)) => {
813                        // Allowed.
814                    }
815                    Some(fdecl::Ref::Framework(_))
816                    | Some(fdecl::Ref::Self_(_))
817                    | Some(fdecl::Ref::Debug(_)) => {
818                        // Allowed in general but not for event streams, add an error.
819                        self.errors.push(Error::invalid_field(decl, "source"));
820                    }
821                    Some(fdecl::Ref::Collection(_)) | Some(fdecl::RefUnknown!()) | None => {
822                        // Already handled by validate_use_fields.
823                    }
824                }
825                if let Some(scope) = &u.scope {
826                    for reference in scope {
827                        if !matches!(reference, fdecl::Ref::Child(_) | fdecl::Ref::Collection(_)) {
828                            self.errors.push(Error::invalid_field(decl, "scope"));
829                        }
830                    }
831                }
832            }
833            #[cfg(fuchsia_api_level_at_least = "HEAD")]
834            fdecl::Use::Runner(u) => {
835                const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
836                    Some(fdecl::DependencyType::Strong);
837                const AVAILABILITY: Option<fdecl::Availability> =
838                    Some(fdecl::Availability::Required);
839                let decl = DeclType::UseRunner;
840                self.validate_use_fields(
841                    decl,
842                    Self::runner_checker,
843                    u.source.as_ref(),
844                    u.source_name.as_ref(),
845                    get_source_dictionary!(u),
846                    None,
847                    DEPENDENCY_TYPE.as_ref(),
848                    AVAILABILITY.as_ref(),
849                );
850            }
851            #[cfg(fuchsia_api_level_at_least = "HEAD")]
852            fdecl::Use::Config(u) => {
853                const DEPENDENCY_TYPE: Option<fdecl::DependencyType> =
854                    Some(fdecl::DependencyType::Strong);
855                let decl = DeclType::UseConfiguration;
856                self.validate_use_fields(
857                    decl,
858                    Self::config_checker,
859                    u.source.as_ref(),
860                    u.source_name.as_ref(),
861                    None,
862                    None,
863                    DEPENDENCY_TYPE.as_ref(),
864                    u.availability.as_ref(),
865                );
866            }
867            fdecl::UseUnknown!() => {
868                self.errors.push(Error::invalid_field(DeclType::Component, "use"));
869            }
870        }
871    }
872
873    /// Validates the "program" declaration. This does not check runner-specific properties
874    /// since those are checked by the runner.
875    fn validate_program(
876        &mut self,
877        program: &fdecl::Program,
878        use_runner_name: Option<&String>,
879        _use_runner_source: Option<&fdecl::Ref>,
880    ) {
881        match &program.runner {
882            Some(_) =>
883            {
884                #[cfg(fuchsia_api_level_at_least = "HEAD")]
885                if use_runner_name.is_some() {
886                    if use_runner_name != program.runner.as_ref()
887                        || _use_runner_source
888                            != Some(&fdecl::Ref::Environment(fdecl::EnvironmentRef))
889                    {
890                        self.errors.push(Error::ConflictingRunners);
891                    }
892                }
893            }
894            None => {
895                if use_runner_name.is_none() {
896                    self.errors.push(Error::MissingRunner);
897                }
898            }
899        }
900
901        if program.info.is_none() {
902            self.errors.push(Error::missing_field(DeclType::Program, "info"));
903        }
904    }
905
906    /// Validates that paths-based capabilities (service, directory, protocol)
907    /// are different, are not prefixes of each other, and do not collide "/pkg".
908    fn validate_use_paths(&mut self, uses: &[fdecl::Use]) {
909        #[derive(Debug, PartialEq, Clone, Copy)]
910        struct PathCapability<'a> {
911            decl: DeclType,
912            dir: &'a Path,
913            use_: &'a fdecl::Use,
914        }
915        let mut used_paths = HashMap::new();
916        for use_ in uses.iter() {
917            match use_ {
918                fdecl::Use::Service(fdecl::UseService { target_path: Some(path), .. })
919                | fdecl::Use::Protocol(fdecl::UseProtocol { target_path: Some(path), .. })
920                | fdecl::Use::Directory(fdecl::UseDirectory { target_path: Some(path), .. })
921                | fdecl::Use::Storage(fdecl::UseStorage { target_path: Some(path), .. }) => {
922                    let capability = match use_ {
923                        fdecl::Use::Service(_) => {
924                            let dir = match Path::new(path).parent() {
925                                Some(p) => p,
926                                None => continue, // Invalid path, validated elsewhere
927                            };
928                            PathCapability { decl: DeclType::UseService, dir, use_ }
929                        }
930                        fdecl::Use::Protocol(_) => {
931                            let dir = match Path::new(path).parent() {
932                                Some(p) => p,
933                                None => continue, // Invalid path, validated elsewhere
934                            };
935                            PathCapability { decl: DeclType::UseProtocol, dir, use_ }
936                        }
937                        fdecl::Use::Directory(_) => PathCapability {
938                            decl: DeclType::UseDirectory,
939                            dir: Path::new(path),
940                            use_,
941                        },
942                        fdecl::Use::Storage(_) => PathCapability {
943                            decl: DeclType::UseStorage,
944                            dir: Path::new(path),
945                            use_,
946                        },
947                        _ => unreachable!(),
948                    };
949                    if used_paths.insert(path, capability).is_some() {
950                        // Disallow multiple capabilities for the same path.
951                        self.errors.push(Error::duplicate_field(
952                            capability.decl,
953                            "target_path",
954                            path,
955                        ));
956                    }
957                }
958                _ => {}
959            }
960        }
961        for ((&path_a, capability_a), (&path_b, capability_b)) in
962            used_paths.iter().tuple_combinations()
963        {
964            if match (capability_a.use_, capability_b.use_) {
965                // Directories and storage can't be the same or partially overlap.
966                (fdecl::Use::Directory(_), fdecl::Use::Directory(_))
967                | (fdecl::Use::Storage(_), fdecl::Use::Directory(_))
968                | (fdecl::Use::Directory(_), fdecl::Use::Storage(_))
969                | (fdecl::Use::Storage(_), fdecl::Use::Storage(_)) => {
970                    capability_b.dir == capability_a.dir
971                        || capability_b.dir.starts_with(capability_a.dir)
972                        || capability_a.dir.starts_with(capability_b.dir)
973                }
974
975                // Protocols and Services can't overlap with Directories.
976                (_, fdecl::Use::Directory(_)) | (fdecl::Use::Directory(_), _) => {
977                    capability_b.dir == capability_a.dir
978                        || capability_b.dir.starts_with(capability_a.dir)
979                        || capability_a.dir.starts_with(capability_b.dir)
980                }
981
982                // Protocols and Services containing directories may be same, but
983                // partial overlap is disallowed.
984                (_, _) => {
985                    capability_b.dir != capability_a.dir
986                        && (capability_b.dir.starts_with(capability_a.dir)
987                            || capability_a.dir.starts_with(capability_b.dir))
988                }
989            } {
990                self.errors.push(Error::invalid_path_overlap(
991                    capability_a.decl,
992                    path_a,
993                    capability_b.decl,
994                    path_b,
995                ));
996            }
997        }
998        for (used_path, capability) in used_paths.iter() {
999            if used_path.as_str() == "/pkg" || used_path.starts_with("/pkg/") {
1000                self.errors.push(Error::pkg_path_overlap(capability.decl, *used_path));
1001            }
1002        }
1003    }
1004
1005    fn validate_use_fields(
1006        &mut self,
1007        decl: DeclType,
1008        // This takes a callback that returns a [Container], instead of the &[Container] directly,
1009        // to avoid a borrow checker error that would occur from a simultaneous borrow on
1010        // &mut self.
1011        capability_checker: impl Fn(&Self) -> &dyn Container,
1012        source: Option<&'a fdecl::Ref>,
1013        source_name: Option<&'a String>,
1014        source_dictionary: Option<&'a String>,
1015        target_path: Option<&'a String>,
1016        dependency_type: Option<&fdecl::DependencyType>,
1017        availability: Option<&'a fdecl::Availability>,
1018    ) {
1019        self.validate_use_source(decl, source, source_dictionary);
1020
1021        check_name(source_name, decl, "source_name", &mut self.errors);
1022        if source_dictionary.is_some() {
1023            check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
1024        }
1025        if decl != DeclType::UseRunner && decl != DeclType::UseConfiguration {
1026            check_path(target_path, decl, "target_path", &mut self.errors);
1027        }
1028        check_use_availability(decl, availability, &mut self.errors);
1029
1030        // Only allow `weak` dependency with `use from child`.
1031        let is_use_from_child = match source {
1032            Some(fdecl::Ref::Child(_)) => true,
1033            _ => false,
1034        };
1035        match (is_use_from_child, dependency_type) {
1036            (false, Some(fdecl::DependencyType::Weak)) => {
1037                self.errors.push(Error::invalid_field(decl, "dependency_type"));
1038            }
1039            _ => {}
1040        }
1041
1042        self.validate_route_from_self(
1043            decl,
1044            source,
1045            source_name,
1046            source_dictionary,
1047            capability_checker,
1048        );
1049    }
1050
1051    fn validate_use_source(
1052        &mut self,
1053        decl: DeclType,
1054        source: Option<&'a fdecl::Ref>,
1055        source_dictionary: Option<&'a String>,
1056    ) {
1057        match (source, source_dictionary) {
1058            // These sources support source_dictionary.
1059            (Some(fdecl::Ref::Parent(_)), _) => {}
1060            (Some(fdecl::Ref::Self_(_)), _) => {}
1061            (Some(fdecl::Ref::Child(child)), _) => {
1062                self.validate_child_ref(decl, "source", &child, OfferType::Static);
1063                return;
1064            }
1065            // These sources don't.
1066            (Some(fdecl::Ref::Framework(_)), None) => {}
1067            (Some(fdecl::Ref::Debug(_)), None) => {}
1068            (Some(fdecl::Ref::Capability(c)), None) => {
1069                self.validate_source_capability(&c, decl, "source");
1070                return;
1071            }
1072            #[cfg(fuchsia_api_level_at_least = "HEAD")]
1073            (Some(fdecl::Ref::Environment(_)), None) => {}
1074            (Some(fdecl::Ref::Collection(collection)), None) if decl == DeclType::UseService => {
1075                self.validate_collection_ref(decl, "source", &collection);
1076                return;
1077            }
1078            // `source` is required.
1079            (None, _) => self.errors.push(Error::missing_field(decl, "source")),
1080            // Any combination that was not recognized above must be invalid.
1081            (_, _) => self.errors.push(Error::invalid_field(decl, "source")),
1082        }
1083    }
1084
1085    fn validate_child_decl(&mut self, child: &'a fdecl::Child) {
1086        if let Err(mut e) = validate_child(child, check_name) {
1087            self.errors.append(&mut e.errs);
1088        }
1089        if let Some(name) = child.name.as_ref() {
1090            let name: &str = name;
1091            if self.all_children.insert(name, child).is_some() {
1092                self.errors.push(Error::duplicate_field(DeclType::Child, "name", name));
1093            }
1094        }
1095        if let Some(environment) = child.environment.as_ref() {
1096            if !self.all_environment_names.contains(environment.as_str()) {
1097                self.errors.push(Error::invalid_environment(
1098                    DeclType::Child,
1099                    "environment",
1100                    environment,
1101                ));
1102            }
1103        }
1104    }
1105
1106    fn validate_collection_decl(&mut self, collection: &'a fdecl::Collection) {
1107        let name = collection.name.as_ref();
1108        if check_name(name, DeclType::Collection, "name", &mut self.errors) {
1109            let name: &str = name.unwrap();
1110            if !self.all_collections.insert(name) {
1111                self.errors.push(Error::duplicate_field(DeclType::Collection, "name", name));
1112            }
1113        }
1114        if collection.durability.is_none() {
1115            self.errors.push(Error::missing_field(DeclType::Collection, "durability"));
1116        }
1117        if let Some(environment) = collection.environment.as_ref() {
1118            if !self.all_environment_names.contains(environment.as_str()) {
1119                self.errors.push(Error::invalid_environment(
1120                    DeclType::Collection,
1121                    "environment",
1122                    environment,
1123                ));
1124            }
1125        }
1126        // Allow `allowed_offers` & `allow_long_names` to be unset/unvalidated, for backwards compatibility.
1127    }
1128
1129    fn validate_environment_decl(&mut self, environment: &'a fdecl::Environment) {
1130        let name = environment.name.as_ref();
1131        check_name(name, DeclType::Environment, "name", &mut self.errors);
1132        if environment.extends.is_none() {
1133            self.errors.push(Error::missing_field(DeclType::Environment, "extends"));
1134        }
1135        if let Some(runners) = environment.runners.as_ref() {
1136            let mut registered_runners = HashSet::new();
1137            for runner in runners {
1138                self.validate_runner_registration(runner, &mut registered_runners);
1139            }
1140        }
1141        if let Some(resolvers) = environment.resolvers.as_ref() {
1142            let mut registered_schemes = HashSet::new();
1143            for resolver in resolvers {
1144                self.validate_resolver_registration(resolver, &mut registered_schemes);
1145            }
1146        }
1147
1148        match environment.extends.as_ref() {
1149            Some(fdecl::EnvironmentExtends::None) => {
1150                if environment.stop_timeout_ms.is_none() {
1151                    self.errors
1152                        .push(Error::missing_field(DeclType::Environment, "stop_timeout_ms"));
1153                }
1154            }
1155            None | Some(fdecl::EnvironmentExtends::Realm) => {}
1156        }
1157
1158        if let Some(debugs) = environment.debug_capabilities.as_ref() {
1159            for debug in debugs {
1160                self.validate_environment_debug_registration(debug);
1161            }
1162        }
1163    }
1164
1165    fn validate_runner_registration(
1166        &mut self,
1167        runner_registration: &'a fdecl::RunnerRegistration,
1168        runner_names: &mut HashSet<&'a str>,
1169    ) {
1170        check_name(
1171            runner_registration.source_name.as_ref(),
1172            DeclType::RunnerRegistration,
1173            "source_name",
1174            &mut self.errors,
1175        );
1176        self.validate_registration_source(
1177            runner_registration.source.as_ref(),
1178            DeclType::RunnerRegistration,
1179        );
1180        // If the source is `self`, ensure we have a corresponding Runner.
1181        if let (Some(fdecl::Ref::Self_(_)), Some(ref name)) =
1182            (&runner_registration.source, &runner_registration.source_name)
1183        {
1184            if !self.all_runners.contains(name as &str) {
1185                self.errors.push(Error::invalid_runner(
1186                    DeclType::RunnerRegistration,
1187                    "source_name",
1188                    name,
1189                ));
1190            }
1191        }
1192
1193        check_name(
1194            runner_registration.target_name.as_ref(),
1195            DeclType::RunnerRegistration,
1196            "target_name",
1197            &mut self.errors,
1198        );
1199        if let Some(name) = runner_registration.target_name.as_ref() {
1200            if !runner_names.insert(name.as_str()) {
1201                self.errors.push(Error::duplicate_field(
1202                    DeclType::RunnerRegistration,
1203                    "target_name",
1204                    name,
1205                ));
1206            }
1207        }
1208    }
1209
1210    fn validate_resolver_registration(
1211        &mut self,
1212        resolver_registration: &'a fdecl::ResolverRegistration,
1213        schemes: &mut HashSet<&'a str>,
1214    ) {
1215        check_name(
1216            resolver_registration.resolver.as_ref(),
1217            DeclType::ResolverRegistration,
1218            "resolver",
1219            &mut self.errors,
1220        );
1221        self.validate_registration_source(
1222            resolver_registration.source.as_ref(),
1223            DeclType::ResolverRegistration,
1224        );
1225        check_url_scheme(
1226            resolver_registration.scheme.as_ref(),
1227            DeclType::ResolverRegistration,
1228            "scheme",
1229            &mut self.errors,
1230        );
1231        if let Some(scheme) = resolver_registration.scheme.as_ref() {
1232            if !schemes.insert(scheme.as_str()) {
1233                self.errors.push(Error::duplicate_field(
1234                    DeclType::ResolverRegistration,
1235                    "scheme",
1236                    scheme,
1237                ));
1238            }
1239        }
1240    }
1241
1242    fn validate_registration_source(&mut self, source: Option<&'a fdecl::Ref>, ty: DeclType) {
1243        match source {
1244            Some(fdecl::Ref::Parent(_)) => {}
1245            Some(fdecl::Ref::Self_(_)) => {}
1246            Some(fdecl::Ref::Child(child_ref)) => {
1247                // Make sure the child is valid.
1248                self.validate_child_ref(ty, "source", &child_ref, OfferType::Static);
1249            }
1250            Some(_) => {
1251                self.errors.push(Error::invalid_field(ty, "source"));
1252            }
1253            None => {
1254                self.errors.push(Error::missing_field(ty, "source"));
1255            }
1256        }
1257    }
1258
1259    fn validate_service_decl(&mut self, service: &'a fdecl::Service, as_builtin: bool) {
1260        if check_name(service.name.as_ref(), DeclType::Service, "name", &mut self.errors) {
1261            let name = service.name.as_ref().unwrap();
1262            if !self.all_capability_ids.insert(name) {
1263                self.errors.push(Error::duplicate_field(DeclType::Service, "name", name.as_str()));
1264            }
1265            self.all_services.insert(name);
1266        }
1267        match as_builtin {
1268            true => {
1269                if let Some(path) = service.source_path.as_ref() {
1270                    self.errors.push(Error::extraneous_source_path(DeclType::Service, path))
1271                }
1272            }
1273            false => {
1274                check_path(
1275                    service.source_path.as_ref(),
1276                    DeclType::Service,
1277                    "source_path",
1278                    &mut self.errors,
1279                );
1280            }
1281        }
1282    }
1283
1284    fn validate_protocol_decl(&mut self, protocol: &'a fdecl::Protocol, as_builtin: bool) {
1285        if check_name(protocol.name.as_ref(), DeclType::Protocol, "name", &mut self.errors) {
1286            let name = protocol.name.as_ref().unwrap();
1287            if !self.all_capability_ids.insert(name) {
1288                self.errors.push(Error::duplicate_field(DeclType::Protocol, "name", name.as_str()));
1289            }
1290            self.all_protocols.insert(name);
1291        }
1292        match as_builtin {
1293            true => {
1294                if let Some(path) = protocol.source_path.as_ref() {
1295                    self.errors.push(Error::extraneous_source_path(DeclType::Protocol, path))
1296                }
1297            }
1298            false => {
1299                check_path(
1300                    protocol.source_path.as_ref(),
1301                    DeclType::Protocol,
1302                    "source_path",
1303                    &mut self.errors,
1304                );
1305            }
1306        }
1307
1308        #[cfg(fuchsia_api_level_at_least = "HEAD")]
1309        match protocol.delivery {
1310            Some(delivery) => match cm_types::DeliveryType::try_from(delivery) {
1311                Ok(_) => {}
1312                Err(_) => self.errors.push(Error::invalid_field(DeclType::Protocol, "delivery")),
1313            },
1314            None => {}
1315        }
1316    }
1317
1318    fn validate_directory_decl(&mut self, directory: &'a fdecl::Directory, as_builtin: bool) {
1319        if check_name(directory.name.as_ref(), DeclType::Directory, "name", &mut self.errors) {
1320            let name = directory.name.as_ref().unwrap();
1321            if !self.all_capability_ids.insert(name) {
1322                self.errors.push(Error::duplicate_field(
1323                    DeclType::Directory,
1324                    "name",
1325                    name.as_str(),
1326                ));
1327            }
1328            self.all_directories.insert(name);
1329        }
1330        match as_builtin {
1331            true => {
1332                if let Some(path) = directory.source_path.as_ref() {
1333                    self.errors.push(Error::extraneous_source_path(DeclType::Directory, path))
1334                }
1335            }
1336            false => {
1337                check_path(
1338                    directory.source_path.as_ref(),
1339                    DeclType::Directory,
1340                    "source_path",
1341                    &mut self.errors,
1342                );
1343            }
1344        }
1345        if directory.rights.is_none() {
1346            self.errors.push(Error::missing_field(DeclType::Directory, "rights"));
1347        }
1348    }
1349
1350    fn validate_storage_decl(&mut self, storage: &'a fdecl::Storage) {
1351        match storage.source.as_ref() {
1352            Some(fdecl::Ref::Parent(_)) => {}
1353            Some(fdecl::Ref::Self_(_)) => {}
1354            Some(fdecl::Ref::Child(child)) => {
1355                let _ =
1356                    self.validate_child_ref(DeclType::Storage, "source", &child, OfferType::Static);
1357            }
1358            Some(_) => {
1359                self.errors.push(Error::invalid_field(DeclType::Storage, "source"));
1360            }
1361            None => {
1362                self.errors.push(Error::missing_field(DeclType::Storage, "source"));
1363            }
1364        };
1365        if check_name(storage.name.as_ref(), DeclType::Storage, "name", &mut self.errors) {
1366            let name = storage.name.as_ref().unwrap();
1367            if !self.all_capability_ids.insert(name) {
1368                self.errors.push(Error::duplicate_field(DeclType::Storage, "name", name.as_str()));
1369            }
1370            self.all_storages.insert(name, storage.source.as_ref());
1371        }
1372        if storage.storage_id.is_none() {
1373            self.errors.push(Error::missing_field(DeclType::Storage, "storage_id"));
1374        }
1375        check_name(
1376            storage.backing_dir.as_ref(),
1377            DeclType::Storage,
1378            "backing_dir",
1379            &mut self.errors,
1380        );
1381    }
1382
1383    fn validate_runner_decl(&mut self, runner: &'a fdecl::Runner, as_builtin: bool) {
1384        if check_name(runner.name.as_ref(), DeclType::Runner, "name", &mut self.errors) {
1385            let name = runner.name.as_ref().unwrap();
1386            if !self.all_capability_ids.insert(name) {
1387                self.errors.push(Error::duplicate_field(DeclType::Runner, "name", name.as_str()));
1388            }
1389            self.all_runners.insert(name);
1390        }
1391        match as_builtin {
1392            true => {
1393                if let Some(path) = runner.source_path.as_ref() {
1394                    self.errors.push(Error::extraneous_source_path(DeclType::Runner, path))
1395                }
1396            }
1397            false => {
1398                check_path(
1399                    runner.source_path.as_ref(),
1400                    DeclType::Runner,
1401                    "source_path",
1402                    &mut self.errors,
1403                );
1404            }
1405        }
1406    }
1407
1408    fn validate_resolver_decl(&mut self, resolver: &'a fdecl::Resolver, as_builtin: bool) {
1409        if check_name(resolver.name.as_ref(), DeclType::Resolver, "name", &mut self.errors) {
1410            let name = resolver.name.as_ref().unwrap();
1411            if !self.all_capability_ids.insert(name) {
1412                self.errors.push(Error::duplicate_field(DeclType::Resolver, "name", name.as_str()));
1413            }
1414            self.all_resolvers.insert(name);
1415        }
1416        match as_builtin {
1417            true => {
1418                if let Some(path) = resolver.source_path.as_ref() {
1419                    self.errors.push(Error::extraneous_source_path(DeclType::Resolver, path))
1420                }
1421            }
1422            false => {
1423                check_path(
1424                    resolver.source_path.as_ref(),
1425                    DeclType::Resolver,
1426                    "source_path",
1427                    &mut self.errors,
1428                );
1429            }
1430        }
1431    }
1432
1433    // Dictionaries can reference other dictionaries in the same manifest, so before processing any
1434    // dictionary declarations this function should be called to do a first pass to pre-populate
1435    // the dictionary map.
1436    #[cfg(fuchsia_api_level_at_least = "25")]
1437    fn load_dictionary_names(&mut self, dictionaries: impl Iterator<Item = &'a fdecl::Dictionary>) {
1438        for dictionary in dictionaries {
1439            let decl = DeclType::Dictionary;
1440            if check_name(dictionary.name.as_ref(), decl, "name", &mut self.errors) {
1441                let name = dictionary.name.as_ref().unwrap();
1442                if !self.all_capability_ids.insert(name) {
1443                    self.errors.push(Error::duplicate_field(decl, "name", name.as_str()));
1444                }
1445                self.all_dictionaries.insert(name, &dictionary);
1446            }
1447        }
1448    }
1449
1450    #[cfg(fuchsia_api_level_at_least = "25")]
1451    fn validate_dictionary_decl(&mut self, dictionary: &'a fdecl::Dictionary) {
1452        let decl = DeclType::Dictionary;
1453        if let Some(path) = dictionary.source_path.as_ref() {
1454            if dictionary.source.is_some() {
1455                self.errors.push(Error::extraneous_field(decl, "source"));
1456            }
1457            check_path(Some(path), DeclType::Dictionary, "source_path", &mut self.errors);
1458        }
1459    }
1460
1461    #[cfg(fuchsia_api_level_at_least = "HEAD")]
1462    fn validate_configuration_decl(&mut self, config: &'a fdecl::Configuration) {
1463        let decl = DeclType::Configuration;
1464        if check_name(config.name.as_ref(), decl, "name", &mut self.errors) {
1465            let name = config.name.as_ref().unwrap();
1466            if !self.all_capability_ids.insert(name) {
1467                self.errors.push(Error::duplicate_field(decl, "name", name.as_str()));
1468            }
1469            self.all_configs.insert(name);
1470        }
1471    }
1472
1473    fn validate_environment_debug_registration(&mut self, debug: &'a fdecl::DebugRegistration) {
1474        match debug {
1475            fdecl::DebugRegistration::Protocol(o) => {
1476                let decl = DeclType::DebugProtocolRegistration;
1477                self.validate_environment_debug_fields(
1478                    decl,
1479                    o.source.as_ref(),
1480                    o.source_name.as_ref(),
1481                    o.target_name.as_ref(),
1482                );
1483
1484                if let (Some(fdecl::Ref::Self_(_)), Some(ref name)) = (&o.source, &o.source_name) {
1485                    if !self.all_protocols.contains(&name as &str) {
1486                        self.errors.push(Error::invalid_field(decl, "source"));
1487                    }
1488                }
1489            }
1490            _ => {
1491                self.errors.push(Error::invalid_field(DeclType::Environment, "debug"));
1492            }
1493        }
1494    }
1495
1496    fn validate_environment_debug_fields(
1497        &mut self,
1498        decl: DeclType,
1499        source: Option<&fdecl::Ref>,
1500        source_name: Option<&String>,
1501        target_name: Option<&'a String>,
1502    ) {
1503        // We don't support "source" from "capability" for now.
1504        match source {
1505            Some(fdecl::Ref::Parent(_)) => {}
1506            Some(fdecl::Ref::Self_(_)) => {}
1507            Some(fdecl::Ref::Framework(_)) => {}
1508            Some(fdecl::Ref::Child(child)) => {
1509                let _ = self.validate_child_ref(decl, "source", &child, OfferType::Static);
1510            }
1511            Some(_) => self.errors.push(Error::invalid_field(decl, "source")),
1512            None => self.errors.push(Error::missing_field(decl, "source")),
1513        }
1514        check_name(source_name, decl, "source_name", &mut self.errors);
1515        check_name(target_name, decl, "target_name", &mut self.errors);
1516    }
1517
1518    fn validate_event_stream_decl(&mut self, event: &'a fdecl::EventStream) {
1519        if check_name(event.name.as_ref(), DeclType::EventStream, "name", &mut self.errors) {
1520            let name = event.name.as_ref().unwrap();
1521            if !self.all_capability_ids.insert(name) {
1522                self.errors.push(Error::duplicate_field(
1523                    DeclType::EventStream,
1524                    "name",
1525                    name.as_str(),
1526                ));
1527            }
1528        }
1529    }
1530
1531    fn validate_source_collection(
1532        &mut self,
1533        collection: &fdecl::CollectionRef,
1534        decl_type: DeclType,
1535    ) -> bool {
1536        let num_errors = self.errors.len();
1537        if check_name(Some(&collection.name), decl_type, "source.collection.name", &mut self.errors)
1538            && !self.all_collections.contains(&collection.name as &str)
1539        {
1540            self.errors.push(Error::invalid_collection(
1541                decl_type,
1542                "source",
1543                &collection.name as &str,
1544            ));
1545        }
1546        num_errors == self.errors.len()
1547    }
1548
1549    fn validate_filtered_service_fields(
1550        &mut self,
1551        decl_type: DeclType,
1552        source_instance_filter: Option<&Vec<String>>,
1553        renamed_instances: Option<&Vec<fdecl::NameMapping>>,
1554    ) {
1555        if let Some(source_instance_filter) = source_instance_filter {
1556            if source_instance_filter.is_empty() {
1557                // if the  source_instance_filter is empty the offered service will have 0 instances,
1558                // which means the offer shouldn't have been created at all.
1559                self.errors.push(Error::invalid_field(decl_type, "source_instance_filter"));
1560            }
1561            for name in source_instance_filter {
1562                check_name(Some(name), decl_type, "source_instance_filter", &mut self.errors);
1563            }
1564        }
1565        if let Some(renamed_instances) = renamed_instances {
1566            // Multiple sources shouldn't map to the same target name
1567            let mut seen_target_names = HashSet::<String>::new();
1568            for mapping in renamed_instances {
1569                check_name(
1570                    Some(&mapping.source_name),
1571                    decl_type,
1572                    "renamed_instances.source_name",
1573                    &mut self.errors,
1574                );
1575                check_name(
1576                    Some(&mapping.target_name),
1577                    decl_type,
1578                    "renamed_instances.target_name",
1579                    &mut self.errors,
1580                );
1581                if !seen_target_names.insert(mapping.target_name.clone()) {
1582                    self.errors.push(Error::invalid_field(decl_type, "renamed_instances"));
1583                    break;
1584                }
1585            }
1586        }
1587    }
1588
1589    fn validate_source_capability(
1590        &mut self,
1591        capability: &fdecl::CapabilityRef,
1592        decl_type: DeclType,
1593        field: &str,
1594    ) -> bool {
1595        let num_errors = self.errors.len();
1596        if check_name(Some(&capability.name), decl_type, "source.capability.name", &mut self.errors)
1597            && !self.all_capability_ids.contains(capability.name.as_str())
1598        {
1599            self.errors.push(Error::invalid_capability(decl_type, field, &capability.name));
1600        }
1601        num_errors == self.errors.len()
1602    }
1603
1604    /// Return a key that can be used in `HashMap` to group aggregate declarations.
1605    ///
1606    /// Returns `None` if the input resembles an invalid declaration.
1607    fn make_group_key(
1608        target_name: Option<&'a String>,
1609        target: Option<&'a fdecl::Ref>,
1610    ) -> Option<(&'a str, RefKey<'a>)> {
1611        if target_name.is_none() {
1612            return None;
1613        }
1614        let target_name = target_name.unwrap().as_str();
1615        if target.is_none() {
1616            return None;
1617        }
1618        let target = match target.unwrap() {
1619            fdecl::Ref::Parent(_) => RefKey::Parent,
1620            fdecl::Ref::Self_(_) => RefKey::Self_,
1621            fdecl::Ref::Child(r) => RefKey::Child(r.name.as_str()),
1622            fdecl::Ref::Collection(r) => RefKey::Collection(r.name.as_str()),
1623            fdecl::Ref::Framework(_) => RefKey::Framework,
1624            fdecl::Ref::Capability(_) => RefKey::Capability,
1625            fdecl::Ref::Debug(_) => RefKey::Debug,
1626            fdecl::RefUnknown!() => {
1627                return None;
1628            }
1629        };
1630        Some((target_name, target))
1631    }
1632
1633    fn validate_aggregation_has_same_availability(
1634        &mut self,
1635        route_group: &Vec<impl HasAvailability>,
1636    ) {
1637        // Use `BtreeSet` for stable ordering of items in error message.
1638        let availability_of_sources: BTreeSet<_> =
1639            route_group.iter().map(|r| r.availability()).collect();
1640
1641        // All sources that feed into an aggregation operation should have the same availability.
1642        if availability_of_sources.len() > 1 {
1643            self.errors.push(Error::different_availability_in_aggregation(
1644                availability_of_sources.into_iter().collect(),
1645            ));
1646        }
1647    }
1648
1649    // Checks a group of expose decls to confirm that any duplicate exposes are
1650    // valid aggregate expose declarations.
1651    fn validate_expose_group(&mut self, exposes: &'a Vec<fdecl::Expose>) {
1652        let mut expose_groups: HashMap<_, Vec<fdecl::ExposeService>> = HashMap::new();
1653        let service_exposes = exposes.into_iter().filter_map(|o| {
1654            if let fdecl::Expose::Service(s) = o {
1655                Some(s)
1656            } else {
1657                None
1658            }
1659        });
1660        for expose in service_exposes {
1661            let key = Self::make_group_key(expose.target_name.as_ref(), expose.target.as_ref());
1662            if let Some(key) = key {
1663                expose_groups.entry(key).or_insert_with(|| vec![]).push(expose.clone());
1664            }
1665        }
1666        for expose_group in expose_groups.into_values() {
1667            if expose_group.len() == 1 {
1668                // If there are not multiple exposes for a (target_name, target) pair then there are
1669                // no aggregation conditions to check.
1670                continue;
1671            }
1672
1673            self.validate_aggregation_has_same_availability(&expose_group);
1674        }
1675    }
1676
1677    fn validate_expose_decl(
1678        &mut self,
1679        expose: &'a fdecl::Expose,
1680        expose_to_parent_ids: &mut HashMap<&'a str, AllowableIds>,
1681        expose_to_framework_ids: &mut HashMap<&'a str, AllowableIds>,
1682    ) {
1683        match expose {
1684            fdecl::Expose::Service(e) => {
1685                let decl = DeclType::ExposeService;
1686                self.validate_expose_fields(
1687                    decl,
1688                    AllowableIds::Many,
1689                    CollectionSource::Allow,
1690                    Self::service_checker,
1691                    e.source.as_ref(),
1692                    e.source_name.as_ref(),
1693                    get_source_dictionary!(e),
1694                    e.target.as_ref(),
1695                    e.target_name.as_ref(),
1696                    e.availability.as_ref(),
1697                    expose_to_parent_ids,
1698                    expose_to_framework_ids,
1699                );
1700            }
1701            fdecl::Expose::Protocol(e) => {
1702                let decl = DeclType::ExposeProtocol;
1703                self.validate_expose_fields(
1704                    decl,
1705                    AllowableIds::One,
1706                    CollectionSource::Deny,
1707                    Self::protocol_checker,
1708                    e.source.as_ref(),
1709                    e.source_name.as_ref(),
1710                    get_source_dictionary!(e),
1711                    e.target.as_ref(),
1712                    e.target_name.as_ref(),
1713                    e.availability.as_ref(),
1714                    expose_to_parent_ids,
1715                    expose_to_framework_ids,
1716                );
1717            }
1718            fdecl::Expose::Directory(e) => {
1719                let decl = DeclType::ExposeDirectory;
1720                self.validate_expose_fields(
1721                    decl,
1722                    AllowableIds::One,
1723                    CollectionSource::Deny,
1724                    Self::directory_checker,
1725                    e.source.as_ref(),
1726                    e.source_name.as_ref(),
1727                    get_source_dictionary!(e),
1728                    e.target.as_ref(),
1729                    e.target_name.as_ref(),
1730                    e.availability.as_ref(),
1731                    expose_to_parent_ids,
1732                    expose_to_framework_ids,
1733                );
1734
1735                // Subdir makes sense when routing, but when exposing to framework the subdirectory
1736                // can be exposed directly.
1737                match e.target.as_ref() {
1738                    Some(fdecl::Ref::Framework(_)) => {
1739                        if e.subdir.is_some() {
1740                            self.errors.push(Error::invalid_field(decl, "subdir"));
1741                        }
1742                    }
1743                    _ => {}
1744                }
1745
1746                if let Some(subdir) = e.subdir.as_ref() {
1747                    check_relative_path(Some(subdir), decl, "subdir", &mut self.errors);
1748                }
1749            }
1750            fdecl::Expose::Runner(e) => {
1751                let decl = DeclType::ExposeRunner;
1752                self.validate_expose_fields(
1753                    decl,
1754                    AllowableIds::One,
1755                    CollectionSource::Deny,
1756                    Self::runner_checker,
1757                    e.source.as_ref(),
1758                    e.source_name.as_ref(),
1759                    get_source_dictionary!(e),
1760                    e.target.as_ref(),
1761                    e.target_name.as_ref(),
1762                    Some(&fdecl::Availability::Required),
1763                    expose_to_parent_ids,
1764                    expose_to_framework_ids,
1765                );
1766            }
1767            fdecl::Expose::Resolver(e) => {
1768                let decl = DeclType::ExposeResolver;
1769                self.validate_expose_fields(
1770                    decl,
1771                    AllowableIds::One,
1772                    CollectionSource::Deny,
1773                    Self::resolver_checker,
1774                    e.source.as_ref(),
1775                    e.source_name.as_ref(),
1776                    get_source_dictionary!(e),
1777                    e.target.as_ref(),
1778                    e.target_name.as_ref(),
1779                    Some(&fdecl::Availability::Required),
1780                    expose_to_parent_ids,
1781                    expose_to_framework_ids,
1782                );
1783            }
1784            #[cfg(fuchsia_api_level_at_least = "25")]
1785            fdecl::Expose::Dictionary(e) => {
1786                let decl = DeclType::ExposeDictionary;
1787                self.validate_expose_fields(
1788                    decl,
1789                    AllowableIds::One,
1790                    CollectionSource::Deny,
1791                    Self::dictionary_checker,
1792                    e.source.as_ref(),
1793                    e.source_name.as_ref(),
1794                    get_source_dictionary!(e),
1795                    e.target.as_ref(),
1796                    e.target_name.as_ref(),
1797                    Some(&fdecl::Availability::Required),
1798                    expose_to_parent_ids,
1799                    expose_to_framework_ids,
1800                );
1801            }
1802            #[cfg(fuchsia_api_level_at_least = "HEAD")]
1803            fdecl::Expose::Config(e) => {
1804                let decl = DeclType::ExposeConfig;
1805                self.validate_expose_fields(
1806                    decl,
1807                    AllowableIds::One,
1808                    CollectionSource::Deny,
1809                    Self::config_checker,
1810                    e.source.as_ref(),
1811                    e.source_name.as_ref(),
1812                    None,
1813                    e.target.as_ref(),
1814                    e.target_name.as_ref(),
1815                    e.availability.as_ref(),
1816                    expose_to_parent_ids,
1817                    expose_to_framework_ids,
1818                );
1819            }
1820            _ => {
1821                self.errors.push(Error::invalid_field(DeclType::Component, "expose"));
1822            }
1823        }
1824    }
1825
1826    fn validate_expose_fields(
1827        &mut self,
1828        decl: DeclType,
1829        allowable_ids: AllowableIds,
1830        collection_source: CollectionSource,
1831        // This takes a callback that returns a [Container], instead of the &[Container] directly,
1832        // to avoid a borrow checker error that would occur from a simultaneous borrow on
1833        // &mut self.
1834        capability_checker: impl Fn(&Self) -> &dyn Container,
1835        source: Option<&fdecl::Ref>,
1836        source_name: Option<&String>,
1837        source_dictionary: Option<&String>,
1838        target: Option<&fdecl::Ref>,
1839        target_name: Option<&'a String>,
1840        availability: Option<&fdecl::Availability>,
1841        expose_to_parent_ids: &mut HashMap<&'a str, AllowableIds>,
1842        expose_to_framework_ids: &mut HashMap<&'a str, AllowableIds>,
1843    ) {
1844        self.validate_expose_source(decl, collection_source, source, source_dictionary);
1845        check_route_availability(decl, availability, source, source_name, &mut self.errors);
1846        match target {
1847            Some(r) => match r {
1848                fdecl::Ref::Parent(_) => {}
1849                fdecl::Ref::Framework(_) => {}
1850                _ => {
1851                    self.errors.push(Error::invalid_field(decl, "target"));
1852                }
1853            },
1854            None => {
1855                self.errors.push(Error::missing_field(decl, "target"));
1856            }
1857        }
1858        check_name(source_name, decl, "source_name", &mut self.errors);
1859        if source_dictionary.is_some() {
1860            check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
1861        }
1862        if check_name(target_name, decl, "target_name", &mut self.errors) {
1863            let maybe_ids_set = match target {
1864                Some(fdecl::Ref::Parent(_)) => Some(expose_to_parent_ids),
1865                Some(fdecl::Ref::Framework(_)) => Some(expose_to_framework_ids),
1866                _ => None,
1867            };
1868            if let Some(ids_set) = maybe_ids_set {
1869                let target_name = target_name.unwrap();
1870                if let Some(prev_state) = ids_set.insert(target_name, allowable_ids) {
1871                    if prev_state == AllowableIds::One || prev_state != allowable_ids {
1872                        self.errors.push(Error::duplicate_field(decl, "target_name", target_name));
1873                    }
1874                }
1875            }
1876        }
1877
1878        self.validate_route_from_self(
1879            decl,
1880            source,
1881            source_name,
1882            source_dictionary,
1883            capability_checker,
1884        );
1885    }
1886
1887    fn validate_expose_source(
1888        &mut self,
1889        decl: DeclType,
1890        collection_source: CollectionSource,
1891        source: Option<&fdecl::Ref>,
1892        source_dictionary: Option<&String>,
1893    ) {
1894        match (source, source_dictionary) {
1895            // These sources support source_dictionary.
1896            (Some(fdecl::Ref::Self_(_)), _) => {}
1897            (Some(fdecl::Ref::Child(child)), _) => {
1898                let _ = self.validate_child_ref(decl, "source", &child, OfferType::Static);
1899            }
1900            // These sources don't.
1901            (Some(fdecl::Ref::VoidType(_)), None) => {}
1902            (Some(fdecl::Ref::Framework(_)), None) => {}
1903            (Some(fdecl::Ref::Capability(c)), None) => {
1904                self.validate_source_capability(c, decl, "source");
1905            }
1906            (Some(fdecl::Ref::Collection(c)), None)
1907                if collection_source == CollectionSource::Allow =>
1908            {
1909                self.validate_source_collection(c, decl);
1910            }
1911            // `source` is required.
1912            (None, _) => {
1913                self.errors.push(Error::missing_field(decl, "source"));
1914            }
1915            // Any combination that was not recognized above must be invalid.
1916            (_, _) => {
1917                self.errors.push(Error::invalid_field(decl, "source"));
1918            }
1919        }
1920    }
1921
1922    // Checks a group of offer decls to confirm that any duplicate offers are
1923    // valid aggregate offer declarations.
1924    fn validate_offer_group(&mut self, offers: &'a Vec<fdecl::Offer>, offer_type: OfferType) {
1925        let mut offer_groups: HashMap<_, Vec<fdecl::OfferService>> = HashMap::new();
1926        let service_offers = offers.into_iter().filter_map(|o| {
1927            if let fdecl::Offer::Service(s) = o {
1928                Some(s)
1929            } else {
1930                None
1931            }
1932        });
1933        for offer in service_offers {
1934            let key = Self::make_group_key(offer.target_name.as_ref(), offer.target.as_ref());
1935            if let Some(key) = key {
1936                offer_groups.entry(key).or_insert_with(|| vec![]).push(offer.clone());
1937            }
1938        }
1939        for offer_group in offer_groups.into_values() {
1940            if offer_group.len() == 1 {
1941                // If there are not multiple offers for a (target_name, target) pair then there are
1942                // no aggregation conditions to check.
1943                continue;
1944            }
1945
1946            self.validate_aggregation_has_same_availability(&offer_group);
1947
1948            let mut source_instance_filter_entries: HashSet<String> = HashSet::new();
1949            let mut service_source_names: HashSet<String> = HashSet::new();
1950            for o in offer_group {
1951                // Currently only service capabilities can be aggregated
1952                match (o.source_instance_filter, offer_type) {
1953                    (Some(source_instance_filter), _) => {
1954                        for instance_name in source_instance_filter {
1955                            if !source_instance_filter_entries.insert(instance_name.clone()) {
1956                                // If the source instance in the filter has been seen before this
1957                                // means there is a conflicting aggregate service offer.
1958                                self.errors.push(Error::invalid_aggregate_offer(format!(
1959                                    "Conflicting source_instance_filter in aggregate service \
1960                                    offer, instance_name '{}' seen in filter lists multiple times",
1961                                    instance_name,
1962                                )));
1963                            }
1964                        }
1965                    }
1966                    (None, OfferType::Static) => {}
1967                    (None, OfferType::Dynamic) => {
1968                        // Dynamic offers must include a filter.
1969                        self.errors.push(Error::invalid_aggregate_offer(
1970                            "source_instance_filter must be set for dynamic aggregate service \
1971                            offers",
1972                        ));
1973                    }
1974                }
1975                service_source_names.insert(
1976                    o.source_name
1977                        .expect("Offer Service declarations must always contain source_name"),
1978                );
1979            }
1980
1981            if service_source_names.len() > 1 {
1982                self.errors.push(Error::invalid_aggregate_offer(format!(
1983                    "All aggregate service offers must have the same source_name, saw {}. Use \
1984                    renamed_instances to rename instance names to avoid conflict.",
1985                    service_source_names.into_iter().sorted().collect::<Vec<String>>().join(", ")
1986                )));
1987            }
1988        }
1989    }
1990
1991    fn validate_offer_decl(&mut self, offer: &'a fdecl::Offer, offer_type: OfferType) {
1992        match offer {
1993            fdecl::Offer::Service(o) => {
1994                let decl = DeclType::OfferService;
1995                self.validate_offer_fields(
1996                    decl,
1997                    AllowableIds::Many,
1998                    CollectionSource::Allow,
1999                    Self::service_checker,
2000                    o.source.as_ref(),
2001                    o.source_name.as_ref(),
2002                    get_source_dictionary!(o),
2003                    o.target.as_ref(),
2004                    o.target_name.as_ref(),
2005                    #[cfg(fuchsia_api_level_at_least = "HEAD")]
2006                    Some(o.dependency_type.as_ref().unwrap_or(&fdecl::DependencyType::Strong)),
2007                    #[cfg(fuchsia_api_level_less_than = "HEAD")]
2008                    Some(&fdecl::DependencyType::Strong),
2009                    o.availability.as_ref(),
2010                    offer_type,
2011                );
2012                self.validate_filtered_service_fields(
2013                    decl,
2014                    o.source_instance_filter.as_ref(),
2015                    o.renamed_instances.as_ref(),
2016                );
2017            }
2018            fdecl::Offer::Protocol(o) => {
2019                let decl = DeclType::OfferProtocol;
2020                self.validate_offer_fields(
2021                    decl,
2022                    AllowableIds::One,
2023                    CollectionSource::Deny,
2024                    Self::protocol_checker,
2025                    o.source.as_ref(),
2026                    o.source_name.as_ref(),
2027                    get_source_dictionary!(o),
2028                    o.target.as_ref(),
2029                    o.target_name.as_ref(),
2030                    o.dependency_type.as_ref(),
2031                    o.availability.as_ref(),
2032                    offer_type,
2033                );
2034            }
2035            fdecl::Offer::Directory(o) => {
2036                let decl = DeclType::OfferDirectory;
2037                self.validate_offer_fields(
2038                    decl,
2039                    AllowableIds::One,
2040                    CollectionSource::Deny,
2041                    Self::directory_checker,
2042                    o.source.as_ref(),
2043                    o.source_name.as_ref(),
2044                    get_source_dictionary!(o),
2045                    o.target.as_ref(),
2046                    o.target_name.as_ref(),
2047                    o.dependency_type.as_ref(),
2048                    o.availability.as_ref(),
2049                    offer_type,
2050                );
2051                if let Some(subdir) = o.subdir.as_ref() {
2052                    check_relative_path(
2053                        Some(subdir),
2054                        DeclType::OfferDirectory,
2055                        "subdir",
2056                        &mut self.errors,
2057                    );
2058                }
2059            }
2060            fdecl::Offer::Storage(o) => {
2061                let decl = DeclType::OfferStorage;
2062                self.validate_storage_offer_fields(
2063                    decl,
2064                    Self::storage_checker,
2065                    o.source.as_ref(),
2066                    o.source_name.as_ref(),
2067                    o.target.as_ref(),
2068                    o.target_name.as_ref(),
2069                    o.availability.as_ref(),
2070                    offer_type,
2071                );
2072            }
2073            fdecl::Offer::Runner(o) => {
2074                let decl = DeclType::OfferRunner;
2075                self.validate_offer_fields(
2076                    decl,
2077                    AllowableIds::One,
2078                    CollectionSource::Deny,
2079                    Self::runner_checker,
2080                    o.source.as_ref(),
2081                    o.source_name.as_ref(),
2082                    get_source_dictionary!(o),
2083                    o.target.as_ref(),
2084                    o.target_name.as_ref(),
2085                    Some(&fdecl::DependencyType::Strong),
2086                    Some(&fdecl::Availability::Required),
2087                    offer_type,
2088                );
2089            }
2090            fdecl::Offer::Resolver(o) => {
2091                let decl = DeclType::OfferResolver;
2092                self.validate_offer_fields(
2093                    decl,
2094                    AllowableIds::One,
2095                    CollectionSource::Deny,
2096                    Self::resolver_checker,
2097                    o.source.as_ref(),
2098                    o.source_name.as_ref(),
2099                    get_source_dictionary!(o),
2100                    o.target.as_ref(),
2101                    o.target_name.as_ref(),
2102                    Some(&fdecl::DependencyType::Strong),
2103                    Some(&fdecl::Availability::Required),
2104                    offer_type,
2105                );
2106            }
2107            fdecl::Offer::EventStream(e) => {
2108                self.validate_event_stream_offer_fields(e, offer_type);
2109            }
2110            #[cfg(fuchsia_api_level_at_least = "25")]
2111            fdecl::Offer::Dictionary(o) => {
2112                let decl = DeclType::OfferDictionary;
2113                self.validate_offer_fields(
2114                    decl,
2115                    AllowableIds::One,
2116                    CollectionSource::Deny,
2117                    Self::dictionary_checker,
2118                    o.source.as_ref(),
2119                    o.source_name.as_ref(),
2120                    get_source_dictionary!(o),
2121                    o.target.as_ref(),
2122                    o.target_name.as_ref(),
2123                    o.dependency_type.as_ref(),
2124                    o.availability.as_ref(),
2125                    offer_type,
2126                );
2127            }
2128            #[cfg(fuchsia_api_level_at_least = "HEAD")]
2129            fdecl::Offer::Config(o) => {
2130                let decl = DeclType::OfferConfig;
2131                self.validate_offer_fields(
2132                    decl,
2133                    AllowableIds::One,
2134                    CollectionSource::Deny,
2135                    Self::config_checker,
2136                    o.source.as_ref(),
2137                    o.source_name.as_ref(),
2138                    None,
2139                    o.target.as_ref(),
2140                    o.target_name.as_ref(),
2141                    Some(&fdecl::DependencyType::Strong),
2142                    o.availability.as_ref(),
2143                    offer_type,
2144                );
2145            }
2146            fdecl::OfferUnknown!() => {
2147                self.errors.push(Error::invalid_field(DeclType::Component, "offer"));
2148            }
2149        }
2150    }
2151
2152    fn validate_offer_fields(
2153        &mut self,
2154        decl: DeclType,
2155        allowable_names: AllowableIds,
2156        collection_source: CollectionSource,
2157        capability_checker: impl Fn(&Self) -> &dyn Container,
2158        source: Option<&'a fdecl::Ref>,
2159        source_name: Option<&'a String>,
2160        source_dictionary: Option<&'a String>,
2161        target: Option<&'a fdecl::Ref>,
2162        target_name: Option<&'a String>,
2163        dependency_type: Option<&'a fdecl::DependencyType>,
2164        availability: Option<&'a fdecl::Availability>,
2165        offer_type: OfferType,
2166    ) {
2167        self.validate_offer_source(decl, collection_source, source, source_dictionary, offer_type);
2168        check_route_availability(decl, availability, source, source_name, &mut self.errors);
2169        check_offer_name(source_name, decl, "source_name", offer_type, &mut self.errors);
2170        if source_dictionary.is_some() {
2171            check_relative_path(source_dictionary, decl, "source_dictionary", &mut self.errors);
2172        }
2173        self.validate_offer_target(decl, allowable_names, target, target_name, offer_type);
2174        check_offer_name(target_name, decl, "target_name", offer_type, &mut self.errors);
2175
2176        if dependency_type.is_none() {
2177            self.errors.push(Error::missing_field(decl, "dependency_type"));
2178        }
2179
2180        self.validate_route_from_self(
2181            decl,
2182            source,
2183            source_name,
2184            source_dictionary,
2185            capability_checker,
2186        );
2187    }
2188
2189    fn validate_offer_source(
2190        &mut self,
2191        decl: DeclType,
2192        collection_source: CollectionSource,
2193        source: Option<&'a fdecl::Ref>,
2194        source_dictionary: Option<&'a String>,
2195        offer_type: OfferType,
2196    ) {
2197        match (source, source_dictionary) {
2198            // These sources support source_dictionary.
2199            (Some(fdecl::Ref::Parent(_)), _) => {}
2200            (Some(fdecl::Ref::Self_(_)), _) => {}
2201            (Some(fdecl::Ref::Child(child)), _) => {
2202                self.validate_child_ref(decl, "source", &child, offer_type);
2203            }
2204            // These sources don't.
2205            (Some(fdecl::Ref::VoidType(_)), None) => {}
2206            (Some(fdecl::Ref::Framework(_)), None) => {}
2207            (Some(fdecl::Ref::Capability(c)), None) => {
2208                self.validate_source_capability(c, decl, "source");
2209            }
2210            (Some(fdecl::Ref::Collection(c)), None)
2211                if collection_source == CollectionSource::Allow =>
2212            {
2213                self.validate_source_collection(c, decl);
2214            }
2215            // `source` is required.
2216            (None, _) => self.errors.push(Error::missing_field(decl, "source")),
2217            // Any combination that was not recognized above must be invalid.
2218            (_, _) => self.errors.push(Error::invalid_field(decl, "source")),
2219        }
2220    }
2221
2222    fn validate_storage_offer_fields(
2223        &mut self,
2224        decl: DeclType,
2225        // This takes a callback that returns a [Container], instead of the &[Container] directly,
2226        // to avoid a borrow checker error that would occur from a simultaneous borrow on
2227        // &mut self.
2228        capability_checker: impl Fn(&Self) -> &dyn Container,
2229        source: Option<&'a fdecl::Ref>,
2230        source_name: Option<&'a String>,
2231        target: Option<&'a fdecl::Ref>,
2232        target_name: Option<&'a String>,
2233        availability: Option<&fdecl::Availability>,
2234        offer_type: OfferType,
2235    ) {
2236        match source {
2237            Some(fdecl::Ref::Parent(_) | fdecl::Ref::VoidType(_) | fdecl::Ref::Self_(_)) => {}
2238            Some(_) => {
2239                self.errors.push(Error::invalid_field(decl, "source"));
2240            }
2241            None => {
2242                self.errors.push(Error::missing_field(decl, "source"));
2243            }
2244        }
2245        check_route_availability(decl, availability, source, source_name, &mut self.errors);
2246        check_offer_name(source_name, decl, "source_name", offer_type, &mut self.errors);
2247        self.validate_offer_target(decl, AllowableIds::One, target, target_name, offer_type);
2248        check_offer_name(target_name, decl, "target_name", offer_type, &mut self.errors);
2249
2250        if let (Some(fdecl::Ref::Self_(_)), Some(name)) = (source, source_name) {
2251            if !(capability_checker)(self).contains(name) {
2252                self.errors.push(Error::invalid_capability(decl, "source", name));
2253            }
2254        }
2255    }
2256
2257    fn validate_event_stream_offer_fields(
2258        &mut self,
2259        event_stream: &'a fdecl::OfferEventStream,
2260        offer_type: OfferType,
2261    ) {
2262        let decl = DeclType::OfferEventStream;
2263        check_name(event_stream.source_name.as_ref(), decl, "source_name", &mut self.errors);
2264        if event_stream.target == Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})) {
2265            // Expose to framework from framework is never valid.
2266            self.errors.push(Error::invalid_field(DeclType::OfferEventStream, "target"));
2267        }
2268        if let Some(scope) = &event_stream.scope {
2269            if scope.is_empty() {
2270                self.errors.push(Error::invalid_field(decl, "scope"));
2271            }
2272            for value in scope {
2273                match value {
2274                    fdecl::Ref::Child(child) => {
2275                        self.validate_child_ref(
2276                            DeclType::OfferEventStream,
2277                            "scope",
2278                            &child,
2279                            offer_type,
2280                        );
2281                    }
2282                    fdecl::Ref::Collection(collection) => {
2283                        self.validate_collection_ref(
2284                            DeclType::OfferEventStream,
2285                            "scope",
2286                            &collection,
2287                        );
2288                    }
2289                    _ => {
2290                        self.errors.push(Error::invalid_field(DeclType::OfferEventStream, "scope"));
2291                    }
2292                }
2293            }
2294        }
2295        // Only parent, framework, child, and void are valid.
2296        match event_stream.source {
2297            Some(
2298                fdecl::Ref::Parent(_)
2299                | fdecl::Ref::Framework(_)
2300                | fdecl::Ref::Child(_)
2301                | fdecl::Ref::VoidType(_),
2302            ) => {}
2303            Some(_) => {
2304                self.errors.push(Error::invalid_field(decl, "source"));
2305            }
2306            None => {
2307                self.errors.push(Error::missing_field(decl, "source"));
2308            }
2309        };
2310
2311        check_route_availability(
2312            decl,
2313            event_stream.availability.as_ref(),
2314            event_stream.source.as_ref(),
2315            event_stream.source_name.as_ref(),
2316            &mut self.errors,
2317        );
2318
2319        self.validate_offer_target(
2320            decl,
2321            AllowableIds::One,
2322            event_stream.target.as_ref(),
2323            event_stream.target_name.as_ref(),
2324            offer_type,
2325        );
2326        check_name(event_stream.target_name.as_ref(), decl, "target_name", &mut self.errors);
2327    }
2328
2329    /// Check a `ChildRef` contains a valid child that exists.
2330    fn validate_child_ref(
2331        &mut self,
2332        decl: DeclType,
2333        field_name: &str,
2334        child: &fdecl::ChildRef,
2335        offer_type: OfferType,
2336    ) -> bool {
2337        if offer_type == OfferType::Dynamic && child.collection.is_some() {
2338            return self.validate_dynamic_child_ref(decl, field_name, child);
2339        }
2340        // Ensure the name is valid, and the reference refers to a static child.
2341        //
2342        // We attempt to list all errors if possible.
2343        let mut valid = true;
2344        if !check_name(
2345            Some(&child.name),
2346            decl,
2347            &format!("{}.child.name", field_name),
2348            &mut self.errors,
2349        ) {
2350            valid = false;
2351        }
2352        if child.collection.is_some() {
2353            self.errors
2354                .push(Error::extraneous_field(decl, format!("{}.child.collection", field_name)));
2355            valid = false;
2356        }
2357        if !valid {
2358            return false;
2359        }
2360
2361        // Ensure the child exists.
2362        let name: &str = &child.name;
2363        if !self.all_children.contains_key(name) {
2364            self.errors.push(Error::invalid_child(decl, field_name, name));
2365            return false;
2366        }
2367
2368        true
2369    }
2370
2371    /// Check a `ChildRef` contains a valid dynamic child.
2372    ///
2373    /// The manifest we're validating doesn't contain dynamic children so we can't check if the dynamic
2374    /// child actually exists, but we can confirm things like the name is valid.
2375    fn validate_dynamic_child_ref(
2376        &mut self,
2377        decl: DeclType,
2378        field_name: &str,
2379        child: &fdecl::ChildRef,
2380    ) -> bool {
2381        // Ensure the name is valid.
2382        //
2383        // We attempt to list all errors if possible.
2384        let mut valid = true;
2385        if !check_dynamic_name(
2386            Some(&child.name),
2387            decl,
2388            &format!("{}.child.name", field_name),
2389            &mut self.errors,
2390        ) {
2391            valid = false;
2392        }
2393        if !check_name(
2394            child.collection.as_ref(),
2395            decl,
2396            &format!("{}.child.collection", field_name),
2397            &mut self.errors,
2398        ) {
2399            valid = false;
2400        }
2401        valid
2402    }
2403
2404    /// Check a `CollectionRef` is valid and refers to an existing collection.
2405    fn validate_collection_ref(
2406        &mut self,
2407        decl: DeclType,
2408        field_name: &str,
2409        collection: &fdecl::CollectionRef,
2410    ) -> bool {
2411        // Ensure the name is valid.
2412        if !check_name(
2413            Some(&collection.name),
2414            decl,
2415            &format!("{}.collection.name", field_name),
2416            &mut self.errors,
2417        ) {
2418            return false;
2419        }
2420
2421        // Ensure the collection exists.
2422        if !self.all_collections.contains(&collection.name as &str) {
2423            self.errors.push(Error::invalid_collection(decl, field_name, &collection.name as &str));
2424            return false;
2425        }
2426
2427        true
2428    }
2429
2430    fn validate_offer_target(
2431        &mut self,
2432        decl: DeclType,
2433        allowable_names: AllowableIds,
2434        target: Option<&'a fdecl::Ref>,
2435        target_name: Option<&'a String>,
2436        offer_type: OfferType,
2437    ) {
2438        match target {
2439            Some(fdecl::Ref::Child(c)) => {
2440                self.validate_target_child(decl, allowable_names, c, target_name, offer_type);
2441            }
2442            Some(fdecl::Ref::Collection(c)) => {
2443                self.validate_target_collection(decl, allowable_names, c, target_name);
2444            }
2445            Some(fdecl::Ref::Capability(c)) => {
2446                // Only offers to dictionary capabilities are valid.
2447                #[cfg(fuchsia_api_level_at_least = "25")]
2448                if let Some(d) = self.all_dictionaries.get(&c.name.as_str()) {
2449                    if d.source_path.is_some() {
2450                        // If `source_path` is present that means this is an offer into a
2451                        // dynamic dictionary, which is not allowed.
2452                        self.errors.push(Error::invalid_field(decl, "target"));
2453                    }
2454                } else {
2455                    self.errors.push(Error::invalid_field(decl, "target"));
2456                }
2457                #[cfg(not(fuchsia_api_level_at_least = "25"))]
2458                {
2459                    let _ = c;
2460                    self.errors.push(Error::invalid_field(decl, "target"));
2461                }
2462            }
2463            Some(_) => {
2464                self.errors.push(Error::invalid_field(decl, "target"));
2465            }
2466            None => {
2467                self.errors.push(Error::missing_field(decl, "target"));
2468            }
2469        }
2470    }
2471
2472    fn validate_target_child(
2473        &mut self,
2474        decl: DeclType,
2475        allowable_names: AllowableIds,
2476        child: &'a fdecl::ChildRef,
2477        target_name: Option<&'a String>,
2478        offer_type: OfferType,
2479    ) {
2480        if !self.validate_child_ref(decl, "target", child, offer_type) {
2481            return;
2482        }
2483        if let Some(target_name) = target_name {
2484            let names_for_target = self
2485                .target_ids
2486                .entry(TargetId::Component(
2487                    &child.name,
2488                    child.collection.as_ref().map(|s| s.as_str()),
2489                ))
2490                .or_insert(HashMap::new());
2491            if let Some(prev_state) = names_for_target.insert(target_name, allowable_names) {
2492                if prev_state == AllowableIds::One || prev_state != allowable_names {
2493                    self.errors.push(Error::duplicate_field(
2494                        decl,
2495                        "target_name",
2496                        target_name as &str,
2497                    ));
2498                }
2499            }
2500            if let Some(collection) = child.collection.as_ref() {
2501                if let Some(names_for_target) =
2502                    self.target_ids.get(&TargetId::Collection(&collection))
2503                {
2504                    if names_for_target.contains_key(&target_name.as_str()) {
2505                        // This dynamic offer conflicts with a static offer to the same collection.
2506                        self.errors.push(Error::duplicate_field(
2507                            decl,
2508                            "target_name",
2509                            target_name as &str,
2510                        ));
2511                    }
2512                }
2513            }
2514        }
2515    }
2516
2517    fn validate_target_collection(
2518        &mut self,
2519        decl: DeclType,
2520        allowable_names: AllowableIds,
2521        collection: &'a fdecl::CollectionRef,
2522        target_name: Option<&'a String>,
2523    ) {
2524        if !self.validate_collection_ref(decl, "target", &collection) {
2525            return;
2526        }
2527        if let Some(target_name) = target_name {
2528            let names_for_target = self
2529                .target_ids
2530                .entry(TargetId::Collection(&collection.name))
2531                .or_insert(HashMap::new());
2532            if let Some(prev_state) = names_for_target.insert(target_name, allowable_names) {
2533                if prev_state == AllowableIds::One || prev_state != allowable_names {
2534                    self.errors.push(Error::duplicate_field(
2535                        decl,
2536                        "target_name",
2537                        target_name as &str,
2538                    ));
2539                }
2540            }
2541        }
2542    }
2543
2544    fn validate_route_from_self(
2545        &mut self,
2546        decl: DeclType,
2547        source: Option<&fdecl::Ref>,
2548        source_name: Option<&String>,
2549        source_dictionary: Option<&String>,
2550        capability_checker: impl Fn(&Self) -> &dyn Container,
2551    ) {
2552        let (Some(fdecl::Ref::Self_(_)), Some(name)) = (source, source_name) else {
2553            return;
2554        };
2555        match source_dictionary {
2556            Some(source_dictionary) => {
2557                #[cfg(fuchsia_api_level_at_least = "25")]
2558                {
2559                    use cm_types::IterablePath;
2560                    if let Ok(path) = cm_types::RelativePath::new(source_dictionary) {
2561                        if let Some(first_segment) = path.iter_segments().next().map(|s| s.as_str())
2562                        {
2563                            if !self.all_dictionaries.contains_key(first_segment) {
2564                                self.errors.push(Error::invalid_capability(
2565                                    decl,
2566                                    "source",
2567                                    first_segment,
2568                                ));
2569                            }
2570                        }
2571                    }
2572                }
2573                #[cfg(not(fuchsia_api_level_at_least = "25"))]
2574                let _ = source_dictionary;
2575            }
2576            None => {
2577                if !(capability_checker)(self).contains(name) {
2578                    self.errors.push(Error::invalid_capability(decl, "source", name));
2579                }
2580            }
2581        }
2582    }
2583
2584    // The following functions can be used to convert a type-specific collection of capabilities
2585    // into [Container].
2586    fn service_checker(&self) -> &dyn Container {
2587        &self.all_services
2588    }
2589    fn protocol_checker(&self) -> &dyn Container {
2590        &self.all_protocols
2591    }
2592    fn directory_checker(&self) -> &dyn Container {
2593        &self.all_directories
2594    }
2595    fn runner_checker(&self) -> &dyn Container {
2596        &self.all_runners
2597    }
2598    fn resolver_checker(&self) -> &dyn Container {
2599        &self.all_resolvers
2600    }
2601
2602    #[cfg(fuchsia_api_level_at_least = "25")]
2603    fn dictionary_checker(&self) -> &dyn Container {
2604        &self.all_dictionaries
2605    }
2606
2607    #[cfg(fuchsia_api_level_at_least = "HEAD")]
2608    fn config_checker(&self) -> &dyn Container {
2609        &self.all_configs
2610    }
2611    fn storage_checker(&self) -> &dyn Container {
2612        &self.all_storages
2613    }
2614    fn event_stream_checker(&self) -> &dyn Container {
2615        // Components can't define their own event streams. If someone tries to route an event
2616        // stream from Self it should generate some other error. So just return `true` to bypass
2617        // the logic.
2618        struct AlwaysTrueContainer {}
2619        impl Container for AlwaysTrueContainer {
2620            fn contains(&self, _key: &str) -> bool {
2621                true
2622            }
2623        }
2624        static CONTAINER: AlwaysTrueContainer = AlwaysTrueContainer {};
2625        &CONTAINER
2626    }
2627}
2628
2629#[cfg(test)]
2630mod tests {
2631    use super::*;
2632    use cm_types::MAX_LONG_NAME_LENGTH;
2633    use test_case::test_case;
2634    use {
2635        fidl_fuchsia_component_decl as fdecl, fidl_fuchsia_data as fdata, fidl_fuchsia_io as fio,
2636    };
2637
2638    macro_rules! test_validate {
2639        (
2640            $(
2641                $test_name:ident => {
2642                    input = $input:expr,
2643                    result = $result:expr,
2644                },
2645            )+
2646        ) => {
2647            $(
2648                #[test]
2649                fn $test_name() {
2650                    validate_test($input, $result);
2651                }
2652            )+
2653        }
2654    }
2655
2656    macro_rules! test_validate_any_result {
2657        (
2658            $(
2659                $test_name:ident => {
2660                    input = $input:expr,
2661                    results = $results:expr,
2662                },
2663            )+
2664        ) => {
2665            $(
2666                #[test]
2667                fn $test_name() {
2668                    validate_test_any_result($input, $results);
2669                }
2670            )+
2671        }
2672    }
2673
2674    macro_rules! test_validate_values_data {
2675        (
2676            $(
2677                $test_name:ident => {
2678                    input = $input:expr,
2679                    result = $result:expr,
2680                },
2681            )+
2682        ) => {
2683            $(
2684                #[test]
2685                fn $test_name() {
2686                    validate_values_data_test($input, $result);
2687                }
2688            )+
2689        }
2690    }
2691
2692    macro_rules! test_validate_capabilities {
2693        (
2694            $(
2695                $test_name:ident => {
2696                    input = $input:expr,
2697                    as_builtin = $as_builtin:expr,
2698                    result = $result:expr,
2699                },
2700            )+
2701        ) => {
2702            $(
2703                #[test]
2704                fn $test_name() {
2705                    validate_capabilities_test($input, $as_builtin, $result);
2706                }
2707            )+
2708        }
2709    }
2710
2711    macro_rules! test_dependency {
2712        (
2713            $(
2714                ($test_name:ident) => {
2715                    ty = $ty:expr,
2716                    offer_decl = $offer_decl:expr,
2717                },
2718            )+
2719        ) => {
2720            $(
2721                #[test]
2722                fn $test_name() {
2723                    let mut decl = new_component_decl();
2724                    let dependencies = vec![
2725                        ("a", "b"),
2726                        ("b", "a"),
2727                    ];
2728                    let offers = dependencies.into_iter().map(|(from,to)| {
2729                        let mut offer_decl = $offer_decl;
2730                        offer_decl.source = Some(fdecl::Ref::Child(
2731                           fdecl::ChildRef { name: from.to_string(), collection: None },
2732                        ));
2733                        offer_decl.target = Some(fdecl::Ref::Child(
2734                           fdecl::ChildRef { name: to.to_string(), collection: None },
2735                        ));
2736                        $ty(offer_decl)
2737                    }).collect();
2738                    let children = ["a", "b"].iter().map(|name| {
2739                        fdecl::Child {
2740                            name: Some(name.to_string()),
2741                            url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
2742                            startup: Some(fdecl::StartupMode::Lazy),
2743                            on_terminate: None,
2744                            environment: None,
2745                            ..Default::default()
2746                        }
2747                    }).collect();
2748                    decl.offers = Some(offers);
2749                    decl.children = Some(children);
2750                    let result = Err(ErrorList::new(vec![
2751                        Error::dependency_cycle(
2752                            directed_graph::Error::CyclesDetected([vec!["child a", "child b", "child a"]].iter().cloned().collect()).format_cycle()),
2753                    ]));
2754                    validate_test(decl, result);
2755                }
2756            )+
2757        }
2758    }
2759
2760    macro_rules! test_weak_dependency {
2761        (
2762            $(
2763                ($test_name:ident) => {
2764                    ty = $ty:expr,
2765                    offer_decl = $offer_decl:expr,
2766                },
2767            )+
2768        ) => {
2769            $(
2770                #[test_case(fdecl::DependencyType::Weak)]
2771                fn $test_name(weak_dep: fdecl::DependencyType) {
2772                    let mut decl = new_component_decl();
2773                    let offers = vec![
2774                        {
2775                            let mut offer_decl = $offer_decl;
2776                            offer_decl.source = Some(fdecl::Ref::Child(
2777                               fdecl::ChildRef { name: "a".to_string(), collection: None },
2778                            ));
2779                            offer_decl.target = Some(fdecl::Ref::Child(
2780                               fdecl::ChildRef { name: "b".to_string(), collection: None },
2781                            ));
2782                            offer_decl.dependency_type = Some(fdecl::DependencyType::Strong);
2783                            $ty(offer_decl)
2784                        },
2785                        {
2786                            let mut offer_decl = $offer_decl;
2787                            offer_decl.source = Some(fdecl::Ref::Child(
2788                               fdecl::ChildRef { name: "b".to_string(), collection: None },
2789                            ));
2790                            offer_decl.target = Some(fdecl::Ref::Child(
2791                               fdecl::ChildRef { name: "a".to_string(), collection: None },
2792                            ));
2793                            offer_decl.dependency_type = Some(weak_dep);
2794                            $ty(offer_decl)
2795                        },
2796                    ];
2797                    let children = ["a", "b"].iter().map(|name| {
2798                        fdecl::Child {
2799                            name: Some(name.to_string()),
2800                            url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
2801                            startup: Some(fdecl::StartupMode::Lazy),
2802                            on_terminate: None,
2803                            environment: None,
2804                            ..Default::default()
2805                        }
2806                    }).collect();
2807                    decl.offers = Some(offers);
2808                    decl.children = Some(children);
2809                    let result = Ok(());
2810                    validate_test(decl, result);
2811                }
2812            )+
2813        }
2814    }
2815
2816    #[track_caller]
2817    fn validate_test(input: fdecl::Component, expected_res: Result<(), ErrorList>) {
2818        let res = validate(&input);
2819        assert_eq!(res, expected_res);
2820    }
2821
2822    #[track_caller]
2823    fn validate_test_any_result(input: fdecl::Component, expected_res: Vec<Result<(), ErrorList>>) {
2824        let res = format!("{:?}", validate(&input));
2825        let expected_res_debug = format!("{:?}", expected_res);
2826
2827        let matched_exp =
2828            expected_res.into_iter().find(|expected| res == format!("{:?}", expected));
2829
2830        assert!(
2831            matched_exp.is_some(),
2832            "assertion failed: Expected one of:\n{:?}\nActual:\n{:?}",
2833            expected_res_debug,
2834            res
2835        );
2836    }
2837
2838    #[track_caller]
2839    fn validate_values_data_test(
2840        input: fdecl::ConfigValuesData,
2841        expected_res: Result<(), ErrorList>,
2842    ) {
2843        let res = validate_values_data(&input);
2844        assert_eq!(res, expected_res);
2845    }
2846
2847    #[track_caller]
2848    fn validate_capabilities_test(
2849        input: Vec<fdecl::Capability>,
2850        as_builtin: bool,
2851        expected_res: Result<(), ErrorList>,
2852    ) {
2853        let res = validate_capabilities(&input, as_builtin);
2854        assert_eq!(res, expected_res);
2855    }
2856
2857    fn new_component_decl() -> fdecl::Component {
2858        fdecl::Component {
2859            program: None,
2860            uses: None,
2861            exposes: None,
2862            offers: None,
2863            facets: None,
2864            capabilities: None,
2865            children: None,
2866            collections: None,
2867            environments: None,
2868            ..Default::default()
2869        }
2870    }
2871
2872    test_validate_any_result! {
2873        test_validate_use_disallows_nested_dirs => {
2874            input = {
2875                let mut decl = new_component_decl();
2876                decl.uses = Some(vec![
2877                    fdecl::Use::Directory(fdecl::UseDirectory {
2878                        dependency_type: Some(fdecl::DependencyType::Strong),
2879                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2880                        source_name: Some("abc".to_string()),
2881                        target_path: Some("/foo/bar".to_string()),
2882                        rights: Some(fio::Operations::CONNECT),
2883                        subdir: None,
2884                        ..Default::default()
2885                    }),
2886                    fdecl::Use::Directory(fdecl::UseDirectory {
2887                        dependency_type: Some(fdecl::DependencyType::Strong),
2888                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2889                        source_name: Some("abc".to_string()),
2890                        target_path: Some("/foo/bar/baz".to_string()),
2891                        rights: Some(fio::Operations::CONNECT),
2892                        subdir: None,
2893                        ..Default::default()
2894                    }),
2895                ]);
2896                decl
2897            },
2898            results = vec![
2899                Err(ErrorList::new(vec![
2900                    Error::invalid_path_overlap(
2901                        DeclType::UseDirectory, "/foo/bar/baz", DeclType::UseDirectory, "/foo/bar"),
2902                ])),
2903                Err(ErrorList::new(vec![
2904                    Error::invalid_path_overlap(
2905                        DeclType::UseDirectory, "/foo/bar", DeclType::UseDirectory, "/foo/bar/baz"),
2906                ])),
2907            ],
2908        },
2909        test_validate_use_disallows_nested_dirs_storage => {
2910            input = {
2911                let mut decl = new_component_decl();
2912                decl.uses = Some(vec![
2913                    fdecl::Use::Storage(fdecl::UseStorage {
2914                        source_name: Some("abc".to_string()),
2915                        target_path: Some("/foo/bar".to_string()),
2916                        ..Default::default()
2917                    }),
2918                    fdecl::Use::Storage(fdecl::UseStorage {
2919                        source_name: Some("abc".to_string()),
2920                        target_path: Some("/foo/bar/baz".to_string()),
2921                        ..Default::default()
2922                    }),
2923                ]);
2924                decl
2925            },
2926            results = vec![
2927                Err(ErrorList::new(vec![
2928                    Error::invalid_path_overlap(
2929                        DeclType::UseStorage, "/foo/bar/baz", DeclType::UseStorage, "/foo/bar"),
2930                ])),
2931                Err(ErrorList::new(vec![
2932                    Error::invalid_path_overlap(
2933                        DeclType::UseStorage, "/foo/bar", DeclType::UseStorage, "/foo/bar/baz"),
2934                ])),
2935            ],
2936        },
2937        test_validate_use_disallows_nested_dirs_directory_and_storage => {
2938            input = {
2939                let mut decl = new_component_decl();
2940                decl.uses = Some(vec![
2941                    fdecl::Use::Directory(fdecl::UseDirectory {
2942                        dependency_type: Some(fdecl::DependencyType::Strong),
2943                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2944                        source_name: Some("abc".to_string()),
2945                        target_path: Some("/foo/bar".to_string()),
2946                        rights: Some(fio::Operations::CONNECT),
2947                        subdir: None,
2948                        ..Default::default()
2949                    }),
2950                    fdecl::Use::Storage(fdecl::UseStorage {
2951                        source_name: Some("abc".to_string()),
2952                        target_path: Some("/foo/bar/baz".to_string()),
2953                        ..Default::default()
2954                    }),
2955                ]);
2956                decl
2957            },
2958            results = vec![
2959                Err(ErrorList::new(vec![
2960                    Error::invalid_path_overlap(
2961                        DeclType::UseStorage, "/foo/bar/baz", DeclType::UseDirectory, "/foo/bar"),
2962                ])),
2963                Err(ErrorList::new(vec![
2964                    Error::invalid_path_overlap(
2965                        DeclType::UseDirectory, "/foo/bar", DeclType::UseStorage, "/foo/bar/baz"),
2966                ])),
2967            ],
2968        },
2969        test_validate_use_disallows_common_prefixes_protocol => {
2970            input = {
2971                let mut decl = new_component_decl();
2972                decl.uses = Some(vec![
2973                    fdecl::Use::Directory(fdecl::UseDirectory {
2974                        dependency_type: Some(fdecl::DependencyType::Strong),
2975                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2976                        source_name: Some("abc".to_string()),
2977                        target_path: Some("/foo/bar".to_string()),
2978                        rights: Some(fio::Operations::CONNECT),
2979                        subdir: None,
2980                        ..Default::default()
2981                    }),
2982                    fdecl::Use::Protocol(fdecl::UseProtocol {
2983                        dependency_type: Some(fdecl::DependencyType::Strong),
2984                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2985                        source_name: Some("crow".to_string()),
2986                        target_path: Some("/foo/bar/fuchsia.2".to_string()),
2987                        ..Default::default()
2988                    }),
2989                ]);
2990                decl
2991            },
2992            results = vec![
2993                Err(ErrorList::new(vec![
2994                    Error::invalid_path_overlap(
2995                        DeclType::UseProtocol, "/foo/bar/fuchsia.2", DeclType::UseDirectory, "/foo/bar"),
2996                ])),
2997                Err(ErrorList::new(vec![
2998                    Error::invalid_path_overlap(
2999                        DeclType::UseDirectory, "/foo/bar", DeclType::UseProtocol, "/foo/bar/fuchsia.2"),
3000                ])),
3001            ],
3002        },
3003        test_validate_use_disallows_common_prefixes_service => {
3004            input = {
3005                let mut decl = new_component_decl();
3006                decl.uses = Some(vec![
3007                    fdecl::Use::Directory(fdecl::UseDirectory {
3008                        dependency_type: Some(fdecl::DependencyType::Strong),
3009                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3010                        source_name: Some("abc".to_string()),
3011                        target_path: Some("/foo/bar".to_string()),
3012                        rights: Some(fio::Operations::CONNECT),
3013                        subdir: None,
3014                        ..Default::default()
3015                    }),
3016                    fdecl::Use::Service(fdecl::UseService {
3017                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3018                        source_name: Some("space".to_string()),
3019                        target_path: Some("/foo/bar/baz/fuchsia.logger.Log".to_string()),
3020                        dependency_type: Some(fdecl::DependencyType::Strong),
3021                        ..Default::default()
3022                    }),
3023                ]);
3024                decl
3025            },
3026            results = vec![
3027                Err(ErrorList::new(vec![
3028                    Error::invalid_path_overlap(
3029                        DeclType::UseService, "/foo/bar/baz/fuchsia.logger.Log", DeclType::UseDirectory, "/foo/bar"),
3030                ])),
3031                Err(ErrorList::new(vec![
3032                    Error::invalid_path_overlap(
3033                        DeclType::UseDirectory, "/foo/bar", DeclType::UseService, "/foo/bar/baz/fuchsia.logger.Log"),
3034                ])),
3035            ],
3036        },
3037        test_validate_use_disallows_pkg => {
3038            input = {
3039                let mut decl = new_component_decl();
3040                decl.uses = Some(vec![
3041                    fdecl::Use::Directory(fdecl::UseDirectory {
3042                        dependency_type: Some(fdecl::DependencyType::Strong),
3043                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3044                        source_name: Some("abc".to_string()),
3045                        target_path: Some("/pkg".to_string()),
3046                        rights: Some(fio::Operations::CONNECT),
3047                        subdir: None,
3048                        ..Default::default()
3049                    }),
3050                ]);
3051                decl
3052            },
3053            results = vec![
3054                Err(ErrorList::new(vec![
3055                    Error::pkg_path_overlap(DeclType::UseDirectory, "/pkg"),
3056                ])),
3057            ],
3058        },
3059        test_validate_use_disallows_pkg_overlap => {
3060            input = {
3061                let mut decl = new_component_decl();
3062                decl.uses = Some(vec![
3063                    fdecl::Use::Directory(fdecl::UseDirectory {
3064                        dependency_type: Some(fdecl::DependencyType::Strong),
3065                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3066                        source_name: Some("abc".to_string()),
3067                        target_path: Some("/pkg/foo".to_string()),
3068                        rights: Some(fio::Operations::CONNECT),
3069                        subdir: None,
3070                        ..Default::default()
3071                    }),
3072                ]);
3073                decl
3074            },
3075            results = vec![
3076                Err(ErrorList::new(vec![
3077                    Error::pkg_path_overlap(DeclType::UseDirectory, "/pkg/foo"),
3078                ])),
3079            ],
3080        },
3081        test_validate_use_optional_config_correct => {
3082            input = {
3083                let mut decl = new_component_decl();
3084                decl.uses = Some(vec![
3085                    fdecl::Use::Config(fdecl::UseConfiguration {
3086                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3087                        source_name: Some("abc".to_string()),
3088                        target_name: Some("foo".to_string()),
3089                        availability: Some(fdecl::Availability::Optional),
3090                        type_: Some(fdecl::ConfigType {
3091                            layout: fdecl::ConfigTypeLayout::Bool,
3092                            parameters: Some(Vec::new()),
3093                            constraints: Vec::new(),
3094                        }),
3095                        ..Default::default()
3096                    }),
3097                ]);
3098                decl.config = Some(fdecl::ConfigSchema {
3099                    fields: Some(vec![fdecl::ConfigField {
3100                        key: Some("foo".into()),
3101                        type_: Some(fdecl::ConfigType {
3102                            layout: fdecl::ConfigTypeLayout::Bool,
3103                            parameters: Some(Vec::new()),
3104                            constraints: Vec::new(),
3105                        }),
3106                        mutability: None,
3107                        ..Default::default()}]),
3108                    checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3109                    value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3110                    ..Default::default()
3111                     });
3112                decl
3113            },
3114            results = vec![Ok(())],
3115        },
3116        test_validate_use_optional_config_no_config_schema => {
3117            input = {
3118                let mut decl = new_component_decl();
3119                decl.uses = Some(vec![
3120                    fdecl::Use::Config(fdecl::UseConfiguration {
3121                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3122                        source_name: Some("abc".to_string()),
3123                        target_name: Some("foo".to_string()),
3124                        availability: Some(fdecl::Availability::Optional),
3125                        type_: Some(fdecl::ConfigType {
3126                            layout: fdecl::ConfigTypeLayout::Bool,
3127                            parameters: None,
3128                            constraints: Vec::new(),
3129                        }),
3130                        ..Default::default()
3131                    }),
3132                ]);
3133                decl
3134            },
3135            results = vec![
3136                Err(ErrorList::new(vec![
3137                    Error::missing_field(DeclType::ConfigField, "config"),
3138                ])),
3139            ],
3140        },
3141        test_validate_use_optional_config_no_config_field => {
3142            input = {
3143                let mut decl = new_component_decl();
3144                decl.uses = Some(vec![
3145                    fdecl::Use::Config(fdecl::UseConfiguration {
3146                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3147                        source_name: Some("abc".to_string()),
3148                        target_name: Some("foo".to_string()),
3149                        availability: Some(fdecl::Availability::Optional),
3150                        type_: Some(fdecl::ConfigType {
3151                            layout: fdecl::ConfigTypeLayout::Bool,
3152                            parameters: None,
3153                            constraints: Vec::new(),
3154                        }),
3155                        ..Default::default()
3156                    }),
3157                ]);
3158                decl.config = Some(fdecl::ConfigSchema {
3159                    fields: Some(vec![]),
3160                    checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3161                    value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3162                    ..Default::default()
3163                     });
3164                decl
3165            },
3166            results = vec![
3167                Err(ErrorList::new(vec![
3168                    Error::missing_field(DeclType::ConfigField, "foo"),
3169                ])),
3170            ],
3171        },
3172        test_validate_use_optional_config_bad_type => {
3173            input = {
3174                let mut decl = new_component_decl();
3175                decl.uses = Some(vec![
3176                    fdecl::Use::Config(fdecl::UseConfiguration {
3177                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3178                        source_name: Some("abc".to_string()),
3179                        target_name: Some("foo".to_string()),
3180                        availability: Some(fdecl::Availability::Optional),
3181                        type_: Some(fdecl::ConfigType {
3182                            layout: fdecl::ConfigTypeLayout::Bool,
3183                            parameters: None,
3184                            constraints: Vec::new(),
3185                        }),
3186                        ..Default::default()
3187                    }),
3188                ]);
3189                decl.config = Some(fdecl::ConfigSchema {
3190                    fields: Some(vec![fdecl::ConfigField {
3191                        key: Some("foo".into()),
3192                        type_: Some(fdecl::ConfigType {
3193                            layout: fdecl::ConfigTypeLayout::Int16,
3194                            parameters: Some(Vec::new()),
3195                            constraints: Vec::new(),
3196                        }),
3197                        mutability: None,
3198                        ..Default::default()}]),
3199                    checksum: Some(fdecl::ConfigChecksum::Sha256([0;32])),
3200                    value_source: Some(fdecl::ConfigValueSource::PackagePath("path".into())),
3201                    ..Default::default()
3202                     });
3203                decl
3204            },
3205            results = vec![
3206                Err(ErrorList::new(vec![
3207                    Error::invalid_field(DeclType::ConfigField, "foo"),
3208                ])),
3209            ],
3210        },
3211    }
3212
3213    test_validate_values_data! {
3214        test_values_data_ok => {
3215            input = fdecl::ConfigValuesData {
3216                values: Some(vec![
3217                    fdecl::ConfigValueSpec {
3218                        value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(true))),
3219                        ..Default::default()
3220                    }
3221                ]),
3222                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3223                ..Default::default()
3224            },
3225            result = Ok(()),
3226        },
3227        test_values_data_no_checksum => {
3228            input = fdecl::ConfigValuesData {
3229                values: Some(vec![]),
3230                checksum: None,
3231                ..Default::default()
3232            },
3233            result = Err(ErrorList::new(vec![
3234                Error::missing_field(DeclType::ConfigValuesData, "checksum")
3235            ])),
3236        },
3237        test_values_data_unknown_checksum => {
3238            input = fdecl::ConfigValuesData {
3239                values: Some(vec![]),
3240                checksum: Some(fdecl::ConfigChecksum::unknown_variant_for_testing()),
3241                ..Default::default()
3242            },
3243            result = Err(ErrorList::new(vec![
3244                Error::invalid_field(DeclType::ConfigValuesData, "checksum")
3245            ])),
3246        },
3247        test_values_data_no_values => {
3248            input = fdecl::ConfigValuesData {
3249                values: None,
3250                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3251                ..Default::default()
3252            },
3253            result = Err(ErrorList::new(vec![
3254                Error::missing_field(DeclType::ConfigValuesData, "values")
3255            ])),
3256        },
3257        test_values_data_no_inner_value => {
3258            input = fdecl::ConfigValuesData {
3259                values: Some(vec![
3260                    fdecl::ConfigValueSpec::default()
3261                ]),
3262                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3263                ..Default::default()
3264            },
3265            result = Err(ErrorList::new(vec![
3266                Error::missing_field(DeclType::ConfigValueSpec, "value")
3267            ])),
3268        },
3269        test_values_data_unknown_inner_value => {
3270            input = fdecl::ConfigValuesData {
3271                values: Some(vec![
3272                    fdecl::ConfigValueSpec {
3273                        value: Some(fdecl::ConfigValue::unknown_variant_for_testing()),
3274                        ..Default::default()
3275                    }
3276                ]),
3277                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3278                ..Default::default()
3279            },
3280            result = Err(ErrorList::new(vec![
3281                Error::invalid_field(DeclType::ConfigValueSpec, "value")
3282            ])),
3283        },
3284        test_values_data_unknown_single_value => {
3285            input = fdecl::ConfigValuesData {
3286                values: Some(vec![
3287                    fdecl::ConfigValueSpec {
3288                        value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::unknown_variant_for_testing())),
3289                        ..Default::default()
3290                    }
3291                ]),
3292                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3293                ..Default::default()
3294            },
3295            result = Err(ErrorList::new(vec![
3296                Error::invalid_field(DeclType::ConfigValueSpec, "value")
3297            ])),
3298        },
3299        test_values_data_unknown_list_value => {
3300            input = fdecl::ConfigValuesData {
3301                values: Some(vec![
3302                    fdecl::ConfigValueSpec {
3303                        value: Some(fdecl::ConfigValue::Vector(fdecl::ConfigVectorValue::unknown_variant_for_testing())),
3304                        ..Default::default()
3305                    }
3306                ]),
3307                checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
3308                ..Default::default()
3309            },
3310            result = Err(ErrorList::new(vec![
3311                Error::invalid_field(DeclType::ConfigValueSpec, "value")
3312            ])),
3313        },
3314    }
3315
3316    test_validate! {
3317        // uses
3318        test_validate_uses_empty => {
3319            input = {
3320                let mut decl = new_component_decl();
3321                decl.program = Some(fdecl::Program {
3322                    runner: Some("elf".to_string()),
3323                    info: Some(fdata::Dictionary {
3324                        entries: None,
3325                        ..Default::default()
3326                    }),
3327                    ..Default::default()
3328                });
3329                decl.uses = Some(vec![
3330                    fdecl::Use::Service(fdecl::UseService {
3331                        source: None,
3332                        source_name: None,
3333                        target_path: None,
3334                        dependency_type: None,
3335                        ..Default::default()
3336                    }),
3337                    fdecl::Use::Protocol(fdecl::UseProtocol {
3338                        dependency_type: None,
3339                        source: None,
3340                        source_name: None,
3341                        target_path: None,
3342                        ..Default::default()
3343                    }),
3344                    fdecl::Use::Directory(fdecl::UseDirectory {
3345                        dependency_type: None,
3346                        source: None,
3347                        source_name: None,
3348                        target_path: None,
3349                        rights: None,
3350                        subdir: None,
3351                        ..Default::default()
3352                    }),
3353                    fdecl::Use::Storage(fdecl::UseStorage {
3354                        source_name: None,
3355                        target_path: None,
3356                        ..Default::default()
3357                    }),
3358                    fdecl::Use::EventStream(fdecl::UseEventStream {
3359                        source_name: None,
3360                        source: None,
3361                        target_path: None,
3362                        ..Default::default()
3363                    }),
3364                    fdecl::Use::Runner(fdecl::UseRunner {
3365                        source_name: None,
3366                        source: None,
3367                        ..Default::default()
3368                    }),
3369                ]);
3370                decl
3371            },
3372            result = Err(ErrorList::new(vec![
3373                Error::missing_field(DeclType::UseService, "source"),
3374                Error::missing_field(DeclType::UseService, "source_name"),
3375                Error::missing_field(DeclType::UseService, "target_path"),
3376                Error::missing_field(DeclType::UseService, "dependency_type"),
3377                Error::missing_field(DeclType::UseProtocol, "source"),
3378                Error::missing_field(DeclType::UseProtocol, "source_name"),
3379                Error::missing_field(DeclType::UseProtocol, "target_path"),
3380                Error::missing_field(DeclType::UseProtocol, "dependency_type"),
3381                Error::missing_field(DeclType::UseDirectory, "source"),
3382                Error::missing_field(DeclType::UseDirectory, "source_name"),
3383                Error::missing_field(DeclType::UseDirectory, "target_path"),
3384                Error::missing_field(DeclType::UseDirectory, "dependency_type"),
3385                Error::missing_field(DeclType::UseDirectory, "rights"),
3386                Error::missing_field(DeclType::UseStorage, "source_name"),
3387                Error::missing_field(DeclType::UseStorage, "target_path"),
3388                Error::missing_field(DeclType::UseEventStream, "source"),
3389                Error::missing_field(DeclType::UseEventStream, "source_name"),
3390                Error::missing_field(DeclType::UseEventStream, "target_path"),
3391                Error::missing_field(DeclType::UseRunner, "source"),
3392                Error::missing_field(DeclType::UseRunner, "source_name"),
3393            ])),
3394        },
3395        test_validate_missing_program_info => {
3396            input = {
3397                let mut decl = new_component_decl();
3398                decl.program = Some(fdecl::Program {
3399                    runner: Some("runner".to_string()),
3400                    info: None,
3401                    ..Default::default()
3402                });
3403                decl
3404            },
3405            result = Err(ErrorList::new(vec![
3406                Error::missing_field(DeclType::Program, "info")
3407            ])),
3408        },
3409        test_validate_uses_invalid_identifiers => {
3410            input = {
3411                let mut decl = new_component_decl();
3412                decl.uses = Some(vec![
3413                    fdecl::Use::Service(fdecl::UseService {
3414                        source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
3415                            name: "^bad".to_string(),
3416                        })),
3417                        source_name: Some("foo/".to_string()),
3418                        target_path: Some("a/foo".to_string()),
3419                        dependency_type: Some(fdecl::DependencyType::Strong),
3420                        ..Default::default()
3421                    }),
3422                    fdecl::Use::Protocol(fdecl::UseProtocol {
3423                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3424                            name: "^bad".to_string(),
3425                            collection: None,
3426                        })),
3427                        source_name: Some("foo/".to_string()),
3428                        target_path: Some("b/foo".to_string()),
3429                        dependency_type: Some(fdecl::DependencyType::Strong),
3430                        ..Default::default()
3431                    }),
3432                    fdecl::Use::Directory(fdecl::UseDirectory {
3433                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3434                            name: "^bad".to_string(),
3435                            collection: None,
3436                        })),
3437                        source_name: Some("foo/".to_string()),
3438                        target_path: Some("c".to_string()),
3439                        rights: Some(fio::Operations::CONNECT),
3440                        subdir: Some("/foo".to_string()),
3441                        dependency_type: Some(fdecl::DependencyType::Strong),
3442                        ..Default::default()
3443                    }),
3444                    fdecl::Use::Storage(fdecl::UseStorage {
3445                        source_name: Some("foo/".to_string()),
3446                        target_path: Some("d".to_string()),
3447                        ..Default::default()
3448                    }),
3449                    fdecl::Use::EventStream(fdecl::UseEventStream {
3450                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3451                            name: "^bad".to_string(),
3452                            collection: None,
3453                        })),
3454                        source_name: Some("foo/".to_string()),
3455                        target_path: Some("e".to_string()),
3456                        ..Default::default()
3457                    }),
3458                    fdecl::Use::Runner(fdecl::UseRunner {
3459                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3460                            name: "^bad".to_string(),
3461                            collection: None,
3462                        })),
3463                        source_name: Some("foo/".to_string()),
3464                        ..Default::default()
3465                    }),
3466                ]);
3467                decl
3468            },
3469            result = Err(ErrorList::new(vec![
3470                Error::invalid_field(DeclType::UseService, "source.capability.name"),
3471                Error::invalid_field(DeclType::UseService, "source_name"),
3472                Error::invalid_field(DeclType::UseService, "target_path"),
3473                Error::invalid_field(DeclType::UseProtocol, "source.child.name"),
3474                Error::invalid_field(DeclType::UseProtocol, "source_name"),
3475                Error::invalid_field(DeclType::UseProtocol, "target_path"),
3476                Error::invalid_field(DeclType::UseDirectory, "source.child.name"),
3477                Error::invalid_field(DeclType::UseDirectory, "source_name"),
3478                Error::invalid_field(DeclType::UseDirectory, "target_path"),
3479                Error::invalid_field(DeclType::UseDirectory, "subdir"),
3480                Error::invalid_field(DeclType::UseStorage, "source_name"),
3481                Error::invalid_field(DeclType::UseStorage, "target_path"),
3482                Error::invalid_field(DeclType::UseEventStream, "source.child.name"),
3483                Error::invalid_field(DeclType::UseEventStream, "source_name"),
3484                Error::invalid_field(DeclType::UseEventStream, "target_path"),
3485                Error::invalid_field(DeclType::UseRunner, "source.child.name"),
3486                Error::invalid_field(DeclType::UseRunner, "source_name"),
3487            ])),
3488        },
3489        test_validate_uses_missing_source => {
3490            input = {
3491                fdecl::Component {
3492                    uses: Some(vec![
3493                        fdecl::Use::Protocol(fdecl::UseProtocol {
3494                            dependency_type: Some(fdecl::DependencyType::Strong),
3495                            source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
3496                                name: "this-storage-doesnt-exist".to_string(),
3497                            })),
3498                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3499                            target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3500                            ..Default::default()
3501                        })
3502                    ]),
3503                    ..new_component_decl()
3504                }
3505            },
3506            result = Err(ErrorList::new(vec![
3507                Error::invalid_capability(DeclType::UseProtocol, "source", "this-storage-doesnt-exist"),
3508            ])),
3509        },
3510        test_validate_uses_invalid_child => {
3511            input = {
3512                fdecl::Component {
3513                    uses: Some(vec![
3514                        fdecl::Use::Protocol(fdecl::UseProtocol {
3515                            dependency_type: Some(fdecl::DependencyType::Strong),
3516                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3517                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3518                            target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3519                            ..Default::default()
3520                        }),
3521                        fdecl::Use::Service(fdecl::UseService {
3522                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3523                            source_name: Some("service_name".to_string()),
3524                            target_path: Some("/svc/service_name".to_string()),
3525                            dependency_type: Some(fdecl::DependencyType::Strong),
3526                            ..Default::default()
3527                        }),
3528                        fdecl::Use::Directory(fdecl::UseDirectory {
3529                            dependency_type: Some(fdecl::DependencyType::Strong),
3530                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3531                            source_name: Some("DirectoryName".to_string()),
3532                            target_path: Some("/data/DirectoryName".to_string()),
3533                            rights: Some(fio::Operations::CONNECT),
3534                            subdir: None,
3535                            ..Default::default()
3536                        }),
3537                        fdecl::Use::Runner(fdecl::UseRunner {
3538                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "no-such-child".to_string(), collection: None})),
3539                            source_name: Some("RunnerName".to_string()),
3540                            ..Default::default()
3541                        }),
3542                    ]),
3543                    ..new_component_decl()
3544                }
3545            },
3546            result = Err(ErrorList::new(vec![
3547                Error::invalid_child(DeclType::UseProtocol, "source", "no-such-child"),
3548                Error::invalid_child(DeclType::UseService, "source", "no-such-child"),
3549                Error::invalid_child(DeclType::UseDirectory, "source", "no-such-child"),
3550                Error::invalid_child(DeclType::UseRunner, "source", "no-such-child"),
3551            ])),
3552        },
3553        test_validate_uses_invalid_capability_from_self => {
3554            input = {
3555                let mut decl = new_component_decl();
3556                decl.uses = Some(vec![
3557                    fdecl::Use::Service(fdecl::UseService {
3558                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3559                        source_name: Some("fuchsia.some.library.SomeService".into()),
3560                        target_path: Some("/svc/foo".into()),
3561                        dependency_type: Some(fdecl::DependencyType::Strong),
3562                        ..Default::default()
3563                    }),
3564                    fdecl::Use::Protocol(fdecl::UseProtocol {
3565                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3566                        source_name: Some("fuchsia.some.library.SomeProtocol".into()),
3567                        target_path: Some("/svc/bar".into()),
3568                        dependency_type: Some(fdecl::DependencyType::Strong),
3569                        ..Default::default()
3570                    }),
3571                    fdecl::Use::Directory(fdecl::UseDirectory {
3572                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3573                        source_name: Some("dir".into()),
3574                        target_path: Some("/assets".into()),
3575                        dependency_type: Some(fdecl::DependencyType::Strong),
3576                        rights: Some(fio::Operations::CONNECT),
3577                        ..Default::default()
3578                    }),
3579                    fdecl::Use::Runner(fdecl::UseRunner {
3580                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3581                        source_name: Some("source_elf".into()),
3582                        ..Default::default()
3583                    }),
3584                    fdecl::Use::Config(fdecl::UseConfiguration {
3585                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3586                        source_name: Some("source_config".into()),
3587                        target_name: Some("config".into()),
3588                        type_: Some(fdecl::ConfigType {
3589                            layout: fdecl::ConfigTypeLayout::Bool,
3590                            parameters: Some(Vec::new()),
3591                            constraints: Vec::new(),
3592                        }),
3593                        ..Default::default()
3594                    }),
3595                    fdecl::Use::Protocol(fdecl::UseProtocol {
3596                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3597                        source_name: Some("fuchsia.some.library.SomeProtocol".into()),
3598                        source_dictionary: Some("dict/inner".into()),
3599                        target_path: Some("/svc/baz".into()),
3600                        dependency_type: Some(fdecl::DependencyType::Strong),
3601                        ..Default::default()
3602                    }),
3603                ]);
3604                decl
3605            },
3606            result = Err(ErrorList::new(vec![
3607                Error::invalid_capability(
3608                    DeclType::UseService,
3609                    "source",
3610                    "fuchsia.some.library.SomeService"),
3611                Error::invalid_capability(
3612                    DeclType::UseProtocol,
3613                    "source",
3614                    "fuchsia.some.library.SomeProtocol"),
3615                Error::invalid_capability(DeclType::UseDirectory, "source", "dir"),
3616                Error::invalid_capability(DeclType::UseRunner, "source", "source_elf"),
3617                Error::invalid_capability(DeclType::UseConfiguration, "source", "source_config"),
3618                Error::invalid_capability(DeclType::UseProtocol, "source", "dict"),
3619            ])),
3620        },
3621        test_validate_use_from_child_offer_to_child_strong_cycle => {
3622            input = {
3623                fdecl::Component {
3624                    capabilities: Some(vec![
3625                        fdecl::Capability::Service(fdecl::Service {
3626                            name: Some("a".to_string()),
3627                            source_path: Some("/a".to_string()),
3628                            ..Default::default()
3629                        })]),
3630                    uses: Some(vec![
3631                        fdecl::Use::Protocol(fdecl::UseProtocol {
3632                            dependency_type: Some(fdecl::DependencyType::Strong),
3633                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3634                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3635                            target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3636                            ..Default::default()
3637                        }),
3638                        fdecl::Use::Service(fdecl::UseService {
3639                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3640                            source_name: Some("service_name".to_string()),
3641                            target_path: Some("/svc/service_name".to_string()),
3642                            dependency_type: Some(fdecl::DependencyType::Strong),
3643                            ..Default::default()
3644                        }),
3645                        fdecl::Use::Directory(fdecl::UseDirectory {
3646                            dependency_type: Some(fdecl::DependencyType::Strong),
3647                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3648                            source_name: Some("DirectoryName".to_string()),
3649                            target_path: Some("/data/DirectoryName".to_string()),
3650                            rights: Some(fio::Operations::CONNECT),
3651                            subdir: None,
3652                            ..Default::default()
3653                        }),
3654                    ]),
3655                    offers: Some(vec![
3656                        fdecl::Offer::Service(fdecl::OfferService {
3657                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3658                            source_name: Some("a".to_string()),
3659                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
3660                            target_name: Some("a".to_string()),
3661                            ..Default::default()
3662                        })
3663                    ]),
3664                    children: Some(vec![
3665                        fdecl::Child {
3666                            name: Some("child".to_string()),
3667                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3668                            startup: Some(fdecl::StartupMode::Lazy),
3669                            on_terminate: None,
3670                            ..Default::default()
3671                        }
3672                    ]),
3673                    ..new_component_decl()
3674                }
3675            },
3676            result = Err(ErrorList::new(vec![
3677                Error::dependency_cycle("{{self -> child child -> self}}".to_string()),
3678            ])),
3679        },
3680        test_validate_use_from_child_storage_no_cycle => {
3681            input = {
3682                fdecl::Component {
3683                    capabilities: Some(vec![
3684                        fdecl::Capability::Storage(fdecl::Storage {
3685                            name: Some("cdata".to_string()),
3686                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None } )),
3687                            backing_dir: Some("minfs".to_string()),
3688                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
3689                            ..Default::default()
3690                        }),
3691                        fdecl::Capability::Storage(fdecl::Storage {
3692                            name: Some("pdata".to_string()),
3693                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
3694                            backing_dir: Some("minfs".to_string()),
3695                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
3696                            ..Default::default()
3697                        }),
3698                    ]),
3699                    uses: Some(vec![
3700                        fdecl::Use::Protocol(fdecl::UseProtocol {
3701                            dependency_type: Some(fdecl::DependencyType::Strong),
3702                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child1".to_string(), collection: None})),
3703                            source_name: Some("a".to_string()),
3704                            target_path: Some("/svc/a".to_string()),
3705                            ..Default::default()
3706                        }),
3707                    ]),
3708                    offers: Some(vec![
3709                        fdecl::Offer::Storage(fdecl::OfferStorage {
3710                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3711                            source_name: Some("cdata".to_string()),
3712                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3713                            target_name: Some("cdata".to_string()),
3714                            ..Default::default()
3715                        }),
3716                        fdecl::Offer::Storage(fdecl::OfferStorage {
3717                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3718                            source_name: Some("pdata".to_string()),
3719                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3720                            target_name: Some("pdata".to_string()),
3721                            ..Default::default()
3722                        }),
3723                    ]),
3724                    children: Some(vec![
3725                        fdecl::Child {
3726                            name: Some("child1".to_string()),
3727                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3728                            startup: Some(fdecl::StartupMode::Lazy),
3729                            on_terminate: None,
3730                            ..Default::default()
3731                        },
3732                        fdecl::Child {
3733                            name: Some("child2".to_string()),
3734                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
3735                            startup: Some(fdecl::StartupMode::Lazy),
3736                            on_terminate: None,
3737                            ..Default::default()
3738                        }
3739                    ]),
3740                    ..new_component_decl()
3741                }
3742            },
3743            result = Ok(()),
3744        },
3745        test_validate_use_from_child_storage_cycle => {
3746            input = {
3747                fdecl::Component {
3748                    capabilities: Some(vec![
3749                        fdecl::Capability::Storage(fdecl::Storage {
3750                            name: Some("data".to_string()),
3751                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
3752                            backing_dir: Some("minfs".to_string()),
3753                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
3754                            ..Default::default()
3755                        }),
3756                    ]),
3757                    uses: Some(vec![
3758                        fdecl::Use::Protocol(fdecl::UseProtocol {
3759                            dependency_type: Some(fdecl::DependencyType::Strong),
3760                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
3761                            source_name: Some("a".to_string()),
3762                            target_path: Some("/svc/a".to_string()),
3763                            ..Default::default()
3764                        }),
3765                    ]),
3766                    offers: Some(vec![
3767                        fdecl::Offer::Storage(fdecl::OfferStorage {
3768                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3769                            source_name: Some("data".to_string()),
3770                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
3771                            target_name: Some("data".to_string()),
3772                            ..Default::default()
3773                        }),
3774                    ]),
3775                    children: Some(vec![
3776                        fdecl::Child {
3777                            name: Some("child".to_string()),
3778                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3779                            startup: Some(fdecl::StartupMode::Lazy),
3780                            on_terminate: None,
3781                            ..Default::default()
3782                        },
3783                    ]),
3784                    ..new_component_decl()
3785                }
3786            },
3787            result = Err(ErrorList::new(vec![
3788                Error::dependency_cycle("{{self -> capability data -> child child -> self}}".to_string()),
3789            ])),
3790        },
3791        test_validate_storage_strong_cycle_between_children => {
3792            input = {
3793                fdecl::Component {
3794                    capabilities: Some(vec![
3795                        fdecl::Capability::Storage(fdecl::Storage {
3796                            name: Some("data".to_string()),
3797                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None } )),
3798                            backing_dir: Some("minfs".to_string()),
3799                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
3800                            ..Default::default()
3801                        })
3802                    ]),
3803                    offers: Some(vec![
3804                        fdecl::Offer::Storage(fdecl::OfferStorage {
3805                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
3806                            source_name: Some("data".to_string()),
3807                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
3808                            target_name: Some("data".to_string()),
3809                            ..Default::default()
3810                        }),
3811                        fdecl::Offer::Service(fdecl::OfferService {
3812                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
3813                            source_name: Some("a".to_string()),
3814                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3815                            target_name: Some("a".to_string()),
3816                            ..Default::default()
3817                        }),
3818                    ]),
3819                    children: Some(vec![
3820                        fdecl::Child {
3821                            name: Some("child1".to_string()),
3822                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3823                            startup: Some(fdecl::StartupMode::Lazy),
3824                            on_terminate: None,
3825                            ..Default::default()
3826                        },
3827                        fdecl::Child {
3828                            name: Some("child2".to_string()),
3829                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
3830                            startup: Some(fdecl::StartupMode::Lazy),
3831                            on_terminate: None,
3832                            ..Default::default()
3833                        }
3834                    ]),
3835                    ..new_component_decl()
3836                }
3837            },
3838            result = Err(ErrorList::new(vec![
3839                Error::dependency_cycle("{{child child1 -> capability data -> child child2 -> child child1}}".to_string()),
3840            ])),
3841        },
3842        test_validate_strong_cycle_between_children_through_environment_debug => {
3843            input = {
3844                fdecl::Component {
3845                    environments: Some(vec![
3846                        fdecl::Environment {
3847                            name: Some("env".to_string()),
3848                            extends: Some(fdecl::EnvironmentExtends::Realm),
3849                            debug_capabilities: Some(vec![
3850                                fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
3851                                    source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3852                                    source_name: Some("fuchsia.foo.Bar".to_string()),
3853                                    target_name: Some("fuchsia.foo.Bar".to_string()),
3854                                    ..Default::default()
3855                                }),
3856                            ]),
3857                            ..Default::default()
3858                        },
3859                    ]),
3860                    offers: Some(vec![
3861                        fdecl::Offer::Service(fdecl::OfferService {
3862                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
3863                            source_name: Some("a".to_string()),
3864                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3865                            target_name: Some("a".to_string()),
3866                            ..Default::default()
3867                        }),
3868                    ]),
3869                    children: Some(vec![
3870                        fdecl::Child {
3871                            name: Some("child1".to_string()),
3872                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3873                            startup: Some(fdecl::StartupMode::Lazy),
3874                            on_terminate: None,
3875                            ..Default::default()
3876                        },
3877                        fdecl::Child {
3878                            name: Some("child2".to_string()),
3879                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
3880                            startup: Some(fdecl::StartupMode::Lazy),
3881                            environment: Some("env".to_string()),
3882                            on_terminate: None,
3883                            ..Default::default()
3884                        }
3885                    ]),
3886                    ..new_component_decl()
3887                }
3888            },
3889            result = Err(ErrorList::new(vec![
3890                Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}".to_string()),
3891            ])),
3892        },
3893        test_validate_strong_cycle_between_children_through_environment_runner => {
3894            input = {
3895                fdecl::Component {
3896                    environments: Some(vec![
3897                        fdecl::Environment {
3898                            name: Some("env".to_string()),
3899                            extends: Some(fdecl::EnvironmentExtends::Realm),
3900                            runners: Some(vec![
3901                                fdecl::RunnerRegistration {
3902                                    source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3903                                    source_name: Some("coff".to_string()),
3904                                    target_name: Some("coff".to_string()),
3905                                    ..Default::default()
3906                                }
3907                            ]),
3908                            ..Default::default()
3909                        },
3910                    ]),
3911                    offers: Some(vec![
3912                        fdecl::Offer::Service(fdecl::OfferService {
3913                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
3914                            source_name: Some("a".to_string()),
3915                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3916                            target_name: Some("a".to_string()),
3917                            ..Default::default()
3918                        }),
3919                    ]),
3920                    children: Some(vec![
3921                        fdecl::Child {
3922                            name: Some("child1".to_string()),
3923                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3924                            startup: Some(fdecl::StartupMode::Lazy),
3925                            on_terminate: None,
3926                            ..Default::default()
3927                        },
3928                        fdecl::Child {
3929                            name: Some("child2".to_string()),
3930                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
3931                            startup: Some(fdecl::StartupMode::Lazy),
3932                            environment: Some("env".to_string()),
3933                            on_terminate: None,
3934                            ..Default::default()
3935                        }
3936                    ]),
3937                    ..new_component_decl()
3938                }
3939            },
3940            result = Err(ErrorList::new(vec![
3941                Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}".to_string()),
3942            ])),
3943        },
3944        test_validate_strong_cycle_between_children_through_environment_resolver => {
3945            input = {
3946                fdecl::Component {
3947                    environments: Some(vec![
3948                        fdecl::Environment {
3949                            name: Some("env".to_string()),
3950                            extends: Some(fdecl::EnvironmentExtends::Realm),
3951                            resolvers: Some(vec![
3952                                fdecl::ResolverRegistration {
3953                                    resolver: Some("gopher".to_string()),
3954                                    source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3955                                    scheme: Some("gopher".to_string()),
3956                                    ..Default::default()
3957                                }
3958                            ]),
3959                            ..Default::default()
3960                        },
3961                    ]),
3962                    offers: Some(vec![
3963                        fdecl::Offer::Service(fdecl::OfferService {
3964                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
3965                            source_name: Some("a".to_string()),
3966                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
3967                            target_name: Some("a".to_string()),
3968                            ..Default::default()
3969                        }),
3970                    ]),
3971                    children: Some(vec![
3972                        fdecl::Child {
3973                            name: Some("child1".to_string()),
3974                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
3975                            startup: Some(fdecl::StartupMode::Lazy),
3976                            on_terminate: None,
3977                            ..Default::default()
3978                        },
3979                        fdecl::Child {
3980                            name: Some("child2".to_string()),
3981                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
3982                            startup: Some(fdecl::StartupMode::Lazy),
3983                            environment: Some("env".to_string()),
3984                            on_terminate: None,
3985                            ..Default::default()
3986                        }
3987                    ]),
3988                    ..new_component_decl()
3989                }
3990            },
3991            result = Err(ErrorList::new(vec![
3992                Error::dependency_cycle("{{child child1 -> environment env -> child child2 -> child child1}}".to_string()),
3993            ])),
3994        },
3995        test_validate_strong_cycle_between_self_and_two_children => {
3996            input = {
3997                fdecl::Component {
3998                    capabilities: Some(vec![
3999                        fdecl::Capability::Protocol(fdecl::Protocol {
4000                            name: Some("fuchsia.foo.Bar".to_string()),
4001                            source_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4002                            ..Default::default()
4003                        })
4004                    ]),
4005                    offers: Some(vec![
4006                        fdecl::Offer::Protocol(fdecl::OfferProtocol {
4007                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4008                            source_name: Some("fuchsia.foo.Bar".to_string()),
4009                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4010                            target_name: Some("fuchsia.foo.Bar".to_string()),
4011                            dependency_type: Some(fdecl::DependencyType::Strong),
4012                            ..Default::default()
4013                        }),
4014                        fdecl::Offer::Protocol(fdecl::OfferProtocol {
4015                            source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child1".to_string(), collection: None })),
4016                            source_name: Some("fuchsia.bar.Baz".to_string()),
4017                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child2".to_string(), collection: None })),
4018                            target_name: Some("fuchsia.bar.Baz".to_string()),
4019                            dependency_type: Some(fdecl::DependencyType::Strong),
4020                            ..Default::default()
4021                        }),
4022                    ]),
4023                    uses: Some(vec![
4024                        fdecl::Use::Protocol(fdecl::UseProtocol {
4025                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child2".to_string(), collection: None})),
4026                            source_name: Some("fuchsia.baz.Foo".to_string()),
4027                            target_path: Some("/svc/fuchsia.baz.Foo".to_string()),
4028                            dependency_type: Some(fdecl::DependencyType::Strong),
4029                            ..Default::default()
4030                        }),
4031                    ]),
4032                    children: Some(vec![
4033                        fdecl::Child {
4034                            name: Some("child1".to_string()),
4035                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4036                            startup: Some(fdecl::StartupMode::Lazy),
4037                            on_terminate: None,
4038                            ..Default::default()
4039                        },
4040                        fdecl::Child {
4041                            name: Some("child2".to_string()),
4042                            url: Some("fuchsia-pkg://fuchsia.com/foo2".to_string()),
4043                            startup: Some(fdecl::StartupMode::Lazy),
4044                            on_terminate: None,
4045                            ..Default::default()
4046                        }
4047                    ]),
4048                    ..new_component_decl()
4049                }
4050            },
4051            result = Err(ErrorList::new(vec![
4052                Error::dependency_cycle("{{self -> child child1 -> child child2 -> self}}".to_string()),
4053            ])),
4054        },
4055        test_validate_strong_cycle_with_self_storage => {
4056            input = {
4057                fdecl::Component {
4058                    capabilities: Some(vec![
4059                        fdecl::Capability::Storage(fdecl::Storage {
4060                            name: Some("data".to_string()),
4061                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4062                            backing_dir: Some("minfs".to_string()),
4063                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4064                            ..Default::default()
4065                        }),
4066                        fdecl::Capability::Directory(fdecl::Directory {
4067                            name: Some("minfs".to_string()),
4068                            source_path: Some("/minfs".to_string()),
4069                            rights: Some(fio::RW_STAR_DIR),
4070                            ..Default::default()
4071                        }),
4072                    ]),
4073                    offers: Some(vec![
4074                        fdecl::Offer::Storage(fdecl::OfferStorage {
4075                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4076                            source_name: Some("data".to_string()),
4077                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4078                            target_name: Some("data".to_string()),
4079                            ..Default::default()
4080                        }),
4081                    ]),
4082                    uses: Some(vec![
4083                        fdecl::Use::Protocol(fdecl::UseProtocol {
4084                            dependency_type: Some(fdecl::DependencyType::Strong),
4085                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4086                            source_name: Some("fuchsia.foo.Bar".to_string()),
4087                            target_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4088                            ..Default::default()
4089                        }),
4090                    ]),
4091                    children: Some(vec![
4092                        fdecl::Child {
4093                            name: Some("child".to_string()),
4094                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4095                            startup: Some(fdecl::StartupMode::Lazy),
4096                            ..Default::default()
4097                        },
4098                    ]),
4099                    ..new_component_decl()
4100                }
4101            },
4102            result = Err(ErrorList::new(vec![
4103                Error::dependency_cycle("{{self -> capability data -> child child -> self}}".to_string()),
4104            ])),
4105        },
4106        test_validate_strong_cycle_with_self_storage_admin_protocol => {
4107            input = {
4108                fdecl::Component {
4109                    capabilities: Some(vec![
4110                        fdecl::Capability::Storage(fdecl::Storage {
4111                            name: Some("data".to_string()),
4112                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4113                            backing_dir: Some("minfs".to_string()),
4114                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4115                            ..Default::default()
4116                        }),
4117                        fdecl::Capability::Directory(fdecl::Directory {
4118                            name: Some("minfs".to_string()),
4119                            source_path: Some("/minfs".to_string()),
4120                            rights: Some(fio::RW_STAR_DIR),
4121                            ..Default::default()
4122                        }),
4123                    ]),
4124                    offers: Some(vec![
4125                        fdecl::Offer::Protocol(fdecl::OfferProtocol {
4126                            source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef { name: "data".to_string() })),
4127                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4128                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4129                            target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4130                            dependency_type: Some(fdecl::DependencyType::Strong),
4131                            ..Default::default()
4132                        }),
4133                    ]),
4134                    uses: Some(vec![
4135                        fdecl::Use::Protocol(fdecl::UseProtocol {
4136                            dependency_type: Some(fdecl::DependencyType::Strong),
4137                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4138                            source_name: Some("fuchsia.foo.Bar".to_string()),
4139                            target_path: Some("/svc/fuchsia.foo.Bar".to_string()),
4140                            ..Default::default()
4141                        }),
4142                    ]),
4143                    children: Some(vec![
4144                        fdecl::Child {
4145                            name: Some("child".to_string()),
4146                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4147                            startup: Some(fdecl::StartupMode::Lazy),
4148                            ..Default::default()
4149                        },
4150                    ]),
4151                    ..new_component_decl()
4152                }
4153            },
4154            result = Err(ErrorList::new(vec![
4155                Error::dependency_cycle("{{self -> capability data -> child child -> self}}".to_string()),
4156            ])),
4157        },
4158        test_validate_strong_cycle_with_dictionary => {
4159            input = fdecl::Component {
4160                offers: Some(vec![
4161                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
4162                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4163                        source_name: Some("dict".into()),
4164                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4165                            name: "a".into(),
4166                            collection: None,
4167                        })),
4168                        target_name: Some("dict".into()),
4169                        dependency_type: Some(fdecl::DependencyType::Strong),
4170                        ..Default::default()
4171                    }),
4172                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4173                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4174                            name: "b".into(),
4175                            collection: None,
4176                        })),
4177                        source_name: Some("1".into()),
4178                        target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4179                            name: "dict".into(),
4180                        })),
4181                        target_name: Some("1".into()),
4182                        dependency_type: Some(fdecl::DependencyType::Strong),
4183                        ..Default::default()
4184                    }),
4185                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4186                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4187                            name: "a".into(),
4188                            collection: None,
4189                        })),
4190                        source_name: Some("2".into()),
4191                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4192                            name: "b".into(),
4193                            collection: None,
4194                        })),
4195                        target_name: Some("2".into()),
4196                        dependency_type: Some(fdecl::DependencyType::Strong),
4197                        ..Default::default()
4198                    }),
4199                ]),
4200                children: Some(vec![
4201                    fdecl::Child {
4202                        name: Some("a".into()),
4203                        url: Some("fuchsia-pkg://child".into()),
4204                        startup: Some(fdecl::StartupMode::Lazy),
4205                        ..Default::default()
4206                    },
4207                    fdecl::Child {
4208                        name: Some("b".into()),
4209                        url: Some("fuchsia-pkg://child".into()),
4210                        startup: Some(fdecl::StartupMode::Lazy),
4211                        ..Default::default()
4212                    },
4213                ]),
4214                capabilities: Some(vec![
4215                    fdecl::Capability::Dictionary(fdecl::Dictionary {
4216                        name: Some("dict".into()),
4217                        ..Default::default()
4218                    }),
4219                ]),
4220                ..Default::default()
4221            },
4222            result = Err(ErrorList::new(vec![
4223                Error::dependency_cycle("{{child a -> child b -> capability dict -> child a}}".to_string()),
4224            ])),
4225        },
4226        test_validate_strong_cycle_with_dictionary_indirect => {
4227            input = fdecl::Component {
4228                offers: Some(vec![
4229                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4230                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4231                        source_name: Some("3".into()),
4232                        source_dictionary: Some("dict".into()),
4233                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4234                            name: "a".into(),
4235                            collection: None,
4236                        })),
4237                        target_name: Some("3".into()),
4238                        dependency_type: Some(fdecl::DependencyType::Strong),
4239                        ..Default::default()
4240                    }),
4241                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4242                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4243                            name: "b".into(),
4244                            collection: None,
4245                        })),
4246                        source_name: Some("1".into()),
4247                        target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4248                            name: "dict".into(),
4249                        })),
4250                        target_name: Some("1".into()),
4251                        dependency_type: Some(fdecl::DependencyType::Strong),
4252                        ..Default::default()
4253                    }),
4254                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4255                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4256                            name: "a".into(),
4257                            collection: None,
4258                        })),
4259                        source_name: Some("2".into()),
4260                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4261                            name: "b".into(),
4262                            collection: None,
4263                        })),
4264                        target_name: Some("2".into()),
4265                        dependency_type: Some(fdecl::DependencyType::Strong),
4266                        ..Default::default()
4267                    }),
4268                ]),
4269                children: Some(vec![
4270                    fdecl::Child {
4271                        name: Some("a".into()),
4272                        url: Some("fuchsia-pkg://child".into()),
4273                        startup: Some(fdecl::StartupMode::Lazy),
4274                        ..Default::default()
4275                    },
4276                    fdecl::Child {
4277                        name: Some("b".into()),
4278                        url: Some("fuchsia-pkg://child".into()),
4279                        startup: Some(fdecl::StartupMode::Lazy),
4280                        ..Default::default()
4281                    },
4282                ]),
4283                capabilities: Some(vec![
4284                    fdecl::Capability::Dictionary(fdecl::Dictionary {
4285                        name: Some("dict".into()),
4286                        ..Default::default()
4287                    }),
4288                ]),
4289                ..Default::default()
4290            },
4291            result = Err(ErrorList::new(vec![
4292                Error::dependency_cycle("{{child a -> child b -> capability dict -> child a}}".to_string()),
4293            ])),
4294        },
4295        test_validate_use_from_child_offer_to_child_weak_cycle => {
4296            input = {
4297                fdecl::Component {
4298                    capabilities: Some(vec![
4299                        fdecl::Capability::Service(fdecl::Service {
4300                            name: Some("a".to_string()),
4301                            source_path: Some("/a".to_string()),
4302                            ..Default::default()
4303                        })]),
4304                    uses: Some(vec![
4305                        fdecl::Use::Protocol(fdecl::UseProtocol {
4306                            dependency_type: Some(fdecl::DependencyType::Weak),
4307                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4308                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4309                            target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
4310                            ..Default::default()
4311                        }),
4312                        fdecl::Use::Service(fdecl::UseService {
4313                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4314                            source_name: Some("service_name".to_string()),
4315                            target_path: Some("/svc/service_name".to_string()),
4316                            dependency_type: Some(fdecl::DependencyType::Weak),
4317                            ..Default::default()
4318                        }),
4319                        fdecl::Use::Directory(fdecl::UseDirectory {
4320                            dependency_type: Some(fdecl::DependencyType::Weak),
4321                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{ name: "child".to_string(), collection: None})),
4322                            source_name: Some("DirectoryName".to_string()),
4323                            target_path: Some("/data/DirectoryName".to_string()),
4324                            rights: Some(fio::Operations::CONNECT),
4325                            subdir: None,
4326                            ..Default::default()
4327                        }),
4328                    ]),
4329                    offers: Some(vec![
4330                        fdecl::Offer::Service(fdecl::OfferService {
4331                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4332                            source_name: Some("a".to_string()),
4333                            target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
4334                            target_name: Some("a".to_string()),
4335                            ..Default::default()
4336                        })
4337                    ]),
4338                    children: Some(vec![
4339                        fdecl::Child {
4340                            name: Some("child".to_string()),
4341                            url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
4342                            startup: Some(fdecl::StartupMode::Lazy),
4343                            on_terminate: None,
4344                            ..Default::default()
4345                        }
4346                    ]),
4347                    ..new_component_decl()
4348                }
4349            },
4350            result = Ok(()),
4351        },
4352        test_validate_expose_from_self_to_framework_and_parent => {
4353            input = {
4354                fdecl::Component {
4355                    capabilities: Some(vec![
4356                        fdecl::Capability::Protocol(fdecl::Protocol {
4357                            name: Some("a".to_string()),
4358                            source_path: Some("/a".to_string()),
4359                            ..Default::default()
4360                        }),
4361                    ]),
4362                    exposes: Some(vec![
4363                        fdecl::Expose::Protocol(fdecl::ExposeProtocol {
4364                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4365                            source_name: Some("a".to_string()),
4366                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4367                            target_name: Some("a".to_string()),
4368                            ..Default::default()
4369                        }),
4370                        fdecl::Expose::Protocol(fdecl::ExposeProtocol {
4371                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4372                            source_name: Some("a".to_string()),
4373                            target: Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})),
4374                            target_name: Some("a".to_string()),
4375                            ..Default::default()
4376                        }),
4377                    ]),
4378                    ..new_component_decl()
4379                }
4380            },
4381            result = Ok(()),
4382        },
4383        test_validate_use_from_not_child_weak => {
4384            input = {
4385                fdecl::Component {
4386                    uses: Some(vec![
4387                        fdecl::Use::Protocol(fdecl::UseProtocol {
4388                            dependency_type: Some(fdecl::DependencyType::Weak),
4389                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4390                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4391                            target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
4392                            ..Default::default()
4393                        }),
4394                    ]),
4395                    ..new_component_decl()
4396                }
4397            },
4398            result = Err(ErrorList::new(vec![
4399                Error::invalid_field(DeclType::UseProtocol, "dependency_type"),
4400            ])),
4401        },
4402        test_validate_event_stream_offer_valid_decls => {
4403            input = {
4404                let mut decl = new_component_decl();
4405                decl.offers = Some(vec![
4406                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4407                        source_name: Some("stopped".to_string()),
4408                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4409                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4410                        target_name: Some("stopped".to_string()),
4411                        ..Default::default()
4412                    }),
4413                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4414                        source_name: Some("started".to_string()),
4415                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4416                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4417                        target_name: Some("started".to_string()),
4418                        ..Default::default()
4419                    }),
4420                ]);
4421                decl.children = Some(vec![fdecl::Child{
4422                    name: Some("test".to_string()),
4423                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4424                    startup: Some(fdecl::StartupMode::Lazy),
4425                    on_terminate: None,
4426                    environment: None,
4427                    ..Default::default()
4428                },
4429                fdecl::Child{
4430                    name: Some("test2".to_string()),
4431                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4432                    startup: Some(fdecl::StartupMode::Lazy),
4433                    on_terminate: None,
4434                    environment: None,
4435                    ..Default::default()
4436                }
4437                ]);
4438                decl
4439            },
4440            result = Ok(()),
4441        },
4442        test_validate_event_stream_offer_to_framework_invalid => {
4443            input = {
4444                let mut decl = new_component_decl();
4445                decl.offers = Some(vec![
4446                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4447                        source_name: Some("stopped".to_string()),
4448                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4449                        target: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
4450                        target_name: Some("stopped".to_string()),
4451                        ..Default::default()
4452                    }),
4453                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4454                        source_name: Some("started".to_string()),
4455                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4456                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4457                        target_name: Some("started".to_string()),
4458                        ..Default::default()
4459                    }),
4460                ]);
4461                decl.children = Some(vec![fdecl::Child{
4462                    name: Some("test".to_string()),
4463                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4464                    startup: Some(fdecl::StartupMode::Lazy),
4465                    on_terminate: None,
4466                    environment: None,
4467                    ..Default::default()
4468                },
4469                fdecl::Child{
4470                    name: Some("test2".to_string()),
4471                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4472                    startup: Some(fdecl::StartupMode::Lazy),
4473                    on_terminate: None,
4474                    environment: None,
4475                    ..Default::default()
4476                }
4477                ]);
4478                decl
4479            },
4480            result = Err(ErrorList::new(vec![
4481                Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "target".to_string() }),
4482                Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "target".to_string() }),
4483            ])),
4484        },
4485        test_validate_event_stream_offer_to_scope_zero_length_invalid => {
4486            input = {
4487                let mut decl = new_component_decl();
4488                decl.offers = Some(vec![
4489                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4490                        source_name: Some("started".to_string()),
4491                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4492                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4493                        scope: Some(vec![]),
4494                        target_name: Some("started".to_string()),
4495                        ..Default::default()
4496                    }),
4497                ]);
4498                decl.children = Some(vec![fdecl::Child{
4499                    name: Some("test".to_string()),
4500                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4501                    startup: Some(fdecl::StartupMode::Lazy),
4502                    on_terminate: None,
4503                    environment: None,
4504                    ..Default::default()
4505                },
4506                fdecl::Child{
4507                    name: Some("test2".to_string()),
4508                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4509                    startup: Some(fdecl::StartupMode::Lazy),
4510                    on_terminate: None,
4511                    environment: None,
4512                    ..Default::default()
4513                }
4514                ]);
4515                decl
4516            },
4517            result = Err(ErrorList::new(vec![
4518                Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "scope".to_string() }),
4519            ])),
4520        },
4521        test_validate_event_stream_offer_to_scope_framework_invalid => {
4522            input = {
4523                let mut decl = new_component_decl();
4524                decl.offers = Some(vec![
4525                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4526                        source_name: Some("started".to_string()),
4527                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4528                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4529                        scope: Some(vec![fdecl::Ref::Framework(fdecl::FrameworkRef{})]),
4530                        target_name: Some("started".to_string()),
4531                        ..Default::default()
4532                    }),
4533                ]);
4534                decl.children = Some(vec![fdecl::Child{
4535                    name: Some("test".to_string()),
4536                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4537                    startup: Some(fdecl::StartupMode::Lazy),
4538                    on_terminate: None,
4539                    environment: None,
4540                    ..Default::default()
4541                },
4542                fdecl::Child{
4543                    name: Some("test2".to_string()),
4544                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4545                    startup: Some(fdecl::StartupMode::Lazy),
4546                    on_terminate: None,
4547                    environment: None,
4548                    ..Default::default()
4549                }
4550                ]);
4551                decl
4552            },
4553            result = Err(ErrorList::new(vec![
4554                Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "scope".to_string() }),
4555            ])),
4556        },
4557        test_validate_event_stream_offer_to_scope_valid => {
4558            input = {
4559                let mut decl = new_component_decl();
4560                decl.offers = Some(vec![
4561                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4562                        source_name: Some("started".to_string()),
4563                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4564                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4565                        scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
4566                        target_name: Some("started".to_string()),
4567                        ..Default::default()
4568                    }),
4569                ]);
4570                decl.children = Some(vec![fdecl::Child{
4571                    name: Some("test".to_string()),
4572                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4573                    startup: Some(fdecl::StartupMode::Lazy),
4574                    on_terminate: None,
4575                    environment: None,
4576                    ..Default::default()
4577                },
4578                fdecl::Child{
4579                    name: Some("test2".to_string()),
4580                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4581                    startup: Some(fdecl::StartupMode::Lazy),
4582                    on_terminate: None,
4583                    environment: None,
4584                    ..Default::default()
4585                }
4586                ]);
4587                decl
4588            },
4589            result = Ok(()),
4590        },
4591        test_validate_event_stream_offer_to_scope_with_capability_requested => {
4592            input = {
4593                let mut decl = new_component_decl();
4594                decl.offers = Some(vec![
4595                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4596                        source_name: Some("capability_requested".to_string()),
4597                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4598                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4599                        scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
4600                        target_name: Some("started".to_string()),
4601                        ..Default::default()
4602                    }),
4603                ]);
4604                decl.children = Some(vec![fdecl::Child{
4605                    name: Some("test".to_string()),
4606                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4607                    startup: Some(fdecl::StartupMode::Lazy),
4608                    on_terminate: None,
4609                    environment: None,
4610                    ..Default::default()
4611                },
4612                fdecl::Child{
4613                    name: Some("test2".to_string()),
4614                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4615                    startup: Some(fdecl::StartupMode::Lazy),
4616                    on_terminate: None,
4617                    environment: None,
4618                    ..Default::default()
4619                }
4620                ]);
4621                decl
4622            },
4623            result = Ok(()),
4624        },
4625        test_validate_event_stream_offer_with_no_source_name_invalid => {
4626            input = {
4627                let mut decl = new_component_decl();
4628                decl.offers = Some(vec![
4629                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4630                        source_name: None,
4631                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4632                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4633                        scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})]),
4634                        target_name: Some("started".to_string()),
4635                        ..Default::default()
4636                    }),
4637                ]);
4638                decl.children = Some(vec![fdecl::Child{
4639                    name: Some("test".to_string()),
4640                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4641                    startup: Some(fdecl::StartupMode::Lazy),
4642                    on_terminate: None,
4643                    environment: None,
4644                    ..Default::default()
4645                },
4646                fdecl::Child{
4647                    name: Some("test2".to_string()),
4648                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4649                    startup: Some(fdecl::StartupMode::Lazy),
4650                    on_terminate: None,
4651                    environment: None,
4652                    ..Default::default()
4653                }
4654                ]);
4655                decl
4656            },
4657            result = Err(ErrorList::new(vec![
4658                Error::MissingField(DeclField { decl: DeclType::OfferEventStream, field: "source_name".to_string() }),
4659            ])),
4660        },
4661        test_validate_event_stream_offer_invalid_source => {
4662            input = {
4663                let mut decl = new_component_decl();
4664                decl.offers = Some(vec![
4665                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4666                        source_name: Some("stopped".to_string()),
4667                        source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
4668                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4669                        target_name: Some("stopped".to_string()),
4670                        ..Default::default()
4671                    }),
4672                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4673                        source_name: Some("started".to_string()),
4674                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4675                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4676                        target_name: Some("started".to_string()),
4677                        ..Default::default()
4678                    }),
4679                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4680                        source_name: Some("capability_requested".to_string()),
4681                        source: Some(fdecl::Ref::Debug(fdecl::DebugRef{})),
4682                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4683                        target_name: Some("capability_requested".to_string()),
4684                        ..Default::default()
4685                    }),
4686                ]);
4687                decl.children = Some(vec![fdecl::Child{
4688                    name: Some("test".to_string()),
4689                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4690                    startup: Some(fdecl::StartupMode::Lazy),
4691                    on_terminate: None,
4692                    environment: None,
4693                    ..Default::default()
4694                },
4695                fdecl::Child{
4696                    name: Some("test2".to_string()),
4697                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4698                    startup: Some(fdecl::StartupMode::Lazy),
4699                    on_terminate: None,
4700                    environment: None,
4701                    ..Default::default()
4702                }
4703                ]);
4704                decl
4705            },
4706            result = Err(ErrorList::new(vec![
4707                Error::InvalidField(DeclField { decl: DeclType::OfferEventStream, field: "source".to_string() }),
4708            ])),
4709        },
4710
4711        test_validate_event_stream_offer_missing_source => {
4712            input = {
4713                let mut decl = new_component_decl();
4714                decl.offers = Some(vec![
4715                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4716                        source_name: Some("stopped".to_string()),
4717                        source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
4718                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4719                        target_name: Some("stopped".to_string()),
4720                        ..Default::default()
4721                    }),
4722                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4723                        source_name: Some("started".to_string()),
4724                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4725                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4726                        target_name: Some("started".to_string()),
4727                        ..Default::default()
4728                    }),
4729                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
4730                        source_name: Some("capability_requested".to_string()),
4731                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "test2".to_string(), collection: None})),
4732                        target_name: Some("capability_requested".to_string()),
4733                        ..Default::default()
4734                    }),
4735                ]);
4736                decl.children = Some(vec![fdecl::Child{
4737                    name: Some("test".to_string()),
4738                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
4739                    startup: Some(fdecl::StartupMode::Lazy),
4740                    on_terminate: None,
4741                    environment: None,
4742                    ..Default::default()
4743                },
4744                fdecl::Child{
4745                    name: Some("test2".to_string()),
4746                    url: Some("fuchsia-pkg://fuchsia.com/fake_component#meta/fake_component.cm".to_string()),
4747                    startup: Some(fdecl::StartupMode::Lazy),
4748                    on_terminate: None,
4749                    environment: None,
4750                    ..Default::default()
4751                }
4752                ]);
4753                decl
4754            },
4755            result = Err(ErrorList::new(vec![
4756                Error::MissingField(DeclField { decl: DeclType::OfferEventStream, field: "source".to_string() }),
4757            ])),
4758        },
4759        test_validate_event_stream_must_have_target_path => {
4760            input = {
4761                let mut decl = new_component_decl();
4762                decl.uses = Some(vec![
4763                    fdecl::Use::EventStream(fdecl::UseEventStream {
4764                        source_name: Some("bar".to_string()),
4765                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4766                        ..Default::default()
4767                    }),
4768                ]);
4769                decl
4770            },
4771            result = Err(ErrorList::new(vec![
4772                Error::MissingField(DeclField { decl: DeclType::UseEventStream, field: "target_path".to_string() })
4773            ])),
4774        },
4775        test_validate_event_stream_must_have_source_names => {
4776            input = {
4777                let mut decl = new_component_decl();
4778                decl.uses = Some(vec![
4779                    fdecl::Use::EventStream(fdecl::UseEventStream {
4780                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4781                        target_path: Some("/svc/something".to_string()),
4782                        ..Default::default()
4783                    }),
4784                ]);
4785                decl
4786            },
4787            result = Err(ErrorList::new(vec![
4788                Error::MissingField(DeclField { decl: DeclType::UseEventStream, field: "source_name".to_string() })
4789            ])),
4790        },
4791        test_validate_event_stream_scope_must_be_child_or_collection => {
4792            input = {
4793                let mut decl = new_component_decl();
4794                decl.uses = Some(vec![
4795                    fdecl::Use::EventStream(fdecl::UseEventStream {
4796                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
4797                        target_path: Some("/svc/something".to_string()),
4798                        source_name: Some("some_source".to_string()),
4799                        scope: Some(vec![fdecl::Ref::Framework(fdecl::FrameworkRef{})]),
4800                        ..Default::default()
4801                    }),
4802                ]);
4803                decl
4804            },
4805            result = Err(ErrorList::new(vec![
4806                Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "scope".to_string() })
4807            ])),
4808        },
4809        test_validate_event_stream_source_must_be_parent_or_child => {
4810            input = {
4811                let mut decl = new_component_decl();
4812                decl.uses = Some(vec![
4813                    fdecl::Use::EventStream(fdecl::UseEventStream {
4814                        source: Some(fdecl::Ref::Debug(fdecl::DebugRef{})),
4815                        target_path: Some("/svc/something".to_string()),
4816                        source_name: Some("some_source".to_string()),
4817                        scope: Some(vec![]),
4818                        ..Default::default()
4819                    }),
4820                    fdecl::Use::EventStream(fdecl::UseEventStream {
4821                        source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
4822                        target_path: Some("/svc/something_else".to_string()),
4823                        source_name: Some("some_source".to_string()),
4824                        scope: Some(vec![]),
4825                        ..Default::default()
4826                    }),
4827                    fdecl::Use::EventStream(fdecl::UseEventStream {
4828                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
4829                        target_path: Some("/svc/yet_something_else".to_string()),
4830                        source_name: Some("some_source".to_string()),
4831                        scope: Some(vec![]),
4832                        ..Default::default()
4833                    }),
4834                ]);
4835                decl
4836            },
4837            result = Err(ErrorList::new(vec![
4838                Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() }),
4839                Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() }),
4840                Error::InvalidField(DeclField { decl: DeclType::UseEventStream, field: "source".to_string() })
4841            ])),
4842        },
4843        test_validate_no_runner => {
4844            input = {
4845                let mut decl = new_component_decl();
4846                decl.program = Some(fdecl::Program {
4847                    runner: None,
4848                    info: Some(fdata::Dictionary {
4849                        entries: None,
4850                        ..Default::default()
4851                    }),
4852                    ..Default::default()
4853                });
4854                decl
4855            },
4856            result = Err(ErrorList::new(vec![
4857                Error::MissingRunner,
4858            ])),
4859        },
4860        test_validate_uses_runner => {
4861            input = {
4862                let mut decl = new_component_decl();
4863                decl.program = Some(fdecl::Program {
4864                    runner: None,
4865                    info: Some(fdata::Dictionary {
4866                        entries: None,
4867                        ..Default::default()
4868                    }),
4869                    ..Default::default()
4870                });
4871                decl.uses = Some(vec![
4872                    fdecl::Use::Runner(fdecl::UseRunner {
4873                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4874                        source_name: Some("runner".to_string()),
4875                        ..Default::default()
4876                    }),
4877                ]);
4878                decl
4879            },
4880            result = Ok(()),
4881        },
4882        test_validate_program_and_uses_runner_match => {
4883            input = {
4884                let mut decl = new_component_decl();
4885                decl.program = Some(fdecl::Program {
4886                    runner: Some("runner".to_string()),
4887                    info: Some(fdata::Dictionary {
4888                        entries: None,
4889                        ..Default::default()
4890                    }),
4891                    ..Default::default()
4892                });
4893                decl.uses = Some(vec![
4894                    fdecl::Use::Runner(fdecl::UseRunner {
4895                        source: Some(fdecl::Ref::Environment(fdecl::EnvironmentRef {})),
4896                        source_name: Some("runner".to_string()),
4897                        ..Default::default()
4898                    }),
4899                ]);
4900                decl
4901            },
4902            result = Ok(()),
4903        },
4904        test_validate_runner_names_conflict => {
4905            input = {
4906                let mut decl = new_component_decl();
4907                decl.program = Some(fdecl::Program {
4908                    runner: Some("runner".to_string()),
4909                    info: Some(fdata::Dictionary {
4910                        entries: None,
4911                        ..Default::default()
4912                    }),
4913                    ..Default::default()
4914                });
4915                decl.uses = Some(vec![
4916                    fdecl::Use::Runner(fdecl::UseRunner {
4917                        source: Some(fdecl::Ref::Environment(fdecl::EnvironmentRef {})),
4918                        source_name: Some("other.runner".to_string()),
4919                        ..Default::default()
4920                    }),
4921                ]);
4922                decl
4923            },
4924            result = Err(ErrorList::new(vec![
4925                Error::ConflictingRunners,
4926            ])),
4927        },
4928        test_validate_uses_runner_not_environement => {
4929            input = {
4930                let mut decl = new_component_decl();
4931                decl.program = Some(fdecl::Program {
4932                    runner: Some("runner".to_string()),
4933                    info: Some(fdata::Dictionary {
4934                        entries: None,
4935                        ..Default::default()
4936                    }),
4937                    ..Default::default()
4938                });
4939                decl.uses = Some(vec![
4940                    fdecl::Use::Runner(fdecl::UseRunner {
4941                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4942                        source_name: Some("runner".to_string()),
4943                        ..Default::default()
4944                    }),
4945                ]);
4946                decl
4947            },
4948            result = Err(ErrorList::new(vec![
4949                Error::ConflictingRunners,
4950            ])),
4951        },
4952        test_validate_uses_long_identifiers => {
4953            input = {
4954                let mut decl = new_component_decl();
4955                decl.program = Some(fdecl::Program {
4956                    runner: Some("elf".to_string()),
4957                    info: Some(fdata::Dictionary {
4958                        entries: None,
4959                        ..Default::default()
4960                    }),
4961                    ..Default::default()
4962                });
4963                decl.uses = Some(vec![
4964                    fdecl::Use::Service(fdecl::UseService {
4965                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4966                        source_name: Some(format!("{}", "a".repeat(256))),
4967                        target_path: Some("/a".repeat(2048)),
4968                        dependency_type: Some(fdecl::DependencyType::Strong),
4969                        ..Default::default()
4970                    }),
4971                    fdecl::Use::Protocol(fdecl::UseProtocol {
4972                        dependency_type: Some(fdecl::DependencyType::Strong),
4973                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4974                        source_name: Some(format!("{}", "a".repeat(256))),
4975                        target_path: Some("/b".repeat(2048)),
4976                        ..Default::default()
4977                    }),
4978                    fdecl::Use::Directory(fdecl::UseDirectory {
4979                        dependency_type: Some(fdecl::DependencyType::Strong),
4980                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4981                        source_name: Some(format!("{}", "a".repeat(256))),
4982                        target_path: Some("/c".repeat(2048)),
4983                        rights: Some(fio::Operations::CONNECT),
4984                        subdir: None,
4985                        ..Default::default()
4986                    }),
4987                    fdecl::Use::Storage(fdecl::UseStorage {
4988                        source_name: Some("cache".to_string()),
4989                        target_path: Some("/d".repeat(2048)),
4990                        ..Default::default()
4991                    }),
4992                ]);
4993                decl
4994            },
4995            result = Err(ErrorList::new(vec![
4996                Error::field_too_long(DeclType::UseService, "source_name"),
4997                Error::field_too_long(DeclType::UseService, "target_path"),
4998                Error::field_too_long(DeclType::UseProtocol, "source_name"),
4999                Error::field_too_long(DeclType::UseProtocol, "target_path"),
5000                Error::field_too_long(DeclType::UseDirectory, "source_name"),
5001                Error::field_too_long(DeclType::UseDirectory, "target_path"),
5002                Error::field_too_long(DeclType::UseStorage, "target_path"),
5003            ])),
5004        },
5005        test_validate_conflicting_paths => {
5006            input = {
5007                let mut decl = new_component_decl();
5008                decl.uses = Some(vec![
5009                    fdecl::Use::Service(fdecl::UseService {
5010                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5011                        source_name: Some("foo".to_string()),
5012                        target_path: Some("/bar".to_string()),
5013                        dependency_type: Some(fdecl::DependencyType::Strong),
5014                        ..Default::default()
5015                    }),
5016                    fdecl::Use::Protocol(fdecl::UseProtocol {
5017                        dependency_type: Some(fdecl::DependencyType::Strong),
5018                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5019                        source_name: Some("space".to_string()),
5020                        target_path: Some("/bar".to_string()),
5021                        ..Default::default()
5022                    }),
5023                    fdecl::Use::Directory(fdecl::UseDirectory {
5024                        dependency_type: Some(fdecl::DependencyType::Strong),
5025                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5026                        source_name: Some("crow".to_string()),
5027                        target_path: Some("/bar".to_string()),
5028                        rights: Some(fio::Operations::CONNECT),
5029                        subdir: None,
5030                        ..Default::default()
5031                    }),
5032                ]);
5033                decl
5034            },
5035            result = Err(ErrorList::new(vec![
5036                Error::duplicate_field(DeclType::UseProtocol, "target_path", "/bar"),
5037                Error::duplicate_field(DeclType::UseDirectory, "target_path", "/bar"),
5038            ])),
5039        },
5040        // exposes
5041        test_validate_exposes_empty => {
5042            input = {
5043                let mut decl = new_component_decl();
5044                decl.exposes = Some(vec![
5045                    fdecl::Expose::Service(fdecl::ExposeService {
5046                        source: None,
5047                        source_name: None,
5048                        target_name: None,
5049                        target: None,
5050                        ..Default::default()
5051                    }),
5052                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5053                        source: None,
5054                        source_name: None,
5055                        target_name: None,
5056                        target: None,
5057                        ..Default::default()
5058                    }),
5059                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5060                        source: None,
5061                        source_name: None,
5062                        target_name: None,
5063                        target: None,
5064                        rights: None,
5065                        subdir: None,
5066                        ..Default::default()
5067                    }),
5068                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5069                        source: None,
5070                        source_name: None,
5071                        target: None,
5072                        target_name: None,
5073                        ..Default::default()
5074                    }),
5075                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5076                        source: None,
5077                        source_name: None,
5078                        target: None,
5079                        target_name: None,
5080                        ..Default::default()
5081                    }),
5082                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5083                        ..Default::default()
5084                    }),
5085                ]);
5086                decl
5087            },
5088            result = Err(ErrorList::new(vec![
5089                Error::missing_field(DeclType::ExposeService, "source"),
5090                Error::missing_field(DeclType::ExposeService, "target"),
5091                Error::missing_field(DeclType::ExposeService, "source_name"),
5092                Error::missing_field(DeclType::ExposeService, "target_name"),
5093                Error::missing_field(DeclType::ExposeProtocol, "source"),
5094                Error::missing_field(DeclType::ExposeProtocol, "target"),
5095                Error::missing_field(DeclType::ExposeProtocol, "source_name"),
5096                Error::missing_field(DeclType::ExposeProtocol, "target_name"),
5097                Error::missing_field(DeclType::ExposeDirectory, "source"),
5098                Error::missing_field(DeclType::ExposeDirectory, "target"),
5099                Error::missing_field(DeclType::ExposeDirectory, "source_name"),
5100                Error::missing_field(DeclType::ExposeDirectory, "target_name"),
5101                Error::missing_field(DeclType::ExposeRunner, "source"),
5102                Error::missing_field(DeclType::ExposeRunner, "target"),
5103                Error::missing_field(DeclType::ExposeRunner, "source_name"),
5104                Error::missing_field(DeclType::ExposeRunner, "target_name"),
5105                Error::missing_field(DeclType::ExposeResolver, "source"),
5106                Error::missing_field(DeclType::ExposeResolver, "target"),
5107                Error::missing_field(DeclType::ExposeResolver, "source_name"),
5108                Error::missing_field(DeclType::ExposeResolver, "target_name"),
5109                Error::missing_field(DeclType::ExposeDictionary, "source"),
5110                Error::missing_field(DeclType::ExposeDictionary, "target"),
5111                Error::missing_field(DeclType::ExposeDictionary, "source_name"),
5112                Error::missing_field(DeclType::ExposeDictionary, "target_name"),
5113            ])),
5114        },
5115        test_validate_exposes_extraneous => {
5116            input = {
5117                let mut decl = new_component_decl();
5118                decl.exposes = Some(vec![
5119                    fdecl::Expose::Service(fdecl::ExposeService {
5120                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5121                            name: "logger".to_string(),
5122                            collection: Some("modular".to_string()),
5123                        })),
5124                        source_name: Some("logger".to_string()),
5125                        target_name: Some("logger".to_string()),
5126                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5127                        ..Default::default()
5128                    }),
5129                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5130                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5131                            name: "logger".to_string(),
5132                            collection: Some("modular".to_string()),
5133                        })),
5134                        source_name: Some("legacy_logger".to_string()),
5135                        target_name: Some("legacy_logger".to_string()),
5136                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5137                        ..Default::default()
5138                    }),
5139                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5140                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5141                            name: "netstack".to_string(),
5142                            collection: Some("modular".to_string()),
5143                        })),
5144                        source_name: Some("data".to_string()),
5145                        target_name: Some("data".to_string()),
5146                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5147                        rights: Some(fio::Operations::CONNECT),
5148                        subdir: None,
5149                        ..Default::default()
5150                    }),
5151                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5152                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5153                            name: "netstack".to_string(),
5154                            collection: Some("modular".to_string()),
5155                        })),
5156                        source_name: Some("elf".to_string()),
5157                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5158                        target_name: Some("elf".to_string()),
5159                        ..Default::default()
5160                    }),
5161                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5162                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5163                            name: "netstack".to_string(),
5164                            collection: Some("modular".to_string()),
5165                        })),
5166                        source_name: Some("pkg".to_string()),
5167                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5168                        target_name: Some("pkg".to_string()),
5169                        ..Default::default()
5170                    }),
5171                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5172                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5173                            name: "netstack".to_string(),
5174                            collection: Some("modular".to_string()),
5175                        })),
5176                        source_name: Some("dict".to_string()),
5177                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5178                        target_name: Some("dict".to_string()),
5179                        ..Default::default()
5180                    }),
5181                ]);
5182                decl
5183            },
5184            result = Err(ErrorList::new(vec![
5185                Error::extraneous_field(DeclType::ExposeService, "source.child.collection"),
5186                Error::extraneous_field(DeclType::ExposeProtocol, "source.child.collection"),
5187                Error::extraneous_field(DeclType::ExposeDirectory, "source.child.collection"),
5188                Error::extraneous_field(DeclType::ExposeRunner, "source.child.collection"),
5189                Error::extraneous_field(DeclType::ExposeResolver, "source.child.collection"),
5190                Error::extraneous_field(DeclType::ExposeDictionary, "source.child.collection"),
5191            ])),
5192        },
5193        test_validate_exposes_invalid_identifiers => {
5194            input = {
5195                let mut decl = new_component_decl();
5196                decl.exposes = Some(vec![
5197                    fdecl::Expose::Service(fdecl::ExposeService {
5198                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5199                            name: "^bad".to_string(),
5200                            collection: None,
5201                        })),
5202                        source_name: Some("foo/".to_string()),
5203                        target_name: Some("/".to_string()),
5204                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5205                        ..Default::default()
5206                    }),
5207                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5208                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5209                            name: "^bad".to_string(),
5210                            collection: None,
5211                        })),
5212                        source_name: Some("foo/".to_string()),
5213                        target_name: Some("/".to_string()),
5214                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5215                        ..Default::default()
5216                    }),
5217                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5218                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5219                            name: "^bad".to_string(),
5220                            collection: None,
5221                        })),
5222                        source_name: Some("foo/".to_string()),
5223                        target_name: Some("/".to_string()),
5224                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5225                        rights: Some(fio::Operations::CONNECT),
5226                        subdir: Some("/foo".to_string()),
5227                        ..Default::default()
5228                    }),
5229                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5230                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5231                            name: "^bad".to_string(),
5232                            collection: None,
5233                        })),
5234                        source_name: Some("/path".to_string()),
5235                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5236                        target_name: Some("elf!".to_string()),
5237                        ..Default::default()
5238                    }),
5239                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5240                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5241                            name: "^bad".to_string(),
5242                            collection: None,
5243                        })),
5244                        source_name: Some("/path".to_string()),
5245                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5246                        target_name: Some("pkg!".to_string()),
5247                        ..Default::default()
5248                    }),
5249                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5250                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5251                            name: "^bad".to_string(),
5252                            collection: None,
5253                        })),
5254                        source_name: Some("/path".to_string()),
5255                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5256                        target_name: Some("pkg!".to_string()),
5257                        ..Default::default()
5258                    }),
5259                ]);
5260                decl
5261            },
5262            result = Err(ErrorList::new(vec![
5263                Error::invalid_field(DeclType::ExposeService, "source.child.name"),
5264                Error::invalid_field(DeclType::ExposeService, "source_name"),
5265                Error::invalid_field(DeclType::ExposeService, "target_name"),
5266                Error::invalid_field(DeclType::ExposeProtocol, "source.child.name"),
5267                Error::invalid_field(DeclType::ExposeProtocol, "source_name"),
5268                Error::invalid_field(DeclType::ExposeProtocol, "target_name"),
5269                Error::invalid_field(DeclType::ExposeDirectory, "source.child.name"),
5270                Error::invalid_field(DeclType::ExposeDirectory, "source_name"),
5271                Error::invalid_field(DeclType::ExposeDirectory, "target_name"),
5272                Error::invalid_field(DeclType::ExposeDirectory, "subdir"),
5273                Error::invalid_field(DeclType::ExposeRunner, "source.child.name"),
5274                Error::invalid_field(DeclType::ExposeRunner, "source_name"),
5275                Error::invalid_field(DeclType::ExposeRunner, "target_name"),
5276                Error::invalid_field(DeclType::ExposeResolver, "source.child.name"),
5277                Error::invalid_field(DeclType::ExposeResolver, "source_name"),
5278                Error::invalid_field(DeclType::ExposeResolver, "target_name"),
5279                Error::invalid_field(DeclType::ExposeDictionary, "source.child.name"),
5280                Error::invalid_field(DeclType::ExposeDictionary, "source_name"),
5281                Error::invalid_field(DeclType::ExposeDictionary, "target_name"),
5282            ])),
5283        },
5284        test_validate_exposes_invalid_source_target => {
5285            input = {
5286                let mut decl = new_component_decl();
5287                decl.children = Some(vec![fdecl::Child{
5288                    name: Some("logger".to_string()),
5289                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
5290                    startup: Some(fdecl::StartupMode::Lazy),
5291                    on_terminate: None,
5292                    environment: None,
5293                    ..Default::default()
5294                }]);
5295                decl.exposes = Some(vec![
5296                    fdecl::Expose::Service(fdecl::ExposeService {
5297                        source: None,
5298                        source_name: Some("a".to_string()),
5299                        target_name: Some("b".to_string()),
5300                        target: None,
5301                        ..Default::default()
5302                    }),
5303                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5304                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5305                        source_name: Some("c".to_string()),
5306                        target_name: Some("d".to_string()),
5307                        target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5308                        ..Default::default()
5309                    }),
5310                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5311                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "z".to_string()})),
5312                        source_name: Some("e".to_string()),
5313                        target_name: Some("f".to_string()),
5314                        target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "z".to_string()})),
5315                        rights: Some(fio::Operations::CONNECT),
5316                        subdir: None,
5317                        ..Default::default()
5318                    }),
5319                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5320                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5321                        source_name: Some("g".to_string()),
5322                        target_name: Some("h".to_string()),
5323                        target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5324                        rights: Some(fio::Operations::CONNECT),
5325                        subdir: None,
5326                        ..Default::default()
5327                    }),
5328                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5329                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5330                        source_name: Some("i".to_string()),
5331                        target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5332                        target_name: Some("j".to_string()),
5333                        ..Default::default()
5334                    }),
5335                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5336                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5337                        source_name: Some("k".to_string()),
5338                        target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5339                        target_name: Some("l".to_string()),
5340                        ..Default::default()
5341                    }),
5342                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5343                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5344                            name: "logger".to_string(),
5345                            collection: None,
5346                        })),
5347                        source_name: Some("m".to_string()),
5348                        target_name: Some("n".to_string()),
5349                        target: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5350                        ..Default::default()
5351                    }),
5352                ]);
5353                decl
5354            },
5355            result = Err(ErrorList::new(vec![
5356                Error::missing_field(DeclType::ExposeService, "source"),
5357                Error::missing_field(DeclType::ExposeService, "target"),
5358                Error::invalid_field(DeclType::ExposeProtocol, "source"),
5359                Error::invalid_field(DeclType::ExposeProtocol, "target"),
5360                Error::invalid_field(DeclType::ExposeDirectory, "source"),
5361                Error::invalid_field(DeclType::ExposeDirectory, "target"),
5362                Error::invalid_field(DeclType::ExposeDirectory, "source"),
5363                Error::invalid_field(DeclType::ExposeDirectory, "target"),
5364                Error::invalid_field(DeclType::ExposeRunner, "source"),
5365                Error::invalid_field(DeclType::ExposeRunner, "target"),
5366                Error::invalid_field(DeclType::ExposeResolver, "source"),
5367                Error::invalid_field(DeclType::ExposeResolver, "target"),
5368                Error::invalid_field(DeclType::ExposeDictionary, "target"),
5369            ])),
5370        },
5371        test_validate_exposes_invalid_source_collection => {
5372            input = {
5373                let mut decl = new_component_decl();
5374                decl.collections = Some(vec![fdecl::Collection{
5375                    name: Some("col".to_string()),
5376                    durability: Some(fdecl::Durability::Transient),
5377                    allowed_offers: None,
5378                    allow_long_names: None,
5379                    ..Default::default()
5380                }]);
5381                decl.exposes = Some(vec![
5382                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5383                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
5384                        source_name: Some("a".to_string()),
5385                        target_name: Some("a".to_string()),
5386                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5387                        ..Default::default()
5388                    }),
5389                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5390                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5391                        source_name: Some("b".to_string()),
5392                        target_name: Some("b".to_string()),
5393                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5394                        rights: Some(fio::Operations::CONNECT),
5395                        subdir: None,
5396                        ..Default::default()
5397                    }),
5398                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5399                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5400                        source_name: Some("c".to_string()),
5401                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5402                        target_name: Some("c".to_string()),
5403                        ..Default::default()
5404                    }),
5405                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5406                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5407                        source_name: Some("d".to_string()),
5408                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5409                        target_name: Some("d".to_string()),
5410                        ..Default::default()
5411                    }),
5412                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5413                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {name: "col".to_string()})),
5414                        source_name: Some("e".to_string()),
5415                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5416                        target_name: Some("e".to_string()),
5417                        ..Default::default()
5418                    }),
5419                ]);
5420                decl
5421            },
5422            result = Err(ErrorList::new(vec![
5423                Error::invalid_field(DeclType::ExposeProtocol, "source"),
5424                Error::invalid_field(DeclType::ExposeDirectory, "source"),
5425                Error::invalid_field(DeclType::ExposeRunner, "source"),
5426                Error::invalid_field(DeclType::ExposeResolver, "source"),
5427                Error::invalid_field(DeclType::ExposeDictionary, "source"),
5428            ])),
5429        },
5430        test_validate_exposes_sources_collection => {
5431            input = {
5432                let mut decl = new_component_decl();
5433                decl.collections = Some(vec![
5434                    fdecl::Collection {
5435                        name: Some("col".to_string()),
5436                        durability: Some(fdecl::Durability::Transient),
5437                        allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
5438                        allow_long_names: None,
5439                        ..Default::default()
5440                    }
5441                ]);
5442                decl.exposes = Some(vec![
5443                    fdecl::Expose::Service(fdecl::ExposeService {
5444                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
5445                        source_name: Some("a".to_string()),
5446                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5447                        target_name: Some("a".to_string()),
5448                        ..Default::default()
5449                    })
5450                ]);
5451                decl
5452            },
5453            result = Ok(()),
5454        },
5455        test_validate_exposes_long_identifiers => {
5456            input = {
5457                let mut decl = new_component_decl();
5458                decl.exposes = Some(vec![
5459                    fdecl::Expose::Service(fdecl::ExposeService {
5460                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5461                            name: "b".repeat(256),
5462                            collection: None,
5463                        })),
5464                        source_name: Some(format!("{}", "a".repeat(1025))),
5465                        target_name: Some(format!("{}", "b".repeat(1025))),
5466                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5467                        ..Default::default()
5468                    }),
5469                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5470                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5471                            name: "b".repeat(256),
5472                            collection: None,
5473                        })),
5474                        source_name: Some(format!("{}", "a".repeat(256))),
5475                        target_name: Some(format!("{}", "b".repeat(256))),
5476                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5477                        ..Default::default()
5478                    }),
5479                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5480                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5481                            name: "b".repeat(256),
5482                            collection: None,
5483                        })),
5484                        source_name: Some(format!("{}", "a".repeat(256))),
5485                        target_name: Some(format!("{}", "b".repeat(256))),
5486                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5487                        rights: Some(fio::Operations::CONNECT),
5488                        subdir: None,
5489                        ..Default::default()
5490                    }),
5491                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5492                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5493                            name: "b".repeat(256),
5494                            collection: None,
5495                        })),
5496                        source_name: Some("a".repeat(256)),
5497                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5498                        target_name: Some("b".repeat(256)),
5499                        ..Default::default()
5500                    }),
5501                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5502                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5503                            name: "b".repeat(256),
5504                            collection: None,
5505                        })),
5506                        source_name: Some("a".repeat(256)),
5507                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5508                        target_name: Some("b".repeat(256)),
5509                        ..Default::default()
5510                    }),
5511                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5512                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5513                            name: "b".repeat(256),
5514                            collection: None,
5515                        })),
5516                        source_name: Some("a".repeat(256)),
5517                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5518                        target_name: Some("b".repeat(256)),
5519                        ..Default::default()
5520                    }),
5521                ]);
5522                decl
5523            },
5524            result = Err(ErrorList::new(vec![
5525                Error::field_too_long(DeclType::ExposeService, "source.child.name"),
5526                Error::field_too_long(DeclType::ExposeService, "source_name"),
5527                Error::field_too_long(DeclType::ExposeService, "target_name"),
5528                Error::field_too_long(DeclType::ExposeProtocol, "source.child.name"),
5529                Error::field_too_long(DeclType::ExposeProtocol, "source_name"),
5530                Error::field_too_long(DeclType::ExposeProtocol, "target_name"),
5531                Error::field_too_long(DeclType::ExposeDirectory, "source.child.name"),
5532                Error::field_too_long(DeclType::ExposeDirectory, "source_name"),
5533                Error::field_too_long(DeclType::ExposeDirectory, "target_name"),
5534                Error::field_too_long(DeclType::ExposeRunner, "source.child.name"),
5535                Error::field_too_long(DeclType::ExposeRunner, "source_name"),
5536                Error::field_too_long(DeclType::ExposeRunner, "target_name"),
5537                Error::field_too_long(DeclType::ExposeResolver, "source.child.name"),
5538                Error::field_too_long(DeclType::ExposeResolver, "source_name"),
5539                Error::field_too_long(DeclType::ExposeResolver, "target_name"),
5540                Error::field_too_long(DeclType::ExposeDictionary, "source.child.name"),
5541                Error::field_too_long(DeclType::ExposeDictionary, "source_name"),
5542                Error::field_too_long(DeclType::ExposeDictionary, "target_name"),
5543            ])),
5544        },
5545        test_validate_exposes_invalid_child => {
5546            input = {
5547                let mut decl = new_component_decl();
5548                decl.exposes = Some(vec![
5549                    fdecl::Expose::Service(fdecl::ExposeService {
5550                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5551                            name: "netstack".to_string(),
5552                            collection: None,
5553                        })),
5554                        source_name: Some("fuchsia.logger.Log".to_string()),
5555                        target_name: Some("fuchsia.logger.Log".to_string()),
5556                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5557                        ..Default::default()
5558                    }),
5559                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5560                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5561                            name: "netstack".to_string(),
5562                            collection: None,
5563                        })),
5564                        source_name: Some("fuchsia.logger.LegacyLog".to_string()),
5565                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
5566                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5567                        ..Default::default()
5568                    }),
5569                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5570                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5571                            name: "netstack".to_string(),
5572                            collection: None,
5573                        })),
5574                        source_name: Some("data".to_string()),
5575                        target_name: Some("data".to_string()),
5576                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5577                        rights: Some(fio::Operations::CONNECT),
5578                        subdir: None,
5579                        ..Default::default()
5580                    }),
5581                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5582                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5583                            name: "netstack".to_string(),
5584                            collection: None,
5585                        })),
5586                        source_name: Some("elf".to_string()),
5587                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5588                        target_name: Some("elf".to_string()),
5589                        ..Default::default()
5590                    }),
5591                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5592                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5593                            name: "netstack".to_string(),
5594                            collection: None,
5595                        })),
5596                        source_name: Some("pkg".to_string()),
5597                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5598                        target_name: Some("pkg".to_string()),
5599                        ..Default::default()
5600                    }),
5601                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5602                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5603                            name: "netstack".to_string(),
5604                            collection: None,
5605                        })),
5606                        source_name: Some("dict".to_string()),
5607                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5608                        target_name: Some("dict".to_string()),
5609                        ..Default::default()
5610                    }),
5611                ]);
5612                decl
5613            },
5614            result = Err(ErrorList::new(vec![
5615                Error::invalid_child(DeclType::ExposeService, "source", "netstack"),
5616                Error::invalid_child(DeclType::ExposeProtocol, "source", "netstack"),
5617                Error::invalid_child(DeclType::ExposeDirectory, "source", "netstack"),
5618                Error::invalid_child(DeclType::ExposeRunner, "source", "netstack"),
5619                Error::invalid_child(DeclType::ExposeResolver, "source", "netstack"),
5620                Error::invalid_child(DeclType::ExposeDictionary, "source", "netstack"),
5621            ])),
5622        },
5623        test_validate_exposes_invalid_source_capability => {
5624            input = {
5625                fdecl::Component {
5626                    exposes: Some(vec![
5627                        fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5628                            source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
5629                                name: "this-storage-doesnt-exist".to_string(),
5630                            })),
5631                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
5632                            target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
5633                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5634                            ..Default::default()
5635                        }),
5636                    ]),
5637                    ..new_component_decl()
5638                }
5639            },
5640            result = Err(ErrorList::new(vec![
5641                Error::invalid_capability(DeclType::ExposeProtocol, "source", "this-storage-doesnt-exist"),
5642            ])),
5643        },
5644        test_validate_exposes_duplicate_target => {
5645            input = {
5646                let mut decl = new_component_decl();
5647                decl.exposes = Some(vec![
5648                    fdecl::Expose::Service(fdecl::ExposeService {
5649                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
5650                            name: "coll".into(),
5651                        })),
5652                        source_name: Some("netstack".to_string()),
5653                        target_name: Some("fuchsia.net.Stack".to_string()),
5654                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5655                        ..Default::default()
5656                    }),
5657                    fdecl::Expose::Service(fdecl::ExposeService {
5658                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
5659                            name: "coll2".into(),
5660                        })),
5661                        source_name: Some("netstack2".to_string()),
5662                        target_name: Some("fuchsia.net.Stack".to_string()),
5663                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5664                        ..Default::default()
5665                    }),
5666                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5667                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5668                        source_name: Some("fonts".to_string()),
5669                        target_name: Some("fuchsia.fonts.Provider".to_string()),
5670                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5671                        ..Default::default()
5672                    }),
5673                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5674                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5675                        source_name: Some("fonts2".to_string()),
5676                        target_name: Some("fuchsia.fonts.Provider".to_string()),
5677                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5678                        ..Default::default()
5679                    }),
5680                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5681                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5682                        source_name: Some("assets".to_string()),
5683                        target_name: Some("stuff".to_string()),
5684                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5685                        rights: None,
5686                        subdir: None,
5687                        ..Default::default()
5688                    }),
5689                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5690                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5691                        source_name: Some("assets2".to_string()),
5692                        target_name: Some("stuff".to_string()),
5693                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5694                        rights: None,
5695                        subdir: None,
5696                        ..Default::default()
5697                    }),
5698                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5699                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5700                        source_name: Some("source_elf".to_string()),
5701                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5702                        target_name: Some("elf".to_string()),
5703                        ..Default::default()
5704                    }),
5705                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5706                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5707                        source_name: Some("source_elf".to_string()),
5708                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5709                        target_name: Some("elf".to_string()),
5710                        ..Default::default()
5711                    }),
5712                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5713                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5714                        source_name: Some("source_pkg".to_string()),
5715                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5716                        target_name: Some("pkg".to_string()),
5717                        ..Default::default()
5718                    }),
5719                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5720                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5721                        source_name: Some("source_pkg".to_string()),
5722                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5723                        target_name: Some("pkg".to_string()),
5724                        ..Default::default()
5725                    }),
5726                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5727                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5728                        source_name: Some("source_dict".to_string()),
5729                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5730                        target_name: Some("dict".to_string()),
5731                        ..Default::default()
5732                    }),
5733                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5734                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5735                        source_name: Some("source_dict".to_string()),
5736                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5737                        target_name: Some("dict".to_string()),
5738                        ..Default::default()
5739                    }),
5740                ]);
5741                decl.collections = Some(vec![
5742                    fdecl::Collection {
5743                        name: Some("coll".into()),
5744                        durability: Some(fdecl::Durability::Transient),
5745                        ..Default::default()
5746                    },
5747                    fdecl::Collection {
5748                        name: Some("coll2".into()),
5749                        durability: Some(fdecl::Durability::Transient),
5750                        ..Default::default()
5751                    },
5752                ]);
5753                decl.capabilities = Some(vec![
5754                    fdecl::Capability::Service(fdecl::Service {
5755                        name: Some("netstack".to_string()),
5756                        source_path: Some("/path".to_string()),
5757                        ..Default::default()
5758                    }),
5759                    fdecl::Capability::Service(fdecl::Service {
5760                        name: Some("netstack2".to_string()),
5761                        source_path: Some("/path".to_string()),
5762                        ..Default::default()
5763                    }),
5764                    fdecl::Capability::Protocol(fdecl::Protocol {
5765                        name: Some("fonts".to_string()),
5766                        source_path: Some("/path".to_string()),
5767                        ..Default::default()
5768                    }),
5769                    fdecl::Capability::Protocol(fdecl::Protocol {
5770                        name: Some("fonts2".to_string()),
5771                        source_path: Some("/path".to_string()),
5772                        ..Default::default()
5773                    }),
5774                    fdecl::Capability::Directory(fdecl::Directory {
5775                        name: Some("assets".to_string()),
5776                        source_path: Some("/path".to_string()),
5777                        rights: Some(fio::Operations::CONNECT),
5778                        ..Default::default()
5779                    }),
5780                    fdecl::Capability::Directory(fdecl::Directory {
5781                        name: Some("assets2".to_string()),
5782                        source_path: Some("/path".to_string()),
5783                        rights: Some(fio::Operations::CONNECT),
5784                        ..Default::default()
5785                    }),
5786                    fdecl::Capability::Runner(fdecl::Runner {
5787                        name: Some("source_elf".to_string()),
5788                        source_path: Some("/path".to_string()),
5789                        ..Default::default()
5790                    }),
5791                    fdecl::Capability::Resolver(fdecl::Resolver {
5792                        name: Some("source_pkg".to_string()),
5793                        source_path: Some("/path".to_string()),
5794                        ..Default::default()
5795                    }),
5796                    fdecl::Capability::Dictionary(fdecl::Dictionary {
5797                        name: Some("source_dict".to_string()),
5798                        ..Default::default()
5799                    }),
5800                ]);
5801                decl
5802            },
5803            result = Err(ErrorList::new(vec![
5804                // Duplicate services are allowed.
5805                Error::duplicate_field(DeclType::ExposeProtocol, "target_name",
5806                                    "fuchsia.fonts.Provider"),
5807                Error::duplicate_field(DeclType::ExposeDirectory, "target_name",
5808                                    "stuff"),
5809                Error::duplicate_field(DeclType::ExposeRunner, "target_name",
5810                                    "elf"),
5811                Error::duplicate_field(DeclType::ExposeResolver, "target_name", "pkg"),
5812                Error::duplicate_field(DeclType::ExposeDictionary, "target_name", "dict"),
5813            ])),
5814        },
5815        test_validate_exposes_invalid_capability_from_self => {
5816            input = {
5817                let mut decl = new_component_decl();
5818                decl.exposes = Some(vec![
5819                    fdecl::Expose::Service(fdecl::ExposeService {
5820                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5821                        source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
5822                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5823                        target_name: Some("foo".to_string()),
5824                        ..Default::default()
5825                    }),
5826                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5827                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5828                        source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
5829                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5830                        target_name: Some("bar".to_string()),
5831                        ..Default::default()
5832                    }),
5833                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
5834                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5835                        source_name: Some("dir".to_string()),
5836                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5837                        target_name: Some("assets".to_string()),
5838                        rights: None,
5839                        subdir: None,
5840                        ..Default::default()
5841                    }),
5842                    fdecl::Expose::Runner(fdecl::ExposeRunner {
5843                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5844                        source_name: Some("source_elf".to_string()),
5845                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5846                        target_name: Some("elf".to_string()),
5847                        ..Default::default()
5848                    }),
5849                    fdecl::Expose::Resolver(fdecl::ExposeResolver {
5850                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5851                        source_name: Some("source_pkg".to_string()),
5852                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5853                        target_name: Some("pkg".to_string()),
5854                        ..Default::default()
5855                    }),
5856                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
5857                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5858                        source_name: Some("source_dict".to_string()),
5859                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5860                        target_name: Some("dict".to_string()),
5861                        ..Default::default()
5862                    }),
5863                    fdecl::Expose::Config(fdecl::ExposeConfiguration {
5864                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5865                        source_name: Some("source_config".to_string()),
5866                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5867                        target_name: Some("config".to_string()),
5868                        ..Default::default()
5869                    }),
5870                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5871                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
5872                        source_name: Some("fuchsia.some.library.SomeProtocol".to_string()),
5873                        source_dictionary: Some("dict/inner".to_string()),
5874                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5875                        target_name: Some("baz".to_string()),
5876                        ..Default::default()
5877                    }),
5878                ]);
5879                decl
5880            },
5881            result = Err(ErrorList::new(vec![
5882                Error::invalid_capability(
5883                    DeclType::ExposeService,
5884                    "source",
5885                    "fuchsia.some.library.SomeProtocol"),
5886                Error::invalid_capability(
5887                    DeclType::ExposeProtocol,
5888                    "source",
5889                    "fuchsia.some.library.SomeProtocol"),
5890                Error::invalid_capability(DeclType::ExposeDirectory, "source", "dir"),
5891                Error::invalid_capability(DeclType::ExposeRunner, "source", "source_elf"),
5892                Error::invalid_capability(DeclType::ExposeResolver, "source", "source_pkg"),
5893                Error::invalid_capability(DeclType::ExposeDictionary, "source", "source_dict"),
5894                Error::invalid_capability(DeclType::ExposeConfig, "source", "source_config"),
5895                Error::invalid_capability(DeclType::ExposeProtocol, "source", "dict"),
5896            ])),
5897        },
5898
5899        test_validate_exposes_availability_service => {
5900            input = {
5901                let mut decl = generate_expose_different_source_and_availability_decl(
5902                    |source, availability, target_name|
5903                        fdecl::Expose::Service(fdecl::ExposeService {
5904                            source: Some(source),
5905                            source_name: Some("fuchsia.examples.Echo".to_string()),
5906                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5907                            target_name: Some(target_name.to_string()),
5908                            availability: Some(availability),
5909                            ..Default::default()
5910                        })
5911                );
5912                decl.capabilities = Some(vec![
5913                    fdecl::Capability::Service(fdecl::Service {
5914                        name: Some("fuchsia.examples.Echo".to_string()),
5915                        source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
5916                        ..Default::default()
5917                    }),
5918                ]);
5919                decl
5920            },
5921            result = {
5922                Err(ErrorList::new(vec![
5923                    Error::availability_must_be_optional(
5924                        DeclType::ExposeService,
5925                        "availability",
5926                        Some(&"fuchsia.examples.Echo".to_string()),
5927                    ),
5928                    Error::availability_must_be_optional(
5929                        DeclType::ExposeService,
5930                        "availability",
5931                        Some(&"fuchsia.examples.Echo".to_string()),
5932                    ),
5933                ]))
5934            },
5935        },
5936        test_validate_exposes_availability_protocol => {
5937            input = {
5938                let mut decl = generate_expose_different_source_and_availability_decl(
5939                    |source, availability, target_name|
5940                        fdecl::Expose::Protocol(fdecl::ExposeProtocol {
5941                            source: Some(source),
5942                            source_name: Some("fuchsia.examples.Echo".to_string()),
5943                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5944                            target_name: Some(target_name.to_string()),
5945                            availability: Some(availability),
5946                            ..Default::default()
5947                        })
5948                );
5949                decl.capabilities = Some(vec![
5950                    fdecl::Capability::Protocol(fdecl::Protocol {
5951                        name: Some("fuchsia.examples.Echo".to_string()),
5952                        source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
5953                        ..Default::default()
5954                    }),
5955                ]);
5956                decl
5957            },
5958            result = {
5959                Err(ErrorList::new(vec![
5960                    Error::availability_must_be_optional(
5961                        DeclType::ExposeProtocol,
5962                        "availability",
5963                        Some(&"fuchsia.examples.Echo".to_string()),
5964                    ),
5965                    Error::availability_must_be_optional(
5966                        DeclType::ExposeProtocol,
5967                        "availability",
5968                        Some(&"fuchsia.examples.Echo".to_string()),
5969                    ),
5970                ]))
5971            },
5972        },
5973        test_validate_exposes_availability_directory => {
5974            input = {
5975                let mut decl = generate_expose_different_source_and_availability_decl(
5976                    |source, availability, target_name|
5977                        fdecl::Expose::Directory(fdecl::ExposeDirectory {
5978                            source: Some(source),
5979                            source_name: Some("fuchsia.examples.Echo".to_string()),
5980                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5981                            target_name: Some(target_name.to_string()),
5982                            availability: Some(availability),
5983                            ..Default::default()
5984                        })
5985                );
5986                decl.capabilities = Some(vec![
5987                    fdecl::Capability::Directory(fdecl::Directory {
5988                        name: Some("fuchsia.examples.Echo".to_string()),
5989                        source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
5990                        rights: Some(fio::Operations::READ_BYTES),
5991                        ..Default::default()
5992                    }),
5993                ]);
5994                decl
5995            },
5996            result = {
5997                Err(ErrorList::new(vec![
5998                    Error::availability_must_be_optional(
5999                        DeclType::ExposeDirectory,
6000                        "availability",
6001                        Some(&"fuchsia.examples.Echo".to_string()),
6002                    ),
6003                    Error::availability_must_be_optional(
6004                        DeclType::ExposeDirectory,
6005                        "availability",
6006                        Some(&"fuchsia.examples.Echo".to_string()),
6007                    ),
6008                ]))
6009            },
6010        },
6011
6012        // offers
6013        test_validate_offers_empty => {
6014            input = {
6015                let mut decl = new_component_decl();
6016                decl.offers = Some(vec![
6017                    fdecl::Offer::Service(fdecl::OfferService {
6018                        source: None,
6019                        source_name: None,
6020                        target: None,
6021                        target_name: None,
6022                        ..Default::default()
6023                    }),
6024                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6025                        source: None,
6026                        source_name: None,
6027                        target: None,
6028                        target_name: None,
6029                        dependency_type: None,
6030                        ..Default::default()
6031                    }),
6032                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6033                        source: None,
6034                        source_name: None,
6035                        target: None,
6036                        target_name: None,
6037                        rights: None,
6038                        subdir: None,
6039                        dependency_type: None,
6040                        ..Default::default()
6041                    }),
6042                    fdecl::Offer::Storage(fdecl::OfferStorage {
6043                        source_name: None,
6044                        source: None,
6045                        target: None,
6046                        target_name: None,
6047                        ..Default::default()
6048                    }),
6049                    fdecl::Offer::Runner(fdecl::OfferRunner {
6050                        source: None,
6051                        source_name: None,
6052                        target: None,
6053                        target_name: None,
6054                        ..Default::default()
6055                    }),
6056                    fdecl::Offer::Resolver(fdecl::OfferResolver {
6057                        ..Default::default()
6058                    }),
6059                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6060                        ..Default::default()
6061                    }),
6062                ]);
6063                decl
6064            },
6065            // TODO(dgonyeo): we need to handle the availability being unset until we've soft
6066            // migrated all manifests
6067            result = Err(ErrorList::new(vec![
6068                Error::missing_field(DeclType::OfferService, "source"),
6069                Error::missing_field(DeclType::OfferService, "source_name"),
6070                Error::missing_field(DeclType::OfferService, "target"),
6071                Error::missing_field(DeclType::OfferService, "target_name"),
6072                //Error::missing_field(DeclType::OfferService, "availability"),
6073                Error::missing_field(DeclType::OfferProtocol, "source"),
6074                Error::missing_field(DeclType::OfferProtocol, "source_name"),
6075                Error::missing_field(DeclType::OfferProtocol, "target"),
6076                Error::missing_field(DeclType::OfferProtocol, "target_name"),
6077                Error::missing_field(DeclType::OfferProtocol, "dependency_type"),
6078                //Error::missing_field(DeclType::OfferProtocol, "availability"),
6079                Error::missing_field(DeclType::OfferDirectory, "source"),
6080                Error::missing_field(DeclType::OfferDirectory, "source_name"),
6081                Error::missing_field(DeclType::OfferDirectory, "target"),
6082                Error::missing_field(DeclType::OfferDirectory, "target_name"),
6083                Error::missing_field(DeclType::OfferDirectory, "dependency_type"),
6084                //Error::missing_field(DeclType::OfferDirectory, "availability"),
6085                Error::missing_field(DeclType::OfferStorage, "source"),
6086                Error::missing_field(DeclType::OfferStorage, "source_name"),
6087                Error::missing_field(DeclType::OfferStorage, "target"),
6088                Error::missing_field(DeclType::OfferStorage, "target_name"),
6089                //Error::missing_field(DeclType::OfferStorage, "availability"),
6090                Error::missing_field(DeclType::OfferRunner, "source"),
6091                Error::missing_field(DeclType::OfferRunner, "source_name"),
6092                Error::missing_field(DeclType::OfferRunner, "target"),
6093                Error::missing_field(DeclType::OfferRunner, "target_name"),
6094                //Error::missing_field(DeclType::OfferRunner, "availability"),
6095                Error::missing_field(DeclType::OfferResolver, "source"),
6096                Error::missing_field(DeclType::OfferResolver, "source_name"),
6097                Error::missing_field(DeclType::OfferResolver, "target"),
6098                Error::missing_field(DeclType::OfferResolver, "target_name"),
6099                Error::missing_field(DeclType::OfferDictionary, "source"),
6100                Error::missing_field(DeclType::OfferDictionary, "source_name"),
6101                Error::missing_field(DeclType::OfferDictionary, "target"),
6102                Error::missing_field(DeclType::OfferDictionary, "target_name"),
6103                Error::missing_field(DeclType::OfferDictionary, "dependency_type"),
6104            ])),
6105        },
6106        test_validate_offers_long_identifiers => {
6107            input = {
6108                let mut decl = new_component_decl();
6109                decl.offers = Some(vec![
6110                    fdecl::Offer::Service(fdecl::OfferService {
6111                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6112                            name: "a".repeat(256),
6113                            collection: None,
6114                        })),
6115                        source_name: Some(format!("{}", "a".repeat(256))),
6116                        target: Some(fdecl::Ref::Child(
6117                        fdecl::ChildRef {
6118                            name: "b".repeat(256),
6119                            collection: None,
6120                        }
6121                        )),
6122                        target_name: Some(format!("{}", "b".repeat(256))),
6123                        ..Default::default()
6124                    }),
6125                    fdecl::Offer::Service(fdecl::OfferService {
6126                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6127                        source_name: Some("a".to_string()),
6128                        target: Some(fdecl::Ref::Collection(
6129                        fdecl::CollectionRef {
6130                            name: "b".repeat(256),
6131                        }
6132                        )),
6133                        target_name: Some(format!("{}", "b".repeat(256))),
6134                        ..Default::default()
6135                    }),
6136                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6137                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6138                            name: "a".repeat(256),
6139                            collection: None,
6140                        })),
6141                        source_name: Some(format!("{}", "a".repeat(256))),
6142                        target: Some(fdecl::Ref::Child(
6143                        fdecl::ChildRef {
6144                            name: "b".repeat(256),
6145                            collection: None,
6146                        }
6147                        )),
6148                        target_name: Some(format!("{}", "b".repeat(256))),
6149                        dependency_type: Some(fdecl::DependencyType::Strong),
6150                        ..Default::default()
6151                    }),
6152                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6153                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6154                        source_name: Some("a".to_string()),
6155                        target: Some(fdecl::Ref::Collection(
6156                        fdecl::CollectionRef {
6157                            name: "b".repeat(256),
6158                        }
6159                        )),
6160                        target_name: Some(format!("{}", "b".repeat(256))),
6161                        dependency_type: Some(fdecl::DependencyType::Weak),
6162                        ..Default::default()
6163                    }),
6164                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6165                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6166                            name: "a".repeat(256),
6167                            collection: None,
6168                        })),
6169                        source_name: Some(format!("{}", "a".repeat(256))),
6170                        target: Some(fdecl::Ref::Child(
6171                        fdecl::ChildRef {
6172                            name: "b".repeat(256),
6173                            collection: None,
6174                        }
6175                        )),
6176                        target_name: Some(format!("{}", "b".repeat(256))),
6177                        rights: Some(fio::Operations::CONNECT),
6178                        subdir: None,
6179                        dependency_type: Some(fdecl::DependencyType::Strong),
6180                        ..Default::default()
6181                    }),
6182                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6183                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6184                        source_name: Some("a".to_string()),
6185                        target: Some(fdecl::Ref::Collection(
6186                        fdecl::CollectionRef {
6187                            name: "b".repeat(256),
6188                        }
6189                        )),
6190                        target_name: Some(format!("{}", "b".repeat(256))),
6191                        rights: Some(fio::Operations::CONNECT),
6192                        subdir: None,
6193                        dependency_type: Some(fdecl::DependencyType::Weak),
6194                        ..Default::default()
6195                    }),
6196                    fdecl::Offer::Storage(fdecl::OfferStorage {
6197                        source_name: Some("data".to_string()),
6198                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6199                        target: Some(fdecl::Ref::Child(
6200                            fdecl::ChildRef {
6201                                name: "b".repeat(256),
6202                                collection: None,
6203                            }
6204                        )),
6205                        target_name: Some("data".to_string()),
6206                        ..Default::default()
6207                    }),
6208                    fdecl::Offer::Storage(fdecl::OfferStorage {
6209                        source_name: Some("data".to_string()),
6210                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6211                        target: Some(fdecl::Ref::Collection(
6212                            fdecl::CollectionRef { name: "b".repeat(256) }
6213                        )),
6214                        target_name: Some("data".to_string()),
6215                        ..Default::default()
6216                    }),
6217                    fdecl::Offer::Runner(fdecl::OfferRunner {
6218                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6219                            name: "a".repeat(256),
6220                            collection: None,
6221                        })),
6222                        source_name: Some("b".repeat(256)),
6223                        target: Some(fdecl::Ref::Collection(
6224                        fdecl::CollectionRef {
6225                            name: "c".repeat(256),
6226                        }
6227                        )),
6228                        target_name: Some("d".repeat(256)),
6229                        ..Default::default()
6230                    }),
6231                    fdecl::Offer::Resolver(fdecl::OfferResolver {
6232                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6233                            name: "a".repeat(256),
6234                            collection: None,
6235                        })),
6236                        source_name: Some("b".repeat(256)),
6237                        target: Some(fdecl::Ref::Collection(
6238                            fdecl::CollectionRef {
6239                                name: "c".repeat(256),
6240                            }
6241                        )),
6242                        target_name: Some("d".repeat(256)),
6243                        ..Default::default()
6244                    }),
6245                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6246                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6247                            name: "a".repeat(256),
6248                            collection: None,
6249                        })),
6250                        source_name: Some("b".repeat(256)),
6251                        target: Some(fdecl::Ref::Collection(
6252                            fdecl::CollectionRef {
6253                                name: "c".repeat(256),
6254                            }
6255                        )),
6256                        target_name: Some("d".repeat(256)),
6257                        dependency_type: Some(fdecl::DependencyType::Strong),
6258                        ..Default::default()
6259                    }),
6260                ]);
6261                decl
6262            },
6263            result = Err(ErrorList::new(vec![
6264                Error::field_too_long(DeclType::OfferService, "source.child.name"),
6265                Error::field_too_long(DeclType::OfferService, "source_name"),
6266                Error::field_too_long(DeclType::OfferService, "target.child.name"),
6267                Error::field_too_long(DeclType::OfferService, "target_name"),
6268                Error::field_too_long(DeclType::OfferService, "target.collection.name"),
6269                Error::field_too_long(DeclType::OfferService, "target_name"),
6270                Error::field_too_long(DeclType::OfferProtocol, "source.child.name"),
6271                Error::field_too_long(DeclType::OfferProtocol, "source_name"),
6272                Error::field_too_long(DeclType::OfferProtocol, "target.child.name"),
6273                Error::field_too_long(DeclType::OfferProtocol, "target_name"),
6274                Error::field_too_long(DeclType::OfferProtocol, "target.collection.name"),
6275                Error::field_too_long(DeclType::OfferProtocol, "target_name"),
6276                Error::field_too_long(DeclType::OfferDirectory, "source.child.name"),
6277                Error::field_too_long(DeclType::OfferDirectory, "source_name"),
6278                Error::field_too_long(DeclType::OfferDirectory, "target.child.name"),
6279                Error::field_too_long(DeclType::OfferDirectory, "target_name"),
6280                Error::field_too_long(DeclType::OfferDirectory, "target.collection.name"),
6281                Error::field_too_long(DeclType::OfferDirectory, "target_name"),
6282                Error::field_too_long(DeclType::OfferStorage, "target.child.name"),
6283                Error::field_too_long(DeclType::OfferStorage, "target.collection.name"),
6284                Error::field_too_long(DeclType::OfferRunner, "source.child.name"),
6285                Error::field_too_long(DeclType::OfferRunner, "source_name"),
6286                Error::field_too_long(DeclType::OfferRunner, "target.collection.name"),
6287                Error::field_too_long(DeclType::OfferRunner, "target_name"),
6288                Error::field_too_long(DeclType::OfferResolver, "source.child.name"),
6289                Error::field_too_long(DeclType::OfferResolver, "source_name"),
6290                Error::field_too_long(DeclType::OfferResolver, "target.collection.name"),
6291                Error::field_too_long(DeclType::OfferResolver, "target_name"),
6292                Error::field_too_long(DeclType::OfferDictionary, "source.child.name"),
6293                Error::field_too_long(DeclType::OfferDictionary, "source_name"),
6294                Error::field_too_long(DeclType::OfferDictionary, "target.collection.name"),
6295                Error::field_too_long(DeclType::OfferDictionary, "target_name"),
6296            ])),
6297        },
6298        test_validate_offers_extraneous => {
6299            input = {
6300                let mut decl = new_component_decl();
6301                decl.offers = Some(vec![
6302                    fdecl::Offer::Service(fdecl::OfferService {
6303                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6304                            name: "logger".to_string(),
6305                            collection: Some("modular".to_string()),
6306                        })),
6307                        source_name: Some("fuchsia.logger.Log".to_string()),
6308                        target: Some(fdecl::Ref::Child(
6309                            fdecl::ChildRef {
6310                                name: "netstack".to_string(),
6311                                collection: Some("modular".to_string()),
6312                            }
6313                        )),
6314                        target_name: Some("fuchsia.logger.Log".to_string()),
6315                        ..Default::default()
6316                    }),
6317                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6318                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6319                            name: "logger".to_string(),
6320                            collection: Some("modular".to_string()),
6321                        })),
6322                        source_name: Some("fuchsia.logger.Log".to_string()),
6323                        target: Some(fdecl::Ref::Child(
6324                            fdecl::ChildRef {
6325                                name: "netstack".to_string(),
6326                                collection: Some("modular".to_string()),
6327                            }
6328                        )),
6329                        target_name: Some("fuchsia.logger.Log".to_string()),
6330                        dependency_type: Some(fdecl::DependencyType::Strong),
6331                        ..Default::default()
6332                    }),
6333                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6334                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6335                            name: "logger".to_string(),
6336                            collection: Some("modular".to_string()),
6337                        })),
6338                        source_name: Some("assets".to_string()),
6339                        target: Some(fdecl::Ref::Child(
6340                            fdecl::ChildRef {
6341                                name: "netstack".to_string(),
6342                                collection: Some("modular".to_string()),
6343                            }
6344                        )),
6345                        target_name: Some("assets".to_string()),
6346                        rights: Some(fio::Operations::CONNECT),
6347                        subdir: None,
6348                        dependency_type: Some(fdecl::DependencyType::Weak),
6349                        ..Default::default()
6350                    }),
6351                    fdecl::Offer::Storage(fdecl::OfferStorage {
6352                        source_name: Some("data".to_string()),
6353                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{ })),
6354                        target: Some(fdecl::Ref::Child(
6355                            fdecl::ChildRef {
6356                                name: "netstack".to_string(),
6357                                collection: Some("modular".to_string()),
6358                            }
6359                        )),
6360                        target_name: Some("data".to_string()),
6361                        ..Default::default()
6362                    }),
6363                    fdecl::Offer::Runner(fdecl::OfferRunner {
6364                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6365                            name: "logger".to_string(),
6366                            collection: Some("modular".to_string()),
6367                        })),
6368                        source_name: Some("elf".to_string()),
6369                        target: Some(fdecl::Ref::Child(
6370                            fdecl::ChildRef {
6371                                name: "netstack".to_string(),
6372                                collection: Some("modular".to_string()),
6373                            }
6374                        )),
6375                        target_name: Some("elf".to_string()),
6376                        ..Default::default()
6377                    }),
6378                    fdecl::Offer::Resolver(fdecl::OfferResolver {
6379                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6380                            name: "logger".to_string(),
6381                            collection: Some("modular".to_string()),
6382                        })),
6383                        source_name: Some("pkg".to_string()),
6384                        target: Some(fdecl::Ref::Child(
6385                            fdecl::ChildRef {
6386                                name: "netstack".to_string(),
6387                                collection: Some("modular".to_string()),
6388                            }
6389                        )),
6390                        target_name: Some("pkg".to_string()),
6391                        ..Default::default()
6392                    }),
6393                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6394                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6395                            name: "logger".to_string(),
6396                            collection: Some("modular".to_string()),
6397                        })),
6398                        source_name: Some("dict".to_string()),
6399                        target: Some(fdecl::Ref::Child(
6400                            fdecl::ChildRef {
6401                                name: "netstack".to_string(),
6402                                collection: Some("modular".to_string()),
6403                            }
6404                        )),
6405                        target_name: Some("dict".to_string()),
6406                        dependency_type: Some(fdecl::DependencyType::Strong),
6407                        ..Default::default()
6408                    }),
6409                ]);
6410                decl.capabilities = Some(vec![
6411                    fdecl::Capability::Protocol(fdecl::Protocol {
6412                        name: Some("fuchsia.logger.Log".to_string()),
6413                        source_path: Some("/svc/logger".to_string()),
6414                        ..Default::default()
6415                    }),
6416                    fdecl::Capability::Directory(fdecl::Directory {
6417                        name: Some("assets".to_string()),
6418                        source_path: Some("/data/assets".to_string()),
6419                        rights: Some(fio::Operations::CONNECT),
6420                        ..Default::default()
6421                    }),
6422                ]);
6423                decl
6424            },
6425            result = Err(ErrorList::new(vec![
6426                Error::extraneous_field(DeclType::OfferService, "source.child.collection"),
6427                Error::extraneous_field(DeclType::OfferService, "target.child.collection"),
6428                Error::extraneous_field(DeclType::OfferProtocol, "source.child.collection"),
6429                Error::extraneous_field(DeclType::OfferProtocol, "target.child.collection"),
6430                Error::extraneous_field(DeclType::OfferDirectory, "source.child.collection"),
6431                Error::extraneous_field(DeclType::OfferDirectory, "target.child.collection"),
6432                Error::extraneous_field(DeclType::OfferStorage, "target.child.collection"),
6433                Error::extraneous_field(DeclType::OfferRunner, "source.child.collection"),
6434                Error::extraneous_field(DeclType::OfferRunner, "target.child.collection"),
6435                Error::extraneous_field(DeclType::OfferResolver, "source.child.collection"),
6436                Error::extraneous_field(DeclType::OfferResolver, "target.child.collection"),
6437                Error::extraneous_field(DeclType::OfferDictionary, "source.child.collection"),
6438                Error::extraneous_field(DeclType::OfferDictionary, "target.child.collection"),
6439            ])),
6440        },
6441        test_validate_offers_invalid_filtered_service_fields => {
6442            input = {
6443                let mut decl = new_component_decl();
6444                decl.offers = Some(vec![
6445                    fdecl::Offer::Service(fdecl::OfferService {
6446                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6447                        source_name: Some("fuchsia.logger.Log".to_string()),
6448                        target: Some(fdecl::Ref::Child(
6449                            fdecl::ChildRef {
6450                                name: "logger".to_string(),
6451                                collection: None,
6452                            }
6453                        )),
6454                        target_name: Some("fuchsia.logger.Log".to_string()),
6455                        source_instance_filter: Some(vec![]),
6456                        ..Default::default()
6457                    }),
6458                    fdecl::Offer::Service(fdecl::OfferService {
6459                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6460                        source_name: Some("fuchsia.logger.Log".to_string()),
6461                        target: Some(fdecl::Ref::Child(
6462                            fdecl::ChildRef {
6463                                name: "logger".to_string(),
6464                                collection: None,
6465                            }
6466                        )),
6467                        target_name: Some("fuchsia.logger.Log2".to_string()),
6468                        source_instance_filter: Some(vec!["^badname".to_string()]),
6469                        ..Default::default()
6470                    }),
6471                    fdecl::Offer::Service(fdecl::OfferService {
6472                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6473                        source_name: Some("fuchsia.logger.Log".to_string()),
6474                        target: Some(fdecl::Ref::Child(
6475                            fdecl::ChildRef {
6476                                name: "logger".to_string(),
6477                                collection: None,
6478                            }
6479                        )),
6480                        target_name: Some("fuchsia.logger.Log1".to_string()),
6481                        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()}]),
6482                        ..Default::default()
6483                    }),
6484                    fdecl::Offer::Service(fdecl::OfferService {
6485                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6486                        source_name: Some("fuchsia.logger.Log".to_string()),
6487                        target: Some(fdecl::Ref::Child(
6488                            fdecl::ChildRef {
6489                                name: "logger".to_string(),
6490                                collection: None,
6491                            }
6492                        )),
6493                        target_name: Some("fuchsia.logger.Log3".to_string()),
6494                        renamed_instances: Some(vec![
6495                            fdecl::NameMapping {
6496                                source_name: "^badname".to_string(),
6497                                target_name: "^badname".to_string(),
6498                            }
6499                        ]),
6500                        ..Default::default()
6501                    })
6502                ]);
6503                decl.children = Some(vec![
6504                    fdecl::Child {
6505                        name: Some("logger".to_string()),
6506                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
6507                        startup: Some(fdecl::StartupMode::Lazy),
6508                        on_terminate: None,
6509                        environment: None,
6510                        ..Default::default()
6511                    },
6512                ]);
6513                decl
6514            },
6515            result = Err(ErrorList::new(vec![
6516                Error::invalid_field(DeclType::OfferService, "source_instance_filter"),
6517                Error::invalid_field(DeclType::OfferService, "source_instance_filter"),
6518                Error::invalid_field(DeclType::OfferService, "renamed_instances"),
6519                Error::invalid_field(DeclType::OfferService, "renamed_instances.source_name"),
6520                Error::invalid_field(DeclType::OfferService, "renamed_instances.target_name"),
6521            ])),
6522        },
6523        test_validate_offers_invalid_identifiers => {
6524            input = {
6525                let mut decl = new_component_decl();
6526                decl.offers = Some(vec![
6527                    fdecl::Offer::Service(fdecl::OfferService {
6528                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6529                            name: "^bad".to_string(),
6530                            collection: None,
6531                        })),
6532                        source_name: Some("foo/".to_string()),
6533                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6534                            name: "%bad".to_string(),
6535                            collection: None,
6536                        })),
6537                        target_name: Some("/".to_string()),
6538                        ..Default::default()
6539                    }),
6540                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6541                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6542                            name: "^bad".to_string(),
6543                            collection: None,
6544                        })),
6545                        source_name: Some("foo/".to_string()),
6546                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6547                            name: "%bad".to_string(),
6548                            collection: None,
6549                        })),
6550                        target_name: Some("/".to_string()),
6551                        dependency_type: Some(fdecl::DependencyType::Strong),
6552                        ..Default::default()
6553                    }),
6554                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6555                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6556                            name: "^bad".to_string(),
6557                            collection: None,
6558                        })),
6559                        source_name: Some("foo/".to_string()),
6560                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6561                            name: "%bad".to_string(),
6562                            collection: None,
6563                        })),
6564                        target_name: Some("/".to_string()),
6565                        rights: Some(fio::Operations::CONNECT),
6566                        subdir: Some("/foo".to_string()),
6567                        dependency_type: Some(fdecl::DependencyType::Strong),
6568                        ..Default::default()
6569                    }),
6570                    fdecl::Offer::Runner(fdecl::OfferRunner {
6571                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6572                            name: "^bad".to_string(),
6573                            collection: None,
6574                        })),
6575                        source_name: Some("/path".to_string()),
6576                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6577                            name: "%bad".to_string(),
6578                            collection: None,
6579                        })),
6580                        target_name: Some("elf!".to_string()),
6581                        ..Default::default()
6582                    }),
6583                    fdecl::Offer::Resolver(fdecl::OfferResolver {
6584                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6585                            name: "^bad".to_string(),
6586                            collection: None,
6587                        })),
6588                        source_name: Some("/path".to_string()),
6589                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6590                            name: "%bad".to_string(),
6591                            collection: None,
6592                        })),
6593                        target_name: Some("pkg!".to_string()),
6594                        ..Default::default()
6595                    }),
6596                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6597                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6598                            name: "^bad".to_string(),
6599                            collection: None,
6600                        })),
6601                        source_name: Some("/path".to_string()),
6602                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6603                            name: "%bad".to_string(),
6604                            collection: None,
6605                        })),
6606                        target_name: Some("pkg!".to_string()),
6607                        dependency_type: Some(fdecl::DependencyType::Strong),
6608                        ..Default::default()
6609                    }),
6610                ]);
6611                decl
6612            },
6613            result = Err(ErrorList::new(vec![
6614                Error::invalid_field(DeclType::OfferService, "source.child.name"),
6615                Error::invalid_field(DeclType::OfferService, "source_name"),
6616                Error::invalid_field(DeclType::OfferService, "target.child.name"),
6617                Error::invalid_field(DeclType::OfferService, "target_name"),
6618                Error::invalid_field(DeclType::OfferProtocol, "source.child.name"),
6619                Error::invalid_field(DeclType::OfferProtocol, "source_name"),
6620                Error::invalid_field(DeclType::OfferProtocol, "target.child.name"),
6621                Error::invalid_field(DeclType::OfferProtocol, "target_name"),
6622                Error::invalid_field(DeclType::OfferDirectory, "source.child.name"),
6623                Error::invalid_field(DeclType::OfferDirectory, "source_name"),
6624                Error::invalid_field(DeclType::OfferDirectory, "target.child.name"),
6625                Error::invalid_field(DeclType::OfferDirectory, "target_name"),
6626                Error::invalid_field(DeclType::OfferDirectory, "subdir"),
6627                Error::invalid_field(DeclType::OfferRunner, "source.child.name"),
6628                Error::invalid_field(DeclType::OfferRunner, "source_name"),
6629                Error::invalid_field(DeclType::OfferRunner, "target.child.name"),
6630                Error::invalid_field(DeclType::OfferRunner, "target_name"),
6631                Error::invalid_field(DeclType::OfferResolver, "source.child.name"),
6632                Error::invalid_field(DeclType::OfferResolver, "source_name"),
6633                Error::invalid_field(DeclType::OfferResolver, "target.child.name"),
6634                Error::invalid_field(DeclType::OfferResolver, "target_name"),
6635                Error::invalid_field(DeclType::OfferDictionary, "source.child.name"),
6636                Error::invalid_field(DeclType::OfferDictionary, "source_name"),
6637                Error::invalid_field(DeclType::OfferDictionary, "target.child.name"),
6638                Error::invalid_field(DeclType::OfferDictionary, "target_name"),
6639            ])),
6640        },
6641        test_validate_offers_target_equals_source => {
6642            input = {
6643                let mut decl = new_component_decl();
6644                decl.offers = Some(vec![
6645                    fdecl::Offer::Service(fdecl::OfferService {
6646                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6647                            name: "logger".to_string(),
6648                            collection: None,
6649                        })),
6650                        source_name: Some("logger".to_string()),
6651                        target: Some(fdecl::Ref::Child(
6652                        fdecl::ChildRef {
6653                            name: "logger".to_string(),
6654                            collection: None,
6655                        }
6656                        )),
6657                        target_name: Some("logger".to_string()),
6658                        ..Default::default()
6659                    }),
6660                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6661                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6662                            name: "logger".to_string(),
6663                            collection: None,
6664                        })),
6665                        source_name: Some("legacy_logger".to_string()),
6666                        target: Some(fdecl::Ref::Child(
6667                        fdecl::ChildRef {
6668                            name: "logger".to_string(),
6669                            collection: None,
6670                        }
6671                        )),
6672                        target_name: Some("weak_legacy_logger".to_string()),
6673                        dependency_type: Some(fdecl::DependencyType::Weak),
6674                        ..Default::default()
6675                    }),
6676                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6677                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6678                            name: "logger".to_string(),
6679                            collection: None,
6680                        })),
6681                        source_name: Some("legacy_logger".to_string()),
6682                        target: Some(fdecl::Ref::Child(
6683                        fdecl::ChildRef {
6684                            name: "logger".to_string(),
6685                            collection: None,
6686                        }
6687                        )),
6688                        target_name: Some("strong_legacy_logger".to_string()),
6689                        dependency_type: Some(fdecl::DependencyType::Strong),
6690                        ..Default::default()
6691                    }),
6692                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6693                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6694                            name: "logger".to_string(),
6695                            collection: None,
6696                        })),
6697                        source_name: Some("assets".to_string()),
6698                        target: Some(fdecl::Ref::Child(
6699                        fdecl::ChildRef {
6700                            name: "logger".to_string(),
6701                            collection: None,
6702                        }
6703                        )),
6704                        target_name: Some("assets".to_string()),
6705                        rights: Some(fio::Operations::CONNECT),
6706                        subdir: None,
6707                        dependency_type: Some(fdecl::DependencyType::Strong),
6708                        ..Default::default()
6709                    }),
6710                    fdecl::Offer::Runner(fdecl::OfferRunner {
6711                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6712                            name: "logger".to_string(),
6713                            collection: None,
6714                        })),
6715                        source_name: Some("web".to_string()),
6716                        target: Some(fdecl::Ref::Child(
6717                        fdecl::ChildRef {
6718                            name: "logger".to_string(),
6719                            collection: None,
6720                        }
6721                        )),
6722                        target_name: Some("web".to_string()),
6723                        ..Default::default()
6724                    }),
6725                    fdecl::Offer::Resolver(fdecl::OfferResolver {
6726                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6727                            name: "logger".to_string(),
6728                            collection: None,
6729                        })),
6730                        source_name: Some("pkg".to_string()),
6731                        target: Some(fdecl::Ref::Child(
6732                        fdecl::ChildRef {
6733                            name: "logger".to_string(),
6734                            collection: None,
6735                        }
6736                        )),
6737                        target_name: Some("pkg".to_string()),
6738                        ..Default::default()
6739                    }),
6740                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
6741                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6742                            name: "logger".to_string(),
6743                            collection: None,
6744                        })),
6745                        source_name: Some("dict".to_string()),
6746                        target: Some(fdecl::Ref::Child(
6747                        fdecl::ChildRef {
6748                            name: "logger".to_string(),
6749                            collection: None,
6750                        }
6751                        )),
6752                        target_name: Some("dict".to_string()),
6753                        dependency_type: Some(fdecl::DependencyType::Strong),
6754                        ..Default::default()
6755                    }),
6756                ]);
6757                decl.children = Some(vec![fdecl::Child{
6758                    name: Some("logger".to_string()),
6759                    url: Some("fuchsia-pkg://fuchsia.com/logger#meta/logger.cm".to_string()),
6760                    startup: Some(fdecl::StartupMode::Lazy),
6761                    on_terminate: None,
6762                    environment: None,
6763                    ..Default::default()
6764                }]);
6765                decl
6766            },
6767            result = Err(ErrorList::new(vec![
6768                Error::dependency_cycle("{{child logger -> child logger}}".to_string()),
6769            ])),
6770        },
6771        test_validate_offers_storage_target_equals_source => {
6772            input = fdecl::Component {
6773                offers: Some(vec![
6774                    fdecl::Offer::Storage(fdecl::OfferStorage {
6775                        source_name: Some("data".to_string()),
6776                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef { })),
6777                        target: Some(fdecl::Ref::Child(
6778                            fdecl::ChildRef {
6779                                name: "logger".to_string(),
6780                                collection: None,
6781                            }
6782                        )),
6783                        target_name: Some("data".to_string()),
6784                        ..Default::default()
6785                    })
6786                ]),
6787                capabilities: Some(vec![
6788                    fdecl::Capability::Storage(fdecl::Storage {
6789                        name: Some("data".to_string()),
6790                        backing_dir: Some("minfs".to_string()),
6791                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6792                            name: "logger".to_string(),
6793                            collection: None,
6794                        })),
6795                        subdir: None,
6796                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
6797                        ..Default::default()
6798                    }),
6799                ]),
6800                children: Some(vec![
6801                    fdecl::Child {
6802                        name: Some("logger".to_string()),
6803                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
6804                        startup: Some(fdecl::StartupMode::Lazy),
6805                        on_terminate: None,
6806                        environment: None,
6807                        ..Default::default()
6808                    },
6809                ]),
6810                ..new_component_decl()
6811            },
6812            result = Err(ErrorList::new(vec![
6813                Error::dependency_cycle("{{child logger -> capability data -> child logger}}".to_string()),
6814            ])),
6815        },
6816        test_validate_offers_invalid_child => {
6817            input = {
6818                let mut decl = new_component_decl();
6819                decl.offers = Some(vec![
6820                    fdecl::Offer::Service(fdecl::OfferService {
6821                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6822                            name: "logger".to_string(),
6823                            collection: None,
6824                        })),
6825                        source_name: Some("fuchsia.logger.Log".to_string()),
6826                        target: Some(fdecl::Ref::Child(
6827                        fdecl::ChildRef {
6828                            name: "netstack".to_string(),
6829                            collection: None,
6830                        }
6831                        )),
6832                        target_name: Some("fuchsia.logger.Log".to_string()),
6833                        ..Default::default()
6834                    }),
6835                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6836                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6837                            name: "logger".to_string(),
6838                            collection: None,
6839                        })),
6840                        source_name: Some("fuchsia.logger.LegacyLog".to_string()),
6841                        target: Some(fdecl::Ref::Child(
6842                        fdecl::ChildRef {
6843                            name: "netstack".to_string(),
6844                            collection: None,
6845                        }
6846                        )),
6847                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
6848                        dependency_type: Some(fdecl::DependencyType::Strong),
6849                        ..Default::default()
6850                    }),
6851                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6852                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6853                            name: "logger".to_string(),
6854                            collection: None,
6855                        })),
6856                        source_name: Some("assets".to_string()),
6857                        target: Some(fdecl::Ref::Collection(
6858                        fdecl::CollectionRef { name: "modular".to_string() }
6859                        )),
6860                        target_name: Some("assets".to_string()),
6861                        rights: Some(fio::Operations::CONNECT),
6862                        subdir: None,
6863                        dependency_type: Some(fdecl::DependencyType::Weak),
6864                        ..Default::default()
6865                    }),
6866                ]);
6867                decl.capabilities = Some(vec![
6868                    fdecl::Capability::Storage(fdecl::Storage {
6869                        name: Some("memfs".to_string()),
6870                        backing_dir: Some("memfs".to_string()),
6871                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6872                            name: "logger".to_string(),
6873                            collection: None,
6874                        })),
6875                        subdir: None,
6876                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
6877                        ..Default::default()
6878                    }),
6879                ]);
6880                decl.children = Some(vec![
6881                    fdecl::Child {
6882                        name: Some("netstack".to_string()),
6883                        url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
6884                        startup: Some(fdecl::StartupMode::Lazy),
6885                        on_terminate: None,
6886                        environment: None,
6887                        ..Default::default()
6888                    },
6889                ]);
6890                decl.collections = Some(vec![
6891                    fdecl::Collection {
6892                        name: Some("modular".to_string()),
6893                        durability: Some(fdecl::Durability::Transient),
6894                        environment: None,
6895                        allowed_offers: Some(fdecl::AllowedOffers::StaticAndDynamic),
6896                        allow_long_names: None,
6897                        ..Default::default()
6898                    },
6899                ]);
6900                decl
6901            },
6902            result = Err(ErrorList::new(vec![
6903                Error::invalid_child(DeclType::Storage, "source", "logger"),
6904                Error::invalid_child(DeclType::OfferService, "source", "logger"),
6905                Error::invalid_child(DeclType::OfferProtocol, "source", "logger"),
6906                Error::invalid_child(DeclType::OfferDirectory, "source", "logger"),
6907            ])),
6908        },
6909        test_validate_offers_invalid_source_capability => {
6910            input = {
6911                fdecl::Component {
6912                    offers: Some(vec![
6913                        fdecl::Offer::Protocol(fdecl::OfferProtocol {
6914                            source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
6915                                name: "this-storage-doesnt-exist".to_string(),
6916                            })),
6917                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
6918                            target: Some(fdecl::Ref::Child(
6919                            fdecl::ChildRef {
6920                                name: "netstack".to_string(),
6921                                collection: None,
6922                            }
6923                            )),
6924                            target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
6925                            dependency_type: Some(fdecl::DependencyType::Strong),
6926                            ..Default::default()
6927                        }),
6928                    ]),
6929                    ..new_component_decl()
6930                }
6931            },
6932            result = Err(ErrorList::new(vec![
6933                Error::invalid_capability(DeclType::OfferProtocol, "source", "this-storage-doesnt-exist"),
6934                Error::invalid_child(DeclType::OfferProtocol, "target", "netstack"),
6935            ])),
6936        },
6937        test_validate_offers_target => {
6938            input = {
6939                let mut decl = new_component_decl();
6940                decl.offers = Some(vec![
6941                    fdecl::Offer::Service(fdecl::OfferService {
6942                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6943                            name: "modular".into()
6944                        })),
6945                        source_name: Some("logger".to_string()),
6946                        target: Some(fdecl::Ref::Child(
6947                        fdecl::ChildRef {
6948                            name: "netstack".to_string(),
6949                            collection: None,
6950                        }
6951                        )),
6952                        target_name: Some("fuchsia.logger.Log".to_string()),
6953                        ..Default::default()
6954                    }),
6955                    fdecl::Offer::Service(fdecl::OfferService {
6956                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6957                            name: "modular".into()
6958                        })),
6959                        source_name: Some("logger".to_string()),
6960                        target: Some(fdecl::Ref::Child(
6961                        fdecl::ChildRef {
6962                            name: "netstack".to_string(),
6963                            collection: None,
6964                        }
6965                        )),
6966                        target_name: Some("fuchsia.logger.Log".to_string()),
6967                        ..Default::default()
6968                    }),
6969                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6970                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6971                        source_name: Some("fuchsia.logger.LegacyLog".to_string()),
6972                        target: Some(fdecl::Ref::Child(
6973                        fdecl::ChildRef {
6974                            name: "netstack".to_string(),
6975                            collection: None,
6976                        }
6977                        )),
6978                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
6979                        dependency_type: Some(fdecl::DependencyType::Strong),
6980                        ..Default::default()
6981                    }),
6982                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
6983                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6984                        source_name: Some("fuchsia.logger.LegacyLog".to_string()),
6985                        target: Some(fdecl::Ref::Child(
6986                        fdecl::ChildRef {
6987                            name: "netstack".to_string(),
6988                            collection: None,
6989                        }
6990                        )),
6991                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
6992                        dependency_type: Some(fdecl::DependencyType::Strong),
6993                        ..Default::default()
6994                    }),
6995                    fdecl::Offer::Directory(fdecl::OfferDirectory {
6996                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
6997                        source_name: Some("assets".to_string()),
6998                        target: Some(fdecl::Ref::Collection(
6999                        fdecl::CollectionRef { name: "modular".to_string() }
7000                        )),
7001                        target_name: Some("assets".to_string()),
7002                        rights: Some(fio::Operations::CONNECT),
7003                        subdir: None,
7004                        dependency_type: Some(fdecl::DependencyType::Strong),
7005                        ..Default::default()
7006                    }),
7007                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7008                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7009                        source_name: Some("assets".to_string()),
7010                        target: Some(fdecl::Ref::Collection(
7011                        fdecl::CollectionRef { name: "modular".to_string() }
7012                        )),
7013                        target_name: Some("assets".to_string()),
7014                        rights: Some(fio::Operations::CONNECT),
7015                        subdir: None,
7016                        dependency_type: Some(fdecl::DependencyType::Weak),
7017                        ..Default::default()
7018                    }),
7019                    fdecl::Offer::Storage(fdecl::OfferStorage {
7020                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7021                        source_name: Some("data".to_string()),
7022                        target: Some(fdecl::Ref::Collection(
7023                        fdecl::CollectionRef { name: "modular".to_string() }
7024                        )),
7025                        target_name: Some("data".to_string()),
7026                        ..Default::default()
7027                    }),
7028                    fdecl::Offer::Storage(fdecl::OfferStorage {
7029                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7030                        source_name: Some("data".to_string()),
7031                        target: Some(fdecl::Ref::Collection(
7032                        fdecl::CollectionRef { name: "modular".to_string() }
7033                        )),
7034                        target_name: Some("data".to_string()),
7035                        ..Default::default()
7036                    }),
7037                    fdecl::Offer::Runner(fdecl::OfferRunner {
7038                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7039                        source_name: Some("elf".to_string()),
7040                        target: Some(fdecl::Ref::Collection(
7041                        fdecl::CollectionRef { name: "modular".to_string() }
7042                        )),
7043                        target_name: Some("duplicated".to_string()),
7044                        ..Default::default()
7045                    }),
7046                    fdecl::Offer::Runner(fdecl::OfferRunner {
7047                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7048                        source_name: Some("elf".to_string()),
7049                        target: Some(fdecl::Ref::Collection(
7050                        fdecl::CollectionRef { name: "modular".to_string() }
7051                        )),
7052                        target_name: Some("duplicated".to_string()),
7053                        ..Default::default()
7054                    }),
7055                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7056                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7057                        source_name: Some("pkg".to_string()),
7058                        target: Some(fdecl::Ref::Collection(
7059                        fdecl::CollectionRef { name: "modular".to_string() }
7060                        )),
7061                        target_name: Some("duplicated".to_string()),
7062                        ..Default::default()
7063                    }),
7064                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
7065                        source_name: Some("started".to_string()),
7066                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7067                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "netstack".to_string(), collection: None})),
7068                        target_name: Some("started".to_string()),
7069                        ..Default::default()
7070                    }),
7071                    fdecl::Offer::EventStream(fdecl::OfferEventStream {
7072                        source_name: Some("started".to_string()),
7073                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7074                        target: Some(fdecl::Ref::Child(fdecl::ChildRef{name: "netstack".to_string(), collection: None})),
7075                        target_name: Some("started".to_string()),
7076                        ..Default::default()
7077                    }),
7078                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7079                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7080                        source_name: Some("a".to_string()),
7081                        target: Some(fdecl::Ref::Collection(
7082                            fdecl::CollectionRef { name: "modular".to_string() }
7083                        )),
7084                        target_name: Some("dict".to_string()),
7085                        dependency_type: Some(fdecl::DependencyType::Strong),
7086                        ..Default::default()
7087                    }),
7088                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7089                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7090                        source_name: Some("b".to_string()),
7091                        target: Some(fdecl::Ref::Collection(
7092                            fdecl::CollectionRef { name: "modular".to_string() }
7093                        )),
7094                        target_name: Some("dict".to_string()),
7095                        dependency_type: Some(fdecl::DependencyType::Strong),
7096                        ..Default::default()
7097                    }),
7098                ]);
7099                decl.children = Some(vec![
7100                    fdecl::Child{
7101                        name: Some("netstack".to_string()),
7102                        url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
7103                        startup: Some(fdecl::StartupMode::Eager),
7104                        on_terminate: None,
7105                        environment: None,
7106                        ..Default::default()
7107                    },
7108                ]);
7109                decl.collections = Some(vec![
7110                    fdecl::Collection{
7111                        name: Some("modular".to_string()),
7112                        durability: Some(fdecl::Durability::Transient),
7113                        environment: None,
7114                        allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7115                        allow_long_names: None,
7116                        ..Default::default()
7117                    },
7118                ]);
7119                decl
7120            },
7121            result = Err(ErrorList::new(vec![
7122                // Duplicate services are allowed, for aggregation.
7123                Error::duplicate_field(DeclType::OfferProtocol, "target_name", "fuchsia.logger.LegacyLog"),
7124                Error::duplicate_field(DeclType::OfferDirectory, "target_name", "assets"),
7125                Error::duplicate_field(DeclType::OfferStorage, "target_name", "data"),
7126                Error::duplicate_field(DeclType::OfferRunner, "target_name", "duplicated"),
7127                Error::duplicate_field(DeclType::OfferResolver, "target_name", "duplicated"),
7128                Error::duplicate_field(DeclType::OfferEventStream, "target_name", "started"),
7129                Error::duplicate_field(DeclType::OfferDictionary, "target_name", "dict"),
7130            ])),
7131        },
7132        test_validate_offers_target_invalid => {
7133            input = {
7134                let mut decl = new_component_decl();
7135                decl.offers = Some(vec![
7136                    fdecl::Offer::Service(fdecl::OfferService {
7137                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7138                        source_name: Some("logger".to_string()),
7139                        target: Some(fdecl::Ref::Child(
7140                        fdecl::ChildRef {
7141                            name: "netstack".to_string(),
7142                            collection: None,
7143                        }
7144                        )),
7145                        target_name: Some("fuchsia.logger.Log".to_string()),
7146                        ..Default::default()
7147                    }),
7148                    fdecl::Offer::Service(fdecl::OfferService {
7149                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7150                        source_name: Some("logger".to_string()),
7151                        target: Some(fdecl::Ref::Collection(
7152                        fdecl::CollectionRef { name: "modular".to_string(), }
7153                        )),
7154                        target_name: Some("fuchsia.logger.Log".to_string()),
7155                        ..Default::default()
7156                    }),
7157                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7158                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7159                        source_name: Some("legacy_logger".to_string()),
7160                        target: Some(fdecl::Ref::Child(
7161                        fdecl::ChildRef {
7162                            name: "netstack".to_string(),
7163                            collection: None,
7164                        }
7165                        )),
7166                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7167                        dependency_type: Some(fdecl::DependencyType::Weak),
7168                        ..Default::default()
7169                    }),
7170                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7171                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7172                        source_name: Some("legacy_logger".to_string()),
7173                        target: Some(fdecl::Ref::Collection(
7174                        fdecl::CollectionRef { name: "modular".to_string(), }
7175                        )),
7176                        target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7177                        dependency_type: Some(fdecl::DependencyType::Strong),
7178                        ..Default::default()
7179                    }),
7180                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7181                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7182                        source_name: Some("assets".to_string()),
7183                        target: Some(fdecl::Ref::Child(
7184                        fdecl::ChildRef {
7185                            name: "netstack".to_string(),
7186                            collection: None,
7187                        }
7188                        )),
7189                        target_name: Some("data".to_string()),
7190                        rights: Some(fio::Operations::CONNECT),
7191                        subdir: None,
7192                        dependency_type: Some(fdecl::DependencyType::Strong),
7193                        ..Default::default()
7194                    }),
7195                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7196                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7197                        source_name: Some("assets".to_string()),
7198                        target: Some(fdecl::Ref::Collection(
7199                        fdecl::CollectionRef { name: "modular".to_string(), }
7200                        )),
7201                        target_name: Some("data".to_string()),
7202                        rights: Some(fio::Operations::CONNECT),
7203                        subdir: None,
7204                        dependency_type: Some(fdecl::DependencyType::Weak),
7205                        ..Default::default()
7206                    }),
7207                    fdecl::Offer::Storage(fdecl::OfferStorage {
7208                        source_name: Some("data".to_string()),
7209                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7210                        target: Some(fdecl::Ref::Child(
7211                            fdecl::ChildRef {
7212                                name: "netstack".to_string(),
7213                                collection: None,
7214                            }
7215                        )),
7216                        target_name: Some("data".to_string()),
7217                        ..Default::default()
7218                    }),
7219                    fdecl::Offer::Storage(fdecl::OfferStorage {
7220                        source_name: Some("data".to_string()),
7221                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7222                        target: Some(fdecl::Ref::Collection(
7223                            fdecl::CollectionRef { name: "modular".to_string(), }
7224                        )),
7225                        target_name: Some("data".to_string()),
7226                        ..Default::default()
7227                    }),
7228                    fdecl::Offer::Runner(fdecl::OfferRunner {
7229                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7230                        source_name: Some("elf".to_string()),
7231                        target: Some(fdecl::Ref::Child(
7232                            fdecl::ChildRef {
7233                                name: "netstack".to_string(),
7234                                collection: None,
7235                            }
7236                        )),
7237                        target_name: Some("elf".to_string()),
7238                        ..Default::default()
7239                    }),
7240                    fdecl::Offer::Runner(fdecl::OfferRunner {
7241                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7242                        source_name: Some("elf".to_string()),
7243                        target: Some(fdecl::Ref::Collection(
7244                        fdecl::CollectionRef { name: "modular".to_string(), }
7245                        )),
7246                        target_name: Some("elf".to_string()),
7247                        ..Default::default()
7248                    }),
7249                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7250                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7251                        source_name: Some("pkg".to_string()),
7252                        target: Some(fdecl::Ref::Child(
7253                            fdecl::ChildRef {
7254                                name: "netstack".to_string(),
7255                                collection: None,
7256                            }
7257                        )),
7258                        target_name: Some("pkg".to_string()),
7259                        ..Default::default()
7260                    }),
7261                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7262                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7263                        source_name: Some("pkg".to_string()),
7264                        target: Some(fdecl::Ref::Collection(
7265                        fdecl::CollectionRef { name: "modular".to_string(), }
7266                        )),
7267                        target_name: Some("pkg".to_string()),
7268                        ..Default::default()
7269                    }),
7270                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7271                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7272                        source_name: Some("pkg".to_string()),
7273                        target: Some(fdecl::Ref::Child(
7274                            fdecl::ChildRef {
7275                                name: "netstack".to_string(),
7276                                collection: None,
7277                            }
7278                        )),
7279                        target_name: Some("pkg".to_string()),
7280                        dependency_type: Some(fdecl::DependencyType::Strong),
7281                        ..Default::default()
7282                    }),
7283                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7284                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7285                        source_name: Some("pkg".to_string()),
7286                        target: Some(fdecl::Ref::Collection(
7287                        fdecl::CollectionRef { name: "modular".to_string(), }
7288                        )),
7289                        target_name: Some("pkg".to_string()),
7290                        dependency_type: Some(fdecl::DependencyType::Strong),
7291                        ..Default::default()
7292                    }),
7293                ]);
7294                decl
7295            },
7296            result = Err(ErrorList::new(vec![
7297                Error::invalid_child(DeclType::OfferService, "target", "netstack"),
7298                Error::invalid_collection(DeclType::OfferService, "target", "modular"),
7299                Error::invalid_child(DeclType::OfferProtocol, "target", "netstack"),
7300                Error::invalid_collection(DeclType::OfferProtocol, "target", "modular"),
7301                Error::invalid_child(DeclType::OfferDirectory, "target", "netstack"),
7302                Error::invalid_collection(DeclType::OfferDirectory, "target", "modular"),
7303                Error::invalid_child(DeclType::OfferStorage, "target", "netstack"),
7304                Error::invalid_collection(DeclType::OfferStorage, "target", "modular"),
7305                Error::invalid_child(DeclType::OfferRunner, "target", "netstack"),
7306                Error::invalid_collection(DeclType::OfferRunner, "target", "modular"),
7307                Error::invalid_child(DeclType::OfferResolver, "target", "netstack"),
7308                Error::invalid_collection(DeclType::OfferResolver, "target", "modular"),
7309                Error::invalid_child(DeclType::OfferDictionary, "target", "netstack"),
7310                Error::invalid_collection(DeclType::OfferDictionary, "target", "modular"),
7311            ])),
7312        },
7313        test_validate_offers_target_dictionary => {
7314            input = fdecl::Component {
7315                offers: Some(vec![
7316                    // Offer to static dictionary is ok
7317                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7318                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7319                        source_name: Some("p".to_string()),
7320                        target: Some(fdecl::Ref::Capability(
7321                            fdecl::CapabilityRef {
7322                                name: "dict".into(),
7323                            },
7324                        )),
7325                        target_name: Some("p".into()),
7326                        dependency_type: Some(fdecl::DependencyType::Strong),
7327                        ..Default::default()
7328                    }),
7329                    // Offer to dynamic dictionary is forbidden
7330                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7331                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
7332                        source_name: Some("p".to_string()),
7333                        target: Some(fdecl::Ref::Capability(
7334                            fdecl::CapabilityRef {
7335                                name: "dynamic".into(),
7336                            },
7337                        )),
7338                        target_name: Some("p".into()),
7339                        dependency_type: Some(fdecl::DependencyType::Strong),
7340                        ..Default::default()
7341                    }),
7342                ]),
7343                capabilities: Some(vec![
7344                    fdecl::Capability::Dictionary(fdecl::Dictionary {
7345                        name: Some("dict".into()),
7346                        ..Default::default()
7347                    }),
7348                    fdecl::Capability::Dictionary(fdecl::Dictionary {
7349                        name: Some("dynamic".into()),
7350                        source_path: Some("/out/dir".into()),
7351                        ..Default::default()
7352                    }),
7353                ]),
7354                ..Default::default()
7355            },
7356            result = Err(ErrorList::new(vec![
7357                Error::invalid_field(DeclType::OfferProtocol, "target"),
7358            ])),
7359        },
7360        test_validate_offers_invalid_source_collection => {
7361            input = {
7362                let mut decl = new_component_decl();
7363                decl.collections = Some(vec![
7364                    fdecl::Collection {
7365                        name: Some("col".to_string()),
7366                        durability: Some(fdecl::Durability::Transient),
7367                        allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7368                        allow_long_names: None,
7369                        ..Default::default()
7370                    }
7371                ]);
7372                decl.children = Some(vec![
7373                    fdecl::Child {
7374                        name: Some("child".to_string()),
7375                        url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7376                        startup: Some(fdecl::StartupMode::Lazy),
7377                        on_terminate: None,
7378                        ..Default::default()
7379                    }
7380                ]);
7381                decl.offers = Some(vec![
7382                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7383                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7384                        source_name: Some("a".to_string()),
7385                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7386                        target_name: Some("a".to_string()),
7387                        dependency_type: Some(fdecl::DependencyType::Strong),
7388                        ..Default::default()
7389                    }),
7390                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7391                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7392                        source_name: Some("b".to_string()),
7393                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7394                        target_name: Some("b".to_string()),
7395                        rights: Some(fio::Operations::CONNECT),
7396                        subdir: None,
7397                        dependency_type: Some(fdecl::DependencyType::Strong),
7398                        ..Default::default()
7399                    }),
7400                    fdecl::Offer::Storage(fdecl::OfferStorage {
7401                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7402                        source_name: Some("c".to_string()),
7403                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7404                        target_name: Some("c".to_string()),
7405                        ..Default::default()
7406                    }),
7407                    fdecl::Offer::Runner(fdecl::OfferRunner {
7408                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7409                        source_name: Some("d".to_string()),
7410                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7411                        target_name: Some("d".to_string()),
7412                        ..Default::default()
7413                    }),
7414                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7415                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7416                        source_name: Some("e".to_string()),
7417                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7418                        target_name: Some("e".to_string()),
7419                        ..Default::default()
7420                    }),
7421                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7422                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7423                        source_name: Some("f".to_string()),
7424                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7425                        target_name: Some("f".to_string()),
7426                        dependency_type: Some(fdecl::DependencyType::Strong),
7427                        ..Default::default()
7428                    }),
7429                ]);
7430                decl
7431            },
7432            result = Err(ErrorList::new(vec![
7433                Error::invalid_field(DeclType::OfferProtocol, "source"),
7434                Error::invalid_field(DeclType::OfferDirectory, "source"),
7435                Error::invalid_field(DeclType::OfferStorage, "source"),
7436                Error::invalid_field(DeclType::OfferRunner, "source"),
7437                Error::invalid_field(DeclType::OfferResolver, "source"),
7438                Error::invalid_field(DeclType::OfferDictionary, "source"),
7439            ])),
7440        },
7441        test_validate_offers_source_collection => {
7442            input = {
7443                let mut decl = new_component_decl();
7444                decl.collections = Some(vec![
7445                    fdecl::Collection {
7446                        name: Some("col".to_string()),
7447                        durability: Some(fdecl::Durability::Transient),
7448                        allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
7449                        allow_long_names: None,
7450                        ..Default::default()
7451                    }
7452                ]);
7453                decl.children = Some(vec![
7454                    fdecl::Child {
7455                        name: Some("child".to_string()),
7456                        url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7457                        startup: Some(fdecl::StartupMode::Lazy),
7458                        on_terminate: None,
7459                        ..Default::default()
7460                    }
7461                ]);
7462                decl.offers = Some(vec![
7463                    fdecl::Offer::Service(fdecl::OfferService {
7464                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: "col".to_string() })),
7465                        source_name: Some("a".to_string()),
7466                        target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
7467                        target_name: Some("a".to_string()),
7468                        ..Default::default()
7469                    })
7470                ]);
7471                decl
7472            },
7473            result = Ok(()),
7474        },
7475        test_validate_offers_invalid_capability_from_self => {
7476            input = {
7477                let mut decl = new_component_decl();
7478                decl.children = Some(vec![
7479                    fdecl::Child {
7480                        name: Some("child".to_string()),
7481                        url: Some("fuchsia-pkg://fuchsia.com/foo".to_string()),
7482                        startup: Some(fdecl::StartupMode::Lazy),
7483                        ..Default::default()
7484                    }
7485                ]);
7486                decl.offers = Some(vec![
7487                    fdecl::Offer::Service(fdecl::OfferService {
7488                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7489                        source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7490                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7491                            name: "child".into(),
7492                            collection: None
7493                        })),
7494                        target_name: Some("foo".into()),
7495                        ..Default::default()
7496                    }),
7497                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7498                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7499                        source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7500                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7501                            name: "child".into(),
7502                            collection: None
7503                        })),
7504                        target_name: Some("bar".into()),
7505                        dependency_type: Some(fdecl::DependencyType::Strong),
7506                        ..Default::default()
7507                    }),
7508                    fdecl::Offer::Directory(fdecl::OfferDirectory {
7509                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7510                        source_name: Some("dir".into()),
7511                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7512                            name: "child".into(),
7513                            collection: None
7514                        })),
7515                        target_name: Some("assets".into()),
7516                        dependency_type: Some(fdecl::DependencyType::Strong),
7517                        ..Default::default()
7518                    }),
7519                    fdecl::Offer::Runner(fdecl::OfferRunner {
7520                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7521                        source_name: Some("source_elf".into()),
7522                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7523                            name: "child".into(),
7524                            collection: None
7525                        })),
7526                        target_name: Some("elf".into()),
7527                        ..Default::default()
7528                    }),
7529                    fdecl::Offer::Resolver(fdecl::OfferResolver {
7530                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7531                        source_name: Some("source_pkg".into()),
7532                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7533                            name: "child".into(),
7534                            collection: None
7535                        })),
7536                        target_name: Some("pkg".into()),
7537                        ..Default::default()
7538                    }),
7539                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
7540                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7541                        source_name: Some("source_dict".into()),
7542                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7543                            name: "child".into(),
7544                            collection: None
7545                        })),
7546                        target_name: Some("dict".into()),
7547                        dependency_type: Some(fdecl::DependencyType::Strong),
7548                        ..Default::default()
7549                    }),
7550                    fdecl::Offer::Storage(fdecl::OfferStorage {
7551                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7552                        source_name: Some("source_storage".into()),
7553                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7554                            name: "child".into(),
7555                            collection: None
7556                        })),
7557                        target_name: Some("storage".into()),
7558                        ..Default::default()
7559                    }),
7560                    fdecl::Offer::Config(fdecl::OfferConfiguration {
7561                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7562                        source_name: Some("source_config".into()),
7563                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7564                            name: "child".into(),
7565                            collection: None
7566                        })),
7567                        target_name: Some("config".into()),
7568                        ..Default::default()
7569                    }),
7570                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7571                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
7572                        source_name: Some("fuchsia.some.library.SomeProtocol".into()),
7573                        source_dictionary: Some("dict/inner".into()),
7574                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7575                            name: "child".into(),
7576                            collection: None
7577                        })),
7578                        target_name: Some("baz".into()),
7579                        dependency_type: Some(fdecl::DependencyType::Strong),
7580                        ..Default::default()
7581                    }),
7582                ]);
7583                decl
7584            },
7585            result = Err(ErrorList::new(vec![
7586                Error::invalid_capability(
7587                    DeclType::OfferService,
7588                    "source",
7589                    "fuchsia.some.library.SomeProtocol"),
7590                Error::invalid_capability(
7591                    DeclType::OfferProtocol,
7592                    "source",
7593                    "fuchsia.some.library.SomeProtocol"),
7594                Error::invalid_capability(DeclType::OfferDirectory, "source", "dir"),
7595                Error::invalid_capability(DeclType::OfferRunner, "source", "source_elf"),
7596                Error::invalid_capability(DeclType::OfferResolver, "source", "source_pkg"),
7597                Error::invalid_capability(DeclType::OfferDictionary, "source", "source_dict"),
7598                Error::invalid_capability(DeclType::OfferStorage, "source", "source_storage"),
7599                Error::invalid_capability(DeclType::OfferConfig, "source", "source_config"),
7600                Error::invalid_capability(DeclType::OfferProtocol, "source", "dict"),
7601            ])),
7602        },
7603        test_validate_offers_long_dependency_cycle => {
7604            input = {
7605                let mut decl = new_component_decl();
7606                let dependencies = vec![
7607                    ("d", "b"),
7608                    ("a", "b"),
7609                    ("b", "c"),
7610                    ("b", "d"),
7611                    ("c", "a"),
7612                ];
7613                let offers = dependencies.into_iter().map(|(from,to)|
7614                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
7615                        source: Some(fdecl::Ref::Child(
7616                        fdecl::ChildRef { name: from.to_string(), collection: None },
7617                        )),
7618                        source_name: Some(format!("thing_{}", from)),
7619                        target: Some(fdecl::Ref::Child(
7620                        fdecl::ChildRef { name: to.to_string(), collection: None },
7621                        )),
7622                        target_name: Some(format!("thing_{}", from)),
7623                        dependency_type: Some(fdecl::DependencyType::Strong),
7624                        ..Default::default()
7625                    })).collect();
7626                let children = ["a", "b", "c", "d"].iter().map(|name| {
7627                    fdecl::Child {
7628                        name: Some(name.to_string()),
7629                        url: Some(format!("fuchsia-pkg://fuchsia.com/pkg#meta/{}.cm", name)),
7630                        startup: Some(fdecl::StartupMode::Lazy),
7631                        on_terminate: None,
7632                        environment: None,
7633                        ..Default::default()
7634                    }
7635                }).collect();
7636                decl.offers = Some(offers);
7637                decl.children = Some(children);
7638                decl
7639            },
7640            result = Err(ErrorList::new(vec![
7641                Error::dependency_cycle(directed_graph::Error::CyclesDetected([vec!["child a", "child b", "child c", "child a"], vec!["child b", "child d", "child b"]].iter().cloned().collect()).format_cycle()),
7642            ])),
7643        },
7644        test_validate_offers_not_required_invalid_source_service => {
7645            input = {
7646                let mut decl = generate_offer_different_source_and_availability_decl(
7647                    |source, availability, target_name|
7648                        fdecl::Offer::Service(fdecl::OfferService {
7649                            source: Some(source),
7650                            source_name: Some("fuchsia.examples.Echo".to_string()),
7651                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7652                                name: "sink".to_string(),
7653                                collection: None,
7654                            })),
7655                            target_name: Some(target_name.into()),
7656                            availability: Some(availability),
7657                            ..Default::default()
7658                        })
7659                );
7660                decl.capabilities = Some(vec![
7661                    fdecl::Capability::Service(fdecl::Service {
7662                        name: Some("fuchsia.examples.Echo".to_string()),
7663                        source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
7664                        ..Default::default()
7665                    }),
7666                ]);
7667                decl
7668            },
7669            result = {
7670                Err(ErrorList::new(vec![
7671                    Error::availability_must_be_optional(
7672                        DeclType::OfferService,
7673                        "availability",
7674                        Some(&"fuchsia.examples.Echo".to_string()),
7675                    ),
7676                    Error::availability_must_be_optional(
7677                        DeclType::OfferService,
7678                        "availability",
7679                        Some(&"fuchsia.examples.Echo".to_string()),
7680                    ),
7681                ]))
7682            },
7683        },
7684        test_validate_offers_not_required_invalid_source_protocol => {
7685            input = {
7686                let mut decl = generate_offer_different_source_and_availability_decl(
7687                    |source, availability, target_name|
7688                        fdecl::Offer::Protocol(fdecl::OfferProtocol {
7689                            source: Some(source),
7690                            source_name: Some("fuchsia.examples.Echo".to_string()),
7691                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7692                                name: "sink".to_string(),
7693                                collection: None,
7694                            })),
7695                            target_name: Some(target_name.into()),
7696                            dependency_type: Some(fdecl::DependencyType::Strong),
7697                            availability: Some(availability),
7698                            ..Default::default()
7699                        })
7700                );
7701                decl.capabilities = Some(vec![
7702                    fdecl::Capability::Protocol(fdecl::Protocol {
7703                        name: Some("fuchsia.examples.Echo".to_string()),
7704                        source_path: Some("/svc/fuchsia.examples.Echo".to_string()),
7705                        ..Default::default()
7706                    }),
7707                ]);
7708                decl
7709            },
7710            result = {
7711                Err(ErrorList::new(vec![
7712                    Error::availability_must_be_optional(
7713                        DeclType::OfferProtocol,
7714                        "availability",
7715                        Some(&"fuchsia.examples.Echo".to_string()),
7716                    ),
7717                    Error::availability_must_be_optional(
7718                        DeclType::OfferProtocol,
7719                        "availability",
7720                        Some(&"fuchsia.examples.Echo".to_string()),
7721                    ),
7722                ]))
7723            },
7724        },
7725        test_validate_offers_not_required_invalid_source_directory => {
7726            input = {
7727                let mut decl = generate_offer_different_source_and_availability_decl(
7728                    |source, availability, target_name|
7729                        fdecl::Offer::Directory(fdecl::OfferDirectory {
7730                            source: Some(source),
7731                            source_name: Some("assets".to_string()),
7732                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7733                                name: "sink".to_string(),
7734                                collection: None,
7735                            })),
7736                            target_name: Some(target_name.into()),
7737                            rights: Some(fio::Operations::CONNECT),
7738                            subdir: None,
7739                            dependency_type: Some(fdecl::DependencyType::Weak),
7740                            availability: Some(availability),
7741                            ..Default::default()
7742                        })
7743                );
7744                decl.capabilities = Some(vec![
7745                    fdecl::Capability::Directory(fdecl::Directory {
7746                        name: Some("assets".to_string()),
7747                        source_path: Some("/assets".to_string()),
7748                        rights: Some(fio::Operations::CONNECT),
7749                        ..Default::default()
7750                    }),
7751                ]);
7752                decl
7753            },
7754            result = {
7755                Err(ErrorList::new(vec![
7756                    Error::availability_must_be_optional(
7757                        DeclType::OfferDirectory,
7758                        "availability",
7759                        Some(&"assets".to_string()),
7760                    ),
7761                    Error::availability_must_be_optional(
7762                        DeclType::OfferDirectory,
7763                        "availability",
7764                        Some(&"assets".to_string()),
7765                    ),
7766                ]))
7767            },
7768        },
7769        test_validate_offers_not_required_invalid_source_storage => {
7770            input = {
7771                let mut decl = new_component_decl();
7772                decl.children = Some(vec![
7773                    fdecl::Child {
7774                        name: Some("sink".to_string()),
7775                        url: Some("fuchsia-pkg://fuchsia.com/sink#meta/sink.cm".to_string()),
7776                        startup: Some(fdecl::StartupMode::Lazy),
7777                        on_terminate: None,
7778                        environment: None,
7779                        ..Default::default()
7780                    },
7781                ]);
7782                decl.capabilities = Some(vec![
7783                    fdecl::Capability::Storage(fdecl::Storage {
7784                        name: Some("data".to_string()),
7785                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
7786                        backing_dir: Some("minfs".to_string()),
7787                        subdir: None,
7788                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
7789                        ..Default::default()
7790                    }),
7791                ]);
7792                let new_offer = |source: fdecl::Ref, availability: fdecl::Availability,
7793                                        target_name: &str|
7794                {
7795                    fdecl::Offer::Storage(fdecl::OfferStorage {
7796                        source: Some(source),
7797                        source_name: Some("data".to_string()),
7798                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7799                            name: "sink".to_string(),
7800                            collection: None,
7801                        })),
7802                        target_name: Some(target_name.into()),
7803                        availability: Some(availability),
7804                        ..Default::default()
7805                    })
7806                };
7807                decl.offers = Some(vec![
7808                    // These offers are fine, offers with a source of parent or void can be
7809                    // optional.
7810                    new_offer(
7811                        fdecl::Ref::Parent(fdecl::ParentRef {}),
7812                        fdecl::Availability::Required,
7813                        "data0",
7814                    ),
7815                    new_offer(
7816                        fdecl::Ref::Parent(fdecl::ParentRef {}),
7817                        fdecl::Availability::Optional,
7818                        "data1",
7819                    ),
7820                    new_offer(
7821                        fdecl::Ref::Parent(fdecl::ParentRef {}),
7822                        fdecl::Availability::SameAsTarget,
7823                        "data2",
7824                    ),
7825                    new_offer(
7826                        fdecl::Ref::VoidType(fdecl::VoidRef {}),
7827                        fdecl::Availability::Optional,
7828                        "data3",
7829                    ),
7830                    // These offers are not fine, offers with a source other than parent or void
7831                    // must be required.
7832                    new_offer(
7833                        fdecl::Ref::Self_(fdecl::SelfRef {}),
7834                        fdecl::Availability::Optional,
7835                        "data4",
7836                    ),
7837                    new_offer(
7838                        fdecl::Ref::Self_(fdecl::SelfRef {}),
7839                        fdecl::Availability::SameAsTarget,
7840                        "data5",
7841                    ),
7842                    // These offers are also not fine, offers with a source of void must be optional
7843                    new_offer(
7844                        fdecl::Ref::VoidType(fdecl::VoidRef {}),
7845                        fdecl::Availability::Required,
7846                        "data6",
7847                    ),
7848                    new_offer(
7849                        fdecl::Ref::VoidType(fdecl::VoidRef {}),
7850                        fdecl::Availability::SameAsTarget,
7851                        "data7",
7852                    ),
7853                ]);
7854                decl
7855            },
7856            result = {
7857                Err(ErrorList::new(vec![
7858                    Error::availability_must_be_optional(
7859                        DeclType::OfferStorage,
7860                        "availability",
7861                        Some(&"data".to_string()),
7862                    ),
7863                    Error::availability_must_be_optional(
7864                        DeclType::OfferStorage,
7865                        "availability",
7866                        Some(&"data".to_string()),
7867                    ),
7868                ]))
7869            },
7870        },
7871
7872        test_validate_offers_valid_service_aggregation => {
7873            input = {
7874                let mut decl = new_component_decl();
7875                decl.offers = Some(vec![
7876                    fdecl::Offer::Service(fdecl::OfferService {
7877                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
7878                            name: "coll_a".to_string()
7879                        })),
7880                        source_name: Some("fuchsia.logger.Log".to_string()),
7881                        target: Some(fdecl::Ref::Child(
7882                            fdecl::ChildRef {
7883                                name: "child_c".to_string(),
7884                                collection: None,
7885                            }
7886                        )),
7887                        target_name: Some("fuchsia.logger.Log".to_string()),
7888                        source_instance_filter: None,
7889                        ..Default::default()
7890                    }),
7891                    fdecl::Offer::Service(fdecl::OfferService {
7892                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
7893                            name: "coll_b".to_string()
7894                        })),
7895                        source_name: Some("fuchsia.logger.Log".to_string()),
7896                        target: Some(fdecl::Ref::Child(
7897                            fdecl::ChildRef {
7898                                name: "child_c".to_string(),
7899                                collection: None,
7900                            }
7901                        )),
7902                        target_name: Some("fuchsia.logger.Log".to_string()),
7903                        source_instance_filter: Some(vec!["a_different_default".to_string()]),
7904                        ..Default::default()
7905                    })
7906                ]);
7907                decl.children = Some(vec![
7908                    fdecl::Child {
7909                        name: Some("child_c".to_string()),
7910                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
7911                        startup: Some(fdecl::StartupMode::Lazy),
7912                        ..Default::default()
7913                    },
7914                ]);
7915                decl.collections = Some(vec![
7916                    fdecl::Collection {
7917                        name: Some("coll_a".into()),
7918                        durability: Some(fdecl::Durability::Transient),
7919                        ..Default::default()
7920                    },
7921                    fdecl::Collection {
7922                        name: Some("coll_b".into()),
7923                        durability: Some(fdecl::Durability::Transient),
7924                        ..Default::default()
7925                    },
7926                ]);
7927                decl
7928            },
7929            result = Ok(()),
7930        },
7931
7932        // dictionaries
7933        test_validate_source_dictionary => {
7934            input = fdecl::Component {
7935                program: Some(fdecl::Program {
7936                    runner: Some("elf".into()),
7937                    info: Some(fdata::Dictionary {
7938                        entries: None,
7939                        ..Default::default()
7940                    }),
7941                    ..Default::default()
7942                }),
7943                uses: Some(vec![
7944                    fdecl::Use::Protocol(fdecl::UseProtocol {
7945                        dependency_type: Some(fdecl::DependencyType::Strong),
7946                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
7947                        source_dictionary: Some("bad//".into()),
7948                        source_name: Some("foo".into()),
7949                        target_path: Some("/svc/foo".into()),
7950                        ..Default::default()
7951                    }),
7952                ]),
7953                exposes: Some(vec![
7954                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
7955                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7956                            name: "missing".into(),
7957                            collection: None,
7958                        })),
7959                        source_dictionary: Some("in/dict".into()),
7960                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
7961                        source_name: Some("foo".into()),
7962                        target_name: Some("bar".into()),
7963                        ..Default::default()
7964                    }),
7965                ]),
7966                offers: Some(vec![
7967                    fdecl::Offer::Service(fdecl::OfferService {
7968                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
7969                        source_dictionary: Some("bad//".into()),
7970                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7971                            name: "child".into(),
7972                            collection: None,
7973                        })),
7974                        source_name: Some("foo".into()),
7975                        target_name: Some("bar".into()),
7976                        ..Default::default()
7977                    }),
7978                ]),
7979                children: Some(vec![
7980                    fdecl::Child {
7981                        name: Some("child".into()),
7982                        url: Some("fuchsia-pkg://child".into()),
7983                        startup: Some(fdecl::StartupMode::Lazy),
7984                        ..Default::default()
7985                    },
7986                ]),
7987                ..Default::default()
7988            },
7989            result = Err(ErrorList::new(vec![
7990                Error::invalid_field(DeclType::UseProtocol, "source_dictionary"),
7991                Error::invalid_child(DeclType::ExposeDirectory, "source", "missing"),
7992                Error::invalid_field(DeclType::OfferService, "source_dictionary"),
7993            ])),
7994        },
7995        test_validate_dictionary_too_long => {
7996            input = fdecl::Component {
7997                program: Some(fdecl::Program {
7998                    runner: Some("elf".into()),
7999                    info: Some(fdata::Dictionary {
8000                        entries: None,
8001                        ..Default::default()
8002                    }),
8003                    ..Default::default()
8004                }),
8005                uses: Some(vec![
8006                    fdecl::Use::Protocol(fdecl::UseProtocol {
8007                        dependency_type: Some(fdecl::DependencyType::Strong),
8008                        source: Some(fdecl::Ref::Parent( fdecl::ParentRef {} )),
8009                        source_dictionary: Some("a".repeat(4096)),
8010                        source_name: Some("foo".into()),
8011                        target_path: Some("/svc/foo".into()),
8012                        ..Default::default()
8013                    }),
8014                ]),
8015                ..Default::default()
8016            },
8017            result = Err(ErrorList::new(vec![
8018                Error::field_too_long(DeclType::UseProtocol, "source_dictionary"),
8019            ])),
8020        },
8021
8022        // environments
8023        test_validate_environment_empty => {
8024            input = {
8025                let mut decl = new_component_decl();
8026                decl.environments = Some(vec![fdecl::Environment {
8027                    name: None,
8028                    extends: None,
8029                    runners: None,
8030                    resolvers: None,
8031                    stop_timeout_ms: None,
8032                    debug_capabilities: None,
8033                    ..Default::default()
8034                }]);
8035                decl
8036            },
8037            result = Err(ErrorList::new(vec![
8038                Error::missing_field(DeclType::Environment, "name"),
8039                Error::missing_field(DeclType::Environment, "extends"),
8040            ])),
8041        },
8042
8043        test_validate_environment_no_stop_timeout => {
8044            input = {
8045                let mut decl = new_component_decl();
8046                decl.environments = Some(vec![fdecl::Environment {
8047                    name: Some("env".to_string()),
8048                    extends: Some(fdecl::EnvironmentExtends::None),
8049                    runners: None,
8050                    resolvers: None,
8051                    stop_timeout_ms: None,
8052                    ..Default::default()
8053                }]);
8054                decl
8055            },
8056            result = Err(ErrorList::new(vec![Error::missing_field(DeclType::Environment, "stop_timeout_ms")])),
8057        },
8058
8059        test_validate_environment_extends_stop_timeout => {
8060            input = {  let mut decl = new_component_decl();
8061                decl.environments = Some(vec![fdecl::Environment {
8062                    name: Some("env".to_string()),
8063                    extends: Some(fdecl::EnvironmentExtends::Realm),
8064                    runners: None,
8065                    resolvers: None,
8066                    stop_timeout_ms: None,
8067                    ..Default::default()
8068                }]);
8069                decl
8070            },
8071            result = Ok(()),
8072        },
8073        test_validate_environment_long_identifiers => {
8074            input = {
8075                let mut decl = new_component_decl();
8076                decl.environments = Some(vec![fdecl::Environment {
8077                    name: Some("a".repeat(256)),
8078                    extends: Some(fdecl::EnvironmentExtends::None),
8079                    runners: Some(vec![
8080                        fdecl::RunnerRegistration {
8081                            source_name: Some("a".repeat(256)),
8082                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8083                            target_name: Some("a".repeat(256)),
8084                            ..Default::default()
8085                        },
8086                    ]),
8087                    resolvers: Some(vec![
8088                        fdecl::ResolverRegistration {
8089                            resolver: Some("a".repeat(256)),
8090                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8091                            scheme: Some("a".repeat(256)),
8092                            ..Default::default()
8093                        },
8094                    ]),
8095                    stop_timeout_ms: Some(1234),
8096                    ..Default::default()
8097                }]);
8098                decl
8099            },
8100            result = Err(ErrorList::new(vec![
8101                Error::field_too_long(DeclType::Environment, "name"),
8102                Error::field_too_long(DeclType::RunnerRegistration, "source_name"),
8103                Error::field_too_long(DeclType::RunnerRegistration, "target_name"),
8104                Error::field_too_long(DeclType::ResolverRegistration, "resolver"),
8105                Error::field_too_long(DeclType::ResolverRegistration, "scheme"),
8106            ])),
8107        },
8108        test_validate_environment_empty_runner_resolver_fields => {
8109            input = {
8110                let mut decl = new_component_decl();
8111                decl.environments = Some(vec![fdecl::Environment {
8112                    name: Some("a".to_string()),
8113                    extends: Some(fdecl::EnvironmentExtends::None),
8114                    runners: Some(vec![
8115                        fdecl::RunnerRegistration {
8116                            source_name: None,
8117                            source: None,
8118                            target_name: None,
8119                            ..Default::default()
8120                        },
8121                    ]),
8122                    resolvers: Some(vec![
8123                        fdecl::ResolverRegistration {
8124                            resolver: None,
8125                            source: None,
8126                            scheme: None,
8127                            ..Default::default()
8128                        },
8129                    ]),
8130                    stop_timeout_ms: Some(1234),
8131                    ..Default::default()
8132                }]);
8133                decl
8134            },
8135            result = Err(ErrorList::new(vec![
8136                Error::missing_field(DeclType::RunnerRegistration, "source_name"),
8137                Error::missing_field(DeclType::RunnerRegistration, "source"),
8138                Error::missing_field(DeclType::RunnerRegistration, "target_name"),
8139                Error::missing_field(DeclType::ResolverRegistration, "resolver"),
8140                Error::missing_field(DeclType::ResolverRegistration, "source"),
8141                Error::missing_field(DeclType::ResolverRegistration, "scheme"),
8142            ])),
8143        },
8144        test_validate_environment_invalid_fields => {
8145            input = {
8146                let mut decl = new_component_decl();
8147                decl.environments = Some(vec![fdecl::Environment {
8148                    name: Some("a".to_string()),
8149                    extends: Some(fdecl::EnvironmentExtends::None),
8150                    runners: Some(vec![
8151                        fdecl::RunnerRegistration {
8152                            source_name: Some("^a".to_string()),
8153                            source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
8154                            target_name: Some("%a".to_string()),
8155                            ..Default::default()
8156                        },
8157                    ]),
8158                    resolvers: Some(vec![
8159                        fdecl::ResolverRegistration {
8160                            resolver: Some("^a".to_string()),
8161                            source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef{})),
8162                            scheme: Some("9scheme".to_string()),
8163                            ..Default::default()
8164                        },
8165                    ]),
8166                    stop_timeout_ms: Some(1234),
8167                    ..Default::default()
8168                }]);
8169                decl
8170            },
8171            result = Err(ErrorList::new(vec![
8172                Error::invalid_field(DeclType::RunnerRegistration, "source_name"),
8173                Error::invalid_field(DeclType::RunnerRegistration, "source"),
8174                Error::invalid_field(DeclType::RunnerRegistration, "target_name"),
8175                Error::invalid_field(DeclType::ResolverRegistration, "resolver"),
8176                Error::invalid_field(DeclType::ResolverRegistration, "source"),
8177                Error::invalid_field(DeclType::ResolverRegistration, "scheme"),
8178            ])),
8179        },
8180        test_validate_environment_missing_runner => {
8181            input = {
8182                let mut decl = new_component_decl();
8183                decl.environments = Some(vec![fdecl::Environment {
8184                    name: Some("a".to_string()),
8185                    extends: Some(fdecl::EnvironmentExtends::None),
8186                    runners: Some(vec![
8187                        fdecl::RunnerRegistration {
8188                            source_name: Some("dart".to_string()),
8189                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
8190                            target_name: Some("dart".to_string()),
8191                            ..Default::default()
8192                        },
8193                    ]),
8194                    resolvers: None,
8195                    stop_timeout_ms: Some(1234),
8196                    ..Default::default()
8197                }]);
8198                decl
8199            },
8200            result = Err(ErrorList::new(vec![
8201                Error::invalid_runner(DeclType::RunnerRegistration, "source_name", "dart"),
8202            ])),
8203        },
8204        test_validate_environment_duplicate_registrations => {
8205            input = {
8206                let mut decl = new_component_decl();
8207                decl.environments = Some(vec![fdecl::Environment {
8208                    name: Some("a".to_string()),
8209                    extends: Some(fdecl::EnvironmentExtends::None),
8210                    runners: Some(vec![
8211                        fdecl::RunnerRegistration {
8212                            source_name: Some("dart".to_string()),
8213                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8214                            target_name: Some("dart".to_string()),
8215                            ..Default::default()
8216                        },
8217                        fdecl::RunnerRegistration {
8218                            source_name: Some("other-dart".to_string()),
8219                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8220                            target_name: Some("dart".to_string()),
8221                            ..Default::default()
8222                        },
8223                    ]),
8224                    resolvers: Some(vec![
8225                        fdecl::ResolverRegistration {
8226                            resolver: Some("pkg_resolver".to_string()),
8227                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8228                            scheme: Some("fuchsia-pkg".to_string()),
8229                            ..Default::default()
8230                        },
8231                        fdecl::ResolverRegistration {
8232                            resolver: Some("base_resolver".to_string()),
8233                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
8234                            scheme: Some("fuchsia-pkg".to_string()),
8235                            ..Default::default()
8236                        },
8237                    ]),
8238                    stop_timeout_ms: Some(1234),
8239                    ..Default::default()
8240                }]);
8241                decl
8242            },
8243            result = Err(ErrorList::new(vec![
8244                Error::duplicate_field(DeclType::RunnerRegistration, "target_name", "dart"),
8245                Error::duplicate_field(DeclType::ResolverRegistration, "scheme", "fuchsia-pkg"),
8246            ])),
8247        },
8248        test_validate_environment_from_missing_child => {
8249            input = {
8250                let mut decl = new_component_decl();
8251                decl.environments = Some(vec![fdecl::Environment {
8252                    name: Some("a".to_string()),
8253                    extends: Some(fdecl::EnvironmentExtends::None),
8254                    runners: Some(vec![
8255                        fdecl::RunnerRegistration {
8256                            source_name: Some("elf".to_string()),
8257                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8258                                name: "missing".to_string(),
8259                                collection: None,
8260                            })),
8261                            target_name: Some("elf".to_string()),
8262                            ..Default::default()
8263                        },
8264                    ]),
8265                    resolvers: Some(vec![
8266                        fdecl::ResolverRegistration {
8267                            resolver: Some("pkg_resolver".to_string()),
8268                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8269                                name: "missing".to_string(),
8270                                collection: None,
8271                            })),
8272                            scheme: Some("fuchsia-pkg".to_string()),
8273                            ..Default::default()
8274                        },
8275                    ]),
8276                    stop_timeout_ms: Some(1234),
8277                    ..Default::default()
8278                }]);
8279                decl
8280            },
8281            result = Err(ErrorList::new(vec![
8282                Error::invalid_child(DeclType::RunnerRegistration, "source", "missing"),
8283                Error::invalid_child(DeclType::ResolverRegistration, "source", "missing"),
8284            ])),
8285        },
8286        test_validate_environment_runner_child_cycle => {
8287            input = {
8288                let mut decl = new_component_decl();
8289                decl.environments = Some(vec![fdecl::Environment {
8290                    name: Some("env".to_string()),
8291                    extends: Some(fdecl::EnvironmentExtends::None),
8292                    runners: Some(vec![
8293                        fdecl::RunnerRegistration {
8294                            source_name: Some("elf".to_string()),
8295                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8296                                name: "child".to_string(),
8297                                collection: None,
8298                            })),
8299                            target_name: Some("elf".to_string()),
8300                            ..Default::default()
8301                        },
8302                    ]),
8303                    resolvers: None,
8304                    stop_timeout_ms: Some(1234),
8305                    ..Default::default()
8306                }]);
8307                decl.children = Some(vec![fdecl::Child {
8308                    name: Some("child".to_string()),
8309                    startup: Some(fdecl::StartupMode::Lazy),
8310                    on_terminate: None,
8311                    url: Some("fuchsia-pkg://child".to_string()),
8312                    environment: Some("env".to_string()),
8313                    ..Default::default()
8314                }]);
8315                decl
8316            },
8317            result = Err(ErrorList::new(vec![
8318                Error::dependency_cycle(
8319                    directed_graph::Error::CyclesDetected([vec!["child child", "environment env", "child child"]].iter().cloned().collect()).format_cycle()
8320                ),
8321            ])),
8322        },
8323        test_validate_environment_resolver_child_cycle => {
8324            input = {
8325                let mut decl = new_component_decl();
8326                decl.environments = Some(vec![fdecl::Environment {
8327                    name: Some("env".to_string()),
8328                    extends: Some(fdecl::EnvironmentExtends::None),
8329                    runners: None,
8330                    resolvers: Some(vec![
8331                        fdecl::ResolverRegistration {
8332                            resolver: Some("pkg_resolver".to_string()),
8333                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8334                                name: "child".to_string(),
8335                                collection: None,
8336                            })),
8337                            scheme: Some("fuchsia-pkg".to_string()),
8338                            ..Default::default()
8339                        },
8340                    ]),
8341                    stop_timeout_ms: Some(1234),
8342                    ..Default::default()
8343                }]);
8344                decl.children = Some(vec![fdecl::Child {
8345                    name: Some("child".to_string()),
8346                    startup: Some(fdecl::StartupMode::Lazy),
8347                    on_terminate: None,
8348                    url: Some("fuchsia-pkg://child".to_string()),
8349                    environment: Some("env".to_string()),
8350                    ..Default::default()
8351                }]);
8352                decl
8353            },
8354            result = Err(ErrorList::new(vec![
8355                Error::dependency_cycle(
8356                    directed_graph::Error::CyclesDetected([vec!["child child", "environment env", "child child"]].iter().cloned().collect()).format_cycle()
8357                ),
8358            ])),
8359        },
8360        test_validate_environment_resolver_multiple_children_cycle => {
8361            input = {
8362                let mut decl = new_component_decl();
8363                decl.environments = Some(vec![fdecl::Environment {
8364                    name: Some("env".to_string()),
8365                    extends: Some(fdecl::EnvironmentExtends::None),
8366                    runners: None,
8367                    resolvers: Some(vec![
8368                        fdecl::ResolverRegistration {
8369                            resolver: Some("pkg_resolver".to_string()),
8370                            source: Some(fdecl::Ref::Child(fdecl::ChildRef{
8371                                name: "a".to_string(),
8372                                collection: None,
8373                            })),
8374                            scheme: Some("fuchsia-pkg".to_string()),
8375                            ..Default::default()
8376                        },
8377                    ]),
8378                    stop_timeout_ms: Some(1234),
8379                    ..Default::default()
8380                }]);
8381                decl.children = Some(vec![
8382                    fdecl::Child {
8383                        name: Some("a".to_string()),
8384                        startup: Some(fdecl::StartupMode::Lazy),
8385                        on_terminate: None,
8386                        url: Some("fuchsia-pkg://child-a".to_string()),
8387                        environment: None,
8388                        ..Default::default()
8389                    },
8390                    fdecl::Child {
8391                        name: Some("b".to_string()),
8392                        startup: Some(fdecl::StartupMode::Lazy),
8393                        on_terminate: None,
8394                        url: Some("fuchsia-pkg://child-b".to_string()),
8395                        environment: Some("env".to_string()),
8396                        ..Default::default()
8397                    },
8398                ]);
8399                decl.offers = Some(vec![fdecl::Offer::Service(fdecl::OfferService {
8400                    source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8401                        name: "b".to_string(),
8402                        collection: None,
8403                    })),
8404                    source_name: Some("thing".to_string()),
8405                    target: Some(fdecl::Ref::Child(
8406                    fdecl::ChildRef {
8407                        name: "a".to_string(),
8408                        collection: None,
8409                    }
8410                    )),
8411                    target_name: Some("thing".to_string()),
8412                    ..Default::default()
8413                })]);
8414                decl
8415            },
8416            result = Err(ErrorList::new(vec![
8417                Error::dependency_cycle(
8418                    directed_graph::Error::CyclesDetected([vec!["child a", "environment env", "child b", "child a"]].iter().cloned().collect()).format_cycle()
8419                ),
8420            ])),
8421        },
8422        test_validate_environment_debug_empty => {
8423            input = {
8424                let mut decl = new_component_decl();
8425                decl.environments = Some(vec![
8426                    fdecl::Environment {
8427                        name: Some("a".to_string()),
8428                        extends: Some(fdecl::EnvironmentExtends::None),
8429                        stop_timeout_ms: Some(2),
8430                        debug_capabilities:Some(vec![
8431                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8432                                source: None,
8433                                source_name: None,
8434                                target_name: None,
8435                                ..Default::default()
8436                            }),
8437                    ]),
8438                    ..Default::default()
8439                }]);
8440                decl
8441            },
8442            result = Err(ErrorList::new(vec![
8443                Error::missing_field(DeclType::DebugProtocolRegistration, "source"),
8444                Error::missing_field(DeclType::DebugProtocolRegistration, "source_name"),
8445                Error::missing_field(DeclType::DebugProtocolRegistration, "target_name"),
8446            ])),
8447        },
8448        test_validate_environment_debug_log_identifier => {
8449            input = {
8450                let mut decl = new_component_decl();
8451                decl.environments = Some(vec![
8452                    fdecl::Environment {
8453                        name: Some("a".to_string()),
8454                        extends: Some(fdecl::EnvironmentExtends::None),
8455                        stop_timeout_ms: Some(2),
8456                        debug_capabilities:Some(vec![
8457                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8458                                source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8459                                    name: "a".repeat(256),
8460                                    collection: None,
8461                                })),
8462                                source_name: Some(format!("{}", "a".repeat(256))),
8463                                target_name: Some(format!("{}", "b".repeat(256))),
8464                                ..Default::default()
8465                            }),
8466                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8467                                source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8468                                source_name: Some("a".to_string()),
8469                                target_name: Some(format!("{}", "b".repeat(256))),
8470                                ..Default::default()
8471                            }),
8472                    ]),
8473                    ..Default::default()
8474                }]);
8475                decl
8476            },
8477            result = Err(ErrorList::new(vec![
8478                Error::field_too_long(DeclType::DebugProtocolRegistration, "source.child.name"),
8479                Error::field_too_long(DeclType::DebugProtocolRegistration, "source_name"),
8480                Error::field_too_long(DeclType::DebugProtocolRegistration, "target_name"),
8481                Error::field_too_long(DeclType::DebugProtocolRegistration, "target_name"),
8482            ])),
8483        },
8484        test_validate_environment_debug_log_extraneous => {
8485            input = {
8486                let mut decl = new_component_decl();
8487                decl.environments = Some(vec![
8488                    fdecl::Environment {
8489                        name: Some("a".to_string()),
8490                        extends: Some(fdecl::EnvironmentExtends::None),
8491                        stop_timeout_ms: Some(2),
8492                        debug_capabilities:Some(vec![
8493                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8494                                source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8495                                    name: "logger".to_string(),
8496                                    collection: Some("modular".to_string()),
8497                                })),
8498                                source_name: Some("fuchsia.logger.Log".to_string()),
8499                                target_name: Some("fuchsia.logger.Log".to_string()),
8500                                ..Default::default()
8501                            }),
8502                    ]),
8503                    ..Default::default()
8504                }]);
8505                decl
8506            },
8507            result = Err(ErrorList::new(vec![
8508                Error::extraneous_field(DeclType::DebugProtocolRegistration, "source.child.collection"),
8509            ])),
8510        },
8511        test_validate_environment_debug_log_invalid_identifiers => {
8512            input = {
8513                let mut decl = new_component_decl();
8514                decl.environments = Some(vec![
8515                    fdecl::Environment {
8516                        name: Some("a".to_string()),
8517                        extends: Some(fdecl::EnvironmentExtends::None),
8518                        stop_timeout_ms: Some(2),
8519                        debug_capabilities:Some(vec![
8520                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8521                                source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8522                                    name: "^bad".to_string(),
8523                                    collection: None,
8524                                })),
8525                                source_name: Some("foo/".to_string()),
8526                                target_name: Some("/".to_string()),
8527                                ..Default::default()
8528                            }),
8529                    ]),
8530                    ..Default::default()
8531                }]);
8532                decl
8533            },
8534            result = Err(ErrorList::new(vec![
8535                Error::invalid_field(DeclType::DebugProtocolRegistration, "source.child.name"),
8536                Error::invalid_field(DeclType::DebugProtocolRegistration, "source_name"),
8537                Error::invalid_field(DeclType::DebugProtocolRegistration, "target_name"),
8538            ])),
8539        },
8540        test_validate_environment_debug_log_invalid_child => {
8541            input = {
8542                let mut decl = new_component_decl();
8543                decl.environments = Some(vec![
8544                    fdecl::Environment {
8545                        name: Some("a".to_string()),
8546                        extends: Some(fdecl::EnvironmentExtends::None),
8547                        stop_timeout_ms: Some(2),
8548                        debug_capabilities:Some(vec![
8549                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8550                                source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8551                                    name: "logger".to_string(),
8552                                    collection: None,
8553                                })),
8554                                source_name: Some("fuchsia.logger.LegacyLog".to_string()),
8555                                target_name: Some("fuchsia.logger.LegacyLog".to_string()),
8556                                ..Default::default()
8557                            }),
8558                    ]),
8559                    ..Default::default()
8560                }]);
8561                decl.children = Some(vec![
8562                    fdecl::Child {
8563                        name: Some("netstack".to_string()),
8564                        url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
8565                        startup: Some(fdecl::StartupMode::Lazy),
8566                        on_terminate: None,
8567                        environment: None,
8568                        ..Default::default()
8569                    },
8570                ]);
8571                decl
8572            },
8573            result = Err(ErrorList::new(vec![
8574                Error::invalid_child(DeclType::DebugProtocolRegistration, "source", "logger"),
8575
8576            ])),
8577        },
8578        test_validate_environment_debug_source_capability => {
8579            input = {
8580                let mut decl = new_component_decl();
8581                decl.environments = Some(vec![
8582                    fdecl::Environment {
8583                        name: Some("a".to_string()),
8584                        extends: Some(fdecl::EnvironmentExtends::None),
8585                        stop_timeout_ms: Some(2),
8586                        debug_capabilities:Some(vec![
8587                            fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
8588                                source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
8589                                    name: "storage".to_string(),
8590                                })),
8591                                source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
8592                                target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
8593                                ..Default::default()
8594                            }),
8595                    ]),
8596                    ..Default::default()
8597                }]);
8598                decl
8599            },
8600            result = Err(ErrorList::new(vec![
8601                Error::invalid_field(DeclType::DebugProtocolRegistration, "source"),
8602            ])),
8603        },
8604
8605        // children
8606        test_validate_children_empty => {
8607            input = {
8608                let mut decl = new_component_decl();
8609                decl.children = Some(vec![fdecl::Child{
8610                    name: None,
8611                    url: None,
8612                    startup: None,
8613                    on_terminate: None,
8614                    environment: None,
8615                    ..Default::default()
8616                }]);
8617                decl
8618            },
8619            result = Err(ErrorList::new(vec![
8620                Error::missing_field(DeclType::Child, "name"),
8621                Error::missing_field(DeclType::Child, "url"),
8622                Error::missing_field(DeclType::Child, "startup"),
8623                // `on_terminate` is allowed to be None
8624            ])),
8625        },
8626        test_validate_children_invalid_identifiers => {
8627            input = {
8628                let mut decl = new_component_decl();
8629                decl.children = Some(vec![fdecl::Child{
8630                    name: Some("^bad".to_string()),
8631                    url: Some("scheme://invalid-port:99999999/path#frag".to_string()),
8632                    startup: Some(fdecl::StartupMode::Lazy),
8633                    on_terminate: None,
8634                    environment: None,
8635                    ..Default::default()
8636                }]);
8637                decl
8638            },
8639            result = Err(ErrorList::new(vec![
8640                Error::invalid_field(DeclType::Child, "name"),
8641                Error::invalid_url(DeclType::Child, "url", "\"scheme://invalid-port:99999999/path#frag\": Malformed URL: InvalidPort."),
8642            ])),
8643        },
8644        test_validate_children_long_identifiers => {
8645            input = {
8646                let mut decl = new_component_decl();
8647                decl.children = Some(vec![fdecl::Child{
8648                    name: Some("a".repeat(1025)),
8649                    url: Some(format!("fuchsia-pkg://{}", "a".repeat(4083))),
8650                    startup: Some(fdecl::StartupMode::Lazy),
8651                    on_terminate: None,
8652                    environment: Some("a".repeat(1025)),
8653                    ..Default::default()
8654                }]);
8655                decl
8656            },
8657            result = Err(ErrorList::new(vec![
8658                Error::field_too_long(DeclType::Child, "name"),
8659                Error::field_too_long(DeclType::Child, "url"),
8660                Error::field_too_long(DeclType::Child, "environment"),
8661                Error::invalid_environment(DeclType::Child, "environment", "a".repeat(1025)),
8662            ])),
8663        },
8664        test_validate_child_references_unknown_env => {
8665            input = {
8666                let mut decl = new_component_decl();
8667                decl.children = Some(vec![fdecl::Child{
8668                    name: Some("foo".to_string()),
8669                    url: Some("fuchsia-pkg://foo".to_string()),
8670                    startup: Some(fdecl::StartupMode::Lazy),
8671                    on_terminate: None,
8672                    environment: Some("test_env".to_string()),
8673                    ..Default::default()
8674                }]);
8675                decl
8676            },
8677            result = Err(ErrorList::new(vec![
8678                Error::invalid_environment(DeclType::Child, "environment", "test_env"),
8679            ])),
8680        },
8681
8682        // collections
8683        test_validate_collections_empty => {
8684            input = {
8685                let mut decl = new_component_decl();
8686                decl.collections = Some(vec![fdecl::Collection{
8687                    name: None,
8688                    durability: None,
8689                    environment: None,
8690                    allowed_offers: None,
8691                    allow_long_names: None,
8692                    ..Default::default()
8693                }]);
8694                decl
8695            },
8696            result = Err(ErrorList::new(vec![
8697                Error::missing_field(DeclType::Collection, "name"),
8698                Error::missing_field(DeclType::Collection, "durability"),
8699            ])),
8700        },
8701        test_validate_collections_invalid_identifiers => {
8702            input = {
8703                let mut decl = new_component_decl();
8704                decl.collections = Some(vec![fdecl::Collection{
8705                    name: Some("^bad".to_string()),
8706                    durability: Some(fdecl::Durability::Transient),
8707                    environment: None,
8708                    allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
8709                    allow_long_names: None,
8710                    ..Default::default()
8711                }]);
8712                decl
8713            },
8714            result = Err(ErrorList::new(vec![
8715                Error::invalid_field(DeclType::Collection, "name"),
8716            ])),
8717        },
8718        test_validate_collections_long_identifiers => {
8719            input = {
8720                let mut decl = new_component_decl();
8721                decl.collections = Some(vec![fdecl::Collection{
8722                    name: Some("a".repeat(1025)),
8723                    durability: Some(fdecl::Durability::Transient),
8724                    environment: None,
8725                    allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
8726                    allow_long_names: None,
8727                    ..Default::default()
8728                }]);
8729                decl
8730            },
8731            result = Err(ErrorList::new(vec![
8732                Error::field_too_long(DeclType::Collection, "name"),
8733            ])),
8734        },
8735        test_validate_collection_references_unknown_env => {
8736            input = {
8737                let mut decl = new_component_decl();
8738                decl.collections = Some(vec![fdecl::Collection {
8739                    name: Some("foo".to_string()),
8740                    durability: Some(fdecl::Durability::Transient),
8741                    environment: Some("test_env".to_string()),
8742                    allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
8743                    allow_long_names: None,
8744                    ..Default::default()
8745                }]);
8746                decl
8747            },
8748            result = Err(ErrorList::new(vec![
8749                Error::invalid_environment(DeclType::Collection, "environment", "test_env"),
8750            ])),
8751        },
8752
8753        // capabilities
8754        test_validate_capabilities_empty => {
8755            input = {
8756                let mut decl = new_component_decl();
8757                decl.capabilities = Some(vec![
8758                    fdecl::Capability::Service(fdecl::Service {
8759                        name: None,
8760                        source_path: None,
8761                        ..Default::default()
8762                    }),
8763                    fdecl::Capability::Protocol(fdecl::Protocol {
8764                        name: None,
8765                        source_path: None,
8766                        ..Default::default()
8767                    }),
8768                    fdecl::Capability::Directory(fdecl::Directory {
8769                        name: None,
8770                        source_path: None,
8771                        rights: None,
8772                        ..Default::default()
8773                    }),
8774                    fdecl::Capability::Storage(fdecl::Storage {
8775                        name: None,
8776                        source: None,
8777                        backing_dir: None,
8778                        subdir: None,
8779                        storage_id: None,
8780                        ..Default::default()
8781                    }),
8782                    fdecl::Capability::Runner(fdecl::Runner {
8783                        name: None,
8784                        source_path: None,
8785                        ..Default::default()
8786                    }),
8787                    fdecl::Capability::Resolver(fdecl::Resolver {
8788                        name: None,
8789                        source_path: None,
8790                        ..Default::default()
8791                    }),
8792                    fdecl::Capability::Dictionary(fdecl::Dictionary {
8793                        ..Default::default()
8794                    }),
8795                ]);
8796                decl
8797            },
8798            result = Err(ErrorList::new(vec![
8799                Error::missing_field(DeclType::Dictionary, "name"),
8800                Error::missing_field(DeclType::Service, "name"),
8801                Error::missing_field(DeclType::Service, "source_path"),
8802                Error::missing_field(DeclType::Protocol, "name"),
8803                Error::missing_field(DeclType::Protocol, "source_path"),
8804                Error::missing_field(DeclType::Directory, "name"),
8805                Error::missing_field(DeclType::Directory, "source_path"),
8806                Error::missing_field(DeclType::Directory, "rights"),
8807                Error::missing_field(DeclType::Storage, "source"),
8808                Error::missing_field(DeclType::Storage, "name"),
8809                Error::missing_field(DeclType::Storage, "storage_id"),
8810                Error::missing_field(DeclType::Storage, "backing_dir"),
8811                Error::missing_field(DeclType::Runner, "name"),
8812                Error::missing_field(DeclType::Runner, "source_path"),
8813                Error::missing_field(DeclType::Resolver, "name"),
8814                Error::missing_field(DeclType::Resolver, "source_path"),
8815            ])),
8816        },
8817        test_validate_capabilities_invalid_identifiers => {
8818            input = {
8819                let mut decl = new_component_decl();
8820                decl.capabilities = Some(vec![
8821                    fdecl::Capability::Service(fdecl::Service {
8822                        name: Some("^bad".to_string()),
8823                        source_path: Some("&bad".to_string()),
8824                        ..Default::default()
8825                    }),
8826                    fdecl::Capability::Protocol(fdecl::Protocol {
8827                        name: Some("^bad".to_string()),
8828                        source_path: Some("&bad".to_string()),
8829                        ..Default::default()
8830                    }),
8831                    fdecl::Capability::Directory(fdecl::Directory {
8832                        name: Some("^bad".to_string()),
8833                        source_path: Some("&bad".to_string()),
8834                        rights: Some(fio::Operations::CONNECT),
8835                        ..Default::default()
8836                    }),
8837                    fdecl::Capability::Storage(fdecl::Storage {
8838                        name: Some("^bad".to_string()),
8839                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
8840                            name: "/bad".to_string()
8841                        })),
8842                        backing_dir: Some("&bad".to_string()),
8843                        subdir: None,
8844                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
8845                        ..Default::default()
8846                    }),
8847                    fdecl::Capability::Runner(fdecl::Runner {
8848                        name: Some("^bad".to_string()),
8849                        source_path: Some("&bad".to_string()),
8850                        ..Default::default()
8851                    }),
8852                    fdecl::Capability::Resolver(fdecl::Resolver {
8853                        name: Some("^bad".to_string()),
8854                        source_path: Some("&bad".to_string()),
8855                        ..Default::default()
8856                    }),
8857                    fdecl::Capability::Dictionary(fdecl::Dictionary {
8858                        name: Some("^bad".to_string()),
8859                        ..Default::default()
8860                    }),
8861                ]);
8862                decl
8863            },
8864            result = Err(ErrorList::new(vec![
8865                Error::invalid_field(DeclType::Dictionary, "name"),
8866                Error::invalid_field(DeclType::Service, "name"),
8867                Error::invalid_field(DeclType::Service, "source_path"),
8868                Error::invalid_field(DeclType::Protocol, "name"),
8869                Error::invalid_field(DeclType::Protocol, "source_path"),
8870                Error::invalid_field(DeclType::Directory, "name"),
8871                Error::invalid_field(DeclType::Directory, "source_path"),
8872                Error::invalid_field(DeclType::Storage, "source"),
8873                Error::invalid_field(DeclType::Storage, "name"),
8874                Error::invalid_field(DeclType::Storage, "backing_dir"),
8875                Error::invalid_field(DeclType::Runner, "name"),
8876                Error::invalid_field(DeclType::Runner, "source_path"),
8877                Error::invalid_field(DeclType::Resolver, "name"),
8878                Error::invalid_field(DeclType::Resolver, "source_path"),
8879            ])),
8880        },
8881        test_validate_capabilities_invalid_child => {
8882            input = {
8883                let mut decl = new_component_decl();
8884                decl.capabilities = Some(vec![
8885                    fdecl::Capability::Storage(fdecl::Storage {
8886                        name: Some("foo".to_string()),
8887                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
8888                            name: "invalid".to_string(),
8889                        })),
8890                        backing_dir: Some("foo".to_string()),
8891                        subdir: None,
8892                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
8893                        ..Default::default()
8894                    }),
8895                ]);
8896                decl
8897            },
8898            result = Err(ErrorList::new(vec![
8899                Error::invalid_field(DeclType::Storage, "source"),
8900            ])),
8901        },
8902        test_validate_capabilities_long_identifiers => {
8903            input = {
8904                let mut decl = new_component_decl();
8905                decl.capabilities = Some(vec![
8906                    fdecl::Capability::Service(fdecl::Service {
8907                        name: Some("a".repeat(256)),
8908                        source_path: Some("/c".repeat(2048)),
8909                        ..Default::default()
8910                    }),
8911                    fdecl::Capability::Protocol(fdecl::Protocol {
8912                        name: Some("a".repeat(256)),
8913                        source_path: Some("/c".repeat(2048)),
8914                        ..Default::default()
8915                    }),
8916                    fdecl::Capability::Directory(fdecl::Directory {
8917                        name: Some("a".repeat(256)),
8918                        source_path: Some("/c".repeat(2048)),
8919                        rights: Some(fio::Operations::CONNECT),
8920                        ..Default::default()
8921                    }),
8922                    fdecl::Capability::Storage(fdecl::Storage {
8923                        name: Some("a".repeat(256)),
8924                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
8925                            name: "b".repeat(256),
8926                            collection: None,
8927                        })),
8928                        backing_dir: Some(format!("{}", "c".repeat(256))),
8929                        subdir: None,
8930                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
8931                        ..Default::default()
8932                    }),
8933                    fdecl::Capability::Runner(fdecl::Runner {
8934                        name: Some("a".repeat(256)),
8935                        source_path: Some("/c".repeat(2048)),
8936                        ..Default::default()
8937                    }),
8938                    fdecl::Capability::Resolver(fdecl::Resolver {
8939                        name: Some("a".repeat(256)),
8940                        source_path: Some("/c".repeat(2048)),
8941                        ..Default::default()
8942                    }),
8943                    fdecl::Capability::Dictionary(fdecl::Dictionary {
8944                        name: Some("a".repeat(256)),
8945                        ..Default::default()
8946                    }),
8947                ]);
8948                decl
8949            },
8950            result = Err(ErrorList::new(vec![
8951                Error::field_too_long(DeclType::Dictionary, "name"),
8952                Error::field_too_long(DeclType::Service, "name"),
8953                Error::field_too_long(DeclType::Service, "source_path"),
8954                Error::field_too_long(DeclType::Protocol, "name"),
8955                Error::field_too_long(DeclType::Protocol, "source_path"),
8956                Error::field_too_long(DeclType::Directory, "name"),
8957                Error::field_too_long(DeclType::Directory, "source_path"),
8958                Error::field_too_long(DeclType::Storage, "source.child.name"),
8959                Error::field_too_long(DeclType::Storage, "name"),
8960                Error::field_too_long(DeclType::Storage, "backing_dir"),
8961                Error::field_too_long(DeclType::Runner, "name"),
8962                Error::field_too_long(DeclType::Runner, "source_path"),
8963                Error::field_too_long(DeclType::Resolver, "name"),
8964                Error::field_too_long(DeclType::Resolver, "source_path"),
8965            ])),
8966        },
8967        test_validate_capabilities_duplicate_name => {
8968            input = {
8969                let mut decl = new_component_decl();
8970                decl.capabilities = Some(vec![
8971                    fdecl::Capability::Service(fdecl::Service {
8972                        name: Some("service".to_string()),
8973                        source_path: Some("/service".to_string()),
8974                        ..Default::default()
8975                    }),
8976                    fdecl::Capability::Service(fdecl::Service {
8977                        name: Some("service".to_string()),
8978                        source_path: Some("/service".to_string()),
8979                        ..Default::default()
8980                    }),
8981                    fdecl::Capability::Protocol(fdecl::Protocol {
8982                        name: Some("protocol".to_string()),
8983                        source_path: Some("/protocol".to_string()),
8984                        ..Default::default()
8985                    }),
8986                    fdecl::Capability::Protocol(fdecl::Protocol {
8987                        name: Some("protocol".to_string()),
8988                        source_path: Some("/protocol".to_string()),
8989                        ..Default::default()
8990                    }),
8991                    fdecl::Capability::Directory(fdecl::Directory {
8992                        name: Some("directory".to_string()),
8993                        source_path: Some("/directory".to_string()),
8994                        rights: Some(fio::Operations::CONNECT),
8995                        ..Default::default()
8996                    }),
8997                    fdecl::Capability::Directory(fdecl::Directory {
8998                        name: Some("directory".to_string()),
8999                        source_path: Some("/directory".to_string()),
9000                        rights: Some(fio::Operations::CONNECT),
9001                        ..Default::default()
9002                    }),
9003                    fdecl::Capability::Storage(fdecl::Storage {
9004                        name: Some("storage".to_string()),
9005                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
9006                        backing_dir: Some("directory".to_string()),
9007                        subdir: None,
9008                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9009                        ..Default::default()
9010                    }),
9011                    fdecl::Capability::Storage(fdecl::Storage {
9012                        name: Some("storage".to_string()),
9013                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef{})),
9014                        backing_dir: Some("directory".to_string()),
9015                        subdir: None,
9016                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
9017                        ..Default::default()
9018                    }),
9019                    fdecl::Capability::Runner(fdecl::Runner {
9020                        name: Some("runner".to_string()),
9021                        source_path: Some("/runner".to_string()),
9022                        ..Default::default()
9023                    }),
9024                    fdecl::Capability::Runner(fdecl::Runner {
9025                        name: Some("runner".to_string()),
9026                        source_path: Some("/runner".to_string()),
9027                        ..Default::default()
9028                    }),
9029                    fdecl::Capability::Resolver(fdecl::Resolver {
9030                        name: Some("resolver".to_string()),
9031                        source_path: Some("/resolver".to_string()),
9032                        ..Default::default()
9033                    }),
9034                    fdecl::Capability::Resolver(fdecl::Resolver {
9035                        name: Some("resolver".to_string()),
9036                        source_path: Some("/resolver".to_string()),
9037                        ..Default::default()
9038                    }),
9039                    fdecl::Capability::Dictionary(fdecl::Dictionary {
9040                        name: Some("dictionary".to_string()),
9041                        ..Default::default()
9042                    }),
9043                    fdecl::Capability::Dictionary(fdecl::Dictionary {
9044                        name: Some("dictionary".to_string()),
9045                        ..Default::default()
9046                    }),
9047                ]);
9048                decl
9049            },
9050            result = Err(ErrorList::new(vec![
9051                Error::duplicate_field(DeclType::Dictionary, "name", "dictionary"),
9052                Error::duplicate_field(DeclType::Service, "name", "service"),
9053                Error::duplicate_field(DeclType::Protocol, "name", "protocol"),
9054                Error::duplicate_field(DeclType::Directory, "name", "directory"),
9055                Error::duplicate_field(DeclType::Storage, "name", "storage"),
9056                Error::duplicate_field(DeclType::Runner, "name", "runner"),
9057                Error::duplicate_field(DeclType::Resolver, "name", "resolver"),
9058            ])),
9059        },
9060        test_validate_invalid_service_aggregation_conflicting_filter => {
9061            input = {
9062                let mut decl = new_component_decl();
9063                decl.offers = Some(vec![
9064                    fdecl::Offer::Service(fdecl::OfferService {
9065                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9066                            name: "coll_a".to_string()
9067                        })),
9068                        source_name: Some("fuchsia.logger.Log".to_string()),
9069                        target: Some(fdecl::Ref::Child(
9070                            fdecl::ChildRef {
9071                                name: "child_c".to_string(),
9072                                collection: None,
9073                            }
9074                        )),
9075                        target_name: Some("fuchsia.logger.Log1".to_string()),
9076                        source_instance_filter: Some(vec!["default".to_string()]),
9077                        ..Default::default()
9078                    }),
9079                    fdecl::Offer::Service(fdecl::OfferService {
9080                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9081                            name: "coll_b".to_string()
9082                        })),
9083                        source_name: Some("fuchsia.logger.Log".to_string()),
9084                        target: Some(fdecl::Ref::Child(
9085                            fdecl::ChildRef {
9086                                name: "child_c".to_string(),
9087                                collection: None,
9088                            }
9089                        )),
9090                        target_name: Some("fuchsia.logger.Log1".to_string()),
9091                        source_instance_filter: Some(vec!["default".to_string()]),
9092                        ..Default::default()
9093                    }),
9094                ]);
9095                decl.collections = Some(vec![
9096                    fdecl::Collection {
9097                        name: Some("coll_a".to_string()),
9098                        durability: Some(fdecl::Durability::Transient),
9099                        ..Default::default()
9100                    },
9101                    fdecl::Collection {
9102                        name: Some("coll_b".to_string()),
9103                        durability: Some(fdecl::Durability::Transient),
9104                        ..Default::default()
9105                    },
9106                ]);
9107                decl.children = Some(vec![
9108                    fdecl::Child {
9109                        name: Some("child_c".to_string()),
9110                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9111                        startup: Some(fdecl::StartupMode::Lazy),
9112                        ..Default::default()
9113                    },
9114                ]);
9115                decl
9116            },
9117            result = Err(ErrorList::new(vec![
9118                Error::invalid_aggregate_offer("Conflicting source_instance_filter in aggregate \
9119                   service offer, instance_name 'default' seen in filter lists multiple times"),
9120            ])),
9121        },
9122
9123        test_validate_invalid_service_aggregation_conflicting_source_name => {
9124            input = {
9125                let mut decl = new_component_decl();
9126                decl.offers = Some(vec![
9127                    fdecl::Offer::Service(fdecl::OfferService {
9128                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9129                            name: "coll_a".into()
9130                        })),
9131                        source_name: Some("fuchsia.logger.Log".to_string()),
9132                        target: Some(fdecl::Ref::Child(
9133                            fdecl::ChildRef {
9134                                name: "child_c".to_string(),
9135                                collection: None,
9136                            }
9137                        )),
9138                        target_name: Some("fuchsia.logger.Log2".to_string()),
9139                        source_instance_filter: Some(vec!["default2".to_string()]),
9140                        ..Default::default()
9141                    }),
9142                    fdecl::Offer::Service(fdecl::OfferService {
9143                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9144                            name: "coll_b".into()
9145                        })),
9146                        source_name: Some("fuchsia.logger.LogAlt".to_string()),
9147                        target: Some(fdecl::Ref::Child(
9148                            fdecl::ChildRef {
9149                                name: "child_c".to_string(),
9150                                collection: None,
9151                            }
9152                        )),
9153                        target_name: Some("fuchsia.logger.Log2".to_string()),
9154                        source_instance_filter: Some(vec!["default".to_string()]),
9155                        ..Default::default()
9156                    })
9157                ]);
9158                decl.collections = Some(vec![
9159                    fdecl::Collection {
9160                        name: Some("coll_a".to_string()),
9161                        durability: Some(fdecl::Durability::Transient),
9162                        ..Default::default()
9163                    },
9164                    fdecl::Collection {
9165                        name: Some("coll_b".to_string()),
9166                        durability: Some(fdecl::Durability::Transient),
9167                        ..Default::default()
9168                    },
9169                ]);
9170                decl.children = Some(vec![
9171                    fdecl::Child {
9172                        name: Some("child_c".to_string()),
9173                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9174                        startup: Some(fdecl::StartupMode::Lazy),
9175                        on_terminate: None,
9176                        environment: None,
9177                        ..Default::default()
9178                    },
9179                ]);
9180                decl
9181            },
9182            result = Err(ErrorList::new(vec![
9183                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."),
9184            ])),
9185        },
9186
9187        test_validate_resolvers_missing_from_offer => {
9188            input = {
9189                let mut decl = new_component_decl();
9190                decl.offers = Some(vec![fdecl::Offer::Resolver(fdecl::OfferResolver {
9191                    source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
9192                    source_name: Some("a".to_string()),
9193                    target: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "child".to_string(), collection: None })),
9194                    target_name: Some("a".to_string()),
9195                    ..Default::default()
9196                })]);
9197                decl.children = Some(vec![fdecl::Child {
9198                    name: Some("child".to_string()),
9199                    url: Some("test:///child".to_string()),
9200                    startup: Some(fdecl::StartupMode::Eager),
9201                    on_terminate: None,
9202                    environment: None,
9203                    ..Default::default()
9204                }]);
9205                decl
9206            },
9207            result = Err(ErrorList::new(vec![
9208                Error::invalid_capability(DeclType::OfferResolver, "source", "a"),
9209            ])),
9210        },
9211        test_validate_resolvers_missing_from_expose => {
9212            input = {
9213                let mut decl = new_component_decl();
9214                decl.exposes = Some(vec![fdecl::Expose::Resolver(fdecl::ExposeResolver {
9215                    source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
9216                    source_name: Some("a".to_string()),
9217                    target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
9218                    target_name: Some("a".to_string()),
9219                    ..Default::default()
9220                })]);
9221                decl
9222            },
9223            result = Err(ErrorList::new(vec![
9224                Error::invalid_capability(DeclType::ExposeResolver, "source", "a"),
9225            ])),
9226        },
9227
9228        test_validate_config_missing_config => {
9229            input = {
9230                let mut decl = new_component_decl();
9231                decl.config = Some(fdecl::ConfigSchema{
9232                    fields: None,
9233                    checksum: None,
9234                    value_source: None,
9235                    ..Default::default()
9236                });
9237                decl
9238            },
9239            result = Err(ErrorList::new(vec![
9240                Error::missing_field(DeclType::ConfigSchema, "fields"),
9241                Error::missing_field(DeclType::ConfigSchema, "checksum"),
9242                Error::missing_field(DeclType::ConfigSchema, "value_source"),
9243            ])),
9244        },
9245
9246        test_validate_config_missing_config_field => {
9247            input = {
9248                let mut decl = new_component_decl();
9249                decl.config = Some(fdecl::ConfigSchema{
9250                    fields: Some(vec![
9251                        fdecl::ConfigField {
9252                            key: None,
9253                            type_: None,
9254                            ..Default::default()
9255                        }
9256                    ]),
9257                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9258                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9259                    ..Default::default()
9260                });
9261                decl
9262            },
9263            result = Err(ErrorList::new(vec![
9264                Error::missing_field(DeclType::ConfigField, "key"),
9265                Error::missing_field(DeclType::ConfigField, "value_type"),
9266            ])),
9267        },
9268
9269        test_validate_config_bool => {
9270            input = {
9271                let mut decl = new_component_decl();
9272                decl.config = Some(fdecl::ConfigSchema{
9273                    fields: Some(vec![
9274                        fdecl::ConfigField {
9275                            key: Some("test".to_string()),
9276                            type_: Some(fdecl::ConfigType {
9277                                layout: fdecl::ConfigTypeLayout::Bool,
9278                                parameters: Some(vec![]),
9279                                constraints: vec![]
9280                            }),
9281                            ..Default::default()
9282                        }
9283                    ]),
9284                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9285                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9286                    ..Default::default()
9287                });
9288                decl
9289            },
9290            result = Ok(()),
9291        },
9292
9293        test_validate_config_bool_extra_constraint => {
9294            input = {
9295                let mut decl = new_component_decl();
9296                decl.config = Some(fdecl::ConfigSchema{
9297                    fields: Some(vec![
9298                        fdecl::ConfigField {
9299                            key: Some("test".to_string()),
9300                            type_: Some(fdecl::ConfigType {
9301                                layout: fdecl::ConfigTypeLayout::Bool,
9302                                parameters: Some(vec![]),
9303                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9304                            }),
9305                            ..Default::default()
9306                        }
9307                    ]),
9308                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9309                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9310                    ..Default::default()
9311                });
9312                decl
9313            },
9314            result = Err(ErrorList::new(vec![
9315                Error::extraneous_field(DeclType::ConfigType, "constraints")
9316            ])),
9317        },
9318
9319        test_validate_config_bool_missing_parameters => {
9320            input = {
9321                let mut decl = new_component_decl();
9322                decl.config = Some(fdecl::ConfigSchema{
9323                    fields: Some(vec![
9324                        fdecl::ConfigField {
9325                            key: Some("test".to_string()),
9326                            type_: Some(fdecl::ConfigType {
9327                                layout: fdecl::ConfigTypeLayout::Bool,
9328                                parameters: None,
9329                                constraints: vec![]
9330                            }),
9331                            ..Default::default()
9332                        }
9333                    ]),
9334                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9335                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9336                    ..Default::default()
9337                });
9338                decl
9339            },
9340            result = Err(ErrorList::new(vec![
9341                Error::missing_field(DeclType::ConfigType, "parameters")
9342            ])),
9343        },
9344
9345        test_validate_config_string => {
9346            input = {
9347                let mut decl = new_component_decl();
9348                decl.config = Some(fdecl::ConfigSchema{
9349                    fields: Some(vec![
9350                        fdecl::ConfigField {
9351                            key: Some("test".to_string()),
9352                            type_: Some(fdecl::ConfigType {
9353                                layout: fdecl::ConfigTypeLayout::String,
9354                                parameters: Some(vec![]),
9355                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9356                            }),
9357                            ..Default::default()
9358                        }
9359                    ]),
9360                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9361                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9362                    ..Default::default()
9363                });
9364                decl
9365            },
9366            result = Ok(()),
9367        },
9368
9369        test_validate_config_string_missing_parameter => {
9370            input = {
9371                let mut decl = new_component_decl();
9372                decl.config = Some(fdecl::ConfigSchema{
9373                    fields: Some(vec![
9374                        fdecl::ConfigField {
9375                            key: Some("test".to_string()),
9376                            type_: Some(fdecl::ConfigType {
9377                                layout: fdecl::ConfigTypeLayout::String,
9378                                parameters: None,
9379                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9380                            }),
9381                            ..Default::default()
9382                        }
9383                    ]),
9384                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9385                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9386                    ..Default::default()
9387                });
9388                decl
9389            },
9390            result = Err(ErrorList::new(vec![
9391                Error::missing_field(DeclType::ConfigType, "parameters")
9392            ])),
9393        },
9394
9395        test_validate_config_string_missing_constraint => {
9396            input = {
9397                let mut decl = new_component_decl();
9398                decl.config = Some(fdecl::ConfigSchema{
9399                    fields: Some(vec![
9400                        fdecl::ConfigField {
9401                            key: Some("test".to_string()),
9402                            type_: Some(fdecl::ConfigType {
9403                                layout: fdecl::ConfigTypeLayout::String,
9404                                parameters: Some(vec![]),
9405                                constraints: vec![]
9406                            }),
9407                            ..Default::default()
9408                        }
9409                    ]),
9410                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9411                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9412                    ..Default::default()
9413                });
9414                decl
9415            },
9416            result = Err(ErrorList::new(vec![
9417                Error::missing_field(DeclType::ConfigType, "constraints")
9418            ])),
9419        },
9420
9421        test_validate_config_string_extra_constraint => {
9422            input = {
9423                let mut decl = new_component_decl();
9424                decl.config = Some(fdecl::ConfigSchema{
9425                    fields: Some(vec![
9426                        fdecl::ConfigField {
9427                            key: Some("test".to_string()),
9428                            type_: Some(fdecl::ConfigType {
9429                                layout: fdecl::ConfigTypeLayout::String,
9430                                parameters: Some(vec![]),
9431                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10), fdecl::LayoutConstraint::MaxSize(10)]
9432                            }),
9433                            ..Default::default()
9434                        }
9435                    ]),
9436                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9437                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9438                    ..Default::default()
9439                });
9440                decl
9441            },
9442            result = Err(ErrorList::new(vec![
9443                Error::extraneous_field(DeclType::ConfigType, "constraints")
9444            ])),
9445        },
9446
9447        test_validate_config_vector_bool => {
9448            input = {
9449                let mut decl = new_component_decl();
9450                decl.config = Some(fdecl::ConfigSchema{
9451                    fields: Some(vec![
9452                        fdecl::ConfigField {
9453                            key: Some("test".to_string()),
9454                            type_: Some(fdecl::ConfigType {
9455                                layout: fdecl::ConfigTypeLayout::Vector,
9456                                parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9457                                    layout: fdecl::ConfigTypeLayout::Bool,
9458                                    parameters: Some(vec![]),
9459                                    constraints: vec![],
9460                                })]),
9461                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9462                            }),
9463                            ..Default::default()
9464                        }
9465                    ]),
9466                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9467                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9468                    ..Default::default()
9469                });
9470                decl
9471            },
9472            result = Ok(()),
9473        },
9474
9475        test_validate_config_vector_extra_parameter => {
9476            input = {
9477                let mut decl = new_component_decl();
9478                decl.config = Some(fdecl::ConfigSchema{
9479                    fields: Some(vec![
9480                        fdecl::ConfigField {
9481                            key: Some("test".to_string()),
9482                            type_: Some(fdecl::ConfigType {
9483                                layout: fdecl::ConfigTypeLayout::Vector,
9484                                parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9485                                    layout: fdecl::ConfigTypeLayout::Bool,
9486                                    parameters: Some(vec![]),
9487                                    constraints: vec![],
9488                                }), fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9489                                    layout: fdecl::ConfigTypeLayout::Uint8,
9490                                    parameters: Some(vec![]),
9491                                    constraints: vec![],
9492                                })]),
9493                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9494                            }),
9495                            ..Default::default()
9496                        }
9497                    ]),
9498                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9499                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9500                    ..Default::default()
9501                });
9502                decl
9503            },
9504            result = Err(ErrorList::new(vec![
9505                Error::extraneous_field(DeclType::ConfigType, "parameters")
9506            ])),
9507        },
9508
9509        test_validate_config_vector_missing_parameter => {
9510            input = {
9511                let mut decl = new_component_decl();
9512                decl.config = Some(fdecl::ConfigSchema{
9513                    fields: Some(vec![
9514                        fdecl::ConfigField {
9515                            key: Some("test".to_string()),
9516                            type_: Some(fdecl::ConfigType {
9517                                layout: fdecl::ConfigTypeLayout::Vector,
9518                                parameters: Some(vec![]),
9519                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9520                            }),
9521                            ..Default::default()
9522                        }
9523                    ]),
9524                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9525                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9526                    ..Default::default()
9527                });
9528                decl
9529            },
9530            result = Err(ErrorList::new(vec![
9531                Error::missing_field(DeclType::ConfigType, "parameters")
9532            ])),
9533        },
9534
9535        test_validate_config_vector_string => {
9536            input = {
9537                let mut decl = new_component_decl();
9538                decl.config = Some(fdecl::ConfigSchema{
9539                    fields: Some(vec![
9540                        fdecl::ConfigField {
9541                            key: Some("test".to_string()),
9542                            type_: Some(fdecl::ConfigType {
9543                                layout: fdecl::ConfigTypeLayout::Vector,
9544                                parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9545                                    layout: fdecl::ConfigTypeLayout::String,
9546                                    parameters: Some(vec![]),
9547                                    constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9548                                })]),
9549                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9550                            }),
9551                            ..Default::default()
9552                        }
9553                    ]),
9554                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9555                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9556                    ..Default::default()
9557                });
9558                decl
9559            },
9560            result = Ok(()),
9561        },
9562
9563        test_validate_config_vector_vector => {
9564            input = {
9565                let mut decl = new_component_decl();
9566                decl.config = Some(fdecl::ConfigSchema{
9567                    fields: Some(vec![
9568                        fdecl::ConfigField {
9569                            key: Some("test".to_string()),
9570                            type_: Some(fdecl::ConfigType {
9571                                layout: fdecl::ConfigTypeLayout::Vector,
9572                                parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9573                                    layout: fdecl::ConfigTypeLayout::Vector,
9574                                    parameters: Some(vec![fdecl::LayoutParameter::NestedType(fdecl::ConfigType {
9575                                        layout: fdecl::ConfigTypeLayout::String,
9576                                        parameters: Some(vec![]),
9577                                        constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9578                                    })]),
9579                                    constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9580                                })]),
9581                                constraints: vec![fdecl::LayoutConstraint::MaxSize(10)]
9582                            }),
9583                            ..Default::default()
9584                        }
9585                    ]),
9586                    checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
9587                    value_source: Some(fdecl::ConfigValueSource::PackagePath("config/test.cvf".to_string())),
9588                    ..Default::default()
9589                });
9590                decl
9591            },
9592            result = Err(ErrorList::new(vec![
9593                Error::nested_vector()
9594            ])),
9595        },
9596
9597        test_validate_exposes_invalid_aggregation_different_availability => {
9598            input = {
9599                let mut decl = new_component_decl();
9600                decl.exposes = Some(vec![
9601                    fdecl::Expose::Service(fdecl::ExposeService {
9602                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9603                            name: "coll_a".into()
9604                        })),
9605                        source_name: Some("fuchsia.logger.Log".to_string()),
9606                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
9607                        target_name: Some("fuchsia.logger.Log".to_string()),
9608                        availability: Some(fdecl::Availability::Required),
9609                        ..Default::default()
9610                    }),
9611                    fdecl::Expose::Service(fdecl::ExposeService {
9612                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9613                            name: "coll_b".into()
9614                        })),
9615                        source_name: Some("fuchsia.logger.Log".to_string()),
9616                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
9617                        target_name: Some("fuchsia.logger.Log".to_string()),
9618                        availability: Some(fdecl::Availability::Optional),
9619                        ..Default::default()
9620                    })
9621                ]);
9622                decl.collections = Some(vec![
9623                    fdecl::Collection {
9624                        name: Some("coll_a".to_string()),
9625                        durability: Some(fdecl::Durability::Transient),
9626                        ..Default::default()
9627                    },
9628                    fdecl::Collection {
9629                        name: Some("coll_b".to_string()),
9630                        durability: Some(fdecl::Durability::Transient),
9631                        ..Default::default()
9632                    },
9633                ]);
9634                decl
9635            },
9636            result = Err(ErrorList::new(vec![
9637                Error::DifferentAvailabilityInAggregation(AvailabilityList(vec![
9638                    fdecl::Availability::Required,
9639                    fdecl::Availability::Optional,
9640                ]))
9641            ])),
9642        },
9643
9644        test_validate_offers_invalid_aggregation_different_availability => {
9645            input = {
9646                let mut decl = new_component_decl();
9647                decl.offers = Some(vec![
9648                    fdecl::Offer::Service(fdecl::OfferService {
9649                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9650                            name: "coll_a".into()
9651                        })),
9652                        source_name: Some("fuchsia.logger.Log".to_string()),
9653                        target: Some(fdecl::Ref::Child(
9654                            fdecl::ChildRef {
9655                                name: "child_c".to_string(),
9656                                collection: None,
9657                            }
9658                        )),
9659                        target_name: Some("fuchsia.logger.Log".to_string()),
9660                        source_instance_filter: Some(vec!["default".to_string()]),
9661                        availability: Some(fdecl::Availability::Required),
9662                        ..Default::default()
9663                    }),
9664                    fdecl::Offer::Service(fdecl::OfferService {
9665                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
9666                            name: "coll_b".into()
9667                        })),
9668                        source_name: Some("fuchsia.logger.Log".to_string()),
9669                        target: Some(fdecl::Ref::Child(
9670                            fdecl::ChildRef {
9671                                name: "child_c".to_string(),
9672                                collection: None,
9673                            }
9674                        )),
9675                        target_name: Some("fuchsia.logger.Log".to_string()),
9676                        source_instance_filter: Some(vec!["a_different_default".to_string()]),
9677                        availability: Some(fdecl::Availability::Optional),
9678                        ..Default::default()
9679                    })
9680                ]);
9681                decl.collections = Some(vec![
9682                    fdecl::Collection {
9683                        name: Some("coll_a".to_string()),
9684                        durability: Some(fdecl::Durability::Transient),
9685                        ..Default::default()
9686                    },
9687                    fdecl::Collection {
9688                        name: Some("coll_b".to_string()),
9689                        durability: Some(fdecl::Durability::Transient),
9690                        ..Default::default()
9691                    },
9692                ]);
9693                decl.children = Some(vec![
9694                    fdecl::Child {
9695                        name: Some("child_c".to_string()),
9696                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
9697                        startup: Some(fdecl::StartupMode::Lazy),
9698                        on_terminate: None,
9699                        environment: None,
9700                        ..Default::default()
9701                    },
9702                ]);
9703                decl
9704            },
9705            result = Err(ErrorList::new(vec![
9706                Error::DifferentAvailabilityInAggregation(AvailabilityList(vec![
9707                    fdecl::Availability::Required,
9708                    fdecl::Availability::Optional,
9709                ]))
9710            ])),
9711        },
9712    }
9713
9714    test_validate_capabilities! {
9715        test_validate_capabilities_individually_ok => {
9716            input = vec![
9717                fdecl::Capability::Protocol(fdecl::Protocol {
9718                    name: Some("foo_svc".into()),
9719                    source_path: Some("/svc/foo".into()),
9720                    ..Default::default()
9721                }),
9722                fdecl::Capability::Directory(fdecl::Directory {
9723                    name: Some("foo_dir".into()),
9724                    source_path: Some("/foo".into()),
9725                    rights: Some(fio::Operations::CONNECT),
9726                    ..Default::default()
9727                }),
9728            ],
9729            as_builtin = false,
9730            result = Ok(()),
9731        },
9732        test_validate_capabilities_individually_err => {
9733            input = vec![
9734                fdecl::Capability::Protocol(fdecl::Protocol {
9735                    name: None,
9736                    source_path: None,
9737                    ..Default::default()
9738                }),
9739                fdecl::Capability::Directory(fdecl::Directory {
9740                    name: None,
9741                    source_path: None,
9742                    rights: None,
9743                    ..Default::default()
9744                }),
9745            ],
9746            as_builtin = false,
9747            result = Err(ErrorList::new(vec![
9748                Error::missing_field(DeclType::Protocol, "name"),
9749                Error::missing_field(DeclType::Protocol, "source_path"),
9750                Error::missing_field(DeclType::Directory, "name"),
9751                Error::missing_field(DeclType::Directory, "source_path"),
9752                Error::missing_field(DeclType::Directory, "rights"),
9753            ])),
9754        },
9755        test_validate_builtin_capabilities_individually_ok => {
9756            input = vec![
9757                fdecl::Capability::Protocol(fdecl::Protocol {
9758                    name: Some("foo_protocol".into()),
9759                    source_path: None,
9760                    ..Default::default()
9761                }),
9762                fdecl::Capability::Directory(fdecl::Directory {
9763                    name: Some("foo_dir".into()),
9764                    source_path: None,
9765                    rights: Some(fio::Operations::CONNECT),
9766                    ..Default::default()
9767                }),
9768                fdecl::Capability::Service(fdecl::Service {
9769                    name: Some("foo_svc".into()),
9770                    source_path: None,
9771                    ..Default::default()
9772                }),
9773                fdecl::Capability::Runner(fdecl::Runner {
9774                    name: Some("foo_runner".into()),
9775                    source_path: None,
9776                    ..Default::default()
9777                }),
9778                fdecl::Capability::Resolver(fdecl::Resolver {
9779                    name: Some("foo_resolver".into()),
9780                    source_path: None,
9781                    ..Default::default()
9782                }),
9783            ],
9784            as_builtin = true,
9785            result = Ok(()),
9786        },
9787        test_validate_builtin_capabilities_individually_err => {
9788            input = vec![
9789                fdecl::Capability::Protocol(fdecl::Protocol {
9790                    name: None,
9791                    source_path: Some("/svc/foo".into()),
9792                    ..Default::default()
9793                }),
9794                fdecl::Capability::Directory(fdecl::Directory {
9795                    name: None,
9796                    source_path: Some("/foo".into()),
9797                    rights: None,
9798                    ..Default::default()
9799                }),
9800                fdecl::Capability::Service(fdecl::Service {
9801                    name: None,
9802                    source_path: Some("/svc/foo".into()),
9803                    ..Default::default()
9804                }),
9805                fdecl::Capability::Runner(fdecl::Runner {
9806                    name: None,
9807                    source_path:  Some("/foo".into()),
9808                    ..Default::default()
9809                }),
9810                fdecl::Capability::Resolver(fdecl::Resolver {
9811                    name: None,
9812                    source_path:  Some("/foo".into()),
9813                    ..Default::default()
9814                }),
9815                fdecl::Capability::Storage(fdecl::Storage {
9816                    name: None,
9817                    ..Default::default()
9818                }),
9819            ],
9820            as_builtin = true,
9821            result = Err(ErrorList::new(vec![
9822                Error::missing_field(DeclType::Protocol, "name"),
9823                Error::extraneous_source_path(DeclType::Protocol, "/svc/foo"),
9824                Error::missing_field(DeclType::Directory, "name"),
9825                Error::extraneous_source_path(DeclType::Directory, "/foo"),
9826                Error::missing_field(DeclType::Directory, "rights"),
9827                Error::missing_field(DeclType::Service, "name"),
9828                Error::extraneous_source_path(DeclType::Service, "/svc/foo"),
9829                Error::missing_field(DeclType::Runner, "name"),
9830                Error::extraneous_source_path(DeclType::Runner, "/foo"),
9831                Error::missing_field(DeclType::Resolver, "name"),
9832                Error::extraneous_source_path(DeclType::Resolver, "/foo"),
9833                Error::CapabilityCannotBeBuiltin(DeclType::Storage),
9834            ])),
9835        },
9836        test_validate_delivery_type_ok => {
9837            input = vec![
9838                fdecl::Capability::Protocol(fdecl::Protocol {
9839                    name: Some("foo_svc1".into()),
9840                    source_path: Some("/svc/foo1".into()),
9841                    ..Default::default()
9842                }),
9843                fdecl::Capability::Protocol(fdecl::Protocol {
9844                    name: Some("foo_svc2".into()),
9845                    source_path: Some("/svc/foo2".into()),
9846                    delivery: Some(fdecl::DeliveryType::Immediate),
9847                    ..Default::default()
9848                }),
9849                fdecl::Capability::Protocol(fdecl::Protocol {
9850                    name: Some("foo_svc3".into()),
9851                    source_path: Some("/svc/foo3".into()),
9852                    delivery: Some(fdecl::DeliveryType::OnReadable),
9853                    ..Default::default()
9854                }),
9855            ],
9856            as_builtin = false,
9857            result = Ok(()),
9858        },
9859        test_validate_delivery_type_err => {
9860            input = vec![
9861                fdecl::Capability::Protocol(fdecl::Protocol {
9862                    name: Some("foo_svc".into()),
9863                    source_path: Some("/svc/foo".into()),
9864                    delivery: Some(fdecl::DeliveryType::unknown()),
9865                    ..Default::default()
9866                }),
9867            ],
9868            as_builtin = false,
9869            result = Err(ErrorList::new(vec![
9870                Error::invalid_field(DeclType::Protocol, "delivery"),
9871            ])),
9872        },
9873    }
9874
9875    /// Passes different source and availability options to `new_expose` to
9876    /// generate a component declaration.
9877    fn generate_expose_different_source_and_availability_decl(
9878        new_expose: impl Fn(fdecl::Ref, fdecl::Availability, &str) -> fdecl::Expose,
9879    ) -> fdecl::Component {
9880        let mut decl = new_component_decl();
9881        let child = "child";
9882        decl.children = Some(vec![fdecl::Child {
9883            name: Some(child.to_string()),
9884            url: Some("fuchsia-pkg://fuchsia.com/source#meta/source.cm".to_string()),
9885            startup: Some(fdecl::StartupMode::Lazy),
9886            ..Default::default()
9887        }]);
9888        decl.exposes = Some(vec![
9889            // Optional expose from self is okay.
9890            new_expose(
9891                fdecl::Ref::Self_(fdecl::SelfRef {}),
9892                fdecl::Availability::Optional,
9893                "fuchsia.examples.Echo1",
9894            ),
9895            // Optional expose from child is okay.
9896            new_expose(
9897                fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
9898                fdecl::Availability::Optional,
9899                "fuchsia.examples.Echo2",
9900            ),
9901            // Optional expose from void is okay.
9902            new_expose(
9903                fdecl::Ref::VoidType(fdecl::VoidRef {}),
9904                fdecl::Availability::Optional,
9905                "fuchsia.examples.Echo3",
9906            ),
9907            // Optional expose from framework is okay.
9908            new_expose(
9909                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
9910                fdecl::Availability::Optional,
9911                "fuchsia.examples.Echo4",
9912            ),
9913            // Transitional expose from self is okay.
9914            new_expose(
9915                fdecl::Ref::Self_(fdecl::SelfRef {}),
9916                fdecl::Availability::Transitional,
9917                "fuchsia.examples.Echo5",
9918            ),
9919            // Transitional expose from child is okay.
9920            new_expose(
9921                fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
9922                fdecl::Availability::Transitional,
9923                "fuchsia.examples.Echo6",
9924            ),
9925            // Transitional expose from void is okay.
9926            new_expose(
9927                fdecl::Ref::VoidType(fdecl::VoidRef {}),
9928                fdecl::Availability::Transitional,
9929                "fuchsia.examples.Echo7",
9930            ),
9931            // Transitional expose from framework is okay.
9932            new_expose(
9933                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
9934                fdecl::Availability::Transitional,
9935                "fuchsia.examples.Echo8",
9936            ),
9937            // Same-as-target expose from self is okay.
9938            new_expose(
9939                fdecl::Ref::Self_(fdecl::SelfRef {}),
9940                fdecl::Availability::SameAsTarget,
9941                "fuchsia.examples.Echo9",
9942            ),
9943            // Same-as-target expose from child is okay.
9944            new_expose(
9945                fdecl::Ref::Child(fdecl::ChildRef { name: child.to_string(), collection: None }),
9946                fdecl::Availability::SameAsTarget,
9947                "fuchsia.examples.Echo10",
9948            ),
9949            // Same-as-target expose from framework is okay.
9950            new_expose(
9951                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
9952                fdecl::Availability::SameAsTarget,
9953                "fuchsia.examples.Echo11",
9954            ),
9955            // Required expose from void is an error.
9956            new_expose(
9957                fdecl::Ref::VoidType(fdecl::VoidRef {}),
9958                fdecl::Availability::Required,
9959                "fuchsia.examples.Echo12",
9960            ),
9961            // Same-as-target expose from void is an error.
9962            new_expose(
9963                fdecl::Ref::VoidType(fdecl::VoidRef {}),
9964                fdecl::Availability::SameAsTarget,
9965                "fuchsia.examples.Echo13",
9966            ),
9967        ]);
9968        decl
9969    }
9970
9971    /// Passes different source and availability options to `new_offer` to
9972    /// generate a component declaration.
9973    fn generate_offer_different_source_and_availability_decl(
9974        new_offer: impl Fn(fdecl::Ref, fdecl::Availability, &str) -> fdecl::Offer,
9975    ) -> fdecl::Component {
9976        let mut decl = new_component_decl();
9977        decl.children = Some(vec![
9978            fdecl::Child {
9979                name: Some("source".to_string()),
9980                url: Some("fuchsia-pkg://fuchsia.com/source#meta/source.cm".to_string()),
9981                startup: Some(fdecl::StartupMode::Lazy),
9982                on_terminate: None,
9983                environment: None,
9984                ..Default::default()
9985            },
9986            fdecl::Child {
9987                name: Some("sink".to_string()),
9988                url: Some("fuchsia-pkg://fuchsia.com/sink#meta/sink.cm".to_string()),
9989                startup: Some(fdecl::StartupMode::Lazy),
9990                on_terminate: None,
9991                environment: None,
9992                ..Default::default()
9993            },
9994        ]);
9995        decl.offers = Some(vec![
9996            // These offers are fine, offers with a source of parent or void can be
9997            // optional.
9998            new_offer(
9999                fdecl::Ref::Parent(fdecl::ParentRef {}),
10000                fdecl::Availability::Required,
10001                "fuchsia.examples.Echo0",
10002            ),
10003            new_offer(
10004                fdecl::Ref::Parent(fdecl::ParentRef {}),
10005                fdecl::Availability::Optional,
10006                "fuchsia.examples.Echo1",
10007            ),
10008            new_offer(
10009                fdecl::Ref::Parent(fdecl::ParentRef {}),
10010                fdecl::Availability::SameAsTarget,
10011                "fuchsia.examples.Echo2",
10012            ),
10013            new_offer(
10014                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10015                fdecl::Availability::Optional,
10016                "fuchsia.examples.Echo3",
10017            ),
10018            // These offers are fine, offers with a source other than parent or void
10019            // can also be optional.
10020            new_offer(
10021                fdecl::Ref::Self_(fdecl::SelfRef {}),
10022                fdecl::Availability::Optional,
10023                "fuchsia.examples.Echo4",
10024            ),
10025            new_offer(
10026                fdecl::Ref::Self_(fdecl::SelfRef {}),
10027                fdecl::Availability::SameAsTarget,
10028                "fuchsia.examples.Echo5",
10029            ),
10030            new_offer(
10031                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10032                fdecl::Availability::Optional,
10033                "fuchsia.examples.Echo6",
10034            ),
10035            new_offer(
10036                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
10037                fdecl::Availability::SameAsTarget,
10038                "fuchsia.examples.Echo7",
10039            ),
10040            new_offer(
10041                fdecl::Ref::Child(fdecl::ChildRef { name: "source".to_string(), collection: None }),
10042                fdecl::Availability::Optional,
10043                "fuchsia.examples.Echo8",
10044            ),
10045            new_offer(
10046                fdecl::Ref::Child(fdecl::ChildRef { name: "source".to_string(), collection: None }),
10047                fdecl::Availability::SameAsTarget,
10048                "fuchsia.examples.Echo9",
10049            ),
10050            // These offers are also not fine, offers with a source of void must be optional
10051            new_offer(
10052                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10053                fdecl::Availability::Required,
10054                "fuchsia.examples.Echo10",
10055            ),
10056            new_offer(
10057                fdecl::Ref::VoidType(fdecl::VoidRef {}),
10058                fdecl::Availability::SameAsTarget,
10059                "fuchsia.examples.Echo11",
10060            ),
10061        ]);
10062        decl
10063    }
10064
10065    #[test]
10066    fn test_validate_dynamic_offers_empty() {
10067        assert_eq!(validate_dynamic_offers(vec![], &vec![], &fdecl::Component::default()), Ok(()));
10068    }
10069
10070    #[test]
10071    fn test_validate_dynamic_offers_okay() {
10072        assert_eq!(
10073            validate_dynamic_offers(
10074                vec![],
10075                &vec![
10076                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
10077                        dependency_type: Some(fdecl::DependencyType::Strong),
10078                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef)),
10079                        source_name: Some("thing".to_string()),
10080                        target_name: Some("thing".repeat(26)),
10081                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10082                            name: "foo".to_string(),
10083                            collection: Some("foo".to_string()),
10084                        })),
10085                        ..Default::default()
10086                    }),
10087                    fdecl::Offer::Service(fdecl::OfferService {
10088                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10089                        source_name: Some("thang".repeat(26)),
10090                        target_name: Some("thang".repeat(26)),
10091                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10092                            name: "foo".to_string(),
10093                            collection: Some("foo".to_string()),
10094                        })),
10095                        ..Default::default()
10096                    }),
10097                    fdecl::Offer::Directory(fdecl::OfferDirectory {
10098                        dependency_type: Some(fdecl::DependencyType::Strong),
10099                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10100                        source_name: Some("thung1".repeat(26)),
10101                        target_name: Some("thung1".repeat(26)),
10102                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10103                            name: "foo".to_string(),
10104                            collection: Some("foo".to_string()),
10105                        })),
10106                        ..Default::default()
10107                    }),
10108                    fdecl::Offer::Storage(fdecl::OfferStorage {
10109                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10110                        source_name: Some("thung2".repeat(26)),
10111                        target_name: Some("thung2".repeat(26)),
10112                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10113                            name: "foo".to_string(),
10114                            collection: Some("foo".to_string()),
10115                        })),
10116                        ..Default::default()
10117                    }),
10118                    fdecl::Offer::Runner(fdecl::OfferRunner {
10119                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10120                        source_name: Some("thung3".repeat(26)),
10121                        target_name: Some("thung3".repeat(26)),
10122                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10123                            name: "foo".to_string(),
10124                            collection: Some("foo".to_string()),
10125                        })),
10126                        ..Default::default()
10127                    }),
10128                    fdecl::Offer::Resolver(fdecl::OfferResolver {
10129                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10130                        source_name: Some("thung4".repeat(26)),
10131                        target_name: Some("thung4".repeat(26)),
10132                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10133                            name: "foo".to_string(),
10134                            collection: Some("foo".to_string()),
10135                        })),
10136                        ..Default::default()
10137                    }),
10138                ],
10139                &fdecl::Component {
10140                    capabilities: Some(vec![fdecl::Capability::Protocol(fdecl::Protocol {
10141                        name: Some("thing".to_string()),
10142                        source_path: Some("/svc/foo".into()),
10143                        ..Default::default()
10144                    }),]),
10145                    ..Default::default()
10146                }
10147            ),
10148            Ok(())
10149        );
10150    }
10151
10152    #[test]
10153    fn test_validate_dynamic_offers_valid_service_aggregation() {
10154        assert_eq!(
10155            validate_dynamic_offers(
10156                vec![],
10157                &vec![
10158                    fdecl::Offer::Service(fdecl::OfferService {
10159                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10160                            name: "child_a".to_string(),
10161                            collection: None
10162                        })),
10163                        source_name: Some("fuchsia.logger.Log".to_string()),
10164                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10165                            name: "child_c".to_string(),
10166                            collection: None,
10167                        })),
10168                        target_name: Some("fuchsia.logger.Log".to_string()),
10169                        source_instance_filter: Some(vec!["default".to_string()]),
10170                        ..Default::default()
10171                    }),
10172                    fdecl::Offer::Service(fdecl::OfferService {
10173                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10174                            name: "child_b".to_string(),
10175                            collection: None
10176                        })),
10177                        source_name: Some("fuchsia.logger.Log".to_string()),
10178                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10179                            name: "child_c".to_string(),
10180                            collection: None,
10181                        })),
10182                        target_name: Some("fuchsia.logger.Log".to_string()),
10183                        source_instance_filter: Some(vec!["a_different_default".to_string()]),
10184                        ..Default::default()
10185                    })
10186                ],
10187                &fdecl::Component {
10188                    children: Some(vec![
10189                        fdecl::Child {
10190                            name: Some("child_a".to_string()),
10191                            url: Some(
10192                                "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10193                                    .to_string()
10194                            ),
10195                            startup: Some(fdecl::StartupMode::Lazy),
10196                            on_terminate: None,
10197                            environment: None,
10198                            ..Default::default()
10199                        },
10200                        fdecl::Child {
10201                            name: Some("child_b".to_string()),
10202                            url: Some(
10203                                "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10204                                    .to_string()
10205                            ),
10206                            startup: Some(fdecl::StartupMode::Lazy),
10207                            on_terminate: None,
10208                            environment: None,
10209                            ..Default::default()
10210                        },
10211                        fdecl::Child {
10212                            name: Some("child_c".to_string()),
10213                            url: Some(
10214                                "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10215                                    .to_string()
10216                            ),
10217                            startup: Some(fdecl::StartupMode::Lazy),
10218                            on_terminate: None,
10219                            environment: None,
10220                            ..Default::default()
10221                        },
10222                    ]),
10223                    ..Default::default()
10224                }
10225            ),
10226            Ok(())
10227        );
10228    }
10229
10230    #[test]
10231    fn test_validate_dynamic_service_aggregation_missing_filter() {
10232        assert_eq!(
10233            validate_dynamic_offers(
10234                vec![],
10235                &vec![
10236                    fdecl::Offer::Service(fdecl::OfferService {
10237                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10238                            name: "child_a".to_string(),
10239                            collection: None
10240                        })),
10241                        source_name: Some("fuchsia.logger.Log".to_string()),
10242                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10243                            name: "child_c".to_string(),
10244                            collection: None,
10245                        })),
10246                        target_name: Some("fuchsia.logger.Log".to_string()),
10247                        source_instance_filter: Some(vec!["default".to_string()]),
10248                        ..Default::default()
10249                    }),
10250                    fdecl::Offer::Service(fdecl::OfferService {
10251                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10252                            name: "child_b".to_string(),
10253                            collection: None
10254                        })),
10255                        source_name: Some("fuchsia.logger.Log".to_string()),
10256                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10257                            name: "child_c".to_string(),
10258                            collection: None,
10259                        })),
10260                        target_name: Some("fuchsia.logger.Log".to_string()),
10261                        source_instance_filter: None,
10262                        ..Default::default()
10263                    }),
10264                ],
10265                &fdecl::Component {
10266                    children: Some(vec![
10267                        fdecl::Child {
10268                            name: Some("child_a".to_string()),
10269                            url: Some(
10270                                "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10271                                    .to_string()
10272                            ),
10273                            startup: Some(fdecl::StartupMode::Lazy),
10274                            ..Default::default()
10275                        },
10276                        fdecl::Child {
10277                            name: Some("child_b".to_string()),
10278                            url: Some(
10279                                "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10280                                    .to_string()
10281                            ),
10282                            startup: Some(fdecl::StartupMode::Lazy),
10283                            ..Default::default()
10284                        },
10285                        fdecl::Child {
10286                            name: Some("child_c".to_string()),
10287                            url: Some(
10288                                "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
10289                                    .to_string()
10290                            ),
10291                            startup: Some(fdecl::StartupMode::Lazy),
10292                            ..Default::default()
10293                        },
10294                    ]),
10295                    ..Default::default()
10296                },
10297            ),
10298            Err(ErrorList::new(vec![Error::invalid_aggregate_offer(
10299                "source_instance_filter must be set for dynamic aggregate service offers"
10300            ),]))
10301        );
10302    }
10303
10304    #[test]
10305    fn test_validate_dynamic_offers_omit_target() {
10306        assert_eq!(
10307            validate_dynamic_offers(
10308                vec![],
10309                &vec![
10310                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
10311                        dependency_type: Some(fdecl::DependencyType::Strong),
10312                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef)),
10313                        source_name: Some("thing".to_string()),
10314                        target_name: Some("thing".to_string()),
10315                        ..Default::default()
10316                    }),
10317                    fdecl::Offer::Service(fdecl::OfferService {
10318                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10319                        source_name: Some("thang".to_string()),
10320                        target_name: Some("thang".to_string()),
10321                        ..Default::default()
10322                    }),
10323                    fdecl::Offer::Directory(fdecl::OfferDirectory {
10324                        dependency_type: Some(fdecl::DependencyType::Strong),
10325                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10326                        source_name: Some("thung1".to_string()),
10327                        target_name: Some("thung1".to_string()),
10328                        ..Default::default()
10329                    }),
10330                    fdecl::Offer::Storage(fdecl::OfferStorage {
10331                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10332                        source_name: Some("thung2".to_string()),
10333                        target_name: Some("thung2".to_string()),
10334                        ..Default::default()
10335                    }),
10336                    fdecl::Offer::Runner(fdecl::OfferRunner {
10337                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10338                        source_name: Some("thung3".to_string()),
10339                        target_name: Some("thung3".to_string()),
10340                        ..Default::default()
10341                    }),
10342                    fdecl::Offer::Resolver(fdecl::OfferResolver {
10343                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10344                        source_name: Some("thung4".to_string()),
10345                        target_name: Some("thung4".to_string()),
10346                        ..Default::default()
10347                    }),
10348                ],
10349                &fdecl::Component {
10350                    capabilities: Some(vec![fdecl::Capability::Protocol(fdecl::Protocol {
10351                        name: Some("thing".to_string()),
10352                        source_path: Some("/svc/foo".into()),
10353                        ..Default::default()
10354                    }),]),
10355                    ..Default::default()
10356                }
10357            ),
10358            Err(ErrorList::new(vec![
10359                Error::missing_field(DeclType::OfferProtocol, "target"),
10360                Error::missing_field(DeclType::OfferService, "target"),
10361                Error::missing_field(DeclType::OfferDirectory, "target"),
10362                Error::missing_field(DeclType::OfferStorage, "target"),
10363                Error::missing_field(DeclType::OfferRunner, "target"),
10364                Error::missing_field(DeclType::OfferResolver, "target"),
10365            ]))
10366        );
10367    }
10368
10369    #[test]
10370    fn test_validate_dynamic_offers_collection_collision() {
10371        assert_eq!(
10372            validate_dynamic_offers(
10373                vec![],
10374                &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10375                    dependency_type: Some(fdecl::DependencyType::Strong),
10376                    source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10377                    source_name: Some("thing".to_string()),
10378                    target_name: Some("thing".to_string()),
10379                    target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10380                        name: "child".to_string(),
10381                        collection: Some("coll".to_string()),
10382                    })),
10383                    ..Default::default()
10384                }),],
10385                &fdecl::Component {
10386                    offers: Some(vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10387                        dependency_type: Some(fdecl::DependencyType::Strong),
10388                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
10389                        source_name: Some("thing".to_string()),
10390                        target_name: Some("thing".to_string()),
10391                        target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10392                            name: "coll".into()
10393                        })),
10394                        ..Default::default()
10395                    }),]),
10396                    collections: Some(vec![fdecl::Collection {
10397                        name: Some("coll".to_string()),
10398                        durability: Some(fdecl::Durability::Transient),
10399                        ..Default::default()
10400                    },]),
10401                    ..Default::default()
10402                }
10403            ),
10404            Err(ErrorList::new(vec![Error::duplicate_field(
10405                DeclType::OfferProtocol,
10406                "target_name",
10407                "thing"
10408            ),]))
10409        );
10410    }
10411
10412    #[test]
10413    fn test_validate_dynamic_offers_cycle_collection_to_static_child() {
10414        assert_eq!(
10415            validate_dynamic_offers(
10416                vec![("dyn", "coll")],
10417                &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10418                    source_name: Some("bar".to_string()),
10419                    target_name: Some("bar".to_string()),
10420                    source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10421                        name: "static_child".into(),
10422                        collection: None,
10423                    })),
10424                    target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10425                        name: "dyn".to_string(),
10426                        collection: Some("coll".to_string()),
10427                    })),
10428                    dependency_type: Some(fdecl::DependencyType::Strong),
10429                    ..Default::default()
10430                }),],
10431                &fdecl::Component {
10432                    offers: Some(vec![fdecl::Offer::Service(fdecl::OfferService {
10433                        source_name: Some("foo".to_string()),
10434                        target_name: Some("foo".to_string()),
10435                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10436                            name: "coll".into(),
10437                        })),
10438                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10439                            name: "static_child".into(),
10440                            collection: None,
10441                        })),
10442                        ..Default::default()
10443                    })]),
10444                    children: Some(vec![fdecl::Child {
10445                        name: Some("static_child".into()),
10446                        url: Some("url#child.cm".into()),
10447                        startup: Some(fdecl::StartupMode::Lazy),
10448                        ..Default::default()
10449                    }]),
10450                    collections: Some(vec![fdecl::Collection {
10451                        name: Some("coll".into()),
10452                        durability: Some(fdecl::Durability::Transient),
10453                        ..Default::default()
10454                    }]),
10455                    ..Default::default()
10456                }
10457            ),
10458            Err(ErrorList::new(vec![Error::dependency_cycle(
10459                directed_graph::Error::CyclesDetected(
10460                    [vec![
10461                        "child coll:dyn",
10462                        "collection coll",
10463                        "child static_child",
10464                        "child coll:dyn",
10465                    ]]
10466                    .iter()
10467                    .cloned()
10468                    .collect()
10469                )
10470                .format_cycle()
10471            )]))
10472        );
10473    }
10474
10475    #[test]
10476    fn test_validate_dynamic_offers_cycle_collection_to_dynamic_child() {
10477        assert_eq!(
10478            validate_dynamic_offers(
10479                vec![("dyn", "coll1"), ("dyn", "coll2")],
10480                &vec![
10481                    fdecl::Offer::Service(fdecl::OfferService {
10482                        source_name: Some("foo".to_string()),
10483                        target_name: Some("foo".to_string()),
10484                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10485                            name: "coll2".into(),
10486                        })),
10487                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10488                            name: "dyn".into(),
10489                            collection: Some("coll1".into()),
10490                        })),
10491                        ..Default::default()
10492                    }),
10493                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
10494                        source_name: Some("bar".to_string()),
10495                        target_name: Some("bar".to_string()),
10496                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10497                            name: "dyn".into(),
10498                            collection: Some("coll1".into()),
10499                        })),
10500                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10501                            name: "dyn".to_string(),
10502                            collection: Some("coll2".to_string()),
10503                        })),
10504                        dependency_type: Some(fdecl::DependencyType::Strong),
10505                        ..Default::default()
10506                    }),
10507                ],
10508                &fdecl::Component {
10509                    collections: Some(vec![
10510                        fdecl::Collection {
10511                            name: Some("coll1".into()),
10512                            durability: Some(fdecl::Durability::Transient),
10513                            ..Default::default()
10514                        },
10515                        fdecl::Collection {
10516                            name: Some("coll2".into()),
10517                            durability: Some(fdecl::Durability::Transient),
10518                            ..Default::default()
10519                        },
10520                    ]),
10521                    ..Default::default()
10522                }
10523            ),
10524            Err(ErrorList::new(vec![Error::dependency_cycle(
10525                directed_graph::Error::CyclesDetected(
10526                    [vec![
10527                        "child coll1:dyn",
10528                        "child coll2:dyn",
10529                        "collection coll2",
10530                        "child coll1:dyn",
10531                    ]]
10532                    .iter()
10533                    .cloned()
10534                    .collect()
10535                )
10536                .format_cycle()
10537            )]))
10538        );
10539    }
10540
10541    #[test]
10542    fn test_validate_dynamic_offers_cycle_collection_to_collection() {
10543        assert_eq!(
10544            validate_dynamic_offers(
10545                vec![("dyn", "coll1"), ("dyn", "coll2")],
10546                &vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
10547                    source_name: Some("bar".to_string()),
10548                    target_name: Some("bar".to_string()),
10549                    source: Some(fdecl::Ref::Child(fdecl::ChildRef {
10550                        name: "dyn".into(),
10551                        collection: Some("coll2".parse().unwrap()),
10552                    })),
10553                    target: Some(fdecl::Ref::Child(fdecl::ChildRef {
10554                        name: "dyn".into(),
10555                        collection: Some("coll1".parse().unwrap()),
10556                    })),
10557                    dependency_type: Some(fdecl::DependencyType::Strong),
10558                    ..Default::default()
10559                }),],
10560                &fdecl::Component {
10561                    offers: Some(vec![fdecl::Offer::Service(fdecl::OfferService {
10562                        source_name: Some("foo".to_string()),
10563                        target_name: Some("foo".to_string()),
10564                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10565                            name: "coll1".into(),
10566                        })),
10567                        target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
10568                            name: "coll2".into(),
10569                        })),
10570                        ..Default::default()
10571                    })]),
10572                    collections: Some(vec![
10573                        fdecl::Collection {
10574                            name: Some("coll1".into()),
10575                            durability: Some(fdecl::Durability::Transient),
10576                            ..Default::default()
10577                        },
10578                        fdecl::Collection {
10579                            name: Some("coll2".into()),
10580                            durability: Some(fdecl::Durability::Transient),
10581                            ..Default::default()
10582                        },
10583                    ]),
10584                    ..Default::default()
10585                }
10586            ),
10587            Err(ErrorList::new(vec![Error::dependency_cycle(
10588                directed_graph::Error::CyclesDetected(
10589                    [vec![
10590                        "child coll1:dyn",
10591                        "collection coll1",
10592                        "child coll2:dyn",
10593                        "child coll1:dyn",
10594                    ]]
10595                    .iter()
10596                    .cloned()
10597                    .collect()
10598                )
10599                .format_cycle()
10600            )]))
10601        );
10602    }
10603
10604    #[test]
10605    fn test_validate_dynamic_child() {
10606        assert_eq!(
10607            Ok(()),
10608            validate_dynamic_child(&fdecl::Child {
10609                name: Some("a".repeat(MAX_LONG_NAME_LENGTH).to_string()),
10610                url: Some("test:///child".to_string()),
10611                startup: Some(fdecl::StartupMode::Lazy),
10612                on_terminate: None,
10613                environment: None,
10614                ..Default::default()
10615            })
10616        );
10617    }
10618
10619    #[test]
10620    fn test_validate_dynamic_child_environment_is_invalid() {
10621        assert_eq!(
10622            validate_dynamic_child(&fdecl::Child {
10623                name: Some("a".repeat(MAX_LONG_NAME_LENGTH).to_string()),
10624                url: Some("test:///child".to_string()),
10625                startup: Some(fdecl::StartupMode::Lazy),
10626                on_terminate: None,
10627                environment: Some("env".to_string()),
10628                ..Default::default()
10629            }),
10630            Err(ErrorList::new(vec![Error::DynamicChildWithEnvironment]))
10631        );
10632    }
10633
10634    #[test]
10635    fn test_validate_dynamic_offers_missing_stuff() {
10636        assert_eq!(
10637            validate_dynamic_offers(
10638                vec![],
10639                &vec![
10640                    fdecl::Offer::Protocol(fdecl::OfferProtocol::default()),
10641                    fdecl::Offer::Service(fdecl::OfferService::default()),
10642                    fdecl::Offer::Directory(fdecl::OfferDirectory::default()),
10643                    fdecl::Offer::Storage(fdecl::OfferStorage::default()),
10644                    fdecl::Offer::Runner(fdecl::OfferRunner::default()),
10645                    fdecl::Offer::Resolver(fdecl::OfferResolver::default()),
10646                ],
10647                &fdecl::Component::default()
10648            ),
10649            Err(ErrorList::new(vec![
10650                Error::missing_field(DeclType::OfferProtocol, "source"),
10651                Error::missing_field(DeclType::OfferProtocol, "source_name"),
10652                Error::missing_field(DeclType::OfferProtocol, "target"),
10653                Error::missing_field(DeclType::OfferProtocol, "target_name"),
10654                Error::missing_field(DeclType::OfferProtocol, "dependency_type"),
10655                Error::missing_field(DeclType::OfferService, "source"),
10656                Error::missing_field(DeclType::OfferService, "source_name"),
10657                Error::missing_field(DeclType::OfferService, "target"),
10658                Error::missing_field(DeclType::OfferService, "target_name"),
10659                Error::missing_field(DeclType::OfferDirectory, "source"),
10660                Error::missing_field(DeclType::OfferDirectory, "source_name"),
10661                Error::missing_field(DeclType::OfferDirectory, "target"),
10662                Error::missing_field(DeclType::OfferDirectory, "target_name"),
10663                Error::missing_field(DeclType::OfferDirectory, "dependency_type"),
10664                Error::missing_field(DeclType::OfferStorage, "source"),
10665                Error::missing_field(DeclType::OfferStorage, "source_name"),
10666                Error::missing_field(DeclType::OfferStorage, "target"),
10667                Error::missing_field(DeclType::OfferStorage, "target_name"),
10668                Error::missing_field(DeclType::OfferRunner, "source"),
10669                Error::missing_field(DeclType::OfferRunner, "source_name"),
10670                Error::missing_field(DeclType::OfferRunner, "target"),
10671                Error::missing_field(DeclType::OfferRunner, "target_name"),
10672                Error::missing_field(DeclType::OfferResolver, "source"),
10673                Error::missing_field(DeclType::OfferResolver, "source_name"),
10674                Error::missing_field(DeclType::OfferResolver, "target"),
10675                Error::missing_field(DeclType::OfferResolver, "target_name"),
10676            ]))
10677        );
10678    }
10679
10680    test_dependency! {
10681        (test_validate_offers_protocol_dependency_cycle) => {
10682            ty = fdecl::Offer::Protocol,
10683            offer_decl = fdecl::OfferProtocol {
10684                source: None,  // Filled by macro
10685                target: None,  // Filled by macro
10686                source_name: Some(format!("thing")),
10687                target_name: Some(format!("thing")),
10688                dependency_type: Some(fdecl::DependencyType::Strong),
10689                ..Default::default()
10690            },
10691        },
10692        (test_validate_offers_directory_dependency_cycle) => {
10693            ty = fdecl::Offer::Directory,
10694            offer_decl = fdecl::OfferDirectory {
10695                source: None,  // Filled by macro
10696                target: None,  // Filled by macro
10697                source_name: Some(format!("thing")),
10698                target_name: Some(format!("thing")),
10699                rights: Some(fio::Operations::CONNECT),
10700                subdir: None,
10701                dependency_type: Some(fdecl::DependencyType::Strong),
10702                ..Default::default()
10703            },
10704        },
10705        (test_validate_offers_service_dependency_cycle) => {
10706            ty = fdecl::Offer::Service,
10707            offer_decl = fdecl::OfferService {
10708                source: None,  // Filled by macro
10709                target: None,  // Filled by macro
10710                source_name: Some(format!("thing")),
10711                target_name: Some(format!("thing")),
10712                ..Default::default()
10713            },
10714        },
10715        (test_validate_offers_runner_dependency_cycle) => {
10716            ty = fdecl::Offer::Runner,
10717            offer_decl = fdecl::OfferRunner {
10718                source: None,  // Filled by macro
10719                target: None,  // Filled by macro
10720                source_name: Some(format!("thing")),
10721                target_name: Some(format!("thing")),
10722                ..Default::default()
10723            },
10724        },
10725        (test_validate_offers_resolver_dependency_cycle) => {
10726            ty = fdecl::Offer::Resolver,
10727            offer_decl = fdecl::OfferResolver {
10728                source: None,  // Filled by macro
10729                target: None,  // Filled by macro
10730                source_name: Some(format!("thing")),
10731                target_name: Some(format!("thing")),
10732                ..Default::default()
10733            },
10734        },
10735    }
10736    test_weak_dependency! {
10737        (test_validate_offers_protocol_weak_dependency_cycle) => {
10738            ty = fdecl::Offer::Protocol,
10739            offer_decl = fdecl::OfferProtocol {
10740                source: None,  // Filled by macro
10741                target: None,  // Filled by macro
10742                source_name: Some(format!("thing")),
10743                target_name: Some(format!("thing")),
10744                dependency_type: None, // Filled by macro
10745                ..Default::default()
10746            },
10747        },
10748        (test_validate_offers_directory_weak_dependency_cycle) => {
10749            ty = fdecl::Offer::Directory,
10750            offer_decl = fdecl::OfferDirectory {
10751                source: None,  // Filled by macro
10752                target: None,  // Filled by macro
10753                source_name: Some(format!("thing")),
10754                target_name: Some(format!("thing")),
10755                rights: Some(fio::Operations::CONNECT),
10756                subdir: None,
10757                dependency_type: None,  // Filled by macro
10758                ..Default::default()
10759            },
10760        },
10761        (test_validate_offers_service_weak_dependency_cycle) => {
10762            ty = fdecl::Offer::Service,
10763            offer_decl = fdecl::OfferService {
10764                source: None,  // Filled by macro
10765                target: None,  // Filled by macro
10766                source_name: Some(format!("thing")),
10767                target_name: Some(format!("thing")),
10768                dependency_type: None, // Filled by macro
10769                ..Default::default()
10770            },
10771        },
10772    }
10773}