iquery_test_support/
test_support.rs

1// Copyright 2022 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 byteorder::{LittleEndian, WriteBytesExt};
6use fdomain_client::AsHandleRef;
7use fdomain_client::fidl::{RequestStream as FRequestStream, ServerEnd as FServerEnd};
8use fidl::endpoints::{ServerEnd, create_endpoints, create_proxy};
9use fidl_fuchsia_component_decl::{
10    Capability, Component, Dictionary, Expose, ExposeDictionary, ExposeProtocol, ParentRef,
11    Protocol, Ref, SelfRef,
12};
13use futures::{StreamExt, TryStreamExt};
14use moniker::Moniker;
15use std::collections::HashMap;
16use std::io::Write;
17use std::path::Path;
18use std::rc::Rc;
19use std::str::FromStr;
20use std::sync::atomic::{AtomicBool, Ordering};
21use zx_status::Status;
22use {
23    fdomain_fuchsia_io as fio_f, fdomain_fuchsia_sys2 as fsys2_f, fidl_fuchsia_io as fio,
24    fidl_fuchsia_sys2 as fsys2,
25};
26
27/// Builder struct for `RealmQueryResult`/
28/// This is an builder interface meant to simplify building of test fixtures.
29/// Example usage:
30/// ```
31///   MockRealmQuery.add()
32///   .when("other/component") // when client queries for this string ("other/component").
33///   .moniker("./other/component") // Returns the following.
34///   .exposes(vec![Expose::Protocol(ExposeProtocol {
35///       source: Some(Ref::Self_(SelfRef)),
36///       target: Some(Ref::Self_(SelfRef)),
37///       source_name: Some("src".to_owned()),
38///       target_name: Some("fuchsia.io.SomeOtherThing".to_owned()),
39///       ..Default::default()
40///   })])
41///   .add() // Finish building the result.
42///   .when("some/thing") // Start another build.
43///   ...
44/// ```
45#[derive(Default)]
46pub struct MockRealmQueryBuilder {
47    mapping: HashMap<String, Box<MockRealmQueryBuilderInner>>,
48}
49
50/// Inner struct of `MockRealmQueryBuilder` to provide a builder interface for
51/// RealmQuery protocol responses.
52pub struct MockRealmQueryBuilderInner {
53    when: Moniker,
54    moniker: Moniker,
55    exposes: Vec<Expose>,
56    parent: Option<Box<MockRealmQueryBuilder>>,
57}
58
59impl MockRealmQueryBuilderInner {
60    /// Sets the result moniker.
61    pub fn moniker(mut self, moniker: &str) -> Self {
62        self.moniker = moniker.try_into().unwrap();
63        self
64    }
65
66    /// Sets the result vector of `Expose`s.
67    pub fn exposes(mut self, exposes: Vec<Expose>) -> Self {
68        self.exposes = exposes;
69        self
70    }
71
72    /// Completes the build and returns a `MockRealmQueryBuilder`.
73    pub fn add(mut self) -> MockRealmQueryBuilder {
74        let mut parent = *self.parent.unwrap();
75        self.parent = None;
76
77        parent.mapping.insert(self.when.to_string(), Box::new(self));
78        parent
79    }
80
81    pub fn serve_exposed_dir_f(&self, server_end: FServerEnd<fio_f::DirectoryMarker>, path: &str) {
82        let mut mock_dir_top = MockDir::new("expose".to_owned());
83        let mut mock_accessors = MockDir::new("diagnostics-accessors".to_owned());
84        for expose in &self.exposes {
85            let Expose::Protocol(ExposeProtocol {
86                source_name: Some(name), source_dictionary, ..
87            }) = expose
88            else {
89                continue;
90            };
91            if matches!(source_dictionary, Some(d) if d == "diagnostics-accessors") {
92                mock_accessors = mock_accessors.add_entry(MockFile::new_arc(name.to_owned()));
93            }
94        }
95
96        match path {
97            "diagnostics-accessors" => {
98                fuchsia_async::Task::local(async move {
99                    Rc::new(mock_accessors).serve_f(server_end).await
100                })
101                .detach();
102            }
103            _ => {
104                mock_dir_top = mock_dir_top.add_entry(Rc::new(mock_accessors));
105                fuchsia_async::Task::local(async move {
106                    Rc::new(mock_dir_top).serve_f(server_end).await
107                })
108                .detach();
109            }
110        }
111    }
112
113    pub fn serve_exposed_dir(&self, server_end: ServerEnd<fio::DirectoryMarker>, path: &str) {
114        let mut mock_dir_top = MockDir::new("expose".to_owned());
115        let mut mock_accessors = MockDir::new("diagnostics-accessors".to_owned());
116        for expose in &self.exposes {
117            let Expose::Protocol(ExposeProtocol {
118                source_name: Some(name), source_dictionary, ..
119            }) = expose
120            else {
121                continue;
122            };
123            if matches!(source_dictionary, Some(d) if d == "diagnostics-accessors") {
124                mock_accessors = mock_accessors.add_entry(MockFile::new_arc(name.to_owned()));
125            }
126        }
127
128        match path {
129            "diagnostics-accessors" => {
130                fuchsia_async::Task::local(async move {
131                    Rc::new(mock_accessors).serve(server_end).await
132                })
133                .detach();
134            }
135            _ => {
136                mock_dir_top = mock_dir_top.add_entry(Rc::new(mock_accessors));
137                fuchsia_async::Task::local(
138                    async move { Rc::new(mock_dir_top).serve(server_end).await },
139                )
140                .detach();
141            }
142        }
143    }
144
145    fn to_instance(&self) -> fsys2::Instance {
146        fsys2::Instance {
147            moniker: Some(self.moniker.to_string()),
148            url: Some("".to_owned()),
149            instance_id: None,
150            resolved_info: Some(fsys2::ResolvedInfo {
151                resolved_url: Some("".to_owned()),
152                ..Default::default()
153            }),
154            ..Default::default()
155        }
156    }
157
158    fn make_manifest(&self) -> Component {
159        let capabilities = self
160            .exposes
161            .iter()
162            .map(|expose| match expose {
163                Expose::Protocol(ExposeProtocol {
164                    source_name: Some(name),
165                    source: Some(Ref::Self_(SelfRef)),
166                    ..
167                }) => Capability::Protocol(Protocol {
168                    name: Some(name.clone()),
169                    source_path: Some(format!("/svc/{name}")),
170                    ..Protocol::default()
171                }),
172                Expose::Dictionary(ExposeDictionary {
173                    source_name: Some(name),
174                    source: Some(Ref::Self_(SelfRef)),
175                    ..
176                }) => Capability::Dictionary(Dictionary {
177                    name: Some(name.clone()),
178                    source: Some(Ref::Self_(SelfRef)),
179                    ..Dictionary::default()
180                }),
181                _ => unreachable!("we just add protocols for the test purposes"),
182            })
183            .collect::<Vec<_>>();
184        Component {
185            capabilities: Some(capabilities),
186            exposes: Some(self.exposes.clone()),
187            ..Default::default()
188        }
189    }
190}
191
192impl MockRealmQueryBuilder {
193    /// Create a new empty `MockRealmQueryBuilder`.
194    pub fn new() -> Self {
195        Self::default()
196    }
197
198    /// Start a build of `RealmQueryResult` by specifying the
199    /// expected query string.
200    pub fn when(self, at: &str) -> MockRealmQueryBuilderInner {
201        MockRealmQueryBuilderInner {
202            when: at.try_into().unwrap(),
203            moniker: Moniker::root(),
204            exposes: vec![],
205            parent: Some(Box::new(self)),
206        }
207    }
208
209    /// Finish the build and return servable `MockRealmQuery`.
210    pub fn build(self) -> MockRealmQuery {
211        MockRealmQuery { mapping: self.mapping }
212    }
213
214    pub fn prefilled() -> Self {
215        Self::new()
216            .when("example/component")
217            .moniker("./example/component")
218            .exposes(vec![
219                Expose::Protocol(ExposeProtocol {
220                    source: Some(Ref::Self_(SelfRef)),
221                    target: Some(Ref::Parent(ParentRef)),
222                    source_name: Some("fuchsia.diagnostics.ArchiveAccessor".to_owned()),
223                    target_name: Some("fuchsia.diagnostics.ArchiveAccessor".to_owned()),
224                    source_dictionary: Some("diagnostics-accessors".to_owned()),
225                    ..Default::default()
226                }),
227                Expose::Dictionary(ExposeDictionary {
228                    source_name: Some("diagnostics-accessors".into()),
229                    target_name: Some("diagnostics-accessors".into()),
230                    source: Some(Ref::Self_(SelfRef)),
231                    target: Some(Ref::Parent(ParentRef)),
232                    ..Default::default()
233                }),
234            ])
235            .add()
236            .when("other/component")
237            .moniker("./other/component")
238            .exposes(vec![Expose::Protocol(ExposeProtocol {
239                source: Some(Ref::Self_(SelfRef)),
240                target: Some(Ref::Parent(ParentRef)),
241                source_name: Some("src".to_owned()),
242                target_name: Some("fuchsia.io.SomeOtherThing".to_owned()),
243                ..Default::default()
244            })])
245            .add()
246            .when("other/component")
247            .moniker("./other/component")
248            .exposes(vec![Expose::Protocol(ExposeProtocol {
249                source: Some(Ref::Self_(SelfRef)),
250                target: Some(Ref::Parent(ParentRef)),
251                source_name: Some("src".to_owned()),
252                target_name: Some("fuchsia.io.MagicStuff".to_owned()),
253                ..Default::default()
254            })])
255            .add()
256            .when("foo/component")
257            .moniker("./foo/component")
258            .exposes(vec![
259                Expose::Protocol(ExposeProtocol {
260                    source: Some(Ref::Self_(SelfRef)),
261                    target: Some(Ref::Parent(ParentRef)),
262                    source_name: Some("fuchsia.diagnostics.ArchiveAccessor.feedback".to_owned()),
263                    target_name: Some("fuchsia.diagnostics.ArchiveAccessor.feedback".to_owned()),
264                    source_dictionary: Some("diagnostics-accessors".to_owned()),
265                    ..Default::default()
266                }),
267                Expose::Dictionary(ExposeDictionary {
268                    source_name: Some("diagnostics-accessors".into()),
269                    target_name: Some("diagnostics-accessors".into()),
270                    source: Some(Ref::Self_(SelfRef)),
271                    target: Some(Ref::Parent(ParentRef)),
272                    ..Default::default()
273                }),
274            ])
275            .add()
276            .when("foo/bar/thing:instance")
277            .moniker("./foo/bar/thing:instance")
278            .exposes(vec![
279                Expose::Protocol(ExposeProtocol {
280                    source: Some(Ref::Self_(SelfRef)),
281                    target: Some(Ref::Parent(ParentRef)),
282                    source_name: Some("fuchsia.diagnostics.ArchiveAccessor.feedback".to_owned()),
283                    target_name: Some("fuchsia.diagnostics.ArchiveAccessor.feedback".to_owned()),
284                    source_dictionary: Some("diagnostics-accessors".to_owned()),
285                    ..Default::default()
286                }),
287                Expose::Dictionary(ExposeDictionary {
288                    source_name: Some("diagnostics-accessors".into()),
289                    target_name: Some("diagnostics-accessors".into()),
290                    source: Some(Ref::Self_(SelfRef)),
291                    target: Some(Ref::Parent(ParentRef)),
292                    ..Default::default()
293                }),
294            ])
295            .add()
296    }
297}
298
299/// Provides a mock `RealmQuery` interface.
300pub struct MockRealmQuery {
301    /// Mapping from Moniker -> Expose.
302    mapping: HashMap<String, Box<MockRealmQueryBuilderInner>>,
303}
304
305/// Creates the default test fixures for `MockRealmQuery`.
306impl Default for MockRealmQuery {
307    fn default() -> Self {
308        MockRealmQueryBuilder::prefilled().build()
309    }
310}
311
312impl MockRealmQuery {
313    /// Serves the `RealmQuery` interface asynchronously and runs until the program terminates.
314    pub async fn serve_f(self: Rc<Self>, object: FServerEnd<fsys2_f::RealmQueryMarker>) {
315        let client = object.domain();
316        let mut stream = object.into_stream();
317        while let Ok(Some(request)) = stream.try_next().await {
318            match request {
319                fsys2_f::RealmQueryRequest::GetInstance { moniker, responder } => {
320                    let query_moniker = Moniker::from_str(moniker.as_str()).unwrap();
321                    let res = self.mapping.get(&query_moniker.to_string()).unwrap();
322                    responder.send(Ok(&res.to_instance())).unwrap();
323                }
324                fsys2_f::RealmQueryRequest::OpenDirectory {
325                    moniker,
326                    dir_type,
327                    object,
328                    responder,
329                    ..
330                } => {
331                    let query_moniker = Moniker::from_str(moniker.as_str()).unwrap();
332                    if let Some(res) = self.mapping.get(&query_moniker.to_string()) {
333                        if dir_type == fsys2_f::OpenDirType::OutgoingDir {
334                            // Serve the out dir, everything else doesn't get served.
335                            res.serve_exposed_dir_f(object, "");
336                        }
337                        responder.send(Ok(())).unwrap();
338                    } else {
339                        responder.send(Err(fsys2_f::OpenError::InstanceNotFound)).unwrap();
340                    }
341                }
342                fsys2_f::RealmQueryRequest::GetResolvedDeclaration {
343                    moniker, responder, ..
344                } => {
345                    let query_moniker = Moniker::from_str(moniker.as_str()).unwrap();
346                    let res = self.mapping.get(&query_moniker.to_string()).unwrap();
347                    let manifest = res.make_manifest();
348                    let manifest = fidl::persist(&manifest).unwrap();
349                    let (client_end, server_end) =
350                        client.create_endpoints::<fsys2_f::ManifestBytesIteratorMarker>();
351
352                    fuchsia_async::Task::spawn(async move {
353                        let mut stream = server_end.into_stream();
354                        let fsys2_f::ManifestBytesIteratorRequest::Next { responder } =
355                            stream.next().await.unwrap().unwrap();
356                        responder.send(manifest.as_slice()).unwrap();
357                        let fsys2_f::ManifestBytesIteratorRequest::Next { responder } =
358                            stream.next().await.unwrap().unwrap();
359                        responder.send(&[]).unwrap();
360                    })
361                    .detach();
362
363                    responder.send(Ok(client_end)).unwrap();
364                }
365                fsys2_f::RealmQueryRequest::GetAllInstances { responder } => {
366                    let instances: Vec<fsys2_f::Instance> =
367                        self.mapping.values().map(|m| m.to_instance()).collect();
368
369                    let (client_end, server_end) =
370                        client.create_endpoints::<fsys2_f::InstanceIteratorMarker>();
371
372                    fuchsia_async::Task::spawn(async move {
373                        let mut stream = server_end.into_stream();
374                        let fsys2_f::InstanceIteratorRequest::Next { responder } =
375                            stream.next().await.unwrap().unwrap();
376                        responder.send(&instances).unwrap();
377                        let fsys2_f::InstanceIteratorRequest::Next { responder } =
378                            stream.next().await.unwrap().unwrap();
379                        responder.send(&[]).unwrap();
380                    })
381                    .detach();
382
383                    responder.send(Ok(client_end)).unwrap();
384                }
385                _ => unreachable!("request {:?}", request),
386            }
387        }
388    }
389
390    /// Serves the `RealmQuery` interface asynchronously and runs until the program terminates.
391    pub async fn serve(self: Rc<Self>, object: ServerEnd<fsys2::RealmQueryMarker>) {
392        let mut stream = object.into_stream();
393        while let Ok(Some(request)) = stream.try_next().await {
394            match request {
395                fsys2::RealmQueryRequest::GetInstance { moniker, responder } => {
396                    let query_moniker = Moniker::from_str(moniker.as_str()).unwrap();
397                    let res = self.mapping.get(&query_moniker.to_string()).unwrap();
398                    responder.send(Ok(&res.to_instance())).unwrap();
399                }
400                fsys2::RealmQueryRequest::OpenDirectory {
401                    moniker,
402                    dir_type,
403                    object,
404                    responder,
405                    ..
406                } => {
407                    let query_moniker = Moniker::from_str(moniker.as_str()).unwrap();
408                    if let Some(res) = self.mapping.get(&query_moniker.to_string()) {
409                        if dir_type == fsys2::OpenDirType::ExposedDir {
410                            // Serve the out dir, everything else doesn't get served.
411                            res.serve_exposed_dir(object, "");
412                        }
413                        responder.send(Ok(())).unwrap();
414                    } else {
415                        responder.send(Err(fsys2::OpenError::InstanceNotFound)).unwrap();
416                    }
417                }
418                fsys2::RealmQueryRequest::GetResolvedDeclaration { moniker, responder, .. } => {
419                    let query_moniker = Moniker::from_str(moniker.as_str()).unwrap();
420                    let res = self.mapping.get(&query_moniker.to_string()).unwrap();
421                    let manifest = res.make_manifest();
422                    let manifest = fidl::persist(&manifest).unwrap();
423                    let (client_end, server_end) =
424                        create_endpoints::<fsys2::ManifestBytesIteratorMarker>();
425
426                    fuchsia_async::Task::spawn(async move {
427                        let mut stream = server_end.into_stream();
428                        let fsys2::ManifestBytesIteratorRequest::Next { responder } =
429                            stream.next().await.unwrap().unwrap();
430                        responder.send(manifest.as_slice()).unwrap();
431                        let fsys2::ManifestBytesIteratorRequest::Next { responder } =
432                            stream.next().await.unwrap().unwrap();
433                        responder.send(&[]).unwrap();
434                    })
435                    .detach();
436
437                    responder.send(Ok(client_end)).unwrap();
438                }
439                fsys2::RealmQueryRequest::GetAllInstances { responder } => {
440                    let instances: Vec<fsys2::Instance> =
441                        self.mapping.values().map(|m| m.to_instance()).collect();
442
443                    let (client_end, server_end) =
444                        create_endpoints::<fsys2::InstanceIteratorMarker>();
445
446                    fuchsia_async::Task::spawn(async move {
447                        let mut stream = server_end.into_stream();
448                        let fsys2::InstanceIteratorRequest::Next { responder } =
449                            stream.next().await.unwrap().unwrap();
450                        responder.send(&instances).unwrap();
451                        let fsys2::InstanceIteratorRequest::Next { responder } =
452                            stream.next().await.unwrap().unwrap();
453                        responder.send(&[]).unwrap();
454                    })
455                    .detach();
456
457                    responder.send(Ok(client_end)).unwrap();
458                }
459                _ => unreachable!("request {:?}", request),
460            }
461        }
462    }
463
464    /// Serves the `RealmQuery` interface asynchronously and runs until the program terminates.
465    /// Then, instead of needing the client to discover the protocol, return the proxy for futher
466    /// test use.
467    pub async fn get_proxy(self: Rc<Self>) -> fsys2::RealmQueryProxy {
468        let (proxy, server_end) = create_proxy::<fsys2::RealmQueryMarker>();
469        fuchsia_async::Task::local(async move { self.serve(server_end).await }).detach();
470        proxy
471    }
472}
473
474// Mock directory structure.
475pub trait Entry {
476    fn open(self: Rc<Self>, path: &str, object: fidl::Channel);
477    fn open_f(self: Rc<Self>, path: &str, object: fdomain_client::Channel);
478    fn encode(&self, buf: &mut Vec<u8>);
479    fn name(&self) -> String;
480}
481
482pub struct MockDir {
483    subdirs: HashMap<String, Rc<dyn Entry>>,
484    name: String,
485    at_end: AtomicBool,
486}
487
488impl MockDir {
489    pub fn new(name: String) -> Self {
490        MockDir { name, subdirs: HashMap::new(), at_end: AtomicBool::new(false) }
491    }
492
493    pub fn new_arc(name: String) -> Rc<Self> {
494        Rc::new(Self::new(name))
495    }
496
497    pub fn add_entry(mut self, entry: Rc<dyn Entry>) -> Self {
498        self.subdirs.insert(entry.name(), entry);
499        self
500    }
501
502    async fn serve_f(self: Rc<Self>, object: FServerEnd<fio_f::DirectoryMarker>) {
503        let mut stream = object.into_stream();
504        let _ = stream.control_handle().send_on_open_(
505            Status::OK.into_raw(),
506            Some(fio_f::NodeInfoDeprecated::Directory(fio_f::DirectoryObject {})),
507        );
508        while let Ok(Some(request)) = stream.try_next().await {
509            match request {
510                fio_f::DirectoryRequest::Open { path, object, .. } => {
511                    self.clone().open_f(&path, object);
512                }
513                fio_f::DirectoryRequest::Rewind { responder, .. } => {
514                    self.at_end.store(false, Ordering::Relaxed);
515                    responder.send(Status::OK.into_raw()).unwrap();
516                }
517                fio_f::DirectoryRequest::ReadDirents { max_bytes: _, responder, .. } => {
518                    let entries = match self.at_end.compare_exchange(
519                        false,
520                        true,
521                        Ordering::Relaxed,
522                        Ordering::Relaxed,
523                    ) {
524                        Ok(false) => encode_entries(&self.subdirs),
525                        Err(true) => Vec::new(),
526                        _ => unreachable!(),
527                    };
528                    responder.send(Status::OK.into_raw(), &entries).unwrap();
529                }
530                x => panic!("unsupported request: {x:?}"),
531            }
532        }
533    }
534
535    async fn serve(self: Rc<Self>, object: ServerEnd<fio::DirectoryMarker>) {
536        let mut stream = object.into_stream();
537        while let Ok(Some(request)) = stream.try_next().await {
538            match request {
539                fio::DirectoryRequest::Open { path, object, .. } => {
540                    self.clone().open(&path, object);
541                }
542                fio::DirectoryRequest::Rewind { responder, .. } => {
543                    self.at_end.store(false, Ordering::Relaxed);
544                    responder.send(Status::OK.into_raw()).unwrap();
545                }
546                fio::DirectoryRequest::ReadDirents { max_bytes: _, responder, .. } => {
547                    let entries = match self.at_end.compare_exchange(
548                        false,
549                        true,
550                        Ordering::Relaxed,
551                        Ordering::Relaxed,
552                    ) {
553                        Ok(false) => encode_entries(&self.subdirs),
554                        Err(true) => Vec::new(),
555                        _ => unreachable!(),
556                    };
557                    responder.send(Status::OK.into_raw(), &entries).unwrap();
558                }
559                x => panic!("unsupported request: {x:?}"),
560            }
561        }
562    }
563}
564
565fn encode_entries(subdirs: &HashMap<String, Rc<dyn Entry>>) -> Vec<u8> {
566    let mut buf = Vec::new();
567    let mut data = subdirs.iter().collect::<Vec<(_, _)>>();
568    data.sort_by(|a, b| a.0.cmp(b.0));
569    for (_, entry) in data.iter() {
570        entry.encode(&mut buf);
571    }
572    buf
573}
574
575impl Entry for MockDir {
576    fn open(self: Rc<Self>, path: &str, object: fidl::Channel) {
577        let path = Path::new(path);
578        let mut path_iter = path.iter();
579        let segment = if let Some(segment) = path_iter.next() {
580            if let Some(segment) = segment.to_str() {
581                segment
582            } else {
583                let _ =
584                    ServerEnd::<fio::NodeMarker>::new(object).close_with_epitaph(Status::NOT_FOUND);
585                return;
586            }
587        } else {
588            "."
589        };
590        if segment == "." {
591            fuchsia_async::Task::local(self.serve(ServerEnd::new(object))).detach();
592            return;
593        }
594        if let Some(entry) = self.subdirs.get(segment) {
595            entry.clone().open(path_iter.as_path().to_str().unwrap(), object);
596        } else {
597            let _ = ServerEnd::<fio::NodeMarker>::new(object).close_with_epitaph(Status::NOT_FOUND);
598        }
599    }
600
601    fn open_f(self: Rc<Self>, path: &str, object: fdomain_client::Channel) {
602        let path = Path::new(path);
603        let mut path_iter = path.iter();
604        let segment = if let Some(segment) = path_iter.next() {
605            if let Some(segment) = segment.to_str() {
606                segment
607            } else {
608                let _ = FServerEnd::<fio_f::NodeMarker>::new(object)
609                    .close_with_epitaph(Status::NOT_FOUND);
610                return;
611            }
612        } else {
613            "."
614        };
615        if segment == "." {
616            fuchsia_async::Task::local(self.serve_f(FServerEnd::new(object))).detach();
617            return;
618        }
619        if let Some(entry) = self.subdirs.get(segment) {
620            entry.clone().open_f(path_iter.as_path().to_str().unwrap(), object);
621        } else {
622            let _ =
623                FServerEnd::<fio_f::NodeMarker>::new(object).close_with_epitaph(Status::NOT_FOUND);
624        }
625    }
626
627    fn encode(&self, buf: &mut Vec<u8>) {
628        buf.write_u64::<LittleEndian>(fio::INO_UNKNOWN).expect("writing mockdir ino to work");
629        buf.write_u8(self.name.len() as u8).expect("writing mockdir size to work");
630        buf.write_u8(fio::DirentType::Directory.into_primitive())
631            .expect("writing mockdir type to work");
632        buf.write_all(self.name.as_ref()).expect("writing mockdir name to work");
633    }
634
635    fn name(&self) -> String {
636        self.name.clone()
637    }
638}
639
640struct MockFile {
641    name: String,
642}
643
644impl MockFile {
645    pub fn new(name: String) -> Self {
646        MockFile { name }
647    }
648    pub fn new_arc(name: String) -> Rc<Self> {
649        Rc::new(Self::new(name))
650    }
651}
652
653impl Entry for MockFile {
654    fn open(self: Rc<Self>, _path: &str, _object: fidl::Channel) {
655        unimplemented!();
656    }
657
658    fn open_f(self: Rc<Self>, _path: &str, _object: fdomain_client::Channel) {
659        unimplemented!();
660    }
661
662    fn encode(&self, buf: &mut Vec<u8>) {
663        buf.write_u64::<LittleEndian>(fio::INO_UNKNOWN).expect("writing mockdir ino to work");
664        buf.write_u8(self.name.len() as u8).expect("writing mockdir size to work");
665        buf.write_u8(fio::DirentType::Service.into_primitive())
666            .expect("writing mockdir type to work");
667        buf.write_all(self.name.as_ref()).expect("writing mockdir name to work");
668    }
669
670    fn name(&self) -> String {
671        self.name.clone()
672    }
673}