| // 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/camera_presence_notifier.h" |
| |
| #include <string> |
| |
| #include "ash/capture_mode/fake_video_source_provider.h" |
| #include "base/run_loop.h" |
| #include "base/test/gmock_callback_support.h" |
| #include "content/public/browser/video_capture_service.h" |
| #include "content/public/test/browser_task_environment.h" |
| #include "mojo/public/cpp/bindings/pending_receiver.h" |
| #include "services/video_capture/public/mojom/video_capture_service.mojom.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| constexpr char kFakeCameraDeviceId[] = "/dev/videoX"; |
| constexpr char kFakeCameraDisplayName[] = "Fake Camera"; |
| constexpr char kFakeCameraModelId[] = "0def:c000"; |
| |
| } // namespace |
| |
| class FakeVideoCaptureService |
| : public video_capture::mojom::VideoCaptureService { |
| public: |
| FakeVideoCaptureService() = default; |
| ~FakeVideoCaptureService() override = default; |
| |
| void AddFakeCamera() { |
| const std::string camera_id = |
| kFakeCameraDeviceId + base::NumberToString(next_id_++); |
| used_ids_.push_back(camera_id); |
| fake_provider_.AddFakeCameraWithoutNotifying( |
| camera_id, kFakeCameraDisplayName, kFakeCameraModelId, |
| media::MEDIA_VIDEO_FACING_NONE); |
| } |
| |
| void RemoveFakeCamera() { |
| ASSERT_FALSE(used_ids_.empty()); |
| fake_provider_.RemoveFakeCameraWithoutNotifying(used_ids_.back()); |
| used_ids_.pop_back(); |
| } |
| |
| // mojom::VideoCaptureService: |
| void InjectGpuDependencies( |
| mojo::PendingRemote<video_capture::mojom::AcceleratorFactory> |
| accelerator_factory) override {} |
| |
| void ConnectToCameraAppDeviceBridge( |
| mojo::PendingReceiver<cros::mojom::CameraAppDeviceBridge> receiver) |
| override {} |
| |
| void ConnectToDeviceFactory( |
| mojo::PendingReceiver<video_capture::mojom::DeviceFactory> receiver) |
| override {} |
| |
| void ConnectToVideoSourceProvider( |
| mojo::PendingReceiver<video_capture::mojom::VideoSourceProvider> receiver) |
| override { |
| fake_provider_.Bind(std::move(receiver)); |
| } |
| |
| void SetRetryCount(int32_t count) override {} |
| |
| void BindControlsForTesting( |
| mojo::PendingReceiver<video_capture::mojom::TestingControls> receiver) |
| override {} |
| |
| private: |
| FakeVideoSourceProvider fake_provider_; |
| std::vector<std::string> used_ids_; |
| int next_id_{0}; |
| }; |
| |
| class CameraPresenceNotifierTest : public testing::Test { |
| public: |
| CameraPresenceNotifierTest() = default; |
| |
| CameraPresenceNotifierTest(const CameraPresenceNotifierTest&) = delete; |
| CameraPresenceNotifierTest& operator=(const CameraPresenceNotifierTest&) = |
| delete; |
| |
| ~CameraPresenceNotifierTest() override = default; |
| |
| void AddFakeCamera() { fake_service_.AddFakeCamera(); } |
| |
| void RemoveFakeCamera() { fake_service_.RemoveFakeCamera(); } |
| |
| // Advances time past the poll interval. |
| void StepClock() { task_environment_.FastForwardBy(base::Seconds(10)); } |
| |
| // testing::Test: |
| void SetUp() override { |
| content::OverrideVideoCaptureServiceForTesting(&fake_service_); |
| } |
| |
| void TearDown() override { |
| content::OverrideVideoCaptureServiceForTesting(nullptr); |
| } |
| |
| private: |
| content::BrowserTaskEnvironment task_environment_{ |
| base::test::TaskEnvironment::TimeSource::MOCK_TIME}; |
| FakeVideoCaptureService fake_service_; |
| }; |
| |
| // Tests that the observer of CameraPresenceNotifier works correctly when the |
| // camera is added/removed (using the presence callback). |
| TEST_F(CameraPresenceNotifierTest, TestPresenceObserver) { |
| std::vector<bool> values; |
| CameraPresenceNotifier::CameraPresenceCallback callback = base::BindRepeating( |
| [](std::vector<bool>* values, bool camera_is_present) { |
| values->push_back(camera_is_present); |
| }, |
| &values); |
| |
| CameraPresenceNotifier notifier(callback); |
| |
| // No events before start. |
| ASSERT_TRUE(values.empty()); |
| notifier.Start(); |
| StepClock(); |
| // The first result should be false since there are no available cameras. |
| ASSERT_EQ(1U, values.size()); |
| EXPECT_FALSE(values.back()); |
| |
| // Advance clock to verify that unchanged values don't cause callbacks. |
| StepClock(); |
| ASSERT_EQ(1U, values.size()); |
| |
| // Add a camera. |
| AddFakeCamera(); |
| StepClock(); |
| ASSERT_EQ(2U, values.size()); |
| // There is a camera now. |
| EXPECT_TRUE(values.back()); |
| |
| // Camera removed. Next callback is false again. |
| RemoveFakeCamera(); |
| StepClock(); |
| ASSERT_EQ(3U, values.size()); |
| EXPECT_FALSE(values.back()); |
| } |
| |
| // Tests that the observer of CameraPresenceNotifier works correctly when the |
| // cameras are added/removed (using the count callback). |
| TEST_F(CameraPresenceNotifierTest, TestCountObserver) { |
| std::vector<int> values; |
| CameraPresenceNotifier::CameraCountCallback callback = base::BindRepeating( |
| [](std::vector<int>* values, int camera_count) { |
| values->push_back(camera_count); |
| }, |
| &values); |
| |
| CameraPresenceNotifier notifier(callback); |
| |
| // No events before start. |
| ASSERT_TRUE(values.empty()); |
| notifier.Start(); |
| StepClock(); |
| // The first result should be 0 since there are no available cameras. |
| ASSERT_EQ(1U, values.size()); |
| EXPECT_EQ(0, values.back()); |
| |
| // Advance clock to verify that unchanged values don't cause callbacks. |
| StepClock(); |
| ASSERT_EQ(1U, values.size()); |
| |
| // Add a camera. |
| AddFakeCamera(); |
| StepClock(); |
| ASSERT_EQ(2U, values.size()); |
| // There is a camera now. |
| EXPECT_EQ(1, values.back()); |
| |
| // Camera removed. |
| RemoveFakeCamera(); |
| StepClock(); |
| ASSERT_EQ(3U, values.size()); |
| EXPECT_EQ(0, values.back()); |
| |
| // Add 1 camera. |
| AddFakeCamera(); |
| StepClock(); |
| ASSERT_EQ(4U, values.size()); |
| EXPECT_EQ(1, values.back()); |
| // Add 2nd camera. |
| AddFakeCamera(); |
| StepClock(); |
| ASSERT_EQ(5U, values.size()); |
| EXPECT_EQ(2, values.back()); |
| |
| // Remove 2nd camera |
| RemoveFakeCamera(); |
| StepClock(); |
| ASSERT_EQ(6U, values.size()); |
| EXPECT_EQ(1, values.back()); |
| // Remove 1st camera |
| RemoveFakeCamera(); |
| StepClock(); |
| ASSERT_EQ(7U, values.size()); |
| EXPECT_EQ(0, values.back()); |
| } |
| |
| } // namespace ash |