Skip to main content

routing_test_helpers/
dictionary.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::{CheckUse, ExpectedResult, RoutingTestModel, RoutingTestModelBuilder};
6use cm_rust::offer::*;
7use cm_rust::*;
8use cm_rust_testing::*;
9use fidl_fuchsia_io as fio;
10use moniker::Moniker;
11use routing::debug_route_sandbox_path;
12use std::marker::PhantomData;
13use std::path::PathBuf;
14use zx_status::Status;
15
16pub struct CommonDictionaryTest<T: RoutingTestModelBuilder> {
17    builder: PhantomData<T>,
18}
19
20impl<T: RoutingTestModelBuilder> CommonDictionaryTest<T> {
21    pub fn new() -> Self {
22        Self { builder: PhantomData }
23    }
24
25    pub async fn test_use_protocol_from_dictionary(&self) {
26        // Test extracting a protocol from a dictionary with source parent, self and child.
27        let components = vec![
28            (
29                "root",
30                ComponentDeclBuilder::new()
31                    .protocol_default("foo")
32                    .dictionary_default("parent_dict")
33                    .offer(
34                        OfferBuilder::protocol()
35                            .name("foo")
36                            .target_name("B")
37                            .source(OfferSource::Self_)
38                            .target(OfferTarget::Capability("parent_dict".parse().unwrap())),
39                    )
40                    .offer(
41                        OfferBuilder::dictionary()
42                            .name("parent_dict")
43                            .source(OfferSource::Self_)
44                            .target_static_child("mid"),
45                    )
46                    .child_default("mid")
47                    .build(),
48            ),
49            (
50                "mid",
51                ComponentDeclBuilder::new()
52                    .protocol_default("foo")
53                    .dictionary_default("self_dict")
54                    .offer(
55                        OfferBuilder::protocol()
56                            .name("foo")
57                            .target_name("A")
58                            .source(OfferSource::Self_)
59                            .target(OfferTarget::Capability("self_dict".parse().unwrap())),
60                    )
61                    .use_(
62                        UseBuilder::protocol()
63                            .source(UseSource::Self_)
64                            .name("A")
65                            .from_dictionary("self_dict"),
66                    )
67                    .use_(UseBuilder::protocol().name("B").from_dictionary("parent_dict"))
68                    .use_(
69                        UseBuilder::protocol()
70                            .source_static_child("leaf")
71                            .name("C")
72                            .from_dictionary("child_dict"),
73                    )
74                    .child_default("leaf")
75                    .build(),
76            ),
77            (
78                "leaf",
79                ComponentDeclBuilder::new()
80                    .protocol_default("foo")
81                    .dictionary_default("child_dict")
82                    .offer(
83                        OfferBuilder::protocol()
84                            .name("foo")
85                            .target_name("C")
86                            .source(OfferSource::Self_)
87                            .target(OfferTarget::Capability("child_dict".parse().unwrap())),
88                    )
89                    .expose(
90                        ExposeBuilder::dictionary().name("child_dict").source(ExposeSource::Self_),
91                    )
92                    .build(),
93            ),
94        ];
95
96        let test = T::new("root", components).build().await;
97        for path in ["/svc/A", "/svc/B", "/svc/C"] {
98            test.check_use(
99                "mid".try_into().unwrap(),
100                CheckUse::Protocol {
101                    path: path.parse().unwrap(),
102                    expected_res: ExpectedResult::Ok,
103                },
104            )
105            .await;
106        }
107    }
108
109    pub async fn test_use_protocol_from_dictionary_not_a_dictionary(&self) {
110        // Test extracting a protocol from a dictionary, but the dictionary is not actually a
111        // dictionary so it should fail with a type error.
112        let components = vec![
113            (
114                "root",
115                ComponentDeclBuilder::new()
116                    .protocol_default("parent_not_dict")
117                    .offer(
118                        OfferBuilder::protocol()
119                            .name("parent_not_dict")
120                            .source(OfferSource::Self_)
121                            .target_static_child("mid"),
122                    )
123                    .child_default("mid")
124                    .build(),
125            ),
126            (
127                "mid",
128                ComponentDeclBuilder::new()
129                    // We don't test "self_not_dict" here because the manifest schema forbids such a
130                    // manifest from existing.
131                    .use_(UseBuilder::protocol().name("A").from_dictionary("parent_not_dict"))
132                    .use_(
133                        UseBuilder::protocol()
134                            .source_static_child("leaf")
135                            .name("B")
136                            .from_dictionary("child_not_dict"),
137                    )
138                    .child_default("leaf")
139                    .build(),
140            ),
141            (
142                "leaf",
143                ComponentDeclBuilder::new()
144                    .protocol_default("child_not_dict")
145                    .expose(
146                        ExposeBuilder::protocol()
147                            .name("child_not_dict")
148                            .source(ExposeSource::Self_),
149                    )
150                    .build(),
151            ),
152        ];
153
154        let test = T::new("root", components).build().await;
155        for path in ["/svc/A", "/svc/B"] {
156            test.check_use(
157                "mid".try_into().unwrap(),
158                CheckUse::Protocol {
159                    path: path.parse().unwrap(),
160                    expected_res: ExpectedResult::Err(Status::NOT_FOUND),
161                },
162            )
163            .await;
164        }
165    }
166
167    pub async fn test_use_protocol_from_dictionary_not_used(&self) {
168        // Create a dictionary with two protocols. `use` one of the protocols, but not the other.
169        // Only the protocol that is `use`d should be accessible.
170        let components = vec![
171            (
172                "root",
173                ComponentDeclBuilder::new()
174                    .protocol_default("foo")
175                    .protocol_default("bar")
176                    .dictionary_default("parent_dict")
177                    .offer(
178                        OfferBuilder::protocol()
179                            .name("foo")
180                            .target_name("A")
181                            .source(OfferSource::Self_)
182                            .target(OfferTarget::Capability("parent_dict".parse().unwrap())),
183                    )
184                    .offer(
185                        OfferBuilder::protocol()
186                            .name("bar")
187                            .target_name("B")
188                            .source(OfferSource::Self_)
189                            .target(OfferTarget::Capability("parent_dict".parse().unwrap())),
190                    )
191                    .offer(
192                        OfferBuilder::dictionary()
193                            .name("parent_dict")
194                            .source(OfferSource::Self_)
195                            .target_static_child("leaf"),
196                    )
197                    .child_default("leaf")
198                    .build(),
199            ),
200            (
201                "leaf",
202                ComponentDeclBuilder::new()
203                    .use_(UseBuilder::protocol().name("A").from_dictionary("parent_dict"))
204                    .build(),
205            ),
206        ];
207
208        let test = T::new("root", components).build().await;
209        test.check_use(
210            "leaf".try_into().unwrap(),
211            CheckUse::Protocol {
212                path: "/svc/A".parse().unwrap(),
213                expected_res: ExpectedResult::Ok,
214            },
215        )
216        .await;
217        test.check_use(
218            "leaf".try_into().unwrap(),
219            CheckUse::Protocol {
220                path: "/svc/B".parse().unwrap(),
221                expected_res: ExpectedResult::Err(Status::NOT_FOUND),
222            },
223        )
224        .await;
225    }
226
227    pub async fn test_use_protocol_from_dictionary_not_found(&self) {
228        // Test extracting a protocol from a dictionary, but the protocol is missing from the
229        // dictionary.
230        let components = vec![
231            (
232                "root",
233                ComponentDeclBuilder::new()
234                    .protocol_default("foo")
235                    .dictionary_default("dict")
236                    .offer(
237                        OfferBuilder::protocol()
238                            .name("foo")
239                            .target_name("B")
240                            .source(OfferSource::Self_)
241                            .target(OfferTarget::Capability("dict".parse().unwrap())),
242                    )
243                    .offer(
244                        OfferBuilder::dictionary()
245                            .name("dict")
246                            .source(OfferSource::Self_)
247                            .target_static_child("leaf"),
248                    )
249                    .child_default("leaf")
250                    .build(),
251            ),
252            (
253                "leaf",
254                ComponentDeclBuilder::new()
255                    .use_(UseBuilder::protocol().name("A").from_dictionary("dict"))
256                    .build(),
257            ),
258        ];
259
260        let test = T::new("root", components).build().await;
261        test.check_use(
262            "leaf".try_into().unwrap(),
263            CheckUse::Protocol {
264                path: "/svc/A".parse().unwrap(),
265                expected_res: ExpectedResult::Err(Status::NOT_FOUND),
266            },
267        )
268        .await;
269
270        // Test extracting a protocol from a dictionary, but the dictionary is not routed to the
271        // target.
272        let components = vec![
273            (
274                "root",
275                ComponentDeclBuilder::new()
276                    .protocol_default("foo")
277                    .dictionary_default("dict")
278                    .offer(
279                        OfferBuilder::protocol()
280                            .name("foo")
281                            .target_name("A")
282                            .source(OfferSource::Self_)
283                            .target(OfferTarget::Capability("dict".parse().unwrap())),
284                    )
285                    .offer(
286                        OfferBuilder::dictionary()
287                            .name("dict")
288                            .target_name("other_dict")
289                            .source(OfferSource::Self_)
290                            .target_static_child("leaf"),
291                    )
292                    .child_default("leaf")
293                    .build(),
294            ),
295            (
296                "leaf",
297                ComponentDeclBuilder::new()
298                    .use_(UseBuilder::protocol().name("A").from_dictionary("dict"))
299                    .build(),
300            ),
301        ];
302
303        let test = T::new("root", components).build().await;
304        test.check_use(
305            "leaf".try_into().unwrap(),
306            CheckUse::Protocol {
307                path: "/svc/A".parse().unwrap(),
308                expected_res: ExpectedResult::Err(Status::NOT_FOUND),
309            },
310        )
311        .await;
312    }
313
314    pub async fn test_use_directory_from_dictionary(&self) {
315        let components = vec![
316            (
317                "root",
318                ComponentDeclBuilder::new()
319                    .capability(CapabilityBuilder::directory().name("bar_data").path("/data/foo"))
320                    .dictionary_default("parent_dict")
321                    .offer(
322                        OfferBuilder::dictionary()
323                            .name("parent_dict")
324                            .source(OfferSource::Self_)
325                            .target_static_child("leaf"),
326                    )
327                    .offer(
328                        OfferBuilder::directory()
329                            .name("bar_data")
330                            .target_name("B")
331                            .source(OfferSource::Self_)
332                            .target(OfferTarget::Capability("parent_dict".parse().unwrap()))
333                            .rights(fio::R_STAR_DIR),
334                    )
335                    .child_default("leaf")
336                    .build(),
337            ),
338            (
339                "leaf",
340                ComponentDeclBuilder::new()
341                    .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
342                    .dictionary_default("self_dict")
343                    .offer(
344                        OfferBuilder::directory()
345                            .name("foo_data")
346                            .target_name("A")
347                            .source(OfferSource::Self_)
348                            .target(OfferTarget::Capability("self_dict".parse().unwrap()))
349                            .rights(fio::R_STAR_DIR),
350                    )
351                    .use_(
352                        UseBuilder::directory()
353                            .source(UseSource::Self_)
354                            .name("A")
355                            .from_dictionary("self_dict")
356                            .path("/A"),
357                    )
358                    .use_(
359                        UseBuilder::directory().name("B").from_dictionary("parent_dict").path("/B"),
360                    )
361                    .build(),
362            ),
363        ];
364
365        let test = T::new("root", components).build().await;
366        test.check_use(
367            "leaf".try_into().unwrap(),
368            CheckUse::Directory {
369                path: "/A".parse().unwrap(),
370                file: PathBuf::from("hippo"),
371                expected_res: ExpectedResult::Ok,
372            },
373        )
374        .await;
375        test.check_use(
376            "leaf".try_into().unwrap(),
377            CheckUse::Directory {
378                path: "/B".parse().unwrap(),
379                file: PathBuf::from("hippo"),
380                expected_res: ExpectedResult::Ok,
381            },
382        )
383        .await;
384    }
385
386    pub async fn test_expose_directory_from_dictionary(&self) {
387        // Routing a directory into a dictionary isn't supported yet, it should fail.
388        let components = vec![
389            (
390                "root",
391                ComponentDeclBuilder::new()
392                    .use_(UseBuilder::directory().source_static_child("mid").name("A").path("/A"))
393                    .use_(UseBuilder::directory().source_static_child("mid").name("B").path("/B"))
394                    .child_default("mid")
395                    .build(),
396            ),
397            (
398                "mid",
399                ComponentDeclBuilder::new()
400                    .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
401                    .dictionary_default("self_dict")
402                    .offer(
403                        OfferBuilder::directory()
404                            .name("foo_data")
405                            .target_name("A")
406                            .source(OfferSource::Self_)
407                            .target(OfferTarget::Capability("self_dict".parse().unwrap()))
408                            .rights(fio::R_STAR_DIR),
409                    )
410                    .expose(
411                        ExposeBuilder::directory()
412                            .name("A")
413                            .source(ExposeSource::Self_)
414                            .from_dictionary("self_dict"),
415                    )
416                    .expose(
417                        ExposeBuilder::directory()
418                            .name("B")
419                            .source_static_child("leaf")
420                            .from_dictionary("child_dict"),
421                    )
422                    .child_default("leaf")
423                    .build(),
424            ),
425            (
426                "leaf",
427                ComponentDeclBuilder::new()
428                    .capability(CapabilityBuilder::directory().name("bar_data").path("/data/foo"))
429                    .dictionary_default("child_dict")
430                    .expose(
431                        ExposeBuilder::dictionary().name("child_dict").source(ExposeSource::Self_),
432                    )
433                    .offer(
434                        OfferBuilder::directory()
435                            .name("bar_data")
436                            .target_name("B")
437                            .source(OfferSource::Self_)
438                            .target(OfferTarget::Capability("child_dict".parse().unwrap()))
439                            .rights(fio::R_STAR_DIR),
440                    )
441                    .build(),
442            ),
443        ];
444
445        let test = T::new("root", components).build().await;
446        test.check_use(
447            ".".try_into().unwrap(),
448            CheckUse::Directory {
449                path: "/A".parse().unwrap(),
450                file: PathBuf::from("hippo"),
451                expected_res: ExpectedResult::Ok,
452            },
453        )
454        .await;
455        test.check_use(
456            ".".try_into().unwrap(),
457            CheckUse::Directory {
458                path: "/B".parse().unwrap(),
459                file: PathBuf::from("hippo"),
460                expected_res: ExpectedResult::Ok,
461            },
462        )
463        .await;
464    }
465
466    pub async fn test_use_protocol_from_nested_dictionary(&self) {
467        // Test extracting a protocol from a dictionary nested in another dictionary with source
468        // parent, self and child.
469        let components = vec![
470            (
471                "root",
472                ComponentDeclBuilder::new()
473                    .protocol_default("foo")
474                    .dictionary_default("nested")
475                    .dictionary_default("parent_dict")
476                    .offer(
477                        OfferBuilder::protocol()
478                            .name("foo")
479                            .target_name("B")
480                            .source(OfferSource::Self_)
481                            .target(OfferTarget::Capability("nested".parse().unwrap())),
482                    )
483                    .offer(
484                        OfferBuilder::dictionary()
485                            .name("nested")
486                            .source(OfferSource::Self_)
487                            .target(OfferTarget::Capability("parent_dict".parse().unwrap())),
488                    )
489                    .offer(
490                        OfferBuilder::dictionary()
491                            .name("parent_dict")
492                            .source(OfferSource::Self_)
493                            .target_static_child("mid"),
494                    )
495                    .child_default("mid")
496                    .build(),
497            ),
498            (
499                "mid",
500                ComponentDeclBuilder::new()
501                    .protocol_default("foo")
502                    .dictionary_default("nested")
503                    .dictionary_default("self_dict")
504                    .offer(
505                        OfferBuilder::protocol()
506                            .name("foo")
507                            .target_name("A")
508                            .source(OfferSource::Self_)
509                            .target(OfferTarget::Capability("nested".parse().unwrap())),
510                    )
511                    .offer(
512                        OfferBuilder::dictionary()
513                            .name("nested")
514                            .source(OfferSource::Self_)
515                            .target(OfferTarget::Capability("self_dict".parse().unwrap())),
516                    )
517                    .use_(
518                        UseBuilder::protocol()
519                            .source(UseSource::Self_)
520                            .name("A")
521                            .from_dictionary("self_dict/nested"),
522                    )
523                    .use_(UseBuilder::protocol().name("B").from_dictionary("parent_dict/nested"))
524                    .use_(
525                        UseBuilder::protocol()
526                            .source_static_child("leaf")
527                            .name("C")
528                            .from_dictionary("child_dict/nested"),
529                    )
530                    .child_default("leaf")
531                    .build(),
532            ),
533            (
534                "leaf",
535                ComponentDeclBuilder::new()
536                    .protocol_default("foo")
537                    .dictionary_default("nested")
538                    .dictionary_default("child_dict")
539                    .offer(
540                        OfferBuilder::protocol()
541                            .name("foo")
542                            .target_name("C")
543                            .source(OfferSource::Self_)
544                            .target(OfferTarget::Capability("nested".parse().unwrap())),
545                    )
546                    .offer(
547                        OfferBuilder::dictionary()
548                            .name("nested")
549                            .source(OfferSource::Self_)
550                            .target(OfferTarget::Capability("child_dict".parse().unwrap())),
551                    )
552                    .expose(
553                        ExposeBuilder::dictionary().name("child_dict").source(ExposeSource::Self_),
554                    )
555                    .build(),
556            ),
557        ];
558
559        let test = T::new("root", components).build().await;
560        for path in ["/svc/A", "/svc/B", "/svc/C"] {
561            test.check_use(
562                "mid".try_into().unwrap(),
563                CheckUse::Protocol {
564                    path: path.parse().unwrap(),
565                    expected_res: ExpectedResult::Ok,
566                },
567            )
568            .await;
569        }
570    }
571
572    pub async fn test_offer_protocol_from_dictionary(&self) {
573        // Test extracting a protocol from a dictionary with source parent, self, and child.
574        let components = vec![
575            (
576                "root",
577                ComponentDeclBuilder::new()
578                    .protocol_default("foo")
579                    .dictionary_default("parent_dict")
580                    .offer(
581                        OfferBuilder::protocol()
582                            .name("foo")
583                            .target_name("B")
584                            .source(OfferSource::Self_)
585                            .target(OfferTarget::Capability("parent_dict".parse().unwrap())),
586                    )
587                    .offer(
588                        OfferBuilder::dictionary()
589                            .name("parent_dict")
590                            .source(OfferSource::Self_)
591                            .target_static_child("mid"),
592                    )
593                    .child_default("mid")
594                    .build(),
595            ),
596            (
597                "mid",
598                ComponentDeclBuilder::new()
599                    .protocol_default("foo")
600                    .dictionary_default("self_dict")
601                    .offer(
602                        OfferBuilder::protocol()
603                            .name("A")
604                            .target_name("A_svc")
605                            .source(OfferSource::Self_)
606                            .target_static_child("leaf")
607                            .from_dictionary("self_dict"),
608                    )
609                    .offer(
610                        OfferBuilder::protocol()
611                            .name("foo")
612                            .target_name("A")
613                            .source(OfferSource::Self_)
614                            .target(OfferTarget::Capability("self_dict".parse().unwrap())),
615                    )
616                    .offer(
617                        OfferBuilder::protocol()
618                            .name("B")
619                            .target_name("B_svc")
620                            .source(OfferSource::Parent)
621                            .target_static_child("leaf")
622                            .from_dictionary("parent_dict"),
623                    )
624                    .offer(
625                        OfferBuilder::protocol()
626                            .name("C")
627                            .target_name("C_svc")
628                            .source_static_child("provider")
629                            .target_static_child("leaf")
630                            .from_dictionary("child_dict"),
631                    )
632                    .child_default("provider")
633                    .child_default("leaf")
634                    .build(),
635            ),
636            (
637                "provider",
638                ComponentDeclBuilder::new()
639                    .protocol_default("foo")
640                    .dictionary_default("child_dict")
641                    .offer(
642                        OfferBuilder::protocol()
643                            .name("foo")
644                            .target_name("C")
645                            .source(OfferSource::Self_)
646                            .target(OfferTarget::Capability("child_dict".parse().unwrap())),
647                    )
648                    .expose(
649                        ExposeBuilder::dictionary().name("child_dict").source(ExposeSource::Self_),
650                    )
651                    .build(),
652            ),
653            (
654                "leaf",
655                ComponentDeclBuilder::new()
656                    .use_(UseBuilder::protocol().name("A_svc").path("/svc/A"))
657                    .use_(UseBuilder::protocol().name("B_svc").path("/svc/B"))
658                    .use_(UseBuilder::protocol().name("C_svc").path("/svc/C"))
659                    .build(),
660            ),
661        ];
662
663        let test = T::new("root", components).build().await;
664        for path in ["/svc/A", "/svc/B", "/svc/C"] {
665            test.check_use(
666                "mid/leaf".try_into().unwrap(),
667                CheckUse::Protocol {
668                    path: path.parse().unwrap(),
669                    expected_res: ExpectedResult::Ok,
670                },
671            )
672            .await;
673        }
674    }
675
676    pub async fn test_offer_protocol_from_dictionary_not_found(&self) {
677        // Test extracting a protocol from a dictionary, but the protocol is missing from the
678        // dictionary.
679        let components = vec![
680            (
681                "root",
682                ComponentDeclBuilder::new()
683                    .protocol_default("foo")
684                    .dictionary_default("dict")
685                    .offer(
686                        OfferBuilder::protocol()
687                            .name("foo")
688                            .target_name("B")
689                            .source(OfferSource::Self_)
690                            .target(OfferTarget::Capability("dict".parse().unwrap())),
691                    )
692                    .offer(
693                        OfferBuilder::dictionary()
694                            .name("dict")
695                            .source(OfferSource::Self_)
696                            .target_static_child("mid"),
697                    )
698                    .child_default("mid")
699                    .build(),
700            ),
701            (
702                "mid",
703                ComponentDeclBuilder::new()
704                    .offer(
705                        OfferBuilder::protocol()
706                            .name("A")
707                            .target_name("A_svc")
708                            .source(OfferSource::Parent)
709                            .target_static_child("leaf")
710                            .from_dictionary("dict"),
711                    )
712                    .child_default("leaf")
713                    .build(),
714            ),
715            (
716                "leaf",
717                ComponentDeclBuilder::new()
718                    .use_(UseBuilder::protocol().name("A_svc").path("/svc/A"))
719                    .build(),
720            ),
721        ];
722
723        let test = T::new("root", components).build().await;
724        test.check_use(
725            "mid/leaf".try_into().unwrap(),
726            CheckUse::Protocol {
727                path: "/svc/A".parse().unwrap(),
728                expected_res: ExpectedResult::Err(Status::NOT_FOUND),
729            },
730        )
731        .await;
732    }
733
734    pub async fn test_offer_protocol_from_dictionary_to_dictionary(&self) {
735        let components = vec![
736            (
737                "root",
738                ComponentDeclBuilder::new()
739                    .dictionary_default("dict1")
740                    .dictionary_default("dict2")
741                    .offer(
742                        OfferBuilder::protocol()
743                            .name("foo")
744                            .target_name("A")
745                            .source_static_child("provider")
746                            .target(OfferTarget::Capability("dict1".parse().unwrap())),
747                    )
748                    .offer(
749                        OfferBuilder::protocol()
750                            .name("A")
751                            .target_name("A")
752                            .from_dictionary("dict1")
753                            .source(OfferSource::Self_)
754                            .target(OfferTarget::Capability("dict2".parse().unwrap())),
755                    )
756                    .offer(
757                        OfferBuilder::dictionary()
758                            .name("dict2")
759                            .source(OfferSource::Self_)
760                            .target_static_child("leaf"),
761                    )
762                    .child_default("provider")
763                    .child_default("leaf")
764                    .build(),
765            ),
766            (
767                "provider",
768                ComponentDeclBuilder::new()
769                    .protocol_default("foo")
770                    .expose(ExposeBuilder::protocol().name("foo").source(ExposeSource::Self_))
771                    .build(),
772            ),
773            (
774                "leaf",
775                ComponentDeclBuilder::new()
776                    .use_(UseBuilder::protocol().name("A").from_dictionary("dict2").path("/svc/A"))
777                    .build(),
778            ),
779        ];
780
781        let test = T::new("root", components).build().await;
782        test.check_use(
783            "leaf".try_into().unwrap(),
784            CheckUse::Protocol {
785                path: "/svc/A".parse().unwrap(),
786                expected_res: ExpectedResult::Ok,
787            },
788        )
789        .await;
790    }
791
792    pub async fn test_offer_protocol_from_nested_dictionary(&self) {
793        // Test extracting a protocol from a dictionary nested in another dictionary with source
794        // parent, self, and child.
795        let components = vec![
796            (
797                "root",
798                ComponentDeclBuilder::new()
799                    .protocol_default("foo")
800                    .dictionary_default("nested")
801                    .dictionary_default("parent_dict")
802                    .offer(
803                        OfferBuilder::protocol()
804                            .name("foo")
805                            .target_name("B")
806                            .source(OfferSource::Self_)
807                            .target(OfferTarget::Capability("nested".parse().unwrap())),
808                    )
809                    .offer(
810                        OfferBuilder::dictionary()
811                            .name("nested")
812                            .source(OfferSource::Self_)
813                            .target(OfferTarget::Capability("parent_dict".parse().unwrap())),
814                    )
815                    .offer(
816                        OfferBuilder::dictionary()
817                            .name("parent_dict")
818                            .source(OfferSource::Self_)
819                            .target_static_child("mid"),
820                    )
821                    .child_default("mid")
822                    .build(),
823            ),
824            (
825                "mid",
826                ComponentDeclBuilder::new()
827                    .protocol_default("foo")
828                    .dictionary_default("self_dict")
829                    .dictionary_default("nested")
830                    .offer(
831                        OfferBuilder::protocol()
832                            .name("A")
833                            .target_name("A_svc")
834                            .source(OfferSource::Self_)
835                            .target_static_child("leaf")
836                            .from_dictionary("self_dict/nested"),
837                    )
838                    .offer(
839                        OfferBuilder::protocol()
840                            .name("foo")
841                            .target_name("A")
842                            .source(OfferSource::Self_)
843                            .target(OfferTarget::Capability("nested".parse().unwrap())),
844                    )
845                    .offer(
846                        OfferBuilder::dictionary()
847                            .name("nested")
848                            .source(OfferSource::Self_)
849                            .target(OfferTarget::Capability("self_dict".parse().unwrap())),
850                    )
851                    .offer(
852                        OfferBuilder::protocol()
853                            .name("B")
854                            .target_name("B_svc")
855                            .source(OfferSource::Parent)
856                            .target_static_child("leaf")
857                            .from_dictionary("parent_dict/nested"),
858                    )
859                    .offer(
860                        OfferBuilder::protocol()
861                            .name("C")
862                            .target_name("C_svc")
863                            .source_static_child("provider")
864                            .target_static_child("leaf")
865                            .from_dictionary("child_dict/nested"),
866                    )
867                    .child_default("provider")
868                    .child_default("leaf")
869                    .build(),
870            ),
871            (
872                "provider",
873                ComponentDeclBuilder::new()
874                    .protocol_default("foo")
875                    .dictionary_default("child_dict")
876                    .dictionary_default("nested")
877                    .offer(
878                        OfferBuilder::protocol()
879                            .name("foo")
880                            .target_name("C")
881                            .source(OfferSource::Self_)
882                            .target(OfferTarget::Capability("nested".parse().unwrap())),
883                    )
884                    .offer(
885                        OfferBuilder::dictionary()
886                            .name("nested")
887                            .source(OfferSource::Self_)
888                            .target(OfferTarget::Capability("child_dict".parse().unwrap())),
889                    )
890                    .expose(
891                        ExposeBuilder::dictionary().name("child_dict").source(ExposeSource::Self_),
892                    )
893                    .build(),
894            ),
895            (
896                "leaf",
897                ComponentDeclBuilder::new()
898                    .use_(UseBuilder::protocol().name("A_svc").path("/svc/A"))
899                    .use_(UseBuilder::protocol().name("B_svc").path("/svc/B"))
900                    .use_(UseBuilder::protocol().name("C_svc").path("/svc/C"))
901                    .build(),
902            ),
903        ];
904
905        let test = T::new("root", components).build().await;
906        for path in ["/svc/A", "/svc/B", "/svc/C"] {
907            test.check_use(
908                "mid/leaf".try_into().unwrap(),
909                CheckUse::Protocol {
910                    path: path.parse().unwrap(),
911                    expected_res: ExpectedResult::Ok,
912                },
913            )
914            .await;
915        }
916    }
917
918    pub async fn test_expose_protocol_from_dictionary(&self) {
919        // Test extracting a protocol from a dictionary with source self and child.
920        let components = vec![
921            (
922                "root",
923                ComponentDeclBuilder::new()
924                    .use_(
925                        UseBuilder::protocol()
926                            .source_static_child("mid")
927                            .name("A_svc")
928                            .path("/svc/A"),
929                    )
930                    .use_(
931                        UseBuilder::protocol()
932                            .source_static_child("mid")
933                            .name("B_svc")
934                            .path("/svc/B"),
935                    )
936                    .child_default("mid")
937                    .build(),
938            ),
939            (
940                "mid",
941                ComponentDeclBuilder::new()
942                    .protocol_default("foo")
943                    .dictionary_default("self_dict")
944                    .expose(
945                        ExposeBuilder::protocol()
946                            .name("A")
947                            .target_name("A_svc")
948                            .from_dictionary("self_dict")
949                            .source(ExposeSource::Self_),
950                    )
951                    .expose(
952                        ExposeBuilder::protocol()
953                            .name("B")
954                            .target_name("B_svc")
955                            .from_dictionary("child_dict")
956                            .source_static_child("leaf"),
957                    )
958                    .offer(
959                        OfferBuilder::protocol()
960                            .name("foo")
961                            .target_name("A")
962                            .source(OfferSource::Self_)
963                            .target(OfferTarget::Capability("self_dict".parse().unwrap())),
964                    )
965                    .child_default("leaf")
966                    .build(),
967            ),
968            (
969                "leaf",
970                ComponentDeclBuilder::new()
971                    .protocol_default("foo")
972                    .dictionary_default("child_dict")
973                    .offer(
974                        OfferBuilder::protocol()
975                            .name("foo")
976                            .target_name("B")
977                            .source(OfferSource::Self_)
978                            .target(OfferTarget::Capability("child_dict".parse().unwrap())),
979                    )
980                    .expose(
981                        ExposeBuilder::dictionary().name("child_dict").source(ExposeSource::Self_),
982                    )
983                    .build(),
984            ),
985        ];
986
987        let test = T::new("root", components).build().await;
988        for path in ["/svc/A", "/svc/B"] {
989            test.check_use(
990                ".".try_into().unwrap(),
991                CheckUse::Protocol {
992                    path: path.parse().unwrap(),
993                    expected_res: ExpectedResult::Ok,
994                },
995            )
996            .await;
997        }
998    }
999
1000    pub async fn test_expose_protocol_from_dictionary_not_found(&self) {
1001        // Test extracting a protocol from a dictionary, but the protocol is missing.
1002        let components = vec![
1003            (
1004                "root",
1005                ComponentDeclBuilder::new()
1006                    .use_(
1007                        UseBuilder::protocol()
1008                            .source_static_child("mid")
1009                            .name("A_svc")
1010                            .path("/svc/A"),
1011                    )
1012                    .child_default("mid")
1013                    .build(),
1014            ),
1015            (
1016                "mid",
1017                ComponentDeclBuilder::new()
1018                    .expose(
1019                        ExposeBuilder::protocol()
1020                            .name("A")
1021                            .target_name("A_svc")
1022                            .source(ExposeSource::Self_)
1023                            .from_dictionary("dict")
1024                            .build(),
1025                    )
1026                    .child_default("leaf")
1027                    .build(),
1028            ),
1029            (
1030                "leaf",
1031                ComponentDeclBuilder::new()
1032                    .protocol_default("foo")
1033                    .dictionary_default("dict")
1034                    .offer(
1035                        OfferBuilder::protocol()
1036                            .name("foo")
1037                            .target_name("B")
1038                            .source(OfferSource::Self_)
1039                            .target(OfferTarget::Capability("dict".parse().unwrap())),
1040                    )
1041                    .expose(ExposeBuilder::dictionary().name("dict").source(ExposeSource::Self_))
1042                    .build(),
1043            ),
1044        ];
1045
1046        let test = T::new("root", components).build().await;
1047        test.check_use(
1048            ".".try_into().unwrap(),
1049            CheckUse::Protocol {
1050                path: "/svc/A".parse().unwrap(),
1051                expected_res: ExpectedResult::Err(Status::NOT_FOUND),
1052            },
1053        )
1054        .await;
1055    }
1056
1057    pub async fn test_expose_protocol_from_nested_dictionary(&self) {
1058        // Test extracting a protocol from a dictionary nested in a dictionary with source self and
1059        // child.
1060        let components = vec![
1061            (
1062                "root",
1063                ComponentDeclBuilder::new()
1064                    .use_(
1065                        UseBuilder::protocol()
1066                            .source_static_child("mid")
1067                            .name("A_svc")
1068                            .path("/svc/A"),
1069                    )
1070                    .use_(
1071                        UseBuilder::protocol()
1072                            .source_static_child("mid")
1073                            .name("B_svc")
1074                            .path("/svc/B"),
1075                    )
1076                    .child_default("mid")
1077                    .build(),
1078            ),
1079            (
1080                "mid",
1081                ComponentDeclBuilder::new()
1082                    .protocol_default("foo")
1083                    .dictionary_default("self_dict")
1084                    .dictionary_default("nested")
1085                    .expose(
1086                        ExposeBuilder::protocol()
1087                            .name("A")
1088                            .target_name("A_svc")
1089                            .from_dictionary("self_dict/nested")
1090                            .source(ExposeSource::Self_),
1091                    )
1092                    .expose(
1093                        ExposeBuilder::protocol()
1094                            .name("B")
1095                            .target_name("B_svc")
1096                            .from_dictionary("child_dict/nested")
1097                            .source_static_child("leaf"),
1098                    )
1099                    .offer(
1100                        OfferBuilder::protocol()
1101                            .name("foo")
1102                            .target_name("A")
1103                            .source(OfferSource::Self_)
1104                            .target(OfferTarget::Capability("nested".parse().unwrap())),
1105                    )
1106                    .offer(
1107                        OfferBuilder::dictionary()
1108                            .name("nested")
1109                            .source(OfferSource::Self_)
1110                            .target(OfferTarget::Capability("self_dict".parse().unwrap())),
1111                    )
1112                    .child_default("leaf")
1113                    .build(),
1114            ),
1115            (
1116                "leaf",
1117                ComponentDeclBuilder::new()
1118                    .protocol_default("foo")
1119                    .dictionary_default("child_dict")
1120                    .dictionary_default("nested")
1121                    .offer(
1122                        OfferBuilder::dictionary()
1123                            .name("nested")
1124                            .source(OfferSource::Self_)
1125                            .target(OfferTarget::Capability("child_dict".parse().unwrap())),
1126                    )
1127                    .offer(
1128                        OfferBuilder::protocol()
1129                            .name("foo")
1130                            .target_name("B")
1131                            .source(OfferSource::Self_)
1132                            .target(OfferTarget::Capability("nested".parse().unwrap())),
1133                    )
1134                    .expose(
1135                        ExposeBuilder::dictionary().name("child_dict").source(ExposeSource::Self_),
1136                    )
1137                    .build(),
1138            ),
1139        ];
1140
1141        let test = T::new("root", components).build().await;
1142        for path in ["/svc/A", "/svc/B"] {
1143            test.check_use(
1144                ".".try_into().unwrap(),
1145                CheckUse::Protocol {
1146                    path: path.parse().unwrap(),
1147                    expected_res: ExpectedResult::Ok,
1148                },
1149            )
1150            .await;
1151        }
1152    }
1153
1154    pub async fn test_dictionary_in_exposed_dir(&self) {
1155        // Test extracting a protocol from a dictionary with source self and child.
1156        let components = vec![
1157            (
1158                "root",
1159                ComponentDeclBuilder::new()
1160                    .protocol_default("foo")
1161                    .dictionary_default("self_dict")
1162                    .expose(
1163                        ExposeBuilder::dictionary().name("self_dict").source(ExposeSource::Self_),
1164                    )
1165                    .expose(
1166                        ExposeBuilder::dictionary().name("child_dict").source_static_child("leaf"),
1167                    )
1168                    .offer(
1169                        OfferBuilder::protocol()
1170                            .name("foo")
1171                            .target_name("A")
1172                            .source(OfferSource::Self_)
1173                            .target(OfferTarget::Capability("self_dict".parse().unwrap())),
1174                    )
1175                    .child_default("leaf")
1176                    .build(),
1177            ),
1178            (
1179                "leaf",
1180                ComponentDeclBuilder::new()
1181                    .protocol_default("foo")
1182                    .dictionary_default("nested")
1183                    .dictionary_default("child_dict")
1184                    .offer(
1185                        OfferBuilder::dictionary()
1186                            .name("nested")
1187                            .source(OfferSource::Self_)
1188                            .target(OfferTarget::Capability("child_dict".parse().unwrap())),
1189                    )
1190                    .offer(
1191                        OfferBuilder::protocol()
1192                            .name("foo")
1193                            .target_name("B")
1194                            .source(OfferSource::Self_)
1195                            .target(OfferTarget::Capability("nested".parse().unwrap())),
1196                    )
1197                    .expose(
1198                        ExposeBuilder::dictionary().name("child_dict").source(ExposeSource::Self_),
1199                    )
1200                    .build(),
1201            ),
1202        ];
1203
1204        let test = T::new("root", components).build().await;
1205        // The dictionaries in the exposed dir will be converted to subdirectories.
1206        for path in ["/self_dict/A", "/child_dict/nested/B"] {
1207            test.check_use_exposed_dir(
1208                ".".try_into().unwrap(),
1209                CheckUse::Protocol {
1210                    path: path.parse().unwrap(),
1211                    expected_res: ExpectedResult::Ok,
1212                },
1213            )
1214            .await;
1215        }
1216    }
1217
1218    pub async fn test_offer_dictionary_to_dictionary(&self) {
1219        // Tests dictionary nesting when the nested dictionary comes from parent, self, or child.
1220        let components = vec![
1221            (
1222                "root",
1223                ComponentDeclBuilder::new()
1224                    .protocol_default("foo")
1225                    .dictionary_default("parent_dict")
1226                    .offer(
1227                        OfferBuilder::protocol()
1228                            .name("foo")
1229                            .target_name("B")
1230                            .source(OfferSource::Self_)
1231                            .target(OfferTarget::Capability("parent_dict".parse().unwrap())),
1232                    )
1233                    .offer(
1234                        OfferBuilder::dictionary()
1235                            .name("parent_dict")
1236                            .source(OfferSource::Self_)
1237                            .target_static_child("mid"),
1238                    )
1239                    .child_default("mid")
1240                    .build(),
1241            ),
1242            (
1243                "mid",
1244                ComponentDeclBuilder::new()
1245                    .protocol_default("foo")
1246                    .dictionary_default("self_dict")
1247                    .dictionary_default("root_dict")
1248                    .offer(
1249                        OfferBuilder::protocol()
1250                            .name("foo")
1251                            .target_name("A")
1252                            .source(OfferSource::Self_)
1253                            .target(OfferTarget::Capability("self_dict".parse().unwrap())),
1254                    )
1255                    .offer(
1256                        OfferBuilder::dictionary()
1257                            .name("self_dict")
1258                            .source(OfferSource::Self_)
1259                            .target(OfferTarget::Capability("root_dict".parse().unwrap())),
1260                    )
1261                    .offer(
1262                        OfferBuilder::dictionary()
1263                            .name("parent_dict")
1264                            .source(OfferSource::Parent)
1265                            .target(OfferTarget::Capability("root_dict".parse().unwrap())),
1266                    )
1267                    .offer(
1268                        OfferBuilder::dictionary()
1269                            .name("child_dict")
1270                            .source_static_child("leaf")
1271                            .target(OfferTarget::Capability("root_dict".parse().unwrap())),
1272                    )
1273                    .use_(
1274                        UseBuilder::protocol()
1275                            .source(UseSource::Self_)
1276                            .name("A")
1277                            .from_dictionary("root_dict/self_dict"),
1278                    )
1279                    .use_(
1280                        UseBuilder::protocol()
1281                            .source(UseSource::Self_)
1282                            .name("B")
1283                            .from_dictionary("root_dict/parent_dict"),
1284                    )
1285                    .use_(
1286                        UseBuilder::protocol()
1287                            .source(UseSource::Self_)
1288                            .name("C")
1289                            .from_dictionary("root_dict/child_dict"),
1290                    )
1291                    .child_default("leaf")
1292                    .build(),
1293            ),
1294            (
1295                "leaf",
1296                ComponentDeclBuilder::new()
1297                    .protocol_default("foo")
1298                    .dictionary_default("child_dict")
1299                    .offer(
1300                        OfferBuilder::protocol()
1301                            .name("foo")
1302                            .target_name("C")
1303                            .source(OfferSource::Self_)
1304                            .target(OfferTarget::Capability("child_dict".parse().unwrap())),
1305                    )
1306                    .expose(
1307                        ExposeBuilder::dictionary().name("child_dict").source(ExposeSource::Self_),
1308                    )
1309                    .build(),
1310            ),
1311        ];
1312
1313        let test = T::new("root", components).build().await;
1314        for path in ["/svc/A", "/svc/B", "/svc/C"] {
1315            test.check_use(
1316                "mid".try_into().unwrap(),
1317                CheckUse::Protocol {
1318                    path: path.parse().unwrap(),
1319                    expected_res: ExpectedResult::Ok,
1320                },
1321            )
1322            .await;
1323        }
1324    }
1325
1326    pub async fn test_use_from_dictionary_availability_attenuated(&self) {
1327        // required -> optional downgrade allowed, of:
1328        // - a capability in a dictionary
1329        // - a capability in a dictionary in a dictionary
1330        let components = vec![
1331            (
1332                "root",
1333                ComponentDeclBuilder::new()
1334                    .protocol_default("foo")
1335                    .protocol_default("bar")
1336                    .dictionary_default("nested")
1337                    .dictionary_default("dict")
1338                    .offer(
1339                        OfferBuilder::protocol()
1340                            .name("foo")
1341                            .target_name("A")
1342                            .source(OfferSource::Self_)
1343                            .target(OfferTarget::Capability("dict".parse().unwrap())),
1344                    )
1345                    .offer(
1346                        OfferBuilder::protocol()
1347                            .name("bar")
1348                            .target_name("B")
1349                            .source(OfferSource::Self_)
1350                            .target(OfferTarget::Capability("nested".parse().unwrap())),
1351                    )
1352                    .offer(
1353                        OfferBuilder::dictionary()
1354                            .name("nested")
1355                            .source(OfferSource::Self_)
1356                            .target(OfferTarget::Capability("dict".parse().unwrap())),
1357                    )
1358                    .offer(
1359                        OfferBuilder::dictionary()
1360                            .name("dict")
1361                            .source(OfferSource::Self_)
1362                            .target_static_child("leaf"),
1363                    )
1364                    .child_default("leaf")
1365                    .build(),
1366            ),
1367            (
1368                "leaf",
1369                ComponentDeclBuilder::new()
1370                    .use_(
1371                        UseBuilder::protocol()
1372                            .name("A")
1373                            .from_dictionary("dict")
1374                            .availability(Availability::Optional),
1375                    )
1376                    .use_(
1377                        UseBuilder::protocol()
1378                            .name("B")
1379                            .from_dictionary("dict/nested")
1380                            .availability(Availability::Optional),
1381                    )
1382                    .build(),
1383            ),
1384        ];
1385
1386        let test = T::new("root", components).build().await;
1387        test.check_use(
1388            "leaf".try_into().unwrap(),
1389            CheckUse::Protocol {
1390                path: "/svc/A".parse().unwrap(),
1391                expected_res: ExpectedResult::Ok,
1392            },
1393        )
1394        .await;
1395        test.check_use(
1396            "leaf".try_into().unwrap(),
1397            CheckUse::Protocol {
1398                path: "/svc/B".parse().unwrap(),
1399                expected_res: ExpectedResult::Ok,
1400            },
1401        )
1402        .await;
1403    }
1404
1405    pub async fn test_use_from_dictionary_availability_invalid(&self) {
1406        // attempted optional -> required upgrade, disallowed, of:
1407        // - an optional capability in a dictionary.
1408        // - a capability in an optional dictionary.
1409        // - a capability in a dictionary in an optional dictionary.
1410        let components = vec![
1411            (
1412                "root",
1413                ComponentDeclBuilder::new()
1414                    .protocol_default("foo")
1415                    .protocol_default("bar")
1416                    .protocol_default("qux")
1417                    .dictionary_default("required_dict")
1418                    .dictionary_default("optional_dict")
1419                    .dictionary_default("nested")
1420                    .dictionary_default("dict_with_optional_nested")
1421                    .offer(
1422                        OfferBuilder::protocol()
1423                            .name("foo")
1424                            .target_name("A")
1425                            .source(OfferSource::Self_)
1426                            .target(OfferTarget::Capability("required_dict".parse().unwrap()))
1427                            .availability(Availability::Optional),
1428                    )
1429                    .offer(
1430                        OfferBuilder::protocol()
1431                            .name("bar")
1432                            .target_name("B")
1433                            .source(OfferSource::Self_)
1434                            .target(OfferTarget::Capability("optional_dict".parse().unwrap())),
1435                    )
1436                    .offer(
1437                        OfferBuilder::protocol()
1438                            .name("qux")
1439                            .target_name("C")
1440                            .source(OfferSource::Self_)
1441                            .target(OfferTarget::Capability("nested".parse().unwrap())),
1442                    )
1443                    .offer(
1444                        OfferBuilder::dictionary()
1445                            .name("nested")
1446                            .source(OfferSource::Self_)
1447                            .target(OfferTarget::Capability(
1448                                "dict_with_optional_nested".parse().unwrap(),
1449                            ))
1450                            .availability(Availability::Optional),
1451                    )
1452                    .offer(
1453                        OfferBuilder::dictionary()
1454                            .name("required_dict")
1455                            .source(OfferSource::Self_)
1456                            .target_static_child("leaf"),
1457                    )
1458                    .offer(
1459                        OfferBuilder::dictionary()
1460                            .name("optional_dict")
1461                            .source(OfferSource::Self_)
1462                            .target_static_child("leaf")
1463                            .availability(Availability::Optional),
1464                    )
1465                    .offer(
1466                        OfferBuilder::dictionary()
1467                            .name("dict_with_optional_nested")
1468                            .source(OfferSource::Self_)
1469                            .target_static_child("leaf"),
1470                    )
1471                    .child_default("leaf")
1472                    .build(),
1473            ),
1474            (
1475                "leaf",
1476                ComponentDeclBuilder::new()
1477                    .use_(UseBuilder::protocol().name("A").from_dictionary("required_dict"))
1478                    .use_(UseBuilder::protocol().name("B").from_dictionary("optional_dict"))
1479                    .use_(
1480                        UseBuilder::protocol()
1481                            .name("C")
1482                            .from_dictionary("dict_with_optional_nested/nested"),
1483                    )
1484                    .build(),
1485            ),
1486        ];
1487
1488        let test = T::new("root", components).build().await;
1489        test.check_use(
1490            "leaf".try_into().unwrap(),
1491            CheckUse::Protocol {
1492                path: "/svc/A".parse().unwrap(),
1493                expected_res: ExpectedResult::Err(Status::NOT_FOUND),
1494            },
1495        )
1496        .await;
1497        test.check_use(
1498            "leaf".try_into().unwrap(),
1499            CheckUse::Protocol {
1500                path: "/svc/B".parse().unwrap(),
1501                expected_res: ExpectedResult::Err(Status::NOT_FOUND),
1502            },
1503        )
1504        .await;
1505        test.check_use(
1506            "leaf".try_into().unwrap(),
1507            CheckUse::Protocol {
1508                path: "/svc/C".parse().unwrap(),
1509                expected_res: ExpectedResult::Err(Status::NOT_FOUND),
1510            },
1511        )
1512        .await;
1513    }
1514
1515    pub async fn test_offer_from_dictionary_availability_attenuated(&self) {
1516        // required -> optional downgrade allowed, of:
1517        // - a capability in a dictionary
1518        // - a capability in a dictionary in a dictionary
1519        let components = vec![
1520            (
1521                "root",
1522                ComponentDeclBuilder::new()
1523                    .protocol_default("foo")
1524                    .protocol_default("bar")
1525                    .dictionary_default("nested")
1526                    .dictionary_default("dict")
1527                    .offer(
1528                        OfferBuilder::protocol()
1529                            .name("foo")
1530                            .target_name("A")
1531                            .source(OfferSource::Self_)
1532                            .target(OfferTarget::Capability("dict".parse().unwrap())),
1533                    )
1534                    .offer(
1535                        OfferBuilder::protocol()
1536                            .name("bar")
1537                            .target_name("B")
1538                            .source(OfferSource::Self_)
1539                            .target(OfferTarget::Capability("nested".parse().unwrap())),
1540                    )
1541                    .offer(
1542                        OfferBuilder::dictionary()
1543                            .name("nested")
1544                            .source(OfferSource::Self_)
1545                            .target(OfferTarget::Capability("dict".parse().unwrap())),
1546                    )
1547                    .offer(
1548                        OfferBuilder::protocol()
1549                            .name("A")
1550                            .source(OfferSource::Self_)
1551                            .target_static_child("leaf")
1552                            .from_dictionary("dict"),
1553                    )
1554                    .offer(
1555                        OfferBuilder::protocol()
1556                            .name("B")
1557                            .source(OfferSource::Self_)
1558                            .target_static_child("leaf")
1559                            .from_dictionary("dict/nested"),
1560                    )
1561                    .child_default("leaf")
1562                    .build(),
1563            ),
1564            (
1565                "leaf",
1566                ComponentDeclBuilder::new()
1567                    .use_(UseBuilder::protocol().name("A").availability(Availability::Optional))
1568                    .use_(UseBuilder::protocol().name("B").availability(Availability::Optional))
1569                    .build(),
1570            ),
1571        ];
1572
1573        let test = T::new("root", components).build().await;
1574        test.check_use(
1575            "leaf".try_into().unwrap(),
1576            CheckUse::Protocol {
1577                path: "/svc/A".parse().unwrap(),
1578                expected_res: ExpectedResult::Ok,
1579            },
1580        )
1581        .await;
1582        test.check_use(
1583            "leaf".try_into().unwrap(),
1584            CheckUse::Protocol {
1585                path: "/svc/B".parse().unwrap(),
1586                expected_res: ExpectedResult::Ok,
1587            },
1588        )
1589        .await;
1590    }
1591
1592    pub async fn test_offer_from_dictionary_availability_invalid(&self) {
1593        // attempted optional -> required upgrade, disallowed, of:
1594        // - an optional capability in a dictionary.
1595        // - a capability in an optional dictionary.
1596        // - a capability in a dictionary in an optional dictionary.
1597        let components = vec![
1598            (
1599                "root",
1600                ComponentDeclBuilder::new()
1601                    .protocol_default("foo")
1602                    .protocol_default("bar")
1603                    .protocol_default("qux")
1604                    .dictionary_default("required_dict")
1605                    .dictionary_default("optional_dict")
1606                    .dictionary_default("nested")
1607                    .dictionary_default("dict_with_optional_nested")
1608                    .offer(
1609                        OfferBuilder::protocol()
1610                            .name("foo")
1611                            .target_name("A")
1612                            .source(OfferSource::Self_)
1613                            .target(OfferTarget::Capability("required_dict".parse().unwrap()))
1614                            .availability(Availability::Optional),
1615                    )
1616                    .offer(
1617                        OfferBuilder::protocol()
1618                            .name("bar")
1619                            .target_name("B")
1620                            .source(OfferSource::Self_)
1621                            .target(OfferTarget::Capability("optional_dict".parse().unwrap())),
1622                    )
1623                    .offer(
1624                        OfferBuilder::protocol()
1625                            .name("qux")
1626                            .target_name("C")
1627                            .source(OfferSource::Self_)
1628                            .target(OfferTarget::Capability("nested".parse().unwrap())),
1629                    )
1630                    .offer(
1631                        OfferBuilder::dictionary()
1632                            .name("nested")
1633                            .source(OfferSource::Self_)
1634                            .target(OfferTarget::Capability(
1635                                "dict_with_optional_nested".parse().unwrap(),
1636                            ))
1637                            .availability(Availability::Optional),
1638                    )
1639                    .offer(
1640                        OfferBuilder::dictionary()
1641                            .name("required_dict")
1642                            .source(OfferSource::Self_)
1643                            .target_static_child("mid"),
1644                    )
1645                    .offer(
1646                        OfferBuilder::dictionary()
1647                            .name("optional_dict")
1648                            .source(OfferSource::Self_)
1649                            .target_static_child("mid")
1650                            .availability(Availability::Optional),
1651                    )
1652                    .offer(
1653                        OfferBuilder::dictionary()
1654                            .name("dict_with_optional_nested")
1655                            .source(OfferSource::Self_)
1656                            .target_static_child("mid"),
1657                    )
1658                    .child_default("mid")
1659                    .build(),
1660            ),
1661            (
1662                "mid",
1663                ComponentDeclBuilder::new()
1664                    .offer(
1665                        OfferBuilder::protocol()
1666                            .name("A")
1667                            .source(OfferSource::Parent)
1668                            .target_static_child("leaf")
1669                            .from_dictionary("required_dict"),
1670                    )
1671                    .offer(
1672                        OfferBuilder::protocol()
1673                            .name("B")
1674                            .source(OfferSource::Parent)
1675                            .target_static_child("leaf")
1676                            .from_dictionary("optional_dict"),
1677                    )
1678                    .offer(
1679                        OfferBuilder::protocol()
1680                            .name("C")
1681                            .source(OfferSource::Parent)
1682                            .target_static_child("leaf")
1683                            .from_dictionary("dict_with_optional_nested/nested"),
1684                    )
1685                    .child_default("leaf")
1686                    .build(),
1687            ),
1688            (
1689                "leaf",
1690                ComponentDeclBuilder::new()
1691                    .use_(UseBuilder::protocol().name("A"))
1692                    .use_(UseBuilder::protocol().name("B"))
1693                    .use_(UseBuilder::protocol().name("C"))
1694                    .build(),
1695            ),
1696        ];
1697
1698        let test = T::new("root", components).build().await;
1699        test.check_use(
1700            "mid/leaf".try_into().unwrap(),
1701            CheckUse::Protocol {
1702                path: "/svc/A".parse().unwrap(),
1703                expected_res: ExpectedResult::Err(Status::NOT_FOUND),
1704            },
1705        )
1706        .await;
1707        test.check_use(
1708            "mid/leaf".try_into().unwrap(),
1709            CheckUse::Protocol {
1710                path: "/svc/B".parse().unwrap(),
1711                expected_res: ExpectedResult::Err(Status::NOT_FOUND),
1712            },
1713        )
1714        .await;
1715        test.check_use(
1716            "mid/leaf".try_into().unwrap(),
1717            CheckUse::Protocol {
1718                path: "/svc/C".parse().unwrap(),
1719                expected_res: ExpectedResult::Err(Status::NOT_FOUND),
1720            },
1721        )
1722        .await;
1723    }
1724
1725    pub async fn test_expose_from_dictionary_availability_attenuated(&self) {
1726        // required -> optional downgrade allowed, of:
1727        // - a capability in a dictionary
1728        // - a capability in a dictionary in a dictionary
1729        let components = vec![
1730            (
1731                "root",
1732                ComponentDeclBuilder::new()
1733                    .use_(
1734                        UseBuilder::protocol()
1735                            .source_static_child("leaf")
1736                            .name("A")
1737                            .availability(Availability::Optional),
1738                    )
1739                    .use_(
1740                        UseBuilder::protocol()
1741                            .source_static_child("leaf")
1742                            .name("B")
1743                            .availability(Availability::Optional),
1744                    )
1745                    .child_default("leaf")
1746                    .build(),
1747            ),
1748            (
1749                "leaf",
1750                ComponentDeclBuilder::new()
1751                    .protocol_default("foo")
1752                    .protocol_default("bar")
1753                    .dictionary_default("nested")
1754                    .dictionary_default("dict")
1755                    .offer(
1756                        OfferBuilder::protocol()
1757                            .name("foo")
1758                            .target_name("A")
1759                            .source(OfferSource::Self_)
1760                            .target(OfferTarget::Capability("dict".parse().unwrap())),
1761                    )
1762                    .offer(
1763                        OfferBuilder::protocol()
1764                            .name("bar")
1765                            .target_name("B")
1766                            .source(OfferSource::Self_)
1767                            .target(OfferTarget::Capability("nested".parse().unwrap())),
1768                    )
1769                    .offer(
1770                        OfferBuilder::dictionary()
1771                            .name("nested")
1772                            .source(OfferSource::Self_)
1773                            .target(OfferTarget::Capability("dict".parse().unwrap())),
1774                    )
1775                    .expose(
1776                        ExposeBuilder::protocol()
1777                            .name("A")
1778                            .from_dictionary("dict")
1779                            .source(ExposeSource::Self_),
1780                    )
1781                    .expose(
1782                        ExposeBuilder::protocol()
1783                            .name("B")
1784                            .from_dictionary("dict/nested")
1785                            .source(ExposeSource::Self_),
1786                    )
1787                    .build(),
1788            ),
1789        ];
1790
1791        let test = T::new("root", components).build().await;
1792        test.check_use(
1793            ".".try_into().unwrap(),
1794            CheckUse::Protocol {
1795                path: "/svc/A".parse().unwrap(),
1796                expected_res: ExpectedResult::Ok,
1797            },
1798        )
1799        .await;
1800        test.check_use(
1801            ".".try_into().unwrap(),
1802            CheckUse::Protocol {
1803                path: "/svc/B".parse().unwrap(),
1804                expected_res: ExpectedResult::Ok,
1805            },
1806        )
1807        .await;
1808    }
1809
1810    pub async fn test_expose_from_dictionary_availability_invalid(&self) {
1811        // attempted optional -> required upgrade, disallowed, of:
1812        // - an optional capability in a dictionary.
1813        // - a capability in an optional dictionary.
1814        // - a capability in a dictionary in an optional dictionary.
1815        let components = vec![
1816            (
1817                "root",
1818                ComponentDeclBuilder::new()
1819                    .use_(UseBuilder::protocol().source_static_child("mid").name("A"))
1820                    .use_(UseBuilder::protocol().source_static_child("mid").name("B"))
1821                    .use_(UseBuilder::protocol().source_static_child("mid").name("C"))
1822                    .child_default("mid")
1823                    .build(),
1824            ),
1825            (
1826                "mid",
1827                ComponentDeclBuilder::new()
1828                    .expose(
1829                        ExposeBuilder::protocol()
1830                            .name("A")
1831                            .from_dictionary("required_dict")
1832                            .source_static_child("leaf"),
1833                    )
1834                    .expose(
1835                        ExposeBuilder::protocol()
1836                            .name("B")
1837                            .from_dictionary("optional_dict")
1838                            .source_static_child("leaf"),
1839                    )
1840                    .expose(
1841                        ExposeBuilder::protocol()
1842                            .name("C")
1843                            .from_dictionary("dict_with_optional_nested/nested")
1844                            .source_static_child("leaf"),
1845                    )
1846                    .child_default("leaf")
1847                    .build(),
1848            ),
1849            (
1850                "leaf",
1851                ComponentDeclBuilder::new()
1852                    .protocol_default("foo")
1853                    .protocol_default("bar")
1854                    .protocol_default("qux")
1855                    .dictionary_default("required_dict")
1856                    .dictionary_default("optional_dict")
1857                    .dictionary_default("nested")
1858                    .dictionary_default("dict")
1859                    .dictionary_default("dict_with_optional_nested")
1860                    .offer(
1861                        OfferBuilder::protocol()
1862                            .name("foo")
1863                            .target_name("A")
1864                            .source(OfferSource::Self_)
1865                            .target(OfferTarget::Capability("dict".parse().unwrap()))
1866                            .availability(Availability::Optional),
1867                    )
1868                    .offer(
1869                        OfferBuilder::protocol()
1870                            .name("bar")
1871                            .target_name("B")
1872                            .source(OfferSource::Self_)
1873                            .target(OfferTarget::Capability("optional_dict".parse().unwrap())),
1874                    )
1875                    .offer(
1876                        OfferBuilder::protocol()
1877                            .name("qux")
1878                            .target_name("C")
1879                            .source(OfferSource::Self_)
1880                            .target(OfferTarget::Capability("nested".parse().unwrap())),
1881                    )
1882                    .offer(
1883                        OfferBuilder::dictionary()
1884                            .name("nested")
1885                            .source(OfferSource::Self_)
1886                            .target(OfferTarget::Capability(
1887                                "dict_with_optional_nested".parse().unwrap(),
1888                            ))
1889                            .availability(Availability::Optional),
1890                    )
1891                    .expose(
1892                        ExposeBuilder::dictionary()
1893                            .name("required_dict")
1894                            .source(ExposeSource::Self_),
1895                    )
1896                    .expose(
1897                        ExposeBuilder::dictionary()
1898                            .name("optional_dict")
1899                            .source(ExposeSource::Self_)
1900                            .availability(Availability::Optional),
1901                    )
1902                    .expose(
1903                        ExposeBuilder::dictionary()
1904                            .name("dict_with_optional_nested")
1905                            .source(ExposeSource::Self_),
1906                    )
1907                    .build(),
1908            ),
1909        ];
1910
1911        let test = T::new("root", components).build().await;
1912        test.check_use(
1913            ".".try_into().unwrap(),
1914            CheckUse::Protocol {
1915                path: "/svc/A".parse().unwrap(),
1916                expected_res: ExpectedResult::Err(Status::NOT_FOUND),
1917            },
1918        )
1919        .await;
1920        test.check_use(
1921            ".".try_into().unwrap(),
1922            CheckUse::Protocol {
1923                path: "/svc/B".parse().unwrap(),
1924                expected_res: ExpectedResult::Err(Status::NOT_FOUND),
1925            },
1926        )
1927        .await;
1928        test.check_use(
1929            ".".try_into().unwrap(),
1930            CheckUse::Protocol {
1931                path: "/svc/C".parse().unwrap(),
1932                expected_res: ExpectedResult::Err(Status::NOT_FOUND),
1933            },
1934        )
1935        .await;
1936    }
1937
1938    pub async fn test_use_runner_from_dictionary(&self) {
1939        let components = vec![
1940            (
1941                "root",
1942                ComponentDeclBuilder::new()
1943                    .runner_default("foo")
1944                    .dictionary_default("dict")
1945                    .offer(
1946                        OfferBuilder::runner()
1947                            .name("foo")
1948                            .target_name("bar")
1949                            .source(OfferSource::Self_)
1950                            .target(OfferTarget::Capability("dict".parse().unwrap())),
1951                    )
1952                    .offer(
1953                        OfferBuilder::dictionary()
1954                            .name("dict")
1955                            .source(OfferSource::Self_)
1956                            .target_static_child("mid"),
1957                    )
1958                    .child_default("mid")
1959                    .build(),
1960            ),
1961            (
1962                "mid",
1963                ComponentDeclBuilder::new_empty_component()
1964                    .use_(UseBuilder::runner().name("bar").from_dictionary("dict"))
1965                    .build(),
1966            ),
1967        ];
1968
1969        let test = T::new("root", components).build().await;
1970        let mid_component =
1971            test.look_up_instance(&["mid"].try_into().unwrap()).await.expect("mid instance");
1972        let source = debug_route_sandbox_path(
1973            &mid_component,
1974            &UseDecl::Runner(UseRunnerDecl {
1975                source: UseSource::Parent,
1976                source_name: "bar".parse().unwrap(),
1977                source_dictionary: "dict".parse().unwrap(),
1978            }),
1979        )
1980        .await
1981        .expect("failed to route runner");
1982
1983        match source {
1984            capability_source::CapabilitySource::Component(
1985                capability_source::ComponentSource {
1986                    capability:
1987                        capability_source::ComponentCapability::Runner(RunnerDecl { name, .. }),
1988                    moniker,
1989                },
1990            ) => {
1991                assert_eq!(name, "foo");
1992                assert_eq!(moniker, Moniker::root());
1993            }
1994            _ => panic!("unexpected source: {:?}", source),
1995        }
1996    }
1997
1998    pub async fn test_offer_runner_from_dictionary(&self) {
1999        let components = vec![
2000            (
2001                "root",
2002                ComponentDeclBuilder::new()
2003                    .runner_default("foo")
2004                    .dictionary_default("dict")
2005                    .offer(
2006                        OfferBuilder::runner()
2007                            .name("foo")
2008                            .target_name("bar")
2009                            .source(OfferSource::Self_)
2010                            .target(OfferTarget::Capability("dict".parse().unwrap())),
2011                    )
2012                    .offer(
2013                        OfferBuilder::dictionary()
2014                            .name("dict")
2015                            .source(OfferSource::Self_)
2016                            .target_static_child("mid"),
2017                    )
2018                    .child_default("mid")
2019                    .build(),
2020            ),
2021            (
2022                "mid",
2023                ComponentDeclBuilder::new()
2024                    .offer(
2025                        OfferBuilder::runner()
2026                            .name("bar")
2027                            .target_name("baz")
2028                            .from_dictionary("dict")
2029                            .source(OfferSource::Parent)
2030                            .target_static_child("leaf"),
2031                    )
2032                    .child_default("leaf")
2033                    .build(),
2034            ),
2035            (
2036                "leaf",
2037                ComponentDeclBuilder::new_empty_component()
2038                    .use_(UseBuilder::runner().name("baz"))
2039                    .build(),
2040            ),
2041        ];
2042
2043        let test = T::new("root", components).build().await;
2044        let leaf_component = test
2045            .look_up_instance(&["mid", "leaf"].try_into().unwrap())
2046            .await
2047            .expect("leaf instance");
2048        let source = debug_route_sandbox_path(
2049            &leaf_component,
2050            &UseDecl::Runner(UseRunnerDecl {
2051                source: UseSource::Parent,
2052                source_name: "baz".parse().unwrap(),
2053                source_dictionary: Default::default(),
2054            }),
2055        )
2056        .await
2057        .expect("failed to route runner");
2058
2059        match source {
2060            capability_source::CapabilitySource::Component(
2061                capability_source::ComponentSource {
2062                    capability:
2063                        capability_source::ComponentCapability::Runner(RunnerDecl { name, .. }),
2064                    moniker,
2065                },
2066            ) => {
2067                assert_eq!(name, "foo");
2068                assert_eq!(moniker, Moniker::root());
2069            }
2070            _ => panic!("unexpected source: {:?}", source),
2071        }
2072    }
2073
2074    pub async fn test_offer_directory_from_dictionary(&self) {
2075        let use_decl =
2076            UseBuilder::directory().name("baz").rights(fio::R_STAR_DIR).path("/foo").build();
2077        let components = vec![
2078            (
2079                "root",
2080                ComponentDeclBuilder::new()
2081                    .capability(
2082                        CapabilityBuilder::directory()
2083                            .name("foo")
2084                            .rights(fio::R_STAR_DIR)
2085                            .path("/foo"),
2086                    )
2087                    .dictionary_default("dict")
2088                    .offer(
2089                        OfferBuilder::directory()
2090                            .name("foo")
2091                            .target_name("bar")
2092                            .source(OfferSource::Self_)
2093                            .target(OfferTarget::Capability("dict".parse().unwrap())),
2094                    )
2095                    .offer(
2096                        OfferBuilder::dictionary()
2097                            .name("dict")
2098                            .source(OfferSource::Self_)
2099                            .target_static_child("mid"),
2100                    )
2101                    .child_default("mid")
2102                    .build(),
2103            ),
2104            (
2105                "mid",
2106                ComponentDeclBuilder::new()
2107                    .offer(
2108                        OfferBuilder::directory()
2109                            .name("bar")
2110                            .target_name("baz")
2111                            .from_dictionary("dict")
2112                            .source(OfferSource::Parent)
2113                            .target_static_child("leaf"),
2114                    )
2115                    .child_default("leaf")
2116                    .build(),
2117            ),
2118            ("leaf", ComponentDeclBuilder::new().use_(use_decl.clone()).build()),
2119        ];
2120
2121        let test = T::new("root", components).build().await;
2122        let leaf_component = test
2123            .look_up_instance(&["mid", "leaf"].try_into().unwrap())
2124            .await
2125            .expect("leaf instance");
2126
2127        let source = debug_route_sandbox_path(&leaf_component, &use_decl)
2128            .await
2129            .expect("failed to route resolver");
2130
2131        match source {
2132            capability_source::CapabilitySource::StorageBackingDirectory(
2133                capability_source::StorageBackingDirectorySource {
2134                    capability:
2135                        capability_source::ComponentCapability::Directory(DirectoryDecl {
2136                            name, ..
2137                        }),
2138                    moniker,
2139                    ..
2140                },
2141            ) => {
2142                assert_eq!(name, "foo");
2143                assert_eq!(moniker, Moniker::root());
2144            }
2145            _ => panic!("unexpected source: {:?}", source),
2146        }
2147    }
2148
2149    pub async fn test_use_config_from_dictionary(&self) {
2150        let components = vec![
2151            (
2152                "root",
2153                ComponentDeclBuilder::new()
2154                    .capability(CapabilityBuilder::config().name("foo").value(10_i32.into()))
2155                    .dictionary_default("dict")
2156                    .offer(
2157                        OfferBuilder::config()
2158                            .name("foo")
2159                            .target_name("bar")
2160                            .source(OfferSource::Self_)
2161                            .target(OfferTarget::Capability("dict".parse().unwrap())),
2162                    )
2163                    .offer(
2164                        OfferBuilder::dictionary()
2165                            .name("dict")
2166                            .source(OfferSource::Self_)
2167                            .target_static_child("leaf"),
2168                    )
2169                    .child_default("leaf")
2170                    .build(),
2171            ),
2172            (
2173                "leaf",
2174                ComponentDeclBuilder::new()
2175                    .use_(
2176                        UseBuilder::config()
2177                            .name("bar")
2178                            .config_type(ConfigValueType::Int32)
2179                            .from_dictionary("dict"),
2180                    )
2181                    .build(),
2182            ),
2183        ];
2184
2185        let test = T::new("root", components).build().await;
2186        let leaf_component =
2187            test.look_up_instance(&["leaf"].try_into().unwrap()).await.expect("leaf instance");
2188        let source = debug_route_sandbox_path(
2189            &leaf_component,
2190            &UseDecl::Config(Box::new(UseConfigurationDecl {
2191                source: UseSource::Parent,
2192                source_name: "bar".parse().unwrap(),
2193                target_name: "bar".parse().unwrap(),
2194                availability: Availability::Required,
2195                type_: ConfigValueType::Int32,
2196                default: None,
2197                source_dictionary: "dict".parse().unwrap(),
2198            })),
2199        )
2200        .await
2201        .expect("failed to route config");
2202
2203        match source {
2204            capability_source::CapabilitySource::Component(
2205                capability_source::ComponentSource {
2206                    capability: capability_source::ComponentCapability::Config(_),
2207                    moniker,
2208                },
2209            ) => {
2210                assert_eq!(moniker, moniker::Moniker::root());
2211            }
2212            _ => panic!("unexpected source: {:?}", source),
2213        }
2214    }
2215
2216    pub async fn test_offer_config_from_dictionary(&self) {
2217        let components = vec![
2218            (
2219                "root",
2220                ComponentDeclBuilder::new()
2221                    .capability(CapabilityBuilder::config().name("foo").value(10_i32.into()))
2222                    .dictionary_default("dict")
2223                    .offer(
2224                        OfferBuilder::config()
2225                            .name("foo")
2226                            .target_name("bar")
2227                            .source(OfferSource::Self_)
2228                            .target(OfferTarget::Capability("dict".parse().unwrap())),
2229                    )
2230                    .offer(
2231                        OfferBuilder::dictionary()
2232                            .name("dict")
2233                            .source(OfferSource::Self_)
2234                            .target_static_child("mid"),
2235                    )
2236                    .child_default("mid")
2237                    .build(),
2238            ),
2239            (
2240                "mid",
2241                ComponentDeclBuilder::new()
2242                    .offer(
2243                        OfferBuilder::config()
2244                            .name("bar")
2245                            .target_name("baz")
2246                            .from_dictionary("dict")
2247                            .source(OfferSource::Parent)
2248                            .target_static_child("leaf"),
2249                    )
2250                    .child_default("leaf")
2251                    .build(),
2252            ),
2253            (
2254                "leaf",
2255                ComponentDeclBuilder::new()
2256                    .use_(UseBuilder::config().name("baz").config_type(ConfigValueType::Int32))
2257                    .build(),
2258            ),
2259        ];
2260
2261        let test = T::new("root", components).build().await;
2262        let leaf_component = test
2263            .look_up_instance(&["mid", "leaf"].try_into().unwrap())
2264            .await
2265            .expect("leaf instance");
2266        let source = debug_route_sandbox_path(
2267            &leaf_component,
2268            &UseDecl::Config(Box::new(UseConfigurationDecl {
2269                source: UseSource::Parent,
2270                source_name: "baz".parse().unwrap(),
2271                target_name: "baz".parse().unwrap(),
2272                availability: Availability::Required,
2273                type_: ConfigValueType::Int32,
2274                default: None,
2275                source_dictionary: Default::default(),
2276            })),
2277        )
2278        .await
2279        .expect("failed to route config");
2280
2281        match source {
2282            capability_source::CapabilitySource::Component(
2283                capability_source::ComponentSource {
2284                    capability: capability_source::ComponentCapability::Config(_),
2285                    moniker,
2286                },
2287            ) => {
2288                assert_eq!(moniker, moniker::Moniker::root());
2289            }
2290            _ => panic!("unexpected source: {:?}", source),
2291        }
2292    }
2293}