1use crate::util::cobalt_logger::log_cobalt_batch;
6use fidl_fuchsia_metrics::{MetricEvent, MetricEventPayload};
7use fidl_fuchsia_wlan_stats as fidl_stats;
8use fuchsia_async::{self as fasync, TimeoutExt};
9use futures::lock::Mutex;
10
11use log::{error, warn};
12use std::collections::HashMap;
13use std::sync::atomic::{AtomicI64, Ordering};
14use std::sync::Arc;
15use windowed_stats::experimental::clock::Timed;
16use windowed_stats::experimental::series::interpolation::{Constant, LastSample};
17use windowed_stats::experimental::series::statistic::{
18 ArithmeticMean, Last, LatchMax, Max, Min, Sum,
19};
20use windowed_stats::experimental::series::{SamplingProfile, TimeMatrix};
21use windowed_stats::experimental::serve::{InspectSender, InspectedTimeMatrix};
22use wlan_legacy_metrics_registry as metrics;
23
24const GET_IFACE_STATS_TIMEOUT: zx::MonotonicDuration = zx::MonotonicDuration::from_seconds(5);
26
27#[derive(Debug)]
28enum IfaceState {
29 NotAvailable,
30 Created { iface_id: u16, telemetry_proxy: Option<fidl_fuchsia_wlan_sme::TelemetryProxy> },
31}
32
33type CountersTimeSeriesMap = HashMap<u16, InspectedTimeMatrix<u64>>;
34type GaugesTimeSeriesMap = HashMap<u16, Vec<InspectedTimeMatrix<i64>>>;
35
36pub struct ClientIfaceCountersLogger<S> {
37 iface_state: Arc<Mutex<IfaceState>>,
38 cobalt_proxy: fidl_fuchsia_metrics::MetricEventLoggerProxy,
39 monitor_svc_proxy: fidl_fuchsia_wlan_device_service::DeviceMonitorProxy,
40 time_series_stats: IfaceCountersTimeSeries,
41 driver_counters_time_matrix_client: S,
42 driver_counters_time_series: Arc<Mutex<CountersTimeSeriesMap>>,
43 driver_gauges_time_matrix_client: S,
44 driver_gauges_time_series: Arc<Mutex<GaugesTimeSeriesMap>>,
45 prev_connection_stats: Arc<Mutex<Option<fidl_stats::ConnectionStats>>>,
46 boot_mono_drift: AtomicI64,
47}
48
49impl<S: InspectSender> ClientIfaceCountersLogger<S> {
50 pub fn new(
51 cobalt_proxy: fidl_fuchsia_metrics::MetricEventLoggerProxy,
52 monitor_svc_proxy: fidl_fuchsia_wlan_device_service::DeviceMonitorProxy,
53 time_matrix_client: &S,
54 driver_counters_time_matrix_client: S,
55 driver_gauges_time_matrix_client: S,
56 ) -> Self {
57 Self {
58 iface_state: Arc::new(Mutex::new(IfaceState::NotAvailable)),
59 cobalt_proxy,
60 monitor_svc_proxy,
61 time_series_stats: IfaceCountersTimeSeries::new(time_matrix_client),
62 driver_counters_time_matrix_client,
63 driver_counters_time_series: Arc::new(Mutex::new(HashMap::new())),
64 driver_gauges_time_matrix_client,
65 driver_gauges_time_series: Arc::new(Mutex::new(HashMap::new())),
66 prev_connection_stats: Arc::new(Mutex::new(None)),
67 boot_mono_drift: AtomicI64::new(0),
68 }
69 }
70
71 pub async fn handle_iface_created(&self, iface_id: u16) {
72 let (proxy, server) = fidl::endpoints::create_proxy();
73 let telemetry_proxy = match self.monitor_svc_proxy.get_sme_telemetry(iface_id, server).await
74 {
75 Ok(Ok(())) => {
76 let (inspect_counter_configs, inspect_gauge_configs) = match proxy
77 .query_telemetry_support()
78 .await
79 {
80 Ok(Ok(support)) => {
81 (support.inspect_counter_configs, support.inspect_gauge_configs)
82 }
83 Ok(Err(code)) => {
84 warn!("Failed to query telemetry support with status code {}. No driver-specific stats will be captured", code);
85 (None, None)
86 }
87 Err(e) => {
88 error!("Failed to query telemetry support with error {}. No driver-specific stats will be captured", e);
89 (None, None)
90 }
91 };
92 if let Some(inspect_counter_configs) = &inspect_counter_configs {
93 let mut driver_counters_time_series =
94 self.driver_counters_time_series.lock().await;
95 for inspect_counter_config in inspect_counter_configs {
96 if let fidl_stats::InspectCounterConfig {
97 counter_id: Some(counter_id),
98 counter_name: Some(counter_name),
99 ..
100 } = inspect_counter_config
101 {
102 let _time_matrix_ref = driver_counters_time_series
103 .entry(*counter_id)
104 .or_insert_with(|| {
105 self.driver_counters_time_matrix_client.inspect_time_matrix(
106 counter_name,
107 TimeMatrix::<LatchMax<u64>, LastSample>::new(
108 SamplingProfile::balanced(),
109 LastSample::or(0),
110 ),
111 )
112 });
113 }
114 }
115 }
116 if let Some(inspect_gauge_configs) = &inspect_gauge_configs {
117 let mut driver_gauges_time_series = self.driver_gauges_time_series.lock().await;
118 for inspect_gauge_config in inspect_gauge_configs {
119 if let fidl_stats::InspectGaugeConfig {
120 gauge_id: Some(gauge_id),
121 gauge_name: Some(gauge_name),
122 statistics: Some(statistics),
123 ..
124 } = inspect_gauge_config
125 {
126 for statistic in statistics {
127 if let Some(time_matrix) = create_time_series_for_gauge(
128 &self.driver_gauges_time_matrix_client,
129 gauge_name,
130 statistic,
131 ) {
132 let time_matrices =
133 driver_gauges_time_series.entry(*gauge_id).or_default();
134 time_matrices.push(time_matrix);
135 }
136 }
137 }
138 }
139 }
140 Some(proxy)
141 }
142 Ok(Err(e)) => {
143 error!("Request for SME telemetry for iface {} completed with error {}. No telemetry will be captured.", iface_id, e);
144 None
145 }
146 Err(e) => {
147 error!("Failed to request SME telemetry for iface {} with error {}. No telemetry will be captured.", iface_id, e);
148 None
149 }
150 };
151 *self.iface_state.lock().await = IfaceState::Created { iface_id, telemetry_proxy }
152 }
153
154 pub async fn handle_iface_destroyed(&self, iface_id: u16) {
155 let destroyed = matches!(*self.iface_state.lock().await, IfaceState::Created { iface_id: existing_iface_id, .. } if iface_id == existing_iface_id);
156 if destroyed {
157 *self.iface_state.lock().await = IfaceState::NotAvailable;
158 }
159 }
160
161 pub async fn handle_periodic_telemetry(&self) {
162 let boot_now = fasync::BootInstant::now();
163 let mono_now = fasync::MonotonicInstant::now();
164 let boot_mono_drift = boot_now.into_nanos() - mono_now.into_nanos();
165 let prev_boot_mono_drift = self.boot_mono_drift.swap(boot_mono_drift, Ordering::SeqCst);
166 let suspended_during_last_period = boot_mono_drift > prev_boot_mono_drift;
169 match &*self.iface_state.lock().await {
170 IfaceState::NotAvailable => (),
171 IfaceState::Created { telemetry_proxy, .. } => {
172 if let Some(telemetry_proxy) = &telemetry_proxy {
173 match telemetry_proxy
174 .get_iface_stats()
175 .on_timeout(GET_IFACE_STATS_TIMEOUT, || {
176 Ok(Err(zx::Status::TIMED_OUT.into_raw()))
177 })
178 .await
179 {
180 Ok(Ok(stats)) => {
181 self.log_iface_stats_inspect(&stats).await;
182 self.log_iface_stats_cobalt(stats, suspended_during_last_period).await;
183 }
184 error => {
185 warn!("Failed to get interface stats: {:?}", error);
186 }
187 }
188 }
189 }
190 }
191 }
192
193 async fn log_iface_stats_inspect(&self, stats: &fidl_stats::IfaceStats) {
194 if let Some(counters) = &stats.driver_specific_counters {
196 let time_series = Arc::clone(&self.driver_counters_time_series);
197 log_driver_specific_counters(&counters[..], time_series).await;
198 }
199 if let Some(gauges) = &stats.driver_specific_gauges {
201 let time_series = Arc::clone(&self.driver_gauges_time_series);
202 log_driver_specific_gauges(&gauges[..], time_series).await;
203 }
204 log_connection_stats_inspect(
205 stats,
206 &self.time_series_stats,
207 Arc::clone(&self.driver_counters_time_series),
208 Arc::clone(&self.driver_gauges_time_series),
209 )
210 .await;
211 }
212
213 async fn log_iface_stats_cobalt(
214 &self,
215 stats: fidl_stats::IfaceStats,
216 suspended_during_last_period: bool,
217 ) {
218 let mut prev_connection_stats = self.prev_connection_stats.lock().await;
219 if !suspended_during_last_period {
221 if let (Some(prev_connection_stats), Some(current_connection_stats)) =
222 (prev_connection_stats.as_ref(), stats.connection_stats.as_ref())
223 {
224 match (prev_connection_stats.connection_id, current_connection_stats.connection_id)
225 {
226 (Some(prev_id), Some(current_id)) if prev_id == current_id => {
227 diff_and_log_connection_stats_cobalt(
228 &self.cobalt_proxy,
229 prev_connection_stats,
230 current_connection_stats,
231 )
232 .await;
233 }
234 _ => (),
235 }
236 }
237 }
238 *prev_connection_stats = stats.connection_stats;
239 }
240}
241
242fn create_time_series_for_gauge<S: InspectSender>(
243 time_matrix_client: &S,
244 gauge_name: &str,
245 statistic: &fidl_stats::GaugeStatistic,
246) -> Option<InspectedTimeMatrix<i64>> {
247 match statistic {
248 fidl_stats::GaugeStatistic::Min => Some(time_matrix_client.inspect_time_matrix(
249 format!("{gauge_name}.min"),
250 TimeMatrix::<Min<i64>, Constant>::new(SamplingProfile::balanced(), Constant::default()),
251 )),
252 fidl_stats::GaugeStatistic::Max => Some(time_matrix_client.inspect_time_matrix(
253 format!("{gauge_name}.max"),
254 TimeMatrix::<Max<i64>, Constant>::new(SamplingProfile::balanced(), Constant::default()),
255 )),
256 fidl_stats::GaugeStatistic::Sum => Some(time_matrix_client.inspect_time_matrix(
257 format!("{gauge_name}.sum"),
258 TimeMatrix::<Sum<i64>, Constant>::new(SamplingProfile::balanced(), Constant::default()),
259 )),
260 fidl_stats::GaugeStatistic::Last => Some(time_matrix_client.inspect_time_matrix(
261 format!("{gauge_name}.last"),
262 TimeMatrix::<Last<i64>, Constant>::new(
263 SamplingProfile::balanced(),
264 Constant::default(),
265 ),
266 )),
267 fidl_stats::GaugeStatistic::Mean => Some(time_matrix_client.inspect_time_matrix(
268 format!("{gauge_name}.mean"),
269 TimeMatrix::<ArithmeticMean<i64>, Constant>::new(
270 SamplingProfile::balanced(),
271 Constant::default(),
272 ),
273 )),
274 _ => None,
275 }
276}
277
278async fn log_connection_stats_inspect(
279 stats: &fidl_stats::IfaceStats,
280 time_series_stats: &IfaceCountersTimeSeries,
281 driver_counters_time_series: Arc<Mutex<CountersTimeSeriesMap>>,
282 driver_gauges_time_series: Arc<Mutex<GaugesTimeSeriesMap>>,
283) {
284 let connection_stats = match &stats.connection_stats {
285 Some(counters) => counters,
286 None => return,
287 };
288
289 match &connection_stats.connection_id {
291 Some(_connection_id) => (),
292 _ => {
293 warn!("connection_id is not present, no connection counters will be logged");
294 return;
295 }
296 }
297
298 if let fidl_stats::ConnectionStats {
299 rx_unicast_total: Some(rx_unicast_total),
300 rx_unicast_drop: Some(rx_unicast_drop),
301 ..
302 } = connection_stats
303 {
304 time_series_stats.log_rx_unicast_total(*rx_unicast_total);
305 time_series_stats.log_rx_unicast_drop(*rx_unicast_drop);
306 }
307
308 if let fidl_stats::ConnectionStats {
309 tx_total: Some(tx_total), tx_drop: Some(tx_drop), ..
310 } = connection_stats
311 {
312 time_series_stats.log_tx_total(*tx_total);
313 time_series_stats.log_tx_drop(*tx_drop);
314 }
315
316 if let Some(counters) = &connection_stats.driver_specific_counters {
318 log_driver_specific_counters(&counters[..], driver_counters_time_series).await;
319 }
320 if let Some(gauges) = &connection_stats.driver_specific_gauges {
322 log_driver_specific_gauges(&gauges[..], driver_gauges_time_series).await;
323 }
324}
325
326async fn log_driver_specific_counters(
327 driver_specific_counters: &[fidl_stats::UnnamedCounter],
328 driver_counters_time_series: Arc<Mutex<CountersTimeSeriesMap>>,
329) {
330 let time_series_map = driver_counters_time_series.lock().await;
331 for counter in driver_specific_counters {
332 if let Some(ts) = time_series_map.get(&counter.id) {
333 ts.fold_or_log_error(Timed::now(counter.count));
334 }
335 }
336}
337
338async fn log_driver_specific_gauges(
339 driver_specific_gauges: &[fidl_stats::UnnamedGauge],
340 driver_gauges_time_series: Arc<Mutex<GaugesTimeSeriesMap>>,
341) {
342 let time_series_map = driver_gauges_time_series.lock().await;
343 for gauge in driver_specific_gauges {
344 if let Some(time_matrices) = time_series_map.get(&gauge.id) {
345 for ts in time_matrices {
346 ts.fold_or_log_error(Timed::now(gauge.value));
347 }
348 }
349 }
350}
351
352async fn diff_and_log_connection_stats_cobalt(
353 cobalt_proxy: &fidl_fuchsia_metrics::MetricEventLoggerProxy,
354 prev: &fidl_stats::ConnectionStats,
355 current: &fidl_stats::ConnectionStats,
356) {
357 diff_and_log_rx_cobalt(cobalt_proxy, prev, current).await;
358 diff_and_log_tx_cobalt(cobalt_proxy, prev, current).await;
359}
360
361async fn diff_and_log_rx_cobalt(
362 cobalt_proxy: &fidl_fuchsia_metrics::MetricEventLoggerProxy,
363 prev: &fidl_stats::ConnectionStats,
364 current: &fidl_stats::ConnectionStats,
365) {
366 let mut metric_events = vec![];
367
368 let (current_rx_unicast_total, prev_rx_unicast_total) =
369 match (current.rx_unicast_total, prev.rx_unicast_total) {
370 (Some(current), Some(prev)) => (current, prev),
371 _ => return,
372 };
373 let (current_rx_unicast_drop, prev_rx_unicast_drop) =
374 match (current.rx_unicast_drop, prev.rx_unicast_drop) {
375 (Some(current), Some(prev)) => (current, prev),
376 _ => return,
377 };
378
379 let rx_total = current_rx_unicast_total - prev_rx_unicast_total;
380 let rx_drop = current_rx_unicast_drop - prev_rx_unicast_drop;
381 let rx_drop_rate = if rx_total > 0 { rx_drop as f64 / rx_total as f64 } else { 0f64 };
382
383 metric_events.push(MetricEvent {
384 metric_id: metrics::BAD_RX_RATE_METRIC_ID,
385 event_codes: vec![],
386 payload: MetricEventPayload::IntegerValue(float_to_ten_thousandth(rx_drop_rate)),
387 });
388 metric_events.push(MetricEvent {
389 metric_id: metrics::RX_UNICAST_PACKETS_METRIC_ID,
390 event_codes: vec![],
391 payload: MetricEventPayload::IntegerValue(rx_total as i64),
392 });
393
394 log_cobalt_batch!(cobalt_proxy, &metric_events, "diff_and_log_rx_cobalt",);
395}
396
397async fn diff_and_log_tx_cobalt(
398 cobalt_proxy: &fidl_fuchsia_metrics::MetricEventLoggerProxy,
399 prev: &fidl_stats::ConnectionStats,
400 current: &fidl_stats::ConnectionStats,
401) {
402 let mut metric_events = vec![];
403
404 let (current_tx_total, prev_tx_total) = match (current.tx_total, prev.tx_total) {
405 (Some(current), Some(prev)) => (current, prev),
406 _ => return,
407 };
408 let (current_tx_drop, prev_tx_drop) = match (current.tx_drop, prev.tx_drop) {
409 (Some(current), Some(prev)) => (current, prev),
410 _ => return,
411 };
412
413 let tx_total = current_tx_total - prev_tx_total;
414 let tx_drop = current_tx_drop - prev_tx_drop;
415 let tx_drop_rate = if tx_total > 0 { tx_drop as f64 / tx_total as f64 } else { 0f64 };
416
417 metric_events.push(MetricEvent {
418 metric_id: metrics::BAD_TX_RATE_METRIC_ID,
419 event_codes: vec![],
420 payload: MetricEventPayload::IntegerValue(float_to_ten_thousandth(tx_drop_rate)),
421 });
422
423 log_cobalt_batch!(cobalt_proxy, &metric_events, "diff_and_log_tx_cobalt",);
424}
425
426fn float_to_ten_thousandth(value: f64) -> i64 {
429 (value * 10000f64) as i64
430}
431
432#[derive(Debug, Clone)]
433struct IfaceCountersTimeSeries {
434 rx_unicast_total: InspectedTimeMatrix<u64>,
435 rx_unicast_drop: InspectedTimeMatrix<u64>,
436 tx_total: InspectedTimeMatrix<u64>,
437 tx_drop: InspectedTimeMatrix<u64>,
438}
439
440impl IfaceCountersTimeSeries {
441 pub fn new<S: InspectSender>(client: &S) -> Self {
442 let rx_unicast_total = client.inspect_time_matrix(
443 "rx_unicast_total",
444 TimeMatrix::<LatchMax<u64>, LastSample>::new(
445 SamplingProfile::balanced(),
446 LastSample::or(0),
447 ),
448 );
449 let rx_unicast_drop = client.inspect_time_matrix(
450 "rx_unicast_drop",
451 TimeMatrix::<LatchMax<u64>, LastSample>::new(
452 SamplingProfile::balanced(),
453 LastSample::or(0),
454 ),
455 );
456 let tx_total = client.inspect_time_matrix(
457 "tx_total",
458 TimeMatrix::<LatchMax<u64>, LastSample>::new(
459 SamplingProfile::balanced(),
460 LastSample::or(0),
461 ),
462 );
463 let tx_drop = client.inspect_time_matrix(
464 "tx_drop",
465 TimeMatrix::<LatchMax<u64>, LastSample>::new(
466 SamplingProfile::balanced(),
467 LastSample::or(0),
468 ),
469 );
470 Self { rx_unicast_total, rx_unicast_drop, tx_total, tx_drop }
471 }
472
473 fn log_rx_unicast_total(&self, data: u64) {
474 self.rx_unicast_total.fold_or_log_error(Timed::now(data));
475 }
476
477 fn log_rx_unicast_drop(&self, data: u64) {
478 self.rx_unicast_drop.fold_or_log_error(Timed::now(data));
479 }
480
481 fn log_tx_total(&self, data: u64) {
482 self.tx_total.fold_or_log_error(Timed::now(data));
483 }
484
485 fn log_tx_drop(&self, data: u64) {
486 self.tx_drop.fold_or_log_error(Timed::now(data));
487 }
488}
489
490#[cfg(test)]
491mod tests {
492 use super::*;
493 use crate::testing::*;
494 use futures::TryStreamExt;
495 use std::pin::pin;
496 use std::task::Poll;
497 use test_case::test_case;
498 use windowed_stats::experimental::testing::{MockTimeMatrixClient, TimeMatrixCall};
499 use wlan_common::assert_variant;
500
501 const IFACE_ID: u16 = 66;
502
503 #[fuchsia::test]
504 fn test_handle_iface_created() {
505 let mut test_helper = setup_test();
506 let driver_counters_mock_matrix_client = MockTimeMatrixClient::new();
507 let driver_gauges_mock_matrix_client = MockTimeMatrixClient::new();
508 let logger = ClientIfaceCountersLogger::new(
509 test_helper.cobalt_proxy.clone(),
510 test_helper.monitor_svc_proxy.clone(),
511 &test_helper.mock_time_matrix_client,
512 driver_counters_mock_matrix_client.clone(),
513 driver_gauges_mock_matrix_client.clone(),
514 );
515
516 let mut handle_iface_created_fut = pin!(logger.handle_iface_created(IFACE_ID));
517 assert_eq!(
518 test_helper.run_and_handle_get_sme_telemetry(&mut handle_iface_created_fut),
519 Poll::Pending
520 );
521
522 let mocked_inspect_counter_configs = vec![fidl_stats::InspectCounterConfig {
523 counter_id: Some(1),
524 counter_name: Some("foo_counter".to_string()),
525 ..Default::default()
526 }];
527 let telemetry_support = fidl_stats::TelemetrySupport {
528 inspect_counter_configs: Some(mocked_inspect_counter_configs),
529 ..Default::default()
530 };
531 assert_eq!(
532 test_helper.run_and_respond_query_telemetry_support(
533 &mut handle_iface_created_fut,
534 Ok(&telemetry_support)
535 ),
536 Poll::Ready(())
537 );
538
539 assert_variant!(logger.iface_state.try_lock().as_deref(), Some(IfaceState::Created { .. }));
540 let driver_counters_time_series = logger.driver_counters_time_series.try_lock().unwrap();
541 assert_eq!(driver_counters_time_series.keys().copied().collect::<Vec<u16>>(), vec![1u16],);
542 }
543
544 #[fuchsia::test]
545 fn test_handle_periodic_telemetry_connection_stats() {
546 let mut test_helper = setup_test();
547 let driver_counters_mock_matrix_client = MockTimeMatrixClient::new();
548 let driver_gauges_mock_matrix_client = MockTimeMatrixClient::new();
549 let logger = ClientIfaceCountersLogger::new(
550 test_helper.cobalt_proxy.clone(),
551 test_helper.monitor_svc_proxy.clone(),
552 &test_helper.mock_time_matrix_client,
553 driver_counters_mock_matrix_client.clone(),
554 driver_gauges_mock_matrix_client.clone(),
555 );
556
557 handle_iface_created(&mut test_helper, &logger);
559
560 let mut test_fut = pin!(logger.handle_periodic_telemetry());
561 let iface_stats = fidl_stats::IfaceStats {
562 connection_stats: Some(fidl_stats::ConnectionStats {
563 connection_id: Some(1),
564 rx_unicast_total: Some(100),
565 rx_unicast_drop: Some(5),
566 rx_multicast: Some(30),
567 tx_total: Some(50),
568 tx_drop: Some(2),
569 ..Default::default()
570 }),
571 ..Default::default()
572 };
573 assert_eq!(
574 test_helper.run_and_respond_iface_stats_req(&mut test_fut, Ok(&iface_stats)),
575 Poll::Ready(())
576 );
577
578 let mut time_matrix_calls = test_helper.mock_time_matrix_client.drain_calls();
579 assert_eq!(
580 &time_matrix_calls.drain::<u64>("rx_unicast_total")[..],
581 &[TimeMatrixCall::Fold(Timed::now(100u64))]
582 );
583 assert_eq!(
584 &time_matrix_calls.drain::<u64>("rx_unicast_drop")[..],
585 &[TimeMatrixCall::Fold(Timed::now(5u64))]
586 );
587 assert_eq!(
588 &time_matrix_calls.drain::<u64>("tx_total")[..],
589 &[TimeMatrixCall::Fold(Timed::now(50u64))]
590 );
591 assert_eq!(
592 &time_matrix_calls.drain::<u64>("tx_drop")[..],
593 &[TimeMatrixCall::Fold(Timed::now(2u64))]
594 );
595 }
596
597 #[fuchsia::test]
598 fn test_handle_periodic_telemetry_driver_specific_counters() {
599 let mut test_helper = setup_test();
600 let driver_counters_mock_matrix_client = MockTimeMatrixClient::new();
601 let driver_gauges_mock_matrix_client = MockTimeMatrixClient::new();
602 let logger = ClientIfaceCountersLogger::new(
603 test_helper.cobalt_proxy.clone(),
604 test_helper.monitor_svc_proxy.clone(),
605 &test_helper.mock_time_matrix_client,
606 driver_counters_mock_matrix_client.clone(),
607 driver_gauges_mock_matrix_client.clone(),
608 );
609
610 let mut handle_iface_created_fut = pin!(logger.handle_iface_created(IFACE_ID));
611 assert_eq!(
612 test_helper.run_and_handle_get_sme_telemetry(&mut handle_iface_created_fut),
613 Poll::Pending
614 );
615
616 let mocked_inspect_configs = vec![
617 fidl_stats::InspectCounterConfig {
618 counter_id: Some(1),
619 counter_name: Some("foo_counter".to_string()),
620 ..Default::default()
621 },
622 fidl_stats::InspectCounterConfig {
623 counter_id: Some(2),
624 counter_name: Some("bar_counter".to_string()),
625 ..Default::default()
626 },
627 fidl_stats::InspectCounterConfig {
628 counter_id: Some(3),
629 counter_name: Some("baz_counter".to_string()),
630 ..Default::default()
631 },
632 ];
633 let telemetry_support = fidl_stats::TelemetrySupport {
634 inspect_counter_configs: Some(mocked_inspect_configs),
635 ..Default::default()
636 };
637 assert_eq!(
638 test_helper.run_and_respond_query_telemetry_support(
639 &mut handle_iface_created_fut,
640 Ok(&telemetry_support)
641 ),
642 Poll::Ready(())
643 );
644
645 let mut test_fut = pin!(logger.handle_periodic_telemetry());
646 let iface_stats = fidl_stats::IfaceStats {
647 driver_specific_counters: Some(vec![fidl_stats::UnnamedCounter { id: 1, count: 50 }]),
648 connection_stats: Some(fidl_stats::ConnectionStats {
649 connection_id: Some(1),
650 driver_specific_counters: Some(vec![
651 fidl_stats::UnnamedCounter { id: 2, count: 100 },
652 fidl_stats::UnnamedCounter { id: 3, count: 150 },
653 fidl_stats::UnnamedCounter { id: 4, count: 200 },
655 ]),
656 ..Default::default()
657 }),
658 ..Default::default()
659 };
660 assert_eq!(
661 test_helper.run_and_respond_iface_stats_req(&mut test_fut, Ok(&iface_stats)),
662 Poll::Ready(())
663 );
664
665 let time_matrix_calls = test_helper.mock_time_matrix_client.drain_calls();
666 assert!(time_matrix_calls.is_empty());
667
668 let mut driver_counters_matrix_calls = driver_counters_mock_matrix_client.drain_calls();
669 assert_eq!(
670 &driver_counters_matrix_calls.drain::<u64>("foo_counter")[..],
671 &[TimeMatrixCall::Fold(Timed::now(50))]
672 );
673 assert_eq!(
674 &driver_counters_matrix_calls.drain::<u64>("bar_counter")[..],
675 &[TimeMatrixCall::Fold(Timed::now(100))]
676 );
677 assert_eq!(
678 &driver_counters_matrix_calls.drain::<u64>("baz_counter")[..],
679 &[TimeMatrixCall::Fold(Timed::now(150))]
680 );
681
682 let driver_gauges_matrix_calls = driver_gauges_mock_matrix_client.drain_calls();
683 assert!(driver_gauges_matrix_calls.is_empty());
684 }
685
686 #[fuchsia::test]
687 fn test_handle_periodic_telemetry_driver_specific_gauges() {
688 let mut test_helper = setup_test();
689 let driver_counters_mock_matrix_client = MockTimeMatrixClient::new();
690 let driver_gauges_mock_matrix_client = MockTimeMatrixClient::new();
691 let logger = ClientIfaceCountersLogger::new(
692 test_helper.cobalt_proxy.clone(),
693 test_helper.monitor_svc_proxy.clone(),
694 &test_helper.mock_time_matrix_client,
695 driver_counters_mock_matrix_client.clone(),
696 driver_gauges_mock_matrix_client.clone(),
697 );
698
699 let mut handle_iface_created_fut = pin!(logger.handle_iface_created(IFACE_ID));
700 assert_eq!(
701 test_helper.run_and_handle_get_sme_telemetry(&mut handle_iface_created_fut),
702 Poll::Pending
703 );
704
705 let mocked_inspect_configs = vec![
706 fidl_stats::InspectGaugeConfig {
707 gauge_id: Some(1),
708 gauge_name: Some("foo_gauge".to_string()),
709 statistics: Some(vec![
710 fidl_stats::GaugeStatistic::Mean,
711 fidl_stats::GaugeStatistic::Last,
712 ]),
713 ..Default::default()
714 },
715 fidl_stats::InspectGaugeConfig {
716 gauge_id: Some(2),
717 gauge_name: Some("bar_gauge".to_string()),
718 statistics: Some(vec![
719 fidl_stats::GaugeStatistic::Min,
720 fidl_stats::GaugeStatistic::Sum,
721 ]),
722 ..Default::default()
723 },
724 fidl_stats::InspectGaugeConfig {
725 gauge_id: Some(3),
726 gauge_name: Some("baz_gauge".to_string()),
727 statistics: Some(vec![fidl_stats::GaugeStatistic::Max]),
728 ..Default::default()
729 },
730 ];
731 let telemetry_support = fidl_stats::TelemetrySupport {
732 inspect_gauge_configs: Some(mocked_inspect_configs),
733 ..Default::default()
734 };
735 assert_eq!(
736 test_helper.run_and_respond_query_telemetry_support(
737 &mut handle_iface_created_fut,
738 Ok(&telemetry_support)
739 ),
740 Poll::Ready(())
741 );
742
743 let mut test_fut = pin!(logger.handle_periodic_telemetry());
744 let iface_stats = fidl_stats::IfaceStats {
745 driver_specific_gauges: Some(vec![fidl_stats::UnnamedGauge { id: 1, value: 50 }]),
746 connection_stats: Some(fidl_stats::ConnectionStats {
747 connection_id: Some(1),
748 driver_specific_gauges: Some(vec![
749 fidl_stats::UnnamedGauge { id: 2, value: 100 },
750 fidl_stats::UnnamedGauge { id: 3, value: 150 },
751 fidl_stats::UnnamedGauge { id: 4, value: 200 },
753 ]),
754 ..Default::default()
755 }),
756 ..Default::default()
757 };
758 assert_eq!(
759 test_helper.run_and_respond_iface_stats_req(&mut test_fut, Ok(&iface_stats)),
760 Poll::Ready(())
761 );
762
763 let time_matrix_calls = test_helper.mock_time_matrix_client.drain_calls();
764 assert!(time_matrix_calls.is_empty());
765
766 let driver_counters_matrix_calls = driver_counters_mock_matrix_client.drain_calls();
767 assert!(driver_counters_matrix_calls.is_empty());
768
769 let mut driver_gauges_matrix_calls = driver_gauges_mock_matrix_client.drain_calls();
770 assert_eq!(
771 &driver_gauges_matrix_calls.drain::<i64>("foo_gauge.mean")[..],
772 &[TimeMatrixCall::Fold(Timed::now(50))]
773 );
774 assert_eq!(
775 &driver_gauges_matrix_calls.drain::<i64>("foo_gauge.last")[..],
776 &[TimeMatrixCall::Fold(Timed::now(50))]
777 );
778 assert_eq!(
779 &driver_gauges_matrix_calls.drain::<i64>("bar_gauge.min")[..],
780 &[TimeMatrixCall::Fold(Timed::now(100))]
781 );
782 assert_eq!(
783 &driver_gauges_matrix_calls.drain::<i64>("bar_gauge.sum")[..],
784 &[TimeMatrixCall::Fold(Timed::now(100))]
785 );
786 assert_eq!(
787 &driver_gauges_matrix_calls.drain::<i64>("baz_gauge.max")[..],
788 &[TimeMatrixCall::Fold(Timed::now(150))]
789 );
790 }
791
792 #[fuchsia::test]
793 fn test_handle_periodic_telemetry_cobalt() {
794 let mut test_helper = setup_test();
795 let driver_counters_mock_matrix_client = MockTimeMatrixClient::new();
796 let driver_gauges_mock_matrix_client = MockTimeMatrixClient::new();
797 let logger = ClientIfaceCountersLogger::new(
798 test_helper.cobalt_proxy.clone(),
799 test_helper.monitor_svc_proxy.clone(),
800 &test_helper.mock_time_matrix_client,
801 driver_counters_mock_matrix_client.clone(),
802 driver_gauges_mock_matrix_client.clone(),
803 );
804
805 handle_iface_created(&mut test_helper, &logger);
807
808 let mut test_fut = pin!(logger.handle_periodic_telemetry());
809 let iface_stats = fidl_stats::IfaceStats {
810 connection_stats: Some(fidl_stats::ConnectionStats {
811 connection_id: Some(1),
812 rx_unicast_total: Some(100),
813 rx_unicast_drop: Some(5),
814 rx_multicast: Some(30),
815 tx_total: Some(50),
816 tx_drop: Some(2),
817 ..Default::default()
818 }),
819 ..Default::default()
820 };
821 assert_eq!(
822 test_helper.run_and_respond_iface_stats_req(&mut test_fut, Ok(&iface_stats)),
823 Poll::Ready(())
824 );
825
826 let metrics = test_helper.get_logged_metrics(metrics::BAD_RX_RATE_METRIC_ID);
827 assert!(metrics.is_empty());
828 let metrics = test_helper.get_logged_metrics(metrics::RX_UNICAST_PACKETS_METRIC_ID);
829 assert!(metrics.is_empty());
830 let metrics = test_helper.get_logged_metrics(metrics::BAD_TX_RATE_METRIC_ID);
831 assert!(metrics.is_empty());
832
833 let mut test_fut = pin!(logger.handle_periodic_telemetry());
834 let iface_stats = fidl_stats::IfaceStats {
835 connection_stats: Some(fidl_stats::ConnectionStats {
836 connection_id: Some(1),
837 rx_unicast_total: Some(200),
838 rx_unicast_drop: Some(15),
839 rx_multicast: Some(30),
840 tx_total: Some(150),
841 tx_drop: Some(3),
842 ..Default::default()
843 }),
844 ..Default::default()
845 };
846 assert_eq!(
847 test_helper.run_and_respond_iface_stats_req(&mut test_fut, Ok(&iface_stats)),
848 Poll::Pending
849 );
850 assert_eq!(
851 test_helper.run_until_stalled_drain_cobalt_events(&mut test_fut),
852 Poll::Ready(())
853 );
854
855 let metrics = test_helper.get_logged_metrics(metrics::BAD_RX_RATE_METRIC_ID);
856 assert_eq!(metrics.len(), 1);
857 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(1000)); let metrics = test_helper.get_logged_metrics(metrics::RX_UNICAST_PACKETS_METRIC_ID);
859 assert_eq!(metrics.len(), 1);
860 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(100));
861 let metrics = test_helper.get_logged_metrics(metrics::BAD_TX_RATE_METRIC_ID);
862 assert_eq!(metrics.len(), 1);
863 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(100)); }
865
866 #[fuchsia::test]
867 fn test_handle_periodic_telemetry_cobalt_changed_connection_id() {
868 let mut test_helper = setup_test();
869 let driver_counters_mock_matrix_client = MockTimeMatrixClient::new();
870 let driver_gauges_mock_matrix_client = MockTimeMatrixClient::new();
871 let logger = ClientIfaceCountersLogger::new(
872 test_helper.cobalt_proxy.clone(),
873 test_helper.monitor_svc_proxy.clone(),
874 &test_helper.mock_time_matrix_client,
875 driver_counters_mock_matrix_client.clone(),
876 driver_gauges_mock_matrix_client.clone(),
877 );
878
879 handle_iface_created(&mut test_helper, &logger);
881
882 let mut test_fut = pin!(logger.handle_periodic_telemetry());
883 let iface_stats = fidl_stats::IfaceStats {
884 connection_stats: Some(fidl_stats::ConnectionStats {
885 connection_id: Some(1),
886 rx_unicast_total: Some(100),
887 rx_unicast_drop: Some(5),
888 rx_multicast: Some(30),
889 tx_total: Some(50),
890 tx_drop: Some(2),
891 ..Default::default()
892 }),
893 ..Default::default()
894 };
895 assert_eq!(
896 test_helper.run_and_respond_iface_stats_req(&mut test_fut, Ok(&iface_stats)),
897 Poll::Ready(())
898 );
899
900 let metrics = test_helper.get_logged_metrics(metrics::BAD_RX_RATE_METRIC_ID);
901 assert!(metrics.is_empty());
902 let metrics = test_helper.get_logged_metrics(metrics::RX_UNICAST_PACKETS_METRIC_ID);
903 assert!(metrics.is_empty());
904 let metrics = test_helper.get_logged_metrics(metrics::BAD_TX_RATE_METRIC_ID);
905 assert!(metrics.is_empty());
906
907 let mut test_fut = pin!(logger.handle_periodic_telemetry());
908 let iface_stats = fidl_stats::IfaceStats {
909 connection_stats: Some(fidl_stats::ConnectionStats {
910 connection_id: Some(2),
911 rx_unicast_total: Some(200),
912 rx_unicast_drop: Some(15),
913 rx_multicast: Some(30),
914 tx_total: Some(150),
915 tx_drop: Some(3),
916 ..Default::default()
917 }),
918 ..Default::default()
919 };
920 assert_eq!(
921 test_helper.run_and_respond_iface_stats_req(&mut test_fut, Ok(&iface_stats)),
922 Poll::Ready(())
923 );
924
925 let metrics = test_helper.get_logged_metrics(metrics::BAD_RX_RATE_METRIC_ID);
928 assert!(metrics.is_empty());
929 let metrics = test_helper.get_logged_metrics(metrics::RX_UNICAST_PACKETS_METRIC_ID);
930 assert!(metrics.is_empty());
931 let metrics = test_helper.get_logged_metrics(metrics::BAD_TX_RATE_METRIC_ID);
932 assert!(metrics.is_empty());
933
934 let mut test_fut = pin!(logger.handle_periodic_telemetry());
935 let iface_stats = fidl_stats::IfaceStats {
936 connection_stats: Some(fidl_stats::ConnectionStats {
937 connection_id: Some(2),
938 rx_unicast_total: Some(300),
939 rx_unicast_drop: Some(18),
940 rx_multicast: Some(30),
941 tx_total: Some(250),
942 tx_drop: Some(5),
943 ..Default::default()
944 }),
945 ..Default::default()
946 };
947 assert_eq!(
948 test_helper.run_and_respond_iface_stats_req(&mut test_fut, Ok(&iface_stats)),
949 Poll::Pending
950 );
951 assert_eq!(
952 test_helper.run_until_stalled_drain_cobalt_events(&mut test_fut),
953 Poll::Ready(())
954 );
955
956 let metrics = test_helper.get_logged_metrics(metrics::BAD_RX_RATE_METRIC_ID);
957 assert_eq!(metrics.len(), 1);
958 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(300)); let metrics = test_helper.get_logged_metrics(metrics::RX_UNICAST_PACKETS_METRIC_ID);
960 assert_eq!(metrics.len(), 1);
961 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(100));
962 let metrics = test_helper.get_logged_metrics(metrics::BAD_TX_RATE_METRIC_ID);
963 assert_eq!(metrics.len(), 1);
964 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(200)); }
966
967 #[fuchsia::test]
968 fn test_handle_periodic_telemetry_cobalt_suspension_in_between() {
969 let mut test_helper = setup_test();
970 let driver_counters_mock_matrix_client = MockTimeMatrixClient::new();
971 let driver_gauges_mock_matrix_client = MockTimeMatrixClient::new();
972 let logger = ClientIfaceCountersLogger::new(
973 test_helper.cobalt_proxy.clone(),
974 test_helper.monitor_svc_proxy.clone(),
975 &test_helper.mock_time_matrix_client,
976 driver_counters_mock_matrix_client.clone(),
977 driver_gauges_mock_matrix_client.clone(),
978 );
979
980 handle_iface_created(&mut test_helper, &logger);
982
983 let mut test_fut = pin!(logger.handle_periodic_telemetry());
984 let iface_stats = fidl_stats::IfaceStats {
985 connection_stats: Some(fidl_stats::ConnectionStats {
986 connection_id: Some(1),
987 rx_unicast_total: Some(100),
988 rx_unicast_drop: Some(5),
989 rx_multicast: Some(30),
990 tx_total: Some(50),
991 tx_drop: Some(2),
992 ..Default::default()
993 }),
994 ..Default::default()
995 };
996 assert_eq!(
997 test_helper.run_and_respond_iface_stats_req(&mut test_fut, Ok(&iface_stats)),
998 Poll::Ready(())
999 );
1000
1001 let metrics = test_helper.get_logged_metrics(metrics::BAD_RX_RATE_METRIC_ID);
1002 assert!(metrics.is_empty());
1003 let metrics = test_helper.get_logged_metrics(metrics::RX_UNICAST_PACKETS_METRIC_ID);
1004 assert!(metrics.is_empty());
1005 let metrics = test_helper.get_logged_metrics(metrics::BAD_TX_RATE_METRIC_ID);
1006 assert!(metrics.is_empty());
1007
1008 test_helper.exec.set_fake_boot_to_mono_offset(zx::BootDuration::from_millis(1));
1009 let mut test_fut = pin!(logger.handle_periodic_telemetry());
1010 let iface_stats = fidl_stats::IfaceStats {
1011 connection_stats: Some(fidl_stats::ConnectionStats {
1012 connection_id: Some(1),
1013 rx_unicast_total: Some(200),
1014 rx_unicast_drop: Some(15),
1015 rx_multicast: Some(30),
1016 tx_total: Some(150),
1017 tx_drop: Some(3),
1018 ..Default::default()
1019 }),
1020 ..Default::default()
1021 };
1022 assert_eq!(
1023 test_helper.run_and_respond_iface_stats_req(&mut test_fut, Ok(&iface_stats)),
1024 Poll::Ready(())
1025 );
1026
1027 let metrics = test_helper.get_logged_metrics(metrics::BAD_RX_RATE_METRIC_ID);
1030 assert!(metrics.is_empty());
1031 let metrics = test_helper.get_logged_metrics(metrics::RX_UNICAST_PACKETS_METRIC_ID);
1032 assert!(metrics.is_empty());
1033 let metrics = test_helper.get_logged_metrics(metrics::BAD_TX_RATE_METRIC_ID);
1034 assert!(metrics.is_empty());
1035
1036 let mut test_fut = pin!(logger.handle_periodic_telemetry());
1037 let iface_stats = fidl_stats::IfaceStats {
1038 connection_stats: Some(fidl_stats::ConnectionStats {
1039 connection_id: Some(1),
1040 rx_unicast_total: Some(300),
1041 rx_unicast_drop: Some(18),
1042 rx_multicast: Some(30),
1043 tx_total: Some(250),
1044 tx_drop: Some(5),
1045 ..Default::default()
1046 }),
1047 ..Default::default()
1048 };
1049 assert_eq!(
1050 test_helper.run_and_respond_iface_stats_req(&mut test_fut, Ok(&iface_stats)),
1051 Poll::Pending
1052 );
1053 assert_eq!(
1054 test_helper.run_until_stalled_drain_cobalt_events(&mut test_fut),
1055 Poll::Ready(())
1056 );
1057
1058 let metrics = test_helper.get_logged_metrics(metrics::BAD_RX_RATE_METRIC_ID);
1059 assert_eq!(metrics.len(), 1);
1060 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(300)); let metrics = test_helper.get_logged_metrics(metrics::RX_UNICAST_PACKETS_METRIC_ID);
1062 assert_eq!(metrics.len(), 1);
1063 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(100));
1064 let metrics = test_helper.get_logged_metrics(metrics::BAD_TX_RATE_METRIC_ID);
1065 assert_eq!(metrics.len(), 1);
1066 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(200)); }
1068
1069 #[fuchsia::test]
1070 fn test_diff_and_log_rx_cobalt() {
1071 let mut test_helper = setup_test();
1072 let prev_stats = fidl_stats::ConnectionStats {
1073 rx_unicast_total: Some(100),
1074 rx_unicast_drop: Some(5),
1075 ..Default::default()
1076 };
1077 let current_stats = fidl_stats::ConnectionStats {
1078 rx_unicast_total: Some(300),
1079 rx_unicast_drop: Some(7),
1080 ..Default::default()
1081 };
1082 let cobalt_proxy = test_helper.cobalt_proxy.clone();
1083 let mut test_fut = pin!(diff_and_log_rx_cobalt(&cobalt_proxy, &prev_stats, ¤t_stats));
1084 assert_eq!(
1085 test_helper.run_until_stalled_drain_cobalt_events(&mut test_fut),
1086 Poll::Ready(())
1087 );
1088 let metrics = test_helper.get_logged_metrics(metrics::BAD_RX_RATE_METRIC_ID);
1089 assert_eq!(metrics.len(), 1);
1090 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(100)); let metrics = test_helper.get_logged_metrics(metrics::RX_UNICAST_PACKETS_METRIC_ID);
1092 assert_eq!(metrics.len(), 1);
1093 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(200));
1094 }
1095
1096 #[test_case(
1097 fidl_stats::ConnectionStats { ..Default::default() },
1098 fidl_stats::ConnectionStats { ..Default::default() };
1099 "both empty"
1100 )]
1101 #[test_case(
1102 fidl_stats::ConnectionStats {
1103 rx_unicast_total: Some(100),
1104 rx_unicast_drop: Some(5),
1105 ..Default::default()
1106 },
1107 fidl_stats::ConnectionStats { ..Default::default() };
1108 "current empty"
1109 )]
1110 #[test_case(
1111 fidl_stats::ConnectionStats { ..Default::default() },
1112 fidl_stats::ConnectionStats {
1113 rx_unicast_total: Some(300),
1114 rx_unicast_drop: Some(7),
1115 ..Default::default()
1116 };
1117 "prev empty"
1118 )]
1119 #[fuchsia::test(add_test_attr = false)]
1120 fn test_diff_and_log_rx_cobalt_empty(
1121 prev_stats: fidl_stats::ConnectionStats,
1122 current_stats: fidl_stats::ConnectionStats,
1123 ) {
1124 let mut test_helper = setup_test();
1125 let cobalt_proxy = test_helper.cobalt_proxy.clone();
1126 let mut test_fut = pin!(diff_and_log_rx_cobalt(&cobalt_proxy, &prev_stats, ¤t_stats));
1127 assert_eq!(
1128 test_helper.run_until_stalled_drain_cobalt_events(&mut test_fut),
1129 Poll::Ready(())
1130 );
1131 let metrics = test_helper.get_logged_metrics(metrics::BAD_RX_RATE_METRIC_ID);
1132 assert!(metrics.is_empty())
1133 }
1134
1135 #[fuchsia::test]
1136 fn test_diff_and_log_tx_cobalt() {
1137 let mut test_helper = setup_test();
1138 let prev_stats = fidl_stats::ConnectionStats {
1139 tx_total: Some(100),
1140 tx_drop: Some(5),
1141 ..Default::default()
1142 };
1143 let current_stats = fidl_stats::ConnectionStats {
1144 tx_total: Some(300),
1145 tx_drop: Some(7),
1146 ..Default::default()
1147 };
1148 let cobalt_proxy = test_helper.cobalt_proxy.clone();
1149 let mut test_fut = pin!(diff_and_log_tx_cobalt(&cobalt_proxy, &prev_stats, ¤t_stats));
1150 assert_eq!(
1151 test_helper.run_until_stalled_drain_cobalt_events(&mut test_fut),
1152 Poll::Ready(())
1153 );
1154 let metrics = test_helper.get_logged_metrics(metrics::BAD_TX_RATE_METRIC_ID);
1155 assert_eq!(metrics.len(), 1);
1156 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(100)); }
1158
1159 #[test_case(
1160 fidl_stats::ConnectionStats { ..Default::default() },
1161 fidl_stats::ConnectionStats { ..Default::default() };
1162 "both empty"
1163 )]
1164 #[test_case(
1165 fidl_stats::ConnectionStats {
1166 tx_total: Some(100),
1167 tx_drop: Some(5),
1168 ..Default::default()
1169 },
1170 fidl_stats::ConnectionStats { ..Default::default() };
1171 "current empty"
1172 )]
1173 #[test_case(
1174 fidl_stats::ConnectionStats { ..Default::default() },
1175 fidl_stats::ConnectionStats {
1176 tx_total: Some(300),
1177 tx_drop: Some(7),
1178 ..Default::default()
1179 };
1180 "prev empty"
1181 )]
1182 #[fuchsia::test(add_test_attr = false)]
1183 fn test_diff_and_log_tx_cobalt_empty(
1184 prev_stats: fidl_stats::ConnectionStats,
1185 current_stats: fidl_stats::ConnectionStats,
1186 ) {
1187 let mut test_helper = setup_test();
1188 let cobalt_proxy = test_helper.cobalt_proxy.clone();
1189 let mut test_fut = pin!(diff_and_log_tx_cobalt(&cobalt_proxy, &prev_stats, ¤t_stats));
1190 assert_eq!(
1191 test_helper.run_until_stalled_drain_cobalt_events(&mut test_fut),
1192 Poll::Ready(())
1193 );
1194 let metrics = test_helper.get_logged_metrics(metrics::BAD_TX_RATE_METRIC_ID);
1195 assert!(metrics.is_empty())
1196 }
1197
1198 #[fuchsia::test]
1199 fn test_handle_iface_destroyed() {
1200 let mut test_helper = setup_test();
1201 let driver_counters_mock_matrix_client = MockTimeMatrixClient::new();
1202 let driver_gauges_mock_matrix_client = MockTimeMatrixClient::new();
1203 let logger = ClientIfaceCountersLogger::new(
1204 test_helper.cobalt_proxy.clone(),
1205 test_helper.monitor_svc_proxy.clone(),
1206 &test_helper.mock_time_matrix_client,
1207 driver_counters_mock_matrix_client.clone(),
1208 driver_gauges_mock_matrix_client.clone(),
1209 );
1210
1211 handle_iface_created(&mut test_helper, &logger);
1213
1214 let mut handle_iface_destroyed_fut = pin!(logger.handle_iface_destroyed(IFACE_ID));
1215 assert_eq!(
1216 test_helper.exec.run_until_stalled(&mut handle_iface_destroyed_fut),
1217 Poll::Ready(())
1218 );
1219
1220 let mut test_fut = pin!(logger.handle_periodic_telemetry());
1221 assert_eq!(test_helper.exec.run_until_stalled(&mut test_fut), Poll::Ready(()));
1222 let telemetry_svc_stream = test_helper.telemetry_svc_stream.as_mut().unwrap();
1223 let mut telemetry_svc_req_fut = pin!(telemetry_svc_stream.try_next());
1224 match test_helper.exec.run_until_stalled(&mut telemetry_svc_req_fut) {
1226 Poll::Ready(Ok(None)) => (),
1227 other => panic!("unexpected variant: {other:?}"),
1228 }
1229 }
1230
1231 fn handle_iface_created<S: InspectSender>(
1232 test_helper: &mut TestHelper,
1233 logger: &ClientIfaceCountersLogger<S>,
1234 ) {
1235 let mut handle_iface_created_fut = pin!(logger.handle_iface_created(IFACE_ID));
1236 assert_eq!(
1237 test_helper.run_and_handle_get_sme_telemetry(&mut handle_iface_created_fut),
1238 Poll::Pending
1239 );
1240 let telemetry_support = fidl_stats::TelemetrySupport::default();
1241 assert_eq!(
1242 test_helper.run_and_respond_query_telemetry_support(
1243 &mut handle_iface_created_fut,
1244 Ok(&telemetry_support)
1245 ),
1246 Poll::Ready(())
1247 );
1248 }
1249}