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