1*cf78ab8cSAndroid Build Coastguard Worker // Copyright 2022 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 //! Command Line Interface for Netsim
16*cf78ab8cSAndroid Build Coastguard Worker
17*cf78ab8cSAndroid Build Coastguard Worker mod args;
18*cf78ab8cSAndroid Build Coastguard Worker mod browser;
19*cf78ab8cSAndroid Build Coastguard Worker mod display;
20*cf78ab8cSAndroid Build Coastguard Worker mod ffi;
21*cf78ab8cSAndroid Build Coastguard Worker mod file_handler;
22*cf78ab8cSAndroid Build Coastguard Worker mod requests;
23*cf78ab8cSAndroid Build Coastguard Worker mod response;
24*cf78ab8cSAndroid Build Coastguard Worker
25*cf78ab8cSAndroid Build Coastguard Worker use netsim_common::util::os_utils::{get_instance, get_server_address};
26*cf78ab8cSAndroid Build Coastguard Worker use netsim_proto::frontend::{DeleteChipRequest, ListDeviceResponse};
27*cf78ab8cSAndroid Build Coastguard Worker use protobuf::Message;
28*cf78ab8cSAndroid Build Coastguard Worker use std::env;
29*cf78ab8cSAndroid Build Coastguard Worker use std::fs::File;
30*cf78ab8cSAndroid Build Coastguard Worker use std::path::PathBuf;
31*cf78ab8cSAndroid Build Coastguard Worker use tracing::error;
32*cf78ab8cSAndroid Build Coastguard Worker
33*cf78ab8cSAndroid Build Coastguard Worker use crate::ffi::frontend_client_ffi::{
34*cf78ab8cSAndroid Build Coastguard Worker new_frontend_client, ClientResult, FrontendClient, GrpcMethod,
35*cf78ab8cSAndroid Build Coastguard Worker };
36*cf78ab8cSAndroid Build Coastguard Worker use crate::ffi::ClientResponseReader;
37*cf78ab8cSAndroid Build Coastguard Worker use args::{BinaryProtobuf, GetCapture, NetsimArgs};
38*cf78ab8cSAndroid Build Coastguard Worker use clap::Parser;
39*cf78ab8cSAndroid Build Coastguard Worker use cxx::{let_cxx_string, UniquePtr};
40*cf78ab8cSAndroid Build Coastguard Worker use file_handler::FileHandler;
41*cf78ab8cSAndroid Build Coastguard Worker use netsim_common::util::netsim_logger;
42*cf78ab8cSAndroid Build Coastguard Worker
43*cf78ab8cSAndroid Build Coastguard Worker // helper function to process streaming Grpc request
perform_streaming_request( client: &cxx::UniquePtr<FrontendClient>, cmd: &mut GetCapture, req: &BinaryProtobuf, filename: &str, ) -> UniquePtr<ClientResult>44*cf78ab8cSAndroid Build Coastguard Worker fn perform_streaming_request(
45*cf78ab8cSAndroid Build Coastguard Worker client: &cxx::UniquePtr<FrontendClient>,
46*cf78ab8cSAndroid Build Coastguard Worker cmd: &mut GetCapture,
47*cf78ab8cSAndroid Build Coastguard Worker req: &BinaryProtobuf,
48*cf78ab8cSAndroid Build Coastguard Worker filename: &str,
49*cf78ab8cSAndroid Build Coastguard Worker ) -> UniquePtr<ClientResult> {
50*cf78ab8cSAndroid Build Coastguard Worker let dir = if cmd.location.is_some() {
51*cf78ab8cSAndroid Build Coastguard Worker PathBuf::from(cmd.location.to_owned().unwrap())
52*cf78ab8cSAndroid Build Coastguard Worker } else {
53*cf78ab8cSAndroid Build Coastguard Worker env::current_dir().unwrap()
54*cf78ab8cSAndroid Build Coastguard Worker };
55*cf78ab8cSAndroid Build Coastguard Worker let output_file = dir.join(filename);
56*cf78ab8cSAndroid Build Coastguard Worker cmd.current_file = output_file.display().to_string();
57*cf78ab8cSAndroid Build Coastguard Worker client.get_capture(
58*cf78ab8cSAndroid Build Coastguard Worker req,
59*cf78ab8cSAndroid Build Coastguard Worker &ClientResponseReader {
60*cf78ab8cSAndroid Build Coastguard Worker handler: Box::new(FileHandler {
61*cf78ab8cSAndroid Build Coastguard Worker file: File::create(&output_file).unwrap_or_else(|_| {
62*cf78ab8cSAndroid Build Coastguard Worker panic!("Failed to create file: {}", &output_file.display())
63*cf78ab8cSAndroid Build Coastguard Worker }),
64*cf78ab8cSAndroid Build Coastguard Worker path: output_file,
65*cf78ab8cSAndroid Build Coastguard Worker }),
66*cf78ab8cSAndroid Build Coastguard Worker },
67*cf78ab8cSAndroid Build Coastguard Worker )
68*cf78ab8cSAndroid Build Coastguard Worker }
69*cf78ab8cSAndroid Build Coastguard Worker
70*cf78ab8cSAndroid Build Coastguard Worker /// helper function to send the Grpc request(s) and handle the response(s) per the given command
perform_command( command: &mut args::Command, client: cxx::UniquePtr<FrontendClient>, grpc_method: GrpcMethod, verbose: bool, ) -> Result<(), String>71*cf78ab8cSAndroid Build Coastguard Worker fn perform_command(
72*cf78ab8cSAndroid Build Coastguard Worker command: &mut args::Command,
73*cf78ab8cSAndroid Build Coastguard Worker client: cxx::UniquePtr<FrontendClient>,
74*cf78ab8cSAndroid Build Coastguard Worker grpc_method: GrpcMethod,
75*cf78ab8cSAndroid Build Coastguard Worker verbose: bool,
76*cf78ab8cSAndroid Build Coastguard Worker ) -> Result<(), String> {
77*cf78ab8cSAndroid Build Coastguard Worker // Get command's gRPC request(s)
78*cf78ab8cSAndroid Build Coastguard Worker let requests = match command {
79*cf78ab8cSAndroid Build Coastguard Worker args::Command::Capture(args::Capture::Patch(_) | args::Capture::Get(_)) => {
80*cf78ab8cSAndroid Build Coastguard Worker command.get_requests(&client)
81*cf78ab8cSAndroid Build Coastguard Worker }
82*cf78ab8cSAndroid Build Coastguard Worker args::Command::Beacon(args::Beacon::Remove(_)) => {
83*cf78ab8cSAndroid Build Coastguard Worker vec![args::Command::Devices(args::Devices { continuous: false }).get_request_bytes()]
84*cf78ab8cSAndroid Build Coastguard Worker }
85*cf78ab8cSAndroid Build Coastguard Worker _ => vec![command.get_request_bytes()],
86*cf78ab8cSAndroid Build Coastguard Worker };
87*cf78ab8cSAndroid Build Coastguard Worker let mut process_error = false;
88*cf78ab8cSAndroid Build Coastguard Worker // Process each request
89*cf78ab8cSAndroid Build Coastguard Worker for (i, req) in requests.iter().enumerate() {
90*cf78ab8cSAndroid Build Coastguard Worker let result = match command {
91*cf78ab8cSAndroid Build Coastguard Worker // Continuous option sends the gRPC call every second
92*cf78ab8cSAndroid Build Coastguard Worker args::Command::Devices(ref cmd) if cmd.continuous => {
93*cf78ab8cSAndroid Build Coastguard Worker continuous_perform_command(command, &client, grpc_method, req, verbose)?
94*cf78ab8cSAndroid Build Coastguard Worker }
95*cf78ab8cSAndroid Build Coastguard Worker args::Command::Capture(args::Capture::List(ref cmd)) if cmd.continuous => {
96*cf78ab8cSAndroid Build Coastguard Worker continuous_perform_command(command, &client, grpc_method, req, verbose)?
97*cf78ab8cSAndroid Build Coastguard Worker }
98*cf78ab8cSAndroid Build Coastguard Worker // Get Capture use streaming gRPC reader request
99*cf78ab8cSAndroid Build Coastguard Worker args::Command::Capture(args::Capture::Get(ref mut cmd)) => {
100*cf78ab8cSAndroid Build Coastguard Worker perform_streaming_request(&client, cmd, req, &cmd.filenames[i].to_owned())
101*cf78ab8cSAndroid Build Coastguard Worker }
102*cf78ab8cSAndroid Build Coastguard Worker args::Command::Beacon(args::Beacon::Remove(ref cmd)) => {
103*cf78ab8cSAndroid Build Coastguard Worker let devices = client.send_grpc(&GrpcMethod::ListDevice, req);
104*cf78ab8cSAndroid Build Coastguard Worker let id = find_id_for_remove(devices.byte_vec().as_slice(), cmd)?;
105*cf78ab8cSAndroid Build Coastguard Worker let req = &DeleteChipRequest { id, ..Default::default() }
106*cf78ab8cSAndroid Build Coastguard Worker .write_to_bytes()
107*cf78ab8cSAndroid Build Coastguard Worker .map_err(|err| format!("{err}"))?;
108*cf78ab8cSAndroid Build Coastguard Worker
109*cf78ab8cSAndroid Build Coastguard Worker client.send_grpc(&grpc_method, req)
110*cf78ab8cSAndroid Build Coastguard Worker }
111*cf78ab8cSAndroid Build Coastguard Worker // All other commands use a single gRPC call
112*cf78ab8cSAndroid Build Coastguard Worker _ => client.send_grpc(&grpc_method, req),
113*cf78ab8cSAndroid Build Coastguard Worker };
114*cf78ab8cSAndroid Build Coastguard Worker if let Err(e) = process_result(command, result, verbose) {
115*cf78ab8cSAndroid Build Coastguard Worker error!("{}", e);
116*cf78ab8cSAndroid Build Coastguard Worker process_error = true;
117*cf78ab8cSAndroid Build Coastguard Worker };
118*cf78ab8cSAndroid Build Coastguard Worker }
119*cf78ab8cSAndroid Build Coastguard Worker if process_error {
120*cf78ab8cSAndroid Build Coastguard Worker return Err("Not all requests were processed successfully.".to_string());
121*cf78ab8cSAndroid Build Coastguard Worker }
122*cf78ab8cSAndroid Build Coastguard Worker Ok(())
123*cf78ab8cSAndroid Build Coastguard Worker }
124*cf78ab8cSAndroid Build Coastguard Worker
find_id_for_remove(response: &[u8], cmd: &args::BeaconRemove) -> Result<u32, String>125*cf78ab8cSAndroid Build Coastguard Worker fn find_id_for_remove(response: &[u8], cmd: &args::BeaconRemove) -> Result<u32, String> {
126*cf78ab8cSAndroid Build Coastguard Worker let devices = ListDeviceResponse::parse_from_bytes(response).unwrap().devices;
127*cf78ab8cSAndroid Build Coastguard Worker let id = devices
128*cf78ab8cSAndroid Build Coastguard Worker .iter()
129*cf78ab8cSAndroid Build Coastguard Worker .find(|device| device.name == cmd.device_name)
130*cf78ab8cSAndroid Build Coastguard Worker .and_then(|device| cmd.chip_name.as_ref().map_or(
131*cf78ab8cSAndroid Build Coastguard Worker (device.chips.len() == 1).then_some(&device.chips[0]),
132*cf78ab8cSAndroid Build Coastguard Worker |chip_name| device.chips.iter().find(|chip| &chip.name == chip_name)
133*cf78ab8cSAndroid Build Coastguard Worker ))
134*cf78ab8cSAndroid Build Coastguard Worker .ok_or(cmd.chip_name.as_ref().map_or(
135*cf78ab8cSAndroid Build Coastguard Worker format!("failed to delete chip: device '{}' has multiple possible candidates, please specify a chip name", cmd.device_name),
136*cf78ab8cSAndroid Build Coastguard Worker |chip_name| format!("failed to delete chip: could not find chip '{}' on device '{}'", chip_name, cmd.device_name))
137*cf78ab8cSAndroid Build Coastguard Worker )?
138*cf78ab8cSAndroid Build Coastguard Worker .id;
139*cf78ab8cSAndroid Build Coastguard Worker
140*cf78ab8cSAndroid Build Coastguard Worker Ok(id)
141*cf78ab8cSAndroid Build Coastguard Worker }
142*cf78ab8cSAndroid Build Coastguard Worker
143*cf78ab8cSAndroid Build Coastguard Worker /// Check and handle the gRPC call result
continuous_perform_command( command: &args::Command, client: &cxx::UniquePtr<FrontendClient>, grpc_method: GrpcMethod, request: &Vec<u8>, verbose: bool, ) -> Result<UniquePtr<ClientResult>, String>144*cf78ab8cSAndroid Build Coastguard Worker fn continuous_perform_command(
145*cf78ab8cSAndroid Build Coastguard Worker command: &args::Command,
146*cf78ab8cSAndroid Build Coastguard Worker client: &cxx::UniquePtr<FrontendClient>,
147*cf78ab8cSAndroid Build Coastguard Worker grpc_method: GrpcMethod,
148*cf78ab8cSAndroid Build Coastguard Worker request: &Vec<u8>,
149*cf78ab8cSAndroid Build Coastguard Worker verbose: bool,
150*cf78ab8cSAndroid Build Coastguard Worker ) -> Result<UniquePtr<ClientResult>, String> {
151*cf78ab8cSAndroid Build Coastguard Worker loop {
152*cf78ab8cSAndroid Build Coastguard Worker process_result(command, client.send_grpc(&grpc_method, request), verbose)?;
153*cf78ab8cSAndroid Build Coastguard Worker std::thread::sleep(std::time::Duration::from_secs(1));
154*cf78ab8cSAndroid Build Coastguard Worker }
155*cf78ab8cSAndroid Build Coastguard Worker }
156*cf78ab8cSAndroid Build Coastguard Worker /// Check and handle the gRPC call result
process_result( command: &args::Command, result: UniquePtr<ClientResult>, verbose: bool, ) -> Result<(), String>157*cf78ab8cSAndroid Build Coastguard Worker fn process_result(
158*cf78ab8cSAndroid Build Coastguard Worker command: &args::Command,
159*cf78ab8cSAndroid Build Coastguard Worker result: UniquePtr<ClientResult>,
160*cf78ab8cSAndroid Build Coastguard Worker verbose: bool,
161*cf78ab8cSAndroid Build Coastguard Worker ) -> Result<(), String> {
162*cf78ab8cSAndroid Build Coastguard Worker if result.is_ok() {
163*cf78ab8cSAndroid Build Coastguard Worker command.print_response(result.byte_vec().as_slice(), verbose);
164*cf78ab8cSAndroid Build Coastguard Worker } else {
165*cf78ab8cSAndroid Build Coastguard Worker return Err(format!("Grpc call error: {}", result.err()));
166*cf78ab8cSAndroid Build Coastguard Worker }
167*cf78ab8cSAndroid Build Coastguard Worker Ok(())
168*cf78ab8cSAndroid Build Coastguard Worker }
169*cf78ab8cSAndroid Build Coastguard Worker #[no_mangle]
170*cf78ab8cSAndroid Build Coastguard Worker /// main Rust netsim CLI function to be called by C wrapper netsim.cc
rust_main()171*cf78ab8cSAndroid Build Coastguard Worker pub extern "C" fn rust_main() {
172*cf78ab8cSAndroid Build Coastguard Worker let mut args = NetsimArgs::parse();
173*cf78ab8cSAndroid Build Coastguard Worker netsim_logger::init("netsim", args.verbose);
174*cf78ab8cSAndroid Build Coastguard Worker if matches!(args.command, args::Command::Gui) {
175*cf78ab8cSAndroid Build Coastguard Worker println!("Opening netsim web UI on default web browser");
176*cf78ab8cSAndroid Build Coastguard Worker browser::open("http://localhost:7681/");
177*cf78ab8cSAndroid Build Coastguard Worker return;
178*cf78ab8cSAndroid Build Coastguard Worker } else if matches!(args.command, args::Command::Artifact) {
179*cf78ab8cSAndroid Build Coastguard Worker let artifact_dir = netsim_common::system::netsimd_temp_dir();
180*cf78ab8cSAndroid Build Coastguard Worker println!("netsim artifact directory: {}", artifact_dir.display());
181*cf78ab8cSAndroid Build Coastguard Worker browser::open(artifact_dir);
182*cf78ab8cSAndroid Build Coastguard Worker return;
183*cf78ab8cSAndroid Build Coastguard Worker } else if matches!(args.command, args::Command::Bumble) {
184*cf78ab8cSAndroid Build Coastguard Worker println!("Opening Bumble Hive on default web browser");
185*cf78ab8cSAndroid Build Coastguard Worker browser::open("https://google.github.io/bumble/hive/index.html");
186*cf78ab8cSAndroid Build Coastguard Worker return;
187*cf78ab8cSAndroid Build Coastguard Worker }
188*cf78ab8cSAndroid Build Coastguard Worker let grpc_method = args.command.grpc_method();
189*cf78ab8cSAndroid Build Coastguard Worker let server = match (args.vsock, args.port) {
190*cf78ab8cSAndroid Build Coastguard Worker (Some(vsock), _) => format!("vsock:{vsock}"),
191*cf78ab8cSAndroid Build Coastguard Worker (_, Some(port)) => format!("localhost:{port}"),
192*cf78ab8cSAndroid Build Coastguard Worker _ => get_server_address(get_instance(args.instance)).unwrap_or_default(),
193*cf78ab8cSAndroid Build Coastguard Worker };
194*cf78ab8cSAndroid Build Coastguard Worker let_cxx_string!(server = server);
195*cf78ab8cSAndroid Build Coastguard Worker let client = new_frontend_client(&server);
196*cf78ab8cSAndroid Build Coastguard Worker if client.is_null() {
197*cf78ab8cSAndroid Build Coastguard Worker if !server.is_empty() {
198*cf78ab8cSAndroid Build Coastguard Worker error!("Unable to create frontend client. Please ensure netsimd is running and listening on {server:?}.");
199*cf78ab8cSAndroid Build Coastguard Worker } else {
200*cf78ab8cSAndroid Build Coastguard Worker error!("Unable to create frontend client. Please ensure netsimd is running.");
201*cf78ab8cSAndroid Build Coastguard Worker }
202*cf78ab8cSAndroid Build Coastguard Worker return;
203*cf78ab8cSAndroid Build Coastguard Worker }
204*cf78ab8cSAndroid Build Coastguard Worker if let Err(e) = perform_command(&mut args.command, client, grpc_method, args.verbose) {
205*cf78ab8cSAndroid Build Coastguard Worker error!("{e}");
206*cf78ab8cSAndroid Build Coastguard Worker }
207*cf78ab8cSAndroid Build Coastguard Worker }
208*cf78ab8cSAndroid Build Coastguard Worker
209*cf78ab8cSAndroid Build Coastguard Worker #[cfg(test)]
210*cf78ab8cSAndroid Build Coastguard Worker mod tests {
211*cf78ab8cSAndroid Build Coastguard Worker use crate::args::BeaconRemove;
212*cf78ab8cSAndroid Build Coastguard Worker use netsim_proto::{
213*cf78ab8cSAndroid Build Coastguard Worker frontend::ListDeviceResponse,
214*cf78ab8cSAndroid Build Coastguard Worker model::{Chip as ChipProto, Device as DeviceProto},
215*cf78ab8cSAndroid Build Coastguard Worker };
216*cf78ab8cSAndroid Build Coastguard Worker use protobuf::Message;
217*cf78ab8cSAndroid Build Coastguard Worker
218*cf78ab8cSAndroid Build Coastguard Worker use crate::find_id_for_remove;
219*cf78ab8cSAndroid Build Coastguard Worker
220*cf78ab8cSAndroid Build Coastguard Worker #[test]
test_remove_device()221*cf78ab8cSAndroid Build Coastguard Worker fn test_remove_device() {
222*cf78ab8cSAndroid Build Coastguard Worker let device_name = String::from("a-device");
223*cf78ab8cSAndroid Build Coastguard Worker let chip_id = 7;
224*cf78ab8cSAndroid Build Coastguard Worker
225*cf78ab8cSAndroid Build Coastguard Worker let cmd = &BeaconRemove { device_name: device_name.clone(), chip_name: None };
226*cf78ab8cSAndroid Build Coastguard Worker
227*cf78ab8cSAndroid Build Coastguard Worker let response = ListDeviceResponse {
228*cf78ab8cSAndroid Build Coastguard Worker devices: vec![DeviceProto {
229*cf78ab8cSAndroid Build Coastguard Worker id: 0,
230*cf78ab8cSAndroid Build Coastguard Worker name: device_name,
231*cf78ab8cSAndroid Build Coastguard Worker chips: vec![ChipProto { id: chip_id, ..Default::default() }],
232*cf78ab8cSAndroid Build Coastguard Worker ..Default::default()
233*cf78ab8cSAndroid Build Coastguard Worker }],
234*cf78ab8cSAndroid Build Coastguard Worker ..Default::default()
235*cf78ab8cSAndroid Build Coastguard Worker };
236*cf78ab8cSAndroid Build Coastguard Worker
237*cf78ab8cSAndroid Build Coastguard Worker let id = find_id_for_remove(response.write_to_bytes().unwrap().as_slice(), cmd);
238*cf78ab8cSAndroid Build Coastguard Worker assert!(id.is_ok(), "{}", id.unwrap_err());
239*cf78ab8cSAndroid Build Coastguard Worker let id = id.unwrap();
240*cf78ab8cSAndroid Build Coastguard Worker
241*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(chip_id, id);
242*cf78ab8cSAndroid Build Coastguard Worker }
243*cf78ab8cSAndroid Build Coastguard Worker
244*cf78ab8cSAndroid Build Coastguard Worker #[test]
test_remove_chip()245*cf78ab8cSAndroid Build Coastguard Worker fn test_remove_chip() {
246*cf78ab8cSAndroid Build Coastguard Worker let device_name = String::from("a-device");
247*cf78ab8cSAndroid Build Coastguard Worker let chip_name = String::from("should-be-deleted");
248*cf78ab8cSAndroid Build Coastguard Worker let device_id = 4;
249*cf78ab8cSAndroid Build Coastguard Worker let chip_id = 2;
250*cf78ab8cSAndroid Build Coastguard Worker
251*cf78ab8cSAndroid Build Coastguard Worker let cmd =
252*cf78ab8cSAndroid Build Coastguard Worker &BeaconRemove { device_name: device_name.clone(), chip_name: Some(chip_name.clone()) };
253*cf78ab8cSAndroid Build Coastguard Worker
254*cf78ab8cSAndroid Build Coastguard Worker let response = ListDeviceResponse {
255*cf78ab8cSAndroid Build Coastguard Worker devices: vec![DeviceProto {
256*cf78ab8cSAndroid Build Coastguard Worker id: device_id,
257*cf78ab8cSAndroid Build Coastguard Worker name: device_name,
258*cf78ab8cSAndroid Build Coastguard Worker chips: vec![
259*cf78ab8cSAndroid Build Coastguard Worker ChipProto { id: chip_id, name: chip_name, ..Default::default() },
260*cf78ab8cSAndroid Build Coastguard Worker ChipProto {
261*cf78ab8cSAndroid Build Coastguard Worker id: chip_id + 1,
262*cf78ab8cSAndroid Build Coastguard Worker name: String::from("shouldnt-be-deleted"),
263*cf78ab8cSAndroid Build Coastguard Worker ..Default::default()
264*cf78ab8cSAndroid Build Coastguard Worker },
265*cf78ab8cSAndroid Build Coastguard Worker ],
266*cf78ab8cSAndroid Build Coastguard Worker ..Default::default()
267*cf78ab8cSAndroid Build Coastguard Worker }],
268*cf78ab8cSAndroid Build Coastguard Worker ..Default::default()
269*cf78ab8cSAndroid Build Coastguard Worker };
270*cf78ab8cSAndroid Build Coastguard Worker
271*cf78ab8cSAndroid Build Coastguard Worker let id = find_id_for_remove(response.write_to_bytes().unwrap().as_slice(), cmd);
272*cf78ab8cSAndroid Build Coastguard Worker assert!(id.is_ok(), "{}", id.unwrap_err());
273*cf78ab8cSAndroid Build Coastguard Worker let id = id.unwrap();
274*cf78ab8cSAndroid Build Coastguard Worker
275*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(chip_id, id);
276*cf78ab8cSAndroid Build Coastguard Worker }
277*cf78ab8cSAndroid Build Coastguard Worker
278*cf78ab8cSAndroid Build Coastguard Worker #[test]
test_remove_multiple_chips_fails()279*cf78ab8cSAndroid Build Coastguard Worker fn test_remove_multiple_chips_fails() {
280*cf78ab8cSAndroid Build Coastguard Worker let device_name = String::from("a-device");
281*cf78ab8cSAndroid Build Coastguard Worker let device_id = 3;
282*cf78ab8cSAndroid Build Coastguard Worker
283*cf78ab8cSAndroid Build Coastguard Worker let cmd = &BeaconRemove { device_name: device_name.clone(), chip_name: None };
284*cf78ab8cSAndroid Build Coastguard Worker
285*cf78ab8cSAndroid Build Coastguard Worker let response = ListDeviceResponse {
286*cf78ab8cSAndroid Build Coastguard Worker devices: vec![DeviceProto {
287*cf78ab8cSAndroid Build Coastguard Worker id: device_id,
288*cf78ab8cSAndroid Build Coastguard Worker name: device_name,
289*cf78ab8cSAndroid Build Coastguard Worker chips: vec![
290*cf78ab8cSAndroid Build Coastguard Worker ChipProto { id: 1, name: String::from("chip-1"), ..Default::default() },
291*cf78ab8cSAndroid Build Coastguard Worker ChipProto { id: 2, name: String::from("chip-2"), ..Default::default() },
292*cf78ab8cSAndroid Build Coastguard Worker ],
293*cf78ab8cSAndroid Build Coastguard Worker ..Default::default()
294*cf78ab8cSAndroid Build Coastguard Worker }],
295*cf78ab8cSAndroid Build Coastguard Worker ..Default::default()
296*cf78ab8cSAndroid Build Coastguard Worker };
297*cf78ab8cSAndroid Build Coastguard Worker
298*cf78ab8cSAndroid Build Coastguard Worker let id = find_id_for_remove(response.write_to_bytes().unwrap().as_slice(), cmd);
299*cf78ab8cSAndroid Build Coastguard Worker assert!(id.is_err());
300*cf78ab8cSAndroid Build Coastguard Worker }
301*cf78ab8cSAndroid Build Coastguard Worker
302*cf78ab8cSAndroid Build Coastguard Worker #[test]
test_remove_nonexistent_chip_fails()303*cf78ab8cSAndroid Build Coastguard Worker fn test_remove_nonexistent_chip_fails() {
304*cf78ab8cSAndroid Build Coastguard Worker let device_name = String::from("a-device");
305*cf78ab8cSAndroid Build Coastguard Worker let device_id = 1;
306*cf78ab8cSAndroid Build Coastguard Worker
307*cf78ab8cSAndroid Build Coastguard Worker let cmd = &BeaconRemove {
308*cf78ab8cSAndroid Build Coastguard Worker device_name: device_name.clone(),
309*cf78ab8cSAndroid Build Coastguard Worker chip_name: Some(String::from("nonexistent-chip")),
310*cf78ab8cSAndroid Build Coastguard Worker };
311*cf78ab8cSAndroid Build Coastguard Worker
312*cf78ab8cSAndroid Build Coastguard Worker let response = ListDeviceResponse {
313*cf78ab8cSAndroid Build Coastguard Worker devices: vec![DeviceProto {
314*cf78ab8cSAndroid Build Coastguard Worker id: device_id,
315*cf78ab8cSAndroid Build Coastguard Worker name: device_name,
316*cf78ab8cSAndroid Build Coastguard Worker chips: vec![ChipProto {
317*cf78ab8cSAndroid Build Coastguard Worker id: 1,
318*cf78ab8cSAndroid Build Coastguard Worker name: String::from("this-chip-exists"),
319*cf78ab8cSAndroid Build Coastguard Worker ..Default::default()
320*cf78ab8cSAndroid Build Coastguard Worker }],
321*cf78ab8cSAndroid Build Coastguard Worker ..Default::default()
322*cf78ab8cSAndroid Build Coastguard Worker }],
323*cf78ab8cSAndroid Build Coastguard Worker ..Default::default()
324*cf78ab8cSAndroid Build Coastguard Worker };
325*cf78ab8cSAndroid Build Coastguard Worker
326*cf78ab8cSAndroid Build Coastguard Worker let id = find_id_for_remove(response.write_to_bytes().unwrap().as_slice(), cmd);
327*cf78ab8cSAndroid Build Coastguard Worker assert!(id.is_err());
328*cf78ab8cSAndroid Build Coastguard Worker }
329*cf78ab8cSAndroid Build Coastguard Worker }
330