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