1 // Copyright 2023 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 // devices_handler.rs
16 //
17 // Provides the API for the frontend and backend to interact with devices.
18 //
19 // The DeviceManager struct is a singleton for the devices collection.
20 //
21 // Additional functions are
22 // -- inactivity instant
23 // -- vending device identifiers
24 
25 use super::chip;
26 use super::chip::ChipIdentifier;
27 use super::device::DeviceIdentifier;
28 use crate::devices::device::AddChipResult;
29 use crate::devices::device::Device;
30 use crate::events;
31 use crate::events::{
32     ChipAdded, ChipRemoved, DeviceAdded, DevicePatched, DeviceRemoved, Event, Events, ShutDown,
33 };
34 use crate::ffi::ffi_response_writable::CxxServerResponseWriter;
35 use crate::ffi::CxxServerResponseWriterWrapper;
36 use crate::http_server::server_response::ResponseWritable;
37 use crate::wireless;
38 use cxx::{CxxString, CxxVector};
39 use http::Request;
40 use http::Version;
41 use lazy_static::lazy_static;
42 use log::{info, warn};
43 use netsim_proto::common::ChipKind as ProtoChipKind;
44 use netsim_proto::configuration::Controller;
45 use netsim_proto::frontend::CreateDeviceRequest;
46 use netsim_proto::frontend::CreateDeviceResponse;
47 use netsim_proto::frontend::DeleteChipRequest;
48 use netsim_proto::frontend::ListDeviceResponse;
49 use netsim_proto::frontend::PatchDeviceRequest;
50 use netsim_proto::frontend::SubscribeDeviceRequest;
51 use netsim_proto::model::chip_create::Chip as ProtoBuiltin;
52 use netsim_proto::model::Position as ProtoPosition;
53 use netsim_proto::stats::NetsimRadioStats;
54 use protobuf::well_known_types::timestamp::Timestamp;
55 use protobuf::Message;
56 use protobuf::MessageField;
57 use protobuf_json_mapping::merge_from_str;
58 use protobuf_json_mapping::print_to_string;
59 use protobuf_json_mapping::print_to_string_with_options;
60 use protobuf_json_mapping::PrintOptions;
61 use std::collections::btree_map::Entry;
62 use std::collections::BTreeMap;
63 use std::pin::Pin;
64 use std::sync::atomic::{AtomicU32, Ordering};
65 use std::sync::mpsc::Receiver;
66 use std::sync::Arc;
67 use std::sync::Mutex;
68 use std::sync::RwLock;
69 use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
70 
71 // The amount of seconds netsimd will wait until the first device has attached.
72 static IDLE_SECS_FOR_SHUTDOWN: u64 = 15;
73 
74 const INITIAL_DEVICE_ID: u32 = 1;
75 const JSON_PRINT_OPTION: PrintOptions = PrintOptions {
76     enum_values_int: false,
77     proto_field_name: false,
78     always_output_default_values: true,
79     _future_options: (),
80 };
81 
82 lazy_static! {
83     static ref DEVICE_MANAGER: Arc<DeviceManager> = Arc::new(DeviceManager::new());
84 }
85 
get_manager() -> Arc<DeviceManager>86 fn get_manager() -> Arc<DeviceManager> {
87     Arc::clone(&DEVICE_MANAGER)
88 }
89 
90 // TODO: last_modified atomic
91 /// The Device resource is a singleton that manages all devices.
92 struct DeviceManager {
93     // BTreeMap allows ListDevice to output devices in order of identifiers.
94     devices: RwLock<BTreeMap<DeviceIdentifier, Device>>,
95     ids: AtomicU32,
96     last_modified: RwLock<Duration>,
97 }
98 
99 impl DeviceManager {
new() -> Self100     fn new() -> Self {
101         DeviceManager {
102             devices: RwLock::new(BTreeMap::new()),
103             ids: AtomicU32::new(INITIAL_DEVICE_ID),
104             last_modified: RwLock::new(
105                 SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards"),
106             ),
107         }
108     }
109 
next_id(&self) -> DeviceIdentifier110     fn next_id(&self) -> DeviceIdentifier {
111         DeviceIdentifier(self.ids.fetch_add(1, Ordering::SeqCst))
112     }
113 
update_timestamp(&self)114     fn update_timestamp(&self) {
115         *self.last_modified.write().unwrap() =
116             SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards");
117     }
118 
119     /// Get or create a device.
120     /// Returns a (device_id, device_name) pair.
get_or_create_device( &self, guid: Option<&str>, name: Option<&str>, builtin: bool, ) -> (DeviceIdentifier, String)121     fn get_or_create_device(
122         &self,
123         guid: Option<&str>,
124         name: Option<&str>,
125         builtin: bool,
126     ) -> (DeviceIdentifier, String) {
127         // Check if a device with the same guid already exists and if so, return it
128         if let Some(guid) = guid {
129             if let Some(existing_device) =
130                 self.devices.read().unwrap().values().find(|d| d.guid == *guid)
131             {
132                 if existing_device.builtin != builtin {
133                     warn!("builtin mismatch for device {} during add_chip", existing_device.name);
134                 }
135                 return (existing_device.id, existing_device.name.clone());
136             }
137         }
138 
139         // A new device needs to be created and inserted
140         let id = self.next_id();
141         let default = format!("device-{}", id);
142         let name = name.unwrap_or(&default);
143         self.devices.write().unwrap().insert(
144             id,
145             Device::new(id, String::from(guid.unwrap_or(&default)), String::from(name), builtin),
146         );
147         // Update last modified timestamp for devices
148         self.update_timestamp();
149         events::publish(Event::DeviceAdded(DeviceAdded { id, name: name.to_string(), builtin }));
150 
151         (id, String::from(name))
152     }
153 }
154 
155 /// Returns a Result<AddChipResult, String> after adding chip to resource.
156 /// add_chip is called by the transport layer when a new chip is attached.
157 ///
158 /// The guid is a transport layer identifier for the device (host:port)
159 /// that is adding the chip.
160 ///
161 /// TODO: Replace the parameter of add_chip with a single protobuf
add_chip( device_guid: &str, device_name: &str, chip_create_params: &chip::CreateParams, wireless_create_params: &wireless::CreateParam, ) -> Result<AddChipResult, String>162 pub fn add_chip(
163     device_guid: &str,
164     device_name: &str,
165     chip_create_params: &chip::CreateParams,
166     wireless_create_params: &wireless::CreateParam,
167 ) -> Result<AddChipResult, String> {
168     let chip_kind = chip_create_params.kind;
169     let manager = get_manager();
170     let (device_id, _) = manager.get_or_create_device(
171         Some(device_guid),
172         Some(device_name),
173         chip_kind == ProtoChipKind::BLUETOOTH_BEACON,
174     );
175 
176     // Create
177     let chip_id = chip::next_id();
178     let wireless_adaptor = wireless::new(wireless_create_params, chip_id);
179 
180     // This is infrequent, so we can afford to do another lookup for the device.
181     let _ = manager
182         .devices
183         .write()
184         .unwrap()
185         .get_mut(&device_id)
186         .ok_or(format!("Device not found for device_id: {}", device_id))?
187         .add_chip(chip_create_params, chip_id, wireless_adaptor);
188 
189     // Update last modified timestamp for devices
190     manager.update_timestamp();
191 
192     // Update Capture resource
193     events::publish(Event::ChipAdded(ChipAdded {
194         chip_id,
195         chip_kind,
196         device_name: device_name.to_string(),
197         builtin: chip_kind == ProtoChipKind::BLUETOOTH_BEACON,
198     }));
199     Ok(AddChipResult { device_id, chip_id })
200 }
201 
202 /// AddChipResult for C++ to handle
203 pub struct AddChipResultCxx {
204     device_id: u32,
205     chip_id: u32,
206     is_error: bool,
207 }
208 
209 impl AddChipResultCxx {
get_device_id(&self) -> u32210     pub fn get_device_id(&self) -> u32 {
211         self.device_id
212     }
213 
get_chip_id(&self) -> u32214     pub fn get_chip_id(&self) -> u32 {
215         self.chip_id
216     }
217 
is_error(&self) -> bool218     pub fn is_error(&self) -> bool {
219         self.is_error
220     }
221 }
222 
223 /// An AddChip function for Rust Device API.
224 /// The backend gRPC code will be invoking this method.
225 #[allow(clippy::too_many_arguments)]
add_chip_cxx( device_guid: &str, device_name: &str, chip_kind: &CxxString, chip_address: &str, chip_name: &str, chip_manufacturer: &str, chip_product_name: &str, bt_properties: &CxxVector<u8>, ) -> Box<AddChipResultCxx>226 pub fn add_chip_cxx(
227     device_guid: &str,
228     device_name: &str,
229     chip_kind: &CxxString,
230     chip_address: &str,
231     chip_name: &str,
232     chip_manufacturer: &str,
233     chip_product_name: &str,
234     bt_properties: &CxxVector<u8>,
235 ) -> Box<AddChipResultCxx> {
236     let bt_properties_proto = Controller::parse_from_bytes(bt_properties.as_slice());
237     #[cfg(not(test))]
238     let (chip_kind_enum, wireless_create_param) = match chip_kind.to_string().as_str() {
239         "BLUETOOTH" => (
240             ProtoChipKind::BLUETOOTH,
241             wireless::CreateParam::Bluetooth(wireless::bluetooth::CreateParams {
242                 address: chip_address.to_string(),
243                 bt_properties: bt_properties_proto
244                     .as_ref()
245                     .map_or(None, |p| Some(MessageField::some(p.clone()))),
246             }),
247         ),
248         "WIFI" => {
249             (ProtoChipKind::WIFI, wireless::CreateParam::Wifi(wireless::wifi::CreateParams {}))
250         }
251         "UWB" => (
252             ProtoChipKind::UWB,
253             wireless::CreateParam::Uwb(wireless::uwb::CreateParams {
254                 address: chip_address.to_string(),
255             }),
256         ),
257         _ => {
258             return Box::new(AddChipResultCxx {
259                 device_id: u32::MAX,
260                 chip_id: u32::MAX,
261                 is_error: true,
262             })
263         }
264     };
265     #[cfg(test)]
266     let (chip_kind_enum, wireless_create_param) = match chip_kind.to_string().as_str() {
267         "BLUETOOTH" => (
268             ProtoChipKind::BLUETOOTH,
269             wireless::CreateParam::Mock(wireless::mocked::CreateParams {
270                 chip_kind: ProtoChipKind::BLUETOOTH,
271             }),
272         ),
273         "WIFI" => (
274             ProtoChipKind::WIFI,
275             wireless::CreateParam::Mock(wireless::mocked::CreateParams {
276                 chip_kind: ProtoChipKind::WIFI,
277             }),
278         ),
279         "UWB" => (
280             ProtoChipKind::UWB,
281             wireless::CreateParam::Mock(wireless::mocked::CreateParams {
282                 chip_kind: ProtoChipKind::UWB,
283             }),
284         ),
285         _ => {
286             return Box::new(AddChipResultCxx {
287                 device_id: u32::MAX,
288                 chip_id: u32::MAX,
289                 is_error: true,
290             })
291         }
292     };
293     let chip_create_params = chip::CreateParams {
294         kind: chip_kind_enum,
295         address: chip_address.to_string(),
296         name: if chip_name.is_empty() { None } else { Some(chip_name.to_string()) },
297         manufacturer: chip_manufacturer.to_string(),
298         product_name: chip_product_name.to_string(),
299         bt_properties: bt_properties_proto.ok(),
300     };
301     match add_chip(device_guid, device_name, &chip_create_params, &wireless_create_param) {
302         Ok(result) => Box::new(AddChipResultCxx {
303             device_id: result.device_id.0,
304             chip_id: result.chip_id.0,
305             is_error: false,
306         }),
307         Err(_) => {
308             Box::new(AddChipResultCxx { device_id: u32::MAX, chip_id: u32::MAX, is_error: true })
309         }
310     }
311 }
312 
313 /// Remove a device from the simulation.
314 ///
315 /// Called when the last chip for the device is removed.
remove_device(devices: Arc<DeviceManager>, id: &DeviceIdentifier) -> Result<(), String>316 fn remove_device(devices: Arc<DeviceManager>, id: &DeviceIdentifier) -> Result<(), String> {
317     match devices.devices.write().unwrap().remove(id) {
318         Some(device) => events::publish(Event::DeviceRemoved(DeviceRemoved {
319             id: device.id,
320             name: device.name,
321             builtin: device.builtin,
322         })),
323         None => return Err(format!("Device not found for device_id: {id}")),
324     }
325     // Update last modified timestamp for devices
326     devices.update_timestamp();
327     Ok(())
328 }
329 
330 /// Remove a chip from a device.
331 ///
332 /// Called when the packet transport for the chip shuts down.
remove_chip(device_id: DeviceIdentifier, chip_id: ChipIdentifier) -> Result<(), String>333 pub fn remove_chip(device_id: DeviceIdentifier, chip_id: ChipIdentifier) -> Result<(), String> {
334     let manager = get_manager();
335     let (is_empty, radio_stats) = match manager.devices.write().unwrap().entry(device_id) {
336         Entry::Occupied(entry) => {
337             let device = entry.get();
338             let radio_stats = device.remove_chip(&chip_id)?;
339             (device.chips.read().unwrap().is_empty(), radio_stats)
340         }
341         Entry::Vacant(_) => return Err(format!("RemoveChip device id {} not found", device_id)),
342     };
343     if is_empty {
344         remove_device(manager.clone(), &device_id)?;
345     }
346     manager.update_timestamp();
347     events::publish(Event::ChipRemoved(ChipRemoved {
348         chip_id,
349         device_id,
350         remaining_nonbuiltin_devices: manager
351             .devices
352             .read()
353             .unwrap()
354             .values()
355             .filter(|device| !device.builtin)
356             .count(),
357         radio_stats,
358     }));
359     Ok(())
360 }
361 
delete_chip(delete_json: &str) -> Result<(), String>362 pub fn delete_chip(delete_json: &str) -> Result<(), String> {
363     let mut request = DeleteChipRequest::new();
364     if merge_from_str(&mut request, delete_json).is_err() {
365         return Err(format!(
366             "failed to delete chip: incorrectly formatted delete json: {}",
367             delete_json
368         ));
369     };
370 
371     let chip_id = ChipIdentifier(request.id);
372 
373     let device_id = get_manager()
374         .devices
375         .read()
376         .unwrap()
377         .iter()
378         .find(|(_, device)| device.chips.read().unwrap().contains_key(&chip_id))
379         .map(|(id, _)| *id)
380         .ok_or(format!("failed to delete chip: could not find chip with id {}", request.id))?;
381 
382     remove_chip(device_id, chip_id)
383 }
384 
385 /// A RemoveChip function for Rust Device API.
386 /// The backend gRPC code will be invoking this method.
remove_chip_cxx(device_id: u32, chip_id: u32)387 pub fn remove_chip_cxx(device_id: u32, chip_id: u32) {
388     let _ = remove_chip(DeviceIdentifier(device_id), ChipIdentifier(chip_id));
389 }
390 
391 /// Create a device from a CreateDeviceRequest json.
392 /// Uses a default name if none is provided.
393 /// Returns an error if the device already exists.
create_device(create_json: &str) -> Result<DeviceIdentifier, String>394 pub fn create_device(create_json: &str) -> Result<DeviceIdentifier, String> {
395     let mut create_device_request = CreateDeviceRequest::new();
396     if merge_from_str(&mut create_device_request, create_json).is_err() {
397         return Err(format!(
398             "failed to create device: incorrectly formatted create json: {}",
399             create_json
400         ));
401     }
402 
403     let new_device = create_device_request.device;
404     let manager = get_manager();
405     // Check if specified device name is already mapped.
406     if new_device.name != String::default()
407         && manager.devices.read().unwrap().values().any(|d| d.guid == new_device.name)
408     {
409         return Err(String::from("failed to create device: device already exists"));
410     }
411 
412     if new_device.chips.is_empty() {
413         return Err(String::from("failed to create device: device must contain at least 1 chip"));
414     }
415     new_device.chips.iter().try_for_each(|chip| match chip.chip {
416         Some(ProtoBuiltin::BleBeacon(_)) => Ok(()),
417         Some(_) => Err(format!("failed to create device: chip {} was not a built-in", chip.name)),
418         None => Err(format!("failed to create device: chip {} was missing a radio", chip.name)),
419     })?;
420 
421     let device_name = (new_device.name != String::default()).then_some(new_device.name.as_str());
422     let (device_id, device_name) = manager.get_or_create_device(device_name, device_name, true);
423 
424     new_device.chips.iter().try_for_each(|chip| {
425         {
426             let chip_create_params = chip::CreateParams {
427                 kind: chip.kind.enum_value_or_default(),
428                 address: chip.address.clone(),
429                 name: if chip.name.is_empty() { None } else { Some(chip.name.to_string()) },
430                 manufacturer: chip.manufacturer.clone(),
431                 product_name: chip.product_name.clone(),
432                 bt_properties: chip.bt_properties.as_ref().cloned(),
433             };
434             let wireless_create_params =
435                 wireless::CreateParam::BleBeacon(wireless::ble_beacon::CreateParams {
436                     device_name: device_name.clone(),
437                     chip_proto: chip.clone(),
438                 });
439             add_chip(&device_name, &device_name, &chip_create_params, &wireless_create_params)
440         }
441         .map(|_| ())
442     })?;
443 
444     Ok(device_id)
445 }
446 
447 // lock the devices, find the id and call the patch function
448 #[allow(dead_code)]
patch_device(id_option: Option<DeviceIdentifier>, patch_json: &str) -> Result<(), String>449 fn patch_device(id_option: Option<DeviceIdentifier>, patch_json: &str) -> Result<(), String> {
450     let mut patch_device_request = PatchDeviceRequest::new();
451     if merge_from_str(&mut patch_device_request, patch_json).is_ok() {
452         let manager = get_manager();
453         let proto_device = patch_device_request.device;
454         match id_option {
455             Some(id) => match manager.devices.read().unwrap().get(&id) {
456                 Some(device) => {
457                     let result = device.patch(&proto_device);
458                     let name = device.name.clone();
459                     if result.is_ok() {
460                         // Update last modified timestamp for manager
461                         manager.update_timestamp();
462 
463                         // Publish Device Patched event
464                         events::publish(Event::DevicePatched(DevicePatched { id, name }));
465                     }
466                     result
467                 }
468                 None => Err(format!("No such device with id {id}")),
469             },
470             None => {
471                 let mut multiple_matches = false;
472                 let mut target: Option<&Device> = None;
473                 let devices = manager.devices.read().unwrap();
474                 for device in devices.values() {
475                     if device.name.contains(&proto_device.name) {
476                         if device.name == proto_device.name {
477                             let result = device.patch(&proto_device);
478                             let id = device.id;
479                             let name = device.name.clone();
480                             if result.is_ok() {
481                                 // Update last modified timestamp for manager
482                                 manager.update_timestamp();
483 
484                                 // Publish Device Patched event
485                                 events::publish(Event::DevicePatched(DevicePatched { id, name }));
486                             }
487                             return result;
488                         }
489                         multiple_matches = target.is_some();
490                         target = Some(device);
491                     }
492                 }
493                 if multiple_matches {
494                     return Err(format!(
495                         "Multiple ambiguous matches were found with substring {}",
496                         proto_device.name
497                     ));
498                 }
499                 match target {
500                     Some(device) => {
501                         let result = device.patch(&proto_device);
502                         let id = device.id;
503                         let name = device.name.clone();
504                         if result.is_ok() {
505                             // Update last modified timestamp for devices
506                             manager.update_timestamp();
507 
508                             // Publish Device Patched event
509                             events::publish(Event::DevicePatched(DevicePatched { id, name }));
510                         }
511                         result
512                     }
513                     None => Err(format!("No such device with name {}", proto_device.name)),
514                 }
515             }
516         }
517     } else {
518         Err(format!("Incorrect format of patch json {}", patch_json))
519     }
520 }
521 
distance(a: &ProtoPosition, b: &ProtoPosition) -> f32522 fn distance(a: &ProtoPosition, b: &ProtoPosition) -> f32 {
523     ((b.x - a.x).powf(2.0) + (b.y - a.y).powf(2.0) + (b.z - a.z).powf(2.0)).sqrt()
524 }
525 
526 #[allow(dead_code)]
get_distance(id: &ChipIdentifier, other_id: &ChipIdentifier) -> Result<f32, String>527 fn get_distance(id: &ChipIdentifier, other_id: &ChipIdentifier) -> Result<f32, String> {
528     let device_id = crate::devices::chip::get_chip(id)
529         .ok_or(format!("No such device with chip_id {id}"))?
530         .device_id;
531     let other_device_id = crate::devices::chip::get_chip(other_id)
532         .ok_or(format!("No such device with chip_id {other_id}"))?
533         .device_id;
534     let manager = get_manager();
535     let a = manager
536         .devices
537         .read()
538         .unwrap()
539         .get(&device_id)
540         .map(|device_ref| device_ref.position.read().unwrap().clone())
541         .ok_or(format!("No such device with id {id}"))?;
542     let b = manager
543         .devices
544         .read()
545         .unwrap()
546         .get(&other_device_id)
547         .map(|device_ref| device_ref.position.read().unwrap().clone())
548         .ok_or(format!("No such device with id {other_id}"))?;
549     Ok(distance(&a, &b))
550 }
551 
552 /// A GetDistance function for Rust Device API.
553 /// The backend gRPC code will be invoking this method.
get_distance_cxx(a: u32, b: u32) -> f32554 pub fn get_distance_cxx(a: u32, b: u32) -> f32 {
555     match get_distance(&ChipIdentifier(a), &ChipIdentifier(b)) {
556         Ok(distance) => distance,
557         Err(err) => {
558             warn!("get_distance Error: {err}");
559             0.0
560         }
561     }
562 }
563 
564 /// Function to obtain ProtoDevice given a ChipIdentifier
get_device(chip_id: &ChipIdentifier) -> anyhow::Result<netsim_proto::model::Device>565 pub fn get_device(chip_id: &ChipIdentifier) -> anyhow::Result<netsim_proto::model::Device> {
566     let device_id = match chip::get_chip(chip_id) {
567         Some(chip) => chip.device_id,
568         None => return Err(anyhow::anyhow!("Can't find chip for chip_id: {chip_id}")),
569     };
570     get_manager()
571         .devices
572         .read()
573         .unwrap()
574         .get(&device_id)
575         .ok_or(anyhow::anyhow!("Can't find device for device_id: {device_id}"))?
576         .get()
577         .map_err(|e| anyhow::anyhow!("{e:?}"))
578 }
579 
reset_all() -> Result<(), String>580 pub fn reset_all() -> Result<(), String> {
581     let manager = get_manager();
582     // Perform reset for all manager
583     for device in manager.devices.read().unwrap().values() {
584         device.reset()?;
585     }
586     // Update last modified timestamp for manager
587     manager.update_timestamp();
588     events::publish(Event::DeviceReset);
589     Ok(())
590 }
591 
handle_device_create(writer: ResponseWritable, create_json: &str)592 fn handle_device_create(writer: ResponseWritable, create_json: &str) {
593     let mut response = CreateDeviceResponse::new();
594 
595     let mut collate_results = || {
596         let id = create_device(create_json)?;
597 
598         let device_proto = get_manager()
599             .devices
600             .read()
601             .unwrap()
602             .get(&id)
603             .ok_or("failed to create device")?
604             .get()?;
605         response.device = MessageField::some(device_proto);
606         print_to_string(&response).map_err(|_| String::from("failed to convert device to json"))
607     };
608 
609     match collate_results() {
610         Ok(response) => writer.put_ok("text/json", &response, vec![]),
611         Err(err) => writer.put_error(404, err.as_str()),
612     }
613 }
614 
615 /// Performs PatchDevice to patch a single device
handle_device_patch(writer: ResponseWritable, id: Option<DeviceIdentifier>, patch_json: &str)616 fn handle_device_patch(writer: ResponseWritable, id: Option<DeviceIdentifier>, patch_json: &str) {
617     match patch_device(id, patch_json) {
618         Ok(()) => writer.put_ok("text/plain", "Device Patch Success", vec![]),
619         Err(err) => writer.put_error(404, err.as_str()),
620     }
621 }
622 
handle_chip_delete(writer: ResponseWritable, delete_json: &str)623 fn handle_chip_delete(writer: ResponseWritable, delete_json: &str) {
624     match delete_chip(delete_json) {
625         Ok(()) => writer.put_ok("text/plain", "Chip Delete Success", vec![]),
626         Err(err) => writer.put_error(404, err.as_str()),
627     }
628 }
629 
list_device() -> anyhow::Result<ListDeviceResponse, String>630 pub fn list_device() -> anyhow::Result<ListDeviceResponse, String> {
631     // Instantiate ListDeviceResponse and add DeviceManager
632     let mut response = ListDeviceResponse::new();
633     let manager = get_manager();
634     for device in manager.devices.read().unwrap().values() {
635         if let Ok(device_proto) = device.get() {
636             response.devices.push(device_proto);
637         }
638     }
639 
640     // Add Last Modified Timestamp into ListDeviceResponse
641     response.last_modified = Some(Timestamp {
642         seconds: manager.last_modified.read().unwrap().as_secs() as i64,
643         nanos: manager.last_modified.read().unwrap().subsec_nanos() as i32,
644         ..Default::default()
645     })
646     .into();
647     Ok(response)
648 }
649 
650 /// Performs ListDevices to get the list of DeviceManager and write to writer.
handle_device_list(writer: ResponseWritable)651 fn handle_device_list(writer: ResponseWritable) {
652     let response = list_device().unwrap();
653     // Perform protobuf-json-mapping with the given protobuf
654     if let Ok(json_response) = print_to_string_with_options(&response, &JSON_PRINT_OPTION) {
655         writer.put_ok("text/json", &json_response, vec![])
656     } else {
657         writer.put_error(404, "proto to JSON mapping failure")
658     }
659 }
660 
661 /// Performs ResetDevice for all devices
handle_device_reset(writer: ResponseWritable)662 fn handle_device_reset(writer: ResponseWritable) {
663     match reset_all() {
664         Ok(()) => writer.put_ok("text/plain", "Device Reset Success", vec![]),
665         Err(err) => writer.put_error(404, err.as_str()),
666     }
667 }
668 
669 /// Performs SubscribeDevice
handle_device_subscribe(writer: ResponseWritable, subscribe_json: &str)670 fn handle_device_subscribe(writer: ResponseWritable, subscribe_json: &str) {
671     // Check if the provided last_modified timestamp is prior to the current last_modified
672     let mut subscribe_device_request = SubscribeDeviceRequest::new();
673     if merge_from_str(&mut subscribe_device_request, subscribe_json).is_ok() {
674         let timestamp_proto = subscribe_device_request.last_modified;
675         let provided_last_modified =
676             Duration::new(timestamp_proto.seconds as u64, timestamp_proto.nanos as u32);
677         if provided_last_modified < *get_manager().last_modified.read().unwrap() {
678             info!("Immediate return for SubscribeDevice");
679             handle_device_list(writer);
680             return;
681         }
682     }
683 
684     let event_rx = events::subscribe();
685     // Timeout after 15 seconds with no event received
686     match event_rx.recv_timeout(Duration::from_secs(15)) {
687         Ok(Event::DeviceAdded(_))
688         | Ok(Event::DeviceRemoved(_))
689         | Ok(Event::ChipAdded(_))
690         | Ok(Event::ChipRemoved(_))
691         | Ok(Event::DevicePatched(_))
692         | Ok(Event::DeviceReset) => handle_device_list(writer),
693         Err(err) => writer.put_error(404, format!("{err:?}").as_str()),
694         _ => writer.put_error(404, "disconnecting due to unrelated event"),
695     }
696 }
697 
698 /// The Rust device handler used directly by Http frontend or handle_device_cxx for LIST, GET, and PATCH
handle_device(request: &Request<Vec<u8>>, param: &str, writer: ResponseWritable)699 pub fn handle_device(request: &Request<Vec<u8>>, param: &str, writer: ResponseWritable) {
700     // Route handling
701     if request.uri() == "/v1/devices" {
702         // Routes with ID not specified
703         match request.method().as_str() {
704             "GET" => {
705                 handle_device_list(writer);
706             }
707             "PUT" => {
708                 handle_device_reset(writer);
709             }
710             "SUBSCRIBE" => {
711                 let body = request.body();
712                 let subscribe_json = String::from_utf8(body.to_vec()).unwrap();
713                 handle_device_subscribe(writer, subscribe_json.as_str());
714             }
715             "PATCH" => {
716                 let body = request.body();
717                 let patch_json = String::from_utf8(body.to_vec()).unwrap();
718                 handle_device_patch(writer, None, patch_json.as_str());
719             }
720             "POST" => {
721                 let body = &request.body();
722                 let create_json = String::from_utf8(body.to_vec()).unwrap();
723                 handle_device_create(writer, create_json.as_str());
724             }
725             "DELETE" => {
726                 let body = &request.body();
727                 let delete_json = String::from_utf8(body.to_vec()).unwrap();
728                 handle_chip_delete(writer, delete_json.as_str());
729             }
730             _ => writer.put_error(404, "Not found."),
731         }
732     } else {
733         // Routes with ID specified
734         match request.method().as_str() {
735             "PATCH" => {
736                 let id = match param.parse::<u32>() {
737                     Ok(num) => DeviceIdentifier(num),
738                     Err(_) => {
739                         writer.put_error(404, "Incorrect Id type for devices, ID should be u32.");
740                         return;
741                     }
742                 };
743                 let body = request.body();
744                 let patch_json = String::from_utf8(body.to_vec()).unwrap();
745                 handle_device_patch(writer, Some(id), patch_json.as_str());
746             }
747             _ => writer.put_error(404, "Not found."),
748         }
749     }
750 }
751 
752 /// Device handler cxx for grpc server to call
handle_device_cxx( responder: Pin<&mut CxxServerResponseWriter>, method: String, param: String, body: String, )753 pub fn handle_device_cxx(
754     responder: Pin<&mut CxxServerResponseWriter>,
755     method: String,
756     param: String,
757     body: String,
758 ) {
759     let mut builder = Request::builder().method(method.as_str());
760     if param.is_empty() {
761         builder = builder.uri("/v1/devices");
762     } else {
763         builder = builder.uri(format!("/v1/devices/{}", param));
764     }
765     builder = builder.version(Version::HTTP_11);
766     let request = match builder.body(body.as_bytes().to_vec()) {
767         Ok(request) => request,
768         Err(err) => {
769             warn!("{err:?}");
770             return;
771         }
772     };
773     handle_device(
774         &request,
775         param.as_str(),
776         &mut CxxServerResponseWriterWrapper { writer: responder },
777     )
778 }
779 
780 /// return enum type for check_device_event
781 #[derive(Debug, PartialEq)]
782 enum DeviceWaitStatus {
783     LastDeviceRemoved,
784     DeviceAdded,
785     Timeout,
786     IgnoreEvent,
787 }
788 
789 /// listening to events
check_device_event( events_rx: &Receiver<Event>, timeout_time: Option<Instant>, ) -> DeviceWaitStatus790 fn check_device_event(
791     events_rx: &Receiver<Event>,
792     timeout_time: Option<Instant>,
793 ) -> DeviceWaitStatus {
794     let wait_time = timeout_time.map_or(Duration::from_secs(u64::MAX), |t| t - Instant::now());
795     match events_rx.recv_timeout(wait_time) {
796         Ok(Event::ChipRemoved(ChipRemoved { remaining_nonbuiltin_devices: 0, .. })) => {
797             DeviceWaitStatus::LastDeviceRemoved
798         }
799         // DeviceAdded (event from CreateDevice)
800         // ChipAdded (event from add_chip or add_chip_cxx)
801         Ok(Event::DeviceAdded(DeviceAdded { builtin: false, .. }))
802         | Ok(Event::ChipAdded(ChipAdded { builtin: false, .. })) => DeviceWaitStatus::DeviceAdded,
803         Err(_) => DeviceWaitStatus::Timeout,
804         _ => DeviceWaitStatus::IgnoreEvent,
805     }
806 }
807 
808 /// wait loop logic for devices
809 /// the function will publish a ShutDown event when
810 /// 1. Initial timeout before first device is added
811 /// 2. Last Chip Removed from netsimd
812 /// this function should NOT be invoked if running in no-shutdown mode
spawn_shutdown_publisher(events_rx: Receiver<Event>)813 pub fn spawn_shutdown_publisher(events_rx: Receiver<Event>) {
814     spawn_shutdown_publisher_with_timeout(events_rx, IDLE_SECS_FOR_SHUTDOWN, events::get_events());
815 }
816 
817 // separate function for testability
spawn_shutdown_publisher_with_timeout( events_rx: Receiver<Event>, timeout_duration_s: u64, events_tx: Arc<Mutex<Events>>, )818 fn spawn_shutdown_publisher_with_timeout(
819     events_rx: Receiver<Event>,
820     timeout_duration_s: u64,
821     events_tx: Arc<Mutex<Events>>,
822 ) {
823     let _ =
824         std::thread::Builder::new().name("device_event_subscriber".to_string()).spawn(move || {
825             let publish_event =
826                 |e: Event| events_tx.lock().expect("Failed to acquire lock on events").publish(e);
827 
828             let mut timeout_time = Some(Instant::now() + Duration::from_secs(timeout_duration_s));
829             loop {
830                 match check_device_event(&events_rx, timeout_time) {
831                     DeviceWaitStatus::LastDeviceRemoved => {
832                         publish_event(Event::ShutDown(ShutDown {
833                             reason: "last device disconnected".to_string(),
834                         }));
835                         return;
836                     }
837                     DeviceWaitStatus::DeviceAdded => {
838                         timeout_time = None;
839                     }
840                     DeviceWaitStatus::Timeout => {
841                         publish_event(Event::ShutDown(ShutDown {
842                             reason: format!(
843                                 "no devices connected within {IDLE_SECS_FOR_SHUTDOWN}s"
844                             ),
845                         }));
846                         return;
847                     }
848                     DeviceWaitStatus::IgnoreEvent => continue,
849                 }
850             }
851         });
852 }
853 
854 /// Return vector containing current radio chip stats from all devices
get_radio_stats() -> Vec<NetsimRadioStats>855 pub fn get_radio_stats() -> Vec<NetsimRadioStats> {
856     let mut result: Vec<NetsimRadioStats> = Vec::new();
857     // TODO: b/309805437 - optimize logic using get_stats for WirelessAdaptor
858     for (device_id, device) in get_manager().devices.read().unwrap().iter() {
859         for chip in device.chips.read().unwrap().values() {
860             for mut radio_stats in chip.get_stats() {
861                 radio_stats.set_device_id(device_id.0);
862                 result.push(radio_stats);
863             }
864         }
865     }
866     result
867 }
868 
869 #[cfg(test)]
870 mod tests {
871     use crate::events;
872     use netsim_common::util::netsim_logger::init_for_test;
873     use netsim_proto::model::{
874         Device as ProtoDevice, DeviceCreate as ProtoDeviceCreate, Orientation as ProtoOrientation,
875     };
876     use protobuf_json_mapping::print_to_string;
877     use std::{sync::Once, thread};
878 
879     use super::*;
880 
881     // This allows Log init method to be invoked once when running all tests.
882     static INIT: Once = Once::new();
883 
884     /// Logger setup function that is only run once, even if called multiple times.
logger_setup()885     fn logger_setup() {
886         INIT.call_once(|| {
887             init_for_test();
888         });
889     }
890 
891     /// TestChipParameters struct to invoke add_chip
892     /// This struct contains parameters required to invoke add_chip.
893     /// This will eventually be invoked by the facades.
894     struct TestChipParameters {
895         device_guid: String,
896         device_name: String,
897         chip_kind: ProtoChipKind,
898         chip_name: String,
899         chip_manufacturer: String,
900         chip_product_name: String,
901     }
902 
903     impl TestChipParameters {
add_chip(&self) -> Result<AddChipResult, String>904         fn add_chip(&self) -> Result<AddChipResult, String> {
905             let chip_create_params = chip::CreateParams {
906                 kind: self.chip_kind,
907                 address: "".to_string(),
908                 name: Some(self.chip_name.clone()),
909                 manufacturer: self.chip_manufacturer.clone(),
910                 product_name: self.chip_product_name.clone(),
911                 bt_properties: None,
912             };
913             let wireless_create_params =
914                 wireless::CreateParam::Mock(wireless::mocked::CreateParams {
915                     chip_kind: self.chip_kind,
916                 });
917             super::add_chip(
918                 &self.device_guid,
919                 &self.device_name,
920                 &chip_create_params,
921                 &wireless_create_params,
922             )
923         }
924 
get_or_create_device(&self) -> DeviceIdentifier925         fn get_or_create_device(&self) -> DeviceIdentifier {
926             let manager = get_manager();
927             manager.get_or_create_device(Some(&self.device_guid), Some(&self.device_name), false).0
928         }
929     }
930 
931     /// helper function for test cases to instantiate ProtoPosition
new_position(x: f32, y: f32, z: f32) -> ProtoPosition932     fn new_position(x: f32, y: f32, z: f32) -> ProtoPosition {
933         ProtoPosition { x, y, z, ..Default::default() }
934     }
935 
new_orientation(yaw: f32, pitch: f32, roll: f32) -> ProtoOrientation936     fn new_orientation(yaw: f32, pitch: f32, roll: f32) -> ProtoOrientation {
937         ProtoOrientation { yaw, pitch, roll, ..Default::default() }
938     }
939 
test_chip_1_bt() -> TestChipParameters940     fn test_chip_1_bt() -> TestChipParameters {
941         TestChipParameters {
942             device_guid: format!("guid-fs-1-{:?}", thread::current().id()),
943             device_name: format!("test-device-name-1-{:?}", thread::current().id()),
944             chip_kind: ProtoChipKind::BLUETOOTH,
945             chip_name: "bt_chip_name".to_string(),
946             chip_manufacturer: "netsim".to_string(),
947             chip_product_name: "netsim_bt".to_string(),
948         }
949     }
950 
test_chip_1_wifi() -> TestChipParameters951     fn test_chip_1_wifi() -> TestChipParameters {
952         TestChipParameters {
953             device_guid: format!("guid-fs-1-{:?}", thread::current().id()),
954             device_name: format!("test-device-name-1-{:?}", thread::current().id()),
955             chip_kind: ProtoChipKind::WIFI,
956             chip_name: "wifi_chip_name".to_string(),
957             chip_manufacturer: "netsim".to_string(),
958             chip_product_name: "netsim_wifi".to_string(),
959         }
960     }
961 
test_chip_2_bt() -> TestChipParameters962     fn test_chip_2_bt() -> TestChipParameters {
963         TestChipParameters {
964             device_guid: format!("guid-fs-2-{:?}", thread::current().id()),
965             device_name: format!("test-device-name-2-{:?}", thread::current().id()),
966             chip_kind: ProtoChipKind::BLUETOOTH,
967             chip_name: "bt_chip_name".to_string(),
968             chip_manufacturer: "netsim".to_string(),
969             chip_product_name: "netsim_bt".to_string(),
970         }
971     }
972 
reset(id: DeviceIdentifier) -> Result<(), String>973     fn reset(id: DeviceIdentifier) -> Result<(), String> {
974         let manager = get_manager();
975         let mut devices = manager.devices.write().unwrap();
976         match devices.get_mut(&id) {
977             Some(device) => device.reset(),
978             None => Err(format!("No such device with id {id}")),
979         }
980     }
981 
spawn_shutdown_publisher_test_setup(timeout: u64) -> (Arc<Mutex<Events>>, Receiver<Event>)982     fn spawn_shutdown_publisher_test_setup(timeout: u64) -> (Arc<Mutex<Events>>, Receiver<Event>) {
983         let mut events = events::test::new();
984         let events_rx = events::test::subscribe(&mut events);
985         spawn_shutdown_publisher_with_timeout(events_rx, timeout, events.clone());
986 
987         let events_rx2 = events::test::subscribe(&mut events);
988 
989         (events, events_rx2)
990     }
991 
992     #[test]
test_spawn_shutdown_publisher_last_chip_removed()993     fn test_spawn_shutdown_publisher_last_chip_removed() {
994         let (mut events, events_rx) = spawn_shutdown_publisher_test_setup(IDLE_SECS_FOR_SHUTDOWN);
995 
996         events::test::publish(
997             &mut events,
998             Event::ChipRemoved(ChipRemoved {
999                 remaining_nonbuiltin_devices: 0,
1000                 ..Default::default()
1001             }),
1002         );
1003 
1004         // receive our own ChipRemoved
1005         assert!(matches!(events_rx.recv(), Ok(Event::ChipRemoved(ChipRemoved { .. }))));
1006         // receive the ShutDown emitted by the function under test
1007         assert!(matches!(events_rx.recv(), Ok(Event::ShutDown(ShutDown { .. }))));
1008     }
1009 
1010     #[test]
test_spawn_shutdown_publisher_chip_removed_which_is_not_last_chip()1011     fn test_spawn_shutdown_publisher_chip_removed_which_is_not_last_chip() {
1012         let (mut events, events_rx) = spawn_shutdown_publisher_test_setup(IDLE_SECS_FOR_SHUTDOWN);
1013         events::test::publish(
1014             &mut events,
1015             Event::ChipRemoved(ChipRemoved {
1016                 chip_id: ChipIdentifier(1),
1017                 remaining_nonbuiltin_devices: 1,
1018                 ..Default::default()
1019             }),
1020         );
1021 
1022         // give other thread time to generate a ShutDown if it was going to
1023         std::thread::sleep(std::time::Duration::from_secs(1));
1024 
1025         // only the 2nd ChipRemoved should generate a ShutDown as it is marked the last one
1026         events::test::publish(
1027             &mut events,
1028             Event::ChipRemoved(ChipRemoved {
1029                 chip_id: ChipIdentifier(0),
1030                 remaining_nonbuiltin_devices: 0,
1031                 ..Default::default()
1032             }),
1033         );
1034 
1035         // receive our own ChipRemoved
1036         assert!(matches!(events_rx.recv(), Ok(Event::ChipRemoved(ChipRemoved { .. }))));
1037         // receive our own ChipRemoved (with no shutdown)
1038         assert!(matches!(events_rx.recv(), Ok(Event::ChipRemoved(ChipRemoved { .. }))));
1039         // only then receive the ShutDown emitted by the function under test
1040         assert!(matches!(events_rx.recv(), Ok(Event::ShutDown(ShutDown { .. }))));
1041     }
1042 
1043     #[test]
test_spawn_shutdown_publisher_last_chip_removed_with_duplicate_event()1044     fn test_spawn_shutdown_publisher_last_chip_removed_with_duplicate_event() {
1045         let (mut events, events_rx) = spawn_shutdown_publisher_test_setup(IDLE_SECS_FOR_SHUTDOWN);
1046         events::test::publish(
1047             &mut events,
1048             Event::ChipRemoved(ChipRemoved {
1049                 chip_id: ChipIdentifier(0),
1050                 remaining_nonbuiltin_devices: 0,
1051                 ..Default::default()
1052             }),
1053         );
1054 
1055         // give other thread time to generate a ShutDown if it was going to
1056         std::thread::sleep(std::time::Duration::from_secs(1));
1057 
1058         // this is a duplicate event and we already sent that all chips were removed
1059         // this is for strict comparison with test_spawn_shutdown_publisher_chip_removed_which_is_not_last_chip
1060         // to validate that if the first event has remaining_nonbuiltin_devices 0
1061         // we would receive ChipRemoved, ShutDown, ChipRemoved
1062         // but if first ChipRemoved has remaining_nonbuiltin_devices,
1063         // we instead receive ChipRemoved, ChipRemoved, ShutDown
1064         events::test::publish(
1065             &mut events,
1066             Event::ChipRemoved(ChipRemoved {
1067                 chip_id: ChipIdentifier(0),
1068                 remaining_nonbuiltin_devices: 0,
1069                 ..Default::default()
1070             }),
1071         );
1072 
1073         // receive our own ChipRemoved
1074         assert!(matches!(events_rx.recv(), Ok(Event::ChipRemoved(_))));
1075         // receive the ShutDown emitted by the function under test
1076         assert!(matches!(events_rx.recv(), Ok(Event::ShutDown(_))));
1077         // receive our own erroneous ChipRemoved which occurs after we said all chips were removed
1078         // this is just for strict comparison with test_spawn_shutdown_publisher_chip_removed_which_is_not_last_chip
1079         assert!(matches!(events_rx.recv(), Ok(Event::ChipRemoved(_))));
1080         // should timeout now (no further events as we expect shutdown publisher thread to have stopped)
1081         assert!(events_rx.recv_timeout(Duration::from_secs(2)).is_err());
1082     }
1083 
1084     #[test]
test_spawn_shutdown_publisher_timeout()1085     fn test_spawn_shutdown_publisher_timeout() {
1086         let (_, events_rx) = spawn_shutdown_publisher_test_setup(1u64);
1087 
1088         // receive the ShutDown emitted by the function under test
1089         assert!(matches!(events_rx.recv_timeout(Duration::from_secs(2)), Ok(Event::ShutDown(_))));
1090     }
1091 
1092     #[test]
test_spawn_shutdown_publisher_timeout_is_canceled_if_a_chip_is_added()1093     fn test_spawn_shutdown_publisher_timeout_is_canceled_if_a_chip_is_added() {
1094         let (mut events, events_rx) = spawn_shutdown_publisher_test_setup(1u64);
1095 
1096         events::test::publish(
1097             &mut events,
1098             Event::ChipAdded(ChipAdded {
1099                 chip_id: ChipIdentifier(0),
1100                 chip_kind: ProtoChipKind::BLUETOOTH,
1101                 ..Default::default()
1102             }),
1103         );
1104         assert!(matches!(events_rx.recv(), Ok(Event::ChipAdded(_))));
1105 
1106         // should NO longer receive the ShutDown emitted by the function under test
1107         // based on timeout removed when chip added
1108         assert!(events_rx.recv_timeout(Duration::from_secs(2)).is_err());
1109 
1110         events::test::publish(
1111             &mut events,
1112             Event::ChipRemoved(ChipRemoved {
1113                 chip_id: ChipIdentifier(0),
1114                 remaining_nonbuiltin_devices: 0,
1115                 ..Default::default()
1116             }),
1117         );
1118         // receive our own ChipRemoved
1119         assert!(matches!(events_rx.recv(), Ok(Event::ChipRemoved(_))));
1120         // receive the ShutDown emitted by the function under test
1121         assert!(matches!(events_rx.recv(), Ok(Event::ShutDown(_))));
1122     }
1123 
1124     #[test]
test_distance()1125     fn test_distance() {
1126         // Pythagorean quadruples
1127         let a = new_position(0.0, 0.0, 0.0);
1128         let mut b = new_position(1.0, 2.0, 2.0);
1129         assert_eq!(distance(&a, &b), 3.0);
1130         b = new_position(2.0, 3.0, 6.0);
1131         assert_eq!(distance(&a, &b), 7.0);
1132     }
1133 
1134     #[test]
test_add_chip()1135     fn test_add_chip() {
1136         // Initializing Logger
1137         logger_setup();
1138 
1139         // Adding a chip
1140         let chip_params = test_chip_1_bt();
1141         let chip_result = chip_params.add_chip().unwrap();
1142         match get_manager().devices.read().unwrap().get(&chip_result.device_id) {
1143             Some(device) => {
1144                 let chips = device.chips.read().unwrap();
1145                 let chip = chips.get(&chip_result.chip_id).unwrap();
1146                 assert_eq!(chip_params.chip_kind, chip.kind);
1147                 assert_eq!(
1148                     chip_params.chip_manufacturer,
1149                     chip.manufacturer.read().unwrap().to_string()
1150                 );
1151                 assert_eq!(chip_params.chip_name, chip.name);
1152                 assert_eq!(
1153                     chip_params.chip_product_name,
1154                     chip.product_name.read().unwrap().to_string()
1155                 );
1156                 assert_eq!(chip_params.device_name, device.name);
1157             }
1158             None => unreachable!(),
1159         }
1160     }
1161 
1162     #[test]
test_get_or_create_device()1163     fn test_get_or_create_device() {
1164         // Initializing Logger
1165         logger_setup();
1166 
1167         // Creating a device and getting device
1168         let bt_chip_params = test_chip_1_bt();
1169         let device_id_1 = bt_chip_params.get_or_create_device();
1170         let wifi_chip_params = test_chip_1_wifi();
1171         let device_id_2 = wifi_chip_params.get_or_create_device();
1172         assert_eq!(device_id_1, device_id_2);
1173     }
1174 
1175     #[test]
test_patch_device()1176     fn test_patch_device() {
1177         // Initializing Logger
1178         logger_setup();
1179 
1180         // Patching device position and orientation by id
1181         let chip_params = test_chip_1_bt();
1182         let chip_result = chip_params.add_chip().unwrap();
1183         let mut patch_device_request = PatchDeviceRequest::new();
1184         let mut proto_device = ProtoDevice::new();
1185         let request_position = new_position(1.1, 2.2, 3.3);
1186         let request_orientation = new_orientation(4.4, 5.5, 6.6);
1187         proto_device.name = chip_params.device_name;
1188         proto_device.visible = Some(false);
1189         proto_device.position = Some(request_position.clone()).into();
1190         proto_device.orientation = Some(request_orientation.clone()).into();
1191         patch_device_request.device = Some(proto_device.clone()).into();
1192         let patch_json = print_to_string(&patch_device_request).unwrap();
1193         patch_device(Some(chip_result.device_id), patch_json.as_str()).unwrap();
1194         match get_manager().devices.read().unwrap().get(&chip_result.device_id) {
1195             Some(device) => {
1196                 assert_eq!(device.position.read().unwrap().x, request_position.x);
1197                 assert_eq!(device.position.read().unwrap().y, request_position.y);
1198                 assert_eq!(device.position.read().unwrap().z, request_position.z);
1199                 assert_eq!(device.orientation.read().unwrap().yaw, request_orientation.yaw);
1200                 assert_eq!(device.orientation.read().unwrap().pitch, request_orientation.pitch);
1201                 assert_eq!(device.orientation.read().unwrap().roll, request_orientation.roll);
1202                 assert!(!device.visible.load(Ordering::SeqCst));
1203             }
1204             None => unreachable!(),
1205         }
1206 
1207         // Patch device by name with substring match
1208         proto_device.name = format!("test-device-name-1-{:?}", thread::current().id());
1209         patch_device_request.device = Some(proto_device).into();
1210         let patch_json = print_to_string(&patch_device_request).unwrap();
1211         assert!(patch_device(None, patch_json.as_str()).is_ok());
1212     }
1213 
1214     #[test]
test_patch_error()1215     fn test_patch_error() {
1216         // Initializing Logger
1217         logger_setup();
1218 
1219         // Patch Error Testing
1220         let bt_chip_params = test_chip_1_bt();
1221         let bt_chip2_params = test_chip_2_bt();
1222         let bt_chip_result = bt_chip_params.add_chip().unwrap();
1223         bt_chip2_params.add_chip().unwrap();
1224 
1225         // Incorrect value type
1226         let error_json = format!(
1227             "{{\"device\": {{\"name\": \"test-device-name-1-{:?}\", \"position\": 1.1}}}}",
1228             thread::current().id()
1229         );
1230         let patch_result = patch_device(Some(bt_chip_result.device_id), error_json.as_str());
1231         assert!(patch_result.is_err());
1232         assert_eq!(
1233             patch_result.unwrap_err(),
1234             format!("Incorrect format of patch json {}", error_json)
1235         );
1236 
1237         // Incorrect key
1238         let error_json = format!(
1239             "{{\"device\": {{\"name\": \"test-device-name-1-{:?}\", \"hello\": \"world\"}}}}",
1240             thread::current().id()
1241         );
1242         let patch_result = patch_device(Some(bt_chip_result.device_id), error_json.as_str());
1243         assert!(patch_result.is_err());
1244         assert_eq!(
1245             patch_result.unwrap_err(),
1246             format!("Incorrect format of patch json {}", error_json)
1247         );
1248 
1249         // Incorrect Id
1250         let error_json = r#"{"device": {"name": "test-device-name-1"}}"#;
1251         let patch_result = patch_device(Some(DeviceIdentifier(INITIAL_DEVICE_ID - 1)), error_json);
1252         assert!(patch_result.is_err());
1253         assert_eq!(
1254             patch_result.unwrap_err(),
1255             format!("No such device with id {}", INITIAL_DEVICE_ID - 1)
1256         );
1257 
1258         // Incorrect name
1259         let error_json = r#"{"device": {"name": "wrong-name"}}"#;
1260         let patch_result = patch_device(None, error_json);
1261         assert!(patch_result.is_err());
1262         assert_eq!(patch_result.unwrap_err(), "No such device with name wrong-name");
1263 
1264         // Multiple ambiguous matching
1265         let error_json = r#"{"device": {"name": "test-device"}}"#;
1266         let patch_result = patch_device(None, error_json);
1267         assert!(patch_result.is_err());
1268         assert_eq!(
1269             patch_result.unwrap_err(),
1270             "Multiple ambiguous matches were found with substring test-device"
1271         );
1272     }
1273 
1274     #[test]
test_adding_two_chips()1275     fn test_adding_two_chips() {
1276         // Initializing Logger
1277         logger_setup();
1278 
1279         // Adding two chips of the same device
1280         let bt_chip_params = test_chip_1_bt();
1281         let wifi_chip_params = test_chip_1_wifi();
1282         let bt_chip_result = bt_chip_params.add_chip().unwrap();
1283         let wifi_chip_result = wifi_chip_params.add_chip().unwrap();
1284         assert_eq!(bt_chip_result.device_id, wifi_chip_result.device_id);
1285         let manager = get_manager();
1286         let devices = manager.devices.read().unwrap();
1287         let device = devices.get(&bt_chip_result.device_id).unwrap();
1288         assert_eq!(device.id, bt_chip_result.device_id);
1289         assert_eq!(device.name, bt_chip_params.device_name);
1290         assert_eq!(device.chips.read().unwrap().len(), 2);
1291         for chip in device.chips.read().unwrap().values() {
1292             assert!(chip.id == bt_chip_result.chip_id || chip.id == wifi_chip_result.chip_id);
1293             if chip.id == bt_chip_result.chip_id {
1294                 assert_eq!(chip.kind, ProtoChipKind::BLUETOOTH);
1295             } else if chip.id == wifi_chip_result.chip_id {
1296                 assert_eq!(chip.kind, ProtoChipKind::WIFI);
1297             } else {
1298                 unreachable!();
1299             }
1300         }
1301     }
1302 
1303     #[test]
test_reset()1304     fn test_reset() {
1305         // Initializing Logger
1306         logger_setup();
1307 
1308         // Patching Device and Resetting scene
1309         let chip_params = test_chip_1_bt();
1310         let chip_result = chip_params.add_chip().unwrap();
1311         let mut patch_device_request = PatchDeviceRequest::new();
1312         let mut proto_device = ProtoDevice::new();
1313         let request_position = new_position(10.0, 20.0, 30.0);
1314         let request_orientation = new_orientation(1.0, 2.0, 3.0);
1315         proto_device.name = chip_params.device_name;
1316         proto_device.visible = Some(false);
1317         proto_device.position = Some(request_position).into();
1318         proto_device.orientation = Some(request_orientation).into();
1319         patch_device_request.device = Some(proto_device).into();
1320         patch_device(
1321             Some(chip_result.device_id),
1322             print_to_string(&patch_device_request).unwrap().as_str(),
1323         )
1324         .unwrap();
1325         match get_manager().devices.read().unwrap().get(&chip_result.device_id) {
1326             Some(device) => {
1327                 assert_eq!(device.position.read().unwrap().x, 10.0);
1328                 assert_eq!(device.orientation.read().unwrap().yaw, 1.0);
1329                 assert!(!device.visible.load(Ordering::SeqCst));
1330             }
1331             None => unreachable!(),
1332         }
1333         reset(chip_result.device_id).unwrap();
1334         match get_manager().devices.read().unwrap().get(&chip_result.device_id) {
1335             Some(device) => {
1336                 assert_eq!(device.position.read().unwrap().x, 0.0);
1337                 assert_eq!(device.position.read().unwrap().y, 0.0);
1338                 assert_eq!(device.position.read().unwrap().z, 0.0);
1339                 assert_eq!(device.orientation.read().unwrap().yaw, 0.0);
1340                 assert_eq!(device.orientation.read().unwrap().pitch, 0.0);
1341                 assert_eq!(device.orientation.read().unwrap().roll, 0.0);
1342                 assert!(device.visible.load(Ordering::SeqCst));
1343             }
1344             None => unreachable!(),
1345         }
1346     }
1347 
1348     #[test]
test_remove_chip()1349     fn test_remove_chip() {
1350         // Initializing Logger
1351         logger_setup();
1352 
1353         // Add 2 chips of same device and 1 chip of different device
1354         let bt_chip_params = test_chip_1_bt();
1355         let wifi_chip_params = test_chip_1_wifi();
1356         let bt_chip_2_params = test_chip_2_bt();
1357         let bt_chip_result = bt_chip_params.add_chip().unwrap();
1358         let wifi_chip_result = wifi_chip_params.add_chip().unwrap();
1359         let bt_chip_2_result = bt_chip_2_params.add_chip().unwrap();
1360 
1361         // Remove a bt chip of first device
1362         remove_chip(bt_chip_result.device_id, bt_chip_result.chip_id).unwrap();
1363         match get_manager().devices.read().unwrap().get(&bt_chip_result.device_id) {
1364             Some(device) => {
1365                 assert_eq!(device.chips.read().unwrap().len(), 1);
1366                 assert_eq!(
1367                     device.chips.read().unwrap().get(&wifi_chip_result.chip_id).unwrap().kind,
1368                     ProtoChipKind::WIFI
1369                 );
1370             }
1371             None => unreachable!(),
1372         }
1373 
1374         // Remove a wifi chip of first device
1375         remove_chip(wifi_chip_result.device_id, wifi_chip_result.chip_id).unwrap();
1376         assert!(!get_manager().devices.read().unwrap().contains_key(&wifi_chip_result.device_id));
1377 
1378         // Remove a bt chip of second device
1379         remove_chip(bt_chip_2_result.device_id, bt_chip_2_result.chip_id).unwrap();
1380         assert!(!get_manager().devices.read().unwrap().contains_key(&bt_chip_2_result.device_id));
1381     }
1382 
1383     #[test]
test_remove_chip_error()1384     fn test_remove_chip_error() {
1385         // Initializing Logger
1386         logger_setup();
1387 
1388         // Add 2 chips of same device and 1 chip of different device
1389         let bt_chip_params = test_chip_1_bt();
1390         let bt_chip_result = bt_chip_params.add_chip().unwrap();
1391 
1392         // Invoke remove_chip with incorrect chip_id.
1393         match remove_chip(bt_chip_result.device_id, ChipIdentifier(9999)) {
1394             Ok(_) => unreachable!(),
1395             Err(err) => assert_eq!(err, "RemoveChip chip id 9999 not found"),
1396         }
1397 
1398         // Invoke remove_chip with incorrect device_id
1399         match remove_chip(DeviceIdentifier(9999), bt_chip_result.chip_id) {
1400             Ok(_) => unreachable!(),
1401             Err(err) => assert_eq!(err, "RemoveChip device id 9999 not found"),
1402         }
1403         assert!(get_manager().devices.read().unwrap().contains_key(&bt_chip_result.device_id));
1404     }
1405 
1406     #[test]
test_get_distance()1407     fn test_get_distance() {
1408         // Initializing Logger
1409         logger_setup();
1410 
1411         // Add 2 chips of different devices
1412         let bt_chip_params = test_chip_1_bt();
1413         let bt_chip_2_params = test_chip_2_bt();
1414         let bt_chip_result = bt_chip_params.add_chip().unwrap();
1415         let bt_chip_2_result = bt_chip_2_params.add_chip().unwrap();
1416 
1417         // Patch the first chip
1418         let mut patch_device_request = PatchDeviceRequest::new();
1419         let mut proto_device = ProtoDevice::new();
1420         let request_position = new_position(1.0, 1.0, 1.0);
1421         proto_device.name = bt_chip_params.device_name;
1422         proto_device.position = Some(request_position.clone()).into();
1423         patch_device_request.device = Some(proto_device.clone()).into();
1424         let patch_json = print_to_string(&patch_device_request).unwrap();
1425         patch_device(Some(bt_chip_result.device_id), patch_json.as_str()).unwrap();
1426 
1427         // Patch the second chip
1428         let mut patch_device_request = PatchDeviceRequest::new();
1429         let mut proto_device = ProtoDevice::new();
1430         let request_position = new_position(1.0, 4.0, 5.0);
1431         proto_device.name = bt_chip_2_params.device_name;
1432         proto_device.position = Some(request_position.clone()).into();
1433         patch_device_request.device = Some(proto_device.clone()).into();
1434         let patch_json = print_to_string(&patch_device_request).unwrap();
1435         patch_device(Some(bt_chip_2_result.device_id), patch_json.as_str()).unwrap();
1436 
1437         // Verify the get_distance performs the correct computation of
1438         // sqrt((1-1)**2 + (4-1)**2 + (5-1)**2)
1439         assert_eq!(Ok(5.0), get_distance(&bt_chip_result.chip_id, &bt_chip_2_result.chip_id))
1440     }
1441 
1442     #[allow(dead_code)]
list_request() -> Request<Vec<u8>>1443     fn list_request() -> Request<Vec<u8>> {
1444         Request::builder()
1445             .method("GET")
1446             .uri("/v1/devices")
1447             .version(Version::HTTP_11)
1448             .body(Vec::<u8>::new())
1449             .unwrap()
1450     }
1451 
1452     use netsim_proto::model::chip::{
1453         ble_beacon::AdvertiseData, ble_beacon::AdvertiseSettings, BleBeacon, Chip,
1454     };
1455     use netsim_proto::model::chip_create::{BleBeaconCreate, Chip as BuiltChipProto};
1456     use netsim_proto::model::Chip as ChipProto;
1457     use netsim_proto::model::ChipCreate as ProtoChipCreate;
1458     use netsim_proto::model::Device as DeviceProto;
1459     use protobuf::{EnumOrUnknown, MessageField};
1460 
get_test_create_device_request(device_name: Option<String>) -> CreateDeviceRequest1461     fn get_test_create_device_request(device_name: Option<String>) -> CreateDeviceRequest {
1462         let beacon_proto = BleBeaconCreate {
1463             settings: MessageField::some(AdvertiseSettings { ..Default::default() }),
1464             adv_data: MessageField::some(AdvertiseData { ..Default::default() }),
1465             ..Default::default()
1466         };
1467 
1468         let chip_proto = ProtoChipCreate {
1469             name: String::from("test-beacon-chip"),
1470             kind: ProtoChipKind::BLUETOOTH_BEACON.into(),
1471             chip: Some(BuiltChipProto::BleBeacon(beacon_proto)),
1472             ..Default::default()
1473         };
1474 
1475         let device_proto = ProtoDeviceCreate {
1476             name: device_name.unwrap_or_default(),
1477             chips: vec![chip_proto],
1478             ..Default::default()
1479         };
1480 
1481         CreateDeviceRequest { device: MessageField::some(device_proto), ..Default::default() }
1482     }
1483 
get_device_proto(id: DeviceIdentifier) -> DeviceProto1484     fn get_device_proto(id: DeviceIdentifier) -> DeviceProto {
1485         let manager = get_manager();
1486         let devices = manager.devices.read().unwrap();
1487         let device = devices.get(&id).expect("could not find test bluetooth beacon device");
1488 
1489         let device_proto = device.get();
1490         assert!(device_proto.is_ok(), "{}", device_proto.unwrap_err());
1491 
1492         device_proto.unwrap()
1493     }
1494 
1495     #[test]
test_create_device_succeeds()1496     fn test_create_device_succeeds() {
1497         logger_setup();
1498 
1499         let request = get_test_create_device_request(Some(format!(
1500             "bob-the-beacon-{:?}",
1501             thread::current().id()
1502         )));
1503 
1504         let id = create_device(&print_to_string(&request).unwrap());
1505         assert!(id.is_ok(), "{}", id.unwrap_err());
1506         let id = id.unwrap();
1507 
1508         let device_proto = get_device_proto(id);
1509         assert_eq!(request.device.name, device_proto.name);
1510         assert_eq!(1, device_proto.chips.len());
1511         assert_eq!(request.device.chips[0].name, device_proto.chips[0].name);
1512     }
1513 
1514     #[test]
test_create_chipless_device_fails()1515     fn test_create_chipless_device_fails() {
1516         logger_setup();
1517 
1518         let request = CreateDeviceRequest {
1519             device: MessageField::some(ProtoDeviceCreate { ..Default::default() }),
1520             ..Default::default()
1521         };
1522 
1523         let id = create_device(&print_to_string(&request).unwrap());
1524         assert!(id.is_err(), "{}", id.unwrap());
1525     }
1526 
1527     #[test]
test_create_radioless_device_fails()1528     fn test_create_radioless_device_fails() {
1529         logger_setup();
1530 
1531         let request = CreateDeviceRequest {
1532             device: MessageField::some(ProtoDeviceCreate {
1533                 chips: vec![ProtoChipCreate::default()],
1534                 ..Default::default()
1535             }),
1536             ..Default::default()
1537         };
1538 
1539         let id = create_device(&print_to_string(&request).unwrap());
1540         assert!(id.is_err(), "{}", id.unwrap());
1541     }
1542 
1543     #[test]
test_get_beacon_device()1544     fn test_get_beacon_device() {
1545         logger_setup();
1546 
1547         let request = get_test_create_device_request(Some(format!(
1548             "bob-the-beacon-{:?}",
1549             thread::current().id()
1550         )));
1551 
1552         let id = create_device(&print_to_string(&request).unwrap());
1553         assert!(id.is_ok(), "{}", id.unwrap_err());
1554         let id = id.unwrap();
1555 
1556         let device_proto = get_device_proto(id);
1557         assert_eq!(1, device_proto.chips.len());
1558         assert!(device_proto.chips[0].chip.is_some());
1559         assert!(matches!(device_proto.chips[0].chip, Some(Chip::BleBeacon(_))));
1560     }
1561 
1562     #[test]
test_create_device_default_name()1563     fn test_create_device_default_name() {
1564         logger_setup();
1565 
1566         let request = get_test_create_device_request(None);
1567 
1568         let id = create_device(&print_to_string(&request).unwrap());
1569         assert!(id.is_ok(), "{}", id.unwrap_err());
1570         let id = id.unwrap();
1571 
1572         let device_proto = get_device_proto(id);
1573         assert_eq!(format!("device-{id}"), device_proto.name);
1574     }
1575 
1576     #[test]
test_create_existing_device_fails()1577     fn test_create_existing_device_fails() {
1578         logger_setup();
1579 
1580         let request = get_test_create_device_request(Some(format!(
1581             "existing-device-{:?}",
1582             thread::current().id()
1583         )));
1584 
1585         let request_json = print_to_string(&request).unwrap();
1586 
1587         let id = create_device(&request_json);
1588         assert!(id.is_ok(), "{}", id.unwrap_err());
1589 
1590         // Attempt to create the device again. This should fail because the devices have the same name.
1591         let id = create_device(&request_json);
1592         assert!(id.is_err());
1593     }
1594 
1595     #[test]
test_patch_beacon_device()1596     fn test_patch_beacon_device() {
1597         logger_setup();
1598 
1599         let request = get_test_create_device_request(Some(format!(
1600             "bob-the-beacon-{:?}",
1601             thread::current().id()
1602         )));
1603 
1604         let id = create_device(&print_to_string(&request).unwrap());
1605         assert!(id.is_ok(), "{}", id.unwrap_err());
1606         let id = id.unwrap();
1607 
1608         let manager = get_manager();
1609         let mut devices = manager.devices.write().unwrap();
1610 
1611         let device = devices.get_mut(&id).expect("could not find test bluetooth beacon device");
1612 
1613         let device_proto = device.get();
1614         assert!(device_proto.is_ok(), "{}", device_proto.unwrap_err());
1615         let device_proto = device_proto.unwrap();
1616 
1617         let patch_result = device.patch(&DeviceProto {
1618             name: device_proto.name.clone(),
1619             id: id.0,
1620             chips: vec![ChipProto {
1621                 name: request.device.chips[0].name.clone(),
1622                 kind: EnumOrUnknown::new(ProtoChipKind::BLUETOOTH_BEACON),
1623                 chip: Some(Chip::BleBeacon(BleBeacon {
1624                     bt: MessageField::some(Default::default()),
1625                     ..Default::default()
1626                 })),
1627                 ..Default::default()
1628             }],
1629             ..Default::default()
1630         });
1631         assert!(patch_result.is_ok(), "{}", patch_result.unwrap_err());
1632 
1633         let patched_device = device.get();
1634         assert!(patched_device.is_ok(), "{}", patched_device.unwrap_err());
1635         let patched_device = patched_device.unwrap();
1636         assert_eq!(1, patched_device.chips.len());
1637         assert!(matches!(patched_device.chips[0].chip, Some(Chip::BleBeacon(_))));
1638     }
1639 
1640     #[test]
test_remove_beacon_device_succeeds()1641     fn test_remove_beacon_device_succeeds() {
1642         logger_setup();
1643 
1644         let create_request = get_test_create_device_request(None);
1645         let device_id = create_device(&print_to_string(&create_request).unwrap());
1646         assert!(device_id.is_ok(), "{}", device_id.unwrap_err());
1647 
1648         let device_id = device_id.unwrap();
1649         let chip_id = {
1650             let manager = get_manager();
1651             let devices = manager.devices.read().unwrap();
1652             let device = devices.get(&device_id).unwrap();
1653             let chips = device.chips.read().unwrap();
1654             chips.first_key_value().map(|(id, _)| *id).unwrap()
1655         };
1656 
1657         let delete_request = DeleteChipRequest { id: chip_id.0, ..Default::default() };
1658         let delete_result = delete_chip(&print_to_string(&delete_request).unwrap());
1659         assert!(delete_result.is_ok(), "{}", delete_result.unwrap_err());
1660 
1661         assert!(!get_manager().devices.read().unwrap().contains_key(&device_id))
1662     }
1663 
1664     #[test]
test_remove_beacon_device_fails()1665     fn test_remove_beacon_device_fails() {
1666         logger_setup();
1667 
1668         let create_request = get_test_create_device_request(None);
1669         let device_id = create_device(&print_to_string(&create_request).unwrap());
1670         assert!(device_id.is_ok(), "{}", device_id.unwrap_err());
1671 
1672         let device_id = device_id.unwrap();
1673         let chip_id = get_manager()
1674             .devices
1675             .read()
1676             .unwrap()
1677             .get(&device_id)
1678             .unwrap()
1679             .chips
1680             .read()
1681             .unwrap()
1682             .first_key_value()
1683             .map(|(id, _)| *id)
1684             .unwrap();
1685 
1686         let delete_request = DeleteChipRequest { id: chip_id.0, ..Default::default() };
1687         let delete_result = delete_chip(&print_to_string(&delete_request).unwrap());
1688         assert!(delete_result.is_ok(), "{}", delete_result.unwrap_err());
1689 
1690         let delete_result = delete_chip(&print_to_string(&delete_request).unwrap());
1691         assert!(delete_result.is_err());
1692     }
1693 
1694     #[test]
test_check_device_event_initial_timeout()1695     fn test_check_device_event_initial_timeout() {
1696         logger_setup();
1697 
1698         let mut events = events::test::new();
1699         let events_rx = events::test::subscribe(&mut events);
1700         assert_eq!(
1701             check_device_event(&events_rx, Some(std::time::Instant::now())),
1702             DeviceWaitStatus::Timeout
1703         );
1704     }
1705 
1706     #[test]
test_check_device_event_last_device_removed()1707     fn test_check_device_event_last_device_removed() {
1708         logger_setup();
1709 
1710         let mut events = events::test::new();
1711         let events_rx = events::test::subscribe(&mut events);
1712         events::test::publish(
1713             &mut events,
1714             Event::ChipRemoved(ChipRemoved {
1715                 remaining_nonbuiltin_devices: 0,
1716                 ..Default::default()
1717             }),
1718         );
1719         assert_eq!(check_device_event(&events_rx, None), DeviceWaitStatus::LastDeviceRemoved);
1720     }
1721 
1722     #[test]
test_check_device_event_device_chip_added()1723     fn test_check_device_event_device_chip_added() {
1724         logger_setup();
1725 
1726         let mut events = events::test::new();
1727         let events_rx = events::test::subscribe(&mut events);
1728         events::test::publish(
1729             &mut events,
1730             Event::DeviceAdded(DeviceAdded {
1731                 id: DeviceIdentifier(0),
1732                 name: "".to_string(),
1733                 builtin: false,
1734             }),
1735         );
1736         assert_eq!(check_device_event(&events_rx, None), DeviceWaitStatus::DeviceAdded);
1737         events::test::publish(
1738             &mut events,
1739             Event::ChipAdded(ChipAdded { builtin: false, ..Default::default() }),
1740         );
1741         assert_eq!(check_device_event(&events_rx, None), DeviceWaitStatus::DeviceAdded);
1742     }
1743 
1744     #[test]
test_check_device_event_ignore_event()1745     fn test_check_device_event_ignore_event() {
1746         logger_setup();
1747 
1748         let mut events = events::test::new();
1749         let events_rx = events::test::subscribe(&mut events);
1750         events::test::publish(
1751             &mut events,
1752             Event::DevicePatched(DevicePatched { id: DeviceIdentifier(0), name: "".to_string() }),
1753         );
1754         assert_eq!(check_device_event(&events_rx, None), DeviceWaitStatus::IgnoreEvent);
1755         events::test::publish(
1756             &mut events,
1757             Event::ChipRemoved(ChipRemoved {
1758                 remaining_nonbuiltin_devices: 1,
1759                 ..Default::default()
1760             }),
1761         );
1762         assert_eq!(check_device_event(&events_rx, None), DeviceWaitStatus::IgnoreEvent);
1763     }
1764 
1765     #[test]
test_check_device_event_ignore_chip_added_for_builtin()1766     fn test_check_device_event_ignore_chip_added_for_builtin() {
1767         logger_setup();
1768 
1769         let mut events = events::test::new();
1770         let events_rx = events::test::subscribe(&mut events);
1771         events::test::publish(
1772             &mut events,
1773             Event::ChipAdded(ChipAdded { builtin: true, ..Default::default() }),
1774         );
1775         assert_eq!(check_device_event(&events_rx, None), DeviceWaitStatus::IgnoreEvent);
1776     }
1777 }
1778