| // Copyright 2023 Google LLC |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use bt_common::debug_command::CommandRunner; |
| use bt_common::debug_command::CommandSet; |
| use bt_common::gen_commandset; |
| |
| use bt_gatt::{ |
| client::{PeerService, PeerServiceHandle}, |
| Client, |
| }; |
| |
| use crate::*; |
| |
| gen_commandset! { |
| PacsCmd { |
| Print = ("print", [], [], "Print the current PACS status"), |
| } |
| } |
| |
| pub struct PacsDebug<T: bt_gatt::GattTypes> { |
| client: T::Client, |
| } |
| |
| impl<T: bt_gatt::GattTypes> PacsDebug<T> { |
| pub fn new(client: T::Client) -> Self { |
| Self { client } |
| } |
| } |
| |
| impl<T: bt_gatt::GattTypes> CommandRunner for PacsDebug<T> { |
| type Set = PacsCmd; |
| type Error = bt_gatt::types::Error; |
| type Event = (); |
| |
| fn run_command( |
| &self, |
| _cmd: Self::Set, |
| _args: Vec<String>, |
| ) -> impl futures::Future<Output = Result<(), Self::Error>> { |
| async { |
| // Since there is only one command, Print, we just print |
| // all the characteristics that are at the remote PACS server. |
| let handles = self.client.find_service(PACS_UUID).await?; |
| for handle in handles { |
| let service = handle.connect().await?; |
| |
| let chrs = service.discover_characteristics(None).await?; |
| for chr in chrs { |
| match chr.uuid { |
| SourcePac::UUID => { |
| let source_pac = SourcePac::try_read::<T>(chr, &service).await?; |
| println!("{source_pac:?}"); |
| } |
| SinkPac::UUID => { |
| let sink_pac = SinkPac::try_read::<T>(chr, &service).await?; |
| println!("{sink_pac:?}"); |
| } |
| SinkAudioLocations::UUID => { |
| let locations = |
| SinkAudioLocations::try_read::<T>(chr, &service).await?; |
| println!("{locations:?}"); |
| } |
| SourceAudioLocations::UUID => { |
| let locations = |
| SourceAudioLocations::try_read::<T>(chr, &service).await?; |
| println!("{locations:?}"); |
| } |
| AvailableAudioContexts::UUID => { |
| let contexts = |
| AvailableAudioContexts::try_read::<T>(chr, &service).await?; |
| println!("{contexts:?}"); |
| } |
| SupportedAudioContexts::UUID => { |
| let contexts = |
| SupportedAudioContexts::try_read::<T>(chr, &service).await?; |
| println!("{contexts:?}"); |
| } |
| _x => println!("Unrecognized Chr {}", chr.uuid.recognize()), |
| } |
| } |
| } |
| Ok::<(), bt_gatt::types::Error>(()) |
| } |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| use bt_common::debug_command::CliCommand; |
| use bt_gatt::test_utils::{FakeClient, FakeTypes}; |
| use log::LevelFilter; |
| |
| fn setup_fake_pacs_debug() -> PacsDebug<FakeTypes> { |
| PacsDebug::new(FakeClient::new()) |
| } |
| |
| #[test] |
| fn verbosity_controls_and_flags() { |
| let debug = setup_fake_pacs_debug(); |
| |
| log::set_max_level(LevelFilter::Info); |
| |
| // Verbose with no args displays current verbosity without changing it |
| let res = futures::executor::block_on(debug.run(CliCommand::Verbose, vec![])); |
| assert!(res.is_ok()); |
| assert_eq!(log::max_level(), LevelFilter::Info); |
| |
| // Update verbosity to Debug |
| let res = |
| futures::executor::block_on(debug.run(CliCommand::Verbose, vec!["debug".to_string()])); |
| assert!(res.is_ok()); |
| assert_eq!(log::max_level(), LevelFilter::Debug); |
| |
| // Update verbosity to Trace |
| let res = |
| futures::executor::block_on(debug.run(CliCommand::Verbose, vec!["trace".to_string()])); |
| assert!(res.is_ok()); |
| assert_eq!(log::max_level(), LevelFilter::Trace); |
| |
| // Update verbosity to Off |
| let res = |
| futures::executor::block_on(debug.run(CliCommand::Verbose, vec!["off".to_string()])); |
| assert!(res.is_ok()); |
| assert_eq!(log::max_level(), LevelFilter::Off); |
| |
| // Invalid level does not change verbosity |
| let res = futures::executor::block_on( |
| debug.run(CliCommand::Verbose, vec!["invalid_level".to_string()]), |
| ); |
| assert!(res.is_ok()); |
| assert_eq!(log::max_level(), LevelFilter::Off); |
| |
| // Reset back to Info for scoped flags testing |
| log::set_max_level(LevelFilter::Info); |
| |
| // Executing Print with -v flag: |
| // 1. debug.run() strips the flag before run_command. |
| // 2. Temporarily elevates max log level during execution. |
| // 3. Executes successfully. |
| let res = futures::executor::block_on(debug.run(PacsCmd::Print, vec!["-v".to_string()])); |
| assert!(res.is_ok()); |
| |
| // Persistent verbosity remains Info after scoped execution completes |
| assert_eq!(log::max_level(), LevelFilter::Info); |
| |
| // Executing Print with -vv flag elevates to Trace |
| let res = futures::executor::block_on(debug.run(PacsCmd::Print, vec!["-vv".to_string()])); |
| assert!(res.is_ok()); |
| assert_eq!(log::max_level(), LevelFilter::Info); |
| } |
| } |