1use byteorder::{LittleEndian, WriteBytesExt};
6use fdomain_client::fidl::{RequestStream as FRequestStream, ServerEnd as FServerEnd};
7use fdomain_client::AsHandleRef;
8use fidl::endpoints::{create_endpoints, create_proxy, ServerEnd};
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#[derive(Default)]
46pub struct MockRealmQueryBuilder {
47 mapping: HashMap<String, Box<MockRealmQueryBuilderInner>>,
48}
49
50pub struct MockRealmQueryBuilderInner {
53 when: Moniker,
54 moniker: Moniker,
55 exposes: Vec<Expose>,
56 parent: Option<Box<MockRealmQueryBuilder>>,
57}
58
59impl MockRealmQueryBuilderInner {
60 pub fn moniker(mut self, moniker: &str) -> Self {
62 self.moniker = moniker.try_into().unwrap();
63 self
64 }
65
66 pub fn exposes(mut self, exposes: Vec<Expose>) -> Self {
68 self.exposes = exposes;
69 self
70 }
71
72 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 pub fn new() -> Self {
195 Self::default()
196 }
197
198 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 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
299pub struct MockRealmQuery {
301 mapping: HashMap<String, Box<MockRealmQueryBuilderInner>>,
303}
304
305impl Default for MockRealmQuery {
307 fn default() -> Self {
308 MockRealmQueryBuilder::prefilled().build()
309 }
310}
311
312impl MockRealmQuery {
313 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::Open {
325 moniker,
326 dir_type,
327 object,
328 responder,
329 path,
330 ..
331 } => {
332 let query_moniker = Moniker::from_str(moniker.as_str()).unwrap();
333 if let Some(res) = self.mapping.get(&query_moniker.to_string()) {
334 if dir_type == fsys2_f::OpenDirType::ExposedDir {
335 res.serve_exposed_dir_f(object.into_channel().into(), &path);
337 }
338 responder.send(Ok(())).unwrap();
339 } else {
340 responder.send(Err(fsys2_f::OpenError::InstanceNotFound)).unwrap();
341 }
342 }
343 fsys2_f::RealmQueryRequest::OpenDirectory {
344 moniker,
345 dir_type,
346 object,
347 responder,
348 ..
349 } => {
350 let query_moniker = Moniker::from_str(moniker.as_str()).unwrap();
351 if let Some(res) = self.mapping.get(&query_moniker.to_string()) {
352 if dir_type == fsys2_f::OpenDirType::OutgoingDir {
353 res.serve_exposed_dir_f(object, "");
355 }
356 responder.send(Ok(())).unwrap();
357 } else {
358 responder.send(Err(fsys2_f::OpenError::InstanceNotFound)).unwrap();
359 }
360 }
361 fsys2_f::RealmQueryRequest::GetManifest { moniker, responder, .. } => {
362 let query_moniker = Moniker::from_str(moniker.as_str()).unwrap();
363 let res = self.mapping.get(&query_moniker.to_string()).unwrap();
364 let manifest = res.make_manifest();
365 let manifest = fidl::persist(&manifest).unwrap();
366 let (client_end, server_end) =
367 client.create_endpoints::<fsys2_f::ManifestBytesIteratorMarker>();
368
369 fuchsia_async::Task::spawn(async move {
370 let mut stream = server_end.into_stream();
371 let fsys2_f::ManifestBytesIteratorRequest::Next { responder } =
372 stream.next().await.unwrap().unwrap();
373 responder.send(manifest.as_slice()).unwrap();
374 let fsys2_f::ManifestBytesIteratorRequest::Next { responder } =
375 stream.next().await.unwrap().unwrap();
376 responder.send(&[]).unwrap();
377 })
378 .detach();
379
380 responder.send(Ok(client_end)).unwrap();
381 }
382 fsys2_f::RealmQueryRequest::GetResolvedDeclaration {
383 moniker, responder, ..
384 } => {
385 let query_moniker = Moniker::from_str(moniker.as_str()).unwrap();
386 let res = self.mapping.get(&query_moniker.to_string()).unwrap();
387 let manifest = res.make_manifest();
388 let manifest = fidl::persist(&manifest).unwrap();
389 let (client_end, server_end) =
390 client.create_endpoints::<fsys2_f::ManifestBytesIteratorMarker>();
391
392 fuchsia_async::Task::spawn(async move {
393 let mut stream = server_end.into_stream();
394 let fsys2_f::ManifestBytesIteratorRequest::Next { responder } =
395 stream.next().await.unwrap().unwrap();
396 responder.send(manifest.as_slice()).unwrap();
397 let fsys2_f::ManifestBytesIteratorRequest::Next { responder } =
398 stream.next().await.unwrap().unwrap();
399 responder.send(&[]).unwrap();
400 })
401 .detach();
402
403 responder.send(Ok(client_end)).unwrap();
404 }
405 fsys2_f::RealmQueryRequest::GetAllInstances { responder } => {
406 let instances: Vec<fsys2_f::Instance> =
407 self.mapping.values().map(|m| m.to_instance()).collect();
408
409 let (client_end, server_end) =
410 client.create_endpoints::<fsys2_f::InstanceIteratorMarker>();
411
412 fuchsia_async::Task::spawn(async move {
413 let mut stream = server_end.into_stream();
414 let fsys2_f::InstanceIteratorRequest::Next { responder } =
415 stream.next().await.unwrap().unwrap();
416 responder.send(&instances).unwrap();
417 let fsys2_f::InstanceIteratorRequest::Next { responder } =
418 stream.next().await.unwrap().unwrap();
419 responder.send(&[]).unwrap();
420 })
421 .detach();
422
423 responder.send(Ok(client_end)).unwrap();
424 }
425 _ => unreachable!("request {:?}", request),
426 }
427 }
428 }
429
430 pub async fn serve(self: Rc<Self>, object: ServerEnd<fsys2::RealmQueryMarker>) {
432 let mut stream = object.into_stream();
433 while let Ok(Some(request)) = stream.try_next().await {
434 match request {
435 fsys2::RealmQueryRequest::GetInstance { moniker, responder } => {
436 let query_moniker = Moniker::from_str(moniker.as_str()).unwrap();
437 let res = self.mapping.get(&query_moniker.to_string()).unwrap();
438 responder.send(Ok(&res.to_instance())).unwrap();
439 }
440 fsys2::RealmQueryRequest::OpenDirectory {
441 moniker,
442 dir_type,
443 object,
444 responder,
445 ..
446 } => {
447 let query_moniker = Moniker::from_str(moniker.as_str()).unwrap();
448 if let Some(res) = self.mapping.get(&query_moniker.to_string()) {
449 if dir_type == fsys2::OpenDirType::ExposedDir {
450 res.serve_exposed_dir(object, "");
452 }
453 responder.send(Ok(())).unwrap();
454 } else {
455 responder.send(Err(fsys2::OpenError::InstanceNotFound)).unwrap();
456 }
457 }
458 fsys2::RealmQueryRequest::GetManifest { moniker, responder, .. } => {
459 let query_moniker = Moniker::from_str(moniker.as_str()).unwrap();
460 let res = self.mapping.get(&query_moniker.to_string()).unwrap();
461 let manifest = res.make_manifest();
462 let manifest = fidl::persist(&manifest).unwrap();
463 let (client_end, server_end) =
464 create_endpoints::<fsys2::ManifestBytesIteratorMarker>();
465
466 fuchsia_async::Task::spawn(async move {
467 let mut stream = server_end.into_stream();
468 let fsys2::ManifestBytesIteratorRequest::Next { responder } =
469 stream.next().await.unwrap().unwrap();
470 responder.send(manifest.as_slice()).unwrap();
471 let fsys2::ManifestBytesIteratorRequest::Next { responder } =
472 stream.next().await.unwrap().unwrap();
473 responder.send(&[]).unwrap();
474 })
475 .detach();
476
477 responder.send(Ok(client_end)).unwrap();
478 }
479 fsys2::RealmQueryRequest::GetResolvedDeclaration { moniker, responder, .. } => {
480 let query_moniker = Moniker::from_str(moniker.as_str()).unwrap();
481 let res = self.mapping.get(&query_moniker.to_string()).unwrap();
482 let manifest = res.make_manifest();
483 let manifest = fidl::persist(&manifest).unwrap();
484 let (client_end, server_end) =
485 create_endpoints::<fsys2::ManifestBytesIteratorMarker>();
486
487 fuchsia_async::Task::spawn(async move {
488 let mut stream = server_end.into_stream();
489 let fsys2::ManifestBytesIteratorRequest::Next { responder } =
490 stream.next().await.unwrap().unwrap();
491 responder.send(manifest.as_slice()).unwrap();
492 let fsys2::ManifestBytesIteratorRequest::Next { responder } =
493 stream.next().await.unwrap().unwrap();
494 responder.send(&[]).unwrap();
495 })
496 .detach();
497
498 responder.send(Ok(client_end)).unwrap();
499 }
500 fsys2::RealmQueryRequest::GetAllInstances { responder } => {
501 let instances: Vec<fsys2::Instance> =
502 self.mapping.values().map(|m| m.to_instance()).collect();
503
504 let (client_end, server_end) =
505 create_endpoints::<fsys2::InstanceIteratorMarker>();
506
507 fuchsia_async::Task::spawn(async move {
508 let mut stream = server_end.into_stream();
509 let fsys2::InstanceIteratorRequest::Next { responder } =
510 stream.next().await.unwrap().unwrap();
511 responder.send(&instances).unwrap();
512 let fsys2::InstanceIteratorRequest::Next { responder } =
513 stream.next().await.unwrap().unwrap();
514 responder.send(&[]).unwrap();
515 })
516 .detach();
517
518 responder.send(Ok(client_end)).unwrap();
519 }
520 _ => unreachable!("request {:?}", request),
521 }
522 }
523 }
524
525 pub async fn get_proxy(self: Rc<Self>) -> fsys2::RealmQueryProxy {
529 let (proxy, server_end) = create_proxy::<fsys2::RealmQueryMarker>();
530 fuchsia_async::Task::local(async move { self.serve(server_end).await }).detach();
531 proxy
532 }
533}
534
535pub trait Entry {
537 fn open(self: Rc<Self>, path: &str, object: fidl::Channel);
538 fn open_f(self: Rc<Self>, path: &str, object: fdomain_client::Channel);
539 fn encode(&self, buf: &mut Vec<u8>);
540 fn name(&self) -> String;
541}
542
543pub struct MockDir {
544 subdirs: HashMap<String, Rc<dyn Entry>>,
545 name: String,
546 at_end: AtomicBool,
547}
548
549impl MockDir {
550 pub fn new(name: String) -> Self {
551 MockDir { name, subdirs: HashMap::new(), at_end: AtomicBool::new(false) }
552 }
553
554 pub fn new_arc(name: String) -> Rc<Self> {
555 Rc::new(Self::new(name))
556 }
557
558 pub fn add_entry(mut self, entry: Rc<dyn Entry>) -> Self {
559 self.subdirs.insert(entry.name(), entry);
560 self
561 }
562
563 async fn serve_f(self: Rc<Self>, object: FServerEnd<fio_f::DirectoryMarker>) {
564 let mut stream = object.into_stream();
565 let _ = stream.control_handle().send_on_open_(
566 Status::OK.into_raw(),
567 Some(fio_f::NodeInfoDeprecated::Directory(fio_f::DirectoryObject {})),
568 );
569 while let Ok(Some(request)) = stream.try_next().await {
570 match request {
571 fio_f::DirectoryRequest::Open { path, object, .. } => {
572 self.clone().open_f(&path, object);
573 }
574 fio_f::DirectoryRequest::Rewind { responder, .. } => {
575 self.at_end.store(false, Ordering::Relaxed);
576 responder.send(Status::OK.into_raw()).unwrap();
577 }
578 fio_f::DirectoryRequest::ReadDirents { max_bytes: _, responder, .. } => {
579 let entries = match self.at_end.compare_exchange(
580 false,
581 true,
582 Ordering::Relaxed,
583 Ordering::Relaxed,
584 ) {
585 Ok(false) => encode_entries(&self.subdirs),
586 Err(true) => Vec::new(),
587 _ => unreachable!(),
588 };
589 responder.send(Status::OK.into_raw(), &entries).unwrap();
590 }
591 x => panic!("unsupported request: {x:?}"),
592 }
593 }
594 }
595
596 async fn serve(self: Rc<Self>, object: ServerEnd<fio::DirectoryMarker>) {
597 let mut stream = object.into_stream();
598 while let Ok(Some(request)) = stream.try_next().await {
599 match request {
600 fio::DirectoryRequest::Open { path, object, .. } => {
601 self.clone().open(&path, object);
602 }
603 fio::DirectoryRequest::Rewind { responder, .. } => {
604 self.at_end.store(false, Ordering::Relaxed);
605 responder.send(Status::OK.into_raw()).unwrap();
606 }
607 fio::DirectoryRequest::ReadDirents { max_bytes: _, responder, .. } => {
608 let entries = match self.at_end.compare_exchange(
609 false,
610 true,
611 Ordering::Relaxed,
612 Ordering::Relaxed,
613 ) {
614 Ok(false) => encode_entries(&self.subdirs),
615 Err(true) => Vec::new(),
616 _ => unreachable!(),
617 };
618 responder.send(Status::OK.into_raw(), &entries).unwrap();
619 }
620 x => panic!("unsupported request: {x:?}"),
621 }
622 }
623 }
624}
625
626fn encode_entries(subdirs: &HashMap<String, Rc<dyn Entry>>) -> Vec<u8> {
627 let mut buf = Vec::new();
628 let mut data = subdirs.iter().collect::<Vec<(_, _)>>();
629 data.sort_by(|a, b| a.0.cmp(b.0));
630 for (_, entry) in data.iter() {
631 entry.encode(&mut buf);
632 }
633 buf
634}
635
636impl Entry for MockDir {
637 fn open(self: Rc<Self>, path: &str, object: fidl::Channel) {
638 let path = Path::new(path);
639 let mut path_iter = path.iter();
640 let segment = if let Some(segment) = path_iter.next() {
641 if let Some(segment) = segment.to_str() {
642 segment
643 } else {
644 let _ =
645 ServerEnd::<fio::NodeMarker>::new(object).close_with_epitaph(Status::NOT_FOUND);
646 return;
647 }
648 } else {
649 "."
650 };
651 if segment == "." {
652 fuchsia_async::Task::local(self.serve(ServerEnd::new(object))).detach();
653 return;
654 }
655 if let Some(entry) = self.subdirs.get(segment) {
656 entry.clone().open(path_iter.as_path().to_str().unwrap(), object);
657 } else {
658 let _ = ServerEnd::<fio::NodeMarker>::new(object).close_with_epitaph(Status::NOT_FOUND);
659 }
660 }
661
662 fn open_f(self: Rc<Self>, path: &str, object: fdomain_client::Channel) {
663 let path = Path::new(path);
664 let mut path_iter = path.iter();
665 let segment = if let Some(segment) = path_iter.next() {
666 if let Some(segment) = segment.to_str() {
667 segment
668 } else {
669 let _ = FServerEnd::<fio_f::NodeMarker>::new(object)
670 .close_with_epitaph(Status::NOT_FOUND);
671 return;
672 }
673 } else {
674 "."
675 };
676 if segment == "." {
677 fuchsia_async::Task::local(self.serve_f(FServerEnd::new(object))).detach();
678 return;
679 }
680 if let Some(entry) = self.subdirs.get(segment) {
681 entry.clone().open_f(path_iter.as_path().to_str().unwrap(), object);
682 } else {
683 let _ =
684 FServerEnd::<fio_f::NodeMarker>::new(object).close_with_epitaph(Status::NOT_FOUND);
685 }
686 }
687
688 fn encode(&self, buf: &mut Vec<u8>) {
689 buf.write_u64::<LittleEndian>(fio::INO_UNKNOWN).expect("writing mockdir ino to work");
690 buf.write_u8(self.name.len() as u8).expect("writing mockdir size to work");
691 buf.write_u8(fio::DirentType::Directory.into_primitive())
692 .expect("writing mockdir type to work");
693 buf.write_all(self.name.as_ref()).expect("writing mockdir name to work");
694 }
695
696 fn name(&self) -> String {
697 self.name.clone()
698 }
699}
700
701struct MockFile {
702 name: String,
703}
704
705impl MockFile {
706 pub fn new(name: String) -> Self {
707 MockFile { name }
708 }
709 pub fn new_arc(name: String) -> Rc<Self> {
710 Rc::new(Self::new(name))
711 }
712}
713
714impl Entry for MockFile {
715 fn open(self: Rc<Self>, _path: &str, _object: fidl::Channel) {
716 unimplemented!();
717 }
718
719 fn open_f(self: Rc<Self>, _path: &str, _object: fdomain_client::Channel) {
720 unimplemented!();
721 }
722
723 fn encode(&self, buf: &mut Vec<u8>) {
724 buf.write_u64::<LittleEndian>(fio::INO_UNKNOWN).expect("writing mockdir ino to work");
725 buf.write_u8(self.name.len() as u8).expect("writing mockdir size to work");
726 buf.write_u8(fio::DirentType::Service.into_primitive())
727 .expect("writing mockdir type to work");
728 buf.write_all(self.name.as_ref()).expect("writing mockdir name to work");
729 }
730
731 fn name(&self) -> String {
732 self.name.clone()
733 }
734}