wlan_telemetry/processors/
scan.rs1use crate::util::cobalt_logger::log_cobalt_batch;
6use fidl_fuchsia_metrics::{MetricEvent, MetricEventPayload};
7use {fuchsia_async as fasync, wlan_legacy_metrics_registry as metrics};
8
9#[derive(Debug, PartialEq)]
10pub enum ScanResult {
11 Complete { num_results: usize },
12 Failed,
13 Cancelled,
14}
15
16pub struct ScanLogger {
17 cobalt_proxy: fidl_fuchsia_metrics::MetricEventLoggerProxy,
18 scan_started_at: Option<fasync::BootInstant>,
19}
20
21impl ScanLogger {
22 pub fn new(cobalt_proxy: fidl_fuchsia_metrics::MetricEventLoggerProxy) -> Self {
23 Self { cobalt_proxy, scan_started_at: None }
24 }
25
26 pub async fn handle_scan_start(&mut self) {
27 self.scan_started_at = Some(fasync::BootInstant::now());
28 let metric_events = vec![MetricEvent {
29 metric_id: metrics::SCAN_OCCURRENCE_METRIC_ID,
30 event_codes: vec![],
31 payload: MetricEventPayload::Count(1),
32 }];
33 log_cobalt_batch!(self.cobalt_proxy, &metric_events, "handle_scan_start");
34 }
35
36 pub async fn handle_scan_result(&mut self, result: ScanResult) {
37 let mut metric_events = vec![];
38 let now = fasync::BootInstant::now();
39 if let Some(scan_started_at) = self.scan_started_at.take() {
41 match result {
42 ScanResult::Complete { num_results } => {
43 let scan_duration = now - scan_started_at;
44 metric_events.push(MetricEvent {
45 metric_id: metrics::SCAN_FULFILLMENT_TIME_METRIC_ID,
46 event_codes: vec![],
47 payload: MetricEventPayload::IntegerValue(scan_duration.into_millis()),
48 });
49 if num_results == 0 {
50 metric_events.push(MetricEvent {
51 metric_id: metrics::EMPTY_SCAN_RESULTS_METRIC_ID,
52 event_codes: vec![],
53 payload: MetricEventPayload::Count(1),
54 });
55 }
56 }
57 ScanResult::Failed => {
58 metric_events.push(MetricEvent {
59 metric_id: metrics::CLIENT_SCAN_FAILURE_METRIC_ID,
60 event_codes: vec![],
61 payload: MetricEventPayload::Count(1),
62 });
63 }
64 ScanResult::Cancelled => {
65 metric_events.push(MetricEvent {
66 metric_id: metrics::ABORTED_SCAN_METRIC_ID,
67 event_codes: vec![],
68 payload: MetricEventPayload::Count(1),
69 });
70 }
71 }
72 }
73
74 if !metric_events.is_empty() {
75 log_cobalt_batch!(self.cobalt_proxy, &metric_events, "handle_scan_result");
76 }
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83 use crate::testing::{setup_test, TestHelper};
84 use futures::task::Poll;
85 use std::pin::pin;
86 use test_case::test_case;
87
88 fn run_handle_scan_start(test_helper: &mut TestHelper, scan_logger: &mut ScanLogger) {
89 let mut test_fut = pin!(scan_logger.handle_scan_start());
90 assert_eq!(
91 test_helper.run_until_stalled_drain_cobalt_events(&mut test_fut),
92 Poll::Ready(())
93 );
94 }
95
96 fn run_handle_scan_result(
97 test_helper: &mut TestHelper,
98 scan_logger: &mut ScanLogger,
99 scan_result: ScanResult,
100 ) {
101 let mut test_fut = pin!(scan_logger.handle_scan_result(scan_result));
102 assert_eq!(
103 test_helper.run_until_stalled_drain_cobalt_events(&mut test_fut),
104 Poll::Ready(())
105 );
106 }
107
108 #[fuchsia::test]
109 fn test_handle_scan_start() {
110 let mut test_helper = setup_test();
111 let mut scan_logger = ScanLogger::new(test_helper.cobalt_proxy.clone());
112
113 run_handle_scan_start(&mut test_helper, &mut scan_logger);
114
115 let metrics = test_helper.get_logged_metrics(metrics::SCAN_OCCURRENCE_METRIC_ID);
116 assert_eq!(metrics.len(), 1);
117 assert_eq!(metrics[0].payload, MetricEventPayload::Count(1));
118 }
119
120 #[fuchsia::test]
121 fn test_handle_scan_result_complete() {
122 let mut test_helper = setup_test();
123 let mut scan_logger = ScanLogger::new(test_helper.cobalt_proxy.clone());
124
125 test_helper.exec.set_fake_time(fasync::MonotonicInstant::from_nanos(20_000_000));
126 run_handle_scan_start(&mut test_helper, &mut scan_logger);
127
128 test_helper.exec.set_fake_time(fasync::MonotonicInstant::from_nanos(100_000_000));
129 let scan_result = ScanResult::Complete { num_results: 10 };
130 run_handle_scan_result(&mut test_helper, &mut scan_logger, scan_result);
131
132 let metrics = test_helper.get_logged_metrics(metrics::SCAN_FULFILLMENT_TIME_METRIC_ID);
133 assert_eq!(metrics.len(), 1);
134 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(80)); let metrics = test_helper.get_logged_metrics(metrics::EMPTY_SCAN_RESULTS_METRIC_ID);
136 assert!(metrics.is_empty());
137 }
138
139 #[fuchsia::test]
140 fn test_handle_scan_result_empty() {
141 let mut test_helper = setup_test();
142 let mut scan_logger = ScanLogger::new(test_helper.cobalt_proxy.clone());
143
144 run_handle_scan_start(&mut test_helper, &mut scan_logger);
145
146 let scan_result = ScanResult::Complete { num_results: 0 };
147 run_handle_scan_result(&mut test_helper, &mut scan_logger, scan_result);
148
149 let metrics = test_helper.get_logged_metrics(metrics::SCAN_FULFILLMENT_TIME_METRIC_ID);
150 assert_eq!(metrics.len(), 1);
151 let metrics = test_helper.get_logged_metrics(metrics::EMPTY_SCAN_RESULTS_METRIC_ID);
152 assert_eq!(metrics.len(), 1);
153 assert_eq!(metrics[0].payload, MetricEventPayload::Count(1));
154 }
155
156 #[fuchsia::test]
157 fn test_handle_scan_result_cancelled() {
158 let mut test_helper = setup_test();
159 let mut scan_logger = ScanLogger::new(test_helper.cobalt_proxy.clone());
160
161 run_handle_scan_start(&mut test_helper, &mut scan_logger);
162
163 let scan_result = ScanResult::Cancelled;
164 run_handle_scan_result(&mut test_helper, &mut scan_logger, scan_result);
165
166 let metrics = test_helper.get_logged_metrics(metrics::ABORTED_SCAN_METRIC_ID);
167 assert_eq!(metrics.len(), 1);
168 assert_eq!(metrics[0].payload, MetricEventPayload::Count(1));
169 }
170
171 #[fuchsia::test]
172 fn test_handle_scan_result_failure() {
173 let mut test_helper = setup_test();
174 let mut scan_logger = ScanLogger::new(test_helper.cobalt_proxy.clone());
175
176 run_handle_scan_start(&mut test_helper, &mut scan_logger);
177
178 let scan_result = ScanResult::Failed;
179 run_handle_scan_result(&mut test_helper, &mut scan_logger, scan_result);
180
181 let metrics = test_helper.get_logged_metrics(metrics::CLIENT_SCAN_FAILURE_METRIC_ID);
182 assert_eq!(metrics.len(), 1);
183 assert_eq!(metrics[0].payload, MetricEventPayload::Count(1));
184 }
185
186 #[test_case(
187 ScanResult::Complete { num_results: 10 },
188 metrics::SCAN_FULFILLMENT_TIME_METRIC_ID;
189 "scan complete"
190 )]
191 #[test_case(
192 ScanResult::Failed,
193 metrics::CLIENT_SCAN_FAILURE_METRIC_ID;
194 "scan failed"
195 )]
196 #[test_case(
197 ScanResult::Cancelled,
198 metrics::ABORTED_SCAN_METRIC_ID;
199 "scan cancelled"
200 )]
201 #[fuchsia::test(add_test_attr = false)]
202 fn test_handle_scan_result_no_logging_to_cobalt_if_scan_not_started(
203 scan_result: ScanResult,
204 metric_id: u32,
205 ) {
206 let mut test_helper = setup_test();
207 let mut scan_logger = ScanLogger::new(test_helper.cobalt_proxy.clone());
208
209 run_handle_scan_result(&mut test_helper, &mut scan_logger, scan_result);
210
211 let metrics = test_helper.get_logged_metrics(metric_id);
212 assert!(metrics.is_empty());
213 }
214}