1use crate::util::cobalt_logger::log_cobalt_batch;
6use fidl_fuchsia_metrics::{MetricEvent, MetricEventPayload};
7use fuchsia_async::{self as fasync, TimeoutExt};
8use fuchsia_inspect_contrib::nodes::LruCacheNode;
9use fuchsia_inspect_derive::{InspectNode, Unit};
10use futures::TryFutureExt;
11use futures::lock::Mutex;
12use {fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211, fidl_fuchsia_wlan_stats as fidl_stats};
13
14use log::{error, warn};
15use std::collections::HashMap;
16use std::sync::Arc;
17use std::sync::atomic::{AtomicI64, Ordering};
18use windowed_stats::experimental::clock::Timed;
19use windowed_stats::experimental::series::interpolation::{ConstantSample, LastSample};
20use windowed_stats::experimental::series::metadata::BitSetNode;
21use windowed_stats::experimental::series::statistic::{
22 ArithmeticMean, Last, LatchMax, Max, Min, PostAggregation, Sum, Union,
23};
24use windowed_stats::experimental::series::{SamplingProfile, TimeMatrix};
25use windowed_stats::experimental::serve::{InspectSender, InspectedTimeMatrix};
26use wlan_legacy_metrics_registry as metrics;
27
28const GET_IFACE_STATS_TIMEOUT: zx::MonotonicDuration = zx::MonotonicDuration::from_seconds(5);
30const GET_SIGNAL_REPORT_TIMEOUT: zx::MonotonicDuration = zx::MonotonicDuration::from_seconds(5);
31
32#[derive(Debug)]
33enum IfaceState {
34 NotAvailable,
35 Created { iface_id: u16, telemetry_proxy: Option<fidl_fuchsia_wlan_sme::TelemetryProxy> },
36}
37
38#[derive(Debug)]
39enum GetIfaceStatsError {
40 FutureTimeout,
41 FidlError,
42 ResponseError,
43}
44
45type CountersTimeSeriesMap = HashMap<u16, InspectedTimeMatrix<u64>>;
46type GaugesTimeSeriesMap = HashMap<u16, Vec<InspectedTimeMatrix<i64>>>;
47
48pub struct ClientIfaceCountersLogger<S> {
49 iface_state: Arc<Mutex<IfaceState>>,
50 cobalt_proxy: fidl_fuchsia_metrics::MetricEventLoggerProxy,
51 monitor_svc_proxy: fidl_fuchsia_wlan_device_service::DeviceMonitorProxy,
52 inspect_metadata_node: Mutex<InspectMetadataNode>,
53 time_series_stats: IfaceCountersTimeSeries,
54 signal_time_series: SignalTimeSeries,
55 driver_counters_time_matrix_client: S,
56 driver_counters_time_series: Arc<Mutex<CountersTimeSeriesMap>>,
57 driver_gauges_time_matrix_client: S,
58 driver_gauges_time_series: Arc<Mutex<GaugesTimeSeriesMap>>,
59 prev_connection_stats: Arc<Mutex<Option<fidl_stats::ConnectionStats>>>,
60 boot_mono_drift: AtomicI64,
61}
62
63impl<S: InspectSender> ClientIfaceCountersLogger<S> {
64 pub fn new(
65 cobalt_proxy: fidl_fuchsia_metrics::MetricEventLoggerProxy,
66 monitor_svc_proxy: fidl_fuchsia_wlan_device_service::DeviceMonitorProxy,
67 inspect_metadata_node: &InspectNode,
68 inspect_metadata_path: &str,
69 time_matrix_client: &S,
70 driver_counters_time_matrix_client: S,
71 driver_gauges_time_matrix_client: S,
72 ) -> Self {
73 Self {
74 iface_state: Arc::new(Mutex::new(IfaceState::NotAvailable)),
75 cobalt_proxy,
76 monitor_svc_proxy,
77 inspect_metadata_node: Mutex::new(InspectMetadataNode::new(inspect_metadata_node)),
78 time_series_stats: IfaceCountersTimeSeries::new(time_matrix_client),
79 signal_time_series: SignalTimeSeries::new(time_matrix_client, inspect_metadata_path),
80 driver_counters_time_matrix_client,
81 driver_counters_time_series: Arc::new(Mutex::new(HashMap::new())),
82 driver_gauges_time_matrix_client,
83 driver_gauges_time_series: Arc::new(Mutex::new(HashMap::new())),
84 prev_connection_stats: Arc::new(Mutex::new(None)),
85 boot_mono_drift: AtomicI64::new(0),
86 }
87 }
88
89 pub async fn handle_iface_created(&self, iface_id: u16) {
90 let (proxy, server) = fidl::endpoints::create_proxy();
91 let telemetry_proxy = match self.monitor_svc_proxy.get_sme_telemetry(iface_id, server).await
92 {
93 Ok(Ok(())) => {
94 let (inspect_counter_configs, inspect_gauge_configs) = match proxy
95 .query_telemetry_support()
96 .await
97 {
98 Ok(Ok(support)) => {
99 (support.inspect_counter_configs, support.inspect_gauge_configs)
100 }
101 Ok(Err(code)) => {
102 warn!(
103 "Failed to query telemetry support with status code {}. No driver-specific stats will be captured",
104 code
105 );
106 (None, None)
107 }
108 Err(e) => {
109 error!(
110 "Failed to query telemetry support with error {}. No driver-specific stats will be captured",
111 e
112 );
113 (None, None)
114 }
115 };
116 if let Some(inspect_counter_configs) = &inspect_counter_configs {
117 let mut driver_counters_time_series =
118 self.driver_counters_time_series.lock().await;
119 for inspect_counter_config in inspect_counter_configs {
120 if let fidl_stats::InspectCounterConfig {
121 counter_id: Some(counter_id),
122 counter_name: Some(counter_name),
123 ..
124 } = inspect_counter_config
125 {
126 let _time_matrix_ref = driver_counters_time_series
127 .entry(*counter_id)
128 .or_insert_with(|| {
129 self.driver_counters_time_matrix_client.inspect_time_matrix(
130 counter_name,
131 TimeMatrix::<LatchMax<u64>, LastSample>::new(
132 SamplingProfile::balanced(),
133 LastSample::or(0),
134 ),
135 )
136 });
137 }
138 }
139 }
140 if let Some(inspect_gauge_configs) = &inspect_gauge_configs {
141 let mut driver_gauges_time_series = self.driver_gauges_time_series.lock().await;
142 for inspect_gauge_config in inspect_gauge_configs {
143 if let fidl_stats::InspectGaugeConfig {
144 gauge_id: Some(gauge_id),
145 gauge_name: Some(gauge_name),
146 statistics: Some(statistics),
147 ..
148 } = inspect_gauge_config
149 {
150 for statistic in statistics {
151 if let Some(time_matrix) = create_time_series_for_gauge(
152 &self.driver_gauges_time_matrix_client,
153 gauge_name,
154 statistic,
155 ) {
156 let time_matrices =
157 driver_gauges_time_series.entry(*gauge_id).or_default();
158 time_matrices.push(time_matrix);
159 }
160 }
161 }
162 }
163 }
164 Some(proxy)
165 }
166 Ok(Err(e)) => {
167 error!(
168 "Request for SME telemetry for iface {} completed with error {}. No telemetry will be captured.",
169 iface_id, e
170 );
171 None
172 }
173 Err(e) => {
174 error!(
175 "Failed to request SME telemetry for iface {} with error {}. No telemetry will be captured.",
176 iface_id, e
177 );
178 None
179 }
180 };
181 *self.iface_state.lock().await = IfaceState::Created { iface_id, telemetry_proxy }
182 }
183
184 pub async fn handle_iface_destroyed(&self, iface_id: u16) {
185 let destroyed = matches!(*self.iface_state.lock().await, IfaceState::Created { iface_id: existing_iface_id, .. } if iface_id == existing_iface_id);
186 if destroyed {
187 *self.iface_state.lock().await = IfaceState::NotAvailable;
188 }
189 }
190
191 pub async fn handle_periodic_telemetry(&self) {
192 let boot_now = fasync::BootInstant::now();
193 let mono_now = fasync::MonotonicInstant::now();
194 let boot_mono_drift = boot_now.into_nanos() - mono_now.into_nanos();
195 let prev_boot_mono_drift = self.boot_mono_drift.swap(boot_mono_drift, Ordering::SeqCst);
196 let suspended_during_last_period = boot_mono_drift > prev_boot_mono_drift;
199 match &*self.iface_state.lock().await {
200 IfaceState::NotAvailable => (),
201 IfaceState::Created { telemetry_proxy, .. } => {
202 if let Some(telemetry_proxy) = &telemetry_proxy {
203 let result = telemetry_proxy
204 .get_iface_stats()
205 .map_err(|_e| GetIfaceStatsError::FidlError)
206 .map_ok(|response| response.map_err(|_e| GetIfaceStatsError::ResponseError))
207 .on_timeout(GET_IFACE_STATS_TIMEOUT, || {
208 Err(GetIfaceStatsError::FutureTimeout)
209 })
210 .await;
211
212 match result {
213 Ok(Ok(stats)) => {
214 self.log_iface_stats_inspect(&stats).await;
215 self.log_iface_stats_cobalt(stats, suspended_during_last_period).await;
216 }
217 Ok(Err(e)) | Err(e) => {
218 warn!("Failed to get interface stats: {:?}", e);
219 self.log_get_iface_stats_failure_cobalt(&e).await;
220 }
221 }
222
223 match telemetry_proxy
224 .get_signal_report()
225 .on_timeout(GET_SIGNAL_REPORT_TIMEOUT, || {
226 Ok(Err(zx::Status::TIMED_OUT.into_raw()))
227 })
228 .await
229 {
230 Ok(Ok(report)) => {
231 self.log_signal_report_inspect(&report).await;
232 }
233 error => {
234 warn!("Failed to get signal report: {:?}", error);
235 }
236 }
237 }
238 }
239 }
240 }
241
242 async fn log_iface_stats_inspect(&self, stats: &fidl_stats::IfaceStats) {
243 if let Some(counters) = &stats.driver_specific_counters {
245 let time_series = Arc::clone(&self.driver_counters_time_series);
246 log_driver_specific_counters(&counters[..], time_series).await;
247 }
248 if let Some(gauges) = &stats.driver_specific_gauges {
250 let time_series = Arc::clone(&self.driver_gauges_time_series);
251 log_driver_specific_gauges(&gauges[..], time_series).await;
252 }
253 log_connection_stats_inspect(
254 stats,
255 &self.time_series_stats,
256 Arc::clone(&self.driver_counters_time_series),
257 Arc::clone(&self.driver_gauges_time_series),
258 )
259 .await;
260 }
261
262 async fn log_iface_stats_cobalt(
263 &self,
264 stats: fidl_stats::IfaceStats,
265 suspended_during_last_period: bool,
266 ) {
267 let mut prev_connection_stats = self.prev_connection_stats.lock().await;
268 if !suspended_during_last_period {
270 if let (Some(prev_connection_stats), Some(current_connection_stats)) =
271 (prev_connection_stats.as_ref(), stats.connection_stats.as_ref())
272 {
273 match (prev_connection_stats.connection_id, current_connection_stats.connection_id)
274 {
275 (Some(prev_id), Some(current_id)) if prev_id == current_id => {
276 diff_and_log_connection_stats_cobalt(
277 &self.cobalt_proxy,
278 prev_connection_stats,
279 current_connection_stats,
280 )
281 .await;
282 }
283 _ => (),
284 }
285 }
286 }
287 *prev_connection_stats = stats.connection_stats;
288 }
289
290 async fn log_get_iface_stats_failure_cobalt(&self, error: &GetIfaceStatsError) {
291 let mut metric_events = vec![MetricEvent {
292 metric_id: metrics::GET_IFACE_STATS_FAILURE_METRIC_ID,
293 event_codes: vec![],
294 payload: MetricEventPayload::Count(1),
295 }];
296 match error {
297 GetIfaceStatsError::FutureTimeout => {
298 metric_events.push(MetricEvent {
299 metric_id: metrics::GET_IFACE_STATS_TIMEOUT_METRIC_ID,
300 event_codes: vec![],
301 payload: MetricEventPayload::Count(1),
302 });
303 }
304 GetIfaceStatsError::FidlError | GetIfaceStatsError::ResponseError => {
305 metric_events.push(MetricEvent {
306 metric_id: metrics::GET_IFACE_STATS_ERROR_IN_RESPONSE_METRIC_ID,
307 event_codes: vec![],
308 payload: MetricEventPayload::Count(1),
309 });
310 }
311 }
312 log_cobalt_batch!(self.cobalt_proxy, &metric_events, "log_get_iface_stats_failure_cobalt");
313 }
314
315 async fn log_signal_report_inspect(&self, report: &fidl_stats::SignalReport) {
316 if let Some(report) = &report.connection_signal_report {
317 if let Some(channel) = report.channel {
318 let channel = InspectWlanChannel::from(channel);
319 let channel_id =
320 self.inspect_metadata_node.lock().await.wlan_channels.insert(channel) as u64;
321 self.signal_time_series
322 .wlan_channels
323 .fold_or_log_error(Timed::now(1 << channel_id));
324 }
325 if let Some(tx_rate_500kbps) = report.tx_rate_500kbps {
326 self.signal_time_series
327 .tx_rate_500kbps
328 .fold_or_log_error(Timed::now(tx_rate_500kbps as u64));
329 }
330 if let Some(rssi) = report.rssi_dbm {
331 self.signal_time_series.rssi.fold_or_log_error(Timed::now(rssi as i64));
332 }
333 if let Some(snr) = report.snr_db {
334 self.signal_time_series.snr.fold_or_log_error(Timed::now(snr as i64));
335 }
336 }
337 }
338}
339
340fn create_time_series_for_gauge<S: InspectSender>(
341 time_matrix_client: &S,
342 gauge_name: &str,
343 statistic: &fidl_stats::GaugeStatistic,
344) -> Option<InspectedTimeMatrix<i64>> {
345 match statistic {
346 fidl_stats::GaugeStatistic::Min => Some(time_matrix_client.inspect_time_matrix(
347 format!("{gauge_name}.min"),
348 TimeMatrix::<Min<i64>, ConstantSample>::new(
349 SamplingProfile::balanced(),
350 ConstantSample::default(),
351 ),
352 )),
353 fidl_stats::GaugeStatistic::Max => Some(time_matrix_client.inspect_time_matrix(
354 format!("{gauge_name}.max"),
355 TimeMatrix::<Max<i64>, ConstantSample>::new(
356 SamplingProfile::balanced(),
357 ConstantSample::default(),
358 ),
359 )),
360 fidl_stats::GaugeStatistic::Sum => Some(time_matrix_client.inspect_time_matrix(
361 format!("{gauge_name}.sum"),
362 TimeMatrix::<Sum<i64>, ConstantSample>::new(
363 SamplingProfile::balanced(),
364 ConstantSample::default(),
365 ),
366 )),
367 fidl_stats::GaugeStatistic::Last => Some(time_matrix_client.inspect_time_matrix(
368 format!("{gauge_name}.last"),
369 TimeMatrix::<Last<i64>, ConstantSample>::new(
370 SamplingProfile::balanced(),
371 ConstantSample::default(),
372 ),
373 )),
374 fidl_stats::GaugeStatistic::Mean => Some(time_matrix_client.inspect_time_matrix(
375 format!("{gauge_name}.mean"),
376 TimeMatrix::<ArithmeticMean<i64>, ConstantSample>::new(
377 SamplingProfile::balanced(),
378 ConstantSample::default(),
379 ),
380 )),
381 _ => None,
382 }
383}
384
385async fn log_connection_stats_inspect(
386 stats: &fidl_stats::IfaceStats,
387 time_series_stats: &IfaceCountersTimeSeries,
388 driver_counters_time_series: Arc<Mutex<CountersTimeSeriesMap>>,
389 driver_gauges_time_series: Arc<Mutex<GaugesTimeSeriesMap>>,
390) {
391 let connection_stats = match &stats.connection_stats {
392 Some(counters) => counters,
393 None => return,
394 };
395
396 match &connection_stats.connection_id {
398 Some(_connection_id) => (),
399 _ => {
400 warn!("connection_id is not present, no connection counters will be logged");
401 return;
402 }
403 }
404
405 if let fidl_stats::ConnectionStats {
406 rx_unicast_total: Some(rx_unicast_total),
407 rx_unicast_drop: Some(rx_unicast_drop),
408 ..
409 } = connection_stats
410 {
411 time_series_stats.log_rx_unicast_total(*rx_unicast_total);
412 time_series_stats.log_rx_unicast_drop(*rx_unicast_drop);
413 }
414
415 if let fidl_stats::ConnectionStats {
416 tx_total: Some(tx_total), tx_drop: Some(tx_drop), ..
417 } = connection_stats
418 {
419 time_series_stats.log_tx_total(*tx_total);
420 time_series_stats.log_tx_drop(*tx_drop);
421 }
422
423 if let Some(counters) = &connection_stats.driver_specific_counters {
425 log_driver_specific_counters(&counters[..], driver_counters_time_series).await;
426 }
427 if let Some(gauges) = &connection_stats.driver_specific_gauges {
429 log_driver_specific_gauges(&gauges[..], driver_gauges_time_series).await;
430 }
431}
432
433async fn log_driver_specific_counters(
434 driver_specific_counters: &[fidl_stats::UnnamedCounter],
435 driver_counters_time_series: Arc<Mutex<CountersTimeSeriesMap>>,
436) {
437 let time_series_map = driver_counters_time_series.lock().await;
438 for counter in driver_specific_counters {
439 if let Some(ts) = time_series_map.get(&counter.id) {
440 ts.fold_or_log_error(Timed::now(counter.count));
441 }
442 }
443}
444
445async fn log_driver_specific_gauges(
446 driver_specific_gauges: &[fidl_stats::UnnamedGauge],
447 driver_gauges_time_series: Arc<Mutex<GaugesTimeSeriesMap>>,
448) {
449 let time_series_map = driver_gauges_time_series.lock().await;
450 for gauge in driver_specific_gauges {
451 if let Some(time_matrices) = time_series_map.get(&gauge.id) {
452 for ts in time_matrices {
453 ts.fold_or_log_error(Timed::now(gauge.value));
454 }
455 }
456 }
457}
458
459async fn diff_and_log_connection_stats_cobalt(
460 cobalt_proxy: &fidl_fuchsia_metrics::MetricEventLoggerProxy,
461 prev: &fidl_stats::ConnectionStats,
462 current: &fidl_stats::ConnectionStats,
463) {
464 match (current.rx_unicast_total, prev.rx_unicast_total) {
468 (Some(current), Some(prev)) if current < prev => return,
469 _ => (),
470 }
471 match (current.rx_unicast_drop, prev.rx_unicast_drop) {
472 (Some(current), Some(prev)) if current < prev => return,
473 _ => (),
474 }
475 match (current.tx_total, prev.tx_total) {
476 (Some(current), Some(prev)) if current < prev => return,
477 _ => (),
478 }
479 match (current.tx_drop, prev.tx_drop) {
480 (Some(current), Some(prev)) if current < prev => return,
481 _ => (),
482 }
483
484 diff_and_log_rx_cobalt(cobalt_proxy, prev, current).await;
485 diff_and_log_tx_cobalt(cobalt_proxy, prev, current).await;
486}
487
488async fn diff_and_log_rx_cobalt(
489 cobalt_proxy: &fidl_fuchsia_metrics::MetricEventLoggerProxy,
490 prev: &fidl_stats::ConnectionStats,
491 current: &fidl_stats::ConnectionStats,
492) {
493 let mut metric_events = vec![];
494
495 let (current_rx_unicast_total, prev_rx_unicast_total) =
496 match (current.rx_unicast_total, prev.rx_unicast_total) {
497 (Some(current), Some(prev)) => (current, prev),
498 _ => return,
499 };
500 let (current_rx_unicast_drop, prev_rx_unicast_drop) =
501 match (current.rx_unicast_drop, prev.rx_unicast_drop) {
502 (Some(current), Some(prev)) => (current, prev),
503 _ => return,
504 };
505
506 let rx_total = match current_rx_unicast_total.checked_sub(prev_rx_unicast_total) {
507 Some(diff) => diff,
508 _ => return,
509 };
510 let rx_drop = match current_rx_unicast_drop.checked_sub(prev_rx_unicast_drop) {
511 Some(diff) => diff,
512 _ => return,
513 };
514 let rx_drop_rate = if rx_total > 0 { rx_drop as f64 / rx_total as f64 } else { 0f64 };
515
516 metric_events.push(MetricEvent {
517 metric_id: metrics::BAD_RX_RATE_METRIC_ID,
518 event_codes: vec![],
519 payload: MetricEventPayload::IntegerValue(float_to_ten_thousandth(rx_drop_rate)),
520 });
521 metric_events.push(MetricEvent {
522 metric_id: metrics::RX_UNICAST_PACKETS_METRIC_ID,
523 event_codes: vec![],
524 payload: MetricEventPayload::IntegerValue(rx_total as i64),
525 });
526
527 log_cobalt_batch!(cobalt_proxy, &metric_events, "diff_and_log_rx_cobalt",);
528}
529
530async fn diff_and_log_tx_cobalt(
531 cobalt_proxy: &fidl_fuchsia_metrics::MetricEventLoggerProxy,
532 prev: &fidl_stats::ConnectionStats,
533 current: &fidl_stats::ConnectionStats,
534) {
535 let mut metric_events = vec![];
536
537 let (current_tx_total, prev_tx_total) = match (current.tx_total, prev.tx_total) {
538 (Some(current), Some(prev)) => (current, prev),
539 _ => return,
540 };
541 let (current_tx_drop, prev_tx_drop) = match (current.tx_drop, prev.tx_drop) {
542 (Some(current), Some(prev)) => (current, prev),
543 _ => return,
544 };
545
546 let tx_total = match current_tx_total.checked_sub(prev_tx_total) {
547 Some(diff) => diff,
548 _ => return,
549 };
550 let tx_drop = match current_tx_drop.checked_sub(prev_tx_drop) {
551 Some(diff) => diff,
552 _ => return,
553 };
554 let tx_drop_rate = if tx_total > 0 { tx_drop as f64 / tx_total as f64 } else { 0f64 };
555
556 metric_events.push(MetricEvent {
557 metric_id: metrics::BAD_TX_RATE_METRIC_ID,
558 event_codes: vec![],
559 payload: MetricEventPayload::IntegerValue(float_to_ten_thousandth(tx_drop_rate)),
560 });
561
562 log_cobalt_batch!(cobalt_proxy, &metric_events, "diff_and_log_tx_cobalt",);
563}
564
565fn float_to_ten_thousandth(value: f64) -> i64 {
568 (value * 10000f64) as i64
569}
570
571#[derive(Debug, Clone)]
572struct IfaceCountersTimeSeries {
573 rx_unicast_total: InspectedTimeMatrix<u64>,
574 rx_unicast_drop: InspectedTimeMatrix<u64>,
575 tx_total: InspectedTimeMatrix<u64>,
576 tx_drop: InspectedTimeMatrix<u64>,
577}
578
579impl IfaceCountersTimeSeries {
580 pub fn new<S: InspectSender>(client: &S) -> Self {
581 let rx_unicast_total = client.inspect_time_matrix(
582 "rx_unicast_total",
583 TimeMatrix::<LatchMax<u64>, LastSample>::new(
584 SamplingProfile::balanced(),
585 LastSample::or(0),
586 ),
587 );
588 let rx_unicast_drop = client.inspect_time_matrix(
589 "rx_unicast_drop",
590 TimeMatrix::<LatchMax<u64>, LastSample>::new(
591 SamplingProfile::balanced(),
592 LastSample::or(0),
593 ),
594 );
595 let tx_total = client.inspect_time_matrix(
596 "tx_total",
597 TimeMatrix::<LatchMax<u64>, LastSample>::new(
598 SamplingProfile::balanced(),
599 LastSample::or(0),
600 ),
601 );
602 let tx_drop = client.inspect_time_matrix(
603 "tx_drop",
604 TimeMatrix::<LatchMax<u64>, LastSample>::new(
605 SamplingProfile::balanced(),
606 LastSample::or(0),
607 ),
608 );
609 Self { rx_unicast_total, rx_unicast_drop, tx_total, tx_drop }
610 }
611
612 fn log_rx_unicast_total(&self, data: u64) {
613 self.rx_unicast_total.fold_or_log_error(Timed::now(data));
614 }
615
616 fn log_rx_unicast_drop(&self, data: u64) {
617 self.rx_unicast_drop.fold_or_log_error(Timed::now(data));
618 }
619
620 fn log_tx_total(&self, data: u64) {
621 self.tx_total.fold_or_log_error(Timed::now(data));
622 }
623
624 fn log_tx_drop(&self, data: u64) {
625 self.tx_drop.fold_or_log_error(Timed::now(data));
626 }
627}
628
629#[derive(Debug, Clone)]
630struct SignalTimeSeries {
631 wlan_channels: InspectedTimeMatrix<u64>,
632 tx_rate_500kbps: InspectedTimeMatrix<u64>,
633 rssi: InspectedTimeMatrix<i64>,
634 snr: InspectedTimeMatrix<i64>,
635}
636
637impl SignalTimeSeries {
638 pub fn new<S: InspectSender>(client: &S, inspect_metadata_path: &str) -> Self {
639 let wlan_channels = client.inspect_time_matrix_with_metadata(
640 "wlan_channels",
641 TimeMatrix::<Union<u64>, LastSample>::new(
642 SamplingProfile::highly_granular(),
643 LastSample::or(0),
644 ),
645 BitSetNode::from_path(format!(
646 "{}/{}",
647 inspect_metadata_path,
648 InspectMetadataNode::WLAN_CHANNELS,
649 )),
650 );
651 let tx_rate_500kbps = client.inspect_time_matrix(
652 "tx_rate_500kbps",
653 TimeMatrix::<_, ConstantSample>::with_statistic(
654 SamplingProfile::default(),
655 ConstantSample::default(),
656 PostAggregation::<ArithmeticMean<u64>, _>::from_transform(|aggregation: f32| {
657 aggregation.ceil() as u64
658 }),
659 ),
660 );
661 let rssi = client.inspect_time_matrix(
662 "rssi",
663 TimeMatrix::<_, ConstantSample>::with_statistic(
664 SamplingProfile::default(),
665 ConstantSample::default(),
666 PostAggregation::<ArithmeticMean<i64>, _>::from_transform(|aggregation: f32| {
667 aggregation.ceil() as i64
668 }),
669 ),
670 );
671 let snr = client.inspect_time_matrix(
672 "snr",
673 TimeMatrix::<_, ConstantSample>::with_statistic(
674 SamplingProfile::default(),
675 ConstantSample::default(),
676 PostAggregation::<ArithmeticMean<i64>, _>::from_transform(|aggregation: f32| {
677 aggregation.ceil() as i64
678 }),
679 ),
680 );
681 Self { wlan_channels, tx_rate_500kbps, rssi, snr }
682 }
683}
684
685#[derive(PartialEq, Eq, Unit, Hash)]
686struct InspectWlanChannel {
687 primary: u8,
688 cbw: String,
689 secondary80: u8,
690}
691
692impl From<fidl_ieee80211::WlanChannel> for InspectWlanChannel {
693 fn from(channel: fidl_ieee80211::WlanChannel) -> Self {
694 Self {
695 primary: channel.primary,
696 cbw: format!("{:?}", channel.cbw),
697 secondary80: channel.secondary80,
698 }
699 }
700}
701
702struct InspectMetadataNode {
703 wlan_channels: LruCacheNode<InspectWlanChannel>,
704}
705
706impl InspectMetadataNode {
707 const WLAN_CHANNELS: &'static str = "wlan_channels";
708 const WLAN_CHANNELS_ID_LIMIT: usize = 32;
709
710 fn new(inspect_node: &InspectNode) -> Self {
711 let wlan_channels = inspect_node.create_child(Self::WLAN_CHANNELS);
712 Self { wlan_channels: LruCacheNode::new(wlan_channels, Self::WLAN_CHANNELS_ID_LIMIT) }
713 }
714}
715
716#[cfg(test)]
717mod tests {
718 use super::*;
719 use crate::testing::*;
720 use assert_matches::assert_matches;
721 use diagnostics_assertions::{AnyNumericProperty, assert_data_tree};
722 use futures::TryStreamExt;
723 use std::pin::pin;
724 use std::task::Poll;
725 use test_case::test_case;
726 use windowed_stats::experimental::testing::{MockTimeMatrixClient, TimeMatrixCall};
727
728 const IFACE_ID: u16 = 66;
729
730 #[fuchsia::test]
731 fn test_handle_iface_created() {
732 let mut test_helper = setup_test();
733 let driver_counters_mock_matrix_client = MockTimeMatrixClient::new();
734 let driver_gauges_mock_matrix_client = MockTimeMatrixClient::new();
735 let logger = ClientIfaceCountersLogger::new(
736 test_helper.cobalt_proxy.clone(),
737 test_helper.monitor_svc_proxy.clone(),
738 &test_helper.inspect_metadata_node,
739 &test_helper.inspect_metadata_path,
740 &test_helper.mock_time_matrix_client,
741 driver_counters_mock_matrix_client.clone(),
742 driver_gauges_mock_matrix_client.clone(),
743 );
744
745 let mut handle_iface_created_fut = pin!(logger.handle_iface_created(IFACE_ID));
746 assert_eq!(
747 test_helper.run_and_handle_get_sme_telemetry(&mut handle_iface_created_fut),
748 Poll::Pending
749 );
750
751 let mocked_inspect_counter_configs = vec![fidl_stats::InspectCounterConfig {
752 counter_id: Some(1),
753 counter_name: Some("foo_counter".to_string()),
754 ..Default::default()
755 }];
756 let telemetry_support = fidl_stats::TelemetrySupport {
757 inspect_counter_configs: Some(mocked_inspect_counter_configs),
758 ..Default::default()
759 };
760 assert_eq!(
761 test_helper.run_and_respond_query_telemetry_support(
762 &mut handle_iface_created_fut,
763 Ok(&telemetry_support)
764 ),
765 Poll::Ready(())
766 );
767
768 assert_matches!(logger.iface_state.try_lock().as_deref(), Some(IfaceState::Created { .. }));
769 let driver_counters_time_series = logger.driver_counters_time_series.try_lock().unwrap();
770 assert_eq!(driver_counters_time_series.keys().copied().collect::<Vec<u16>>(), vec![1u16],);
771 }
772
773 #[fuchsia::test]
774 fn test_handle_periodic_telemetry_connection_stats() {
775 let mut test_helper = setup_test();
776 let driver_counters_mock_matrix_client = MockTimeMatrixClient::new();
777 let driver_gauges_mock_matrix_client = MockTimeMatrixClient::new();
778 let logger = ClientIfaceCountersLogger::new(
779 test_helper.cobalt_proxy.clone(),
780 test_helper.monitor_svc_proxy.clone(),
781 &test_helper.inspect_metadata_node,
782 &test_helper.inspect_metadata_path,
783 &test_helper.mock_time_matrix_client,
784 driver_counters_mock_matrix_client.clone(),
785 driver_gauges_mock_matrix_client.clone(),
786 );
787
788 handle_iface_created(&mut test_helper, &logger);
790
791 let mut test_fut = pin!(logger.handle_periodic_telemetry());
792 let iface_stats = fidl_stats::IfaceStats {
793 connection_stats: Some(fidl_stats::ConnectionStats {
794 connection_id: Some(1),
795 rx_unicast_total: Some(100),
796 rx_unicast_drop: Some(5),
797 rx_multicast: Some(30),
798 tx_total: Some(50),
799 tx_drop: Some(2),
800 ..Default::default()
801 }),
802 ..Default::default()
803 };
804 assert_eq!(
805 test_helper.run_and_respond_iface_stats_req(&mut test_fut, Ok(&iface_stats)),
806 Poll::Pending,
807 );
808 let signal_report = Default::default();
809 assert_eq!(
810 test_helper.run_and_respond_get_signal_report(&mut test_fut, Ok(&signal_report)),
811 Poll::Ready(())
812 );
813
814 let mut time_matrix_calls = test_helper.mock_time_matrix_client.fold_buffered_samples();
815 assert_eq!(
816 &time_matrix_calls.drain::<u64>("rx_unicast_total")[..],
817 &[TimeMatrixCall::Fold(Timed::now(100u64))]
818 );
819 assert_eq!(
820 &time_matrix_calls.drain::<u64>("rx_unicast_drop")[..],
821 &[TimeMatrixCall::Fold(Timed::now(5u64))]
822 );
823 assert_eq!(
824 &time_matrix_calls.drain::<u64>("tx_total")[..],
825 &[TimeMatrixCall::Fold(Timed::now(50u64))]
826 );
827 assert_eq!(
828 &time_matrix_calls.drain::<u64>("tx_drop")[..],
829 &[TimeMatrixCall::Fold(Timed::now(2u64))]
830 );
831 }
832
833 #[fuchsia::test]
834 fn test_handle_periodic_telemetry_driver_specific_counters() {
835 let mut test_helper = setup_test();
836 let driver_counters_mock_matrix_client = MockTimeMatrixClient::new();
837 let driver_gauges_mock_matrix_client = MockTimeMatrixClient::new();
838 let logger = ClientIfaceCountersLogger::new(
839 test_helper.cobalt_proxy.clone(),
840 test_helper.monitor_svc_proxy.clone(),
841 &test_helper.inspect_metadata_node,
842 &test_helper.inspect_metadata_path,
843 &test_helper.mock_time_matrix_client,
844 driver_counters_mock_matrix_client.clone(),
845 driver_gauges_mock_matrix_client.clone(),
846 );
847
848 let mut handle_iface_created_fut = pin!(logger.handle_iface_created(IFACE_ID));
849 assert_eq!(
850 test_helper.run_and_handle_get_sme_telemetry(&mut handle_iface_created_fut),
851 Poll::Pending
852 );
853
854 let mocked_inspect_configs = vec![
855 fidl_stats::InspectCounterConfig {
856 counter_id: Some(1),
857 counter_name: Some("foo_counter".to_string()),
858 ..Default::default()
859 },
860 fidl_stats::InspectCounterConfig {
861 counter_id: Some(2),
862 counter_name: Some("bar_counter".to_string()),
863 ..Default::default()
864 },
865 fidl_stats::InspectCounterConfig {
866 counter_id: Some(3),
867 counter_name: Some("baz_counter".to_string()),
868 ..Default::default()
869 },
870 ];
871 let telemetry_support = fidl_stats::TelemetrySupport {
872 inspect_counter_configs: Some(mocked_inspect_configs),
873 ..Default::default()
874 };
875 assert_eq!(
876 test_helper.run_and_respond_query_telemetry_support(
877 &mut handle_iface_created_fut,
878 Ok(&telemetry_support)
879 ),
880 Poll::Ready(())
881 );
882
883 let mut test_fut = pin!(logger.handle_periodic_telemetry());
884 let iface_stats = fidl_stats::IfaceStats {
885 driver_specific_counters: Some(vec![fidl_stats::UnnamedCounter { id: 1, count: 50 }]),
886 connection_stats: Some(fidl_stats::ConnectionStats {
887 connection_id: Some(1),
888 driver_specific_counters: Some(vec![
889 fidl_stats::UnnamedCounter { id: 2, count: 100 },
890 fidl_stats::UnnamedCounter { id: 3, count: 150 },
891 fidl_stats::UnnamedCounter { id: 4, count: 200 },
893 ]),
894 ..Default::default()
895 }),
896 ..Default::default()
897 };
898 assert_eq!(
899 test_helper.run_and_respond_iface_stats_req(&mut test_fut, Ok(&iface_stats)),
900 Poll::Pending,
901 );
902 let signal_report = Default::default();
903 assert_eq!(
904 test_helper.run_and_respond_get_signal_report(&mut test_fut, Ok(&signal_report)),
905 Poll::Ready(())
906 );
907
908 let time_matrix_calls = test_helper.mock_time_matrix_client.fold_buffered_samples();
909 assert!(time_matrix_calls.is_empty());
910
911 let mut driver_counters_matrix_calls =
912 driver_counters_mock_matrix_client.fold_buffered_samples();
913 assert_eq!(
914 &driver_counters_matrix_calls.drain::<u64>("foo_counter")[..],
915 &[TimeMatrixCall::Fold(Timed::now(50))]
916 );
917 assert_eq!(
918 &driver_counters_matrix_calls.drain::<u64>("bar_counter")[..],
919 &[TimeMatrixCall::Fold(Timed::now(100))]
920 );
921 assert_eq!(
922 &driver_counters_matrix_calls.drain::<u64>("baz_counter")[..],
923 &[TimeMatrixCall::Fold(Timed::now(150))]
924 );
925
926 let driver_gauges_matrix_calls = driver_gauges_mock_matrix_client.fold_buffered_samples();
927 assert!(driver_gauges_matrix_calls.is_empty());
928 }
929
930 #[fuchsia::test]
931 fn test_handle_periodic_telemetry_driver_specific_gauges() {
932 let mut test_helper = setup_test();
933 let driver_counters_mock_matrix_client = MockTimeMatrixClient::new();
934 let driver_gauges_mock_matrix_client = MockTimeMatrixClient::new();
935 let logger = ClientIfaceCountersLogger::new(
936 test_helper.cobalt_proxy.clone(),
937 test_helper.monitor_svc_proxy.clone(),
938 &test_helper.inspect_metadata_node,
939 &test_helper.inspect_metadata_path,
940 &test_helper.mock_time_matrix_client,
941 driver_counters_mock_matrix_client.clone(),
942 driver_gauges_mock_matrix_client.clone(),
943 );
944
945 let mut handle_iface_created_fut = pin!(logger.handle_iface_created(IFACE_ID));
946 assert_eq!(
947 test_helper.run_and_handle_get_sme_telemetry(&mut handle_iface_created_fut),
948 Poll::Pending
949 );
950
951 let mocked_inspect_configs = vec![
952 fidl_stats::InspectGaugeConfig {
953 gauge_id: Some(1),
954 gauge_name: Some("foo_gauge".to_string()),
955 statistics: Some(vec![
956 fidl_stats::GaugeStatistic::Mean,
957 fidl_stats::GaugeStatistic::Last,
958 ]),
959 ..Default::default()
960 },
961 fidl_stats::InspectGaugeConfig {
962 gauge_id: Some(2),
963 gauge_name: Some("bar_gauge".to_string()),
964 statistics: Some(vec![
965 fidl_stats::GaugeStatistic::Min,
966 fidl_stats::GaugeStatistic::Sum,
967 ]),
968 ..Default::default()
969 },
970 fidl_stats::InspectGaugeConfig {
971 gauge_id: Some(3),
972 gauge_name: Some("baz_gauge".to_string()),
973 statistics: Some(vec![fidl_stats::GaugeStatistic::Max]),
974 ..Default::default()
975 },
976 ];
977 let telemetry_support = fidl_stats::TelemetrySupport {
978 inspect_gauge_configs: Some(mocked_inspect_configs),
979 ..Default::default()
980 };
981 assert_eq!(
982 test_helper.run_and_respond_query_telemetry_support(
983 &mut handle_iface_created_fut,
984 Ok(&telemetry_support)
985 ),
986 Poll::Ready(())
987 );
988
989 let mut test_fut = pin!(logger.handle_periodic_telemetry());
990 let iface_stats = fidl_stats::IfaceStats {
991 driver_specific_gauges: Some(vec![fidl_stats::UnnamedGauge { id: 1, value: 50 }]),
992 connection_stats: Some(fidl_stats::ConnectionStats {
993 connection_id: Some(1),
994 driver_specific_gauges: Some(vec![
995 fidl_stats::UnnamedGauge { id: 2, value: 100 },
996 fidl_stats::UnnamedGauge { id: 3, value: 150 },
997 fidl_stats::UnnamedGauge { id: 4, value: 200 },
999 ]),
1000 ..Default::default()
1001 }),
1002 ..Default::default()
1003 };
1004 assert_eq!(
1005 test_helper.run_and_respond_iface_stats_req(&mut test_fut, Ok(&iface_stats)),
1006 Poll::Pending
1007 );
1008 let signal_report = Default::default();
1009 assert_eq!(
1010 test_helper.run_and_respond_get_signal_report(&mut test_fut, Ok(&signal_report)),
1011 Poll::Ready(())
1012 );
1013
1014 let time_matrix_calls = test_helper.mock_time_matrix_client.fold_buffered_samples();
1015 assert!(time_matrix_calls.is_empty());
1016
1017 let driver_counters_matrix_calls =
1018 driver_counters_mock_matrix_client.fold_buffered_samples();
1019 assert!(driver_counters_matrix_calls.is_empty());
1020
1021 let mut driver_gauges_matrix_calls =
1022 driver_gauges_mock_matrix_client.fold_buffered_samples();
1023 assert_eq!(
1024 &driver_gauges_matrix_calls.drain::<i64>("foo_gauge.mean")[..],
1025 &[TimeMatrixCall::Fold(Timed::now(50))]
1026 );
1027 assert_eq!(
1028 &driver_gauges_matrix_calls.drain::<i64>("foo_gauge.last")[..],
1029 &[TimeMatrixCall::Fold(Timed::now(50))]
1030 );
1031 assert_eq!(
1032 &driver_gauges_matrix_calls.drain::<i64>("bar_gauge.min")[..],
1033 &[TimeMatrixCall::Fold(Timed::now(100))]
1034 );
1035 assert_eq!(
1036 &driver_gauges_matrix_calls.drain::<i64>("bar_gauge.sum")[..],
1037 &[TimeMatrixCall::Fold(Timed::now(100))]
1038 );
1039 assert_eq!(
1040 &driver_gauges_matrix_calls.drain::<i64>("baz_gauge.max")[..],
1041 &[TimeMatrixCall::Fold(Timed::now(150))]
1042 );
1043 }
1044
1045 #[fuchsia::test]
1046 fn test_handle_periodic_telemetry_signal_report() {
1047 let mut test_helper = setup_test();
1048 let driver_counters_mock_matrix_client = MockTimeMatrixClient::new();
1049 let driver_gauges_mock_matrix_client = MockTimeMatrixClient::new();
1050 let logger = ClientIfaceCountersLogger::new(
1051 test_helper.cobalt_proxy.clone(),
1052 test_helper.monitor_svc_proxy.clone(),
1053 &test_helper.inspect_metadata_node,
1054 &test_helper.inspect_metadata_path,
1055 &test_helper.mock_time_matrix_client,
1056 driver_counters_mock_matrix_client.clone(),
1057 driver_gauges_mock_matrix_client.clone(),
1058 );
1059
1060 let mut handle_iface_created_fut = pin!(logger.handle_iface_created(IFACE_ID));
1061 assert_eq!(
1062 test_helper.run_and_handle_get_sme_telemetry(&mut handle_iface_created_fut),
1063 Poll::Pending
1064 );
1065
1066 let telemetry_support = Default::default();
1067 assert_eq!(
1068 test_helper.run_and_respond_query_telemetry_support(
1069 &mut handle_iface_created_fut,
1070 Ok(&telemetry_support)
1071 ),
1072 Poll::Ready(())
1073 );
1074
1075 let mut test_fut = pin!(logger.handle_periodic_telemetry());
1076 let iface_stats = Default::default();
1077 assert_eq!(
1078 test_helper.run_and_respond_iface_stats_req(&mut test_fut, Ok(&iface_stats)),
1079 Poll::Pending
1080 );
1081 let signal_report = fidl_stats::SignalReport {
1082 connection_signal_report: Some(fidl_stats::ConnectionSignalReport {
1083 channel: Some(fidl_ieee80211::WlanChannel {
1084 primary: 6,
1085 cbw: fidl_ieee80211::ChannelBandwidth::Cbw20,
1086 secondary80: 0,
1087 }),
1088 tx_rate_500kbps: Some(11),
1089 rssi_dbm: Some(-40),
1090 snr_db: Some(25),
1091 ..Default::default()
1092 }),
1093 ..Default::default()
1094 };
1095 assert_eq!(
1096 test_helper.run_and_respond_get_signal_report(&mut test_fut, Ok(&signal_report)),
1097 Poll::Ready(())
1098 );
1099
1100 let mut time_matrix_calls = test_helper.mock_time_matrix_client.fold_buffered_samples();
1101 assert_eq!(
1102 &time_matrix_calls.drain::<u64>("wlan_channels")[..],
1103 &[TimeMatrixCall::Fold(Timed::now(1 << 0))] );
1105 assert_eq!(
1106 &time_matrix_calls.drain::<u64>("tx_rate_500kbps")[..],
1107 &[TimeMatrixCall::Fold(Timed::now(11))]
1108 );
1109 assert_eq!(
1110 &time_matrix_calls.drain::<i64>("rssi")[..],
1111 &[TimeMatrixCall::Fold(Timed::now(-40))]
1112 );
1113 assert_eq!(
1114 &time_matrix_calls.drain::<i64>("snr")[..],
1115 &[TimeMatrixCall::Fold(Timed::now(25))]
1116 );
1117
1118 let tree = test_helper.get_inspect_data_tree();
1119 assert_data_tree!(@executor test_helper.exec, tree, root: contains {
1120 test_stats: contains {
1121 metadata: contains {
1122 wlan_channels: contains {
1123 "0": {
1124 "@time": AnyNumericProperty,
1125 "data": contains {
1126 primary: 6u64,
1127 cbw: "Cbw20",
1128 secondary80: 0u64,
1129 }
1130 }
1131 }
1132 }
1133 }
1134 });
1135 }
1136
1137 #[fuchsia::test]
1138 fn test_handle_periodic_telemetry_cobalt() {
1139 let mut test_helper = setup_test();
1140 let driver_counters_mock_matrix_client = MockTimeMatrixClient::new();
1141 let driver_gauges_mock_matrix_client = MockTimeMatrixClient::new();
1142 let logger = ClientIfaceCountersLogger::new(
1143 test_helper.cobalt_proxy.clone(),
1144 test_helper.monitor_svc_proxy.clone(),
1145 &test_helper.inspect_metadata_node,
1146 &test_helper.inspect_metadata_path,
1147 &test_helper.mock_time_matrix_client,
1148 driver_counters_mock_matrix_client.clone(),
1149 driver_gauges_mock_matrix_client.clone(),
1150 );
1151
1152 handle_iface_created(&mut test_helper, &logger);
1154
1155 let mut test_fut = pin!(logger.handle_periodic_telemetry());
1156 let iface_stats = fidl_stats::IfaceStats {
1157 connection_stats: Some(fidl_stats::ConnectionStats {
1158 connection_id: Some(1),
1159 rx_unicast_total: Some(100),
1160 rx_unicast_drop: Some(5),
1161 rx_multicast: Some(30),
1162 tx_total: Some(50),
1163 tx_drop: Some(2),
1164 ..Default::default()
1165 }),
1166 ..Default::default()
1167 };
1168 assert_eq!(
1169 test_helper.run_and_respond_iface_stats_req(&mut test_fut, Ok(&iface_stats)),
1170 Poll::Pending
1171 );
1172 let signal_report = Default::default();
1173 assert_eq!(
1174 test_helper.run_and_respond_get_signal_report(&mut test_fut, Ok(&signal_report)),
1175 Poll::Ready(())
1176 );
1177
1178 let metrics = test_helper.get_logged_metrics(metrics::BAD_RX_RATE_METRIC_ID);
1179 assert!(metrics.is_empty());
1180 let metrics = test_helper.get_logged_metrics(metrics::RX_UNICAST_PACKETS_METRIC_ID);
1181 assert!(metrics.is_empty());
1182 let metrics = test_helper.get_logged_metrics(metrics::BAD_TX_RATE_METRIC_ID);
1183 assert!(metrics.is_empty());
1184
1185 let mut test_fut = pin!(logger.handle_periodic_telemetry());
1186 let iface_stats = fidl_stats::IfaceStats {
1187 connection_stats: Some(fidl_stats::ConnectionStats {
1188 connection_id: Some(1),
1189 rx_unicast_total: Some(200),
1190 rx_unicast_drop: Some(15),
1191 rx_multicast: Some(30),
1192 tx_total: Some(150),
1193 tx_drop: Some(3),
1194 ..Default::default()
1195 }),
1196 ..Default::default()
1197 };
1198 assert_eq!(
1199 test_helper.run_and_respond_iface_stats_req(&mut test_fut, Ok(&iface_stats)),
1200 Poll::Pending
1201 );
1202 assert_eq!(test_helper.run_until_stalled_drain_cobalt_events(&mut test_fut), Poll::Pending);
1203 let signal_report: fidl_fuchsia_wlan_stats::SignalReport = Default::default();
1204 assert_eq!(
1205 test_helper.run_and_respond_get_signal_report(&mut test_fut, Ok(&signal_report)),
1206 Poll::Ready(())
1207 );
1208
1209 let metrics = test_helper.get_logged_metrics(metrics::BAD_RX_RATE_METRIC_ID);
1210 assert_eq!(metrics.len(), 1);
1211 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(1000)); let metrics = test_helper.get_logged_metrics(metrics::RX_UNICAST_PACKETS_METRIC_ID);
1213 assert_eq!(metrics.len(), 1);
1214 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(100));
1215 let metrics = test_helper.get_logged_metrics(metrics::BAD_TX_RATE_METRIC_ID);
1216 assert_eq!(metrics.len(), 1);
1217 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(100)); }
1219
1220 #[fuchsia::test]
1221 fn test_handle_periodic_telemetry_cobalt_changed_connection_id() {
1222 let mut test_helper = setup_test();
1223 let driver_counters_mock_matrix_client = MockTimeMatrixClient::new();
1224 let driver_gauges_mock_matrix_client = MockTimeMatrixClient::new();
1225 let logger = ClientIfaceCountersLogger::new(
1226 test_helper.cobalt_proxy.clone(),
1227 test_helper.monitor_svc_proxy.clone(),
1228 &test_helper.inspect_metadata_node,
1229 &test_helper.inspect_metadata_path,
1230 &test_helper.mock_time_matrix_client,
1231 driver_counters_mock_matrix_client.clone(),
1232 driver_gauges_mock_matrix_client.clone(),
1233 );
1234
1235 handle_iface_created(&mut test_helper, &logger);
1237
1238 let mut test_fut = pin!(logger.handle_periodic_telemetry());
1239 let iface_stats = fidl_stats::IfaceStats {
1240 connection_stats: Some(fidl_stats::ConnectionStats {
1241 connection_id: Some(1),
1242 rx_unicast_total: Some(100),
1243 rx_unicast_drop: Some(5),
1244 rx_multicast: Some(30),
1245 tx_total: Some(50),
1246 tx_drop: Some(2),
1247 ..Default::default()
1248 }),
1249 ..Default::default()
1250 };
1251 assert_eq!(
1252 test_helper.run_and_respond_iface_stats_req(&mut test_fut, Ok(&iface_stats)),
1253 Poll::Pending
1254 );
1255 let signal_report = Default::default();
1256 assert_eq!(
1257 test_helper.run_and_respond_get_signal_report(&mut test_fut, Ok(&signal_report)),
1258 Poll::Ready(())
1259 );
1260
1261 let metrics = test_helper.get_logged_metrics(metrics::BAD_RX_RATE_METRIC_ID);
1262 assert!(metrics.is_empty());
1263 let metrics = test_helper.get_logged_metrics(metrics::RX_UNICAST_PACKETS_METRIC_ID);
1264 assert!(metrics.is_empty());
1265 let metrics = test_helper.get_logged_metrics(metrics::BAD_TX_RATE_METRIC_ID);
1266 assert!(metrics.is_empty());
1267
1268 let mut test_fut = pin!(logger.handle_periodic_telemetry());
1269 let iface_stats = fidl_stats::IfaceStats {
1270 connection_stats: Some(fidl_stats::ConnectionStats {
1271 connection_id: Some(2),
1272 rx_unicast_total: Some(200),
1273 rx_unicast_drop: Some(15),
1274 rx_multicast: Some(30),
1275 tx_total: Some(150),
1276 tx_drop: Some(3),
1277 ..Default::default()
1278 }),
1279 ..Default::default()
1280 };
1281 assert_eq!(
1282 test_helper.run_and_respond_iface_stats_req(&mut test_fut, Ok(&iface_stats)),
1283 Poll::Pending
1284 );
1285 let signal_report = Default::default();
1286 assert_eq!(
1287 test_helper.run_and_respond_get_signal_report(&mut test_fut, Ok(&signal_report)),
1288 Poll::Ready(())
1289 );
1290
1291 let metrics = test_helper.get_logged_metrics(metrics::BAD_RX_RATE_METRIC_ID);
1294 assert!(metrics.is_empty());
1295 let metrics = test_helper.get_logged_metrics(metrics::RX_UNICAST_PACKETS_METRIC_ID);
1296 assert!(metrics.is_empty());
1297 let metrics = test_helper.get_logged_metrics(metrics::BAD_TX_RATE_METRIC_ID);
1298 assert!(metrics.is_empty());
1299
1300 let mut test_fut = pin!(logger.handle_periodic_telemetry());
1301 let iface_stats = fidl_stats::IfaceStats {
1302 connection_stats: Some(fidl_stats::ConnectionStats {
1303 connection_id: Some(2),
1304 rx_unicast_total: Some(300),
1305 rx_unicast_drop: Some(18),
1306 rx_multicast: Some(30),
1307 tx_total: Some(250),
1308 tx_drop: Some(5),
1309 ..Default::default()
1310 }),
1311 ..Default::default()
1312 };
1313 assert_eq!(
1314 test_helper.run_and_respond_iface_stats_req(&mut test_fut, Ok(&iface_stats)),
1315 Poll::Pending
1316 );
1317 assert_eq!(test_helper.run_until_stalled_drain_cobalt_events(&mut test_fut), Poll::Pending);
1318 let signal_report = Default::default();
1319 assert_eq!(
1320 test_helper.run_and_respond_get_signal_report(&mut test_fut, Ok(&signal_report)),
1321 Poll::Ready(())
1322 );
1323
1324 let metrics = test_helper.get_logged_metrics(metrics::BAD_RX_RATE_METRIC_ID);
1325 assert_eq!(metrics.len(), 1);
1326 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(300)); let metrics = test_helper.get_logged_metrics(metrics::RX_UNICAST_PACKETS_METRIC_ID);
1328 assert_eq!(metrics.len(), 1);
1329 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(100));
1330 let metrics = test_helper.get_logged_metrics(metrics::BAD_TX_RATE_METRIC_ID);
1331 assert_eq!(metrics.len(), 1);
1332 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(200)); }
1334
1335 #[fuchsia::test]
1336 fn test_handle_periodic_telemetry_cobalt_suspension_in_between() {
1337 let mut test_helper = setup_test();
1338 let driver_counters_mock_matrix_client = MockTimeMatrixClient::new();
1339 let driver_gauges_mock_matrix_client = MockTimeMatrixClient::new();
1340 let logger = ClientIfaceCountersLogger::new(
1341 test_helper.cobalt_proxy.clone(),
1342 test_helper.monitor_svc_proxy.clone(),
1343 &test_helper.inspect_metadata_node,
1344 &test_helper.inspect_metadata_path,
1345 &test_helper.mock_time_matrix_client,
1346 driver_counters_mock_matrix_client.clone(),
1347 driver_gauges_mock_matrix_client.clone(),
1348 );
1349
1350 handle_iface_created(&mut test_helper, &logger);
1352
1353 let mut test_fut = pin!(logger.handle_periodic_telemetry());
1354 let iface_stats = fidl_stats::IfaceStats {
1355 connection_stats: Some(fidl_stats::ConnectionStats {
1356 connection_id: Some(1),
1357 rx_unicast_total: Some(100),
1358 rx_unicast_drop: Some(5),
1359 rx_multicast: Some(30),
1360 tx_total: Some(50),
1361 tx_drop: Some(2),
1362 ..Default::default()
1363 }),
1364 ..Default::default()
1365 };
1366 assert_eq!(
1367 test_helper.run_and_respond_iface_stats_req(&mut test_fut, Ok(&iface_stats)),
1368 Poll::Pending
1369 );
1370 let signal_report = Default::default();
1371 assert_eq!(
1372 test_helper.run_and_respond_get_signal_report(&mut test_fut, Ok(&signal_report)),
1373 Poll::Ready(())
1374 );
1375
1376 let metrics = test_helper.get_logged_metrics(metrics::BAD_RX_RATE_METRIC_ID);
1377 assert!(metrics.is_empty());
1378 let metrics = test_helper.get_logged_metrics(metrics::RX_UNICAST_PACKETS_METRIC_ID);
1379 assert!(metrics.is_empty());
1380 let metrics = test_helper.get_logged_metrics(metrics::BAD_TX_RATE_METRIC_ID);
1381 assert!(metrics.is_empty());
1382
1383 test_helper.exec.set_fake_boot_to_mono_offset(zx::BootDuration::from_millis(1));
1384 let mut test_fut = pin!(logger.handle_periodic_telemetry());
1385 let iface_stats = fidl_stats::IfaceStats {
1386 connection_stats: Some(fidl_stats::ConnectionStats {
1387 connection_id: Some(1),
1388 rx_unicast_total: Some(200),
1389 rx_unicast_drop: Some(15),
1390 rx_multicast: Some(30),
1391 tx_total: Some(150),
1392 tx_drop: Some(3),
1393 ..Default::default()
1394 }),
1395 ..Default::default()
1396 };
1397 assert_eq!(
1398 test_helper.run_and_respond_iface_stats_req(&mut test_fut, Ok(&iface_stats)),
1399 Poll::Pending
1400 );
1401 let signal_report = Default::default();
1402 assert_eq!(
1403 test_helper.run_and_respond_get_signal_report(&mut test_fut, Ok(&signal_report)),
1404 Poll::Ready(())
1405 );
1406
1407 let metrics = test_helper.get_logged_metrics(metrics::BAD_RX_RATE_METRIC_ID);
1410 assert!(metrics.is_empty());
1411 let metrics = test_helper.get_logged_metrics(metrics::RX_UNICAST_PACKETS_METRIC_ID);
1412 assert!(metrics.is_empty());
1413 let metrics = test_helper.get_logged_metrics(metrics::BAD_TX_RATE_METRIC_ID);
1414 assert!(metrics.is_empty());
1415
1416 let mut test_fut = pin!(logger.handle_periodic_telemetry());
1417 let iface_stats = fidl_stats::IfaceStats {
1418 connection_stats: Some(fidl_stats::ConnectionStats {
1419 connection_id: Some(1),
1420 rx_unicast_total: Some(300),
1421 rx_unicast_drop: Some(18),
1422 rx_multicast: Some(30),
1423 tx_total: Some(250),
1424 tx_drop: Some(5),
1425 ..Default::default()
1426 }),
1427 ..Default::default()
1428 };
1429 assert_eq!(
1430 test_helper.run_and_respond_iface_stats_req(&mut test_fut, Ok(&iface_stats)),
1431 Poll::Pending
1432 );
1433 assert_eq!(test_helper.run_until_stalled_drain_cobalt_events(&mut test_fut), Poll::Pending);
1434 let signal_report = Default::default();
1435 assert_eq!(
1436 test_helper.run_and_respond_get_signal_report(&mut test_fut, Ok(&signal_report)),
1437 Poll::Ready(())
1438 );
1439
1440 let metrics = test_helper.get_logged_metrics(metrics::BAD_RX_RATE_METRIC_ID);
1441 assert_eq!(metrics.len(), 1);
1442 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(300)); let metrics = test_helper.get_logged_metrics(metrics::RX_UNICAST_PACKETS_METRIC_ID);
1444 assert_eq!(metrics.len(), 1);
1445 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(100));
1446 let metrics = test_helper.get_logged_metrics(metrics::BAD_TX_RATE_METRIC_ID);
1447 assert_eq!(metrics.len(), 1);
1448 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(200)); }
1450
1451 #[fuchsia::test]
1452 fn test_handle_periodic_telemetry_get_iface_stats_failure_non_timeout() {
1453 let mut test_helper = setup_test();
1454 let driver_counters_mock_matrix_client = MockTimeMatrixClient::new();
1455 let driver_gauges_mock_matrix_client = MockTimeMatrixClient::new();
1456 let logger = ClientIfaceCountersLogger::new(
1457 test_helper.cobalt_proxy.clone(),
1458 test_helper.monitor_svc_proxy.clone(),
1459 &test_helper.inspect_metadata_node,
1460 &test_helper.inspect_metadata_path,
1461 &test_helper.mock_time_matrix_client,
1462 driver_counters_mock_matrix_client.clone(),
1463 driver_gauges_mock_matrix_client.clone(),
1464 );
1465
1466 handle_iface_created(&mut test_helper, &logger);
1468
1469 let mut test_fut = pin!(logger.handle_periodic_telemetry());
1470 assert_eq!(
1471 test_helper
1472 .run_and_respond_iface_stats_req(&mut test_fut, Err(zx::sys::ZX_ERR_TIMED_OUT)),
1473 Poll::Pending
1474 );
1475 assert_eq!(test_helper.run_until_stalled_drain_cobalt_events(&mut test_fut), Poll::Pending);
1476
1477 let metrics = test_helper.get_logged_metrics(metrics::GET_IFACE_STATS_FAILURE_METRIC_ID);
1478 assert_eq!(metrics.len(), 1);
1479 assert_eq!(metrics[0].payload, MetricEventPayload::Count(1));
1480
1481 let metrics = test_helper.get_logged_metrics(metrics::GET_IFACE_STATS_TIMEOUT_METRIC_ID);
1485 assert!(metrics.is_empty());
1486
1487 let metrics =
1488 test_helper.get_logged_metrics(metrics::GET_IFACE_STATS_ERROR_IN_RESPONSE_METRIC_ID);
1489 assert_eq!(metrics.len(), 1);
1490 assert_eq!(metrics[0].payload, MetricEventPayload::Count(1));
1491 }
1492
1493 #[fuchsia::test]
1494 fn test_handle_periodic_telemetry_get_iface_stats_timeout() {
1495 let mut test_helper = setup_test();
1496 let driver_counters_mock_matrix_client = MockTimeMatrixClient::new();
1497 let driver_gauges_mock_matrix_client = MockTimeMatrixClient::new();
1498 let logger = ClientIfaceCountersLogger::new(
1499 test_helper.cobalt_proxy.clone(),
1500 test_helper.monitor_svc_proxy.clone(),
1501 &test_helper.inspect_metadata_node,
1502 &test_helper.inspect_metadata_path,
1503 &test_helper.mock_time_matrix_client,
1504 driver_counters_mock_matrix_client.clone(),
1505 driver_gauges_mock_matrix_client.clone(),
1506 );
1507
1508 handle_iface_created(&mut test_helper, &logger);
1510
1511 let mut test_fut = pin!(logger.handle_periodic_telemetry());
1512 assert_eq!(test_helper.run_until_stalled_drain_cobalt_events(&mut test_fut), Poll::Pending);
1513 test_helper.exec.set_fake_time(fasync::MonotonicInstant::from_nanos(5_000_000_000));
1514 assert_eq!(test_helper.run_until_stalled_drain_cobalt_events(&mut test_fut), Poll::Pending);
1515
1516 let metrics = test_helper.get_logged_metrics(metrics::GET_IFACE_STATS_FAILURE_METRIC_ID);
1517 assert_eq!(metrics.len(), 1);
1518 assert_eq!(metrics[0].payload, MetricEventPayload::Count(1));
1519
1520 let metrics = test_helper.get_logged_metrics(metrics::GET_IFACE_STATS_TIMEOUT_METRIC_ID);
1521 assert_eq!(metrics.len(), 1);
1522 assert_eq!(metrics[0].payload, MetricEventPayload::Count(1));
1523
1524 let metrics =
1525 test_helper.get_logged_metrics(metrics::GET_IFACE_STATS_ERROR_IN_RESPONSE_METRIC_ID);
1526 assert!(metrics.is_empty());
1527 }
1528
1529 #[fuchsia::test]
1530 fn test_diff_and_log_rx_cobalt() {
1531 let mut test_helper = setup_test();
1532 let prev_stats = fidl_stats::ConnectionStats {
1533 rx_unicast_total: Some(100),
1534 rx_unicast_drop: Some(5),
1535 ..Default::default()
1536 };
1537 let current_stats = fidl_stats::ConnectionStats {
1538 rx_unicast_total: Some(300),
1539 rx_unicast_drop: Some(7),
1540 ..Default::default()
1541 };
1542 let cobalt_proxy = test_helper.cobalt_proxy.clone();
1543 let mut test_fut = pin!(diff_and_log_rx_cobalt(&cobalt_proxy, &prev_stats, ¤t_stats));
1544 assert_eq!(
1545 test_helper.run_until_stalled_drain_cobalt_events(&mut test_fut),
1546 Poll::Ready(())
1547 );
1548 let metrics = test_helper.get_logged_metrics(metrics::BAD_RX_RATE_METRIC_ID);
1549 assert_eq!(metrics.len(), 1);
1550 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(100)); let metrics = test_helper.get_logged_metrics(metrics::RX_UNICAST_PACKETS_METRIC_ID);
1552 assert_eq!(metrics.len(), 1);
1553 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(200));
1554 }
1555
1556 #[test_case(
1557 fidl_stats::ConnectionStats { ..Default::default() },
1558 fidl_stats::ConnectionStats { ..Default::default() };
1559 "both empty"
1560 )]
1561 #[test_case(
1562 fidl_stats::ConnectionStats {
1563 rx_unicast_total: Some(100),
1564 rx_unicast_drop: Some(5),
1565 ..Default::default()
1566 },
1567 fidl_stats::ConnectionStats { ..Default::default() };
1568 "current empty"
1569 )]
1570 #[test_case(
1571 fidl_stats::ConnectionStats { ..Default::default() },
1572 fidl_stats::ConnectionStats {
1573 rx_unicast_total: Some(300),
1574 rx_unicast_drop: Some(7),
1575 ..Default::default()
1576 };
1577 "prev empty"
1578 )]
1579 #[fuchsia::test(add_test_attr = false)]
1580 fn test_diff_and_log_rx_cobalt_empty(
1581 prev_stats: fidl_stats::ConnectionStats,
1582 current_stats: fidl_stats::ConnectionStats,
1583 ) {
1584 let mut test_helper = setup_test();
1585 let cobalt_proxy = test_helper.cobalt_proxy.clone();
1586 let mut test_fut = pin!(diff_and_log_rx_cobalt(&cobalt_proxy, &prev_stats, ¤t_stats));
1587 assert_eq!(
1588 test_helper.run_until_stalled_drain_cobalt_events(&mut test_fut),
1589 Poll::Ready(())
1590 );
1591 let metrics = test_helper.get_logged_metrics(metrics::BAD_RX_RATE_METRIC_ID);
1592 assert!(metrics.is_empty())
1593 }
1594
1595 #[fuchsia::test]
1596 fn test_diff_and_log_tx_cobalt() {
1597 let mut test_helper = setup_test();
1598 let prev_stats = fidl_stats::ConnectionStats {
1599 tx_total: Some(100),
1600 tx_drop: Some(5),
1601 ..Default::default()
1602 };
1603 let current_stats = fidl_stats::ConnectionStats {
1604 tx_total: Some(300),
1605 tx_drop: Some(7),
1606 ..Default::default()
1607 };
1608 let cobalt_proxy = test_helper.cobalt_proxy.clone();
1609 let mut test_fut = pin!(diff_and_log_tx_cobalt(&cobalt_proxy, &prev_stats, ¤t_stats));
1610 assert_eq!(
1611 test_helper.run_until_stalled_drain_cobalt_events(&mut test_fut),
1612 Poll::Ready(())
1613 );
1614 let metrics = test_helper.get_logged_metrics(metrics::BAD_TX_RATE_METRIC_ID);
1615 assert_eq!(metrics.len(), 1);
1616 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(100)); }
1618
1619 #[test_case(
1620 fidl_stats::ConnectionStats { ..Default::default() },
1621 fidl_stats::ConnectionStats { ..Default::default() };
1622 "both empty"
1623 )]
1624 #[test_case(
1625 fidl_stats::ConnectionStats {
1626 tx_total: Some(100),
1627 tx_drop: Some(5),
1628 ..Default::default()
1629 },
1630 fidl_stats::ConnectionStats { ..Default::default() };
1631 "current empty"
1632 )]
1633 #[test_case(
1634 fidl_stats::ConnectionStats { ..Default::default() },
1635 fidl_stats::ConnectionStats {
1636 tx_total: Some(300),
1637 tx_drop: Some(7),
1638 ..Default::default()
1639 };
1640 "prev empty"
1641 )]
1642 #[fuchsia::test(add_test_attr = false)]
1643 fn test_diff_and_log_tx_cobalt_empty(
1644 prev_stats: fidl_stats::ConnectionStats,
1645 current_stats: fidl_stats::ConnectionStats,
1646 ) {
1647 let mut test_helper = setup_test();
1648 let cobalt_proxy = test_helper.cobalt_proxy.clone();
1649 let mut test_fut = pin!(diff_and_log_tx_cobalt(&cobalt_proxy, &prev_stats, ¤t_stats));
1650 assert_eq!(
1651 test_helper.run_until_stalled_drain_cobalt_events(&mut test_fut),
1652 Poll::Ready(())
1653 );
1654 let metrics = test_helper.get_logged_metrics(metrics::BAD_TX_RATE_METRIC_ID);
1655 assert!(metrics.is_empty())
1656 }
1657
1658 #[test_case(
1659 fidl_stats::ConnectionStats {
1660 rx_unicast_total: Some(100),
1661 rx_unicast_drop: Some(5),
1662 tx_total: Some(100),
1663 tx_drop: Some(5),
1664 ..Default::default()
1665 },
1666 fidl_stats::ConnectionStats {
1667 rx_unicast_total: Some(10),
1668 rx_unicast_drop: Some(1),
1669 tx_total: Some(100),
1670 tx_drop: Some(5),
1671 ..Default::default()
1672 };
1673 "rx regressed"
1674 )]
1675 #[test_case(
1676 fidl_stats::ConnectionStats {
1677 tx_total: Some(100),
1678 tx_drop: Some(5),
1679 rx_unicast_total: Some(100),
1680 rx_unicast_drop: Some(5),
1681 ..Default::default()
1682 },
1683 fidl_stats::ConnectionStats {
1684 tx_total: Some(10),
1685 tx_drop: Some(1),
1686 rx_unicast_total: Some(100),
1687 rx_unicast_drop: Some(5),
1688 ..Default::default()
1689 };
1690 "tx regressed"
1691 )]
1692 #[fuchsia::test(add_test_attr = false)]
1693 fn test_diff_and_log_connection_stats_cobalt_counters_reset(
1694 prev_stats: fidl_stats::ConnectionStats,
1695 current_stats: fidl_stats::ConnectionStats,
1696 ) {
1697 let mut test_helper = setup_test();
1698 let cobalt_proxy = test_helper.cobalt_proxy.clone();
1699 let mut test_fut =
1700 pin!(diff_and_log_connection_stats_cobalt(&cobalt_proxy, &prev_stats, ¤t_stats));
1701 assert_eq!(
1703 test_helper.run_until_stalled_drain_cobalt_events(&mut test_fut),
1704 Poll::Ready(())
1705 );
1706 let metrics = test_helper.get_logged_metrics(metrics::BAD_RX_RATE_METRIC_ID);
1708 assert!(metrics.is_empty());
1709 let metrics = test_helper.get_logged_metrics(metrics::BAD_TX_RATE_METRIC_ID);
1710 assert!(metrics.is_empty());
1711 }
1712
1713 #[fuchsia::test]
1714 fn test_handle_iface_destroyed() {
1715 let mut test_helper = setup_test();
1716 let driver_counters_mock_matrix_client = MockTimeMatrixClient::new();
1717 let driver_gauges_mock_matrix_client = MockTimeMatrixClient::new();
1718 let logger = ClientIfaceCountersLogger::new(
1719 test_helper.cobalt_proxy.clone(),
1720 test_helper.monitor_svc_proxy.clone(),
1721 &test_helper.inspect_metadata_node,
1722 &test_helper.inspect_metadata_path,
1723 &test_helper.mock_time_matrix_client,
1724 driver_counters_mock_matrix_client.clone(),
1725 driver_gauges_mock_matrix_client.clone(),
1726 );
1727
1728 handle_iface_created(&mut test_helper, &logger);
1730
1731 let mut handle_iface_destroyed_fut = pin!(logger.handle_iface_destroyed(IFACE_ID));
1732 assert_eq!(
1733 test_helper.exec.run_until_stalled(&mut handle_iface_destroyed_fut),
1734 Poll::Ready(())
1735 );
1736
1737 let mut test_fut = pin!(logger.handle_periodic_telemetry());
1738 assert_eq!(test_helper.exec.run_until_stalled(&mut test_fut), Poll::Ready(()));
1739 let telemetry_svc_stream = test_helper.telemetry_svc_stream.as_mut().unwrap();
1740 let mut telemetry_svc_req_fut = pin!(telemetry_svc_stream.try_next());
1741 match test_helper.exec.run_until_stalled(&mut telemetry_svc_req_fut) {
1743 Poll::Ready(Ok(None)) => (),
1744 other => panic!("unexpected variant: {other:?}"),
1745 }
1746 }
1747
1748 fn handle_iface_created<S: InspectSender>(
1749 test_helper: &mut TestHelper,
1750 logger: &ClientIfaceCountersLogger<S>,
1751 ) {
1752 let mut handle_iface_created_fut = pin!(logger.handle_iface_created(IFACE_ID));
1753 assert_eq!(
1754 test_helper.run_and_handle_get_sme_telemetry(&mut handle_iface_created_fut),
1755 Poll::Pending
1756 );
1757 let telemetry_support = fidl_stats::TelemetrySupport::default();
1758 assert_eq!(
1759 test_helper.run_and_respond_query_telemetry_support(
1760 &mut handle_iface_created_fut,
1761 Ok(&telemetry_support)
1762 ),
1763 Poll::Ready(())
1764 );
1765 }
1766}