| // Copyright 2020 The Fuchsia 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 "src/camera/lib/virtual_camera/virtual_camera.h" |
| |
| #include <fuchsia/sysmem/cpp/fidl.h> |
| #include <lib/fzl/vmo-mapper.h> |
| #include <lib/sys/cpp/component_context.h> |
| #include <zircon/status.h> |
| |
| #include "src/lib/fsl/handles/object_info.h" |
| #include "src/lib/testing/loop_fixture/real_loop_fixture.h" |
| |
| class VirtualCameraTest : public gtest::RealLoopFixture { |
| public: |
| VirtualCameraTest() { context_ = sys::ComponentContext::CreateAndServeOutgoingDirectory(); } |
| |
| protected: |
| virtual void SetUp() override { |
| context_->svc()->Connect(allocator_.NewRequest()); |
| SetFailOnError(allocator_); |
| allocator_->SetDebugClientInfo(fsl::GetCurrentProcessName() + "-client", |
| fsl::GetCurrentProcessKoid()); |
| fidl::InterfaceHandle<fuchsia::sysmem::Allocator> allocator; |
| context_->svc()->Connect(allocator.NewRequest()); |
| auto result = camera::VirtualCamera::Create(std::move(allocator)); |
| ASSERT_TRUE(result.is_ok()); |
| virtual_camera_ = result.take_value(); |
| } |
| |
| virtual void TearDown() override { |
| virtual_camera_ = nullptr; |
| allocator_ = nullptr; |
| } |
| |
| template <class T> |
| static void SetFailOnError(fidl::InterfacePtr<T>& ptr, std::string name = T::Name_) { |
| ptr.set_error_handler([=](zx_status_t status) { |
| ADD_FAILURE() << name << " server disconnected: " << zx_status_get_string(status); |
| }); |
| } |
| |
| void RunLoopUntilFailureOr(bool& condition) { |
| RunLoopUntil([&]() { return HasFailure() || condition; }); |
| } |
| |
| std::unique_ptr<sys::ComponentContext> context_; |
| fuchsia::sysmem::AllocatorPtr allocator_; |
| std::unique_ptr<camera::VirtualCamera> virtual_camera_; |
| }; |
| |
| TEST_F(VirtualCameraTest, FramesReceived) { |
| fuchsia::camera3::DevicePtr camera; |
| SetFailOnError(camera, "Camera"); |
| virtual_camera_->GetHandler()(camera.NewRequest()); |
| |
| bool configurations_received = false; |
| camera->GetConfigurations([&](std::vector<fuchsia::camera3::Configuration> configurations) { |
| ASSERT_FALSE(configurations.empty()); |
| ASSERT_FALSE(configurations[0].streams.empty()); |
| configurations_received = true; |
| }); |
| RunLoopUntilFailureOr(configurations_received); |
| ASSERT_FALSE(HasFailure()); |
| |
| fuchsia::camera3::StreamPtr stream; |
| SetFailOnError(stream, "Stream"); |
| camera->ConnectToStream(0, stream.NewRequest()); |
| |
| fuchsia::sysmem::BufferCollectionTokenPtr token; |
| allocator_->AllocateSharedCollection(token.NewRequest()); |
| token->Sync([&] { stream->SetBufferCollection(std::move(token)); }); |
| fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken> client_token; |
| bool token_received = false; |
| stream->WatchBufferCollection( |
| [&](fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken> token) { |
| client_token = std::move(token); |
| token_received = true; |
| }); |
| RunLoopUntilFailureOr(token_received); |
| |
| fuchsia::sysmem::BufferCollectionPtr collection; |
| SetFailOnError(collection, "BufferCollection"); |
| allocator_->BindSharedCollection(std::move(client_token), collection.NewRequest()); |
| |
| fuchsia::sysmem::BufferCollectionConstraints constraints{ |
| .usage{.cpu = fuchsia::sysmem::cpuUsageRead}, .min_buffer_count_for_camping = 2}; |
| collection->SetConstraints(true, constraints); |
| fuchsia::sysmem::BufferCollectionInfo_2 buffers_received; |
| bool buffers_allocated = false; |
| collection->WaitForBuffersAllocated( |
| [&](zx_status_t status, fuchsia::sysmem::BufferCollectionInfo_2 buffers) { |
| ASSERT_EQ(status, ZX_OK); |
| buffers_received = std::move(buffers); |
| buffers_allocated = true; |
| }); |
| RunLoopUntilFailureOr(buffers_allocated); |
| ASSERT_FALSE(HasFailure()); |
| collection->Close(); |
| |
| constexpr uint32_t kTargetFrameCount = 11; |
| uint32_t frames_received = 0; |
| bool all_frames_received = false; |
| fit::function<void(fuchsia::camera3::FrameInfo)> check_frame; |
| check_frame = [&](fuchsia::camera3::FrameInfo info) { |
| fzl::VmoMapper mapper; |
| ASSERT_EQ(mapper.Map(buffers_received.buffers[info.buffer_index].vmo, 0, |
| buffers_received.settings.buffer_settings.size_bytes, ZX_VM_PERM_READ), |
| ZX_OK); |
| auto result = virtual_camera_->CheckFrame(mapper.start(), mapper.size(), info); |
| EXPECT_TRUE(result.is_ok()) << result.error(); |
| result = virtual_camera_->CheckFrame(mapper.start(), 0, info); |
| EXPECT_TRUE(result.is_error()); |
| mapper.Unmap(); |
| std::vector<char> zeros(0, mapper.size()); |
| result = virtual_camera_->CheckFrame(zeros.data(), zeros.size(), info); |
| EXPECT_TRUE(result.is_error()); |
| if (++frames_received < kTargetFrameCount) { |
| stream->GetNextFrame(check_frame.share()); |
| } else { |
| all_frames_received = true; |
| } |
| }; |
| stream->GetNextFrame(check_frame.share()); |
| RunLoopUntilFailureOr(all_frames_received); |
| ASSERT_FALSE(HasFailure()); |
| } |