| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use { |
| crate::agent::restore_agent, |
| crate::audio::types::{ |
| AudioInfo, AudioInputInfo, AudioSettingSource, AudioStream, AudioStreamType, |
| }, |
| crate::audio::{create_default_modified_counters, default_audio_info}, |
| crate::base::SettingType, |
| crate::handler::device_storage::testing::InMemoryStorageFactory, |
| crate::handler::device_storage::DeviceStorage, |
| crate::input::common::MediaButtonsEventBuilder, |
| crate::tests::fakes::audio_core_service::{self, AudioCoreService}, |
| crate::tests::fakes::input_device_registry_service::InputDeviceRegistryService, |
| crate::tests::fakes::service_registry::ServiceRegistry, |
| crate::tests::fakes::sound_player_service::SoundPlayerService, |
| crate::tests::test_failure_utils::create_test_env_with_failures, |
| crate::AgentType, |
| crate::EnvironmentBuilder, |
| fidl::Error::ClientChannelClosed, |
| fidl_fuchsia_media::AudioRenderUsage, |
| fidl_fuchsia_settings::*, |
| fuchsia_component::server::NestedEnvironment, |
| fuchsia_zircon::Status, |
| futures::lock::Mutex, |
| matches::assert_matches, |
| std::sync::Arc, |
| }; |
| |
| const ENV_NAME: &str = "settings_service_audio_test_environment"; |
| |
| const CHANGED_VOLUME_LEVEL: f32 = 0.7; |
| const CHANGED_VOLUME_LEVEL_2: f32 = 0.95; |
| const CHANGED_VOLUME_MUTED: bool = true; |
| |
| const CHANGED_MEDIA_STREAM: AudioStream = AudioStream { |
| stream_type: AudioStreamType::Media, |
| source: AudioSettingSource::User, |
| user_volume_level: CHANGED_VOLUME_LEVEL, |
| user_volume_muted: CHANGED_VOLUME_MUTED, |
| }; |
| |
| const CHANGED_MEDIA_STREAM_2: AudioStream = AudioStream { |
| stream_type: AudioStreamType::Media, |
| source: AudioSettingSource::User, |
| user_volume_level: CHANGED_VOLUME_LEVEL_2, |
| user_volume_muted: CHANGED_VOLUME_MUTED, |
| }; |
| |
| const CHANGED_MEDIA_STREAM_SETTINGS: AudioStreamSettings = AudioStreamSettings { |
| stream: Some(fidl_fuchsia_media::AudioRenderUsage::Media), |
| source: Some(AudioStreamSettingSource::User), |
| user_volume: Some(Volume { |
| level: Some(CHANGED_VOLUME_LEVEL), |
| muted: Some(CHANGED_VOLUME_MUTED), |
| ..Volume::EMPTY |
| }), |
| ..AudioStreamSettings::EMPTY |
| }; |
| |
| const CHANGED_MEDIA_STREAM_SETTINGS_2: AudioStreamSettings = AudioStreamSettings { |
| stream: Some(fidl_fuchsia_media::AudioRenderUsage::Media), |
| source: Some(AudioStreamSettingSource::User), |
| user_volume: Some(Volume { |
| level: Some(CHANGED_VOLUME_LEVEL_2), |
| muted: Some(CHANGED_VOLUME_MUTED), |
| ..Volume::EMPTY |
| }), |
| ..AudioStreamSettings::EMPTY |
| }; |
| |
| /// Creates an environment that will fail on a get request. |
| async fn create_audio_test_env_with_failures( |
| storage_factory: Arc<InMemoryStorageFactory>, |
| ) -> AudioProxy { |
| create_test_env_with_failures(storage_factory, ENV_NAME, SettingType::Audio) |
| .await |
| .connect_to_service::<AudioMarker>() |
| .unwrap() |
| } |
| |
| // Used to store fake services for mocking dependencies and checking input/outputs. |
| // To add a new fake to these tests, add here, in create_services, and then use |
| // in your test. |
| struct FakeServices { |
| audio_core: Arc<Mutex<AudioCoreService>>, |
| input_device_registry: Arc<Mutex<InputDeviceRegistryService>>, |
| } |
| |
| fn get_default_stream(stream_type: AudioStreamType) -> AudioStream { |
| *default_audio_info() |
| .streams |
| .iter() |
| .find(|x| x.stream_type == stream_type) |
| .expect("contains stream") |
| } |
| |
| async fn set_volume(proxy: &AudioProxy, streams: Vec<AudioStreamSettings>) { |
| let mut audio_settings = AudioSettings::EMPTY; |
| audio_settings.streams = Some(streams); |
| proxy.set(audio_settings).await.expect("set completed").expect("set successful"); |
| } |
| |
| // Verifies that a stream equal to |stream| is inside of |settings|. |
| fn verify_audio_stream(settings: AudioSettings, stream: AudioStreamSettings) { |
| settings |
| .streams |
| .expect("audio settings contain streams") |
| .into_iter() |
| .find(|x| *x == stream) |
| .expect("contains stream"); |
| } |
| |
| // Verify that |streams| contain |stream|. |
| fn verify_contains_stream(streams: &[AudioStream; 5], stream: &AudioStream) { |
| streams.into_iter().find(|x| *x == stream).expect("contains changed media stream"); |
| } |
| |
| // Returns a registry and audio related services it is populated with |
| async fn create_services() -> (Arc<Mutex<ServiceRegistry>>, FakeServices) { |
| let service_registry = ServiceRegistry::create(); |
| let audio_core_service_handle = audio_core_service::Builder::new().build(); |
| service_registry.lock().await.register_service(audio_core_service_handle.clone()); |
| |
| let input_device_registry_service_handle = |
| Arc::new(Mutex::new(InputDeviceRegistryService::new())); |
| service_registry.lock().await.register_service(input_device_registry_service_handle.clone()); |
| |
| let sound_player_service_handle = Arc::new(Mutex::new(SoundPlayerService::new())); |
| service_registry.lock().await.register_service(sound_player_service_handle.clone()); |
| |
| ( |
| service_registry, |
| FakeServices { |
| audio_core: audio_core_service_handle, |
| input_device_registry: input_device_registry_service_handle, |
| }, |
| ) |
| } |
| |
| async fn create_environment( |
| service_registry: Arc<Mutex<ServiceRegistry>>, |
| ) -> (NestedEnvironment, Arc<DeviceStorage>) { |
| let storage_factory = |
| Arc::new(InMemoryStorageFactory::with_initial_data(&default_audio_info())); |
| |
| let env = EnvironmentBuilder::new(Arc::clone(&storage_factory)) |
| .service(ServiceRegistry::serve(service_registry)) |
| .settings(&[SettingType::Audio]) |
| .agents(&[AgentType::MediaButtons.into()]) |
| .spawn_and_get_nested_environment(ENV_NAME) |
| .await |
| .unwrap(); |
| let store = storage_factory.get_device_storage().await; |
| (env, store) |
| } |
| |
| #[fuchsia_async::run_until_stalled(test)] |
| async fn test_audio() { |
| let (service_registry, fake_services) = create_services().await; |
| let (env, store) = create_environment(service_registry).await; |
| |
| let audio_proxy = env.connect_to_service::<AudioMarker>().unwrap(); |
| |
| let settings = audio_proxy.watch().await.expect("watch completed"); |
| verify_audio_stream( |
| settings.clone(), |
| AudioStreamSettings::from(get_default_stream(AudioStreamType::Media)), |
| ); |
| |
| set_volume(&audio_proxy, vec![CHANGED_MEDIA_STREAM_SETTINGS]).await; |
| let settings = audio_proxy.watch().await.expect("watch completed"); |
| verify_audio_stream(settings.clone(), CHANGED_MEDIA_STREAM_SETTINGS); |
| |
| assert_eq!( |
| (CHANGED_VOLUME_LEVEL, CHANGED_VOLUME_MUTED), |
| fake_services.audio_core.lock().await.get_level_and_mute(AudioRenderUsage::Media).unwrap() |
| ); |
| |
| // Check to make sure value wrote out to store correctly. |
| let stored_streams = store.get::<AudioInfo>().await.streams; |
| verify_contains_stream(&stored_streams, &CHANGED_MEDIA_STREAM); |
| } |
| |
| #[fuchsia_async::run_until_stalled(test)] |
| async fn test_consecutive_volume_changes() { |
| let (service_registry, fake_services) = create_services().await; |
| let (env, store) = create_environment(service_registry).await; |
| |
| let audio_proxy = env.connect_to_service::<AudioMarker>().unwrap(); |
| |
| let settings = audio_proxy.watch().await.expect("watch completed"); |
| verify_audio_stream( |
| settings.clone(), |
| AudioStreamSettings::from(get_default_stream(AudioStreamType::Media)), |
| ); |
| |
| set_volume(&audio_proxy, vec![CHANGED_MEDIA_STREAM_SETTINGS]).await; |
| let settings = audio_proxy.watch().await.expect("watch completed"); |
| verify_audio_stream(settings.clone(), CHANGED_MEDIA_STREAM_SETTINGS); |
| |
| assert_eq!( |
| (CHANGED_VOLUME_LEVEL, CHANGED_VOLUME_MUTED), |
| fake_services.audio_core.lock().await.get_level_and_mute(AudioRenderUsage::Media).unwrap() |
| ); |
| |
| set_volume(&audio_proxy, vec![CHANGED_MEDIA_STREAM_SETTINGS_2]).await; |
| let settings = audio_proxy.watch().await.expect("watch completed"); |
| verify_audio_stream(settings.clone(), CHANGED_MEDIA_STREAM_SETTINGS_2); |
| |
| assert_eq!( |
| (CHANGED_VOLUME_LEVEL_2, CHANGED_VOLUME_MUTED), |
| fake_services.audio_core.lock().await.get_level_and_mute(AudioRenderUsage::Media).unwrap() |
| ); |
| |
| // Check to make sure value wrote out to store correctly. |
| let stored_streams = store.get::<AudioInfo>().await.streams; |
| verify_contains_stream(&stored_streams, &CHANGED_MEDIA_STREAM_2); |
| } |
| |
| #[fuchsia_async::run_until_stalled(test)] |
| async fn test_multiple_changes_on_stream() { |
| let (service_registry, _) = create_services().await; |
| let (env, store) = create_environment(service_registry).await; |
| |
| let audio_proxy = env.connect_to_service::<AudioMarker>().unwrap(); |
| |
| let settings = audio_proxy.watch().await.expect("watch completed"); |
| verify_audio_stream( |
| settings.clone(), |
| AudioStreamSettings::from(get_default_stream(AudioStreamType::Media)), |
| ); |
| |
| set_volume(&audio_proxy, vec![CHANGED_MEDIA_STREAM_SETTINGS]).await; |
| set_volume(&audio_proxy, vec![CHANGED_MEDIA_STREAM_SETTINGS_2]).await; |
| let settings = audio_proxy.watch().await.expect("watch completed"); |
| verify_audio_stream(settings.clone(), CHANGED_MEDIA_STREAM_SETTINGS_2); |
| |
| // Check to make sure value wrote out to store correctly. |
| let stored_streams = store.get::<AudioInfo>().await.streams; |
| verify_contains_stream(&stored_streams, &CHANGED_MEDIA_STREAM_2); |
| } |
| |
| #[fuchsia_async::run_until_stalled(test)] |
| async fn test_volume_overwritten() { |
| let (service_registry, fake_services) = create_services().await; |
| let (env, store) = create_environment(service_registry).await; |
| |
| let audio_proxy = env.connect_to_service::<AudioMarker>().unwrap(); |
| |
| let settings = audio_proxy.watch().await.expect("watch completed"); |
| verify_audio_stream( |
| settings.clone(), |
| AudioStreamSettings::from(get_default_stream(AudioStreamType::Media)), |
| ); |
| |
| set_volume(&audio_proxy, vec![CHANGED_MEDIA_STREAM_SETTINGS]).await; |
| let settings = audio_proxy.watch().await.expect("watch completed"); |
| verify_audio_stream(settings.clone(), CHANGED_MEDIA_STREAM_SETTINGS); |
| |
| assert_eq!( |
| (CHANGED_VOLUME_LEVEL, CHANGED_VOLUME_MUTED), |
| fake_services.audio_core.lock().await.get_level_and_mute(AudioRenderUsage::Media).unwrap() |
| ); |
| |
| // Check to make sure value wrote out to store correctly. |
| let stored_streams = store.get::<AudioInfo>().await.streams; |
| verify_contains_stream(&stored_streams, &CHANGED_MEDIA_STREAM); |
| |
| const CHANGED_BACKGROUND_STREAM_SETTINGS: AudioStreamSettings = AudioStreamSettings { |
| stream: Some(fidl_fuchsia_media::AudioRenderUsage::Background), |
| source: Some(AudioStreamSettingSource::User), |
| user_volume: Some(Volume { level: Some(0.3), muted: Some(true), ..Volume::EMPTY }), |
| ..AudioStreamSettings::EMPTY |
| }; |
| |
| set_volume(&audio_proxy, vec![CHANGED_BACKGROUND_STREAM_SETTINGS]).await; |
| let settings = audio_proxy.watch().await.expect("watch completed"); |
| |
| // Changing the background volume should not affect media volume. |
| verify_audio_stream(settings.clone(), CHANGED_MEDIA_STREAM_SETTINGS); |
| verify_audio_stream(settings.clone(), CHANGED_BACKGROUND_STREAM_SETTINGS); |
| } |
| |
| // Tests that the volume level gets rounded to two decimal places. |
| #[fuchsia_async::run_until_stalled(test)] |
| async fn test_volume_rounding() { |
| let (service_registry, fake_services) = create_services().await; |
| |
| let (env, store) = create_environment(service_registry).await; |
| |
| let audio_proxy = env.connect_to_service::<AudioMarker>().unwrap(); |
| |
| let settings = audio_proxy.watch().await.expect("watch completed"); |
| verify_audio_stream( |
| settings.clone(), |
| AudioStreamSettings::from(get_default_stream(AudioStreamType::Media)), |
| ); |
| |
| set_volume( |
| &audio_proxy, |
| vec![AudioStreamSettings { |
| stream: Some(fidl_fuchsia_media::AudioRenderUsage::Media), |
| source: Some(AudioStreamSettingSource::User), |
| user_volume: Some(Volume { |
| level: Some(0.7015), |
| muted: Some(CHANGED_VOLUME_MUTED), |
| ..Volume::EMPTY |
| }), |
| ..AudioStreamSettings::EMPTY |
| }], |
| ) |
| .await; |
| |
| let settings = audio_proxy.watch().await.expect("watch completed"); |
| verify_audio_stream(settings.clone(), CHANGED_MEDIA_STREAM_SETTINGS); |
| |
| assert_eq!( |
| (CHANGED_VOLUME_LEVEL, CHANGED_VOLUME_MUTED), |
| fake_services.audio_core.lock().await.get_level_and_mute(AudioRenderUsage::Media).unwrap() |
| ); |
| |
| // Check to make sure value wrote out to store correctly. |
| let stored_streams = store.get::<AudioInfo>().await.streams; |
| verify_contains_stream(&stored_streams, &CHANGED_MEDIA_STREAM); |
| } |
| |
| // Test to ensure mic input change events are received. |
| #[fuchsia_async::run_until_stalled(test)] |
| async fn test_audio_input() { |
| let (service_registry, fake_services) = create_services().await; |
| |
| let (env, _) = create_environment(service_registry).await; |
| |
| let audio_proxy = env.connect_to_service::<AudioMarker>().unwrap(); |
| |
| let buttons_event = MediaButtonsEventBuilder::new().set_volume(1).set_mic_mute(true).build(); |
| |
| fake_services.input_device_registry.lock().await.send_media_button_event(buttons_event.clone()); |
| |
| let updated_settings = audio_proxy.watch().await.expect("watch completed"); |
| |
| let input = updated_settings.input.expect("Should have input settings"); |
| let mic_mute = input.muted.expect("Should have mic mute value"); |
| assert!(mic_mute); |
| } |
| |
| /// Test that the audio settings are restored correctly. |
| #[fuchsia_async::run_until_stalled(test)] |
| async fn test_volume_restore() { |
| let (service_registry, fake_services) = create_services().await; |
| let expected_info = (0.9, false); |
| let mut stored_info = default_audio_info(); |
| for stream in stored_info.streams.iter_mut() { |
| if stream.stream_type == AudioStreamType::Media { |
| stream.user_volume_level = expected_info.0; |
| stream.user_volume_muted = expected_info.1; |
| } |
| } |
| |
| let storage_factory = InMemoryStorageFactory::with_initial_data(&stored_info); |
| assert!(EnvironmentBuilder::new(Arc::new(storage_factory)) |
| .service(Box::new(ServiceRegistry::serve(service_registry))) |
| .agents(&[restore_agent::blueprint::create()]) |
| .settings(&[SettingType::Audio]) |
| .spawn_nested(ENV_NAME) |
| .await |
| .is_ok()); |
| |
| let stored_info = |
| fake_services.audio_core.lock().await.get_level_and_mute(AudioRenderUsage::Media).unwrap(); |
| assert_eq!(stored_info, expected_info); |
| } |
| |
| // Test to ensure mic input change events are received. |
| // TODO(fxbug.dev/56537): Remove with switchover to input interface. |
| #[fuchsia_async::run_until_stalled(test)] |
| async fn test_bringup_without_input_registry() { |
| let service_registry = ServiceRegistry::create(); |
| let audio_core_service_handle = audio_core_service::Builder::new().build(); |
| service_registry.lock().await.register_service(audio_core_service_handle.clone()); |
| |
| let (env, _) = create_environment(service_registry).await; |
| |
| // At this point we should not crash. |
| assert!(env.connect_to_service::<AudioMarker>().is_ok()); |
| } |
| |
| // Ensure that we won't crash if audio core fails. |
| #[fuchsia_async::run_until_stalled(test)] |
| async fn test_bringup_without_audio_core() { |
| let service_registry = ServiceRegistry::create(); |
| let input_registry_service_handle = Arc::new(Mutex::new(InputDeviceRegistryService::new())); |
| service_registry.lock().await.register_service(input_registry_service_handle.clone()); |
| |
| let (env, _) = create_environment(service_registry).await; |
| |
| // At this point we should not crash. |
| let audio_proxy = env.connect_to_service::<AudioMarker>().unwrap(); |
| |
| let settings = audio_proxy.watch().await.expect("watch completed"); |
| verify_audio_stream( |
| settings.clone(), |
| AudioStreamSettings::from(get_default_stream(AudioStreamType::Media)), |
| ); |
| } |
| |
| #[test] |
| fn test_audio_info_copy() { |
| let audio_info = default_audio_info(); |
| let copy_audio_info = audio_info.clone(); |
| assert_eq!(audio_info, copy_audio_info); |
| } |
| |
| #[fuchsia_async::run_until_stalled(test)] |
| async fn test_persisted_values_applied_at_start() { |
| let (service_registry, fake_services) = create_services().await; |
| |
| let test_audio_info = AudioInfo { |
| streams: [ |
| AudioStream { |
| stream_type: AudioStreamType::Background, |
| source: AudioSettingSource::User, |
| user_volume_level: 0.5, |
| user_volume_muted: true, |
| }, |
| AudioStream { |
| stream_type: AudioStreamType::Media, |
| source: AudioSettingSource::User, |
| user_volume_level: 0.6, |
| user_volume_muted: true, |
| }, |
| AudioStream { |
| stream_type: AudioStreamType::Interruption, |
| source: AudioSettingSource::System, |
| user_volume_level: 0.3, |
| user_volume_muted: false, |
| }, |
| AudioStream { |
| stream_type: AudioStreamType::SystemAgent, |
| source: AudioSettingSource::User, |
| user_volume_level: 0.7, |
| user_volume_muted: true, |
| }, |
| AudioStream { |
| stream_type: AudioStreamType::Communication, |
| source: AudioSettingSource::User, |
| user_volume_level: 0.8, |
| user_volume_muted: false, |
| }, |
| ], |
| input: AudioInputInfo { mic_mute: true }, |
| modified_counters: Some(create_default_modified_counters()), |
| }; |
| |
| let storage_factory = InMemoryStorageFactory::with_initial_data(&test_audio_info); |
| |
| let env = EnvironmentBuilder::new(Arc::new(storage_factory)) |
| .service(ServiceRegistry::serve(service_registry)) |
| .agents(&[restore_agent::blueprint::create()]) |
| .settings(&[SettingType::Audio]) |
| .spawn_and_get_nested_environment(ENV_NAME) |
| .await |
| .unwrap(); |
| |
| let audio_proxy = env.connect_to_service::<AudioMarker>().unwrap(); |
| |
| let settings = audio_proxy.watch().await.expect("watch completed"); |
| |
| // Check to make sure mic mute value is loaded properly. |
| let mut audio_input = AudioInput::EMPTY; |
| audio_input.muted = Some(test_audio_info.input.mic_mute); |
| |
| assert_eq!(settings.input, Some(audio_input)); |
| // Check that the stored values were returned from watch() and applied to the audio core |
| // service. |
| for stream in test_audio_info.streams.iter() { |
| verify_audio_stream(settings.clone(), AudioStreamSettings::from(*stream)); |
| assert_eq!( |
| (stream.user_volume_level, stream.user_volume_muted), |
| fake_services |
| .audio_core |
| .lock() |
| .await |
| .get_level_and_mute(AudioRenderUsage::from(stream.stream_type)) |
| .unwrap() |
| ); |
| } |
| } |
| |
| #[fuchsia_async::run_until_stalled(test)] |
| async fn test_channel_failure_watch() { |
| let audio_proxy = |
| create_audio_test_env_with_failures(Arc::new(InMemoryStorageFactory::new())).await; |
| let result = audio_proxy.watch().await; |
| assert_matches!(result, Err(ClientChannelClosed { status: Status::UNAVAILABLE, .. })); |
| } |
| |
| // Test each of the failure conditions for validating the fidl input. |
| async_property_test!(test_missing_input_returns_failed => [ |
| missing_user_volume(AudioStreamSettings { |
| stream: Some(fidl_fuchsia_media::AudioRenderUsage::Media), |
| source: Some(AudioStreamSettingSource::User), |
| user_volume: None, |
| ..AudioStreamSettings::EMPTY |
| }), |
| missing_user_volume_level(AudioStreamSettings { |
| stream: Some(fidl_fuchsia_media::AudioRenderUsage::Media), |
| source: Some(AudioStreamSettingSource::User), |
| user_volume: Some(Volume { |
| level: None, |
| muted: Some(CHANGED_VOLUME_MUTED), |
| ..Volume::EMPTY |
| }), |
| ..AudioStreamSettings::EMPTY |
| }), |
| missing_user_volume_muted(AudioStreamSettings { |
| stream: Some(fidl_fuchsia_media::AudioRenderUsage::Media), |
| source: Some(AudioStreamSettingSource::User), |
| user_volume: Some(Volume { |
| level: Some(CHANGED_VOLUME_LEVEL), |
| muted: None, |
| ..Volume::EMPTY |
| }), |
| ..AudioStreamSettings::EMPTY |
| }), |
| missing_stream(AudioStreamSettings { |
| stream: None, |
| source: Some(AudioStreamSettingSource::User), |
| user_volume: Some(Volume { |
| level: Some(CHANGED_VOLUME_LEVEL), |
| muted: Some(CHANGED_VOLUME_MUTED), |
| ..Volume::EMPTY |
| }), |
| ..AudioStreamSettings::EMPTY |
| }), |
| missing_source(AudioStreamSettings { |
| stream: Some(fidl_fuchsia_media::AudioRenderUsage::Media), |
| source: None, |
| user_volume: Some(Volume { |
| level: Some(CHANGED_VOLUME_LEVEL), |
| muted: Some(CHANGED_VOLUME_MUTED), |
| ..Volume::EMPTY |
| }), |
| ..AudioStreamSettings::EMPTY |
| }), |
| ]); |
| |
| async fn test_missing_input_returns_failed(setting: AudioStreamSettings) { |
| let (service_registry, _) = create_services().await; |
| let (env, _) = create_environment(service_registry).await; |
| |
| let audio_proxy = env.connect_to_service::<AudioMarker>().unwrap(); |
| |
| let settings = audio_proxy.watch().await.expect("watch completed"); |
| verify_audio_stream( |
| settings.clone(), |
| AudioStreamSettings::from(get_default_stream(AudioStreamType::Media)), |
| ); |
| |
| let result = audio_proxy |
| .set(AudioSettings { streams: Some(vec![setting]), ..AudioSettings::EMPTY }) |
| .await |
| .expect("set completed"); |
| assert_eq!(result, Err(Error::Failed)); |
| } |