wlan_telemetry/processors/
scan.rs1use crate::util::cobalt_logger::log_cobalt_batch;
6use fidl_fuchsia_metrics::{MetricEvent, MetricEventPayload};
7use {
8 fidl_fuchsia_power_battery as fidl_battery, fuchsia_async as fasync,
9 wlan_legacy_metrics_registry as metrics,
10};
11
12#[derive(Debug, PartialEq)]
13pub enum ScanResult {
14 Complete { num_results: usize },
15 Failed,
16 Cancelled,
17}
18
19pub struct ScanLogger {
20 cobalt_proxy: fidl_fuchsia_metrics::MetricEventLoggerProxy,
21 scan_started_at: Option<fasync::BootInstant>,
22 on_battery: bool,
23}
24
25impl ScanLogger {
26 pub fn new(cobalt_proxy: fidl_fuchsia_metrics::MetricEventLoggerProxy) -> Self {
27 Self { cobalt_proxy, scan_started_at: None, on_battery: false }
28 }
29
30 pub async fn handle_scan_start(&mut self) {
31 self.scan_started_at = Some(fasync::BootInstant::now());
32 let mut metric_events = vec![MetricEvent {
33 metric_id: metrics::SCAN_OCCURRENCE_METRIC_ID,
34 event_codes: vec![],
35 payload: MetricEventPayload::Count(1),
36 }];
37 if self.on_battery {
38 metric_events.push(MetricEvent {
39 metric_id: metrics::SCAN_OCCURRENCE_ON_BATTERY_METRIC_ID,
40 event_codes: vec![],
41 payload: MetricEventPayload::Count(1),
42 });
43 }
44 log_cobalt_batch!(self.cobalt_proxy, &metric_events, "handle_scan_start");
45 }
46
47 pub async fn handle_scan_result(&mut self, result: ScanResult) {
48 let mut metric_events = vec![];
49 let now = fasync::BootInstant::now();
50 if let Some(scan_started_at) = self.scan_started_at.take() {
52 match result {
53 ScanResult::Complete { num_results } => {
54 let scan_duration = now - scan_started_at;
55 metric_events.push(MetricEvent {
56 metric_id: metrics::SCAN_FULFILLMENT_TIME_METRIC_ID,
57 event_codes: vec![],
58 payload: MetricEventPayload::IntegerValue(scan_duration.into_millis()),
59 });
60 if num_results == 0 {
61 metric_events.push(MetricEvent {
62 metric_id: metrics::EMPTY_SCAN_RESULTS_METRIC_ID,
63 event_codes: vec![],
64 payload: MetricEventPayload::Count(1),
65 });
66 }
67 }
68 ScanResult::Failed => {
69 metric_events.push(MetricEvent {
70 metric_id: metrics::CLIENT_SCAN_FAILURE_METRIC_ID,
71 event_codes: vec![],
72 payload: MetricEventPayload::Count(1),
73 });
74 }
75 ScanResult::Cancelled => {
76 metric_events.push(MetricEvent {
77 metric_id: metrics::ABORTED_SCAN_METRIC_ID,
78 event_codes: vec![],
79 payload: MetricEventPayload::Count(1),
80 });
81 }
82 }
83 }
84
85 log_cobalt_batch!(self.cobalt_proxy, &metric_events, "handle_scan_result");
86 }
87
88 pub async fn handle_battery_charge_status(
89 &mut self,
90 charge_status: fidl_battery::ChargeStatus,
91 ) {
92 self.on_battery = matches!(charge_status, fidl_battery::ChargeStatus::Discharging);
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99 use crate::testing::{setup_test, TestHelper};
100 use futures::task::Poll;
101 use std::pin::pin;
102 use test_case::test_case;
103
104 fn run_handle_scan_start(test_helper: &mut TestHelper, scan_logger: &mut ScanLogger) {
105 let mut test_fut = pin!(scan_logger.handle_scan_start());
106 assert_eq!(
107 test_helper.run_until_stalled_drain_cobalt_events(&mut test_fut),
108 Poll::Ready(())
109 );
110 }
111
112 fn run_handle_scan_result(
113 test_helper: &mut TestHelper,
114 scan_logger: &mut ScanLogger,
115 scan_result: ScanResult,
116 ) {
117 let mut test_fut = pin!(scan_logger.handle_scan_result(scan_result));
118 assert_eq!(
119 test_helper.run_until_stalled_drain_cobalt_events(&mut test_fut),
120 Poll::Ready(())
121 );
122 }
123
124 fn run_handle_battery_charge_status(
125 test_helper: &mut TestHelper,
126 scan_logger: &mut ScanLogger,
127 charge_status: fidl_battery::ChargeStatus,
128 ) {
129 let mut test_fut = pin!(scan_logger.handle_battery_charge_status(charge_status));
130 assert_eq!(
131 test_helper.run_until_stalled_drain_cobalt_events(&mut test_fut),
132 Poll::Ready(())
133 );
134 }
135
136 #[fuchsia::test]
137 fn test_handle_scan_start() {
138 let mut test_helper = setup_test();
139 let mut scan_logger = ScanLogger::new(test_helper.cobalt_proxy.clone());
140
141 run_handle_scan_start(&mut test_helper, &mut scan_logger);
142
143 let metrics = test_helper.get_logged_metrics(metrics::SCAN_OCCURRENCE_METRIC_ID);
144 assert_eq!(metrics.len(), 1);
145 assert_eq!(metrics[0].payload, MetricEventPayload::Count(1));
146
147 let metrics = test_helper.get_logged_metrics(metrics::SCAN_OCCURRENCE_ON_BATTERY_METRIC_ID);
148 assert!(metrics.is_empty());
149 }
150
151 #[fuchsia::test]
152 fn test_handle_scan_start_on_battery() {
153 let mut test_helper = setup_test();
154 let mut scan_logger = ScanLogger::new(test_helper.cobalt_proxy.clone());
155
156 run_handle_battery_charge_status(
157 &mut test_helper,
158 &mut scan_logger,
159 fidl_battery::ChargeStatus::Discharging,
160 );
161 run_handle_scan_start(&mut test_helper, &mut scan_logger);
162
163 let metrics = test_helper.get_logged_metrics(metrics::SCAN_OCCURRENCE_METRIC_ID);
164 assert_eq!(metrics.len(), 1);
165 assert_eq!(metrics[0].payload, MetricEventPayload::Count(1));
166
167 let metrics = test_helper.get_logged_metrics(metrics::SCAN_OCCURRENCE_ON_BATTERY_METRIC_ID);
168 assert_eq!(metrics.len(), 1);
169 assert_eq!(metrics[0].payload, MetricEventPayload::Count(1));
170
171 test_helper.clear_cobalt_events();
174 run_handle_battery_charge_status(
175 &mut test_helper,
176 &mut scan_logger,
177 fidl_battery::ChargeStatus::Charging,
178 );
179 run_handle_scan_start(&mut test_helper, &mut scan_logger);
180
181 let metrics = test_helper.get_logged_metrics(metrics::SCAN_OCCURRENCE_ON_BATTERY_METRIC_ID);
182 assert!(metrics.is_empty());
183 }
184
185 #[fuchsia::test]
186 fn test_handle_scan_result_complete() {
187 let mut test_helper = setup_test();
188 let mut scan_logger = ScanLogger::new(test_helper.cobalt_proxy.clone());
189
190 test_helper.exec.set_fake_time(fasync::MonotonicInstant::from_nanos(20_000_000));
191 run_handle_scan_start(&mut test_helper, &mut scan_logger);
192
193 test_helper.exec.set_fake_time(fasync::MonotonicInstant::from_nanos(100_000_000));
194 let scan_result = ScanResult::Complete { num_results: 10 };
195 run_handle_scan_result(&mut test_helper, &mut scan_logger, scan_result);
196
197 let metrics = test_helper.get_logged_metrics(metrics::SCAN_FULFILLMENT_TIME_METRIC_ID);
198 assert_eq!(metrics.len(), 1);
199 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(80)); let metrics = test_helper.get_logged_metrics(metrics::EMPTY_SCAN_RESULTS_METRIC_ID);
201 assert!(metrics.is_empty());
202 }
203
204 #[fuchsia::test]
205 fn test_handle_scan_result_empty() {
206 let mut test_helper = setup_test();
207 let mut scan_logger = ScanLogger::new(test_helper.cobalt_proxy.clone());
208
209 run_handle_scan_start(&mut test_helper, &mut scan_logger);
210
211 let scan_result = ScanResult::Complete { num_results: 0 };
212 run_handle_scan_result(&mut test_helper, &mut scan_logger, scan_result);
213
214 let metrics = test_helper.get_logged_metrics(metrics::SCAN_FULFILLMENT_TIME_METRIC_ID);
215 assert_eq!(metrics.len(), 1);
216 let metrics = test_helper.get_logged_metrics(metrics::EMPTY_SCAN_RESULTS_METRIC_ID);
217 assert_eq!(metrics.len(), 1);
218 assert_eq!(metrics[0].payload, MetricEventPayload::Count(1));
219 }
220
221 #[fuchsia::test]
222 fn test_handle_scan_result_cancelled() {
223 let mut test_helper = setup_test();
224 let mut scan_logger = ScanLogger::new(test_helper.cobalt_proxy.clone());
225
226 run_handle_scan_start(&mut test_helper, &mut scan_logger);
227
228 let scan_result = ScanResult::Cancelled;
229 run_handle_scan_result(&mut test_helper, &mut scan_logger, scan_result);
230
231 let metrics = test_helper.get_logged_metrics(metrics::ABORTED_SCAN_METRIC_ID);
232 assert_eq!(metrics.len(), 1);
233 assert_eq!(metrics[0].payload, MetricEventPayload::Count(1));
234 }
235
236 #[fuchsia::test]
237 fn test_handle_scan_result_failure() {
238 let mut test_helper = setup_test();
239 let mut scan_logger = ScanLogger::new(test_helper.cobalt_proxy.clone());
240
241 run_handle_scan_start(&mut test_helper, &mut scan_logger);
242
243 let scan_result = ScanResult::Failed;
244 run_handle_scan_result(&mut test_helper, &mut scan_logger, scan_result);
245
246 let metrics = test_helper.get_logged_metrics(metrics::CLIENT_SCAN_FAILURE_METRIC_ID);
247 assert_eq!(metrics.len(), 1);
248 assert_eq!(metrics[0].payload, MetricEventPayload::Count(1));
249 }
250
251 #[test_case(
252 ScanResult::Complete { num_results: 10 },
253 metrics::SCAN_FULFILLMENT_TIME_METRIC_ID;
254 "scan complete"
255 )]
256 #[test_case(
257 ScanResult::Failed,
258 metrics::CLIENT_SCAN_FAILURE_METRIC_ID;
259 "scan failed"
260 )]
261 #[test_case(
262 ScanResult::Cancelled,
263 metrics::ABORTED_SCAN_METRIC_ID;
264 "scan cancelled"
265 )]
266 #[fuchsia::test(add_test_attr = false)]
267 fn test_handle_scan_result_no_logging_to_cobalt_if_scan_not_started(
268 scan_result: ScanResult,
269 metric_id: u32,
270 ) {
271 let mut test_helper = setup_test();
272 let mut scan_logger = ScanLogger::new(test_helper.cobalt_proxy.clone());
273
274 run_handle_scan_result(&mut test_helper, &mut scan_logger, scan_result);
275
276 let metrics = test_helper.get_logged_metrics(metric_id);
277 assert!(metrics.is_empty());
278 }
279}