chromium/content/browser/renderer_host/media/video_capture_devices_changed_observer_unittest.cc

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/test/mock_devices_changed_observer.h"
#include "content/browser/renderer_host/media/media_devices_manager.h"
#include "content/public/browser/video_capture_service.h"
#include "content/public/test/browser_task_environment.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/video_capture/public/cpp/mock_video_capture_service.h"
#include "services/video_capture/public/cpp/mock_video_source_provider.h"
#include "services/video_capture/public/mojom/video_source_provider.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"

using testing::_;
using testing::Invoke;

namespace content {

class MockVideoCaptureDevicesChangedObserver
    : public MediaDevicesManager::VideoCaptureDevicesChangedObserver {
 public:
  MockVideoCaptureDevicesChangedObserver()
      : VideoCaptureDevicesChangedObserver(
            /*disconnect_cb=*/base::BindRepeating(
                &MockVideoCaptureDevicesChangedObserver::HandleDevicesChanged,
                base::Unretained(this),
                MediaDeviceType::kMediaVideoInput),
            /*listener_cb=*/base::BindRepeating([] {
              if (auto* monitor = base::SystemMonitor::Get()) {
                monitor->ProcessDevicesChanged(
                    base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE);
              }
            })),
        source_provider_receiver_(&mock_source_provider_) {
    OverrideVideoCaptureServiceForTesting(&mock_video_capture_service_);
    ON_CALL(mock_video_capture_service_, DoConnectToVideoSourceProvider(_))
        .WillByDefault(Invoke(
            [this](
                mojo::PendingReceiver<video_capture::mojom::VideoSourceProvider>
                    receiver) {
              if (source_provider_receiver_.is_bound()) {
                source_provider_receiver_.reset();
              }
              source_provider_receiver_.Bind(std::move(receiver));
              base::RunLoop().RunUntilIdle();
            }));

    ConnectToService();
    base::RunLoop().RunUntilIdle();
  }

  void CrashService() { source_provider_receiver_.reset(); }

  MOCK_METHOD1(HandleDevicesChanged, void(MediaDeviceType type));

  mojo::Receiver<video_capture::mojom::VideoSourceProvider>
      source_provider_receiver_;
  video_capture::MockVideoCaptureService mock_video_capture_service_;
  video_capture::MockVideoSourceProvider mock_source_provider_;
};

class DevicesChangedObserverTest : public testing::Test {
 public:
  DevicesChangedObserverTest()
      : mock_video_capture_service_device_changed_observer_(
            std::make_unique<MockVideoCaptureDevicesChangedObserver>()) {
    system_monitor_.AddDevicesChangedObserver(&observer);
  }

  void RaiseVirtualDeviceChangeEvent() {
    mock_video_capture_service_device_changed_observer_->mock_source_provider_
        .RaiseVirtualDeviceChangeEvent();
  }

  void RaiseDeviceChangeEvent() {
    mock_video_capture_service_device_changed_observer_->mock_source_provider_
        .RaiseDeviceChangeEvent();
  }

  void TearDown() override {
    system_monitor_.RemoveDevicesChangedObserver(&observer);
  }

 protected:
  content::BrowserTaskEnvironment task_environment_;
  base::SystemMonitor system_monitor_;
  base::MockDevicesChangedObserver observer;
  std::unique_ptr<MockVideoCaptureDevicesChangedObserver>
      mock_video_capture_service_device_changed_observer_;
};

TEST_F(DevicesChangedObserverTest,
       RegisteredObserverReceivesDeviceChangeEvents) {
  EXPECT_CALL(observer,
              OnDevicesChanged(base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE))
      .Times(1);
  RaiseDeviceChangeEvent();
  base::RunLoop().RunUntilIdle();

  // No OnDevicesChanged event should be observed when a virtual device change
  // event occurs.
  RaiseVirtualDeviceChangeEvent();
  base::RunLoop().RunUntilIdle();
}

TEST_F(DevicesChangedObserverTest, ServiceCrashes) {
  EXPECT_CALL(observer,
              OnDevicesChanged(base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE))
      .Times(2);
  RaiseDeviceChangeEvent();
  base::RunLoop().RunUntilIdle();

  // Crashing the service should call disconnect_cb resulting in calling
  // |HandleDevicesChanged|. But device change events are still registered
  // because the OnConnectionError() function calls ConnectToService().
  EXPECT_CALL(*mock_video_capture_service_device_changed_observer_,
              HandleDevicesChanged(MediaDeviceType::kMediaVideoInput))
      .Times(1);
  mock_video_capture_service_device_changed_observer_->CrashService();
  base::RunLoop().RunUntilIdle();
  RaiseDeviceChangeEvent();
  base::RunLoop().RunUntilIdle();
}

}  // namespace content