Skip to main content

sandbox/
lib.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
5//! A library for interacting with the `fuchsia.component.sandbox` FIDL APIs.
6//!
7//! This library provides type-safe wrappers around the raw FIDL types for
8//! capabilities and the capability store, simplifying their use.
9
10use fidl_fuchsia_component_sandbox as fsandbox;
11use fidl_fuchsia_io as fio;
12use fuchsia_component::client;
13use std::future::Future;
14use std::sync::Arc;
15use std::sync::atomic::{AtomicU64, Ordering};
16use thiserror::Error;
17
18/// A trait for value types that can be imported into a [`CapabilityStore`], such as
19/// String, u64, and fsandbox::DictionaryRef.
20pub trait Importable<'a>: Sized + Into<Capability> + TryFrom<Capability, Error = Error> {
21    /// The type of CapabilityRef that will be returned on import.
22    type Ref: CapabilityRef<'a>;
23}
24
25/// An error that can occur when interacting with the sandbox APIs.
26#[derive(Error, Clone, Debug)]
27pub enum Error {
28    /// Failed to connect to `fuchsia.component.sandbox.CapabilityStore`.
29    #[error("failed to connect to fuchsia.component.sandbox/CapabilityStore: {0}")]
30    FailedConnect(zx::Status),
31    /// An operation expected a capability of a certain type, but received a different one.
32    #[error("wrong Capability type; got \"{got}\", want \"{want}\"")]
33    WrongType { got: &'static str, want: &'static str },
34    /// A FIDL transport error occurred.
35    #[error("FIDL error {0}")]
36    Fidl(#[from] fidl::Error),
37    /// An error returned from the `fuchsia.component.sandbox.CapabilityStore` protocol.
38    #[error("CapabilityStoreError {0:?}")]
39    CapabilityStore(fsandbox::CapabilityStoreError),
40}
41
42impl From<fsandbox::CapabilityStoreError> for Error {
43    fn from(value: fsandbox::CapabilityStoreError) -> Self {
44        Self::CapabilityStore(value)
45    }
46}
47
48/// A helper struct to generate unused capability IDs.
49///
50/// This is clonable and thread-safe. There should generally be one of these
51/// for each [`CapabilityStore`] connection.
52#[derive(Clone, Default, Debug)]
53pub struct CapabilityIdGenerator {
54    next_id: Arc<AtomicU64>,
55}
56
57impl CapabilityIdGenerator {
58    /// Creates a new ID generator.
59    pub fn new() -> Self {
60        Self::default()
61    }
62
63    /// Get the next free id.
64    pub fn next(&self) -> u64 {
65        self.range(1)
66    }
67
68    /// Get a range of free ids of size `size`.
69    /// This returns the first id in the range.
70    pub fn range(&self, size: u64) -> u64 {
71        self.next_id.fetch_add(size, Ordering::Relaxed)
72    }
73}
74
75/// A wrapper over [`fsandbox::CapabilityStoreProxy`] providing self-contained types.
76#[derive(Clone, Debug)]
77pub struct CapabilityStore {
78    proxy: fsandbox::CapabilityStoreProxy,
79    id_gen: CapabilityIdGenerator,
80}
81
82impl<'a> CapabilityStore {
83    /// Connects to the `fuchsia.component.sandbox/CapabilityStore` protocol.
84    pub fn connect() -> Result<Self, Error> {
85        let proxy = client::connect_to_protocol::<fsandbox::CapabilityStoreMarker>().map_err(
86            |e| match e.downcast_ref() {
87                Some(status) => Error::FailedConnect(*status),
88                None => Error::FailedConnect(zx::Status::INTERNAL),
89            },
90        )?;
91        Ok(Self { proxy, id_gen: CapabilityIdGenerator::default() })
92    }
93
94    /// Imports a capability into the store.
95    pub async fn import<T>(&'a self, value: T) -> Result<T::Ref, Error>
96    where
97        T: Importable<'a>,
98    {
99        let id = self.id_gen.next();
100        self.proxy.import(id, value.into().0).await??;
101        Ok(T::Ref::from_store(self, id))
102    }
103
104    /// Creates a new, empty dictionary in the capability store.
105    pub async fn create_dictionary(&'a self) -> Result<Dictionary<'a>, Error> {
106        let id = self.id_gen.next();
107        self.proxy.dictionary_create(id).await??;
108        Ok(Dictionary { store: self, id })
109    }
110
111    /// Creates a new [`Connector`] capability from a `Receiver` client end.
112    pub async fn create_connector(
113        &'a self,
114        client_end: fidl::endpoints::ClientEnd<fsandbox::ReceiverMarker>,
115    ) -> Result<Connector<'a>, Error> {
116        let id = self.id_gen.next();
117        self.proxy.connector_create(id, client_end).await??;
118        Ok(Connector { store: self, id })
119    }
120
121    /// Creates a new [`DirConnector`] capability from a `DirReceiver` client end.
122    pub async fn create_dir_connector(
123        &'a self,
124        client_end: fidl::endpoints::ClientEnd<fsandbox::DirReceiverMarker>,
125    ) -> Result<DirConnector<'a>, Error> {
126        let id = self.id_gen.next();
127        self.proxy.dir_connector_create(id, client_end).await??;
128        Ok(DirConnector { store: self, id })
129    }
130}
131
132/// A reference to a [`fsandbox::Capability`] stored in a repository held by the
133/// component framework.
134///
135/// This reference represents a capability of an unconfirmed type. Specify type
136/// parameters on `export()` methods to derive the type.
137pub trait CapabilityRef<'a>: Sized {
138    /// Create a [`CapabilityRef`] referencing the given store and id.
139    ///
140    /// Warning: If the capability stored in the store at the given key is not
141    /// of type `T`, the type mismatch will only become apparent when attempting
142    /// to call [`CapabilityRef::export`] or call type-specific methods on it.
143    fn from_store(store: &'a CapabilityStore, id: fsandbox::CapabilityId) -> Self;
144
145    /// Return a reference to the store containing this Capability.
146    fn store(&self) -> &'a CapabilityStore;
147
148    /// Return the identifier for this Capability.
149    fn id(&self) -> fsandbox::CapabilityId;
150
151    /// Duplicates the capability referenced by this ref, returning a new
152    /// ref to the duplicated capability.
153    fn duplicate(&'a self) -> impl Future<Output = Result<Self, Error>> {
154        async move {
155            let dup_id = self.store().id_gen.next();
156            self.store().proxy.duplicate(self.id(), dup_id).await??;
157            Ok(Self::from_store(self.store(), dup_id))
158        }
159    }
160
161    /// Drop the referenced Capability held by the component framework runtime.
162    fn drop(self) -> impl Future<Output = Result<(), Error>> {
163        async move { Ok(self.store().proxy.drop(self.id()).await??) }
164    }
165
166    /// Extract the value of the referenced Capability and remove it from the
167    /// component framework runtime.
168    fn export<T>(self) -> impl Future<Output = Result<T, Error>>
169    where
170        T: Importable<'a, Ref = Self>,
171    {
172        async move {
173            let cap = self.store().proxy.export(self.id()).await??;
174            T::try_from(cap.into())
175        }
176    }
177}
178
179/// A type-safe wrapper for a [`fsandbox::Capability`].
180///
181/// This enum is used to represent different kinds of capabilities that can be
182/// stored in the [`CapabilityStore`]. See the [`From`] implementations for how to
183/// create a [`Capability`] from a specific type like [`Dictionary`] or [`Data`].
184#[derive(Debug)]
185pub struct Capability(fsandbox::Capability);
186
187impl From<Capability> for fsandbox::Capability {
188    fn from(value: Capability) -> Self {
189        value.0
190    }
191}
192
193impl From<fsandbox::Capability> for Capability {
194    fn from(value: fsandbox::Capability) -> Self {
195        Self(value)
196    }
197}
198
199/// Returns the type name of a capability as a static string.
200fn capability_type_name(cap: fsandbox::Capability) -> &'static str {
201    match cap {
202        fsandbox::Capability::Unit(_) => "Unit",
203        fsandbox::Capability::Handle(_) => "Handle",
204        fsandbox::Capability::Data(data) => match data {
205            fsandbox::Data::Bytes(_) => "Data::Bytes",
206            fsandbox::Data::String(_) => "Data::String",
207            fsandbox::Data::Int64(_) => "Data::Int64",
208            fsandbox::Data::Uint64(_) => "Data::Uint64",
209            _ => "Data::Unknown",
210        },
211        fsandbox::Capability::Dictionary(_) => "Dictionary",
212        fsandbox::Capability::Connector(_) => "Connector",
213        fsandbox::Capability::DirConnector(_) => "DirConnector",
214        fsandbox::Capability::Directory(_) => "Directory",
215        fsandbox::Capability::DirEntry(_) => "DirEntry",
216        fsandbox::Capability::ConnectorRouter(_) => "ConnectorRouter",
217        fsandbox::Capability::DictionaryRouter(_) => "DictionaryRouter",
218        fsandbox::Capability::DirEntryRouter(_) => "DirEntryRouter",
219        fsandbox::Capability::DataRouter(_) => "DataRouter",
220        fsandbox::Capability::DirConnectorRouter(_) => "DirConnectorRouter",
221        _ => "Unknown",
222    }
223}
224
225/// Generate a struct that implements [`CapabilityRef`]. Optionally generate
226/// a [`Importable`] implementation for `Item`.
227macro_rules! impl_capability_ref {
228    // Generate struct and impls for Item.
229    (
230        $(#[$attr:meta])*
231        $ref:ident, $t:ty, $variant:path
232    ) => {
233        impl_capability_ref!(
234            $(#[$attr])*
235            $ref
236        );
237
238        impl<'a> Importable<'a> for $t {
239            type Ref = $ref<'a>;
240        }
241
242        impl From<$t> for Capability {
243            fn from(value: $t) -> Self {
244                Self($variant(value))
245            }
246        }
247
248        impl TryFrom<Capability> for $t {
249            type Error = Error;
250            fn try_from(value: Capability) -> Result<Self, Self::Error> {
251                match value.0 {
252                    $variant(inner) => Ok(inner),
253                    capability => Err(Error::WrongType {
254                        got: capability_type_name(capability),
255                        want: stringify!($t),
256                    }),
257                }
258            }
259        }
260    };
261    // Generate struct.
262    (
263        $(#[$attr:meta])*
264        $ref:ident
265    ) => {
266        $(#[$attr])*
267        #[derive(Debug)]
268        pub struct $ref<'a> {
269            store: &'a CapabilityStore,
270            id: fsandbox::CapabilityId,
271        }
272
273        impl<'a> CapabilityRef<'a> for $ref<'a> {
274            fn from_store(store: &'a CapabilityStore, id: fsandbox::CapabilityId) -> Self {
275                Self { store, id }
276            }
277
278            fn store(&self) -> &'a CapabilityStore {
279                self.store
280            }
281
282            fn id(&self) -> fsandbox::CapabilityId {
283                self.id
284            }
285        }
286    };
287}
288
289impl_capability_ref!(
290    /// A reference to a Unit capability in the store.
291    Unit, fsandbox::Unit, fsandbox::Capability::Unit
292);
293
294impl_capability_ref!(
295    /// A reference to a Zircon handle capability in the store.
296    Handle, fidl::NullableHandle, fsandbox::Capability::Handle
297);
298
299impl_capability_ref!(
300    /// A reference to a Data capability in the store.
301    ///
302    /// The underlying Data in the component framework runtime holds primitive
303    /// types like strings, integers, and byte vectors.
304    Data
305);
306
307impl_capability_ref!(
308    /// A reference to a Dictionary capability in the store.
309    ///
310    /// Component framework dictionaries are mutable collections of other
311    /// [`fsandbox::Capability`], keyed by strings.
312    Dictionary, fsandbox::DictionaryRef, fsandbox::Capability::Dictionary
313);
314
315impl<'a> Dictionary<'a> {
316    /// Inserts a capability into the dictionary with the given key.
317    pub async fn insert<V>(&self, key: impl Into<String>, value: V) -> Result<(), Error>
318    where
319        V: CapabilityRef<'a>,
320    {
321        self.store
322            .proxy
323            .dictionary_insert(
324                self.id,
325                &fsandbox::DictionaryItem { key: key.into(), value: value.id() },
326            )
327            .await??;
328        Ok(())
329    }
330
331    /// Retrieves a capability from the dictionary by its key.
332    pub async fn get<T>(&self, key: impl AsRef<str>) -> Result<T, Error>
333    where
334        T: CapabilityRef<'a>,
335    {
336        let id = self.store.id_gen.next();
337        self.store.proxy.dictionary_get(self.id, key.as_ref(), id).await??;
338        Ok(T::from_store(self.store, id))
339    }
340}
341
342impl_capability_ref!(
343    /// A reference to a Connector capability in the store.
344    ///
345    /// A [`Connector`] can be used to receive a channel from a client component.
346    Connector, fsandbox::Connector, fsandbox::Capability::Connector
347);
348
349impl<'a> Connector<'a> {
350    /// Open the underlying connection to `Receiver`.
351    pub async fn open(
352        &self,
353        server_end: fidl::endpoints::ServerEnd<fsandbox::ReceiverMarker>,
354    ) -> Result<(), Error> {
355        Ok(self.store.proxy.connector_open(self.id, server_end.into_channel()).await??)
356    }
357}
358
359impl_capability_ref!(
360    /// A reference to a DirConnector capability in the store.
361    DirConnector, fsandbox::DirConnector, fsandbox::Capability::DirConnector
362);
363
364impl<'a> DirConnector<'a> {
365    /// Open the underlying connection to `Directory`.
366    pub async fn open(
367        &self,
368        server_end: fidl::endpoints::ServerEnd<fio::DirectoryMarker>,
369    ) -> Result<(), Error> {
370        Ok(self
371            .store
372            .proxy
373            .dir_connector_open(fsandbox::CapabilityStoreDirConnectorOpenRequest {
374                id: Some(self.id),
375                server_end: Some(server_end),
376                flags: None,
377                path: None,
378                ..Default::default()
379            })
380            .await??)
381    }
382}
383
384impl_capability_ref!(
385    /// A reference to a Directory capability in the store.
386    Directory, fidl::endpoints::ClientEnd<fio::DirectoryMarker>, fsandbox::Capability::Directory
387);
388
389impl_capability_ref!(
390    /// A reference to a DirEntry capability in the store.
391    DirEntry, fsandbox::DirEntry, fsandbox::Capability::DirEntry
392);
393
394impl_capability_ref!(
395    /// A reference to a ConnectorRouter capability in the store.
396    ConnectorRouter, fidl::endpoints::ClientEnd<fsandbox::ConnectorRouterMarker>, fsandbox::Capability::ConnectorRouter
397);
398
399impl_capability_ref!(
400    /// A reference to a DictionaryRouter capability in the store.
401    DictionaryRouter, fidl::endpoints::ClientEnd<fsandbox::DictionaryRouterMarker>, fsandbox::Capability::DictionaryRouter
402);
403
404impl_capability_ref!(
405    /// A reference to a DirEntryRouter capability in the store.
406    DirEntryRouter, fidl::endpoints::ClientEnd<fsandbox::DirEntryRouterMarker>, fsandbox::Capability::DirEntryRouter
407);
408
409impl_capability_ref!(
410    /// A reference to a DataRouter capability in the store.
411    DataRouter, fidl::endpoints::ClientEnd<fsandbox::DataRouterMarker>, fsandbox::Capability::DataRouter
412);
413
414impl_capability_ref!(
415    /// A reference to a DirConnectorRouter capability in the store.
416    DirConnectorRouter, fidl::endpoints::ClientEnd<fsandbox::DirConnectorRouterMarker>, fsandbox::Capability::DirConnectorRouter
417);
418
419/// Implement TryFrom on [`fsandbox::Capability::Data`] variants.
420macro_rules! impl_data_capability {
421    ($t:ty, $variant:path, $variant_type_name:expr) => {
422        impl<'a> Importable<'a> for $t {
423            type Ref = Data<'a>;
424        }
425
426        impl From<$t> for Capability {
427            fn from(value: $t) -> Self {
428                Self(fsandbox::Capability::Data($variant(value)))
429            }
430        }
431
432        impl TryFrom<Capability> for $t {
433            type Error = Error;
434            fn try_from(value: Capability) -> Result<Self, Self::Error> {
435                match value.0 {
436                    fsandbox::Capability::Data($variant(inner)) => Ok(inner),
437                    capability => Err(Error::WrongType {
438                        got: capability_type_name(capability),
439                        want: $variant_type_name,
440                    }),
441                }
442            }
443        }
444    };
445}
446
447impl_data_capability!(Vec<u8>, fsandbox::Data::Bytes, "Data::Bytes");
448impl_data_capability!(String, fsandbox::Data::String, "Data::String");
449impl_data_capability!(i64, fsandbox::Data::Int64, "Data::Int64");
450impl_data_capability!(u64, fsandbox::Data::Uint64, "Data::Uint64");
451
452#[cfg(test)]
453mod tests {
454    use super::*;
455    use assert_matches::assert_matches;
456    use fidl::endpoints::create_proxy_and_stream;
457    use fuchsia_async as fasync;
458    use futures::future::FutureExt;
459    use futures::prelude::*;
460    use futures::task::Poll;
461    use std::pin::Pin;
462    use test_case::test_case;
463    use zx::AsHandleRef;
464
465    fn setup_store() -> (CapabilityStore, fsandbox::CapabilityStoreRequestStream) {
466        let (proxy, stream) = create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
467        let store = CapabilityStore { proxy, id_gen: CapabilityIdGenerator::default() };
468        (store, stream)
469    }
470
471    async fn wait_for_request<F, T>(f: &mut Pin<Box<F>>)
472    where
473        F: Future<Output = T> + ?Sized,
474        T: std::fmt::Debug,
475    {
476        assert_matches!(fasync::TestExecutor::poll_until_stalled(f.as_mut()).await, Poll::Pending);
477    }
478
479    #[test]
480    fn capability_id_generator() {
481        let id_gen = CapabilityIdGenerator::new();
482        assert_eq!(id_gen.next(), 0);
483        assert_eq!(id_gen.next(), 1);
484        assert_eq!(id_gen.range(5), 2);
485        assert_eq!(id_gen.next(), 7);
486    }
487
488    #[test_case("hello".to_string(); "string")]
489    #[test_case(123u64; "u64")]
490    #[test_case(-123i64; "i64")]
491    #[test_case(vec![1, 2, 3]; "bytes")]
492    fn data_capability_conversions<T>(val: T)
493    where
494        T: for<'a> Importable<'a, Ref = Data<'a>> + Clone + PartialEq + std::fmt::Debug,
495    {
496        let cap: Capability = val.clone().into();
497        let val_again: T = cap.try_into().unwrap();
498        assert_eq!(val_again, val);
499    }
500
501    #[test]
502    fn data_capability_conversions_wrong_type() {
503        let cap: Capability = "hello".to_string().into();
504        let res: Result<u64, _> = cap.try_into();
505        assert_matches!(res, Err(Error::WrongType { got: "Data::String", want: "Data::Uint64" }));
506    }
507
508    #[test_case("hello".to_string(); "string")]
509    #[test_case(123u64; "u64")]
510    #[test_case(-123i64; "i64")]
511    #[test_case(vec![1, 2, 3]; "bytes")]
512    #[fuchsia::test(allow_stalls = false)]
513    async fn capability_store_import_export<T>(val: T)
514    where
515        T: for<'a> Importable<'a, Ref: std::fmt::Debug> + Clone + PartialEq + std::fmt::Debug,
516    {
517        let (store, mut stream) = setup_store();
518
519        // Test import
520        let mut import_fut = store.import(val.clone()).boxed_local();
521        wait_for_request(&mut import_fut).await;
522
523        let (capability, responder) = assert_matches!(
524            stream.next().await.unwrap().unwrap(),
525            fsandbox::CapabilityStoreRequest::Import { id, capability, responder } if id == 0 => (capability, responder)
526        );
527
528        // Check that the imported capability matches the value.
529        let imported_val: T = Capability(capability).try_into().unwrap();
530        assert_eq!(imported_val, val);
531
532        responder.send(Ok(())).unwrap();
533        let cap = import_fut.await.unwrap();
534        assert_eq!(cap.id(), 0);
535
536        // Test export
537        let mut export_fut = cap.export::<T>().boxed_local();
538        wait_for_request(&mut export_fut).await;
539
540        let responder = assert_matches!(
541            stream.next().await.unwrap().unwrap(),
542            fsandbox::CapabilityStoreRequest::Export { id, responder } if id == 0 => responder
543        );
544
545        let cap_to_return: Capability = val.clone().into();
546        responder.send(Ok(cap_to_return.into())).unwrap();
547
548        let result = export_fut.await.unwrap();
549        assert_eq!(result, val);
550    }
551
552    #[fuchsia::test(allow_stalls = false)]
553    async fn capability_ref_duplicate() {
554        let (store, mut stream) = setup_store();
555
556        // Simulate an existing capability with id 100
557        let data = Data { id: 100, store: &store };
558
559        let mut duplicate_fut = data.duplicate().boxed_local();
560        wait_for_request(&mut duplicate_fut).await;
561
562        let responder = assert_matches!(
563            stream.next().await.unwrap().unwrap(),
564            fsandbox::CapabilityStoreRequest::Duplicate { id, dest_id, responder } if id == 100 && dest_id == 0 => responder
565        );
566
567        responder.send(Ok(())).unwrap();
568        let new_data: Data<'_> = duplicate_fut.await.unwrap();
569        assert_eq!(new_data.id, 0);
570        assert_eq!(store.id_gen.next(), 1); // next id is 1
571    }
572
573    #[fuchsia::test(allow_stalls = false)]
574    async fn capability_ref_drop() {
575        let (store, mut stream) = setup_store();
576        let data = Data { id: 100, store: &store };
577
578        let mut drop_fut = data.drop().boxed_local();
579        wait_for_request(&mut drop_fut).await;
580
581        let responder = assert_matches!(
582            stream.next().await.unwrap().unwrap(),
583            fsandbox::CapabilityStoreRequest::Drop { id, responder } if id == 100 => responder
584        );
585
586        responder.send(Ok(())).unwrap();
587        drop_fut.await.unwrap();
588    }
589
590    #[fuchsia::test(allow_stalls = false)]
591    async fn dictionary_create() {
592        let (store, mut stream) = setup_store();
593
594        let mut create_fut = store.create_dictionary().boxed_local();
595        wait_for_request(&mut create_fut).await;
596
597        let responder = assert_matches!(
598            stream.next().await.unwrap().unwrap(),
599            fsandbox::CapabilityStoreRequest::DictionaryCreate { id, responder } if id == 0 => responder
600        );
601
602        responder.send(Ok(())).unwrap();
603        let dict = create_fut.await.unwrap();
604        assert_eq!(dict.id, 0);
605    }
606
607    #[fuchsia::test(allow_stalls = false)]
608    async fn dictionary_insert() {
609        let (store, mut stream) = setup_store();
610        let dict = Dictionary { id: 10, store: &store };
611        let data = Data { id: 20, store: &store };
612
613        let mut insert_fut = dict.insert("key", data).boxed_local();
614        wait_for_request(&mut insert_fut).await;
615
616        let responder = assert_matches!(
617            stream.next().await.unwrap().unwrap(),
618            fsandbox::CapabilityStoreRequest::DictionaryInsert { id, item, responder } if id == 10 && item.key == "key" && item.value == 20 => responder
619        );
620
621        responder.send(Ok(())).unwrap();
622        insert_fut.await.unwrap();
623    }
624
625    #[fuchsia::test(allow_stalls = false)]
626    async fn dictionary_get() {
627        let (store, mut stream) = setup_store();
628        let dict = Dictionary { id: 10, store: &store };
629
630        let mut get_fut = dict.get::<Data<'_>>("key").boxed_local();
631        wait_for_request(&mut get_fut).await;
632
633        let responder = assert_matches!(
634            stream.next().await.unwrap().unwrap(),
635            fsandbox::CapabilityStoreRequest::DictionaryGet { id, key, dest_id, responder } if id == 10 && key == "key" && dest_id == 0 => responder
636        );
637
638        responder.send(Ok(())).unwrap();
639        let data = get_fut.await.unwrap();
640        assert_eq!(data.id, 0);
641    }
642
643    #[fuchsia::test(allow_stalls = false)]
644    async fn dictionary_export() {
645        let (store, mut stream) = setup_store();
646        let dict = Dictionary { id: 10, store: &store };
647
648        let mut export_fut = dict.export().boxed_local();
649        wait_for_request(&mut export_fut).await;
650
651        let responder = assert_matches!(
652            stream.next().await.unwrap().unwrap(),
653            fsandbox::CapabilityStoreRequest::Export { id, responder } if id == 10 => responder
654        );
655
656        let (client, _server) = zx::EventPair::create();
657        let cap = fsandbox::Capability::Dictionary(fsandbox::DictionaryRef { token: client });
658        responder.send(Ok(cap)).unwrap();
659
660        let result: fsandbox::DictionaryRef = export_fut.await.unwrap();
661        assert_eq!(result.token.as_handle_ref().is_invalid(), false);
662    }
663
664    #[fuchsia::test(allow_stalls = false)]
665    async fn connector_create() {
666        let (store, mut stream) = setup_store();
667        let (client_end, _server_end) =
668            fidl::endpoints::create_endpoints::<fsandbox::ReceiverMarker>();
669
670        let mut create_fut = store.create_connector(client_end).boxed_local();
671        wait_for_request(&mut create_fut).await;
672
673        let responder = assert_matches!(
674            stream.next().await.unwrap().unwrap(),
675            fsandbox::CapabilityStoreRequest::ConnectorCreate { id, receiver, responder } if id == 0 && receiver.as_handle_ref().is_invalid() == false => responder
676        );
677
678        responder.send(Ok(())).unwrap();
679        let connector = create_fut.await.unwrap();
680        assert_eq!(connector.id, 0);
681    }
682
683    #[fuchsia::test(allow_stalls = false)]
684    async fn connector_open() {
685        let (store, mut stream) = setup_store();
686        let connector = Connector { id: 10, store: &store };
687
688        let (client_end, server_end) =
689            fidl::endpoints::create_endpoints::<fsandbox::ReceiverMarker>();
690        let mut open_fut = connector.open(server_end).boxed_local();
691        wait_for_request(&mut open_fut).await;
692
693        let responder = assert_matches!(
694            stream.next().await.unwrap().unwrap(),
695            fsandbox::CapabilityStoreRequest::ConnectorOpen { id, server_end, responder } if id == 10 && server_end.as_handle_ref().is_invalid() == false => responder
696        );
697
698        responder.send(Ok(())).unwrap();
699        open_fut.await.unwrap();
700        assert_eq!(client_end.as_handle_ref().is_invalid(), false);
701    }
702
703    #[fuchsia::test(allow_stalls = false)]
704    async fn dir_connector_create() {
705        let (store, mut stream) = setup_store();
706        let (client_end, _server_end) =
707            fidl::endpoints::create_endpoints::<fsandbox::DirReceiverMarker>();
708
709        let mut create_fut = store.create_dir_connector(client_end).boxed_local();
710        wait_for_request(&mut create_fut).await;
711
712        let responder = assert_matches!(
713            stream.next().await.unwrap().unwrap(),
714            fsandbox::CapabilityStoreRequest::DirConnectorCreate { id, receiver, responder } if id == 0 && receiver.as_handle_ref().is_invalid() == false => responder
715        );
716
717        responder.send(Ok(())).unwrap();
718        let connector = create_fut.await.unwrap();
719        assert_eq!(connector.id, 0);
720    }
721
722    #[fuchsia::test(allow_stalls = false)]
723    async fn dir_connector_open() {
724        let (store, mut stream) = setup_store();
725        let connector = DirConnector { id: 10, store: &store };
726
727        let (client_end, server_end) = fidl::endpoints::create_endpoints::<fio::DirectoryMarker>();
728        let mut open_fut = connector.open(server_end).boxed_local();
729        wait_for_request(&mut open_fut).await;
730
731        let responder = assert_matches!(
732            stream.next().await.unwrap().unwrap(),
733            fsandbox::CapabilityStoreRequest::DirConnectorOpen { payload, responder } if payload.id == Some(10) && payload.server_end.as_ref().expect("server_end").as_handle_ref().is_invalid() == false => responder
734        );
735
736        responder.send(Ok(())).unwrap();
737        open_fut.await.unwrap();
738        assert_eq!(client_end.as_handle_ref().is_invalid(), false);
739    }
740}