1use crate::agent::{AgentCreator, Context, CreationFunc, Payload};
14use crate::base::{SettingInfo, SettingType};
15use crate::handler::base::{Error, Payload as HandlerPayload, Request};
16use crate::message::base::{MessageEvent, MessengerType};
17use crate::message::receptor::Receptor;
18use crate::service::TryFromWithClient;
19use crate::{clock, service, trace};
20use futures::channel::mpsc::UnboundedReceiver;
21use settings_common::inspect::event::{Direction, ResponseType, UsageEvent};
22use settings_inspect_utils::joinable_inspect_vecdeque::JoinableInspectVecDeque;
23use settings_inspect_utils::managed_inspect_map::ManagedInspectMap;
24use settings_inspect_utils::managed_inspect_queue::ManagedInspectQueue;
25
26use fuchsia_async as fasync;
27use fuchsia_inspect::{self as inspect, component, NumericProperty};
28use fuchsia_inspect_derive::{IValue, Inspect};
29use futures::stream::FuturesUnordered;
30use futures::StreamExt;
31use std::cell::RefCell;
32use std::rc::Rc;
33
34const MAX_PENDING_REQUESTS: usize = 20;
38
39const MAX_REQUEST_RESPONSE_PAIRS: usize = 10;
41
42const MAX_REQUEST_RESPONSE_TIMESTAMPS: usize = 10;
44
45const REQUEST_RESPONSE_NODE_NAME: &str = "requests_and_responses";
47
48const RESPONSE_COUNTS_NODE_NAME: &str = "response_counts";
50
51pub(crate) fn create_registrar(rx: UnboundedReceiver<UsageEvent>) -> AgentCreator {
52 let rx = Rc::new(RefCell::new(rx));
53 AgentCreator {
54 debug_id: "SettingProxyInspectAgent",
55 create: CreationFunc::Dynamic(Rc::new(move |context| {
56 let rx = Rc::clone(&rx);
57 Box::pin(async move {
58 SettingProxyInspectAgent::create(context, rx).await;
59 })
60 })),
61 }
62}
63#[derive(Default, Inspect)]
64struct SettingTypeResponseCountInfo {
66 #[inspect(forward)]
69 response_counts_by_type: ManagedInspectMap<ResponseTypeCount>,
70}
71
72#[derive(Default, Inspect)]
73struct ResponseTypeCount {
75 count: inspect::UintProperty,
76 inspect_node: inspect::Node,
77}
78
79#[derive(Inspect)]
81struct SettingTypeRequestResponseInfo {
82 #[inspect(rename = "requests_and_responses")]
89 requests_and_responses_by_type: ManagedInspectMap<ManagedInspectMap<RequestResponsePairInfo>>,
90
91 pending_requests: ManagedInspectQueue<PendingRequestInspectInfo>,
93
94 inspect_node: inspect::Node,
96
97 #[inspect(skip)]
102 count: u64,
103}
104
105impl SettingTypeRequestResponseInfo {
106 fn new() -> Self {
107 Self {
108 requests_and_responses_by_type: Default::default(),
109 pending_requests: ManagedInspectQueue::<PendingRequestInspectInfo>::new(
110 MAX_PENDING_REQUESTS,
111 ),
112 inspect_node: Default::default(),
113 count: 0,
114 }
115 }
116}
117
118#[derive(Debug, Default, Inspect)]
120struct PendingRequestInspectInfo {
121 request: IValue<String>,
123
124 #[inspect(skip)]
127 request_type: String,
128
129 timestamp: IValue<String>,
132
133 #[inspect(skip)]
135 count: u64,
136
137 inspect_node: inspect::Node,
139}
140
141#[derive(Default, Inspect)]
147struct RequestResponsePairInfo {
148 request: IValue<String>,
150
151 response: IValue<String>,
153
154 #[inspect(skip)]
156 request_count: u64,
157
158 request_timestamps: IValue<JoinableInspectVecDeque>,
163
164 response_timestamps: IValue<JoinableInspectVecDeque>,
169
170 inspect_node: inspect::Node,
172}
173
174impl RequestResponsePairInfo {
175 fn new(request: String, response: String, count: u64) -> Self {
176 Self {
177 request: IValue::new(request),
178 response: IValue::new(response),
179 request_count: count,
180 request_timestamps: Default::default(),
181 response_timestamps: Default::default(),
182 inspect_node: Default::default(),
183 }
184 }
185}
186
187pub(crate) struct SettingProxyInspectAgent {
190 response_counts: ManagedInspectMap<SettingTypeResponseCountInfo>,
192
193 setting_request_response_info: ManagedInspectMap<SettingTypeRequestResponseInfo>,
195}
196
197impl SettingProxyInspectAgent {
198 pub(crate) async fn create(context: Context, rx: Rc<RefCell<UnboundedReceiver<UsageEvent>>>) {
199 Self::create_with_node(
200 context,
201 rx,
202 component::inspector().root().create_child(REQUEST_RESPONSE_NODE_NAME),
203 component::inspector().root().create_child(RESPONSE_COUNTS_NODE_NAME),
204 )
205 .await;
206 }
207
208 async fn create_with_node(
209 context: Context,
210 rx: Rc<RefCell<UnboundedReceiver<UsageEvent>>>,
211 request_response_inspect_node: inspect::Node,
212 response_counts_node: inspect::Node,
213 ) {
214 let (_, message_rx) = context
215 .delegate
216 .create(MessengerType::Broker(Rc::new(move |message| {
217 matches!(message.payload(), service::Payload::Setting(HandlerPayload::Request(_)))
219 })))
220 .await
221 .expect("should receive client");
222
223 let mut agent = SettingProxyInspectAgent {
224 response_counts: ManagedInspectMap::<SettingTypeResponseCountInfo>::with_node(
225 response_counts_node,
226 ),
227 setting_request_response_info:
228 ManagedInspectMap::<SettingTypeRequestResponseInfo>::with_node(
229 request_response_inspect_node,
230 ),
231 };
232
233 fasync::Task::local({
234 async move {
235 let _ = &context;
236 let id = fuchsia_trace::Id::new();
237 trace!(id, c"setting_proxy_inspect_agent");
238 let event = message_rx.fuse();
239 let agent_event = context.receptor.fuse();
240 let mut usage_event = rx.borrow_mut();
241 futures::pin_mut!(agent_event, event);
242
243 let mut unordered = FuturesUnordered::new();
246 loop {
247 futures::select! {
248 message_event = event.select_next_some() => {
249 trace!(
250 id,
251 c"message_event"
252 );
253 if let Some((setting_type, count, mut reply_receptor)) =
254 agent.process_message_event(message_event) {
255 unordered.push(async move {
256 let payload = reply_receptor.next_payload().await;
257 (setting_type, count, payload)
258 });
259 };
260 },
261 usage_event = usage_event.select_next_some() => {
262 if let Direction::Request(_) = usage_event.direction {
263 agent.process_usage_event(usage_event);
264 } else {
265 agent.process_usage_response_event(usage_event);
266 }
267 }
268 reply = unordered.select_next_some() => {
269 let (setting_type, count, payload) = reply;
270 if let Ok((
271 service::Payload::Setting(
272 HandlerPayload::Response(response)),
273 _,
274 )) = payload
275 {
276 agent.record_response(setting_type, count, response);
277 }
278 },
279 agent_message = agent_event.select_next_some() => {
280 trace!(
281 id,
282 c"agent_event"
283 );
284 if let MessageEvent::Message(
285 service::Payload::Agent(Payload::Invocation(_invocation)), client)
286 = agent_message {
287 let _ = client.reply(Payload::Complete(Ok(())).into());
290 }
291 },
292 }
293 }
294 }})
295 .detach();
296 }
297
298 fn process_usage_event(&mut self, event: UsageEvent) {
299 let timestamp = clock::inspect_format_now();
300
301 let request_response_info = self
303 .setting_request_response_info
304 .get_or_insert_with(event.setting.to_string(), SettingTypeRequestResponseInfo::new);
305
306 request_response_info.count += 1;
307
308 let Direction::Request(request) = event.direction else {
309 panic!("Call process_usage_event with response!");
310 };
311 let pending_request_info = PendingRequestInspectInfo {
312 request: request.into(),
313 request_type: format!("{:?}", event.request_type),
314 timestamp: timestamp.into(),
315 count: event.id,
316 inspect_node: inspect::Node::default(),
317 };
318
319 let count_key = format!("{:020}", event.id);
320 request_response_info.pending_requests.push(&count_key, pending_request_info);
321 }
322
323 fn process_usage_response_event(&mut self, event: UsageEvent) {
324 let setting_type_str = event.setting.to_string();
325 let timestamp = clock::inspect_format_now();
326 let Direction::Response(response, response_type) = event.direction else {
327 panic!("Called process_usage_response_event with a request!");
328 };
329
330 self.increment_response_count(setting_type_str.clone(), response_type);
332
333 let condensed_setting_type_info = self
336 .setting_request_response_info
337 .map_mut()
338 .get_mut(&setting_type_str)
339 .expect("Missing info for request");
340
341 let pending_requests = &mut condensed_setting_type_info.pending_requests;
342
343 let position = match pending_requests.iter_mut().position(|info| info.count == event.id) {
347 Some(position) => position,
348 None => {
349 return;
352 }
353 };
354 let pending =
355 pending_requests.items_mut().remove(position).expect("Failed to find pending item");
356
357 let request_type_info_map = condensed_setting_type_info
359 .requests_and_responses_by_type
360 .get_or_insert_with(pending.request_type, || {
361 ManagedInspectMap::<RequestResponsePairInfo>::default()
362 });
363
364 let map_key = format!("{:?}{:?}", pending.request, response);
367
368 let removed_info = request_type_info_map.map_mut().remove(&map_key);
373
374 let mut info = removed_info.unwrap_or_else(|| {
375 RequestResponsePairInfo::new(pending.request.into_inner(), response, pending.count)
376 });
377 {
378 let mut_requests = &mut info.request_timestamps.as_mut().0;
382 let mut_responses = &mut info.response_timestamps.as_mut().0;
383
384 mut_requests.push_back(pending.timestamp.into_inner());
385 mut_responses.push_back(timestamp);
386
387 if mut_requests.len() > MAX_REQUEST_RESPONSE_TIMESTAMPS {
389 let _ = mut_requests.pop_front();
390 }
391 if mut_responses.len() > MAX_REQUEST_RESPONSE_TIMESTAMPS {
392 let _ = mut_responses.pop_front();
393 }
394 }
395
396 let count_key = format!("{:020}", pending.count);
398 let _ = request_type_info_map.insert_with_property_name(map_key, count_key, info);
399
400 let num_request_response_pairs = request_type_info_map.map().len();
402 if num_request_response_pairs > MAX_REQUEST_RESPONSE_PAIRS {
403 let mut lowest_count: u64 = u64::MAX;
406 let mut lowest_key: Option<String> = None;
407 for (key, inspect_info) in request_type_info_map.map() {
408 if inspect_info.request_count < lowest_count {
409 lowest_count = inspect_info.request_count;
410 lowest_key = Some(key.clone());
411 }
412 }
413
414 if let Some(key_to_remove) = lowest_key {
415 let _ = request_type_info_map.map_mut().remove(&key_to_remove);
416 }
417 }
418 }
419
420 fn process_message_event(
423 &mut self,
424 event: service::message::MessageEvent,
425 ) -> Option<(SettingType, u64, Receptor)> {
426 if let Ok((HandlerPayload::Request(request), mut client)) =
427 HandlerPayload::try_from_with_client(event)
428 {
429 if let service::message::Audience::Address(service::Address::Handler(setting_type)) =
430 client.get_audience()
431 {
432 if request != Request::Listen {
436 let count = self.record_request(setting_type, request);
437 return Some((setting_type, count, client.spawn_observer()));
438 }
439 }
440 }
441 None
442 }
443
444 fn record_request(&mut self, setting_type: SettingType, request: Request) -> u64 {
448 let setting_type_str = format!("{setting_type:?}");
449 let timestamp = clock::inspect_format_now();
450
451 let request_response_info = self
453 .setting_request_response_info
454 .get_or_insert_with(setting_type_str, SettingTypeRequestResponseInfo::new);
455
456 let request_count = request_response_info.count;
457 request_response_info.count += 1;
458
459 let pending_request_info = PendingRequestInspectInfo {
460 request: format!("{request:?}").into(),
461 request_type: request.for_inspect().to_string(),
462 timestamp: timestamp.into(),
463 count: request_count,
464 inspect_node: inspect::Node::default(),
465 };
466
467 let count_key = format!("{request_count:020}");
468 request_response_info.pending_requests.push(&count_key, pending_request_info);
469
470 request_count
471 }
472
473 fn record_response(
476 &mut self,
477 setting_type: SettingType,
478 count: u64,
479 response: Result<Option<SettingInfo>, Error>,
480 ) {
481 let setting_type_str = format!("{setting_type:?}");
482 let timestamp = clock::inspect_format_now();
483
484 self.increment_response_count(
486 setting_type_str.clone(),
487 match &response {
488 Ok(Some(_)) => ResponseType::OkSome,
489 Ok(None) => ResponseType::OkNone,
490 Err(e) => ResponseType::from(e.clone()),
491 },
492 );
493
494 let condensed_setting_type_info = self
497 .setting_request_response_info
498 .map_mut()
499 .get_mut(&setting_type_str)
500 .expect("Missing info for request");
501
502 let pending_requests = &mut condensed_setting_type_info.pending_requests;
503
504 let position = match pending_requests.iter_mut().position(|info| info.count == count) {
508 Some(position) => position,
509 None => {
510 return;
513 }
514 };
515 let pending =
516 pending_requests.items_mut().remove(position).expect("Failed to find pending item");
517
518 let request_type_info_map = condensed_setting_type_info
520 .requests_and_responses_by_type
521 .get_or_insert_with(pending.request_type, || {
522 ManagedInspectMap::<RequestResponsePairInfo>::default()
523 });
524
525 let map_key = format!("{:?}{:?}", pending.request, response);
528
529 let removed_info = request_type_info_map.map_mut().remove(&map_key);
534
535 let response_str = format!("{response:?}");
536 let mut info = removed_info.unwrap_or_else(|| {
537 RequestResponsePairInfo::new(pending.request.into_inner(), response_str, pending.count)
538 });
539 {
540 let mut_requests = &mut info.request_timestamps.as_mut().0;
544 let mut_responses = &mut info.response_timestamps.as_mut().0;
545
546 mut_requests.push_back(pending.timestamp.into_inner());
547 mut_responses.push_back(timestamp);
548
549 if mut_requests.len() > MAX_REQUEST_RESPONSE_TIMESTAMPS {
551 let _ = mut_requests.pop_front();
552 }
553 if mut_responses.len() > MAX_REQUEST_RESPONSE_TIMESTAMPS {
554 let _ = mut_responses.pop_front();
555 }
556 }
557
558 let count_key = format!("{:020}", pending.count);
560 let _ = request_type_info_map.insert_with_property_name(map_key, count_key, info);
561
562 let num_request_response_pairs = request_type_info_map.map().len();
564 if num_request_response_pairs > MAX_REQUEST_RESPONSE_PAIRS {
565 let mut lowest_count: u64 = u64::MAX;
568 let mut lowest_key: Option<String> = None;
569 for (key, inspect_info) in request_type_info_map.map() {
570 if inspect_info.request_count < lowest_count {
571 lowest_count = inspect_info.request_count;
572 lowest_key = Some(key.clone());
573 }
574 }
575
576 if let Some(key_to_remove) = lowest_key {
577 let _ = request_type_info_map.map_mut().remove(&key_to_remove);
578 }
579 }
580 }
581
582 fn increment_response_count(&mut self, setting_type_str: String, response_type: ResponseType) {
583 let response_count_info = self
586 .response_counts
587 .get_or_insert_with(setting_type_str, SettingTypeResponseCountInfo::default);
588
589 let response_count = response_count_info
592 .response_counts_by_type
593 .get_or_insert_with(format!("{response_type:?}"), ResponseTypeCount::default);
594 let _ = response_count.count.add(1u64);
595 }
596}
597
598#[cfg(test)]
599mod tests {
600 use super::*;
601 use crate::display::types::SetDisplayInfo;
602 use crate::intl::types::{IntlInfo, LocaleId, TemperatureUnit};
603 use diagnostics_assertions::{assert_data_tree, TreeAssertion};
604 use futures::channel::mpsc;
605 use std::collections::HashSet;
606 use zx::MonotonicInstant;
607
608 struct RequestProcessor {
612 delegate: service::message::Delegate,
613 }
614
615 impl RequestProcessor {
616 fn new(delegate: service::message::Delegate) -> Self {
617 RequestProcessor { delegate }
618 }
619
620 async fn send_request(
621 &self,
622 setting_type: SettingType,
623 setting_request: Request,
624 should_reply: bool,
625 ) {
626 let (messenger, _) =
627 self.delegate.create(MessengerType::Unbound).await.expect("should be created");
628
629 let (_, mut receptor) = self
630 .delegate
631 .create(MessengerType::Addressable(service::Address::Handler(setting_type)))
632 .await
633 .expect("should be created");
634
635 let mut reply_receptor = messenger.message(
636 HandlerPayload::Request(setting_request).into(),
637 service::message::Audience::Address(service::Address::Handler(setting_type)),
638 );
639
640 if let Some(message_event) = futures::StreamExt::next(&mut receptor).await {
641 if let Ok((_, reply_client)) = HandlerPayload::try_from_with_client(message_event) {
642 if should_reply {
643 let _ = reply_client.reply(HandlerPayload::Response(Ok(None)).into());
644 }
645 }
646 }
647 let _ = reply_receptor.next_payload().await;
648 }
649
650 async fn send_and_receive(&self, setting_type: SettingType, setting_request: Request) {
651 self.send_request(setting_type, setting_request, true).await;
652 }
653 }
654
655 async fn create_context() -> Context {
656 Context::new(
657 service::MessageHub::create_hub()
658 .create(MessengerType::Unbound)
659 .await
660 .expect("should be present")
661 .1,
662 service::MessageHub::create_hub(),
663 HashSet::new(),
664 )
665 .await
666 }
667
668 #[fuchsia::test(allow_stalls = false)]
671 async fn test_inspect_grouped_responses() {
672 clock::mock::set(MonotonicInstant::from_nanos(0));
674
675 let inspector = inspect::Inspector::default();
676 let condense_node = inspector.root().create_child(REQUEST_RESPONSE_NODE_NAME);
677 let response_counts_node = inspector.root().create_child(RESPONSE_COUNTS_NODE_NAME);
678 let context = create_context().await;
679
680 let request_processor = RequestProcessor::new(context.delegate.clone());
681
682 let (_tx, rx) = mpsc::unbounded();
683 SettingProxyInspectAgent::create_with_node(
684 context,
685 Rc::new(RefCell::new(rx)),
686 condense_node,
687 response_counts_node,
688 )
689 .await;
690
691 let turn_off_auto_brightness = Request::SetDisplayInfo(SetDisplayInfo {
693 auto_brightness: Some(false),
694 ..SetDisplayInfo::default()
695 });
696 request_processor
697 .send_and_receive(SettingType::Display, turn_off_auto_brightness.clone())
698 .await;
699
700 clock::mock::set(MonotonicInstant::from_nanos(100));
702 request_processor
703 .send_and_receive(
704 SettingType::Display,
705 Request::SetDisplayInfo(SetDisplayInfo {
706 auto_brightness: Some(true),
707 ..SetDisplayInfo::default()
708 }),
709 )
710 .await;
711
712 clock::mock::set(MonotonicInstant::from_nanos(200));
715 request_processor.send_and_receive(SettingType::Display, turn_off_auto_brightness).await;
716
717 assert_data_tree!(inspector, root: contains {
718 requests_and_responses: {
719 "Display": {
720 "pending_requests": {},
721 "requests_and_responses": {
722 "SetDisplayInfo": {
723 "00000000000000000001": {
724 "request": "SetDisplayInfo(SetDisplayInfo { \
725 manual_brightness_value: None, \
726 auto_brightness_value: None, \
727 auto_brightness: Some(true), \
728 screen_enabled: None, \
729 low_light_mode: None, \
730 theme: None \
731 })",
732 "request_timestamps": "0.000000100",
733 "response": "Ok(None)",
734 "response_timestamps": "0.000000100"
735 },
736 "00000000000000000002": {
737 "request": "SetDisplayInfo(SetDisplayInfo { \
738 manual_brightness_value: None, \
739 auto_brightness_value: None, \
740 auto_brightness: Some(false), \
741 screen_enabled: None, \
742 low_light_mode: None, \
743 theme: None \
744 })",
745 "request_timestamps": "0.000000000,0.000000200",
746 "response": "Ok(None)",
747 "response_timestamps": "0.000000000,0.000000200"
748 }
749 }
750 }
751 }
752 },
753 });
754 }
755
756 #[fuchsia::test(allow_stalls = false)]
759 async fn test_inspect_mixed_request_types() {
760 clock::mock::set(MonotonicInstant::from_nanos(0));
762
763 let inspector = inspect::Inspector::default();
764 let condense_node = inspector.root().create_child(REQUEST_RESPONSE_NODE_NAME);
765 let response_counts_node = inspector.root().create_child(RESPONSE_COUNTS_NODE_NAME);
766 let context = create_context().await;
767
768 let request_processor = RequestProcessor::new(context.delegate.clone());
769
770 let (_tx, rx) = mpsc::unbounded();
771 SettingProxyInspectAgent::create_with_node(
772 context,
773 Rc::new(RefCell::new(rx)),
774 condense_node,
775 response_counts_node,
776 )
777 .await;
778
779 request_processor
781 .send_and_receive(
782 SettingType::Display,
783 Request::SetDisplayInfo(SetDisplayInfo {
784 auto_brightness: Some(false),
785 ..SetDisplayInfo::default()
786 }),
787 )
788 .await;
789
790 clock::mock::set(MonotonicInstant::from_nanos(100));
792 request_processor.send_and_receive(SettingType::Display, Request::Get).await;
793
794 clock::mock::set(MonotonicInstant::from_nanos(200));
796 request_processor
797 .send_and_receive(
798 SettingType::Display,
799 Request::SetDisplayInfo(SetDisplayInfo {
800 auto_brightness: Some(true),
801 ..SetDisplayInfo::default()
802 }),
803 )
804 .await;
805
806 clock::mock::set(MonotonicInstant::from_nanos(300));
807 request_processor.send_and_receive(SettingType::Display, Request::Get).await;
808
809 assert_data_tree!(inspector, root: contains {
810 requests_and_responses: {
811 "Display": {
812 "pending_requests": {},
813 "requests_and_responses": {
814 "Get": {
815 "00000000000000000003": {
816 "request": "Get",
817 "request_timestamps": "0.000000100,0.000000300",
818 "response": "Ok(None)",
819 "response_timestamps": "0.000000100,0.000000300"
820 }
821 },
822 "SetDisplayInfo": {
823 "00000000000000000000": {
824 "request": "SetDisplayInfo(SetDisplayInfo { \
825 manual_brightness_value: None, \
826 auto_brightness_value: None, \
827 auto_brightness: Some(false), \
828 screen_enabled: None, \
829 low_light_mode: None, \
830 theme: None \
831 })",
832 "request_timestamps": "0.000000000",
833 "response": "Ok(None)",
834 "response_timestamps": "0.000000000"
835 },
836 "00000000000000000002": {
837 "request": "SetDisplayInfo(SetDisplayInfo { \
838 manual_brightness_value: None, \
839 auto_brightness_value: None, \
840 auto_brightness: Some(true), \
841 screen_enabled: None, \
842 low_light_mode: None, \
843 theme: None \
844 })",
845 "request_timestamps": "0.000000200",
846 "response": "Ok(None)",
847 "response_timestamps": "0.000000200"
848 }
849 }
850 }
851 }
852 },
853 response_counts: {
854 "Display": {
855 "OkNone": {
856 count: 4u64,
857 }
858 },
859 },
860 });
861 }
862
863 #[fuchsia::test(allow_stalls = false)]
864 async fn test_pending_request() {
865 clock::mock::set(MonotonicInstant::from_nanos(0));
867
868 let inspector = inspect::Inspector::default();
869 let condense_node = inspector.root().create_child(REQUEST_RESPONSE_NODE_NAME);
870 let request_counts_node = inspector.root().create_child(RESPONSE_COUNTS_NODE_NAME);
871 let context = create_context().await;
872
873 let request_processor = RequestProcessor::new(context.delegate.clone());
874
875 let (_tx, rx) = mpsc::unbounded();
876 SettingProxyInspectAgent::create_with_node(
877 context,
878 Rc::new(RefCell::new(rx)),
879 condense_node,
880 request_counts_node,
881 )
882 .await;
883
884 request_processor
885 .send_request(
886 SettingType::Display,
887 Request::SetDisplayInfo(SetDisplayInfo {
888 auto_brightness: Some(false),
889 ..SetDisplayInfo::default()
890 }),
891 false,
892 )
893 .await;
894
895 assert_data_tree!(inspector, root: contains {
896 requests_and_responses: {
897 "Display": {
898 "pending_requests": {
899 "00000000000000000000": {
900 "request": "SetDisplayInfo(SetDisplayInfo { \
901 manual_brightness_value: None, \
902 auto_brightness_value: None, \
903 auto_brightness: Some(false), \
904 screen_enabled: None, \
905 low_light_mode: None, \
906 theme: None \
907 })",
908 "timestamp": "0.000000000",
909 }
910 },
911 "requests_and_responses": {}
912 }
913 },
914 });
915 }
916
917 #[fuchsia::test(allow_stalls = false)]
918 async fn test_response_counts_inspect() {
919 clock::mock::set(MonotonicInstant::from_nanos(0));
921
922 let inspector = inspect::Inspector::default();
923 let condense_node = inspector.root().create_child(REQUEST_RESPONSE_NODE_NAME);
924 let request_counts_node = inspector.root().create_child(RESPONSE_COUNTS_NODE_NAME);
925 let context = create_context().await;
926
927 let request_processor = RequestProcessor::new(context.delegate.clone());
928
929 let (_tx, rx) = mpsc::unbounded();
930 SettingProxyInspectAgent::create_with_node(
931 context,
932 Rc::new(RefCell::new(rx)),
933 condense_node,
934 request_counts_node,
935 )
936 .await;
937
938 request_processor
939 .send_and_receive(
940 SettingType::Display,
941 Request::SetDisplayInfo(SetDisplayInfo {
942 auto_brightness: Some(false),
943 ..SetDisplayInfo::default()
944 }),
945 )
946 .await;
947
948 clock::mock::set(MonotonicInstant::from_nanos(100));
949 request_processor.send_and_receive(SettingType::Display, Request::Get).await;
950
951 clock::mock::set(MonotonicInstant::from_nanos(200));
952 request_processor
953 .send_and_receive(
954 SettingType::Display,
955 Request::SetDisplayInfo(SetDisplayInfo {
956 auto_brightness: None,
957 ..SetDisplayInfo::default()
958 }),
959 )
960 .await;
961
962 clock::mock::set(MonotonicInstant::from_nanos(300));
963 request_processor.send_and_receive(SettingType::Display, Request::Get).await;
964
965 assert_data_tree!(inspector, root: contains {
966 response_counts: {
967 "Display": {
968 "OkNone": {
969 count: 4u64,
970 },
971 },
972 },
973 });
974 }
975
976 #[fuchsia::test(allow_stalls = false)]
979 async fn inspect_queue_test() {
980 clock::mock::set(MonotonicInstant::from_nanos(0));
982 let inspector = inspect::Inspector::default();
983 let condense_node = inspector.root().create_child(REQUEST_RESPONSE_NODE_NAME);
984 let response_counts_node = inspector.root().create_child(RESPONSE_COUNTS_NODE_NAME);
985 let context = create_context().await;
986 let request_processor = RequestProcessor::new(context.delegate.clone());
987
988 let (_tx, rx) = mpsc::unbounded();
989 SettingProxyInspectAgent::create_with_node(
990 context,
991 Rc::new(RefCell::new(rx)),
992 condense_node,
993 response_counts_node,
994 )
995 .await;
996
997 request_processor
998 .send_and_receive(
999 SettingType::Intl,
1000 Request::SetIntlInfo(IntlInfo {
1001 locales: Some(vec![LocaleId { id: "en-US".to_string() }]),
1002 temperature_unit: Some(TemperatureUnit::Celsius),
1003 time_zone_id: Some("UTC".to_string()),
1004 hour_cycle: None,
1005 }),
1006 )
1007 .await;
1008
1009 for i in 0..MAX_REQUEST_RESPONSE_PAIRS + 1 {
1012 request_processor
1013 .send_and_receive(
1014 SettingType::Display,
1015 Request::SetDisplayInfo(SetDisplayInfo {
1016 manual_brightness_value: Some((i as f32) / 100f32),
1017 ..SetDisplayInfo::default()
1018 }),
1019 )
1020 .await;
1021 }
1022
1023 fn display_subtree_assertion() -> TreeAssertion {
1026 let mut tree_assertion = TreeAssertion::new("Display", false);
1027 let mut request_response_assertion = TreeAssertion::new("requests_and_responses", true);
1028 let mut request_assertion = TreeAssertion::new("SetDisplayInfo", true);
1029
1030 for i in 1..MAX_REQUEST_RESPONSE_PAIRS + 1 {
1031 request_assertion
1033 .add_child_assertion(TreeAssertion::new(&format!("{i:020}"), false));
1034 }
1035 request_response_assertion.add_child_assertion(request_assertion);
1036 tree_assertion.add_child_assertion(request_response_assertion);
1037 tree_assertion
1038 }
1039
1040 assert_data_tree!(inspector, root: contains {
1041 requests_and_responses: {
1042 display_subtree_assertion(),
1043 "Intl": {
1044 "pending_requests": {},
1045 "requests_and_responses": {
1046 "SetIntlInfo": {
1047 "00000000000000000000": {
1048 "request": "SetIntlInfo(IntlInfo { \
1049 locales: Some([LocaleId { id: \"en-US\" }]), \
1050 temperature_unit: Some(Celsius), \
1051 time_zone_id: Some(\"UTC\"), \
1052 hour_cycle: None \
1053 })",
1054 "request_timestamps": "0.000000000",
1055 "response": "Ok(None)",
1056 "response_timestamps": "0.000000000"
1057 }
1058 }
1059 }
1060 }
1061 },
1062 });
1063 }
1064}