blob: f31e6d09934bfa0acb19a4b41d01b46955c810fc [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use {
crate::model::{
component::{ComponentInstance, WeakComponentInstance},
routing::{open_capability, RouteRequest},
},
::routing::{
component_instance::ComponentInstanceInterface,
resolving::{ComponentAddress, ResolvedComponent, ResolverError},
},
async_trait::async_trait,
cm_rust::{ConfigValueSource, FidlIntoNative, ResolverRegistration},
fidl_fuchsia_component_decl as fdecl, fidl_fuchsia_component_resolution as fresolution,
fidl_fuchsia_mem as fmem,
std::{collections::HashMap, sync::Arc},
tracing::error,
};
/// Resolves a component URL to its content.
#[async_trait]
pub trait Resolver: std::fmt::Debug {
/// Resolves a component URL to its content. This function takes in the
/// `component_address` (from an absolute or relative URL), and the `target`
/// component that is trying to be resolved.
async fn resolve(
&self,
component_address: &ComponentAddress,
) -> Result<ResolvedComponent, ResolverError>;
}
/// Resolves a component URL using a resolver selected based on the URL's scheme.
#[derive(Debug, Default)]
pub struct ResolverRegistry {
resolvers: HashMap<String, Box<dyn Resolver + Send + Sync + 'static>>,
}
impl ResolverRegistry {
pub fn new() -> ResolverRegistry {
Default::default()
}
pub fn register(
&mut self,
scheme: String,
resolver: Box<dyn Resolver + Send + Sync + 'static>,
) {
// ComponentDecl validation checks that there aren't any duplicate schemes.
assert!(
self.resolvers.insert(scheme, resolver).is_none(),
"Found duplicate scheme in ComponentDecl"
);
}
/// Creates and populates a `ResolverRegistry` with `RemoteResolvers` that
/// have been registered with an environment.
pub fn from_decl(decl: &[ResolverRegistration], parent: &Arc<ComponentInstance>) -> Self {
let mut registry = ResolverRegistry::new();
for resolver in decl {
registry.register(
resolver.scheme.clone().into(),
Box::new(RemoteResolver::new(resolver.clone(), parent.as_weak())),
);
}
registry
}
}
#[async_trait]
impl Resolver for ResolverRegistry {
async fn resolve(
&self,
component_address: &ComponentAddress,
) -> Result<ResolvedComponent, ResolverError> {
if let Some(resolver) = self.resolvers.get(component_address.scheme()) {
resolver.resolve(component_address).await
} else {
Err(ResolverError::SchemeNotRegistered)
}
}
}
/// A resolver whose implementation lives in an external component. The source
/// of the resolver is determined through capability routing.
#[derive(Debug)]
pub struct RemoteResolver {
registration: ResolverRegistration,
component: WeakComponentInstance,
}
impl RemoteResolver {
pub fn new(registration: ResolverRegistration, component: WeakComponentInstance) -> Self {
RemoteResolver { registration, component }
}
}
// TODO(61288): Implement some sort of caching of the routed capability. Multiple
// component URL resolutions should be possible on a single channel.
#[async_trait]
impl Resolver for RemoteResolver {
async fn resolve(
&self,
component_address: &ComponentAddress,
) -> Result<ResolvedComponent, ResolverError> {
let component = self.component.upgrade().map_err(ResolverError::routing_error)?;
let proxy: fresolution::ResolverProxy =
open_capability(&RouteRequest::Resolver(self.registration.clone()), &component)
.await
.map_err(ResolverError::routing_error)?;
let (component_url, some_context) = component_address.to_url_and_context();
let component = if component_address.is_relative_path() {
let context = some_context.ok_or_else(|| {
error!(url=%component_url, "calling resolve_with_context() with absolute");
ResolverError::RelativeUrlMissingContext(component_url.to_string())
})?;
proxy
.resolve_with_context(component_url, &context.into())
.await
.map_err(ResolverError::fidl_error)??
} else {
proxy.resolve(component_url).await.map_err(ResolverError::fidl_error)??
};
let decl_buffer: fmem::Data = component.decl.ok_or(ResolverError::RemoteInvalidData)?;
let decl = read_and_validate_manifest(&decl_buffer)?;
let config_values = match &decl.config {
Some(config) => match config.value_source {
ConfigValueSource::PackagePath(_) => Some(read_and_validate_config_values(
&component.config_values.ok_or(ResolverError::RemoteInvalidData)?,
)?),
ConfigValueSource::Capabilities(_) => None,
},
None => None,
};
let resolved_url = component.url.ok_or(ResolverError::RemoteInvalidData)?;
let context_to_resolve_children = component.resolution_context.map(Into::into);
let abi_revision = component.abi_revision.map(Into::into);
Ok(ResolvedComponent {
resolved_url,
context_to_resolve_children,
decl,
package: component.package.map(TryInto::try_into).transpose()?,
config_values,
abi_revision,
})
}
}
/// Given a ref-counted resolver, returns a boxed resolver that delegates to the ref-counted
/// resolver.
pub fn box_arc_resolver(
arc: &Arc<impl Resolver + Send + Sync + 'static>,
) -> Box<dyn Resolver + Send + Sync + 'static> {
Box::new(InternalResolver(arc.clone()))
}
#[derive(Debug)]
struct InternalResolver(Arc<dyn Resolver + Send + Sync + 'static>);
#[async_trait]
impl Resolver for InternalResolver {
async fn resolve(
&self,
component_address: &ComponentAddress,
) -> Result<ResolvedComponent, ResolverError> {
self.0.resolve(component_address).await
}
}
pub fn read_and_validate_manifest(
data: &fmem::Data,
) -> Result<cm_rust::ComponentDecl, ResolverError> {
let bytes = mem_util::bytes_from_data(data).map_err(ResolverError::manifest_invalid)?;
read_and_validate_manifest_bytes(&bytes)
}
pub fn read_and_validate_manifest_bytes(
bytes: &[u8],
) -> Result<cm_rust::ComponentDecl, ResolverError> {
let component_decl: fdecl::Component =
fidl::unpersist(bytes).map_err(ResolverError::manifest_invalid)?;
cm_fidl_validator::validate(&component_decl).map_err(ResolverError::manifest_invalid)?;
Ok(component_decl.fidl_into_native())
}
pub fn read_and_validate_config_values(
data: &fmem::Data,
) -> Result<cm_rust::ConfigValuesData, ResolverError> {
let bytes = mem_util::bytes_from_data(&data).map_err(ResolverError::config_values_invalid)?;
let values = fidl::unpersist(&bytes).map_err(ResolverError::fidl_error)?;
cm_fidl_validator::validate_values_data(&values)
.map_err(|e| ResolverError::config_values_invalid(e))?;
Ok(values.fidl_into_native())
}
#[cfg(test)]
mod tests {
use {
super::*,
crate::model::{
actions::DiscoverAction,
component::{manager::ComponentManagerInstance, WeakExtendedInstance},
context::ModelContext,
environment::Environment,
hooks::Hooks,
structured_dict::ComponentInput,
},
anyhow::{format_err, Error},
assert_matches::assert_matches,
async_trait::async_trait,
cm_moniker::InstancedMoniker,
cm_rust::NativeIntoFidl,
cm_rust_testing::new_decl_from_json,
fidl_fuchsia_component_decl as fdecl,
lazy_static::lazy_static,
moniker::MonikerBase,
routing::environment::{DebugRegistry, RunnerRegistry},
routing::resolving::ComponentResolutionContext,
serde_json::json,
std::sync::{Mutex, Weak},
};
#[derive(Debug)]
struct MockOkResolver {
pub expected_url: String,
pub resolved_url: String,
}
#[async_trait]
impl Resolver for MockOkResolver {
async fn resolve(
&self,
component_address: &ComponentAddress,
) -> Result<ResolvedComponent, ResolverError> {
assert_eq!(self.expected_url.as_str(), component_address.url());
Ok(ResolvedComponent {
resolved_url: self.resolved_url.clone(),
// MockOkResolver only resolves one component, so it does not
// need to provide a context for resolving children.
context_to_resolve_children: None,
decl: cm_rust::ComponentDecl::default(),
package: None,
config_values: None,
abi_revision: Some(
version_history::HISTORY.get_example_supported_version_for_tests().abi_revision,
),
})
}
}
struct MockErrorResolver {
pub expected_url: String,
pub error: Box<dyn Fn(&str) -> ResolverError + Send + Sync + 'static>,
}
impl core::fmt::Debug for MockErrorResolver {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MockErrorResolver").finish()
}
}
#[async_trait]
impl Resolver for MockErrorResolver {
async fn resolve(
&self,
component_address: &ComponentAddress,
) -> Result<ResolvedComponent, ResolverError> {
assert_eq!(self.expected_url, component_address.url());
Err((self.error)(component_address.url()))
}
}
#[derive(Debug, Clone)]
struct ResolveState {
pub expected_url: String,
pub resolved_url: String,
pub expected_context: Option<ComponentResolutionContext>,
pub context_to_resolve_children: Option<ComponentResolutionContext>,
}
impl ResolveState {
fn new(
url: &str,
expected_context: Option<ComponentResolutionContext>,
context_to_resolve_children: Option<ComponentResolutionContext>,
) -> Self {
Self {
expected_url: url.to_string(),
resolved_url: url.to_string(),
expected_context,
context_to_resolve_children,
}
}
}
#[derive(Debug)]
struct MockMultipleOkResolver {
pub resolve_states: Arc<Mutex<Vec<ResolveState>>>,
}
impl MockMultipleOkResolver {
fn new(resolve_states: Vec<ResolveState>) -> Self {
Self { resolve_states: Arc::new(Mutex::new(resolve_states)) }
}
}
#[async_trait]
impl Resolver for MockMultipleOkResolver {
async fn resolve(
&self,
component_address: &ComponentAddress,
) -> Result<ResolvedComponent, ResolverError> {
let ResolveState {
expected_url,
resolved_url,
expected_context,
context_to_resolve_children,
} = self.resolve_states.lock().unwrap().remove(0);
let (component_url, some_context) = component_address.to_url_and_context();
assert_eq!(expected_url, component_url);
assert_eq!(expected_context.as_ref(), some_context, "resolving {}", component_url);
Ok(ResolvedComponent {
resolved_url,
context_to_resolve_children,
// We don't actually need to return a valid component here as these unit tests only
// cover the process of going from relative -> full URL.
decl: cm_rust::ComponentDecl::default(),
package: None,
config_values: None,
abi_revision: Some(
version_history::HISTORY.get_example_supported_version_for_tests().abi_revision,
),
})
}
}
async fn new_root_discovered_component(
environment: Environment,
context: Arc<ModelContext>,
component_manager_instance: Weak<ComponentManagerInstance>,
component_url: String,
) -> Arc<ComponentInstance> {
let component = ComponentInstance::new_root(
environment,
context,
component_manager_instance,
component_url,
)
.await;
// We don't care about waiting for the discover action to complete, just that it's started.
let _ = component
.lock_actions()
.await
.register_no_wait(&component, DiscoverAction::new(ComponentInput::default()))
.await;
component
}
async fn new_discovered_component(
environment: Arc<Environment>,
instanced_moniker: InstancedMoniker,
component_url: String,
startup: fdecl::StartupMode,
on_terminate: fdecl::OnTerminate,
config_parent_overrides: Option<Vec<cm_rust::ConfigOverride>>,
context: Arc<ModelContext>,
parent: WeakExtendedInstance,
hooks: Arc<Hooks>,
persistent_storage: bool,
) -> Arc<ComponentInstance> {
let component = ComponentInstance::new(
environment,
instanced_moniker,
component_url,
startup,
on_terminate,
config_parent_overrides,
context,
parent,
hooks,
persistent_storage,
)
.await;
// We don't care about waiting for the discover action to complete, just that it's started.
let _ = component
.lock_actions()
.await
.register_no_wait(&component, DiscoverAction::new(ComponentInput::default()))
.await;
component
}
#[fuchsia_async::run_until_stalled(test)]
async fn register_and_resolve() {
let mut registry = ResolverRegistry::new();
registry.register(
"foo".to_string(),
Box::new(MockOkResolver {
expected_url: "foo://url".to_string(),
resolved_url: "foo://resolved".to_string(),
}),
);
registry.register(
"bar".to_string(),
Box::new(MockErrorResolver {
expected_url: "bar://url".to_string(),
error: Box::new(|_| {
ResolverError::manifest_not_found(format_err!("not available"))
}),
}),
);
let root = new_root_discovered_component(
Environment::empty(),
Arc::new(ModelContext::new_for_test()),
Weak::new(),
"fuchsia-boot:///#meta/root.cm".to_string(),
)
.await;
// Resolve known scheme that returns success.
let component = registry
.resolve(&ComponentAddress::from_absolute_url("foo://url").unwrap())
.await
.unwrap();
assert_eq!("foo://resolved", component.resolved_url);
// Resolve a different scheme that produces an error.
let expected_res: Result<ResolvedComponent, ResolverError> =
Err(ResolverError::manifest_not_found(format_err!("not available")));
assert_eq!(
format!("{:?}", expected_res),
format!(
"{:?}",
registry.resolve(&ComponentAddress::from_absolute_url("bar://url").unwrap()).await
)
);
// Resolve an unknown scheme
let expected_res: Result<ResolvedComponent, ResolverError> =
Err(ResolverError::SchemeNotRegistered);
assert_eq!(
format!("{:?}", expected_res),
format!(
"{:?}",
registry
.resolve(&ComponentAddress::from_absolute_url("unknown://url").unwrap())
.await
),
);
// Resolve a possible relative path (e.g., subpackage) URL lacking a
// resolvable parent causes a SchemeNotRegistered.
assert_matches!(
ComponentAddress::from("xxx#meta/comp.cm", &root).await,
Err(ResolverError::NoParentContext(_))
);
}
#[fuchsia::test]
#[should_panic(expected = "Found duplicate scheme in ComponentDecl")]
fn test_duplicate_registration() {
let mut registry = ResolverRegistry::new();
let resolver_a =
MockOkResolver { expected_url: "".to_string(), resolved_url: "".to_string() };
let resolver_b =
MockOkResolver { expected_url: "".to_string(), resolved_url: "".to_string() };
registry.register("fuchsia-pkg".to_string(), Box::new(resolver_a));
registry.register("fuchsia-pkg".to_string(), Box::new(resolver_b));
}
#[fuchsia::test]
fn test_multiple_scheme_registration() {
let mut registry = ResolverRegistry::new();
let resolver_a =
MockOkResolver { expected_url: "".to_string(), resolved_url: "".to_string() };
let resolver_b =
MockOkResolver { expected_url: "".to_string(), resolved_url: "".to_string() };
registry.register("fuchsia-pkg".to_string(), Box::new(resolver_a));
registry.register("fuchsia-boot".to_string(), Box::new(resolver_b));
}
lazy_static! {
static ref COMPONENT_DECL: cm_rust::ComponentDecl = new_decl_from_json(json!(
{
"include": [ "syslog/client.shard.cml" ],
"program": {
"runner": "elf",
"binary": "bin/example",
},
"children": [
{
"name": "logger",
"url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm",
"environment": "#env_one",
},
],
"collections": [
{
"name": "modular",
"durability": "transient",
},
],
"capabilities": [
{
"protocol": "fuchsia.logger.Log2",
"path": "/svc/fuchsia.logger.Log2",
},
],
"use": [
{
"protocol": "fuchsia.fonts.LegacyProvider",
},
],
"environments": [
{
"name": "env_one",
"extends": "none",
"__stop_timeout_ms": 1337,
},
],
"facets": {
"author": "Fuchsia",
}}))
.expect("failed to construct manifest");
}
#[fuchsia::test]
fn test_read_and_validate_manifest() {
let manifest = fmem::Data::Bytes(
fidl::persist(&COMPONENT_DECL.clone().native_into_fidl())
.expect("failed to encode manifest"),
);
let actual = read_and_validate_manifest(&manifest).expect("failed to decode manifest");
assert_eq!(actual, COMPONENT_DECL.clone());
}
#[fuchsia::test]
async fn test_read_and_validate_config_values() {
let fidl_config_values = fdecl::ConfigValuesData {
values: Some(vec![
fdecl::ConfigValueSpec {
value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(false))),
..Default::default()
},
fdecl::ConfigValueSpec {
value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Uint8(5))),
..Default::default()
},
fdecl::ConfigValueSpec {
value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::String(
"hello!".to_string(),
))),
..Default::default()
},
fdecl::ConfigValueSpec {
value: Some(fdecl::ConfigValue::Vector(fdecl::ConfigVectorValue::BoolVector(
vec![true, false],
))),
..Default::default()
},
fdecl::ConfigValueSpec {
value: Some(fdecl::ConfigValue::Vector(
fdecl::ConfigVectorValue::StringVector(vec![
"hello!".to_string(),
"world!".to_string(),
]),
)),
..Default::default()
},
]),
checksum: Some(fdecl::ConfigChecksum::Sha256([0; 32])),
..Default::default()
};
let config_values = cm_rust::ConfigValuesData {
values: vec![
cm_rust::ConfigValueSpec {
value: cm_rust::ConfigValue::Single(cm_rust::ConfigSingleValue::Bool(false)),
},
cm_rust::ConfigValueSpec {
value: cm_rust::ConfigValue::Single(cm_rust::ConfigSingleValue::Uint8(5)),
},
cm_rust::ConfigValueSpec {
value: cm_rust::ConfigValue::Single(cm_rust::ConfigSingleValue::String(
"hello!".to_string(),
)),
},
cm_rust::ConfigValueSpec {
value: cm_rust::ConfigValue::Vector(cm_rust::ConfigVectorValue::BoolVector(
vec![true, false],
)),
},
cm_rust::ConfigValueSpec {
value: cm_rust::ConfigValue::Vector(cm_rust::ConfigVectorValue::StringVector(
vec!["hello!".to_string(), "world!".to_string()],
)),
},
],
checksum: cm_rust::ConfigChecksum::Sha256([0; 32]),
};
let data = fmem::Data::Bytes(
fidl::persist(&fidl_config_values).expect("failed to encode config values"),
);
let actual =
read_and_validate_config_values(&data).expect("failed to decode config values");
assert_eq!(actual, config_values);
}
#[fuchsia::test]
async fn test_from_absolute_component_url_with_component_instance() -> Result<(), Error> {
let top_instance = Arc::new(ComponentManagerInstance::new(vec![], vec![]));
let environment = Environment::new_root(
&top_instance,
RunnerRegistry::default(),
ResolverRegistry::new(),
DebugRegistry::default(),
);
let root = new_root_discovered_component(
environment,
Arc::new(ModelContext::new_for_test()),
Weak::new(),
"fuchsia-pkg://fuchsia.com/package#meta/comp.cm".to_string(),
)
.await;
let abs =
ComponentAddress::from("fuchsia-pkg://fuchsia.com/package#meta/comp.cm", &root).await?;
assert_matches!(abs, ComponentAddress::Absolute { .. });
assert_eq!(abs.scheme(), "fuchsia-pkg");
assert_eq!(abs.path(), "/package");
assert_eq!(abs.resource(), Some("meta/comp.cm"));
Ok(())
}
#[fuchsia::test]
async fn test_from_relative_path_component_url_with_component_instance() -> Result<(), Error> {
let expected_urls_and_contexts = vec![
ResolveState::new(
"fuchsia-pkg://fuchsia.com/package#meta/comp.cm",
None,
Some(ComponentResolutionContext::new("package_context".as_bytes().to_vec())),
),
ResolveState::new(
"subpackage#meta/subcomp.cm",
Some(ComponentResolutionContext::new("package_context".as_bytes().to_vec())),
Some(ComponentResolutionContext::new("subpackage_context".as_bytes().to_vec())),
),
];
let mut resolver = ResolverRegistry::new();
resolver.register(
"fuchsia-pkg".to_string(),
Box::new(MockMultipleOkResolver::new(expected_urls_and_contexts.clone())),
);
let top_instance = Arc::new(ComponentManagerInstance::new(vec![], vec![]));
let environment = Environment::new_root(
&top_instance,
RunnerRegistry::default(),
resolver,
DebugRegistry::default(),
);
let root = new_root_discovered_component(
environment,
Arc::new(ModelContext::new_for_test()),
Weak::new(),
"fuchsia-pkg://fuchsia.com/package#meta/comp.cm".to_string(),
)
.await;
let child = new_discovered_component(
root.environment.clone(),
InstancedMoniker::parse_str("/root:0/child:0")?,
"subpackage#meta/subcomp.cm".to_string(),
fdecl::StartupMode::Lazy,
fdecl::OnTerminate::None,
None,
Arc::new(ModelContext::new_for_test()),
WeakExtendedInstance::Component(WeakComponentInstance::from(&root)),
Arc::new(Hooks::new()),
false,
)
.await;
let relpath = ComponentAddress::from("subpackage#meta/subcomp.cm", &child).await?;
assert_matches!(relpath, ComponentAddress::RelativePath { .. });
assert_eq!(relpath.path(), "subpackage");
assert_eq!(relpath.resource(), Some("meta/subcomp.cm"));
assert_eq!(
relpath.context(),
&ComponentResolutionContext::new("package_context".as_bytes().to_vec())
);
// Test some error conditions in `ComponentAddress::from(<invalid relative URL>)`
assert_matches!(
ComponentAddress::from("", &child).await,
Err(ResolverError::MalformedUrl(..))
);
assert_matches!(
ComponentAddress::from("?query_param=value", &child).await,
Err(ResolverError::MalformedUrl(..))
);
Ok(())
}
#[fuchsia::test]
async fn test_from_relative_path_component_url_with_fuchsia_boot_component_instance(
) -> Result<(), Error> {
let expected_urls_and_contexts = vec![
ResolveState::new(
"fuchsia-boot:///package#meta/comp.cm",
None,
Some(ComponentResolutionContext::new("package_context".as_bytes().to_vec())),
),
ResolveState::new(
"subpackage#meta/subcomp.cm",
Some(ComponentResolutionContext::new("package_context".as_bytes().to_vec())),
Some(ComponentResolutionContext::new("subpackage_context".as_bytes().to_vec())),
),
];
let mut resolver = ResolverRegistry::new();
resolver.register(
"fuchsia-boot".to_string(),
Box::new(MockMultipleOkResolver::new(expected_urls_and_contexts.clone())),
);
let top_instance = Arc::new(ComponentManagerInstance::new(vec![], vec![]));
let environment = Environment::new_root(
&top_instance,
RunnerRegistry::default(),
resolver,
DebugRegistry::default(),
);
let root = new_root_discovered_component(
environment,
Arc::new(ModelContext::new_for_test()),
Weak::new(),
"fuchsia-boot:///package#meta/comp.cm".to_string(),
)
.await;
let child = new_discovered_component(
root.environment.clone(),
InstancedMoniker::parse_str("/root:0/child:0")?,
"subpackage#meta/subcomp.cm".to_string(),
fdecl::StartupMode::Lazy,
fdecl::OnTerminate::None,
None,
Arc::new(ModelContext::new_for_test()),
WeakExtendedInstance::Component(WeakComponentInstance::from(&root)),
Arc::new(Hooks::new()),
false,
)
.await;
let relpath = ComponentAddress::from("subpackage#meta/subcomp.cm", &child).await?;
assert_matches!(relpath, ComponentAddress::RelativePath { .. });
assert_eq!(relpath.path(), "subpackage");
assert_eq!(relpath.resource(), Some("meta/subcomp.cm"));
assert_eq!(
relpath.context(),
&ComponentResolutionContext::new("package_context".as_bytes().to_vec())
);
Ok(())
}
#[fuchsia::test]
async fn test_from_relative_path_component_url_with_cast_component_instance(
) -> Result<(), Error> {
let expected_urls_and_contexts = vec![
ResolveState::new(
"cast:00000000/package#meta/comp.cm",
None,
Some(ComponentResolutionContext::new("package_context".as_bytes().to_vec())),
),
ResolveState::new(
"subpackage#meta/subcomp.cm",
Some(ComponentResolutionContext::new("package_context".as_bytes().to_vec())),
Some(ComponentResolutionContext::new("subpackage_context".as_bytes().to_vec())),
),
];
let mut resolver = ResolverRegistry::new();
resolver.register(
"cast".to_string(),
Box::new(MockMultipleOkResolver::new(expected_urls_and_contexts.clone())),
);
let top_instance = Arc::new(ComponentManagerInstance::new(vec![], vec![]));
let environment = Environment::new_root(
&top_instance,
RunnerRegistry::default(),
resolver,
DebugRegistry::default(),
);
let root = new_root_discovered_component(
environment,
Arc::new(ModelContext::new_for_test()),
Weak::new(),
"cast:00000000/package#meta/comp.cm".to_string(),
)
.await;
let child = new_discovered_component(
root.environment.clone(),
InstancedMoniker::parse_str("/root:0/child:0")?,
"subpackage#meta/subcomp.cm".to_string(),
fdecl::StartupMode::Lazy,
fdecl::OnTerminate::None,
None,
Arc::new(ModelContext::new_for_test()),
WeakExtendedInstance::Component(WeakComponentInstance::from(&root)),
Arc::new(Hooks::new()),
false,
)
.await;
let relpath = ComponentAddress::from("subpackage#meta/subcomp.cm", &child).await?;
assert_matches!(relpath, ComponentAddress::RelativePath { .. });
assert_eq!(relpath.path(), "subpackage");
assert_eq!(relpath.resource(), Some("meta/subcomp.cm"));
assert_eq!(
relpath.context(),
&ComponentResolutionContext::new("package_context".as_bytes().to_vec())
);
Ok(())
}
#[fuchsia::test]
async fn relative_to_fuchsia_pkg() -> Result<(), Error> {
let expected_urls_and_contexts = vec![
ResolveState::new(
"fuchsia-pkg://fuchsia.com/my-package#meta/my-root.cm",
None,
Some(ComponentResolutionContext::new("package_context".as_bytes().to_vec())),
),
ResolveState::new(
"fuchsia-pkg://fuchsia.com/my-package#meta/my-child.cm",
None,
Some(ComponentResolutionContext::new("package_context".as_bytes().to_vec())),
),
];
let mut resolver = ResolverRegistry::new();
resolver.register(
"fuchsia-pkg".to_string(),
Box::new(MockMultipleOkResolver::new(expected_urls_and_contexts.clone())),
);
let top_instance = Arc::new(ComponentManagerInstance::new(vec![], vec![]));
let environment = Environment::new_root(
&top_instance,
RunnerRegistry::default(),
resolver,
DebugRegistry::default(),
);
let root = new_root_discovered_component(
environment,
Arc::new(ModelContext::new_for_test()),
Weak::new(),
"fuchsia-pkg://fuchsia.com/my-package#meta/my-root.cm".to_string(),
)
.await;
let child = new_discovered_component(
root.environment.clone(),
InstancedMoniker::parse_str("/root:0/child:0")?,
"#meta/my-child.cm".to_string(),
fdecl::StartupMode::Lazy,
fdecl::OnTerminate::None,
None,
Arc::new(ModelContext::new_for_test()),
WeakExtendedInstance::Component(WeakComponentInstance::from(&root)),
Arc::new(Hooks::new()),
false,
)
.await;
let resolved = child
.environment
.resolve(&ComponentAddress::from(&child.component_url, &child).await?)
.await?;
let expected = expected_urls_and_contexts.as_slice().last().unwrap();
assert_eq!(&resolved.resolved_url, &expected.resolved_url);
assert_eq!(&resolved.context_to_resolve_children, &expected.context_to_resolve_children);
Ok(())
}
#[fuchsia::test]
async fn two_relative_to_fuchsia_pkg() -> Result<(), Error> {
let expected_urls_and_contexts = vec![
ResolveState::new(
"fuchsia-pkg://fuchsia.com/my-package#meta/my-root.cm",
None,
Some(ComponentResolutionContext::new("package_context".as_bytes().to_vec())),
),
ResolveState::new(
"fuchsia-pkg://fuchsia.com/my-package#meta/my-child.cm",
None,
Some(ComponentResolutionContext::new("package_context".as_bytes().to_vec())),
),
ResolveState::new(
"fuchsia-pkg://fuchsia.com/my-package#meta/my-child2.cm",
None,
Some(ComponentResolutionContext::new("package_context".as_bytes().to_vec())),
),
];
let mut resolver = ResolverRegistry::new();
resolver.register(
"fuchsia-pkg".to_string(),
Box::new(MockMultipleOkResolver::new(expected_urls_and_contexts.clone())),
);
let top_instance = Arc::new(ComponentManagerInstance::new(vec![], vec![]));
let environment = Environment::new_root(
&top_instance,
RunnerRegistry::default(),
resolver,
DebugRegistry::default(),
);
let root = new_root_discovered_component(
environment,
Arc::new(ModelContext::new_for_test()),
Weak::new(),
"fuchsia-pkg://fuchsia.com/my-package#meta/my-root.cm".to_string(),
)
.await;
let child_one = new_discovered_component(
root.environment.clone(),
InstancedMoniker::parse_str("/root:0/child:0")?,
"#meta/my-child.cm".to_string(),
fdecl::StartupMode::Lazy,
fdecl::OnTerminate::None,
None,
Arc::new(ModelContext::new_for_test()),
WeakExtendedInstance::Component(WeakComponentInstance::from(&root)),
Arc::new(Hooks::new()),
false,
)
.await;
let child_two = new_discovered_component(
root.environment.clone(),
InstancedMoniker::parse_str("/root:0/child:0")?,
"#meta/my-child2.cm".to_string(),
fdecl::StartupMode::Lazy,
fdecl::OnTerminate::None,
None,
Arc::new(ModelContext::new_for_test()),
WeakExtendedInstance::Component(WeakComponentInstance::from(&child_one)),
Arc::new(Hooks::new()),
false,
)
.await;
let resolved = child_two
.environment
.resolve(&ComponentAddress::from(&child_two.component_url, &child_two).await?)
.await?;
let expected = expected_urls_and_contexts.as_slice().last().unwrap();
assert_eq!(&resolved.resolved_url, &expected.resolved_url);
assert_eq!(&resolved.context_to_resolve_children, &expected.context_to_resolve_children);
Ok(())
}
#[fuchsia::test]
async fn relative_to_fuchsia_boot() -> Result<(), Error> {
let expected_urls_and_contexts = vec![
ResolveState::new(
"fuchsia-boot:///#meta/my-root.cm",
None,
Some(ComponentResolutionContext::new("package_context".as_bytes().to_vec())),
),
ResolveState::new(
"fuchsia-boot:///#meta/my-child.cm",
None,
Some(ComponentResolutionContext::new("package_context".as_bytes().to_vec())),
),
];
let mut resolver = ResolverRegistry::new();
resolver.register(
"fuchsia-boot".to_string(),
Box::new(MockMultipleOkResolver::new(expected_urls_and_contexts.clone())),
);
let top_instance = Arc::new(ComponentManagerInstance::new(vec![], vec![]));
let environment = Environment::new_root(
&top_instance,
RunnerRegistry::default(),
resolver,
DebugRegistry::default(),
);
let root = new_root_discovered_component(
environment,
Arc::new(ModelContext::new_for_test()),
Weak::new(),
"fuchsia-boot:///#meta/my-root.cm".to_string(),
)
.await;
let child = new_discovered_component(
root.environment.clone(),
InstancedMoniker::parse_str("/root:0/child:0")?,
"#meta/my-child.cm".to_string(),
fdecl::StartupMode::Lazy,
fdecl::OnTerminate::None,
None,
Arc::new(ModelContext::new_for_test()),
WeakExtendedInstance::Component(WeakComponentInstance::from(&root)),
Arc::new(Hooks::new()),
false,
)
.await;
let resolved = child
.environment
.resolve(&ComponentAddress::from(&child.component_url, &child).await?)
.await?;
let expected = expected_urls_and_contexts.as_slice().last().unwrap();
assert_eq!(&resolved.resolved_url, &expected.resolved_url);
assert_eq!(&resolved.context_to_resolve_children, &expected.context_to_resolve_children);
Ok(())
}
#[fuchsia::test]
async fn relative_to_cast() -> Result<(), Error> {
let expected_urls_and_contexts = vec![
ResolveState::new(
"cast:00000000#meta/my-root.cm",
None,
Some(ComponentResolutionContext::new("package_context".as_bytes().to_vec())),
),
ResolveState::new(
"cast:00000000#meta/my-child.cm",
None,
Some(ComponentResolutionContext::new("package_context".as_bytes().to_vec())),
),
];
let mut resolver = ResolverRegistry::new();
resolver.register(
"cast".to_string(),
Box::new(MockMultipleOkResolver::new(expected_urls_and_contexts.clone())),
);
let top_instance = Arc::new(ComponentManagerInstance::new(vec![], vec![]));
let environment = Environment::new_root(
&top_instance,
RunnerRegistry::default(),
resolver,
DebugRegistry::default(),
);
let root = new_root_discovered_component(
environment,
Arc::new(ModelContext::new_for_test()),
Weak::new(),
"cast:00000000#meta/my-root.cm".to_string(),
)
.await;
let child = new_discovered_component(
root.environment.clone(),
InstancedMoniker::parse_str("/root:0/child:0")?,
"#meta/my-child.cm".to_string(),
fdecl::StartupMode::Lazy,
fdecl::OnTerminate::None,
None,
Arc::new(ModelContext::new_for_test()),
WeakExtendedInstance::Component(WeakComponentInstance::from(&root)),
Arc::new(Hooks::new()),
false,
)
.await;
let resolved = child
.environment
.resolve(&ComponentAddress::from(&child.component_url, &child).await?)
.await?;
let expected = expected_urls_and_contexts.as_slice().last().unwrap();
assert_eq!(&resolved.resolved_url, &expected.resolved_url);
assert_eq!(&resolved.context_to_resolve_children, &expected.context_to_resolve_children);
Ok(())
}
#[fuchsia::test]
async fn resolve_above_root_error() -> Result<(), Error> {
let resolver = ResolverRegistry::new();
let top_instance = Arc::new(ComponentManagerInstance::new(vec![], vec![]));
let environment = Environment::new_root(
&top_instance,
RunnerRegistry::default(),
resolver,
DebugRegistry::default(),
);
let root = new_root_discovered_component(
environment,
Arc::new(ModelContext::new_for_test()),
Weak::new(),
"#meta/my-root.cm".to_string(),
)
.await;
let child = new_discovered_component(
root.environment.clone(),
InstancedMoniker::parse_str("/root:0/child:0")?,
"#meta/my-child.cm".to_string(),
fdecl::StartupMode::Lazy,
fdecl::OnTerminate::None,
None,
Arc::new(ModelContext::new_for_test()),
WeakExtendedInstance::Component(WeakComponentInstance::from(&root)),
Arc::new(Hooks::new()),
false,
)
.await;
let result = ComponentAddress::from(&child.component_url, &child).await;
assert_matches!(result, Err(ResolverError::Internal(..)));
Ok(())
}
#[fuchsia::test]
async fn relative_resource_and_path_to_fuchsia_pkg() -> Result<(), Error> {
let expected_urls_and_contexts = vec![
ResolveState::new(
"fuchsia-pkg://fuchsia.com/my-package#meta/my-root.cm",
None,
Some(ComponentResolutionContext::new("fuchsia.com...".as_bytes().to_vec())),
),
ResolveState::new(
"my-subpackage#meta/my-child.cm",
Some(ComponentResolutionContext::new("fuchsia.com...".as_bytes().to_vec())),
Some(ComponentResolutionContext::new("my-subpackage...".as_bytes().to_vec())),
),
ResolveState::new(
"my-subpackage#meta/my-child2.cm",
Some(ComponentResolutionContext::new("fuchsia.com...".as_bytes().to_vec())),
Some(ComponentResolutionContext::new("my-subpackage...".as_bytes().to_vec())),
),
];
let mut resolver = ResolverRegistry::new();
resolver.register(
"fuchsia-pkg".to_string(),
Box::new(MockMultipleOkResolver::new(expected_urls_and_contexts.clone())),
);
let top_instance = Arc::new(ComponentManagerInstance::new(vec![], vec![]));
let environment = Environment::new_root(
&top_instance,
RunnerRegistry::default(),
resolver,
DebugRegistry::default(),
);
let root = new_root_discovered_component(
environment,
Arc::new(ModelContext::new_for_test()),
Weak::new(),
"fuchsia-pkg://fuchsia.com/my-package#meta/my-root.cm".to_string(),
)
.await;
let child_one = new_discovered_component(
root.environment.clone(),
InstancedMoniker::parse_str("/root:0/child:0")?,
"my-subpackage#meta/my-child.cm".to_string(),
fdecl::StartupMode::Lazy,
fdecl::OnTerminate::None,
None,
Arc::new(ModelContext::new_for_test()),
WeakExtendedInstance::Component(WeakComponentInstance::from(&root)),
Arc::new(Hooks::new()),
false,
)
.await;
let child_two = new_discovered_component(
root.environment.clone(),
InstancedMoniker::parse_str("/root:0/child:0/child2:0")?,
"#meta/my-child2.cm".to_string(),
fdecl::StartupMode::Lazy,
fdecl::OnTerminate::None,
None,
Arc::new(ModelContext::new_for_test()),
WeakExtendedInstance::Component(WeakComponentInstance::from(&child_one)),
Arc::new(Hooks::new()),
false,
)
.await;
let resolved = child_two
.environment
.resolve(&ComponentAddress::from(&child_two.component_url, &child_two).await?)
.await?;
let expected = expected_urls_and_contexts.as_slice().last().unwrap();
assert_eq!(&resolved.resolved_url, &expected.resolved_url);
assert_eq!(&resolved.context_to_resolve_children, &expected.context_to_resolve_children);
Ok(())
}
#[fuchsia::test]
async fn two_relative_resources_and_path_to_fuchsia_pkg() -> Result<(), Error> {
let expected_urls_and_contexts = vec![
ResolveState::new(
"fuchsia-pkg://fuchsia.com/my-package#meta/my-root.cm",
None,
Some(ComponentResolutionContext::new("fuchsia.com...".as_bytes().to_vec())),
),
ResolveState::new(
"my-subpackage#meta/my-child.cm",
Some(ComponentResolutionContext::new("fuchsia.com...".as_bytes().to_vec())),
Some(ComponentResolutionContext::new("my-subpackage...".as_bytes().to_vec())),
),
ResolveState::new(
"my-subpackage#meta/my-child2.cm",
Some(ComponentResolutionContext::new("fuchsia.com...".as_bytes().to_vec())),
Some(ComponentResolutionContext::new("my-subpackage...".as_bytes().to_vec())),
),
ResolveState::new(
"my-subpackage#meta/my-child3.cm",
Some(ComponentResolutionContext::new("fuchsia.com...".as_bytes().to_vec())),
Some(ComponentResolutionContext::new("my-subpackage...".as_bytes().to_vec())),
),
];
let mut resolver = ResolverRegistry::new();
resolver.register(
"fuchsia-pkg".to_string(),
Box::new(MockMultipleOkResolver::new(expected_urls_and_contexts.clone())),
);
let top_instance = Arc::new(ComponentManagerInstance::new(vec![], vec![]));
let environment = Environment::new_root(
&top_instance,
RunnerRegistry::default(),
resolver,
DebugRegistry::default(),
);
let root = new_root_discovered_component(
environment,
Arc::new(ModelContext::new_for_test()),
Weak::new(),
"fuchsia-pkg://fuchsia.com/my-package#meta/my-root.cm".to_string(),
)
.await;
let child_one = new_discovered_component(
root.environment.clone(),
InstancedMoniker::parse_str("/root:0/child:0")?,
"my-subpackage#meta/my-child.cm".to_string(),
fdecl::StartupMode::Lazy,
fdecl::OnTerminate::None,
None,
Arc::new(ModelContext::new_for_test()),
WeakExtendedInstance::Component(WeakComponentInstance::from(&root)),
Arc::new(Hooks::new()),
false,
)
.await;
let child_two = new_discovered_component(
root.environment.clone(),
InstancedMoniker::parse_str("/root:0/child:0/child2:0")?,
"#meta/my-child2.cm".to_string(),
fdecl::StartupMode::Lazy,
fdecl::OnTerminate::None,
None,
Arc::new(ModelContext::new_for_test()),
WeakExtendedInstance::Component(WeakComponentInstance::from(&child_one)),
Arc::new(Hooks::new()),
false,
)
.await;
let child_three = new_discovered_component(
root.environment.clone(),
InstancedMoniker::parse_str("/root:0/child:0/child2:0/child3:0")?,
"#meta/my-child3.cm".to_string(),
fdecl::StartupMode::Lazy,
fdecl::OnTerminate::None,
None,
Arc::new(ModelContext::new_for_test()),
WeakExtendedInstance::Component(WeakComponentInstance::from(&child_two)),
Arc::new(Hooks::new()),
false,
)
.await;
let resolved = child_three
.environment
.resolve(&ComponentAddress::from(&child_three.component_url, &child_three).await?)
.await?;
let expected = expected_urls_and_contexts.as_slice().last().unwrap();
assert_eq!(&resolved.resolved_url, &expected.resolved_url);
assert_eq!(&resolved.context_to_resolve_children, &expected.context_to_resolve_children);
Ok(())
}
#[fuchsia::test]
async fn relative_resources_and_paths_to_realm_builder() -> Result<(), Error> {
let expected_urls_and_contexts = vec![
ResolveState::new(
"fuchsia-pkg://fuchsia.com/my-package#meta/my-root.cm",
None,
Some(ComponentResolutionContext::new("fuchsia.com...".as_bytes().to_vec())),
),
ResolveState::new(
"my-subpackage1#meta/sub1.cm",
Some(ComponentResolutionContext::new("fuchsia.com...".as_bytes().to_vec())),
Some(ComponentResolutionContext::new("my-subpackage1...".as_bytes().to_vec())),
),
ResolveState::new(
"my-subpackage1#meta/sub1-child.cm",
Some(ComponentResolutionContext::new("fuchsia.com...".as_bytes().to_vec())),
Some(ComponentResolutionContext::new("my-subpackage1...".as_bytes().to_vec())),
),
ResolveState::new(
"my-subpackage2#meta/sub2.cm",
Some(ComponentResolutionContext::new("my-subpackage1...".as_bytes().to_vec())),
Some(ComponentResolutionContext::new("my-subpackage2...".as_bytes().to_vec())),
),
ResolveState::new(
"my-subpackage2#meta/sub2-child.cm",
Some(ComponentResolutionContext::new("my-subpackage1...".as_bytes().to_vec())),
Some(ComponentResolutionContext::new("my-subpackage2...".as_bytes().to_vec())),
),
];
let mut resolver = ResolverRegistry::new();
resolver.register(
"fuchsia-pkg".to_string(),
Box::new(MockMultipleOkResolver::new(expected_urls_and_contexts.clone())),
);
resolver.register(
"realm-builder".to_string(),
Box::new(MockOkResolver {
expected_url: "realm-builder://0/my-realm".to_string(),
resolved_url: "realm-builder://0/my-realm".to_string(),
}),
);
let top_instance = Arc::new(ComponentManagerInstance::new(vec![], vec![]));
let environment = Environment::new_root(
&top_instance,
RunnerRegistry::default(),
resolver,
DebugRegistry::default(),
);
let root = new_root_discovered_component(
environment,
Arc::new(ModelContext::new_for_test()),
Weak::new(),
"fuchsia-pkg://fuchsia.com/my-package#meta/my-root.cm".to_string(),
)
.await;
let realm = new_discovered_component(
root.environment.clone(),
InstancedMoniker::parse_str("/root:0/realm:0/child:0")?,
"realm-builder://0/my-realm".to_string(),
fdecl::StartupMode::Lazy,
fdecl::OnTerminate::None,
None,
Arc::new(ModelContext::new_for_test()),
WeakExtendedInstance::Component(WeakComponentInstance::from(&root)),
Arc::new(Hooks::new()),
false,
)
.await;
let child_one = new_discovered_component(
root.environment.clone(),
InstancedMoniker::parse_str("/root:0/realm:0/child:0")?,
"my-subpackage1#meta/sub1.cm".to_string(),
fdecl::StartupMode::Lazy,
fdecl::OnTerminate::None,
None,
Arc::new(ModelContext::new_for_test()),
WeakExtendedInstance::Component(WeakComponentInstance::from(&realm)),
Arc::new(Hooks::new()),
false,
)
.await;
let child_two = new_discovered_component(
root.environment.clone(),
InstancedMoniker::parse_str("/root:0/realm:0/child:0/child2:0")?,
"#meta/sub1-child.cm".to_string(),
fdecl::StartupMode::Lazy,
fdecl::OnTerminate::None,
None,
Arc::new(ModelContext::new_for_test()),
WeakExtendedInstance::Component(WeakComponentInstance::from(&child_one)),
Arc::new(Hooks::new()),
false,
)
.await;
let child_three = new_discovered_component(
root.environment.clone(),
InstancedMoniker::parse_str("/root:0/realm:0/child:0/child2:0/child3:0")?,
"my-subpackage2#meta/sub2.cm".to_string(),
fdecl::StartupMode::Lazy,
fdecl::OnTerminate::None,
None,
Arc::new(ModelContext::new_for_test()),
WeakExtendedInstance::Component(WeakComponentInstance::from(&child_two)),
Arc::new(Hooks::new()),
false,
)
.await;
let child_four = new_discovered_component(
root.environment.clone(),
InstancedMoniker::parse_str("/root:0/realm:0/child:0/child2:0/child3:0/child4:0")?,
"#meta/sub2-child.cm".to_string(),
fdecl::StartupMode::Lazy,
fdecl::OnTerminate::None,
None,
Arc::new(ModelContext::new_for_test()),
WeakExtendedInstance::Component(WeakComponentInstance::from(&child_three)),
Arc::new(Hooks::new()),
false,
)
.await;
let resolved = child_four
.environment
.resolve(&ComponentAddress::from(&child_four.component_url, &child_four).await?)
.await?;
let expected = expected_urls_and_contexts.as_slice().last().unwrap();
assert_eq!(&resolved.resolved_url, &expected.resolved_url);
assert_eq!(&resolved.context_to_resolve_children, &expected.context_to_resolve_children);
Ok(())
}
}