1// Copyright 2020 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.
45use anyhow::{Context, Error};
6use diagnostics_reader::{ArchiveReader, ComponentSelector, DiagnosticsHierarchy};
7use fidl_fuchsia_bluetooth_sys::{AccessMarker, AccessProxy};
8use fuchsia_async::DurationExt;
9use fuchsia_bluetooth::expectation::asynchronous::{
10 expectable, Expectable, ExpectableExt, ExpectableState, ExpectableStateExt,
11};
12use fuchsia_bluetooth::expectation::Predicate;
13use futures::future::BoxFuture;
14use futures::FutureExt;
15use std::ops::{Deref, DerefMut};
16use std::sync::Arc;
17use test_harness::{SharedState, TestHarness, SHARED_STATE_TEST_COMPONENT_INDEX};
18use zx::MonotonicDuration;
1920use crate::core_realm::{CoreRealm, SHARED_STATE_INDEX};
21use crate::host_watcher::ActivatedFakeHost;
22use crate::timeout_duration;
2324// Controls the rate at which to snapshot the inspect tree (i.e. update InspectState). Arbitrarily
25// set to snapshot the inspect tree every 1 second.
26const SNAPSHOT_INSPECT_EVERY_N_SECONDS: MonotonicDuration = MonotonicDuration::from_seconds(1);
2728#[derive(Clone)]
29pub struct InspectState {
30/// The moniker of the component whose inspect this tracks. Should be relative to the root realm
31 /// component, and each component of the moniker should be separate.
32 /// Example: Let's say we have Component A with name "component-a", and Component A has a child
33 /// with name "component-b". If we add component A to the RealmBuilder, and we want to monitor
34 /// the Inspect state for "component-b", we would set this value to
35 /// `vec!["component-a", "component-b"]`.
36// Note that this is not the final moniker used as a component selector; we also have to prepend
37 // the realm child's moniker (which is based on the realm_child_name member).
38pub moniker_to_track: Vec<String>,
39/// The Diagnostic Hierarchies of the monitored component (if any)
40pub hierarchies: Vec<DiagnosticsHierarchy>,
41 realm_child_name: String,
42}
4344#[derive(Clone)]
45pub struct InspectHarness(Expectable<InspectState, AccessProxy>);
4647impl InspectHarness {
48// Check if there are at least `min_num` hierarchies in our Inspect State. If so, return the
49 // inspect state, otherwise return Error.
50pub async fn expect_n_hierarchies(&self, min_num: usize) -> Result<InspectState, Error> {
51self.when_satisfied(
52 Predicate::<InspectState>::predicate(
53move |state| state.hierarchies.len() >= min_num,
54"Expected number of hierarchies received",
55 ),
56 timeout_duration(),
57 )
58 .await
59}
6061fn get_component_selector(&self) -> ComponentSelector {
62let realm_child_moniker = format!("realm_builder\\:{}", self.read().realm_child_name);
63let mut complete_moniker = self.read().moniker_to_track;
64 complete_moniker.insert(0, realm_child_moniker);
65return ComponentSelector::new(complete_moniker);
66 }
67}
6869impl Deref for InspectHarness {
70type Target = Expectable<InspectState, AccessProxy>;
7172fn deref(&self) -> &Self::Target {
73&self.0
74}
75}
7677impl DerefMut for InspectHarness {
78fn deref_mut(&mut self) -> &mut Self::Target {
79&mut self.0
80}
81}
8283pub async fn handle_inspect_updates(harness: InspectHarness) -> Result<(), Error> {
84loop {
85if harness.read().moniker_to_track.len() > 0 {
86let mut reader = ArchiveReader::inspect();
87let _ = reader.add_selector(harness.get_component_selector());
88 harness.write_state().hierarchies =
89 reader.snapshot().await?.into_iter().flat_map(|result| result.payload).collect();
90 harness.notify_state_changed();
91 }
92 fuchsia_async::Timer::new(SNAPSHOT_INSPECT_EVERY_N_SECONDS.after_now()).await;
93 }
94}
9596impl TestHarness for InspectHarness {
97type Env = (ActivatedFakeHost, Arc<CoreRealm>);
98type Runner = BoxFuture<'static, Result<(), Error>>;
99100fn init(
101 shared_state: &Arc<SharedState>,
102 ) -> BoxFuture<'static, Result<(Self, Self::Env, Self::Runner), Error>> {
103let shared_state = shared_state.clone();
104async move {
105let test_component: Arc<String> = shared_state
106 .get(SHARED_STATE_TEST_COMPONENT_INDEX)
107 .expect("SharedState must have TEST-COMPONENT")?;
108let inserter = move || CoreRealm::create(test_component.to_string());
109let realm = shared_state.get_or_insert_with(SHARED_STATE_INDEX, inserter).await?;
110// Publish emulator to driver stack
111let fake_host = ActivatedFakeHost::new(realm.clone()).await?;
112113let access_proxy = realm
114 .instance()
115 .connect_to_protocol_at_exposed_dir::<AccessMarker>()
116 .context("Failed to connect to Access service")?;
117let state = InspectState {
118 moniker_to_track: Vec::new(),
119 hierarchies: Vec::new(),
120 realm_child_name: realm.instance().child_name().to_string(),
121 };
122123let harness = InspectHarness(expectable(state, access_proxy));
124let run_inspect = handle_inspect_updates(harness.clone()).boxed();
125Ok((harness, (fake_host, realm), run_inspect))
126 }
127 .boxed()
128 }
129130fn terminate((emulator, realm): Self::Env) -> BoxFuture<'static, Result<(), Error>> {
131// The realm must be kept alive in order for ActivatedFakeHost::release to work properly.
132async move {
133let _realm = realm;
134 emulator.release().await
135}
136 .boxed()
137 }
138}