1#[cfg(test)]
6pub(crate) mod for_tests {
7 use crate::cache::for_tests::CacheForTest;
8 use anyhow::{Error, anyhow};
9 use blobfs_ramdisk::BlobfsRamdisk;
10 use fidl_fuchsia_pkg_ext::RepositoryConfigs;
11 use fuchsia_component_test::{
12 Capability, ChildOptions, ChildRef, DirectoryContents, RealmBuilder, RealmInstance, Ref,
13 Route,
14 };
15 use fuchsia_pkg_testing::serve::ServedRepository;
16 use fuchsia_url::RepositoryUrl;
17 use futures::{FutureExt as _, StreamExt as _};
18 use std::sync::Arc;
19 use {
20 fidl_fuchsia_io as fio, fidl_fuchsia_metrics as fmetrics, fidl_fuchsia_pkg as fpkg,
21 fidl_fuchsia_pkg_ext as pkg, fuchsia_async as fasync,
22 };
23
24 const SSL_TEST_CERTS_PATH: &str = "/pkg/data/ssl/cert.pem";
25 const SSL_CERT_FILE_NAME: &str = "cert.pem";
26 pub const EMPTY_REPO_PATH: &str = "/pkg/empty-repo";
27
28 pub struct Resolver {
30 proxy: fpkg::PackageResolverProxy,
31 }
32
33 impl Resolver {
34 fn new_with_proxy(pkg_resolver_proxy: fpkg::PackageResolverProxy) -> Result<Self, Error> {
35 Ok(Self { proxy: pkg_resolver_proxy })
36 }
37 }
38
39 pub struct ResolverForTest {
41 pub cache: CacheForTest,
42 pub resolver: Arc<Resolver>,
43 _served_repo: Arc<ServedRepository>,
44 }
45
46 pub struct ResolverRealm {
47 pub resolver: ChildRef,
48 pub cache: ChildRef,
49 }
50
51 impl ResolverForTest {
52 pub async fn realm_setup(
53 realm_builder: &RealmBuilder,
54 served_repo: Arc<ServedRepository>,
55 repo_url: RepositoryUrl,
56 blobfs: &BlobfsRamdisk,
57 ) -> Result<ResolverRealm, Error> {
58 let cache_ref =
59 CacheForTest::realm_setup(realm_builder, blobfs).await.expect("setting up cache");
60
61 let repo_config =
62 RepositoryConfigs::Version1(vec![served_repo.make_repo_config(repo_url)]);
63
64 let cert_bytes = std::fs::read(std::path::Path::new(SSL_TEST_CERTS_PATH)).unwrap();
65
66 let service_reflector = realm_builder
67 .add_local_child(
68 "pkg_resolver_service_reflector",
69 move |handles| {
70 let mut fs = fuchsia_component::server::ServiceFs::new();
71 fs.dir("svc").add_fidl_service(move |stream| {
73 fasync::Task::spawn(
74 Arc::new(mock_metrics::MockMetricEventLoggerFactory::new())
75 .run_logger_factory(stream),
76 )
77 .detach()
78 });
79 async move {
80 fs.serve_connection(handles.outgoing_dir).unwrap();
81 let () = fs.collect().await;
82 Ok(())
83 }
84 .boxed()
85 },
86 ChildOptions::new(),
87 )
88 .await
89 .unwrap();
90
91 let http_client = realm_builder
92 .add_child("http-client", "#meta/http-client.cm", ChildOptions::new())
93 .await
94 .unwrap();
95 realm_builder
96 .add_route(
97 Route::new()
98 .capability(Capability::configuration(
99 "fuchsia.http-client.StopOnIdleTimeoutMillis",
100 ))
101 .from(Ref::void())
102 .to(&http_client),
103 )
104 .await
105 .unwrap();
106
107 let pkg_resolver = realm_builder
108 .add_child("pkg-resolver", "#meta/pkg-resolver.cm", ChildOptions::new())
109 .await
110 .unwrap();
111
112 realm_builder
113 .read_only_directory(
114 "config-data",
115 vec![&pkg_resolver],
116 DirectoryContents::new().add_file(
117 "repositories/test.json",
118 serde_json::to_string(&repo_config).unwrap(),
119 ),
120 )
121 .await
122 .unwrap();
123
124 realm_builder
125 .read_only_directory(
126 "root-ssl-certificates",
127 vec![&pkg_resolver, &http_client],
128 DirectoryContents::new().add_file(SSL_CERT_FILE_NAME, cert_bytes),
129 )
130 .await
131 .unwrap();
132
133 realm_builder
134 .add_route(
135 Route::new()
136 .capability(Capability::protocol_by_name("fuchsia.logger.LogSink"))
137 .capability(Capability::protocol_by_name("fuchsia.net.name.Lookup"))
138 .capability(Capability::protocol_by_name("fuchsia.posix.socket.Provider"))
139 .from(Ref::parent())
140 .to(&pkg_resolver)
141 .to(&http_client),
142 )
143 .await
144 .unwrap();
145
146 realm_builder
147 .add_route(
148 Route::new()
149 .capability(Capability::protocol_by_name("fuchsia.pkg.PackageResolver-ota"))
150 .from(&pkg_resolver)
151 .to(Ref::parent()),
152 )
153 .await
154 .unwrap();
155
156 realm_builder
157 .add_route(
158 Route::new()
159 .capability(Capability::protocol_by_name("fuchsia.pkg.http.Client"))
160 .from(&http_client)
161 .to(&pkg_resolver),
162 )
163 .await
164 .unwrap();
165
166 realm_builder
167 .add_route(
168 Route::new()
169 .capability(Capability::protocol_by_name("fuchsia.pkg.PackageCache"))
170 .capability(Capability::protocol_by_name(
171 "fuchsia.pkg.garbagecollector.Manager",
172 ))
173 .from(&cache_ref)
174 .to(&pkg_resolver),
175 )
176 .await
177 .unwrap();
178
179 realm_builder
180 .add_route(
181 Route::new()
182 .capability(
183 Capability::protocol::<fmetrics::MetricEventLoggerFactoryMarker>(),
184 )
185 .from(&service_reflector)
186 .to(&pkg_resolver),
187 )
188 .await
189 .unwrap();
190
191 realm_builder
192 .add_route(
193 Route::new()
194 .capability(Capability::configuration(
195 "fuchsia.pkgresolver.BlobNetworkHeaderTimeoutSeconds",
196 ))
197 .capability(Capability::configuration(
198 "fuchsia.pkgresolver.BlobNetworkBodyTimeoutSeconds",
199 ))
200 .from(Ref::void())
201 .to(&pkg_resolver),
202 )
203 .await
204 .unwrap();
205
206 Ok(ResolverRealm { resolver: pkg_resolver, cache: cache_ref })
207 }
208
209 pub async fn new(
210 realm_instance: &RealmInstance,
211 blobfs: BlobfsRamdisk,
212 served_repo: Arc<ServedRepository>,
213 ) -> Result<Self, Error> {
214 let cache = CacheForTest::new(realm_instance, blobfs).await.unwrap();
215
216 let resolver_proxy = realm_instance
217 .root
218 .connect_to_named_protocol_at_exposed_dir::<fpkg::PackageResolverMarker>(
219 "fuchsia.pkg.PackageResolver-ota",
220 )
221 .expect("connect to pkg resolver");
222
223 let resolver = Resolver::new_with_proxy(resolver_proxy).unwrap();
224
225 let cache_proxy = cache.cache.package_cache_proxy().unwrap();
226
227 assert_eq!(cache_proxy.sync().await.unwrap(), Ok(()));
228
229 Ok(ResolverForTest { cache, resolver: Arc::new(resolver), _served_repo: served_repo })
230 }
231
232 pub async fn resolve_package(
235 &self,
236 url: &str,
237 ) -> Result<(fio::DirectoryProxy, pkg::ResolutionContext), Error> {
238 let proxy = &self.resolver.proxy;
239 let (package, package_remote) = fidl::endpoints::create_proxy();
240 let resolved_context = proxy
241 .resolve(url, package_remote)
242 .await
243 .unwrap()
244 .map_err(|e| anyhow!("Package resolver error: {:?}", e))?;
245 Ok((package, (&resolved_context).try_into().expect("resolver returns valid context")))
246 }
247 }
248}
249
250#[cfg(test)]
251pub mod tests {
252 use super::for_tests::{EMPTY_REPO_PATH, ResolverForTest};
253 use anyhow::{Context, Error};
254 use fuchsia_async as fasync;
255 use fuchsia_component_test::RealmBuilder;
256 use fuchsia_pkg_testing::{PackageBuilder, RepositoryBuilder};
257 use std::sync::Arc;
258
259 const TEST_REPO_URL: &str = "fuchsia-pkg://test";
260
261 #[fasync::run_singlethreaded(test)]
262 pub async fn test_resolver() -> Result<(), Error> {
263 let name = "test-resolver";
264 let package = PackageBuilder::new(name)
265 .add_resource_at("data/file1", "hello".as_bytes())
266 .add_resource_at("data/file2", "hello two".as_bytes())
267 .build()
268 .await
269 .unwrap();
270 let repo = Arc::new(
271 RepositoryBuilder::from_template_dir(EMPTY_REPO_PATH)
272 .add_package(&package)
273 .build()
274 .await
275 .context("Building repo")
276 .unwrap(),
277 );
278
279 let realm_builder = RealmBuilder::new().await.unwrap();
280 let served_repo = Arc::new(Arc::clone(&repo).server().start().unwrap());
281 let blobfs =
282 blobfs_ramdisk::BlobfsRamdisk::start().await.context("starting blobfs").unwrap();
283 let _resolver_realm = ResolverForTest::realm_setup(
284 &realm_builder,
285 Arc::clone(&served_repo),
286 TEST_REPO_URL.parse().unwrap(),
287 &blobfs,
288 )
289 .await
290 .unwrap();
291
292 let realm_instance = realm_builder.build().await.unwrap();
293
294 let resolver = ResolverForTest::new(&realm_instance, blobfs, served_repo)
295 .await
296 .context("launching resolver")?;
297 let (root_dir, _resolved_context) =
298 resolver.resolve_package(&format!("{TEST_REPO_URL}/{name}")).await.unwrap();
299
300 package.verify_contents(&root_dir).await.unwrap();
301 Ok(())
302 }
303}