// Copyright 2018 The Chromium Authors. All rights reserved. // 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/callback_helpers.h" #include "base/memory/raw_ptr.h" #include "base/run_loop.h" #include "base/test/task_environment.h" #include "build/chromeos_buildflags.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::Invoke; 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( video_frame_handler_.InitWithNewPipeAndPassReceiver()); auto mock_device = std::make_unique(); mock_device_ptr_ = mock_device.get(); #if BUILDFLAG(IS_CHROMEOS_ASH) adapter_ = std::make_unique( std::move(mock_device), base::DoNothing(), base::ThreadTaskRunnerHandle::Get()); #elif BUILDFLAG(IS_WIN) adapter_ = std::make_unique( std::move(mock_device), nullptr); #else adapter_ = std::make_unique(std::move(mock_device)); #endif // BUILDFLAG(IS_CHROMEOS_ASH) } void TearDown() override { // The internals of ReceiverOnTaskRunner perform a DeleteSoon(). adapter_.reset(); base::RunLoop wait_loop; wait_loop.RunUntilIdle(); } protected: raw_ptr mock_device_ptr_; std::unique_ptr adapter_; std::unique_ptr mock_video_frame_handler_; mojo::PendingRemote video_frame_handler_; base::test::TaskEnvironment task_environment_; }; TEST_F(DeviceMediaToMojoAdapterTest, DeviceIsStoppedWhenReceiverClosesConnection) { { base::RunLoop run_loop; EXPECT_CALL(*mock_device_ptr_, DoAllocateAndStart(_, _)) .WillOnce(Invoke( [](const media::VideoCaptureParams& params, std::unique_ptr* client) { (*client)->OnStarted(); })); EXPECT_CALL(*mock_video_frame_handler_, OnStarted()) .WillOnce(Invoke([&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(Invoke([&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(Invoke( [](const media::VideoCaptureParams& params, std::unique_ptr* client) { (*client)->OnStarted(); })); EXPECT_CALL(*mock_video_frame_handler_, OnStarted()) .WillOnce(Invoke([&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