910e62b5创建于 1月15日历史提交
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "services/video_capture/device_media_to_mojo_adapter.h"

#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/task_environment.h"
#include "media/capture/video/mock_device.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "services/video_capture/public/cpp/mock_video_frame_handler.h"
#include "services/video_capture/public/mojom/video_frame_handler.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

using testing::_;

namespace video_capture {

class DeviceMediaToMojoAdapterTest : public ::testing::Test {
 public:
  DeviceMediaToMojoAdapterTest() = default;
  ~DeviceMediaToMojoAdapterTest() override = default;

  void SetUp() override {
    mock_video_frame_handler_ = std::make_unique<MockVideoFrameHandler>(
        video_frame_handler_.InitWithNewPipeAndPassReceiver());
    auto mock_device = std::make_unique<media::MockDevice>();
    mock_device_ptr_ = mock_device.get();
#if BUILDFLAG(IS_CHROMEOS)
    adapter_ = std::make_unique<DeviceMediaToMojoAdapter>(
        std::move(mock_device), base::DoNothing(),
        base::SingleThreadTaskRunner::GetCurrentDefault());
#elif BUILDFLAG(IS_WIN)
    adapter_ = std::make_unique<DeviceMediaToMojoAdapter>(
        std::move(mock_device), nullptr);
#else
    adapter_ =
        std::make_unique<DeviceMediaToMojoAdapter>(std::move(mock_device));
#endif  // BUILDFLAG(IS_CHROMEOS)
  }

  void TearDown() override {
    // The internals of ReceiverOnTaskRunner perform a DeleteSoon().
    adapter_.reset();
    base::RunLoop wait_loop;
    wait_loop.RunUntilIdle();
  }

 protected:
  raw_ptr<media::MockDevice, DanglingUntriaged> mock_device_ptr_;
  std::unique_ptr<DeviceMediaToMojoAdapter> adapter_;
  std::unique_ptr<MockVideoFrameHandler> mock_video_frame_handler_;
  mojo::PendingRemote<mojom::VideoFrameHandler> video_frame_handler_;
  base::test::TaskEnvironment task_environment_;
};

TEST_F(DeviceMediaToMojoAdapterTest,
       DeviceIsStoppedWhenReceiverClosesConnection) {
  {
    base::RunLoop run_loop;
    EXPECT_CALL(*mock_device_ptr_, DoAllocateAndStart(_, _))
        .WillOnce(
            [](const media::VideoCaptureParams& params,
               std::unique_ptr<media::VideoCaptureDevice::Client>* client) {
              (*client)->OnStarted();
            });
    EXPECT_CALL(*mock_video_frame_handler_, OnStarted())
        .WillOnce([&run_loop]() { run_loop.Quit(); });

    const media::VideoCaptureParams kArbitrarySettings;
    adapter_->Start(kArbitrarySettings, std::move(video_frame_handler_));
    run_loop.Run();
  }
  {
    base::RunLoop run_loop;
    EXPECT_CALL(*mock_device_ptr_, DoStopAndDeAllocate())
        .WillOnce([&run_loop]() { run_loop.Quit(); });
    mock_video_frame_handler_.reset();
    run_loop.Run();
  }
}

// Triggers a condition that caused a use-after-free reported in
// https://crbug.com/807887. The use-after-free happened because the connection
// lost event handler got invoked on a base::Unretained() pointer to |adapter_|
// after |adapter_| was released.
TEST_F(DeviceMediaToMojoAdapterTest,
       ReleaseInstanceSynchronouslyAfterReceiverClosedConnection) {
  {
    base::RunLoop run_loop;
    EXPECT_CALL(*mock_device_ptr_, DoAllocateAndStart(_, _))
        .WillOnce(
            [](const media::VideoCaptureParams& params,
               std::unique_ptr<media::VideoCaptureDevice::Client>* client) {
              (*client)->OnStarted();
            });
    EXPECT_CALL(*mock_video_frame_handler_, OnStarted())
        .WillOnce([&run_loop]() { run_loop.Quit(); });

    const media::VideoCaptureParams kArbitrarySettings;
    adapter_->Start(kArbitrarySettings, std::move(video_frame_handler_));
    run_loop.Run();
  }

  {
    base::RunLoop run_loop;

    // This posts invocation of the error event handler to the end of the
    // current sequence.
    mock_video_frame_handler_.reset();

    // This destroys the DeviceMediaToMojoAdapter, which in turn posts a
    // DeleteSoon in ~ReceiverOnTaskRunner() to the end of the current sequence.
    adapter_.reset();

    // Give error handle chance to get invoked
    run_loop.RunUntilIdle();
  }
}

}  // namespace video_capture