xref: /aosp_15_r20/tools/netsim/rust/daemon/src/session.rs (revision cf78ab8cffb8fc9207af348f23af247fb04370a6)
1*cf78ab8cSAndroid Build Coastguard Worker // Copyright 2023 Google LLC
2*cf78ab8cSAndroid Build Coastguard Worker //
3*cf78ab8cSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*cf78ab8cSAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*cf78ab8cSAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*cf78ab8cSAndroid Build Coastguard Worker //
7*cf78ab8cSAndroid Build Coastguard Worker //     https://www.apache.org/licenses/LICENSE-2.0
8*cf78ab8cSAndroid Build Coastguard Worker //
9*cf78ab8cSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*cf78ab8cSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*cf78ab8cSAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*cf78ab8cSAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*cf78ab8cSAndroid Build Coastguard Worker // limitations under the License.
14*cf78ab8cSAndroid Build Coastguard Worker 
15*cf78ab8cSAndroid Build Coastguard Worker //! A module to collect and write session stats
16*cf78ab8cSAndroid Build Coastguard Worker 
17*cf78ab8cSAndroid Build Coastguard Worker use crate::devices::devices_handler::get_radio_stats;
18*cf78ab8cSAndroid Build Coastguard Worker use crate::events::{ChipRemoved, DeviceAdded, Event, ShutDown};
19*cf78ab8cSAndroid Build Coastguard Worker use crate::version::get_version;
20*cf78ab8cSAndroid Build Coastguard Worker use anyhow::Context;
21*cf78ab8cSAndroid Build Coastguard Worker use log::error;
22*cf78ab8cSAndroid Build Coastguard Worker use log::info;
23*cf78ab8cSAndroid Build Coastguard Worker use netsim_common::system::netsimd_temp_dir;
24*cf78ab8cSAndroid Build Coastguard Worker use netsim_proto::stats::NetsimStats as ProtoNetsimStats;
25*cf78ab8cSAndroid Build Coastguard Worker use protobuf_json_mapping::print_to_string;
26*cf78ab8cSAndroid Build Coastguard Worker use std::fs::File;
27*cf78ab8cSAndroid Build Coastguard Worker use std::io::Write;
28*cf78ab8cSAndroid Build Coastguard Worker use std::sync::mpsc::Receiver;
29*cf78ab8cSAndroid Build Coastguard Worker use std::sync::Arc;
30*cf78ab8cSAndroid Build Coastguard Worker use std::sync::RwLock;
31*cf78ab8cSAndroid Build Coastguard Worker use std::sync::RwLockWriteGuard;
32*cf78ab8cSAndroid Build Coastguard Worker use std::thread::{Builder, JoinHandle};
33*cf78ab8cSAndroid Build Coastguard Worker use std::time::{Duration, Instant};
34*cf78ab8cSAndroid Build Coastguard Worker 
35*cf78ab8cSAndroid Build Coastguard Worker const WRITE_INTERVAL: Duration = Duration::from_secs(10);
36*cf78ab8cSAndroid Build Coastguard Worker 
37*cf78ab8cSAndroid Build Coastguard Worker pub struct Session {
38*cf78ab8cSAndroid Build Coastguard Worker     // Handle for the session monitor thread
39*cf78ab8cSAndroid Build Coastguard Worker     handle: Option<JoinHandle<String>>,
40*cf78ab8cSAndroid Build Coastguard Worker     // Session info is accessed by multiple threads
41*cf78ab8cSAndroid Build Coastguard Worker     info: Arc<RwLock<SessionInfo>>,
42*cf78ab8cSAndroid Build Coastguard Worker }
43*cf78ab8cSAndroid Build Coastguard Worker 
44*cf78ab8cSAndroid Build Coastguard Worker // Fields accessed by threads
45*cf78ab8cSAndroid Build Coastguard Worker struct SessionInfo {
46*cf78ab8cSAndroid Build Coastguard Worker     stats_proto: ProtoNetsimStats,
47*cf78ab8cSAndroid Build Coastguard Worker     current_device_count: i32,
48*cf78ab8cSAndroid Build Coastguard Worker     session_start: Instant,
49*cf78ab8cSAndroid Build Coastguard Worker     write_json: bool,
50*cf78ab8cSAndroid Build Coastguard Worker }
51*cf78ab8cSAndroid Build Coastguard Worker 
52*cf78ab8cSAndroid Build Coastguard Worker impl Session {
new() -> Self53*cf78ab8cSAndroid Build Coastguard Worker     pub fn new() -> Self {
54*cf78ab8cSAndroid Build Coastguard Worker         Self::new_internal(true)
55*cf78ab8cSAndroid Build Coastguard Worker     }
new_internal(write_json: bool) -> Self56*cf78ab8cSAndroid Build Coastguard Worker     fn new_internal(write_json: bool) -> Self {
57*cf78ab8cSAndroid Build Coastguard Worker         Session {
58*cf78ab8cSAndroid Build Coastguard Worker             handle: None,
59*cf78ab8cSAndroid Build Coastguard Worker             info: Arc::new(RwLock::new(SessionInfo {
60*cf78ab8cSAndroid Build Coastguard Worker                 stats_proto: ProtoNetsimStats {
61*cf78ab8cSAndroid Build Coastguard Worker                     version: Some(get_version()),
62*cf78ab8cSAndroid Build Coastguard Worker                     ..Default::default()
63*cf78ab8cSAndroid Build Coastguard Worker                 },
64*cf78ab8cSAndroid Build Coastguard Worker                 current_device_count: 0,
65*cf78ab8cSAndroid Build Coastguard Worker                 session_start: Instant::now(),
66*cf78ab8cSAndroid Build Coastguard Worker                 write_json,
67*cf78ab8cSAndroid Build Coastguard Worker             })),
68*cf78ab8cSAndroid Build Coastguard Worker         }
69*cf78ab8cSAndroid Build Coastguard Worker     }
70*cf78ab8cSAndroid Build Coastguard Worker 
71*cf78ab8cSAndroid Build Coastguard Worker     // Start the netsim states session.
72*cf78ab8cSAndroid Build Coastguard Worker     //
73*cf78ab8cSAndroid Build Coastguard Worker     // Starts the session monitor thread to handle events and
74*cf78ab8cSAndroid Build Coastguard Worker     // write session stats to json file on event and periodically.
start(&mut self, events_rx: Receiver<Event>) -> &mut Self75*cf78ab8cSAndroid Build Coastguard Worker     pub fn start(&mut self, events_rx: Receiver<Event>) -> &mut Self {
76*cf78ab8cSAndroid Build Coastguard Worker         let info = Arc::clone(&self.info);
77*cf78ab8cSAndroid Build Coastguard Worker 
78*cf78ab8cSAndroid Build Coastguard Worker         // Start up session monitor thread
79*cf78ab8cSAndroid Build Coastguard Worker         self.handle = Some(
80*cf78ab8cSAndroid Build Coastguard Worker             Builder::new()
81*cf78ab8cSAndroid Build Coastguard Worker                 .name("session_monitor".to_string())
82*cf78ab8cSAndroid Build Coastguard Worker                 .spawn(move || {
83*cf78ab8cSAndroid Build Coastguard Worker                     let mut next_instant = Instant::now() + WRITE_INTERVAL;
84*cf78ab8cSAndroid Build Coastguard Worker                     loop {
85*cf78ab8cSAndroid Build Coastguard Worker                         let mut write_stats = true;
86*cf78ab8cSAndroid Build Coastguard Worker                         let this_instant = Instant::now();
87*cf78ab8cSAndroid Build Coastguard Worker                         let timeout = if next_instant > this_instant {
88*cf78ab8cSAndroid Build Coastguard Worker                             next_instant - this_instant
89*cf78ab8cSAndroid Build Coastguard Worker                         } else {
90*cf78ab8cSAndroid Build Coastguard Worker                             Duration::ZERO
91*cf78ab8cSAndroid Build Coastguard Worker                         };
92*cf78ab8cSAndroid Build Coastguard Worker                         let next_event = events_rx.recv_timeout(timeout);
93*cf78ab8cSAndroid Build Coastguard Worker 
94*cf78ab8cSAndroid Build Coastguard Worker                         // Hold the write lock for the duration of this loop iteration
95*cf78ab8cSAndroid Build Coastguard Worker                         let mut lock = info.write().expect("Could not acquire session lock");
96*cf78ab8cSAndroid Build Coastguard Worker                         match next_event {
97*cf78ab8cSAndroid Build Coastguard Worker                             Ok(Event::ShutDown(ShutDown { reason })) => {
98*cf78ab8cSAndroid Build Coastguard Worker                                 // Shutting down, save the session duration and exit
99*cf78ab8cSAndroid Build Coastguard Worker                                 update_session_duration(&mut lock);
100*cf78ab8cSAndroid Build Coastguard Worker                                 return reason;
101*cf78ab8cSAndroid Build Coastguard Worker                             }
102*cf78ab8cSAndroid Build Coastguard Worker 
103*cf78ab8cSAndroid Build Coastguard Worker                             Ok(Event::DeviceRemoved(_)) => {
104*cf78ab8cSAndroid Build Coastguard Worker                                 lock.current_device_count -= 1;
105*cf78ab8cSAndroid Build Coastguard Worker                             }
106*cf78ab8cSAndroid Build Coastguard Worker 
107*cf78ab8cSAndroid Build Coastguard Worker                             Ok(Event::DeviceAdded(DeviceAdded { device_stats, .. })) => {
108*cf78ab8cSAndroid Build Coastguard Worker                                 // update the current_device_count and peak device usage
109*cf78ab8cSAndroid Build Coastguard Worker                                 lock.current_device_count += 1;
110*cf78ab8cSAndroid Build Coastguard Worker                                 let current_device_count = lock.current_device_count;
111*cf78ab8cSAndroid Build Coastguard Worker                                 // incremement total number of devices started
112*cf78ab8cSAndroid Build Coastguard Worker                                 let device_count = lock.stats_proto.device_count();
113*cf78ab8cSAndroid Build Coastguard Worker                                 lock.stats_proto.set_device_count(device_count + 1);
114*cf78ab8cSAndroid Build Coastguard Worker                                 // track the peak device usage
115*cf78ab8cSAndroid Build Coastguard Worker                                 if current_device_count > lock.stats_proto.peak_concurrent_devices()
116*cf78ab8cSAndroid Build Coastguard Worker                                 {
117*cf78ab8cSAndroid Build Coastguard Worker                                     lock.stats_proto
118*cf78ab8cSAndroid Build Coastguard Worker                                         .set_peak_concurrent_devices(current_device_count);
119*cf78ab8cSAndroid Build Coastguard Worker                                 }
120*cf78ab8cSAndroid Build Coastguard Worker                                 // Add added device's stats
121*cf78ab8cSAndroid Build Coastguard Worker                                 lock.stats_proto.device_stats.push(device_stats);
122*cf78ab8cSAndroid Build Coastguard Worker                             }
123*cf78ab8cSAndroid Build Coastguard Worker 
124*cf78ab8cSAndroid Build Coastguard Worker                             Ok(Event::ChipRemoved(ChipRemoved {
125*cf78ab8cSAndroid Build Coastguard Worker                                 radio_stats, device_id, ..
126*cf78ab8cSAndroid Build Coastguard Worker                             })) => {
127*cf78ab8cSAndroid Build Coastguard Worker                                 // Update the radio stats proto when a
128*cf78ab8cSAndroid Build Coastguard Worker                                 // chip is removed.  In the case of
129*cf78ab8cSAndroid Build Coastguard Worker                                 // bluetooth there will be 2 radios,
130*cf78ab8cSAndroid Build Coastguard Worker                                 // otherwise 1
131*cf78ab8cSAndroid Build Coastguard Worker                                 for mut r in radio_stats {
132*cf78ab8cSAndroid Build Coastguard Worker                                     r.set_device_id(device_id.0);
133*cf78ab8cSAndroid Build Coastguard Worker                                     lock.stats_proto.radio_stats.push(r);
134*cf78ab8cSAndroid Build Coastguard Worker                                 }
135*cf78ab8cSAndroid Build Coastguard Worker                             }
136*cf78ab8cSAndroid Build Coastguard Worker 
137*cf78ab8cSAndroid Build Coastguard Worker                             Ok(Event::ChipAdded(_)) => {
138*cf78ab8cSAndroid Build Coastguard Worker                                 // No session stat update required when Chip is added but do proceed to write stats
139*cf78ab8cSAndroid Build Coastguard Worker                             }
140*cf78ab8cSAndroid Build Coastguard Worker                             _ => {
141*cf78ab8cSAndroid Build Coastguard Worker                                 // other events are ignored, check to perform periodic write
142*cf78ab8cSAndroid Build Coastguard Worker                                 if next_instant > Instant::now() {
143*cf78ab8cSAndroid Build Coastguard Worker                                     write_stats = false;
144*cf78ab8cSAndroid Build Coastguard Worker                                 }
145*cf78ab8cSAndroid Build Coastguard Worker                             }
146*cf78ab8cSAndroid Build Coastguard Worker                         }
147*cf78ab8cSAndroid Build Coastguard Worker                         // End of event match - write current stats to json
148*cf78ab8cSAndroid Build Coastguard Worker                         if write_stats {
149*cf78ab8cSAndroid Build Coastguard Worker                             update_session_duration(&mut lock);
150*cf78ab8cSAndroid Build Coastguard Worker                             if lock.write_json {
151*cf78ab8cSAndroid Build Coastguard Worker                                 let current_stats = get_current_stats(lock.stats_proto.clone());
152*cf78ab8cSAndroid Build Coastguard Worker                                 if let Err(err) = write_stats_to_json(current_stats) {
153*cf78ab8cSAndroid Build Coastguard Worker                                     error!("Failed to write stats to json: {err:?}");
154*cf78ab8cSAndroid Build Coastguard Worker                                 }
155*cf78ab8cSAndroid Build Coastguard Worker                             }
156*cf78ab8cSAndroid Build Coastguard Worker                             next_instant = Instant::now() + WRITE_INTERVAL;
157*cf78ab8cSAndroid Build Coastguard Worker                         }
158*cf78ab8cSAndroid Build Coastguard Worker                     }
159*cf78ab8cSAndroid Build Coastguard Worker                 })
160*cf78ab8cSAndroid Build Coastguard Worker                 .expect("failed to start session monitor thread"),
161*cf78ab8cSAndroid Build Coastguard Worker         );
162*cf78ab8cSAndroid Build Coastguard Worker         self
163*cf78ab8cSAndroid Build Coastguard Worker     }
164*cf78ab8cSAndroid Build Coastguard Worker 
165*cf78ab8cSAndroid Build Coastguard Worker     // Stop the netsim stats session.
166*cf78ab8cSAndroid Build Coastguard Worker     //
167*cf78ab8cSAndroid Build Coastguard Worker     // Waits for the session monitor thread to finish and writes
168*cf78ab8cSAndroid Build Coastguard Worker     // the session proto to a json file. Consumes the session.
stop(mut self) -> anyhow::Result<()>169*cf78ab8cSAndroid Build Coastguard Worker     pub fn stop(mut self) -> anyhow::Result<()> {
170*cf78ab8cSAndroid Build Coastguard Worker         if !self.handle.as_ref().expect("no session monitor").is_finished() {
171*cf78ab8cSAndroid Build Coastguard Worker             info!("session monitor active, waiting...");
172*cf78ab8cSAndroid Build Coastguard Worker         }
173*cf78ab8cSAndroid Build Coastguard Worker 
174*cf78ab8cSAndroid Build Coastguard Worker         // Synchronize on session monitor thread
175*cf78ab8cSAndroid Build Coastguard Worker         self.handle.take().map(JoinHandle::join);
176*cf78ab8cSAndroid Build Coastguard Worker 
177*cf78ab8cSAndroid Build Coastguard Worker         let lock = self.info.read().expect("Could not acquire session lock");
178*cf78ab8cSAndroid Build Coastguard Worker         if lock.write_json {
179*cf78ab8cSAndroid Build Coastguard Worker             let current_stats = get_current_stats(lock.stats_proto.clone());
180*cf78ab8cSAndroid Build Coastguard Worker             write_stats_to_json(current_stats)?;
181*cf78ab8cSAndroid Build Coastguard Worker         }
182*cf78ab8cSAndroid Build Coastguard Worker         Ok(())
183*cf78ab8cSAndroid Build Coastguard Worker     }
184*cf78ab8cSAndroid Build Coastguard Worker }
185*cf78ab8cSAndroid Build Coastguard Worker 
186*cf78ab8cSAndroid Build Coastguard Worker /// Update session duration
update_session_duration(session_lock: &mut RwLockWriteGuard<'_, SessionInfo>)187*cf78ab8cSAndroid Build Coastguard Worker fn update_session_duration(session_lock: &mut RwLockWriteGuard<'_, SessionInfo>) {
188*cf78ab8cSAndroid Build Coastguard Worker     let duration_secs = session_lock.session_start.elapsed().as_secs();
189*cf78ab8cSAndroid Build Coastguard Worker     session_lock.stats_proto.set_duration_secs(duration_secs);
190*cf78ab8cSAndroid Build Coastguard Worker }
191*cf78ab8cSAndroid Build Coastguard Worker 
192*cf78ab8cSAndroid Build Coastguard Worker /// Construct current radio stats
get_current_stats(mut current_stats: ProtoNetsimStats) -> ProtoNetsimStats193*cf78ab8cSAndroid Build Coastguard Worker fn get_current_stats(mut current_stats: ProtoNetsimStats) -> ProtoNetsimStats {
194*cf78ab8cSAndroid Build Coastguard Worker     current_stats.radio_stats.extend(get_radio_stats());
195*cf78ab8cSAndroid Build Coastguard Worker     current_stats
196*cf78ab8cSAndroid Build Coastguard Worker }
197*cf78ab8cSAndroid Build Coastguard Worker 
198*cf78ab8cSAndroid Build Coastguard Worker /// Write netsim stats to json file
write_stats_to_json(stats_proto: ProtoNetsimStats) -> anyhow::Result<()>199*cf78ab8cSAndroid Build Coastguard Worker fn write_stats_to_json(stats_proto: ProtoNetsimStats) -> anyhow::Result<()> {
200*cf78ab8cSAndroid Build Coastguard Worker     let filename = netsimd_temp_dir().join("netsim_session_stats.json");
201*cf78ab8cSAndroid Build Coastguard Worker     let mut file = File::create(filename)?;
202*cf78ab8cSAndroid Build Coastguard Worker     let json = print_to_string(&stats_proto)?;
203*cf78ab8cSAndroid Build Coastguard Worker     file.write(json.as_bytes()).context("Unable to write json session stats")?;
204*cf78ab8cSAndroid Build Coastguard Worker     file.flush()?;
205*cf78ab8cSAndroid Build Coastguard Worker     Ok(())
206*cf78ab8cSAndroid Build Coastguard Worker }
207*cf78ab8cSAndroid Build Coastguard Worker 
208*cf78ab8cSAndroid Build Coastguard Worker #[cfg(test)]
209*cf78ab8cSAndroid Build Coastguard Worker mod tests {
210*cf78ab8cSAndroid Build Coastguard Worker     use super::*;
211*cf78ab8cSAndroid Build Coastguard Worker     use crate::devices::chip::ChipIdentifier;
212*cf78ab8cSAndroid Build Coastguard Worker     use crate::devices::device::DeviceIdentifier;
213*cf78ab8cSAndroid Build Coastguard Worker     use crate::events;
214*cf78ab8cSAndroid Build Coastguard Worker     use crate::events::{ChipAdded, ChipRemoved, DeviceRemoved, Event, Events, ShutDown};
215*cf78ab8cSAndroid Build Coastguard Worker     use netsim_proto::stats::{
216*cf78ab8cSAndroid Build Coastguard Worker         NetsimDeviceStats as ProtoDeviceStats, NetsimRadioStats as ProtoRadioStats,
217*cf78ab8cSAndroid Build Coastguard Worker     };
218*cf78ab8cSAndroid Build Coastguard Worker     use std::sync::Mutex;
219*cf78ab8cSAndroid Build Coastguard Worker 
220*cf78ab8cSAndroid Build Coastguard Worker     const TEST_DEVICE_KIND: &str = "TEST_DEVICE";
221*cf78ab8cSAndroid Build Coastguard Worker 
222*cf78ab8cSAndroid Build Coastguard Worker     #[test]
test_new()223*cf78ab8cSAndroid Build Coastguard Worker     fn test_new() {
224*cf78ab8cSAndroid Build Coastguard Worker         let session: Session = Session::new();
225*cf78ab8cSAndroid Build Coastguard Worker         assert!(session.handle.is_none());
226*cf78ab8cSAndroid Build Coastguard Worker         let lock = session.info.read().expect("Could not acquire session lock");
227*cf78ab8cSAndroid Build Coastguard Worker         assert_eq!(lock.current_device_count, 0);
228*cf78ab8cSAndroid Build Coastguard Worker         assert!(matches!(
229*cf78ab8cSAndroid Build Coastguard Worker             lock.stats_proto,
230*cf78ab8cSAndroid Build Coastguard Worker             ProtoNetsimStats { version: Some(_), duration_secs: None, .. }
231*cf78ab8cSAndroid Build Coastguard Worker         ));
232*cf78ab8cSAndroid Build Coastguard Worker         assert_eq!(lock.stats_proto.version.clone().unwrap(), get_version().clone());
233*cf78ab8cSAndroid Build Coastguard Worker         assert_eq!(lock.stats_proto.radio_stats.len(), 0);
234*cf78ab8cSAndroid Build Coastguard Worker     }
235*cf78ab8cSAndroid Build Coastguard Worker 
setup_session_start_test() -> (Session, Arc<Mutex<Events>>)236*cf78ab8cSAndroid Build Coastguard Worker     fn setup_session_start_test() -> (Session, Arc<Mutex<Events>>) {
237*cf78ab8cSAndroid Build Coastguard Worker         let mut session = Session::new_internal(false);
238*cf78ab8cSAndroid Build Coastguard Worker         let mut events = events::test::new();
239*cf78ab8cSAndroid Build Coastguard Worker         let events_rx = events::test::subscribe(&mut events);
240*cf78ab8cSAndroid Build Coastguard Worker         session.start(events_rx);
241*cf78ab8cSAndroid Build Coastguard Worker         (session, events)
242*cf78ab8cSAndroid Build Coastguard Worker     }
243*cf78ab8cSAndroid Build Coastguard Worker 
get_stats_proto(session: &Session) -> ProtoNetsimStats244*cf78ab8cSAndroid Build Coastguard Worker     fn get_stats_proto(session: &Session) -> ProtoNetsimStats {
245*cf78ab8cSAndroid Build Coastguard Worker         session.info.read().expect("Could not acquire session lock").stats_proto.clone()
246*cf78ab8cSAndroid Build Coastguard Worker     }
247*cf78ab8cSAndroid Build Coastguard Worker 
248*cf78ab8cSAndroid Build Coastguard Worker     #[test]
test_start_and_shutdown()249*cf78ab8cSAndroid Build Coastguard Worker     fn test_start_and_shutdown() {
250*cf78ab8cSAndroid Build Coastguard Worker         let (mut session, mut events) = setup_session_start_test();
251*cf78ab8cSAndroid Build Coastguard Worker 
252*cf78ab8cSAndroid Build Coastguard Worker         // we want to be able to check the session time gets incremented
253*cf78ab8cSAndroid Build Coastguard Worker         std::thread::sleep(std::time::Duration::from_secs(1));
254*cf78ab8cSAndroid Build Coastguard Worker 
255*cf78ab8cSAndroid Build Coastguard Worker         // publish the shutdown afterwards to cause the separate thread to stop
256*cf78ab8cSAndroid Build Coastguard Worker         events::test::publish(
257*cf78ab8cSAndroid Build Coastguard Worker             &mut events,
258*cf78ab8cSAndroid Build Coastguard Worker             Event::ShutDown(ShutDown { reason: "Stop the session".to_string() }),
259*cf78ab8cSAndroid Build Coastguard Worker         );
260*cf78ab8cSAndroid Build Coastguard Worker 
261*cf78ab8cSAndroid Build Coastguard Worker         // join the handle
262*cf78ab8cSAndroid Build Coastguard Worker         session.handle.take().map(JoinHandle::join);
263*cf78ab8cSAndroid Build Coastguard Worker 
264*cf78ab8cSAndroid Build Coastguard Worker         let stats_proto = get_stats_proto(&session);
265*cf78ab8cSAndroid Build Coastguard Worker 
266*cf78ab8cSAndroid Build Coastguard Worker         // check device counts are missing if no device add/remove events occurred
267*cf78ab8cSAndroid Build Coastguard Worker         assert!(stats_proto.device_count.is_none());
268*cf78ab8cSAndroid Build Coastguard Worker 
269*cf78ab8cSAndroid Build Coastguard Worker         assert!(stats_proto.peak_concurrent_devices.is_none());
270*cf78ab8cSAndroid Build Coastguard Worker 
271*cf78ab8cSAndroid Build Coastguard Worker         // check the session time is > 0
272*cf78ab8cSAndroid Build Coastguard Worker         assert!(stats_proto.duration_secs.unwrap() > 0u64);
273*cf78ab8cSAndroid Build Coastguard Worker     }
274*cf78ab8cSAndroid Build Coastguard Worker 
275*cf78ab8cSAndroid Build Coastguard Worker     #[test]
test_start_and_stop()276*cf78ab8cSAndroid Build Coastguard Worker     fn test_start_and_stop() {
277*cf78ab8cSAndroid Build Coastguard Worker         let (session, mut events) = setup_session_start_test();
278*cf78ab8cSAndroid Build Coastguard Worker 
279*cf78ab8cSAndroid Build Coastguard Worker         // we want to be able to check the session time gets incremented
280*cf78ab8cSAndroid Build Coastguard Worker         std::thread::sleep(std::time::Duration::from_secs(1));
281*cf78ab8cSAndroid Build Coastguard Worker 
282*cf78ab8cSAndroid Build Coastguard Worker         // publish the shutdown which is required when using `session.stop()`
283*cf78ab8cSAndroid Build Coastguard Worker         events::test::publish(
284*cf78ab8cSAndroid Build Coastguard Worker             &mut events,
285*cf78ab8cSAndroid Build Coastguard Worker             Event::ShutDown(ShutDown { reason: "Stop the session".to_string() }),
286*cf78ab8cSAndroid Build Coastguard Worker         );
287*cf78ab8cSAndroid Build Coastguard Worker 
288*cf78ab8cSAndroid Build Coastguard Worker         // should not panic or deadlock
289*cf78ab8cSAndroid Build Coastguard Worker         session.stop().unwrap();
290*cf78ab8cSAndroid Build Coastguard Worker     }
291*cf78ab8cSAndroid Build Coastguard Worker 
292*cf78ab8cSAndroid Build Coastguard Worker     // Tests for session.rs involving devices
293*cf78ab8cSAndroid Build Coastguard Worker     #[test]
test_start_and_device_add()294*cf78ab8cSAndroid Build Coastguard Worker     fn test_start_and_device_add() {
295*cf78ab8cSAndroid Build Coastguard Worker         let (mut session, mut events) = setup_session_start_test();
296*cf78ab8cSAndroid Build Coastguard Worker 
297*cf78ab8cSAndroid Build Coastguard Worker         // we want to be able to check the session time gets incremented
298*cf78ab8cSAndroid Build Coastguard Worker         std::thread::sleep(std::time::Duration::from_secs(1));
299*cf78ab8cSAndroid Build Coastguard Worker 
300*cf78ab8cSAndroid Build Coastguard Worker         // Create a device, publishing DeviceAdded event
301*cf78ab8cSAndroid Build Coastguard Worker         events::test::publish(
302*cf78ab8cSAndroid Build Coastguard Worker             &mut events,
303*cf78ab8cSAndroid Build Coastguard Worker             Event::DeviceAdded(DeviceAdded {
304*cf78ab8cSAndroid Build Coastguard Worker                 builtin: false,
305*cf78ab8cSAndroid Build Coastguard Worker                 id: DeviceIdentifier(1),
306*cf78ab8cSAndroid Build Coastguard Worker                 device_stats: ProtoDeviceStats {
307*cf78ab8cSAndroid Build Coastguard Worker                     kind: Some(TEST_DEVICE_KIND.to_string()),
308*cf78ab8cSAndroid Build Coastguard Worker                     ..Default::default()
309*cf78ab8cSAndroid Build Coastguard Worker                 },
310*cf78ab8cSAndroid Build Coastguard Worker                 ..Default::default()
311*cf78ab8cSAndroid Build Coastguard Worker             }),
312*cf78ab8cSAndroid Build Coastguard Worker         );
313*cf78ab8cSAndroid Build Coastguard Worker 
314*cf78ab8cSAndroid Build Coastguard Worker         // publish the shutdown afterwards to cause the separate thread to stop
315*cf78ab8cSAndroid Build Coastguard Worker         events::test::publish(
316*cf78ab8cSAndroid Build Coastguard Worker             &mut events,
317*cf78ab8cSAndroid Build Coastguard Worker             Event::ShutDown(ShutDown { reason: "Stop the session".to_string() }),
318*cf78ab8cSAndroid Build Coastguard Worker         );
319*cf78ab8cSAndroid Build Coastguard Worker 
320*cf78ab8cSAndroid Build Coastguard Worker         // join the handle
321*cf78ab8cSAndroid Build Coastguard Worker         session.handle.take().map(JoinHandle::join);
322*cf78ab8cSAndroid Build Coastguard Worker 
323*cf78ab8cSAndroid Build Coastguard Worker         // check device counts were incremented
324*cf78ab8cSAndroid Build Coastguard Worker         assert_eq!(
325*cf78ab8cSAndroid Build Coastguard Worker             session.info.read().expect("Could not acquire session lock").current_device_count,
326*cf78ab8cSAndroid Build Coastguard Worker             1i32
327*cf78ab8cSAndroid Build Coastguard Worker         );
328*cf78ab8cSAndroid Build Coastguard Worker         let stats_proto = get_stats_proto(&session);
329*cf78ab8cSAndroid Build Coastguard Worker 
330*cf78ab8cSAndroid Build Coastguard Worker         assert_eq!(stats_proto.device_count.unwrap(), 1i32);
331*cf78ab8cSAndroid Build Coastguard Worker         assert_eq!(stats_proto.peak_concurrent_devices.unwrap(), 1i32);
332*cf78ab8cSAndroid Build Coastguard Worker         // check the session time is > 0
333*cf78ab8cSAndroid Build Coastguard Worker         assert!(stats_proto.duration_secs.unwrap() > 0u64);
334*cf78ab8cSAndroid Build Coastguard Worker         // check the device stats is populated
335*cf78ab8cSAndroid Build Coastguard Worker         assert_eq!(stats_proto.device_stats.len(), 1);
336*cf78ab8cSAndroid Build Coastguard Worker     }
337*cf78ab8cSAndroid Build Coastguard Worker 
338*cf78ab8cSAndroid Build Coastguard Worker     #[test]
test_start_and_device_add_and_remove()339*cf78ab8cSAndroid Build Coastguard Worker     fn test_start_and_device_add_and_remove() {
340*cf78ab8cSAndroid Build Coastguard Worker         let (mut session, mut events) = setup_session_start_test();
341*cf78ab8cSAndroid Build Coastguard Worker 
342*cf78ab8cSAndroid Build Coastguard Worker         // we want to be able to check the session time gets incremented
343*cf78ab8cSAndroid Build Coastguard Worker         std::thread::sleep(std::time::Duration::from_secs(1));
344*cf78ab8cSAndroid Build Coastguard Worker 
345*cf78ab8cSAndroid Build Coastguard Worker         // Create a device, publishing DeviceAdded event
346*cf78ab8cSAndroid Build Coastguard Worker         events::test::publish(
347*cf78ab8cSAndroid Build Coastguard Worker             &mut events,
348*cf78ab8cSAndroid Build Coastguard Worker             Event::DeviceAdded(DeviceAdded {
349*cf78ab8cSAndroid Build Coastguard Worker                 builtin: false,
350*cf78ab8cSAndroid Build Coastguard Worker                 id: DeviceIdentifier(1),
351*cf78ab8cSAndroid Build Coastguard Worker                 device_stats: ProtoDeviceStats {
352*cf78ab8cSAndroid Build Coastguard Worker                     kind: Some(TEST_DEVICE_KIND.to_string()),
353*cf78ab8cSAndroid Build Coastguard Worker                     ..Default::default()
354*cf78ab8cSAndroid Build Coastguard Worker                 },
355*cf78ab8cSAndroid Build Coastguard Worker                 ..Default::default()
356*cf78ab8cSAndroid Build Coastguard Worker             }),
357*cf78ab8cSAndroid Build Coastguard Worker         );
358*cf78ab8cSAndroid Build Coastguard Worker 
359*cf78ab8cSAndroid Build Coastguard Worker         events::test::publish(
360*cf78ab8cSAndroid Build Coastguard Worker             &mut events,
361*cf78ab8cSAndroid Build Coastguard Worker             Event::DeviceRemoved(DeviceRemoved {
362*cf78ab8cSAndroid Build Coastguard Worker                 builtin: false,
363*cf78ab8cSAndroid Build Coastguard Worker                 id: DeviceIdentifier(1),
364*cf78ab8cSAndroid Build Coastguard Worker                 ..Default::default()
365*cf78ab8cSAndroid Build Coastguard Worker             }),
366*cf78ab8cSAndroid Build Coastguard Worker         );
367*cf78ab8cSAndroid Build Coastguard Worker 
368*cf78ab8cSAndroid Build Coastguard Worker         // Create another device, publishing DeviceAdded event
369*cf78ab8cSAndroid Build Coastguard Worker         events::test::publish(
370*cf78ab8cSAndroid Build Coastguard Worker             &mut events,
371*cf78ab8cSAndroid Build Coastguard Worker             Event::DeviceAdded(DeviceAdded {
372*cf78ab8cSAndroid Build Coastguard Worker                 builtin: false,
373*cf78ab8cSAndroid Build Coastguard Worker                 id: DeviceIdentifier(2),
374*cf78ab8cSAndroid Build Coastguard Worker                 device_stats: ProtoDeviceStats {
375*cf78ab8cSAndroid Build Coastguard Worker                     kind: Some(TEST_DEVICE_KIND.to_string()),
376*cf78ab8cSAndroid Build Coastguard Worker                     ..Default::default()
377*cf78ab8cSAndroid Build Coastguard Worker                 },
378*cf78ab8cSAndroid Build Coastguard Worker                 ..Default::default()
379*cf78ab8cSAndroid Build Coastguard Worker             }),
380*cf78ab8cSAndroid Build Coastguard Worker         );
381*cf78ab8cSAndroid Build Coastguard Worker 
382*cf78ab8cSAndroid Build Coastguard Worker         events::test::publish(
383*cf78ab8cSAndroid Build Coastguard Worker             &mut events,
384*cf78ab8cSAndroid Build Coastguard Worker             Event::DeviceRemoved(DeviceRemoved {
385*cf78ab8cSAndroid Build Coastguard Worker                 builtin: false,
386*cf78ab8cSAndroid Build Coastguard Worker                 id: DeviceIdentifier(2),
387*cf78ab8cSAndroid Build Coastguard Worker                 ..Default::default()
388*cf78ab8cSAndroid Build Coastguard Worker             }),
389*cf78ab8cSAndroid Build Coastguard Worker         );
390*cf78ab8cSAndroid Build Coastguard Worker 
391*cf78ab8cSAndroid Build Coastguard Worker         // publish the shutdown afterwards to cause the separate thread to stop
392*cf78ab8cSAndroid Build Coastguard Worker         events::test::publish(
393*cf78ab8cSAndroid Build Coastguard Worker             &mut events,
394*cf78ab8cSAndroid Build Coastguard Worker             Event::ShutDown(ShutDown { reason: "Stop the session".to_string() }),
395*cf78ab8cSAndroid Build Coastguard Worker         );
396*cf78ab8cSAndroid Build Coastguard Worker 
397*cf78ab8cSAndroid Build Coastguard Worker         // join the handle
398*cf78ab8cSAndroid Build Coastguard Worker         session.handle.take().map(JoinHandle::join);
399*cf78ab8cSAndroid Build Coastguard Worker 
400*cf78ab8cSAndroid Build Coastguard Worker         // check the different device counts were incremented as expected
401*cf78ab8cSAndroid Build Coastguard Worker         assert_eq!(
402*cf78ab8cSAndroid Build Coastguard Worker             session.info.read().expect("Could not acquire session lock").current_device_count,
403*cf78ab8cSAndroid Build Coastguard Worker             0i32
404*cf78ab8cSAndroid Build Coastguard Worker         );
405*cf78ab8cSAndroid Build Coastguard Worker         let stats_proto = get_stats_proto(&session);
406*cf78ab8cSAndroid Build Coastguard Worker         assert_eq!(stats_proto.device_count.unwrap(), 2i32);
407*cf78ab8cSAndroid Build Coastguard Worker         assert_eq!(stats_proto.peak_concurrent_devices.unwrap(), 1i32);
408*cf78ab8cSAndroid Build Coastguard Worker 
409*cf78ab8cSAndroid Build Coastguard Worker         // check the session time is > 0
410*cf78ab8cSAndroid Build Coastguard Worker         assert!(stats_proto.duration_secs.unwrap() > 0u64);
411*cf78ab8cSAndroid Build Coastguard Worker         // check the device stats are populated
412*cf78ab8cSAndroid Build Coastguard Worker         assert_eq!(stats_proto.device_stats.len(), 2);
413*cf78ab8cSAndroid Build Coastguard Worker     }
414*cf78ab8cSAndroid Build Coastguard Worker 
415*cf78ab8cSAndroid Build Coastguard Worker     #[test]
test_start_and_chip_add_and_remove()416*cf78ab8cSAndroid Build Coastguard Worker     fn test_start_and_chip_add_and_remove() {
417*cf78ab8cSAndroid Build Coastguard Worker         let (mut session, mut events) = setup_session_start_test();
418*cf78ab8cSAndroid Build Coastguard Worker 
419*cf78ab8cSAndroid Build Coastguard Worker         // we want to be able to check the session time gets incremented
420*cf78ab8cSAndroid Build Coastguard Worker         std::thread::sleep(std::time::Duration::from_secs(1));
421*cf78ab8cSAndroid Build Coastguard Worker 
422*cf78ab8cSAndroid Build Coastguard Worker         events::test::publish(
423*cf78ab8cSAndroid Build Coastguard Worker             &mut events,
424*cf78ab8cSAndroid Build Coastguard Worker             Event::ChipAdded(ChipAdded {
425*cf78ab8cSAndroid Build Coastguard Worker                 builtin: false,
426*cf78ab8cSAndroid Build Coastguard Worker                 chip_id: ChipIdentifier(0),
427*cf78ab8cSAndroid Build Coastguard Worker                 ..Default::default()
428*cf78ab8cSAndroid Build Coastguard Worker             }),
429*cf78ab8cSAndroid Build Coastguard Worker         );
430*cf78ab8cSAndroid Build Coastguard Worker 
431*cf78ab8cSAndroid Build Coastguard Worker         std::thread::sleep(std::time::Duration::from_secs(1));
432*cf78ab8cSAndroid Build Coastguard Worker 
433*cf78ab8cSAndroid Build Coastguard Worker         // no radio stats until after we remove the chip
434*cf78ab8cSAndroid Build Coastguard Worker         assert_eq!(get_stats_proto(&session).radio_stats.len(), 0usize);
435*cf78ab8cSAndroid Build Coastguard Worker 
436*cf78ab8cSAndroid Build Coastguard Worker         events::test::publish(
437*cf78ab8cSAndroid Build Coastguard Worker             &mut events,
438*cf78ab8cSAndroid Build Coastguard Worker             Event::ChipRemoved(ChipRemoved {
439*cf78ab8cSAndroid Build Coastguard Worker                 chip_id: ChipIdentifier(0),
440*cf78ab8cSAndroid Build Coastguard Worker                 radio_stats: vec![ProtoRadioStats { ..Default::default() }],
441*cf78ab8cSAndroid Build Coastguard Worker                 ..Default::default()
442*cf78ab8cSAndroid Build Coastguard Worker             }),
443*cf78ab8cSAndroid Build Coastguard Worker         );
444*cf78ab8cSAndroid Build Coastguard Worker 
445*cf78ab8cSAndroid Build Coastguard Worker         // publish the shutdown afterwards to cause the separate thread to stop
446*cf78ab8cSAndroid Build Coastguard Worker         events::test::publish(
447*cf78ab8cSAndroid Build Coastguard Worker             &mut events,
448*cf78ab8cSAndroid Build Coastguard Worker             Event::ShutDown(ShutDown { reason: "Stop the session".to_string() }),
449*cf78ab8cSAndroid Build Coastguard Worker         );
450*cf78ab8cSAndroid Build Coastguard Worker 
451*cf78ab8cSAndroid Build Coastguard Worker         // join the handle
452*cf78ab8cSAndroid Build Coastguard Worker         session.handle.take().map(JoinHandle::join);
453*cf78ab8cSAndroid Build Coastguard Worker 
454*cf78ab8cSAndroid Build Coastguard Worker         // check devices were not incremented (here we only added and removed the chip)
455*cf78ab8cSAndroid Build Coastguard Worker         assert_eq!(
456*cf78ab8cSAndroid Build Coastguard Worker             session.info.read().expect("Could not acquire session lock").current_device_count,
457*cf78ab8cSAndroid Build Coastguard Worker             0i32
458*cf78ab8cSAndroid Build Coastguard Worker         );
459*cf78ab8cSAndroid Build Coastguard Worker 
460*cf78ab8cSAndroid Build Coastguard Worker         let stats_proto = get_stats_proto(&session);
461*cf78ab8cSAndroid Build Coastguard Worker         assert_eq!(stats_proto.radio_stats.len(), 1usize);
462*cf78ab8cSAndroid Build Coastguard Worker 
463*cf78ab8cSAndroid Build Coastguard Worker         // these will still be none since no device level events were processed
464*cf78ab8cSAndroid Build Coastguard Worker         assert!(stats_proto.device_count.is_none());
465*cf78ab8cSAndroid Build Coastguard Worker 
466*cf78ab8cSAndroid Build Coastguard Worker         assert!(stats_proto.peak_concurrent_devices.is_none());
467*cf78ab8cSAndroid Build Coastguard Worker 
468*cf78ab8cSAndroid Build Coastguard Worker         // check the session time is > 0
469*cf78ab8cSAndroid Build Coastguard Worker         assert!(stats_proto.duration_secs.unwrap() > 0u64);
470*cf78ab8cSAndroid Build Coastguard Worker     }
471*cf78ab8cSAndroid Build Coastguard Worker }
472