Chromium Code Reviews| Index: media/capture/video/chromeos/camera_device_delegate_unittest.cc |
| diff --git a/media/capture/video/chromeos/camera_device_delegate_unittest.cc b/media/capture/video/chromeos/camera_device_delegate_unittest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..b36db3c259e5276a120c5579b92815e5c0240807 |
| --- /dev/null |
| +++ b/media/capture/video/chromeos/camera_device_delegate_unittest.cc |
| @@ -0,0 +1,327 @@ |
| +// Copyright 2017 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 "media/capture/video/chromeos/camera_device_delegate.h" |
| + |
| +#include <stddef.h> |
| +#include <stdint.h> |
| + |
| +#include <memory> |
| +#include <utility> |
| + |
| +#include "base/message_loop/message_loop.h" |
| +#include "base/run_loop.h" |
| +#include "media/capture/video/chromeos/camera_hal_delegate_unittest.h" |
| +#include "media/capture/video/chromeos/mojo/arc_camera3.mojom.h" |
| +#include "testing/gmock/include/gmock/gmock.h" |
| +#include "testing/gtest/include/gtest/gtest.h" |
| + |
| +using testing::_; |
| +using testing::A; |
| +using testing::Invoke; |
| + |
| +namespace media { |
| + |
| +namespace { |
| + |
| +void DumpError(const tracked_objects::Location& location, |
| + const std::string& message) { |
| + DPLOG(ERROR) << location.ToString() << " " << message; |
| +} |
| + |
| +class MockVideoCaptureClient : public VideoCaptureDevice::Client { |
| + public: |
| + MOCK_METHOD0(DoReserveOutputBuffer, void(void)); |
| + MOCK_METHOD0(DoOnIncomingCapturedBuffer, void(void)); |
| + MOCK_METHOD0(DoOnIncomingCapturedVideoFrame, void(void)); |
| + MOCK_METHOD0(DoResurrectLastOutputBuffer, void(void)); |
| + MOCK_METHOD2(OnError, |
| + void(const tracked_objects::Location& from_here, |
| + const std::string& reason)); |
| + MOCK_CONST_METHOD0(GetBufferPoolUtilization, double(void)); |
| + MOCK_METHOD0(OnStarted, void(void)); |
| + |
| + explicit MockVideoCaptureClient( |
| + base::Callback<void(const VideoCaptureFormat&)> frame_cb) |
| + : main_thread_(base::ThreadTaskRunnerHandle::Get()), frame_cb_(frame_cb) { |
| + ON_CALL(*this, OnError(_, _)).WillByDefault(Invoke(DumpError)); |
| + } |
| + |
| + void OnIncomingCapturedData(const uint8_t* data, |
| + int length, |
| + const VideoCaptureFormat& format, |
| + int rotation, |
| + base::TimeTicks reference_time, |
| + base::TimeDelta timestamp, |
| + int frame_feedback_id) override { |
| + ASSERT_GT(length, 0); |
| + ASSERT_TRUE(data); |
| + main_thread_->PostTask(FROM_HERE, base::Bind(frame_cb_, format)); |
| + } |
| + |
| + // Trampoline methods to workaround GMOCK problems with std::unique_ptr<>. |
| + Buffer ReserveOutputBuffer(const gfx::Size& dimensions, |
| + media::VideoPixelFormat format, |
| + media::VideoPixelStorage storage, |
| + int frame_feedback_id) override { |
| + DoReserveOutputBuffer(); |
| + NOTREACHED() << "This should never be called"; |
| + return Buffer(); |
| + } |
| + void OnIncomingCapturedBuffer(Buffer buffer, |
| + const VideoCaptureFormat& format, |
| + base::TimeTicks reference_time, |
| + base::TimeDelta timestamp) override { |
| + DoOnIncomingCapturedBuffer(); |
| + } |
| + void OnIncomingCapturedBufferExt( |
| + Buffer buffer, |
| + const VideoCaptureFormat& format, |
| + base::TimeTicks reference_time, |
| + base::TimeDelta timestamp, |
| + gfx::Rect visible_rect, |
| + const VideoFrameMetadata& additional_metadata) override { |
| + DoOnIncomingCapturedVideoFrame(); |
| + } |
| + Buffer ResurrectLastOutputBuffer(const gfx::Size& dimensions, |
| + media::VideoPixelFormat format, |
| + media::VideoPixelStorage storage, |
| + int frame_feedback_id) { |
| + DoResurrectLastOutputBuffer(); |
| + NOTREACHED() << "This should never be called"; |
| + return Buffer(); |
| + } |
| + |
| + private: |
| + scoped_refptr<base::SingleThreadTaskRunner> main_thread_; |
| + base::Callback<void(const VideoCaptureFormat&)> frame_cb_; |
| +}; |
| + |
| +class MockCameraDevice : public arc::mojom::Camera3DeviceOps { |
| + public: |
| + MockCameraDevice() : mock_device_thread_("MockDeviceThread"), binding_(this) { |
| + mock_device_thread_.Start(); |
| + } |
| + |
| + ~MockCameraDevice() { |
| + mock_device_thread_.task_runner()->PostTask( |
| + FROM_HERE, base::Bind(&MockCameraDevice::CloseBindingOnThread, |
| + base::Unretained(this))); |
| + mock_device_thread_.Stop(); |
| + } |
| + |
| + void Initialize(arc::mojom::Camera3CallbackOpsPtr callback_ops, |
| + InitializeCallback callback) override { |
| + DoInitialize(callback_ops, callback); |
| + callback_ops_ = std::move(callback_ops); |
| + std::move(callback).Run(0); |
| + } |
| + MOCK_METHOD2(DoInitialize, |
| + void(arc::mojom::Camera3CallbackOpsPtr& callback_ops, |
| + InitializeCallback& callback)); |
| + |
| + void ConfigureStreams(arc::mojom::Camera3StreamConfigurationPtr config, |
| + ConfigureStreamsCallback callback) override { |
| + DoConfigureStreams(config, callback); |
| + } |
| + MOCK_METHOD2(DoConfigureStreams, |
| + void(arc::mojom::Camera3StreamConfigurationPtr& config, |
| + ConfigureStreamsCallback& callback)); |
| + |
| + void ConstructDefaultRequestSettings( |
| + arc::mojom::Camera3RequestTemplate type, |
| + ConstructDefaultRequestSettingsCallback callback) override { |
| + DoConstructDefaultRequestSettings(type, callback); |
| + } |
| + MOCK_METHOD2(DoConstructDefaultRequestSettings, |
| + void(arc::mojom::Camera3RequestTemplate type, |
| + ConstructDefaultRequestSettingsCallback& callback)); |
| + |
| + void ProcessCaptureRequest(arc::mojom::Camera3CaptureRequestPtr request, |
| + ProcessCaptureRequestCallback callback) override { |
| + DoProcessCaptureRequest(request, callback); |
| + } |
| + MOCK_METHOD2(DoProcessCaptureRequest, |
| + void(arc::mojom::Camera3CaptureRequestPtr& request, |
| + ProcessCaptureRequestCallback& callback)); |
| + |
| + void Dump(mojo::ScopedHandle fd) override { DoDump(fd); } |
| + MOCK_METHOD1(DoDump, void(mojo::ScopedHandle& fd)); |
| + |
| + void Flush(FlushCallback callback) override { DoFlush(callback); } |
| + MOCK_METHOD1(DoFlush, void(FlushCallback& callback)); |
| + |
| + void RegisterBuffer(uint64_t buffer_id, |
| + arc::mojom::Camera3DeviceOps::BufferType type, |
| + std::vector<mojo::ScopedHandle> fds, |
| + uint32_t drm_format, |
| + arc::mojom::HalPixelFormat hal_pixel_format, |
| + uint32_t width, |
| + uint32_t height, |
| + const std::vector<uint32_t>& strides, |
| + const std::vector<uint32_t>& offsets, |
| + RegisterBufferCallback callback) override { |
| + DoRegisterBuffer(buffer_id, type, fds, drm_format, hal_pixel_format, width, |
| + height, strides, offsets, callback); |
| + } |
| + MOCK_METHOD10(DoRegisterBuffer, |
| + void(uint64_t buffer_id, |
| + arc::mojom::Camera3DeviceOps::BufferType type, |
| + std::vector<mojo::ScopedHandle>& fds, |
| + uint32_t drm_format, |
| + arc::mojom::HalPixelFormat hal_pixel_format, |
| + uint32_t width, |
| + uint32_t height, |
| + const std::vector<uint32_t>& strides, |
| + const std::vector<uint32_t>& offsets, |
| + RegisterBufferCallback& callback)); |
| + |
| + void Close(CloseCallback callback) override { DoClose(callback); } |
| + MOCK_METHOD1(DoClose, void(CloseCallback& callback)); |
| + |
| + arc::mojom::Camera3DeviceOpsPtrInfo GetInterfacePtrInfo() { |
|
chfremer
2017/06/01 00:16:27
This is the pattern as in MockCameraModule, which
jcliang
2017/06/01 17:11:17
This I can fix in this CL since the follow-up CL d
|
| + base::WaitableEvent done(base::WaitableEvent::ResetPolicy::MANUAL, |
| + base::WaitableEvent::InitialState::NOT_SIGNALED); |
| + arc::mojom::Camera3DeviceOpsPtrInfo ptr_info; |
| + mock_device_thread_.task_runner()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&MockCameraDevice::BindOnThread, base::Unretained(this), |
| + base::Unretained(&done), base::Unretained(&ptr_info))); |
| + done.Wait(); |
| + return ptr_info; |
| + } |
| + |
| + private: |
| + void CloseBindingOnThread() { |
| + if (binding_.is_bound()) { |
| + binding_.Close(); |
| + } |
| + } |
| + |
| + void BindOnThread(base::WaitableEvent* done, |
| + arc::mojom::Camera3DeviceOpsPtrInfo* ptr_info) { |
| + *ptr_info = binding_.CreateInterfacePtrAndBind().PassInterface(); |
| + done->Signal(); |
| + } |
| + |
| + base::Thread mock_device_thread_; |
| + mojo::Binding<arc::mojom::Camera3DeviceOps> binding_; |
| + arc::mojom::Camera3CallbackOpsPtr callback_ops_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(MockCameraDevice); |
| +}; |
| + |
| +} // namespace |
| + |
| +class CameraDeviceDelegateTest : public ::testing::Test { |
| + public: |
| + CameraDeviceDelegateTest() |
| + : device_delegate_thread_("DeviceDelegateThread"), |
| + message_loop_(new base::MessageLoop), |
|
chfremer
2017/06/01 00:16:27
Please use ScopedTaskEnvironment instead
jcliang
2017/06/01 17:11:17
Done.
|
| + hal_delegate_thread_("HalDelegateThread") {} |
| + |
| + void SetUp() override { |
| + hal_delegate_thread_.Start(); |
| + camera_hal_delegate_ = |
| + new CameraHalDelegate(hal_delegate_thread_.task_runner()); |
| + camera_hal_delegate_->StartForTesting( |
| + mock_camera_module_.GetInterfacePtrInfo()); |
| + } |
| + |
| + void TearDown() override { |
| + while (!camera_hal_delegate_->HasOneRef()) { |
| + base::PlatformThread::YieldCurrentThread(); |
| + } |
| + camera_hal_delegate_ = nullptr; |
| + hal_delegate_thread_.Stop(); |
| + } |
| + |
| + void AllocateDeviceWithDescriptor(VideoCaptureDeviceDescriptor descriptor) { |
| + ASSERT_FALSE(device_delegate_thread_.IsRunning()); |
| + ASSERT_FALSE(camera_device_delegate_); |
| + device_delegate_thread_.Start(); |
| + camera_device_delegate_ = |
| + new CameraDeviceDelegate(descriptor, camera_hal_delegate_, |
| + device_delegate_thread_.task_runner()); |
| + } |
| + |
| + void ResetDevice() { |
| + ASSERT_TRUE(device_delegate_thread_.IsRunning()); |
| + ASSERT_TRUE(camera_device_delegate_); |
| + while (!camera_device_delegate_->HasOneRef()) { |
| + base::PlatformThread::YieldCurrentThread(); |
| + } |
| + camera_device_delegate_ = nullptr; |
| + device_delegate_thread_.Stop(); |
| + } |
| + |
| + void Wait() { |
|
chfremer
2017/06/01 00:16:27
needed?
jcliang
2017/06/01 17:11:17
This is needed after I extend the test.
|
| + run_loop_.reset(new base::RunLoop()); |
| + run_loop_->Run(); |
| + } |
| + |
| + protected: |
| + scoped_refptr<CameraHalDelegate> camera_hal_delegate_; |
| + scoped_refptr<CameraDeviceDelegate> camera_device_delegate_; |
| + testing::StrictMock<unittest_internal::MockCameraModule> mock_camera_module_; |
| + testing::StrictMock<MockCameraDevice> mock_camera_device_; |
| + base::Thread device_delegate_thread_; |
| + |
| + private: |
| + std::unique_ptr<base::MessageLoop> message_loop_; |
| + base::Thread hal_delegate_thread_; |
| + std::unique_ptr<base::RunLoop> run_loop_; |
| + DISALLOW_COPY_AND_ASSIGN(CameraDeviceDelegateTest); |
| +}; |
| + |
| +TEST_F(CameraDeviceDelegateTest, AllocateAndStop) { |
|
chfremer
2017/06/01 00:16:27
Please add more test cases to get coverage for the
jcliang
2017/06/01 17:11:17
I've extended the test case to allocate a capture
|
| + VideoCaptureDeviceDescriptor descriptor("Fake device", "0"); |
| + AllocateDeviceWithDescriptor(descriptor); |
| + |
| + auto get_camera_info_cb = |
| + [](uint32_t camera_id, |
| + arc::mojom::CameraModule::GetCameraInfoCallback& cb) { |
| + arc::mojom::CameraInfoPtr camera_info = arc::mojom::CameraInfo::New(); |
| + arc::mojom::CameraMetadataPtr static_metadata = |
| + arc::mojom::CameraMetadata::New(); |
| + switch (camera_id) { |
| + case 0: |
| + camera_info->facing = arc::mojom::CameraFacing::CAMERA_FACING_BACK; |
| + camera_info->orientation = 0; |
| + camera_info->static_camera_characteristics = |
| + std::move(static_metadata); |
| + break; |
| + default: |
| + FAIL() << "Invalid camera id"; |
| + } |
| + std::move(cb).Run(0, std::move(camera_info)); |
| + }; |
| + |
| + VideoCaptureParams params; |
| + params.requested_format = |
| + VideoCaptureFormat(gfx::Size(1280, 720), 30.0, PIXEL_FORMAT_I420); |
| + |
| + base::Callback<void(const VideoCaptureFormat&)> frame_cb = |
| + base::BindRepeating([](const VideoCaptureFormat& format) {}); |
| + |
| + std::unique_ptr<VideoCaptureDevice::Client> mock_client( |
| + new MockVideoCaptureClient(frame_cb)); |
| + |
| + EXPECT_CALL(mock_camera_module_, DoGetCameraInfo(0, _)) |
| + .Times(1) |
| + .WillOnce(Invoke(get_camera_info_cb)); |
| + |
| + device_delegate_thread_.task_runner()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&CameraDeviceDelegate::AllocateAndStart, |
| + camera_device_delegate_, params, base::Passed(&mock_client))); |
| + |
| + device_delegate_thread_.task_runner()->PostTask( |
| + FROM_HERE, base::Bind(&CameraDeviceDelegate::StopAndDeAllocate, |
| + camera_device_delegate_)); |
| + |
| + ResetDevice(); |
| +} |
| + |
| +} // namespace media |