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