blob: 246d46965ae20d3e617c4b2cebe8dcdca1fe0859 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/media_effects/media_device_info.h"
#include <optional>
#include "base/system/system_monitor.h"
#include "base/test/test_future.h"
#include "components/media_effects/test/fake_audio_service.h"
#include "components/media_effects/test/fake_video_capture_service.h"
#include "content/public/browser/audio_service.h"
#include "content/public/browser/video_capture_service.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
using media_effects::GetRealAudioDeviceNames;
using media_effects::GetRealDefaultDeviceId;
using media_effects::GetRealVideoDeviceNames;
media::AudioDeviceDescription GetAudioDeviceDescription(size_t index) {
return media::AudioDeviceDescription{
/*device_name=*/base::StringPrintf("name_%zu", index),
/*unique_id=*/base::StringPrintf("id_%zu", index),
/*group_id=*/base::StringPrintf("group_%zu", index)};
}
media::VideoCaptureDeviceDescriptor GetVideoCaptureDeviceDescriptor(
size_t index) {
return media::VideoCaptureDeviceDescriptor{
/*display_name=*/base::StringPrintf("name_%zu", index),
/*device_id=*/base::StringPrintf("id_%zu", index)};
}
class MockDeviceChangeObserver
: public media_effects::MediaDeviceInfo::Observer {
public:
MOCK_METHOD(void,
OnAudioDevicesChanged,
(const std::optional<std::vector<media::AudioDeviceDescription>>&
device_infos));
MOCK_METHOD(void,
OnVideoDevicesChanged,
(const std::optional<std::vector<media::VideoCaptureDeviceInfo>>&
device_infos));
};
auto IsVideoDevice(size_t expected_index) {
return testing::Field(&media::VideoCaptureDeviceInfo::descriptor,
GetVideoCaptureDeviceDescriptor(expected_index));
}
} // namespace
class MediaDeviceInfoTest : public testing::Test {
void SetUp() override {
auto_reset_audio_service_.emplace(
content::OverrideAudioServiceForTesting(&fake_audio_service_));
content::OverrideVideoCaptureServiceForTesting(
&fake_video_capture_service_);
auto_reset_media_device_info_override_.emplace(
media_effects::MediaDeviceInfo::OverrideInstanceForTesting());
}
void TearDown() override {
content::OverrideVideoCaptureServiceForTesting(nullptr);
}
protected:
media::AudioDeviceDescription AddFakeAudioDevice(size_t index) {
base::test::TestFuture<void> replied_audio_device_infos_future;
fake_audio_service_.SetOnRepliedWithInputDeviceDescriptionsCallback(
replied_audio_device_infos_future.GetCallback());
auto device = GetAudioDeviceDescription(index);
fake_audio_service_.AddFakeInputDevice(device);
CHECK(replied_audio_device_infos_future.WaitAndClear());
return device;
}
media::VideoCaptureDeviceDescriptor AddFakeVideoDevice(size_t index) {
base::test::TestFuture<void> replied_video_device_infos_future;
fake_video_capture_service_.SetOnRepliedWithSourceInfosCallback(
replied_video_device_infos_future.GetCallback());
auto device = GetVideoCaptureDeviceDescriptor(index);
fake_video_capture_service_.AddFakeCamera(device);
CHECK(replied_video_device_infos_future.WaitAndClear());
return device;
}
content::BrowserTaskEnvironment task_environment_;
base::SystemMonitor monitor_;
media_effects::FakeAudioService fake_audio_service_;
std::optional<base::AutoReset<audio::mojom::AudioService*>>
auto_reset_audio_service_;
media_effects::FakeVideoCaptureService fake_video_capture_service_;
// When `MediaDeviceInfo::OverrideInstanceForTesting()` is called it returns
// an `AutoReset` that removes the override when it's destructed.
// std::optional is used to hold it because we don't get the value until
// `SetUp()` and `AutoReset` doesn't have a default constructor.
std::optional<std::pair<std::unique_ptr<media_effects::MediaDeviceInfo>,
base::AutoReset<media_effects::MediaDeviceInfo*>>>
auto_reset_media_device_info_override_;
};
// Test that audio device infos are retrieved on construction and updated when
// changes are observed from `SystemMonitor`.
TEST_F(MediaDeviceInfoTest, GetAudioDeviceInfos) {
base::test::TestFuture<void> replied_audio_device_infos_future;
fake_audio_service_.SetOnRepliedWithInputDeviceDescriptionsCallback(
replied_audio_device_infos_future.GetCallback());
EXPECT_FALSE(media_effects::MediaDeviceInfo::GetInstance()
->GetAudioDeviceInfos()
.has_value());
ASSERT_TRUE(replied_audio_device_infos_future.WaitAndClear());
EXPECT_TRUE(media_effects::MediaDeviceInfo::GetInstance()
->GetAudioDeviceInfos()
.has_value());
auto device_0 = AddFakeAudioDevice(0);
EXPECT_THAT(
media_effects::MediaDeviceInfo::GetInstance()->GetAudioDeviceInfos(),
testing::Optional(testing::ElementsAre(device_0)));
auto device_1 = AddFakeAudioDevice(1);
EXPECT_THAT(
media_effects::MediaDeviceInfo::GetInstance()->GetAudioDeviceInfos(),
testing::Optional(testing::ElementsAre(device_0, device_1)));
}
// Test that getting audio device info by ID returns the correct device or
// nullopt if not found.
TEST_F(MediaDeviceInfoTest, GetAudioDeviceInfoForId) {
auto device_0 = AddFakeAudioDevice(0);
auto device_1 = AddFakeAudioDevice(1);
auto device_2 = AddFakeAudioDevice(2);
EXPECT_EQ(device_1, media_effects::MediaDeviceInfo::GetInstance()
->GetAudioDeviceInfoForId(device_1.unique_id));
EXPECT_EQ(std::nullopt, media_effects::MediaDeviceInfo::GetInstance()
->GetAudioDeviceInfoForId("not_found_id"));
}
// Test that video device infos are retrieved on construction and updated when
// changes are observed from `SystemMonitor`.
TEST_F(MediaDeviceInfoTest, GetVideoDeviceInfos) {
base::test::TestFuture<void> replied_video_device_infos_future;
fake_video_capture_service_.SetOnRepliedWithSourceInfosCallback(
replied_video_device_infos_future.GetCallback());
EXPECT_FALSE(media_effects::MediaDeviceInfo::GetInstance()
->GetVideoDeviceInfos()
.has_value());
ASSERT_TRUE(replied_video_device_infos_future.WaitAndClear());
EXPECT_TRUE(media_effects::MediaDeviceInfo::GetInstance()
->GetVideoDeviceInfos()
.has_value());
auto device_0 = AddFakeVideoDevice(0);
EXPECT_THAT(
media_effects::MediaDeviceInfo::GetInstance()->GetVideoDeviceInfos(),
testing::Optional(testing::ElementsAre(IsVideoDevice(0))));
auto device_1 = AddFakeVideoDevice(1);
EXPECT_THAT(
media_effects::MediaDeviceInfo::GetInstance()->GetVideoDeviceInfos(),
testing::Optional(
testing::ElementsAre(IsVideoDevice(0), IsVideoDevice(1))));
}
// Test that getting video device info by ID returns the correct device or
// nullopt if not found.
TEST_F(MediaDeviceInfoTest, GetVideoDeviceInfoForId) {
auto device_0 = AddFakeVideoDevice(0);
auto device_1 = AddFakeVideoDevice(1);
auto device_2 = AddFakeVideoDevice(2);
EXPECT_EQ(device_1, media_effects::MediaDeviceInfo::GetInstance()
->GetVideoDeviceInfoForId(device_1.device_id)
->descriptor);
EXPECT_EQ(std::nullopt, media_effects::MediaDeviceInfo::GetInstance()
->GetVideoDeviceInfoForId("not_found_id"));
}
// Test that appropriate observer methods are called when device infos change.
TEST_F(MediaDeviceInfoTest, Observers) {
MockDeviceChangeObserver observer;
media_effects::MediaDeviceInfo::GetInstance()->AddObserver(&observer);
base::test::TestFuture<void> replied_video_device_infos_future;
fake_video_capture_service_.SetOnRepliedWithSourceInfosCallback(
replied_video_device_infos_future.GetCallback());
EXPECT_CALL(observer,
OnAudioDevicesChanged(testing::Optional(testing::IsEmpty())));
EXPECT_CALL(observer,
OnVideoDevicesChanged(testing::Optional(testing::IsEmpty())));
ASSERT_TRUE(replied_video_device_infos_future.WaitAndClear());
// Audio device changes
EXPECT_CALL(observer,
OnAudioDevicesChanged(testing::Optional(
testing::ElementsAre(GetAudioDeviceDescription(0)))));
AddFakeAudioDevice(0);
EXPECT_CALL(
observer,
OnAudioDevicesChanged(testing::Optional(testing::ElementsAre(
GetAudioDeviceDescription(0), GetAudioDeviceDescription(1)))));
AddFakeAudioDevice(1);
EXPECT_CALL(observer,
OnAudioDevicesChanged(testing::Optional(testing::ElementsAre(
GetAudioDeviceDescription(0), GetAudioDeviceDescription(1),
GetAudioDeviceDescription(2)))));
AddFakeAudioDevice(2);
// Video device changes
EXPECT_CALL(observer, OnVideoDevicesChanged(testing::Optional(
testing::ElementsAre(IsVideoDevice(0)))));
AddFakeVideoDevice(0);
EXPECT_CALL(observer,
OnVideoDevicesChanged(testing::Optional(
testing::ElementsAre(IsVideoDevice(0), IsVideoDevice(1)))));
auto device_1 = AddFakeVideoDevice(1);
EXPECT_CALL(observer,
OnVideoDevicesChanged(testing::Optional(testing::ElementsAre(
IsVideoDevice(0), IsVideoDevice(1), IsVideoDevice(2)))));
auto device_2 = AddFakeVideoDevice(2);
// Remove observer
EXPECT_CALL(observer, OnAudioDevicesChanged(testing::_)).Times(0);
EXPECT_CALL(observer, OnVideoDevicesChanged(testing::_)).Times(0);
media_effects::MediaDeviceInfo::GetInstance()->RemoveObserver(&observer);
AddFakeAudioDevice(3);
AddFakeVideoDevice(3);
}
TEST(MediaDeviceInfoTestGeneral, DefaultAudioDeviceHandling) {
std::vector<media::AudioDeviceDescription> infos;
EXPECT_EQ(GetRealDefaultDeviceId(infos), std::nullopt);
EXPECT_EQ(GetRealAudioDeviceNames(infos).size(), 0u);
infos.push_back(GetAudioDeviceDescription(0));
infos.push_back(GetAudioDeviceDescription(1));
infos.push_back(GetAudioDeviceDescription(2));
EXPECT_EQ(GetRealDefaultDeviceId(infos), std::nullopt);
EXPECT_THAT(GetRealAudioDeviceNames(infos),
testing::ElementsAre(infos[0].device_name, infos[1].device_name,
infos[2].device_name));
infos.front().is_system_default = true;
EXPECT_EQ(GetRealDefaultDeviceId(infos), infos.front().unique_id);
EXPECT_THAT(GetRealAudioDeviceNames(infos),
testing::ElementsAre(infos[0].device_name, infos[1].device_name,
infos[2].device_name));
infos.front().unique_id = media::AudioDeviceDescription::kDefaultDeviceId;
EXPECT_EQ(GetRealDefaultDeviceId(infos), std::nullopt);
EXPECT_THAT(GetRealAudioDeviceNames(infos),
testing::ElementsAre(infos[1].device_name, infos[2].device_name));
infos[1].is_system_default = true;
EXPECT_EQ(GetRealDefaultDeviceId(infos), infos[1].unique_id);
EXPECT_THAT(GetRealAudioDeviceNames(infos),
testing::ElementsAre(infos[1].device_name, infos[2].device_name));
infos[2].unique_id = media::AudioDeviceDescription::kCommunicationsDeviceId;
EXPECT_EQ(GetRealDefaultDeviceId(infos), infos[1].unique_id);
EXPECT_THAT(GetRealAudioDeviceNames(infos),
testing::ElementsAre(infos[1].device_name));
}
TEST(MediaDeviceInfoTestGeneral, GetVideoDeviceNames) {
std::vector<media::VideoCaptureDeviceInfo> infos;
EXPECT_EQ(GetRealVideoDeviceNames(infos).size(), 0u);
infos.emplace_back(GetVideoCaptureDeviceDescriptor(0));
infos.emplace_back(GetVideoCaptureDeviceDescriptor(1));
infos.emplace_back(GetVideoCaptureDeviceDescriptor(2));
EXPECT_THAT(GetRealVideoDeviceNames(infos),
testing::ElementsAre(infos[0].descriptor.GetNameAndModel(),
infos[1].descriptor.GetNameAndModel(),
infos[2].descriptor.GetNameAndModel()));
}