test_manager_lib/
above_root_capabilities.rs1use crate::constants::{
6 HERMETIC_RESOLVER_REALM_NAME, TEST_ROOT_COLLECTION, TEST_TYPE_REALM_MAP, WRAPPER_REALM_NAME,
7};
8use anyhow::{format_err, Error};
9use fuchsia_component_test::error::Error as RealmBuilderError;
10use fuchsia_component_test::{Capability, RealmBuilder, Ref, RefContext, Route, SubRealmBuilder};
11use std::collections::HashMap;
12use {fidl_fuchsia_component_decl as fdecl, fidl_fuchsia_component_test as ftest};
13
14#[derive(Default)]
15struct CollectionData {
16 capabilities: Vec<ftest::Capability>,
17 required_event_streams: RequiredEventStreams,
18}
19
20#[derive(Default)]
21struct RequiredEventStreams {
22 capability_requested: bool,
23}
24
25impl RequiredEventStreams {
26 fn validate(&self) -> bool {
27 self.capability_requested
28 }
29}
30
31pub struct AboveRootCapabilitiesForTest {
32 collection_data: HashMap<&'static str, CollectionData>,
33}
34
35impl AboveRootCapabilitiesForTest {
36 pub async fn new(manifest_name: &str) -> Result<Self, Error> {
37 let path = format!("/pkg/meta/{}", manifest_name);
38 let file_proxy = fuchsia_fs::file::open_in_namespace(&path, fuchsia_fs::PERM_READABLE)?;
39 let component_decl = fuchsia_fs::file::read_fidl::<fdecl::Component>(&file_proxy).await?;
40 let collection_data = Self::load(component_decl);
41 Ok(Self { collection_data })
42 }
43
44 #[cfg(test)]
45 pub fn new_empty_for_tests() -> Self {
46 let empty_collection_data = HashMap::new();
47 Self { collection_data: empty_collection_data }
48 }
49
50 pub fn validate(&self, collection: &str) -> Result<(), Error> {
51 match self.collection_data.get(collection) {
52 Some(c) if !c.required_event_streams.validate() => {
53 return Err(format_err!(
54 "The collection `{collection}` must be routed the events \
55 `capability_requested` from `parent` scoped \
56 to it"
57 ));
58 }
59 _ => Ok(()),
60 }
61 }
62
63 pub async fn apply(
64 &self,
65 collection: &str,
66 builder: &RealmBuilder,
67 wrapper_realm: &SubRealmBuilder,
68 ) -> Result<(), RealmBuilderError> {
69 if !self.collection_data.contains_key(collection) {
70 return Ok(());
71 }
72 for capability in &self.collection_data[collection].capabilities {
73 let (capability_for_test_wrapper, capability_for_test_root) =
74 if let ftest::Capability::EventStream(event_stream) = &capability {
75 let mut test_wrapper_event_stream = event_stream.clone();
76 let (wrapper_realm, _) =
77 Ref::child(WRAPPER_REALM_NAME).into_fidl(RefContext::Source);
78 let (hermetic_resolver_realm, _) =
79 Ref::child(HERMETIC_RESOLVER_REALM_NAME).into_fidl(RefContext::Source);
80 let (test_root_collection, _) =
81 Ref::collection(TEST_ROOT_COLLECTION).into_fidl(RefContext::Source);
82 test_wrapper_event_stream.scope = Some(vec![wrapper_realm]);
83 let mut test_root_event_stream = event_stream.clone();
84 test_root_event_stream.scope =
85 Some(vec![test_root_collection, hermetic_resolver_realm]);
86 (
87 ftest::Capability::EventStream(test_wrapper_event_stream),
88 ftest::Capability::EventStream(test_root_event_stream),
89 )
90 } else {
91 (capability.clone(), capability.clone())
92 };
93 builder
94 .add_route(
95 Route::new()
96 .capability(capability_for_test_wrapper.clone())
97 .from(Ref::parent())
98 .to(wrapper_realm),
99 )
100 .await?;
101 wrapper_realm
102 .add_route(
103 Route::new()
104 .capability(capability_for_test_root.clone())
105 .from(Ref::parent())
106 .to(Ref::collection(TEST_ROOT_COLLECTION)),
107 )
108 .await?;
109 }
110 Ok(())
111 }
112
113 fn load(decl: fdecl::Component) -> HashMap<&'static str, CollectionData> {
114 let mut collection_data: HashMap<_, _> =
115 TEST_TYPE_REALM_MAP.values().map(|v| (*v, CollectionData::default())).collect();
116 for offer_decl in decl.offers.unwrap_or(vec![]) {
117 match offer_decl {
118 fdecl::Offer::Protocol(fdecl::OfferProtocol {
119 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name })),
120 target_name: Some(target_name),
121 ..
122 }) if collection_data.contains_key(name.as_str())
123 && target_name != "fuchsia.logger.LogSink"
124 && target_name != "fuchsia.inspect.InspectSink" =>
125 {
126 collection_data.get_mut(name.as_str()).unwrap().capabilities.push(
127 Capability::protocol_by_name(target_name)
128 .availability_same_as_target()
129 .into(),
130 );
131 }
132 fdecl::Offer::Directory(fdecl::OfferDirectory {
133 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name })),
134 target_name: Some(target_name),
135 ..
136 }) if collection_data.contains_key(name.as_str()) => {
137 collection_data.get_mut(name.as_str()).unwrap().capabilities.push(
138 Capability::directory(target_name).availability_same_as_target().into(),
139 );
140 }
141 fdecl::Offer::Storage(fdecl::OfferStorage {
142 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name })),
143 target_name: Some(target_name),
144 ..
145 }) if collection_data.contains_key(name.as_str()) => {
146 let use_path = format!("/{}", target_name);
147 collection_data.get_mut(name.as_str()).unwrap().capabilities.push(
148 Capability::storage(target_name)
149 .path(use_path)
150 .availability_same_as_target()
151 .into(),
152 );
153 }
154 fdecl::Offer::EventStream(fdecl::OfferEventStream {
155 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name })),
156 target_name: Some(target_name),
157 source: Some(source),
158 scope,
159 ..
160 }) if collection_data.contains_key(name.as_str()) => {
161 collection_data
162 .get_mut(name.as_str())
163 .unwrap()
164 .capabilities
165 .push(Capability::event_stream(target_name.clone()).into());
166
167 let coll_ref =
170 fdecl::Ref::Collection(fdecl::CollectionRef { name: name.clone() });
171 if target_name == "capability_requested"
172 && matches!(source, fdecl::Ref::Parent(_))
173 && scope.map(|s| s.contains(&coll_ref)).unwrap_or(false)
174 {
175 let entry = collection_data.get_mut(name.as_str()).unwrap();
176 entry.required_event_streams.capability_requested =
177 entry.required_event_streams.capability_requested
178 || target_name == "capability_requested";
179 }
180 }
181 fdecl::Offer::Service(fdecl::OfferService {
182 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name })),
183 target_name: Some(target_name),
184 ..
185 }) if collection_data.contains_key(name.as_str()) => {
186 collection_data.get_mut(name.as_str()).unwrap().capabilities.push(
187 Capability::service_by_name(target_name)
188 .availability_same_as_target()
189 .into(),
190 );
191 }
192 fdecl::Offer::Runner(fdecl::OfferRunner {
193 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name })),
194 ..
195 })
196 | fdecl::Offer::Resolver(fdecl::OfferResolver {
197 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef { name })),
198 ..
199 }) if collection_data.contains_key(name.as_str()) => {
200 unimplemented!("Runners and resolvers are not supported by realm builder");
201 }
202 _ => {
203 }
205 }
206 }
207 collection_data
208 }
209}