1use crate::commands::types::DiagnosticsProvider;
6use crate::types::Error;
7use anyhow::anyhow;
8use cm_rust::{ExposeDeclCommon, ExposeSource, SourceName};
9
10#[cfg(not(feature = "fdomain"))]
11use component_debug;
12#[cfg(feature = "fdomain")]
13use component_debug_fdomain as component_debug;
14
15use component_debug::dirs::*;
16use component_debug::realm::*;
17use flex_client::ProxyHasDomain;
18use flex_client::fidl::DiscoverableProtocolMarker;
19use flex_fuchsia_diagnostics::{All, ArchiveAccessorMarker, Selector, TreeNames};
20
21#[cfg(not(feature = "fdomain"))]
22use fuchsia_fs;
23#[cfg(feature = "fdomain")]
24use fuchsia_fs_fdomain as fuchsia_fs;
25
26use flex_fuchsia_io as fio;
27use flex_fuchsia_sys2 as fsys2;
28use fuchsia_fs::directory;
29use moniker::Moniker;
30
31const ACCESSORS_DICTIONARY: &str = "diagnostics-accessors";
32
33pub async fn connect_accessor<P: DiscoverableProtocolMarker>(
36 moniker: &Moniker,
37 accessor_name: &str,
38 proxy: &fsys2::RealmQueryProxy,
39) -> Result<P::Proxy, Error> {
40 let proxy = connect_to_instance_protocol_at_path::<P>(
41 moniker,
42 OpenDirType::Exposed,
43 &format!("{ACCESSORS_DICTIONARY}/{accessor_name}"),
44 proxy,
45 )
46 .await
47 .map_err(|e| Error::ConnectToProtocol(accessor_name.to_string(), anyhow!("{:?}", e)))?;
48 Ok(proxy)
49}
50
51async fn list_accessors(
52 realm: &fsys2::RealmQueryProxy,
53 moniker: &Moniker,
54) -> Option<Vec<fuchsia_fs::directory::DirEntry>> {
55 let Ok(exposed_dir) = open_instance_directory(moniker, OpenDirType::Exposed, realm).await
56 else {
57 return None;
58 };
59
60 let (dir_proxy, server_end) = realm.domain().create_proxy::<fio::DirectoryMarker>();
61 exposed_dir
62 .open(
63 ACCESSORS_DICTIONARY,
64 fio::Flags::PROTOCOL_DIRECTORY | fio::PERM_READABLE,
65 &Default::default(),
66 server_end.into_channel(),
67 )
68 .expect("Open call failed!");
69
70 directory::readdir(&dir_proxy).await.ok()
71}
72
73pub async fn fuzzy_search_accessors(
95 query: &str,
96 realm_query: &fsys2::RealmQueryProxy,
97) -> Result<(Moniker, String), Error> {
98 let (moniker_query, protocol_query) = query.rsplit_once(':').unwrap_or((query, ""));
99 let mut matching_monikers_and_protocols: Vec<_> = get_accessor_selectors(realm_query)
100 .await?
101 .iter()
102 .filter_map(|i| i.rsplit_once(":"))
103 .filter(|(m, p)| m.contains(moniker_query) && p.contains(protocol_query))
104 .filter_map(|(m, p)| Moniker::parse_str(m).ok().map(|moniker| (moniker, p.to_string())))
105 .collect();
106 if matching_monikers_and_protocols.is_empty() {
107 return Err(Error::SearchParameterNotFound(query.to_string()));
108 } else if matching_monikers_and_protocols.len() > 1 {
109 let mut moniker_exact_match: Vec<_> = matching_monikers_and_protocols
110 .iter()
111 .filter(|(m, _)| m.to_string() == moniker_query)
112 .cloned()
113 .collect();
114 if moniker_exact_match.len() == 1 {
116 return Ok(moniker_exact_match.pop().unwrap());
117 } else if moniker_exact_match.len() > 1 {
118 moniker_exact_match.retain(|(_, p)| p == protocol_query);
120 if moniker_exact_match.len() == 1 {
121 return Ok(moniker_exact_match.pop().unwrap());
122 }
123 }
124
125 let mut protocol_exact_match: Vec<_> = matching_monikers_and_protocols
128 .iter()
129 .filter(|(_, p)| p == protocol_query)
130 .cloned()
131 .collect();
132 if protocol_exact_match.len() == 1 {
133 return Ok(protocol_exact_match.pop().unwrap());
134 }
135
136 return Err(Error::FuzzyMatchTooManyMatches(
137 matching_monikers_and_protocols.into_iter().map(|(m, p)| format!("{m}:{p}")).collect(),
138 ));
139 }
140
141 Ok(matching_monikers_and_protocols.pop().unwrap())
142}
143
144async fn fuzzy_search(
153 query: &str,
154 realm_query: &fsys2::RealmQueryProxy,
155) -> Result<Instance, Error> {
156 let mut instances = component_debug::query::get_instances_from_query(query, realm_query)
157 .await
158 .map_err(Error::FuzzyMatchRealmQuery)?;
159 if instances.is_empty() {
160 return Err(Error::SearchParameterNotFound(query.to_string()));
161 } else if instances.len() > 1 {
162 return Err(Error::FuzzyMatchTooManyMatches(
163 instances.into_iter().map(|i| i.moniker.to_string()).collect(),
164 ));
165 }
166
167 Ok(instances.pop().unwrap())
168}
169
170pub async fn process_fuzzy_inputs<P: DiagnosticsProvider>(
171 queries: impl IntoIterator<Item = String>,
172 provider: &P,
173) -> Result<Vec<Selector>, Error> {
174 let mut queries = queries.into_iter().peekable();
175 if queries.peek().is_none() {
176 return Ok(vec![]);
177 }
178
179 let realm_query = provider.realm_query();
180 let mut results = vec![];
181 for value in queries {
182 match fuzzy_search(&value, realm_query).await {
183 Err(Error::SearchParameterNotFound(_)) => {
185 if let Ok(selector) = selectors::parse_verbose(&value) {
188 results.push(selector);
189 } else {
190 let selector_string = format!("{value}:root");
196 results.push(
197 selectors::parse_verbose(&selector_string)
198 .map_err(|e| Error::ParseSelector(selector_string, e.into()))?,
199 )
200 }
201 }
202 Err(e) => return Err(e),
203 Ok(instance) => {
204 let selector_string = format!(
205 "{}:root",
206 selectors::sanitize_moniker_for_selectors(instance.moniker.as_ref()),
207 );
208 results.push(
209 selectors::parse_verbose(&selector_string)
210 .map_err(|e| Error::ParseSelector(selector_string, e.into()))?,
211 )
212 }
213 }
214 }
215
216 Ok(results)
217}
218
219pub async fn process_component_query_with_partial_selectors<P: DiagnosticsProvider>(
222 component: &str,
223 tree_selectors: impl Iterator<Item = String>,
224 provider: &P,
225) -> Result<Vec<Selector>, Error> {
226 let mut tree_selectors = tree_selectors.into_iter().peekable();
227 let realm_query = provider.realm_query();
228 let instance = fuzzy_search(component, realm_query).await?;
229
230 let mut results = vec![];
231 if tree_selectors.peek().is_none() {
232 let selector_string = format!(
233 "{}:root",
234 selectors::sanitize_moniker_for_selectors(instance.moniker.as_ref())
235 );
236 results
237 .push(selectors::parse_verbose(&selector_string).map_err(Error::PartialSelectorHint)?);
238 } else {
239 for s in tree_selectors {
240 let selector_string = format!(
241 "{}:{}",
242 selectors::sanitize_moniker_for_selectors(instance.moniker.as_ref()),
243 s
244 );
245 results.push(
246 selectors::parse_verbose(&selector_string).map_err(Error::PartialSelectorHint)?,
247 )
248 }
249 }
250
251 Ok(results)
252}
253
254fn add_tree_name(selector: &mut Selector, tree_name: String) -> Result<(), Error> {
255 match selector.tree_names {
256 None => selector.tree_names = Some(TreeNames::Some(vec![tree_name])),
257 Some(ref mut names) => match names {
258 TreeNames::Some(names) => {
259 if !names.iter().any(|n| n == &tree_name) {
260 names.push(tree_name)
261 }
262 }
263 TreeNames::All(_) => {}
264 TreeNames::__SourceBreaking { unknown_ordinal } => {
265 let unknown_ordinal = *unknown_ordinal;
266 return Err(Error::InvalidSelector(format!(
267 "selector had invalid TreeNames variant {unknown_ordinal}: {selector:?}",
268 )));
269 }
270 },
271 }
272 Ok(())
273}
274
275pub fn ensure_tree_field_is_set(
280 selectors: &mut Vec<Selector>,
281 tree_name: Option<String>,
282) -> Result<(), Error> {
283 if selectors.is_empty() {
284 let Some(tree_name) = tree_name else {
285 return Ok(());
286 };
287
288 let mut selector = selectors::parse_verbose("**:*").unwrap();
290 selector.tree_names = Some(TreeNames::Some(vec![tree_name]));
291 selectors.push(selector);
292 return Ok(());
293 }
294
295 for selector in selectors.iter_mut() {
296 if let Some(tree_name) = &tree_name {
297 add_tree_name(selector, tree_name.clone())?;
298 } else if selector.tree_names.is_none() {
299 selector.tree_names = Some(TreeNames::All(All {}))
300 }
301 }
302
303 Ok(())
304}
305
306pub async fn get_accessor_selectors(
309 realm_query: &fsys2::RealmQueryProxy,
310) -> Result<Vec<String>, Error> {
311 let mut result = vec![];
312 let instances = get_all_instances(realm_query).await?;
313 for instance in instances {
314 match get_resolved_declaration(&instance.moniker, realm_query).await {
315 Err(GetDeclarationError::InstanceNotFound(_))
316 | Err(GetDeclarationError::InstanceNotResolved(_)) => continue,
317 Err(err) => return Err(err.into()),
318 Ok(decl) => {
319 for capability in decl.capabilities {
320 let capability_name = capability.name().to_string();
321 if capability_name != ACCESSORS_DICTIONARY {
322 continue;
323 }
324 if !decl.exposes.iter().any(|expose| {
325 expose.source_name() == capability.name()
326 && *expose.source() == ExposeSource::Self_
327 }) {
328 continue;
329 }
330
331 let Some(entries) = list_accessors(realm_query, &instance.moniker).await else {
332 continue;
333 };
334
335 for entry in entries {
336 let directory::DirEntry { name, kind: fio::DirentType::Service } = entry
337 else {
338 continue;
339 };
340 if !name.starts_with(ArchiveAccessorMarker::PROTOCOL_NAME) {
342 continue;
343 }
344 result.push(format!("{}:{name}", instance.moniker));
345 }
346 }
347 }
348 }
349 }
350 result.sort();
351 Ok(result)
352}
353
354#[cfg(test)]
355mod test {
356 use super::*;
357 use assert_matches::assert_matches;
358 use iquery_test_support::{MockRealmQuery, MockRealmQueryBuilder};
359 use selectors::parse_verbose;
360 use std::rc::Rc;
361
362 #[fuchsia::test]
363 async fn test_get_accessors() {
364 let fake_realm_query = Rc::new(MockRealmQuery::default());
365 let realm_query = Rc::clone(&fake_realm_query).get_proxy().await;
366
367 let res = get_accessor_selectors(&realm_query).await;
368
369 assert_matches!(res, Ok(_));
370
371 assert_eq!(
372 res.unwrap(),
373 vec![
374 String::from("example/component:fuchsia.diagnostics.ArchiveAccessor"),
375 String::from("foo/bar/thing:instance:fuchsia.diagnostics.ArchiveAccessor.feedback"),
376 String::from("foo/component:fuchsia.diagnostics.ArchiveAccessor.feedback"),
377 ]
378 );
379 }
380
381 #[fuchsia::test]
382 fn test_ensure_tree_field_is_set() {
383 let name = Some("abc".to_string());
384 let expected = vec![
385 parse_verbose("core/one:[name=abc]root").unwrap(),
386 parse_verbose("core/one:[name=xyz, name=abc]root").unwrap(),
387 ];
388
389 let mut actual = vec![
390 parse_verbose("core/one:root").unwrap(),
391 parse_verbose("core/one:[name=xyz]root").unwrap(),
392 ];
393 ensure_tree_field_is_set(&mut actual, name.clone()).unwrap();
394 assert_eq!(actual, expected);
395 }
396
397 #[fuchsia::test]
398 fn test_ensure_tree_field_is_set_noop_when_tree_names_set() {
399 let expected = vec![
400 parse_verbose("core/one:[...]root").unwrap(),
401 parse_verbose("core/one:[name=xyz]root").unwrap(),
402 ];
403 let mut actual = vec![
404 parse_verbose("core/one:root").unwrap(),
405 parse_verbose("core/one:[name=xyz]root").unwrap(),
406 ];
407 ensure_tree_field_is_set(&mut actual, None).unwrap();
408 assert_eq!(actual, expected);
409 }
410
411 #[fuchsia::test]
412 fn test_ensure_tree_field_is_set_noop_on_empty_vec_no_name() {
413 let mut actual = vec![];
414 ensure_tree_field_is_set(&mut actual, None).unwrap();
415 assert_eq!(actual, vec![]);
416 }
417
418 #[fuchsia::test]
419 fn test_ensure_tree_field_is_set_all_components_when_empty_and_name() {
420 let expected = vec![parse_verbose("**:[name=abc]*").unwrap()];
421 let mut actual = vec![];
422 let name = Some("abc".to_string());
423 ensure_tree_field_is_set(&mut actual, name).unwrap();
424 assert_eq!(actual, expected);
425 }
426
427 struct FakeProvider {
428 realm_query: fsys2::RealmQueryProxy,
429 }
430
431 impl FakeProvider {
432 async fn new(monikers: &'static [&'static str]) -> Self {
433 let mut builder = MockRealmQueryBuilder::default();
434 for name in monikers {
435 builder = builder.when(name).moniker(name).add();
436 }
437 let realm_query_proxy = Rc::new(builder.build()).get_proxy().await;
438 Self { realm_query: realm_query_proxy }
439 }
440 }
441
442 impl DiagnosticsProvider for FakeProvider {
443 async fn snapshot(
444 &self,
445 _: Option<&str>,
446 _: impl IntoIterator<Item = Selector>,
447 ) -> Result<Vec<diagnostics_data::Data<diagnostics_data::Inspect>>, Error> {
448 unreachable!("unimplemented");
449 }
450
451 async fn get_accessor_paths(&self) -> Result<Vec<String>, Error> {
452 unreachable!("unimplemented");
453 }
454
455 fn realm_query(&self) -> &fsys2::RealmQueryProxy {
456 &self.realm_query
457 }
458 }
459
460 #[fuchsia::test]
461 async fn test_process_fuzzy_inputs_success() {
462 let actual = process_fuzzy_inputs(
463 ["moniker1".to_string()],
464 &FakeProvider::new(&["core/moniker1", "core/moniker2"]).await,
465 )
466 .await
467 .unwrap();
468
469 let expected = vec![parse_verbose("core/moniker1:root").unwrap()];
470
471 assert_eq!(actual, expected);
472
473 let actual = process_fuzzy_inputs(
474 ["moniker1:collection".to_string()],
475 &FakeProvider::new(&["core/moniker1:collection", "core/moniker1", "core/moniker2"])
476 .await,
477 )
478 .await
479 .unwrap();
480
481 let expected = vec![parse_verbose(r"core/moniker1\:collection:root").unwrap()];
482
483 assert_eq!(actual, expected);
484
485 let actual = process_fuzzy_inputs(
486 [r"core/moniker1\:collection".to_string()],
487 &FakeProvider::new(&["core/moniker1:collection"]).await,
488 )
489 .await
490 .unwrap();
491
492 let expected = vec![parse_verbose(r"core/moniker1\:collection:root").unwrap()];
493
494 assert_eq!(actual, expected);
495
496 let actual = process_fuzzy_inputs(
497 ["core/moniker1:root:prop".to_string()],
498 &FakeProvider::new(&["core/moniker1:collection", "core/moniker1"]).await,
499 )
500 .await
501 .unwrap();
502
503 let expected = vec![parse_verbose(r"core/moniker1:root:prop").unwrap()];
504
505 assert_eq!(actual, expected);
506
507 let actual = process_fuzzy_inputs(
508 ["core/moniker1".to_string(), "core/moniker2".to_string()],
509 &FakeProvider::new(&["core/moniker1", "core/moniker2"]).await,
510 )
511 .await
512 .unwrap();
513
514 let expected = vec![
515 parse_verbose(r"core/moniker1:root").unwrap(),
516 parse_verbose(r"core/moniker2:root").unwrap(),
517 ];
518
519 assert_eq!(actual, expected);
520
521 let actual = process_fuzzy_inputs(
522 ["moniker1".to_string(), "moniker2".to_string()],
523 &FakeProvider::new(&["core/moniker1"]).await,
524 )
525 .await
526 .unwrap();
527
528 let expected = vec![
529 parse_verbose(r"core/moniker1:root").unwrap(),
530 parse_verbose("moniker2:root").unwrap(),
532 ];
533
534 assert_eq!(actual, expected);
535
536 let actual = process_fuzzy_inputs(
537 ["core/moniker1:root:prop".to_string(), "core/moniker2".to_string()],
538 &FakeProvider::new(&["core/moniker1", "core/moniker2"]).await,
539 )
540 .await
541 .unwrap();
542
543 let expected = vec![
544 parse_verbose(r"core/moniker1:root:prop").unwrap(),
545 parse_verbose(r"core/moniker2:root").unwrap(),
546 ];
547
548 assert_eq!(actual, expected);
549 }
550
551 #[fuchsia::test]
552 async fn test_process_fuzzy_inputs_failures() {
553 let actual =
554 process_fuzzy_inputs(["moniker ".to_string()], &FakeProvider::new(&["moniker"]).await)
555 .await;
556
557 assert_matches!(actual, Err(Error::ParseSelector(_, _)));
558
559 let actual = process_fuzzy_inputs(
560 ["moniker".to_string()],
561 &FakeProvider::new(&["core/moniker1", "core/moniker2"]).await,
562 )
563 .await;
564
565 assert_matches!(actual, Err(Error::FuzzyMatchTooManyMatches(_)));
566 }
567
568 #[fuchsia::test]
569 async fn test_fuzzy_component_search() {
570 let actual = process_component_query_with_partial_selectors(
571 "moniker1",
572 [].into_iter(),
573 &FakeProvider::new(&["core/moniker1", "core/moniker2"]).await,
574 )
575 .await
576 .unwrap();
577
578 let expected = vec![parse_verbose(r"core/moniker1:root").unwrap()];
579
580 assert_eq!(actual, expected);
581
582 let actual = process_component_query_with_partial_selectors(
583 "moniker1",
584 ["root/foo:bar".to_string()].into_iter(),
585 &FakeProvider::new(&["core/moniker1", "core/moniker2"]).await,
586 )
587 .await
588 .unwrap();
589
590 let expected = vec![parse_verbose(r"core/moniker1:root/foo:bar").unwrap()];
591
592 assert_eq!(actual, expected);
593
594 let actual = process_component_query_with_partial_selectors(
595 "moniker1",
596 ["root/foo:bar".to_string()].into_iter(),
597 &FakeProvider::new(&["core/moniker2", "core/moniker3"]).await,
598 )
599 .await;
600
601 assert_matches!(actual, Err(Error::SearchParameterNotFound(_)));
602 }
603
604 #[fuchsia::test]
605 async fn test_fuzzy_search_accessors_exact_match() {
606 let fake_realm_query = Rc::new(MockRealmQuery::default());
607 let realm_query = Rc::clone(&fake_realm_query).get_proxy().await;
608
609 let (moniker, protocol) = fuzzy_search_accessors(
610 "example/component:fuchsia.diagnostics.ArchiveAccessor",
611 &realm_query,
612 )
613 .await
614 .unwrap();
615 assert_eq!(moniker, Moniker::parse_str("example/component").unwrap());
616 assert_eq!(protocol, "fuchsia.diagnostics.ArchiveAccessor");
617 }
618
619 #[fuchsia::test]
620 async fn test_fuzzy_search_accessors_fuzzy_match_moniker_and_protocol() {
621 let fake_realm_query = Rc::new(MockRealmQuery::default());
622 let realm_query = Rc::clone(&fake_realm_query).get_proxy().await;
623
624 let (moniker, protocol) =
625 fuzzy_search_accessors("example:ArchiveAccessor", &realm_query).await.unwrap();
626 assert_eq!(moniker, Moniker::parse_str("example/component").unwrap());
627 assert_eq!(protocol, "fuchsia.diagnostics.ArchiveAccessor");
628 }
629
630 #[fuchsia::test]
631 async fn test_fuzzy_search_accessors_moniker_with_colon() {
632 let fake_realm_query = Rc::new(MockRealmQuery::default());
633 let realm_query = Rc::clone(&fake_realm_query).get_proxy().await;
634
635 let (moniker, protocol) =
636 fuzzy_search_accessors("thing:instance:feedback", &realm_query).await.unwrap();
637 assert_eq!(moniker, Moniker::parse_str("foo/bar/thing:instance").unwrap());
638 assert_eq!(protocol, "fuchsia.diagnostics.ArchiveAccessor.feedback");
639 }
640
641 #[fuchsia::test]
642 async fn test_fuzzy_search_accessors_disambiguation_by_exact_moniker() {
643 let fake_realm_query = Rc::new(
644 MockRealmQueryBuilder::new()
645 .when("example/component")
646 .moniker("./example/component")
647 .accessors(&["fuchsia.diagnostics.ArchiveAccessor"])
648 .add()
649 .when("example/component/subcomponent")
650 .moniker("./example/component/subcomponent")
651 .accessors(&["fuchsia.diagnostics.ArchiveAccessor"])
652 .add()
653 .build(),
654 );
655 let realm_query = Rc::clone(&fake_realm_query).get_proxy().await;
656
657 let (moniker, protocol) = fuzzy_search_accessors(
658 "example/component:fuchsia.diagnostics.ArchiveAccessor",
659 &realm_query,
660 )
661 .await
662 .unwrap();
663 assert_eq!(moniker, Moniker::parse_str("example/component").unwrap());
664 assert_eq!(protocol, "fuchsia.diagnostics.ArchiveAccessor");
665
666 let (moniker, protocol) =
667 fuzzy_search_accessors("example/component:ArchiveAccessor", &realm_query)
668 .await
669 .unwrap();
670 assert_eq!(moniker, Moniker::parse_str("example/component").unwrap());
671 assert_eq!(protocol, "fuchsia.diagnostics.ArchiveAccessor");
672 }
673
674 #[fuchsia::test]
675 async fn test_fuzzy_search_accessors_disambiguation_by_exact_protocol() {
676 let fake_realm_query = Rc::new(MockRealmQuery::default());
677 let realm_query = Rc::clone(&fake_realm_query).get_proxy().await;
678
679 let (moniker, protocol) =
690 fuzzy_search_accessors("component:fuchsia.diagnostics.ArchiveAccessor", &realm_query)
691 .await
692 .unwrap();
693 assert_eq!(moniker, Moniker::parse_str("example/component").unwrap());
694 assert_eq!(protocol, "fuchsia.diagnostics.ArchiveAccessor");
695 }
696
697 #[fuchsia::test]
698 async fn test_fuzzy_search_accessors_disambiguation_by_both_exact_moniker_and_protocol() {
699 let fake_realm_query = Rc::new(
700 MockRealmQueryBuilder::new()
701 .when("example/component")
702 .moniker("./example/component")
703 .accessors(&[
704 "fuchsia.diagnostics.ArchiveAccessor",
705 "fuchsia.diagnostics.ArchiveAccessor.feedback",
706 ])
707 .add()
708 .when("example/component/subcomponent")
709 .moniker("./example/component/subcomponent")
710 .accessors(&[
711 "fuchsia.diagnostics.ArchiveAccessor",
712 "fuchsia.diagnostics.ArchiveAccessor.feedback",
713 ])
714 .add()
715 .build(),
716 );
717 let realm_query = Rc::clone(&fake_realm_query).get_proxy().await;
718
719 let (moniker, protocol) = fuzzy_search_accessors(
720 "example/component:fuchsia.diagnostics.ArchiveAccessor",
721 &realm_query,
722 )
723 .await
724 .unwrap();
725 assert_eq!(moniker, Moniker::parse_str("example/component").unwrap());
726 assert_eq!(protocol, "fuchsia.diagnostics.ArchiveAccessor");
727 }
728
729 #[fuchsia::test]
730 async fn test_fuzzy_search_accessors_no_protocol_single_accessor() {
731 let fake_realm_query = Rc::new(MockRealmQuery::default());
732 let realm_query = Rc::clone(&fake_realm_query).get_proxy().await;
733
734 let (moniker, protocol) =
735 fuzzy_search_accessors("example/component", &realm_query).await.unwrap();
736 assert_eq!(moniker, Moniker::parse_str("example/component").unwrap());
737 assert_eq!(protocol, "fuchsia.diagnostics.ArchiveAccessor");
738 }
739
740 #[fuchsia::test]
741 async fn test_fuzzy_search_accessors_no_protocol_multiple_accessors() {
742 let fake_realm_query = Rc::new(
743 MockRealmQueryBuilder::new()
744 .when("example/component")
745 .moniker("./example/component")
746 .accessors(&[
747 "fuchsia.diagnostics.ArchiveAccessor",
748 "fuchsia.diagnostics.ArchiveAccessor.feedback",
749 ])
750 .add()
751 .build(),
752 );
753 let realm_query = Rc::clone(&fake_realm_query).get_proxy().await;
754
755 let res = fuzzy_search_accessors("example/component", &realm_query).await;
756 assert_matches!(
757 res,
758 Err(Error::FuzzyMatchTooManyMatches(matches)) if matches.0 == [
759 "example/component:fuchsia.diagnostics.ArchiveAccessor",
760 "example/component:fuchsia.diagnostics.ArchiveAccessor.feedback",
761 ]
762 );
763 }
764
765 #[fuchsia::test]
766 async fn test_fuzzy_search_accessors_moniker_not_found() {
767 let fake_realm_query = Rc::new(MockRealmQuery::default());
768 let realm_query = Rc::clone(&fake_realm_query).get_proxy().await;
769
770 let res = fuzzy_search_accessors("nonexistent:ArchiveAccessor", &realm_query).await;
771 assert_matches!(res, Err(Error::SearchParameterNotFound(query)) if query == "nonexistent:ArchiveAccessor");
772 }
773
774 #[fuchsia::test]
775 async fn test_fuzzy_search_accessors_protocol_not_found() {
776 let fake_realm_query = Rc::new(MockRealmQuery::default());
777 let realm_query = Rc::clone(&fake_realm_query).get_proxy().await;
778
779 let res = fuzzy_search_accessors("example:nonexistent", &realm_query).await;
780 assert_matches!(res, Err(Error::SearchParameterNotFound(query)) if query == "example:nonexistent");
781 }
782
783 #[fuchsia::test]
784 async fn test_fuzzy_search_accessors_too_many_matches_fuzzy_protocol() {
785 let fake_realm_query = Rc::new(MockRealmQuery::default());
786 let realm_query = Rc::clone(&fake_realm_query).get_proxy().await;
787
788 let res = fuzzy_search_accessors("foo:feedback", &realm_query).await;
789 assert_matches!(
790 res,
791 Err(Error::FuzzyMatchTooManyMatches(matches)) if matches.0 == [
792 "foo/bar/thing:instance:fuchsia.diagnostics.ArchiveAccessor.feedback",
793 "foo/component:fuchsia.diagnostics.ArchiveAccessor.feedback",
794 ]
795 );
796 }
797
798 #[fuchsia::test]
799 async fn test_fuzzy_search_accessors_too_many_matches_exact_protocol() {
800 let fake_realm_query = Rc::new(MockRealmQuery::default());
801 let realm_query = Rc::clone(&fake_realm_query).get_proxy().await;
802
803 let res = fuzzy_search_accessors(
804 "foo:fuchsia.diagnostics.ArchiveAccessor.feedback",
805 &realm_query,
806 )
807 .await;
808 assert_matches!(
809 res,
810 Err(Error::FuzzyMatchTooManyMatches(matches)) if matches.0 == [
811 "foo/bar/thing:instance:fuchsia.diagnostics.ArchiveAccessor.feedback",
812 "foo/component:fuchsia.diagnostics.ArchiveAccessor.feedback",
813 ]
814 );
815 }
816}