Bryant Chandler | b214ba4 | 2024-02-28 22:27:42 | [diff] [blame] | 1 | // Copyright 2024 The Chromium Authors |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "components/media_effects/media_device_info.h" |
| 6 | |
ahmedmoussa | 354d047 | 2024-03-06 18:36:06 | [diff] [blame] | 7 | #include <optional> |
| 8 | |
Bryant Chandler | b214ba4 | 2024-02-28 22:27:42 | [diff] [blame] | 9 | #include "base/system/system_monitor.h" |
| 10 | #include "base/test/test_future.h" |
| 11 | #include "components/media_effects/test/fake_audio_service.h" |
| 12 | #include "components/media_effects/test/fake_video_capture_service.h" |
| 13 | #include "content/public/browser/audio_service.h" |
| 14 | #include "content/public/browser/video_capture_service.h" |
| 15 | #include "content/public/test/browser_task_environment.h" |
| 16 | #include "testing/gmock/include/gmock/gmock.h" |
| 17 | #include "testing/gtest/include/gtest/gtest.h" |
| 18 | |
| 19 | namespace { |
| 20 | |
ahmedmoussa | 5b526e95 | 2024-03-16 16:15:26 | [diff] [blame] | 21 | using media_effects::GetRealAudioDeviceNames; |
ahmedmoussa | 354d047 | 2024-03-06 18:36:06 | [diff] [blame] | 22 | using media_effects::GetRealDefaultDeviceId; |
ahmedmoussa | 5b526e95 | 2024-03-16 16:15:26 | [diff] [blame] | 23 | using media_effects::GetRealVideoDeviceNames; |
ahmedmoussa | 354d047 | 2024-03-06 18:36:06 | [diff] [blame] | 24 | |
Bryant Chandler | b214ba4 | 2024-02-28 22:27:42 | [diff] [blame] | 25 | media::AudioDeviceDescription GetAudioDeviceDescription(size_t index) { |
| 26 | return media::AudioDeviceDescription{ |
| 27 | /*device_name=*/base::StringPrintf("name_%zu", index), |
| 28 | /*unique_id=*/base::StringPrintf("id_%zu", index), |
| 29 | /*group_id=*/base::StringPrintf("group_%zu", index)}; |
| 30 | } |
| 31 | |
| 32 | media::VideoCaptureDeviceDescriptor GetVideoCaptureDeviceDescriptor( |
| 33 | size_t index) { |
| 34 | return media::VideoCaptureDeviceDescriptor{ |
| 35 | /*display_name=*/base::StringPrintf("name_%zu", index), |
| 36 | /*device_id=*/base::StringPrintf("id_%zu", index)}; |
| 37 | } |
| 38 | |
| 39 | class MockDeviceChangeObserver |
| 40 | : public media_effects::MediaDeviceInfo::Observer { |
| 41 | public: |
| 42 | MOCK_METHOD(void, |
| 43 | OnAudioDevicesChanged, |
| 44 | (const std::optional<std::vector<media::AudioDeviceDescription>>& |
| 45 | device_infos)); |
| 46 | MOCK_METHOD(void, |
| 47 | OnVideoDevicesChanged, |
| 48 | (const std::optional<std::vector<media::VideoCaptureDeviceInfo>>& |
| 49 | device_infos)); |
| 50 | }; |
| 51 | |
| 52 | auto IsVideoDevice(size_t expected_index) { |
| 53 | return testing::Field(&media::VideoCaptureDeviceInfo::descriptor, |
| 54 | GetVideoCaptureDeviceDescriptor(expected_index)); |
| 55 | } |
| 56 | |
| 57 | } // namespace |
| 58 | |
| 59 | class MediaDeviceInfoTest : public testing::Test { |
| 60 | void SetUp() override { |
| 61 | auto_reset_audio_service_.emplace( |
| 62 | content::OverrideAudioServiceForTesting(&fake_audio_service_)); |
| 63 | content::OverrideVideoCaptureServiceForTesting( |
| 64 | &fake_video_capture_service_); |
| 65 | auto_reset_media_device_info_override_.emplace( |
| 66 | media_effects::MediaDeviceInfo::OverrideInstanceForTesting()); |
| 67 | } |
| 68 | |
| 69 | void TearDown() override { |
| 70 | content::OverrideVideoCaptureServiceForTesting(nullptr); |
| 71 | } |
| 72 | |
| 73 | protected: |
| 74 | media::AudioDeviceDescription AddFakeAudioDevice(size_t index) { |
| 75 | base::test::TestFuture<void> replied_audio_device_infos_future; |
| 76 | fake_audio_service_.SetOnRepliedWithInputDeviceDescriptionsCallback( |
| 77 | replied_audio_device_infos_future.GetCallback()); |
| 78 | auto device = GetAudioDeviceDescription(index); |
| 79 | fake_audio_service_.AddFakeInputDevice(device); |
| 80 | CHECK(replied_audio_device_infos_future.WaitAndClear()); |
| 81 | return device; |
| 82 | } |
| 83 | |
| 84 | media::VideoCaptureDeviceDescriptor AddFakeVideoDevice(size_t index) { |
| 85 | base::test::TestFuture<void> replied_video_device_infos_future; |
| 86 | fake_video_capture_service_.SetOnRepliedWithSourceInfosCallback( |
| 87 | replied_video_device_infos_future.GetCallback()); |
| 88 | auto device = GetVideoCaptureDeviceDescriptor(index); |
| 89 | fake_video_capture_service_.AddFakeCamera(device); |
| 90 | CHECK(replied_video_device_infos_future.WaitAndClear()); |
| 91 | return device; |
| 92 | } |
| 93 | |
| 94 | content::BrowserTaskEnvironment task_environment_; |
| 95 | base::SystemMonitor monitor_; |
| 96 | media_effects::FakeAudioService fake_audio_service_; |
| 97 | std::optional<base::AutoReset<audio::mojom::AudioService*>> |
| 98 | auto_reset_audio_service_; |
| 99 | media_effects::FakeVideoCaptureService fake_video_capture_service_; |
| 100 | // When `MediaDeviceInfo::OverrideInstanceForTesting()` is called it returns |
| 101 | // an `AutoReset` that removes the override when it's destructed. |
| 102 | // std::optional is used to hold it because we don't get the value until |
| 103 | // `SetUp()` and `AutoReset` doesn't have a default constructor. |
| 104 | std::optional<std::pair<std::unique_ptr<media_effects::MediaDeviceInfo>, |
| 105 | base::AutoReset<media_effects::MediaDeviceInfo*>>> |
| 106 | auto_reset_media_device_info_override_; |
| 107 | }; |
| 108 | |
| 109 | // Test that audio device infos are retrieved on construction and updated when |
| 110 | // changes are observed from `SystemMonitor`. |
| 111 | TEST_F(MediaDeviceInfoTest, GetAudioDeviceInfos) { |
| 112 | base::test::TestFuture<void> replied_audio_device_infos_future; |
| 113 | fake_audio_service_.SetOnRepliedWithInputDeviceDescriptionsCallback( |
| 114 | replied_audio_device_infos_future.GetCallback()); |
| 115 | EXPECT_FALSE(media_effects::MediaDeviceInfo::GetInstance() |
| 116 | ->GetAudioDeviceInfos() |
| 117 | .has_value()); |
| 118 | ASSERT_TRUE(replied_audio_device_infos_future.WaitAndClear()); |
| 119 | EXPECT_TRUE(media_effects::MediaDeviceInfo::GetInstance() |
| 120 | ->GetAudioDeviceInfos() |
| 121 | .has_value()); |
| 122 | |
| 123 | auto device_0 = AddFakeAudioDevice(0); |
| 124 | EXPECT_THAT( |
| 125 | media_effects::MediaDeviceInfo::GetInstance()->GetAudioDeviceInfos(), |
| 126 | testing::Optional(testing::ElementsAre(device_0))); |
| 127 | auto device_1 = AddFakeAudioDevice(1); |
| 128 | EXPECT_THAT( |
| 129 | media_effects::MediaDeviceInfo::GetInstance()->GetAudioDeviceInfos(), |
| 130 | testing::Optional(testing::ElementsAre(device_0, device_1))); |
| 131 | } |
| 132 | |
| 133 | // Test that getting audio device info by ID returns the correct device or |
| 134 | // nullopt if not found. |
| 135 | TEST_F(MediaDeviceInfoTest, GetAudioDeviceInfoForId) { |
| 136 | auto device_0 = AddFakeAudioDevice(0); |
| 137 | auto device_1 = AddFakeAudioDevice(1); |
| 138 | auto device_2 = AddFakeAudioDevice(2); |
| 139 | EXPECT_EQ(device_1, media_effects::MediaDeviceInfo::GetInstance() |
| 140 | ->GetAudioDeviceInfoForId(device_1.unique_id)); |
| 141 | EXPECT_EQ(std::nullopt, media_effects::MediaDeviceInfo::GetInstance() |
| 142 | ->GetAudioDeviceInfoForId("not_found_id")); |
| 143 | } |
| 144 | |
| 145 | // Test that video device infos are retrieved on construction and updated when |
| 146 | // changes are observed from `SystemMonitor`. |
| 147 | TEST_F(MediaDeviceInfoTest, GetVideoDeviceInfos) { |
| 148 | base::test::TestFuture<void> replied_video_device_infos_future; |
| 149 | fake_video_capture_service_.SetOnRepliedWithSourceInfosCallback( |
| 150 | replied_video_device_infos_future.GetCallback()); |
| 151 | EXPECT_FALSE(media_effects::MediaDeviceInfo::GetInstance() |
| 152 | ->GetVideoDeviceInfos() |
| 153 | .has_value()); |
| 154 | ASSERT_TRUE(replied_video_device_infos_future.WaitAndClear()); |
| 155 | EXPECT_TRUE(media_effects::MediaDeviceInfo::GetInstance() |
| 156 | ->GetVideoDeviceInfos() |
| 157 | .has_value()); |
| 158 | |
| 159 | auto device_0 = AddFakeVideoDevice(0); |
| 160 | EXPECT_THAT( |
| 161 | media_effects::MediaDeviceInfo::GetInstance()->GetVideoDeviceInfos(), |
| 162 | testing::Optional(testing::ElementsAre(IsVideoDevice(0)))); |
| 163 | auto device_1 = AddFakeVideoDevice(1); |
| 164 | EXPECT_THAT( |
| 165 | media_effects::MediaDeviceInfo::GetInstance()->GetVideoDeviceInfos(), |
| 166 | testing::Optional( |
| 167 | testing::ElementsAre(IsVideoDevice(0), IsVideoDevice(1)))); |
| 168 | } |
| 169 | |
| 170 | // Test that getting video device info by ID returns the correct device or |
| 171 | // nullopt if not found. |
| 172 | TEST_F(MediaDeviceInfoTest, GetVideoDeviceInfoForId) { |
| 173 | auto device_0 = AddFakeVideoDevice(0); |
| 174 | auto device_1 = AddFakeVideoDevice(1); |
| 175 | auto device_2 = AddFakeVideoDevice(2); |
| 176 | EXPECT_EQ(device_1, media_effects::MediaDeviceInfo::GetInstance() |
| 177 | ->GetVideoDeviceInfoForId(device_1.device_id) |
| 178 | ->descriptor); |
| 179 | EXPECT_EQ(std::nullopt, media_effects::MediaDeviceInfo::GetInstance() |
| 180 | ->GetVideoDeviceInfoForId("not_found_id")); |
| 181 | } |
| 182 | |
| 183 | // Test that appropriate observer methods are called when device infos change. |
| 184 | TEST_F(MediaDeviceInfoTest, Observers) { |
| 185 | MockDeviceChangeObserver observer; |
| 186 | media_effects::MediaDeviceInfo::GetInstance()->AddObserver(&observer); |
| 187 | base::test::TestFuture<void> replied_video_device_infos_future; |
| 188 | fake_video_capture_service_.SetOnRepliedWithSourceInfosCallback( |
| 189 | replied_video_device_infos_future.GetCallback()); |
| 190 | EXPECT_CALL(observer, |
| 191 | OnAudioDevicesChanged(testing::Optional(testing::IsEmpty()))); |
| 192 | EXPECT_CALL(observer, |
| 193 | OnVideoDevicesChanged(testing::Optional(testing::IsEmpty()))); |
| 194 | ASSERT_TRUE(replied_video_device_infos_future.WaitAndClear()); |
| 195 | |
| 196 | // Audio device changes |
| 197 | EXPECT_CALL(observer, |
| 198 | OnAudioDevicesChanged(testing::Optional( |
| 199 | testing::ElementsAre(GetAudioDeviceDescription(0))))); |
| 200 | AddFakeAudioDevice(0); |
| 201 | |
| 202 | EXPECT_CALL( |
| 203 | observer, |
| 204 | OnAudioDevicesChanged(testing::Optional(testing::ElementsAre( |
| 205 | GetAudioDeviceDescription(0), GetAudioDeviceDescription(1))))); |
| 206 | AddFakeAudioDevice(1); |
| 207 | |
| 208 | EXPECT_CALL(observer, |
| 209 | OnAudioDevicesChanged(testing::Optional(testing::ElementsAre( |
| 210 | GetAudioDeviceDescription(0), GetAudioDeviceDescription(1), |
| 211 | GetAudioDeviceDescription(2))))); |
| 212 | AddFakeAudioDevice(2); |
| 213 | |
| 214 | // Video device changes |
| 215 | EXPECT_CALL(observer, OnVideoDevicesChanged(testing::Optional( |
| 216 | testing::ElementsAre(IsVideoDevice(0))))); |
| 217 | AddFakeVideoDevice(0); |
| 218 | |
| 219 | EXPECT_CALL(observer, |
| 220 | OnVideoDevicesChanged(testing::Optional( |
| 221 | testing::ElementsAre(IsVideoDevice(0), IsVideoDevice(1))))); |
| 222 | auto device_1 = AddFakeVideoDevice(1); |
| 223 | |
| 224 | EXPECT_CALL(observer, |
| 225 | OnVideoDevicesChanged(testing::Optional(testing::ElementsAre( |
| 226 | IsVideoDevice(0), IsVideoDevice(1), IsVideoDevice(2))))); |
| 227 | auto device_2 = AddFakeVideoDevice(2); |
| 228 | |
| 229 | // Remove observer |
| 230 | EXPECT_CALL(observer, OnAudioDevicesChanged(testing::_)).Times(0); |
| 231 | EXPECT_CALL(observer, OnVideoDevicesChanged(testing::_)).Times(0); |
| 232 | media_effects::MediaDeviceInfo::GetInstance()->RemoveObserver(&observer); |
| 233 | AddFakeAudioDevice(3); |
| 234 | AddFakeVideoDevice(3); |
| 235 | } |
ahmedmoussa | 354d047 | 2024-03-06 18:36:06 | [diff] [blame] | 236 | |
| 237 | TEST(MediaDeviceInfoTestGeneral, DefaultAudioDeviceHandling) { |
| 238 | std::vector<media::AudioDeviceDescription> infos; |
| 239 | EXPECT_EQ(GetRealDefaultDeviceId(infos), std::nullopt); |
ahmedmoussa | 5b526e95 | 2024-03-16 16:15:26 | [diff] [blame] | 240 | EXPECT_EQ(GetRealAudioDeviceNames(infos).size(), 0u); |
ahmedmoussa | 354d047 | 2024-03-06 18:36:06 | [diff] [blame] | 241 | |
| 242 | infos.push_back(GetAudioDeviceDescription(0)); |
| 243 | infos.push_back(GetAudioDeviceDescription(1)); |
| 244 | infos.push_back(GetAudioDeviceDescription(2)); |
| 245 | EXPECT_EQ(GetRealDefaultDeviceId(infos), std::nullopt); |
ahmedmoussa | 5b526e95 | 2024-03-16 16:15:26 | [diff] [blame] | 246 | EXPECT_THAT(GetRealAudioDeviceNames(infos), |
| 247 | testing::ElementsAre(infos[0].device_name, infos[1].device_name, |
| 248 | infos[2].device_name)); |
ahmedmoussa | 354d047 | 2024-03-06 18:36:06 | [diff] [blame] | 249 | |
| 250 | infos.front().is_system_default = true; |
| 251 | EXPECT_EQ(GetRealDefaultDeviceId(infos), infos.front().unique_id); |
ahmedmoussa | 5b526e95 | 2024-03-16 16:15:26 | [diff] [blame] | 252 | EXPECT_THAT(GetRealAudioDeviceNames(infos), |
| 253 | testing::ElementsAre(infos[0].device_name, infos[1].device_name, |
| 254 | infos[2].device_name)); |
ahmedmoussa | 354d047 | 2024-03-06 18:36:06 | [diff] [blame] | 255 | |
| 256 | infos.front().unique_id = media::AudioDeviceDescription::kDefaultDeviceId; |
| 257 | EXPECT_EQ(GetRealDefaultDeviceId(infos), std::nullopt); |
ahmedmoussa | 5b526e95 | 2024-03-16 16:15:26 | [diff] [blame] | 258 | EXPECT_THAT(GetRealAudioDeviceNames(infos), |
| 259 | testing::ElementsAre(infos[1].device_name, infos[2].device_name)); |
ahmedmoussa | 354d047 | 2024-03-06 18:36:06 | [diff] [blame] | 260 | |
| 261 | infos[1].is_system_default = true; |
| 262 | EXPECT_EQ(GetRealDefaultDeviceId(infos), infos[1].unique_id); |
ahmedmoussa | 5b526e95 | 2024-03-16 16:15:26 | [diff] [blame] | 263 | EXPECT_THAT(GetRealAudioDeviceNames(infos), |
| 264 | testing::ElementsAre(infos[1].device_name, infos[2].device_name)); |
ahmedmoussa | 354d047 | 2024-03-06 18:36:06 | [diff] [blame] | 265 | |
| 266 | infos[2].unique_id = media::AudioDeviceDescription::kCommunicationsDeviceId; |
| 267 | EXPECT_EQ(GetRealDefaultDeviceId(infos), infos[1].unique_id); |
ahmedmoussa | 5b526e95 | 2024-03-16 16:15:26 | [diff] [blame] | 268 | EXPECT_THAT(GetRealAudioDeviceNames(infos), |
| 269 | testing::ElementsAre(infos[1].device_name)); |
| 270 | } |
| 271 | |
| 272 | TEST(MediaDeviceInfoTestGeneral, GetVideoDeviceNames) { |
| 273 | std::vector<media::VideoCaptureDeviceInfo> infos; |
| 274 | EXPECT_EQ(GetRealVideoDeviceNames(infos).size(), 0u); |
| 275 | |
| 276 | infos.emplace_back(GetVideoCaptureDeviceDescriptor(0)); |
| 277 | infos.emplace_back(GetVideoCaptureDeviceDescriptor(1)); |
| 278 | infos.emplace_back(GetVideoCaptureDeviceDescriptor(2)); |
| 279 | |
| 280 | EXPECT_THAT(GetRealVideoDeviceNames(infos), |
| 281 | testing::ElementsAre(infos[0].descriptor.GetNameAndModel(), |
| 282 | infos[1].descriptor.GetNameAndModel(), |
| 283 | infos[2].descriptor.GetNameAndModel())); |
ahmedmoussa | 354d047 | 2024-03-06 18:36:06 | [diff] [blame] | 284 | } |