blob: 23583a2c4a2f1beaa82241f52e64b3f0c22e8084 [file] [log] [blame]
// Copyright 2021 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::constants;
use anyhow::{Context, Error, Result};
use fidl::endpoints::{create_endpoints, DiscoverableProtocolMarker, ProtocolMarker};
use fidl_fuchsia_archivist_test as ftest;
use fidl_fuchsia_component_decl as fdecl;
use fidl_fuchsia_testing_harness as fharness;
use fuchsia_component::client::connect_to_protocol;
use fuchsia_component_test::{
Capability, ChildOptions, ChildRef, RealmBuilder, RealmBuilderParams, Ref, Route,
SubRealmBuilder,
};
use realm_proxy_client::RealmProxyClient;
/// Options for creating a test topology.
#[derive(Default)]
pub struct Options {
pub archivist_config: ftest::ArchivistConfig,
pub realm_name: Option<&'static str>,
}
/// Creates a new test realm with an archivist inside.
/// `options_fn` is called with a default RealmOptions struct and can modify any options
/// before the realm is created.
pub async fn create_realm(options: ftest::RealmOptions) -> Result<RealmProxyClient> {
let realm_factory = connect_to_protocol::<ftest::RealmFactoryMarker>()?;
let (client, server) = create_endpoints::<fharness::RealmProxy_Marker>();
realm_factory
.create_realm(&options, server)
.await?
.map_err(realm_proxy_client::Error::OperationError)?;
Ok(RealmProxyClient::from(client))
}
// Helper type for constructing `PuppetDecl`.
pub(crate) struct PuppetDeclBuilder {
name: String,
}
impl PuppetDeclBuilder {
pub fn new(name: impl Into<String>) -> Self {
Self { name: name.into() }
}
}
impl Into<ftest::PuppetDecl> for PuppetDeclBuilder {
fn into(self) -> ftest::PuppetDecl {
ftest::PuppetDecl { name: Some(self.name), ..Default::default() }
}
}
/// Connects to the puppet in the test realm with the given name.
pub async fn connect_to_puppet(
realm_proxy: &RealmProxyClient,
puppet_name: &str,
) -> Result<<ftest::PuppetMarker as ProtocolMarker>::Proxy> {
let puppet_protocol_alias = format!("{}.{puppet_name}", ftest::PuppetMarker::PROTOCOL_NAME);
realm_proxy
.connect_to_named_protocol::<ftest::PuppetMarker>(&puppet_protocol_alias)
.await
.with_context(|| format!("failed to connect to {puppet_name}"))
}
/// Creates a new topology for tests with an archivist inside.
pub async fn create(opts: Options) -> Result<(RealmBuilder, SubRealmBuilder), Error> {
let mut params = RealmBuilderParams::new();
if let Some(realm_name) = opts.realm_name {
params = params.realm_name(realm_name);
}
let builder = RealmBuilder::with_params(params).await?;
let test_realm = builder.add_child_realm("test", ChildOptions::new().eager()).await?;
let archivist = test_realm
.add_child("archivist", constants::INTEGRATION_ARCHIVIST_URL, ChildOptions::new().eager())
.await?;
// The following configurations are tweakable.
builder
.add_capability(cm_rust::CapabilityDecl::Config(cm_rust::ConfigurationDecl {
name: "fuchsia.diagnostics.EnableKlog".parse()?,
value: cm_rust::ConfigValue::Single(cm_rust::ConfigSingleValue::Bool(
opts.archivist_config.enable_klog.unwrap_or(false),
)),
}))
.await?;
if let Some(logs_max_cached_original_bytes) =
opts.archivist_config.logs_max_cached_original_bytes
{
builder
.add_capability(cm_rust::CapabilityDecl::Config(cm_rust::ConfigurationDecl {
name: "fuchsia.diagnostics.LogsMaxCachedOriginalBytes".parse()?,
value: cm_rust::ConfigValue::Single(cm_rust::ConfigSingleValue::Uint64(
logs_max_cached_original_bytes,
)),
}))
.await?;
}
builder
.add_capability(cm_rust::CapabilityDecl::Config(cm_rust::ConfigurationDecl {
name: "fuchsia.diagnostics.PipelinesPath".parse()?,
value: cm_rust::ConfigValue::Single(cm_rust::ConfigSingleValue::String(
opts.archivist_config.pipelines_path.unwrap_or("/pkg/data/config".to_string()),
)),
}))
.await?;
builder
.add_route(
Route::new()
.capability(
Capability::event_stream("capability_requested").with_scope(&test_realm),
)
.from(Ref::parent())
.to(&test_realm),
)
.await?;
test_realm
.add_route(
Route::new()
.capability(Capability::event_stream("capability_requested"))
.from(Ref::parent())
.to(&archivist),
)
.await?;
let mut self_to_archivist = Route::new()
.capability(Capability::configuration("fuchsia.diagnostics.EnableKlog"))
.capability(Capability::configuration("fuchsia.diagnostics.PipelinesPath"));
// The following config capabilities are routed from "void" to archivist since we use the
// package default config values for them.
let mut void_to_archivist = Route::new()
.capability(Capability::configuration("fuchsia.diagnostics.BindServices"))
.capability(Capability::configuration(
"fuchsia.diagnostics.MaximumConcurrentSnapshotsPerReader",
))
.capability(Capability::configuration("fuchsia.diagnostics.NumThreads"))
.capability(Capability::configuration("fuchsia.diagnostics.AllowSerialLogs"))
.capability(Capability::configuration("fuchsia.diagnostics.DenySerialLogs"))
.capability(Capability::configuration("fuchsia.diagnostics.LogToDebuglog"));
let logs_max_cached_original_bytes_config_capability =
Capability::configuration("fuchsia.diagnostics.LogsMaxCachedOriginalBytes");
if opts.archivist_config.logs_max_cached_original_bytes.is_none() {
void_to_archivist =
void_to_archivist.capability(logs_max_cached_original_bytes_config_capability);
} else {
self_to_archivist =
self_to_archivist.capability(logs_max_cached_original_bytes_config_capability);
}
builder.add_route(self_to_archivist.clone().from(Ref::self_()).to(&test_realm)).await?;
test_realm.add_route(self_to_archivist.from(Ref::parent()).to(&archivist)).await?;
test_realm.add_route(void_to_archivist.from(Ref::void()).to(&archivist)).await?;
let parent_to_archivist = Route::new()
.capability(Capability::protocol_by_name("fuchsia.boot.ReadOnlyLog"))
.capability(Capability::protocol_by_name("fuchsia.logger.LogSink"))
.capability(Capability::protocol_by_name("fuchsia.tracing.provider.Registry").optional());
builder.add_route(parent_to_archivist.clone().from(Ref::parent()).to(&test_realm)).await?;
test_realm.add_route(parent_to_archivist.from(Ref::parent()).to(&archivist)).await?;
let archivist_to_parent = Route::new()
.capability(Capability::protocol_by_name("fuchsia.diagnostics.ArchiveAccessor"))
.capability(Capability::protocol_by_name("fuchsia.diagnostics.FeedbackArchiveAccessor"))
.capability(Capability::protocol_by_name("fuchsia.diagnostics.LoWPANArchiveAccessor"))
.capability(Capability::protocol_by_name("fuchsia.diagnostics.LogSettings"))
.capability(Capability::protocol_by_name("fuchsia.logger.LogSink"))
.capability(Capability::protocol_by_name("fuchsia.inspect.InspectSink"))
.capability(Capability::protocol_by_name("fuchsia.logger.Log"));
test_realm.add_route(archivist_to_parent.clone().from(&archivist).to(Ref::parent())).await?;
builder.add_route(archivist_to_parent.from(&test_realm).to(Ref::parent())).await?;
Ok((builder, test_realm))
}
pub async fn add_eager_child(
test_realm: &SubRealmBuilder,
name: &str,
url: &str,
) -> Result<ChildRef, Error> {
let child_ref = test_realm.add_child(name, url, ChildOptions::new().eager()).await?;
test_realm
.add_route(
Route::new()
.capability(Capability::protocol_by_name("fuchsia.logger.LogSink"))
.capability(Capability::protocol_by_name("fuchsia.inspect.InspectSink"))
.from(Ref::child("archivist"))
.to(&child_ref),
)
.await?;
Ok(child_ref)
}
pub async fn add_collection(test_realm: &SubRealmBuilder, name: &str) -> Result<(), Error> {
let mut decl = test_realm.get_realm_decl().await?;
decl.collections.push(cm_rust::CollectionDecl {
name: name.parse().unwrap(),
durability: fdecl::Durability::Transient,
environment: None,
allowed_offers: cm_types::AllowedOffers::StaticOnly,
allow_long_names: false,
persistent_storage: None,
});
test_realm.replace_realm_decl(decl).await?;
test_realm
.add_route(
Route::new()
.capability(Capability::protocol_by_name("fuchsia.logger.LogSink"))
.capability(Capability::protocol_by_name("fuchsia.inspect.InspectSink"))
.from(Ref::child("archivist"))
.to(Ref::collection(name)),
)
.await?;
Ok(())
}
pub async fn expose_test_realm_protocol(builder: &RealmBuilder, test_realm: &SubRealmBuilder) {
test_realm
.add_route(
Route::new()
.capability(Capability::protocol_by_name("fuchsia.component.Realm"))
.from(Ref::framework())
.to(Ref::parent()),
)
.await
.unwrap();
builder
.add_route(
Route::new()
.capability(Capability::protocol_by_name("fuchsia.component.Realm"))
.from(Ref::child("test"))
.to(Ref::parent()),
)
.await
.unwrap();
}