| // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <memory> |
| |
| #include "base/auto_reset.h" |
| #include "base/command_line.h" |
| #include "base/run_loop.h" |
| #include "build/build_config.h" |
| #include "extensions/common/features/feature_session_type.h" |
| #include "extensions/common/switches.h" |
| #include "extensions/shell/test/shell_apitest.h" |
| #include "extensions/test/extension_test_message_listener.h" |
| #include "extensions/test/result_catcher.h" |
| |
| #if defined(OS_CHROMEOS) |
| #include "chromeos/audio/audio_devices_pref_handler_stub.h" |
| #include "chromeos/audio/cras_audio_handler.h" |
| #include "chromeos/dbus/dbus_thread_manager.h" |
| #include "chromeos/dbus/fake_cras_audio_client.h" |
| #endif |
| |
| namespace extensions { |
| |
| #if defined(OS_CHROMEOS) |
| using chromeos::AudioDevice; |
| using chromeos::AudioDeviceList; |
| using chromeos::AudioNode; |
| using chromeos::AudioNodeList; |
| |
| const uint64_t kJabraSpeaker1Id = 30001; |
| const uint64_t kJabraSpeaker1StableDeviceId = 80001; |
| const uint64_t kJabraSpeaker2Id = 30002; |
| const uint64_t kJabraSpeaker2StableDeviceId = 80002; |
| const uint64_t kHDMIOutputId = 30003; |
| const uint64_t kHDMIOutputStabeDevicelId = 80003; |
| const uint64_t kJabraMic1Id = 40001; |
| const uint64_t kJabraMic1StableDeviceId = 90001; |
| const uint64_t kJabraMic2Id = 40002; |
| const uint64_t kJabraMic2StableDeviceId = 90002; |
| const uint64_t kWebcamMicId = 40003; |
| const uint64_t kWebcamMicStableDeviceId = 90003; |
| |
| struct AudioNodeInfo { |
| bool is_input; |
| uint64_t id; |
| uint64_t stable_id; |
| const char* const device_name; |
| const char* const type; |
| const char* const name; |
| }; |
| |
| const AudioNodeInfo kJabraSpeaker1 = { |
| false, kJabraSpeaker1Id, kJabraSpeaker1StableDeviceId, "Jabra Speaker", |
| "USB", "Jabra Speaker 1"}; |
| |
| const AudioNodeInfo kJabraSpeaker2 = { |
| false, kJabraSpeaker2Id, kJabraSpeaker2StableDeviceId, "Jabra Speaker", |
| "USB", "Jabra Speaker 2"}; |
| |
| const AudioNodeInfo kHDMIOutput = { |
| false, kHDMIOutputId, kHDMIOutputStabeDevicelId, |
| "HDMI output", "HDMI", "HDA Intel MID"}; |
| |
| const AudioNodeInfo kJabraMic1 = { |
| true, kJabraMic1Id, kJabraMic1StableDeviceId, |
| "Jabra Mic", "USB", "Jabra Mic 1"}; |
| |
| const AudioNodeInfo kJabraMic2 = { |
| true, kJabraMic2Id, kJabraMic2StableDeviceId, |
| "Jabra Mic", "USB", "Jabra Mic 2"}; |
| |
| const AudioNodeInfo kUSBCameraMic = { |
| true, kWebcamMicId, kWebcamMicStableDeviceId, |
| "Webcam Mic", "USB", "Logitech Webcam"}; |
| |
| AudioNode CreateAudioNode(const AudioNodeInfo& info, int version) { |
| return AudioNode(info.is_input, info.id, version == 2, |
| // stable_device_id_v1: |
| info.stable_id, |
| // stable_device_id_v2: |
| version == 2 ? info.stable_id ^ 0xFFFF : 0, info.device_name, |
| info.type, info.name, false, 0); |
| } |
| |
| class AudioApiTest : public ShellApiTest { |
| public: |
| AudioApiTest() : cras_audio_handler_(NULL), fake_cras_audio_client_(NULL) {} |
| ~AudioApiTest() override {} |
| |
| void SetUp() override { |
| session_feature_type_ = extensions::ScopedCurrentFeatureSessionType( |
| extensions::FeatureSessionType::KIOSK); |
| |
| ShellApiTest::SetUp(); |
| } |
| |
| void SetUpCrasAudioHandlerWithTestingNodes(const AudioNodeList& audio_nodes) { |
| chromeos::DBusThreadManager* dbus_manager = |
| chromeos::DBusThreadManager::Get(); |
| DCHECK(dbus_manager); |
| fake_cras_audio_client_ = static_cast<chromeos::FakeCrasAudioClient*>( |
| dbus_manager->GetCrasAudioClient()); |
| fake_cras_audio_client_->SetAudioNodesAndNotifyObserversForTesting( |
| audio_nodes); |
| cras_audio_handler_ = chromeos::CrasAudioHandler::Get(); |
| DCHECK(cras_audio_handler_); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| void ChangeAudioNodes(const AudioNodeList& audio_nodes) { |
| DCHECK(fake_cras_audio_client_); |
| fake_cras_audio_client_->SetAudioNodesAndNotifyObserversForTesting( |
| audio_nodes); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| protected: |
| std::unique_ptr<base::AutoReset<extensions::FeatureSessionType>> |
| session_feature_type_; |
| chromeos::CrasAudioHandler* cras_audio_handler_; // Not owned. |
| chromeos::FakeCrasAudioClient* fake_cras_audio_client_; // Not owned. |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(AudioApiTest, Audio) { |
| // Set up the audio nodes for testing. |
| AudioNodeList audio_nodes = { |
| CreateAudioNode(kJabraSpeaker1, 2), CreateAudioNode(kJabraSpeaker2, 2), |
| CreateAudioNode(kHDMIOutput, 2), CreateAudioNode(kJabraMic1, 2), |
| CreateAudioNode(kJabraMic2, 2), CreateAudioNode(kUSBCameraMic, 2)}; |
| |
| SetUpCrasAudioHandlerWithTestingNodes(audio_nodes); |
| |
| EXPECT_TRUE(RunAppTest("api_test/audio")) << message_; |
| } |
| |
| IN_PROC_BROWSER_TEST_F(AudioApiTest, OnLevelChangedOutputDevice) { |
| AudioNodeList audio_nodes = {CreateAudioNode(kJabraSpeaker1, 2), |
| CreateAudioNode(kHDMIOutput, 2)}; |
| SetUpCrasAudioHandlerWithTestingNodes(audio_nodes); |
| |
| // Verify the jabra speaker is the active output device. |
| AudioDevice device; |
| EXPECT_TRUE(cras_audio_handler_->GetPrimaryActiveOutputDevice(&device)); |
| EXPECT_EQ(device.id, kJabraSpeaker1.id); |
| |
| // Loads background app. |
| ResultCatcher result_catcher; |
| ExtensionTestMessageListener load_listener("loaded", false); |
| ASSERT_TRUE(LoadApp("api_test/audio/volume_change")); |
| ASSERT_TRUE(load_listener.WaitUntilSatisfied()); |
| |
| // Change output device volume. |
| const int kVolume = 60; |
| cras_audio_handler_->SetOutputVolumePercent(kVolume); |
| |
| // Verify the output volume is changed to the designated value. |
| EXPECT_EQ(kVolume, cras_audio_handler_->GetOutputVolumePercent()); |
| EXPECT_EQ(kVolume, |
| cras_audio_handler_->GetOutputVolumePercentForDevice(device.id)); |
| |
| // Verify the background app got the OnOutputNodeVolumeChanged event |
| // with the expected node id and volume value. |
| ASSERT_TRUE(result_catcher.GetNextResult()) << result_catcher.message(); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(AudioApiTest, OnOutputMuteChanged) { |
| AudioNodeList audio_nodes = {CreateAudioNode(kJabraSpeaker1, 2), |
| CreateAudioNode(kHDMIOutput, 2)}; |
| SetUpCrasAudioHandlerWithTestingNodes(audio_nodes); |
| |
| // Verify the jabra speaker is the active output device. |
| AudioDevice device; |
| EXPECT_TRUE(cras_audio_handler_->GetPrimaryActiveOutputDevice(&device)); |
| EXPECT_EQ(device.id, kJabraSpeaker1.id); |
| |
| // Mute the output. |
| cras_audio_handler_->SetOutputMute(true); |
| EXPECT_TRUE(cras_audio_handler_->IsOutputMuted()); |
| |
| // Loads background app. |
| ResultCatcher result_catcher; |
| ExtensionTestMessageListener load_listener("loaded", false); |
| ASSERT_TRUE(LoadApp("api_test/audio/output_mute_change")); |
| ASSERT_TRUE(load_listener.WaitUntilSatisfied()); |
| |
| // Un-mute the output. |
| cras_audio_handler_->SetOutputMute(false); |
| EXPECT_FALSE(cras_audio_handler_->IsOutputMuted()); |
| |
| // Verify the background app got the OnMuteChanged event |
| // with the expected output un-muted state. |
| EXPECT_TRUE(result_catcher.GetNextResult()) << result_catcher.message(); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(AudioApiTest, OnInputMuteChanged) { |
| AudioNodeList audio_nodes = {CreateAudioNode(kJabraMic1, 2), |
| CreateAudioNode(kUSBCameraMic, 2)}; |
| SetUpCrasAudioHandlerWithTestingNodes(audio_nodes); |
| |
| // Set the jabra mic to be the active input device. |
| AudioDevice jabra_mic(CreateAudioNode(kJabraMic1, 2)); |
| cras_audio_handler_->SwitchToDevice( |
| jabra_mic, true, chromeos::CrasAudioHandler::ACTIVATE_BY_USER); |
| EXPECT_EQ(kJabraMic1.id, cras_audio_handler_->GetPrimaryActiveInputNode()); |
| |
| // Un-mute the input. |
| cras_audio_handler_->SetInputMute(false); |
| EXPECT_FALSE(cras_audio_handler_->IsInputMuted()); |
| |
| // Loads background app. |
| ResultCatcher result_catcher; |
| ExtensionTestMessageListener load_listener("loaded", false); |
| ASSERT_TRUE(LoadApp("api_test/audio/input_mute_change")); |
| ASSERT_TRUE(load_listener.WaitUntilSatisfied()); |
| |
| // Mute the input. |
| cras_audio_handler_->SetInputMute(true); |
| EXPECT_TRUE(cras_audio_handler_->IsInputMuted()); |
| |
| // Verify the background app got the OnMuteChanged event |
| // with the expected input muted state. |
| EXPECT_TRUE(result_catcher.GetNextResult()) << result_catcher.message(); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(AudioApiTest, OnNodesChangedAddNodes) { |
| AudioNodeList audio_nodes = {CreateAudioNode(kJabraSpeaker1, 2), |
| CreateAudioNode(kJabraSpeaker2, 2)}; |
| SetUpCrasAudioHandlerWithTestingNodes(audio_nodes); |
| const size_t init_device_size = audio_nodes.size(); |
| |
| AudioDeviceList audio_devices; |
| cras_audio_handler_->GetAudioDevices(&audio_devices); |
| EXPECT_EQ(init_device_size, audio_devices.size()); |
| |
| // Load background app. |
| ResultCatcher result_catcher; |
| ExtensionTestMessageListener load_listener("loaded", false); |
| ASSERT_TRUE(LoadApp("api_test/audio/add_nodes")); |
| ASSERT_TRUE(load_listener.WaitUntilSatisfied()); |
| |
| // Plug in HDMI output. |
| audio_nodes.push_back(CreateAudioNode(kHDMIOutput, 2)); |
| ChangeAudioNodes(audio_nodes); |
| cras_audio_handler_->GetAudioDevices(&audio_devices); |
| EXPECT_EQ(init_device_size + 1, audio_devices.size()); |
| |
| // Verify the background app got the OnNodesChanged event |
| // with the new node added. |
| EXPECT_TRUE(result_catcher.GetNextResult()) << result_catcher.message(); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(AudioApiTest, OnNodesChangedRemoveNodes) { |
| AudioNodeList audio_nodes = {CreateAudioNode(kJabraMic1, 2), |
| CreateAudioNode(kJabraMic2, 2), |
| CreateAudioNode(kUSBCameraMic, 2)}; |
| SetUpCrasAudioHandlerWithTestingNodes(audio_nodes); |
| const size_t init_device_size = audio_nodes.size(); |
| |
| AudioDeviceList audio_devices; |
| cras_audio_handler_->GetAudioDevices(&audio_devices); |
| EXPECT_EQ(init_device_size, audio_devices.size()); |
| |
| // Load background app. |
| ResultCatcher result_catcher; |
| ExtensionTestMessageListener load_listener("loaded", false); |
| ASSERT_TRUE(LoadApp("api_test/audio/remove_nodes")); |
| ASSERT_TRUE(load_listener.WaitUntilSatisfied()); |
| |
| // Remove camera mic. |
| audio_nodes.erase(audio_nodes.begin() + init_device_size - 1); |
| ChangeAudioNodes(audio_nodes); |
| cras_audio_handler_->GetAudioDevices(&audio_devices); |
| EXPECT_EQ(init_device_size - 1, audio_devices.size()); |
| |
| // Verify the background app got the onNodesChanged event |
| // with the last node removed. |
| EXPECT_TRUE(result_catcher.GetNextResult()) << result_catcher.message(); |
| } |
| |
| class WhitelistedAudioApiTest : public AudioApiTest { |
| public: |
| WhitelistedAudioApiTest() {} |
| ~WhitelistedAudioApiTest() override = default; |
| |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| command_line->AppendSwitchASCII( |
| extensions::switches::kWhitelistedExtensionID, |
| "jlgnoeceollaejlkenecblnjmdcfhfgc"); |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(WhitelistedAudioApiTest, DeprecatedApi) { |
| // Set up the audio nodes for testing. |
| AudioNodeList audio_nodes = { |
| CreateAudioNode(kJabraSpeaker1, 2), CreateAudioNode(kJabraSpeaker2, 2), |
| CreateAudioNode(kHDMIOutput, 2), CreateAudioNode(kJabraMic1, 2), |
| CreateAudioNode(kJabraMic2, 2), CreateAudioNode(kUSBCameraMic, 2)}; |
| |
| SetUpCrasAudioHandlerWithTestingNodes(audio_nodes); |
| |
| EXPECT_TRUE(RunAppTest("api_test/audio/deprecated_api")) << message_; |
| } |
| |
| #endif // OS_CHROMEOS |
| |
| } // namespace extensions |