| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ash/crosapi/audio_service_ash.h" |
| |
| #include "chrome/test/base/testing_profile.h" |
| #include "chromeos/ash/components/audio/cras_audio_handler.h" |
| #include "content/public/test/browser_task_environment.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace crosapi { |
| |
| using ::testing::_; |
| using ::testing::IsTrue; |
| using ::testing::StrictMock; |
| |
| class MockCallbacks { |
| public: |
| MOCK_METHOD2(GetMuteResponse, void(bool, bool)); |
| MOCK_METHOD1(GetDevicesResponse, |
| void(absl::optional<std::vector<mojom::AudioDeviceInfoPtr>>)); |
| MOCK_METHOD1(SetMuteResponse, void(bool)); |
| MOCK_METHOD1(SetActiveDeviceListsResponse, void(bool)); |
| MOCK_METHOD1(SetPropertiesResponse, void(bool)); |
| }; |
| |
| class MockAudioChangeObserver : public mojom::AudioChangeObserver { |
| public: |
| // mojom::AudioChangeObserver |
| void OnDeviceListChanged( |
| std::vector<mojom::AudioDeviceInfoPtr> devices) override { |
| OnDeviceListChangedMock(devices); |
| } |
| |
| void OnLevelChanged(const std::string& id, int32_t level) override { |
| OnLevelChangedMock(id, level); |
| } |
| |
| void OnMuteChanged(bool is_input, bool is_muted) override { |
| OnMuteChangedMock(is_input, is_muted); |
| } |
| |
| // mock methods to check a correct call |
| MOCK_METHOD(void, |
| OnDeviceListChangedMock, |
| (const std::vector<mojom::AudioDeviceInfoPtr>& devices)); |
| MOCK_METHOD(void, OnLevelChangedMock, (const std::string& id, int32_t level)); |
| MOCK_METHOD(void, OnMuteChangedMock, (bool is_input, bool is_muted)); |
| |
| void Wait() { run_loop_.Run(); } |
| |
| auto GetRemote() { return receiver_.BindNewPipeAndPassRemote(); } |
| |
| void Quit() { run_loop_.Quit(); } |
| |
| private: |
| mojo::Receiver<crosapi::mojom::AudioChangeObserver> receiver_{this}; |
| base::RunLoop run_loop_; |
| }; |
| |
| class AudioServiceAshTest : public ::testing::Test { |
| public: |
| void SetUp() override { |
| testing_profile_ = TestingProfile::Builder().Build(); |
| audio_service_ash_.Initialize(testing_profile_.get()); |
| audio_service_ash_.AddAudioChangeObserver(mock_observer_.GetRemote()); |
| } |
| |
| protected: |
| content::BrowserTaskEnvironment task_environment_{ |
| base::test::TaskEnvironment::TimeSource::MOCK_TIME}; |
| |
| ash::ScopedCrasAudioHandlerForTesting cras_audio_handler_; |
| StrictMock<MockCallbacks> mock_callbacks_; |
| StrictMock<MockAudioChangeObserver> mock_observer_; |
| std::unique_ptr<TestingProfile> testing_profile_; |
| AudioServiceAsh audio_service_ash_; |
| }; |
| |
| TEST_F(AudioServiceAshTest, GetDevices) { |
| auto empty_filter = crosapi::mojom::DeviceFilter::New(); |
| mojom::AudioService::GetDevicesCallback cb = base::BindOnce( |
| &MockCallbacks::GetDevicesResponse, base::Unretained(&mock_callbacks_)); |
| |
| EXPECT_CALL(mock_callbacks_, GetDevicesResponse(IsTrue())); |
| |
| audio_service_ash_.GetDevices(std::move(empty_filter), std::move(cb)); |
| } |
| |
| TEST_F(AudioServiceAshTest, GetMute) { |
| mojom::AudioService::GetMuteCallback cb = base::BindOnce( |
| &MockCallbacks::GetMuteResponse, base::Unretained(&mock_callbacks_)); |
| |
| EXPECT_CALL(mock_callbacks_, GetMuteResponse(true, false)); |
| |
| audio_service_ash_.GetMute(crosapi::mojom::StreamType::kOutput, |
| std::move(cb)); |
| } |
| |
| TEST_F(AudioServiceAshTest, SetActiveDeviceListsNull) { |
| mojom::AudioService::SetActiveDeviceListsCallback cb = |
| base::BindOnce(&MockCallbacks::SetActiveDeviceListsResponse, |
| base::Unretained(&mock_callbacks_)); |
| |
| EXPECT_CALL(mock_callbacks_, SetActiveDeviceListsResponse(true)); |
| |
| audio_service_ash_.SetActiveDeviceLists(nullptr, std::move(cb)); |
| } |
| |
| TEST_F(AudioServiceAshTest, SetActiveDeviceListsBadId) { |
| auto dev_lists = crosapi::mojom::DeviceIdLists::New(); |
| const std::string kBadId = "-1"; |
| dev_lists->outputs.push_back(kBadId); |
| mojom::AudioService::SetActiveDeviceListsCallback cb = |
| base::BindOnce(&MockCallbacks::SetActiveDeviceListsResponse, |
| base::Unretained(&mock_callbacks_)); |
| |
| EXPECT_CALL(mock_callbacks_, SetActiveDeviceListsResponse(false)); |
| |
| audio_service_ash_.SetActiveDeviceLists(std::move(dev_lists), std::move(cb)); |
| } |
| |
| TEST_F(AudioServiceAshTest, SetActiveDeviceListsSpeaker) { |
| auto dev_lists = crosapi::mojom::DeviceIdLists::New(); |
| const std::string kSpeakerId = |
| base::NumberToString(0x100000001); // from FakeCrasAudioClient |
| dev_lists->outputs.push_back(kSpeakerId); |
| mojom::AudioService::SetActiveDeviceListsCallback cb = |
| base::BindOnce(&MockCallbacks::SetActiveDeviceListsResponse, |
| base::Unretained(&mock_callbacks_)); |
| |
| EXPECT_CALL(mock_callbacks_, SetActiveDeviceListsResponse(true)); |
| EXPECT_CALL(mock_observer_, OnLevelChangedMock(kSpeakerId, _)).WillOnce([&] { |
| mock_observer_.Quit(); |
| }); |
| |
| audio_service_ash_.SetActiveDeviceLists(std::move(dev_lists), std::move(cb)); |
| mock_observer_.Wait(); |
| } |
| |
| TEST_F(AudioServiceAshTest, SetPropertiesBadId) { |
| const std::string kBadId = "-1"; |
| auto props = crosapi::mojom::AudioDeviceProperties::New(50); |
| mojom::AudioService::SetPropertiesCallback cb = |
| base::BindOnce(&MockCallbacks::SetPropertiesResponse, |
| base::Unretained(&mock_callbacks_)); |
| |
| EXPECT_CALL(mock_callbacks_, SetPropertiesResponse(false)); |
| |
| audio_service_ash_.SetProperties(kBadId, std::move(props), std::move(cb)); |
| } |
| |
| TEST_F(AudioServiceAshTest, SetPropertiesNullProp) { |
| const std::string kId = base::NumberToString(0x100000001); |
| mojom::AudioService::SetPropertiesCallback cb = |
| base::BindOnce(&MockCallbacks::SetPropertiesResponse, |
| base::Unretained(&mock_callbacks_)); |
| |
| EXPECT_CALL(mock_callbacks_, SetPropertiesResponse(false)); |
| |
| audio_service_ash_.SetProperties(kId, nullptr, std::move(cb)); |
| } |
| |
| TEST_F(AudioServiceAshTest, SetPropertiesHeadphone) { |
| // taken from FakeCrasAudioClient, device must be active to trigger event |
| const std::string kId = base::NumberToString(0x200000001); |
| const int kNewVolume = 12; |
| auto props = crosapi::mojom::AudioDeviceProperties::New(kNewVolume); |
| mojom::AudioService::SetPropertiesCallback cb = |
| base::BindOnce(&MockCallbacks::SetPropertiesResponse, |
| base::Unretained(&mock_callbacks_)); |
| |
| EXPECT_CALL(mock_callbacks_, SetPropertiesResponse(true)); |
| EXPECT_CALL(mock_observer_, OnLevelChangedMock(kId, kNewVolume)) |
| .WillOnce([&] { mock_observer_.Quit(); }); |
| |
| audio_service_ash_.SetProperties(kId, std::move(props), std::move(cb)); |
| mock_observer_.Wait(); |
| } |
| |
| TEST_F(AudioServiceAshTest, SetMuteOut) { |
| const bool kNewMuteValue = true; |
| mojom::AudioService::SetMuteCallback cb = base::BindOnce( |
| &MockCallbacks::SetMuteResponse, base::Unretained(&mock_callbacks_)); |
| |
| EXPECT_CALL(mock_callbacks_, SetMuteResponse(true)); |
| EXPECT_CALL(mock_observer_, OnMuteChangedMock(false, kNewMuteValue)) |
| .WillOnce([&] { mock_observer_.Quit(); }); |
| |
| audio_service_ash_.SetMute(crosapi::mojom::StreamType::kOutput, kNewMuteValue, |
| std::move(cb)); |
| mock_observer_.Wait(); |
| } |
| |
| TEST_F(AudioServiceAshTest, SetMuteIn) { |
| const bool kNewMuteValue = true; |
| mojom::AudioService::SetMuteCallback cb = base::BindOnce( |
| &MockCallbacks::SetMuteResponse, base::Unretained(&mock_callbacks_)); |
| |
| EXPECT_CALL(mock_callbacks_, SetMuteResponse(true)); |
| EXPECT_CALL(mock_observer_, OnMuteChangedMock(true, kNewMuteValue)) |
| .WillOnce([&] { mock_observer_.Quit(); }); |
| |
| audio_service_ash_.SetMute(crosapi::mojom::StreamType::kInput, kNewMuteValue, |
| std::move(cb)); |
| mock_observer_.Wait(); |
| } |
| |
| TEST_F(AudioServiceAshTest, OnMuteChangedOut) { |
| const bool kNewMuteValue = true; |
| |
| EXPECT_CALL(mock_observer_, OnMuteChangedMock(false, kNewMuteValue)) |
| .WillOnce([&] { mock_observer_.Quit(); }); |
| |
| cras_audio_handler_.Get().SetOutputMute(kNewMuteValue); |
| mock_observer_.Wait(); |
| } |
| |
| TEST_F(AudioServiceAshTest, OnMuteChangedIn) { |
| const bool kNewMuteValue = true; |
| |
| EXPECT_CALL(mock_observer_, OnMuteChangedMock(true, kNewMuteValue)) |
| .WillOnce([&] { mock_observer_.Quit(); }); |
| |
| cras_audio_handler_.Get().SetInputMute( |
| kNewMuteValue, ash::CrasAudioHandler::InputMuteChangeMethod::kOther); |
| mock_observer_.Wait(); |
| } |
| |
| TEST_F(AudioServiceAshTest, OnLevelChangedOut) { |
| const uint64_t kId = 0x200000001; // taken from FakeCrasAudioClient |
| const int kNewVolume = 36; |
| |
| EXPECT_CALL(mock_observer_, |
| OnLevelChangedMock(base::NumberToString(kId), kNewVolume)) |
| .WillOnce([&] { mock_observer_.Quit(); }); |
| |
| ash::FakeCrasAudioClient::Get()->NotifyOutputNodeVolumeChangedForTesting( |
| kId, kNewVolume); |
| mock_observer_.Wait(); |
| } |
| |
| TEST_F(AudioServiceAshTest, OnLevelChangedIn) { |
| const uint64_t kId = 0x200000002; // taken from FakeCrasAudioClient |
| const int kNewGain = 36; |
| |
| EXPECT_CALL(mock_observer_, |
| OnLevelChangedMock(base::NumberToString(kId), kNewGain)) |
| .WillOnce([&] { mock_observer_.Quit(); }); |
| |
| ash::FakeCrasAudioClient::Get()->NotifyInputNodeGainChangedForTesting( |
| kId, kNewGain); |
| mock_observer_.Wait(); |
| } |
| |
| TEST_F(AudioServiceAshTest, OnDeviceListChangedAdd) { |
| ash::AudioNode new_device; |
| new_device.id = 5; |
| |
| EXPECT_CALL(mock_observer_, OnDeviceListChangedMock(_)).WillOnce([&] { |
| mock_observer_.Quit(); |
| }); |
| |
| ash::FakeCrasAudioClient::Get()->InsertAudioNodeToList(new_device); |
| mock_observer_.Wait(); |
| } |
| |
| TEST_F(AudioServiceAshTest, OnDeviceListChangedRemove) { |
| const uint64_t kId = 0x200000002; // taken from FakeCrasAudioClient |
| |
| ash::FakeCrasAudioClient::Get()->RemoveAudioNodeFromList(kId); |
| |
| // Active input device changes and level is set to preference when devices are |
| // changed. |
| const int kPreferredGain = 50; |
| uint64_t kInputId = cras_audio_handler_.Get().GetPrimaryActiveInputNode(); |
| EXPECT_CALL(mock_observer_, OnLevelChangedMock(base::NumberToString(kInputId), |
| kPreferredGain)); |
| |
| // Device list change happens after input level change. |
| EXPECT_CALL(mock_observer_, OnDeviceListChangedMock(_)).WillOnce([&] { |
| mock_observer_.Quit(); |
| }); |
| |
| mock_observer_.Wait(); |
| } |
| |
| } // namespace crosapi |