1use crate::{
6 CheckUse, ComponentEventRoute, ExpectedResult, RoutingTestModel, RoutingTestModelBuilder,
7 ServiceInstance,
8};
9use cm_rust::offer::*;
10use cm_rust::*;
11use cm_rust_testing::*;
12use fidl_fuchsia_io as fio;
13use moniker::Moniker;
14use std::marker::PhantomData;
15use std::path::{Path, PathBuf};
16use zx_status;
17
18pub struct CommonAvailabilityTest<T: RoutingTestModelBuilder> {
19 builder: PhantomData<T>,
20}
21
22#[derive(Debug)]
23struct TestCase {
24 provider_availability: Availability,
26 use_availability: Availability,
27}
28
29impl<T: RoutingTestModelBuilder> CommonAvailabilityTest<T> {
30 pub fn new() -> Self {
31 Self { builder: PhantomData }
32 }
33
34 const VALID_AVAILABILITY_PAIRS: &'static [TestCase] = &[
35 TestCase {
36 provider_availability: Availability::Required,
37 use_availability: Availability::Required,
38 },
39 TestCase {
40 provider_availability: Availability::Optional,
41 use_availability: Availability::Optional,
42 },
43 TestCase {
44 provider_availability: Availability::Required,
45 use_availability: Availability::Optional,
46 },
47 TestCase {
48 provider_availability: Availability::SameAsTarget,
49 use_availability: Availability::Required,
50 },
51 TestCase {
52 provider_availability: Availability::SameAsTarget,
53 use_availability: Availability::Optional,
54 },
55 TestCase {
56 provider_availability: Availability::Required,
57 use_availability: Availability::Transitional,
58 },
59 TestCase {
60 provider_availability: Availability::Optional,
61 use_availability: Availability::Transitional,
62 },
63 TestCase {
64 provider_availability: Availability::Transitional,
65 use_availability: Availability::Transitional,
66 },
67 TestCase {
68 provider_availability: Availability::SameAsTarget,
69 use_availability: Availability::Transitional,
70 },
71 ];
72
73 pub async fn test_offer_availability_successful_routes(&self) {
74 for test_case in Self::VALID_AVAILABILITY_PAIRS {
75 let components = vec![
76 (
77 "a",
78 ComponentDeclBuilder::new()
79 .offer(
80 OfferBuilder::service()
81 .name("fuchsia.examples.EchoService")
82 .source_static_child("b")
83 .target_static_child("c")
84 .availability(test_case.provider_availability),
85 )
86 .offer(
87 OfferBuilder::protocol()
88 .name("fuchsia.examples.Echo")
89 .source_static_child("b")
90 .target_static_child("c")
91 .availability(test_case.provider_availability),
92 )
93 .offer(
94 OfferBuilder::directory()
95 .name("dir")
96 .source_static_child("b")
97 .target_static_child("c")
98 .rights(fio::R_STAR_DIR)
99 .availability(test_case.provider_availability),
100 )
101 .capability(
102 CapabilityBuilder::directory()
103 .name("data")
104 .path("/data")
105 .rights(fio::RW_STAR_DIR),
106 )
107 .capability(
108 CapabilityBuilder::storage()
109 .name("cache")
110 .backing_dir("data")
111 .source(StorageDirectorySource::Self_)
112 .subdir("cache"),
113 )
114 .offer(
115 OfferBuilder::storage()
116 .name("cache")
117 .source(OfferSource::Self_)
118 .target_static_child("c")
119 .availability(test_case.provider_availability),
120 )
121 .offer(
122 OfferBuilder::event_stream()
123 .name("started")
124 .source(OfferSource::Parent)
125 .target_static_child("c")
126 .availability(test_case.provider_availability),
127 )
128 .child_default("b")
129 .child_default("c")
130 .build(),
131 ),
132 (
133 "b",
134 ComponentDeclBuilder::new()
135 .capability(
136 CapabilityBuilder::service()
137 .name("fuchsia.examples.EchoService")
138 .path("/svc/foo.service"),
139 )
140 .expose(
141 ExposeBuilder::service()
142 .name("fuchsia.examples.EchoService")
143 .source(ExposeSource::Self_),
144 )
145 .capability(
146 CapabilityBuilder::protocol()
147 .name("fuchsia.examples.Echo")
148 .path("/svc/foo"),
149 )
150 .expose(
151 ExposeBuilder::protocol()
152 .name("fuchsia.examples.Echo")
153 .source(ExposeSource::Self_),
154 )
155 .capability(CapabilityBuilder::directory().name("dir").path("/data/dir"))
156 .expose(ExposeBuilder::directory().name("dir").source(ExposeSource::Self_))
157 .build(),
158 ),
159 (
160 "c",
161 ComponentDeclBuilder::new()
162 .use_(
163 UseBuilder::service()
164 .name("fuchsia.examples.EchoService")
165 .availability(test_case.use_availability),
166 )
167 .use_(
168 UseBuilder::protocol()
169 .name("fuchsia.examples.Echo")
170 .availability(test_case.use_availability),
171 )
172 .use_(
173 UseBuilder::directory()
174 .name("dir")
175 .path("/dir")
176 .availability(test_case.use_availability),
177 )
178 .use_(
179 UseBuilder::storage()
180 .name("cache")
181 .path("/storage")
182 .availability(test_case.use_availability),
183 )
184 .use_(
185 UseBuilder::event_stream()
186 .name("started")
187 .path("/event/stream")
188 .availability(test_case.use_availability),
189 )
190 .build(),
191 ),
192 ];
193 let mut builder = T::new("a", components);
194 builder.set_builtin_capabilities(vec![CapabilityDecl::EventStream(EventStreamDecl {
195 name: "started".parse().unwrap(),
196 })]);
197 let model = builder.build().await;
198 model
199 .create_static_file(Path::new("dir/hippo"), "hello")
200 .await
201 .expect("failed to create file");
202 for check_use in vec![
203 CheckUse::Service {
204 path: "/svc/fuchsia.examples.EchoService".parse().unwrap(),
205 instance: ServiceInstance::Named("default".to_owned()),
206 member: "echo".to_owned(),
207 expected_res: ExpectedResult::Ok,
208 },
209 CheckUse::Protocol {
210 path: "/svc/fuchsia.examples.Echo".parse().unwrap(),
211 expected_res: ExpectedResult::Ok,
212 },
213 CheckUse::Directory {
214 path: "/dir".parse().unwrap(),
215 file: PathBuf::from("hippo"),
216 expected_res: ExpectedResult::Ok,
217 },
218 CheckUse::Storage {
219 path: "/storage".parse().unwrap(),
220 storage_relation: Some(Moniker::try_from(["c"]).unwrap()),
221 from_cm_namespace: false,
222 storage_subdir: Some("cache".to_string()),
223 expected_res: ExpectedResult::Ok,
224 },
225 CheckUse::EventStream {
226 expected_res: ExpectedResult::Ok,
227 path: "/event/stream".parse().unwrap(),
228 scope: Some(ComponentEventRoute { component: ".".to_string(), scope: None }),
229 name: "started".parse().unwrap(),
230 },
231 ] {
232 model.check_use(["c"].try_into().unwrap(), check_use).await;
233 }
234 }
235 }
236
237 pub async fn test_offer_availability_invalid_routes(&self) {
238 struct TestCase {
239 source: OfferSource,
240 storage_source: Option<OfferSource>,
241 offer_availability: Availability,
242 use_availability: Availability,
243 }
244 for test_case in &[
245 TestCase {
246 source: offer_source_static_child("b"),
247 storage_source: Some(OfferSource::Self_),
248 offer_availability: Availability::Optional,
249 use_availability: Availability::Required,
250 },
251 TestCase {
252 source: OfferSource::Void,
253 storage_source: None,
254 offer_availability: Availability::Optional,
255 use_availability: Availability::Required,
256 },
257 TestCase {
258 source: OfferSource::Void,
259 storage_source: None,
260 offer_availability: Availability::Optional,
261 use_availability: Availability::Optional,
262 },
263 TestCase {
264 source: OfferSource::Void,
265 storage_source: None,
266 offer_availability: Availability::Transitional,
267 use_availability: Availability::Optional,
268 },
269 TestCase {
270 source: OfferSource::Void,
271 storage_source: None,
272 offer_availability: Availability::Transitional,
273 use_availability: Availability::Required,
274 },
275 ] {
276 let components = vec![
277 (
278 "a",
279 ComponentDeclBuilder::new()
280 .offer(
281 OfferBuilder::service()
282 .name("fuchsia.examples.EchoService")
283 .source(test_case.source.clone())
284 .target_static_child("c")
285 .availability(test_case.offer_availability),
286 )
287 .offer(
288 OfferBuilder::protocol()
289 .name("fuchsia.examples.Echo")
290 .source(test_case.source.clone())
291 .target_static_child("c")
292 .availability(test_case.offer_availability),
293 )
294 .offer(
295 OfferBuilder::directory()
296 .name("dir")
297 .source(test_case.source.clone())
298 .target_static_child("c")
299 .rights(fio::Operations::CONNECT)
300 .availability(test_case.offer_availability),
301 )
302 .offer(
303 OfferBuilder::storage()
304 .name("data")
305 .source(
306 test_case
307 .storage_source
308 .as_ref()
309 .map(Clone::clone)
310 .unwrap_or_else(|| test_case.source.clone()),
311 )
312 .target_static_child("c")
313 .availability(test_case.offer_availability),
314 )
315 .capability(
316 CapabilityBuilder::storage()
317 .name("data")
318 .backing_dir("dir")
319 .source(StorageDirectorySource::Child("b".into())),
320 )
321 .child_default("b")
322 .child_default("c")
323 .build(),
324 ),
325 (
326 "b",
327 ComponentDeclBuilder::new()
328 .capability(
329 CapabilityBuilder::service()
330 .name("fuchsia.examples.EchoService")
331 .path("/svc/foo.service"),
332 )
333 .expose(
334 ExposeBuilder::service()
335 .name("fuchsia.examples.EchoService")
336 .source(ExposeSource::Self_),
337 )
338 .capability(
339 CapabilityBuilder::protocol()
340 .name("fuchsia.examples.Echo")
341 .path("/svc/foo"),
342 )
343 .expose(
344 ExposeBuilder::protocol()
345 .name("fuchsia.examples.Echo")
346 .source(ExposeSource::Self_),
347 )
348 .capability(
349 CapabilityBuilder::directory()
350 .name("dir")
351 .path("/dir")
352 .rights(fio::Operations::CONNECT),
353 )
354 .expose(ExposeBuilder::directory().name("dir").source(ExposeSource::Self_))
355 .build(),
356 ),
357 (
358 "c",
359 ComponentDeclBuilder::new()
360 .use_(
361 UseBuilder::service()
362 .name("fuchsia.examples.EchoService")
363 .availability(test_case.use_availability),
364 )
365 .use_(
366 UseBuilder::protocol()
367 .name("fuchsia.examples.Echo")
368 .availability(test_case.use_availability),
369 )
370 .use_(
371 UseBuilder::directory()
372 .name("dir")
373 .path("/dir")
374 .rights(fio::Operations::CONNECT)
375 .availability(test_case.use_availability),
376 )
377 .use_(
378 UseBuilder::storage()
379 .name("data")
380 .path("/data")
381 .availability(test_case.use_availability),
382 )
383 .build(),
384 ),
385 ];
386 let model = T::new("a", components).build().await;
387 for check_use in vec![
388 CheckUse::Service {
389 path: "/svc/fuchsia.examples.EchoService".parse().unwrap(),
390 instance: ServiceInstance::Named("default".to_owned()),
391 member: "echo".to_owned(),
392 expected_res: ExpectedResult::Err(zx_status::Status::NOT_FOUND),
393 },
394 CheckUse::Protocol {
395 path: "/svc/fuchsia.examples.Echo".parse().unwrap(),
396 expected_res: ExpectedResult::Err(zx_status::Status::NOT_FOUND),
397 },
398 CheckUse::Directory {
399 path: "/dir".parse().unwrap(),
400 file: PathBuf::from("hippo"),
401 expected_res: ExpectedResult::Err(zx_status::Status::NOT_FOUND),
402 },
403 CheckUse::Storage {
404 path: "/data".parse().unwrap(),
405 storage_relation: None,
406 from_cm_namespace: false,
407 storage_subdir: None,
408 expected_res: ExpectedResult::Err(zx_status::Status::NOT_FOUND),
409 },
410 ] {
411 model.check_use(["c"].try_into().unwrap(), check_use).await;
412 }
413 }
414 }
415
416 pub async fn test_expose_availability_successful_routes(&self) {
428 for test_case in Self::VALID_AVAILABILITY_PAIRS {
429 let components = vec![
430 (
431 "a",
432 ComponentDeclBuilder::new()
433 .use_(
434 UseBuilder::service()
435 .source_static_child("b")
436 .name("fuchsia.examples.EchoService")
437 .path("/svc/fuchsia.examples.EchoService_a")
438 .availability(test_case.use_availability),
439 )
440 .use_(
441 UseBuilder::protocol()
442 .source_static_child("b")
443 .name("fuchsia.examples.Echo")
444 .path("/svc/fuchsia.examples.Echo_a")
445 .availability(test_case.use_availability),
446 )
447 .use_(
448 UseBuilder::directory()
449 .source_static_child("b")
450 .name("dir")
451 .path("/dir_a")
452 .availability(test_case.use_availability),
453 )
454 .child_default("b")
455 .build(),
456 ),
457 (
458 "b",
459 ComponentDeclBuilder::new()
460 .capability(
461 CapabilityBuilder::service()
462 .name("fuchsia.examples.EchoService")
463 .path("/svc/foo.service"),
464 )
465 .expose(
466 ExposeBuilder::service()
467 .name("fuchsia.examples.EchoService")
468 .source(ExposeSource::Self_)
469 .availability(test_case.provider_availability),
470 )
471 .capability(
472 CapabilityBuilder::protocol()
473 .name("fuchsia.examples.Echo")
474 .path("/svc/foo"),
475 )
476 .expose(
477 ExposeBuilder::protocol()
478 .name("fuchsia.examples.Echo")
479 .source(ExposeSource::Self_)
480 .availability(test_case.provider_availability),
481 )
482 .capability(CapabilityBuilder::directory().name("dir").path("/data/dir"))
483 .expose(
484 ExposeBuilder::directory()
485 .name("dir")
486 .source(ExposeSource::Self_)
487 .availability(test_case.provider_availability),
488 )
489 .build(),
490 ),
491 ];
492 let builder = T::new("a", components);
493 let model = builder.build().await;
494
495 model
497 .create_static_file(Path::new("dir/hippo"), "hello")
498 .await
499 .expect("failed to create file");
500
501 for check_use in vec![
502 CheckUse::Service {
503 path: "/svc/fuchsia.examples.EchoService_a".parse().unwrap(),
504 instance: ServiceInstance::Named("default".to_owned()),
505 member: "echo".to_owned(),
506 expected_res: ExpectedResult::Ok,
507 },
508 CheckUse::Protocol {
509 path: "/svc/fuchsia.examples.Echo_a".parse().unwrap(),
510 expected_res: ExpectedResult::Ok,
511 },
512 CheckUse::Directory {
513 path: "/dir_a".parse().unwrap(),
514 file: PathBuf::from("hippo"),
515 expected_res: ExpectedResult::Ok,
516 },
517 ] {
518 model.check_use(Moniker::root(), check_use).await;
519 }
520
521 for check_use in vec![
522 CheckUse::Service {
523 path: "/fuchsia.examples.EchoService".parse().unwrap(),
524 instance: ServiceInstance::Named("default".to_owned()),
525 member: "echo".to_owned(),
526 expected_res: ExpectedResult::Ok,
527 },
528 CheckUse::Protocol {
529 path: "/fuchsia.examples.Echo".parse().unwrap(),
530 expected_res: ExpectedResult::Ok,
531 },
532 CheckUse::Directory {
533 path: "/dir".parse().unwrap(),
534 file: PathBuf::from("hippo"),
535 expected_res: ExpectedResult::Ok,
536 },
537 ] {
538 model.check_use_exposed_dir(["b"].try_into().unwrap(), check_use).await;
539 }
540 }
541 }
542
543 pub async fn test_expose_availability_invalid_routes(&self) {
553 struct TestCase {
554 source: ExposeSource,
555 expose_availability: Availability,
556 use_availability: Availability,
557 }
558 for test_case in &[
559 TestCase {
560 source: ExposeSource::Self_,
561 expose_availability: Availability::Optional,
562 use_availability: Availability::Required,
563 },
564 TestCase {
565 source: ExposeSource::Void,
566 expose_availability: Availability::Optional,
567 use_availability: Availability::Required,
568 },
569 TestCase {
570 source: ExposeSource::Void,
571 expose_availability: Availability::Optional,
572 use_availability: Availability::Optional,
573 },
574 TestCase {
575 source: ExposeSource::Void,
576 expose_availability: Availability::Transitional,
577 use_availability: Availability::Optional,
578 },
579 TestCase {
580 source: ExposeSource::Void,
581 expose_availability: Availability::Transitional,
582 use_availability: Availability::Required,
583 },
584 ] {
585 let components = vec![
586 (
587 "a",
588 ComponentDeclBuilder::new()
589 .use_(
590 UseBuilder::service()
591 .source_static_child("b")
592 .name("fuchsia.examples.EchoService")
593 .path("/svc/fuchsia.examples.EchoService_a")
594 .availability(test_case.use_availability),
595 )
596 .use_(
597 UseBuilder::protocol()
598 .source_static_child("b")
599 .name("fuchsia.examples.Echo")
600 .path("/svc/fuchsia.examples.Echo_a")
601 .availability(test_case.use_availability),
602 )
603 .use_(
604 UseBuilder::directory()
605 .source_static_child("b")
606 .name("dir")
607 .path("/dir_a")
608 .availability(test_case.use_availability),
609 )
610 .child_default("b")
611 .build(),
612 ),
613 (
614 "b",
615 ComponentDeclBuilder::new()
616 .capability(
617 CapabilityBuilder::service()
618 .name("fuchsia.examples.EchoService")
619 .path("/svc/foo.service"),
620 )
621 .expose(
622 ExposeBuilder::service()
623 .name("fuchsia.examples.EchoService")
624 .source(test_case.source.clone())
625 .availability(test_case.expose_availability),
626 )
627 .capability(
628 CapabilityBuilder::protocol()
629 .name("fuchsia.examples.Echo")
630 .path("/svc/foo"),
631 )
632 .expose(
633 ExposeBuilder::protocol()
634 .name("fuchsia.examples.Echo")
635 .source(test_case.source.clone())
636 .availability(test_case.expose_availability),
637 )
638 .capability(CapabilityBuilder::directory().name("dir").path("/data/dir"))
639 .expose(
640 ExposeBuilder::directory()
641 .name("dir")
642 .source(test_case.source.clone())
643 .availability(test_case.expose_availability),
644 )
645 .build(),
646 ),
647 ];
648 let builder = T::new("a", components);
649 let model = builder.build().await;
650
651 model
653 .create_static_file(Path::new("dir/hippo"), "hello")
654 .await
655 .expect("failed to create file");
656 for check_use in vec![
657 CheckUse::Service {
658 path: "/svc/fuchsia.examples.EchoService_a".parse().unwrap(),
659 instance: ServiceInstance::Named("default".to_owned()),
660 member: "echo".to_owned(),
661 expected_res: ExpectedResult::Err(zx_status::Status::NOT_FOUND),
662 },
663 CheckUse::Protocol {
664 path: "/svc/fuchsia.examples.Echo_a".parse().unwrap(),
665 expected_res: ExpectedResult::Err(zx_status::Status::NOT_FOUND),
666 },
667 CheckUse::Directory {
668 path: "/dir_a".parse().unwrap(),
669 file: PathBuf::from("hippo"),
670 expected_res: ExpectedResult::Err(zx_status::Status::NOT_FOUND),
671 },
672 ] {
673 model.check_use(Moniker::root(), check_use).await;
674 }
675 }
676 }
677}