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 .capability(Capability::configuration(
103 "fuchsia.http-client.TcpReceiveBufferSizeBytes",
104 ))
105 .from(Ref::void())
106 .to(&http_client),
107 )
108 .await
109 .unwrap();
110
111 let pkg_resolver = realm_builder
112 .add_child("pkg-resolver", "#meta/pkg-resolver.cm", ChildOptions::new())
113 .await
114 .unwrap();
115
116 realm_builder
117 .read_only_directory(
118 "config-data",
119 vec![&pkg_resolver],
120 DirectoryContents::new().add_file(
121 "repositories/test.json",
122 serde_json::to_string(&repo_config).unwrap(),
123 ),
124 )
125 .await
126 .unwrap();
127
128 realm_builder
129 .read_only_directory(
130 "root-ssl-certificates",
131 vec![&http_client],
132 DirectoryContents::new().add_file(SSL_CERT_FILE_NAME, cert_bytes),
133 )
134 .await
135 .unwrap();
136
137 realm_builder
138 .add_route(
139 Route::new()
140 .capability(Capability::protocol_by_name("fuchsia.net.name.Lookup"))
141 .capability(Capability::protocol_by_name("fuchsia.posix.socket.Provider"))
142 .from(Ref::parent())
143 .to(&http_client),
144 )
145 .await
146 .unwrap();
147
148 realm_builder
149 .add_route(
150 Route::new()
151 .capability(Capability::protocol_by_name("fuchsia.logger.LogSink"))
152 .from(Ref::parent())
153 .to(&pkg_resolver)
154 .to(&http_client),
155 )
156 .await
157 .unwrap();
158
159 realm_builder
160 .add_route(
161 Route::new()
162 .capability(Capability::protocol_by_name("fuchsia.pkg.PackageResolver-ota"))
163 .from(&pkg_resolver)
164 .to(Ref::parent()),
165 )
166 .await
167 .unwrap();
168
169 realm_builder
170 .add_route(
171 Route::new()
172 .capability(Capability::protocol_by_name("fuchsia.pkg.http.Client"))
173 .capability(Capability::protocol_by_name("fuchsia.net.http.Loader"))
174 .from(&http_client)
175 .to(&pkg_resolver),
176 )
177 .await
178 .unwrap();
179
180 realm_builder
181 .add_route(
182 Route::new()
183 .capability(Capability::protocol_by_name("fuchsia.pkg.PackageCache"))
184 .capability(Capability::protocol_by_name(
185 "fuchsia.pkg.garbagecollector.Manager",
186 ))
187 .from(&cache_ref)
188 .to(&pkg_resolver),
189 )
190 .await
191 .unwrap();
192
193 realm_builder
194 .add_route(
195 Route::new()
196 .capability(
197 Capability::protocol::<fmetrics::MetricEventLoggerFactoryMarker>(),
198 )
199 .from(&service_reflector)
200 .to(&pkg_resolver),
201 )
202 .await
203 .unwrap();
204
205 realm_builder
206 .add_route(
207 Route::new()
208 .capability(Capability::configuration(
209 "fuchsia.pkgresolver.TufNetworkHeaderTimeoutSeconds",
210 ))
211 .capability(Capability::configuration(
212 "fuchsia.pkgresolver.BlobNetworkHeaderTimeoutSeconds",
213 ))
214 .capability(Capability::configuration(
215 "fuchsia.pkgresolver.BlobNetworkBodyTimeoutSeconds",
216 ))
217 .from(Ref::void())
218 .to(&pkg_resolver),
219 )
220 .await
221 .unwrap();
222
223 Ok(ResolverRealm { resolver: pkg_resolver, cache: cache_ref })
224 }
225
226 pub async fn new(
227 realm_instance: &RealmInstance,
228 blobfs: BlobfsRamdisk,
229 served_repo: Arc<ServedRepository>,
230 ) -> Result<Self, Error> {
231 let cache = CacheForTest::new(realm_instance, blobfs).await.unwrap();
232
233 let resolver_proxy = realm_instance
234 .root
235 .connect_to_named_protocol_at_exposed_dir::<fpkg::PackageResolverMarker>(
236 "fuchsia.pkg.PackageResolver-ota",
237 )
238 .expect("connect to pkg resolver");
239
240 let resolver = Resolver::new_with_proxy(resolver_proxy).unwrap();
241
242 let cache_proxy = cache.cache.package_cache_proxy().unwrap();
243
244 assert_eq!(cache_proxy.sync().await.unwrap(), Ok(()));
245
246 Ok(ResolverForTest { cache, resolver: Arc::new(resolver), _served_repo: served_repo })
247 }
248
249 pub async fn resolve_package(
252 &self,
253 url: &str,
254 ) -> Result<(fio::DirectoryProxy, pkg::ResolutionContext), Error> {
255 let proxy = &self.resolver.proxy;
256 let (package, package_remote) = fidl::endpoints::create_proxy();
257 let resolved_context = proxy
258 .resolve(url, package_remote)
259 .await
260 .unwrap()
261 .map_err(|e| anyhow!("Package resolver error: {:?}", e))?;
262 Ok((package, (&resolved_context).try_into().expect("resolver returns valid context")))
263 }
264 }
265}
266
267#[cfg(test)]
268pub mod tests {
269 use super::for_tests::{EMPTY_REPO_PATH, ResolverForTest};
270 use anyhow::{Context, Error};
271 use fuchsia_component_test::RealmBuilder;
272 use fuchsia_pkg_testing::{PackageBuilder, RepositoryBuilder};
273 use std::sync::Arc;
274
275 const TEST_REPO_URL: &str = "fuchsia-pkg://test";
276
277 #[fuchsia::test]
278 pub async fn test_resolver() -> Result<(), Error> {
279 let name = "test-resolver";
280 let package = PackageBuilder::new(name)
281 .add_resource_at("data/file1", "hello".as_bytes())
282 .add_resource_at("data/file2", "hello two".as_bytes())
283 .build()
284 .await
285 .unwrap();
286 let repo = Arc::new(
287 RepositoryBuilder::from_template_dir(EMPTY_REPO_PATH)
288 .add_package(&package)
289 .build()
290 .await
291 .context("Building repo")
292 .unwrap(),
293 );
294
295 let realm_builder = RealmBuilder::new().await.unwrap();
296 let served_repo = Arc::new(Arc::clone(&repo).server().start().unwrap());
297 let blobfs =
298 blobfs_ramdisk::BlobfsRamdisk::start().await.context("starting blobfs").unwrap();
299 let _resolver_realm = ResolverForTest::realm_setup(
300 &realm_builder,
301 Arc::clone(&served_repo),
302 TEST_REPO_URL.parse().unwrap(),
303 &blobfs,
304 )
305 .await
306 .unwrap();
307
308 let realm_instance = realm_builder.build().await.unwrap();
309
310 let resolver = ResolverForTest::new(&realm_instance, blobfs, served_repo)
311 .await
312 .context("launching resolver")?;
313 let (root_dir, _resolved_context) =
314 resolver.resolve_package(&format!("{TEST_REPO_URL}/{name}")).await.unwrap();
315
316 package.verify_contents(&root_dir).await.unwrap();
317 Ok(())
318 }
319}