blob: dbbc7e0d82e913962ba1a5cde041a10df228c7a7 [file] [log] [blame]
// 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/sysmem2/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_);
fuchsia::sysmem2::AllocatorSetDebugClientInfoRequest set_debug_request;
set_debug_request.set_name(fsl::GetCurrentProcessName() + "-client");
set_debug_request.set_id(fsl::GetCurrentProcessKoid());
allocator_->SetDebugClientInfo(std::move(set_debug_request));
fidl::InterfaceHandle<fuchsia::sysmem2::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::sysmem2::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::sysmem2::BufferCollectionTokenPtr token;
fuchsia::sysmem2::AllocatorAllocateSharedCollectionRequest allocate_shared_request;
allocate_shared_request.set_token_request(token.NewRequest());
allocator_->AllocateSharedCollection(std::move(allocate_shared_request));
token->Sync([&](fuchsia::sysmem2::Node_Sync_Result result) {
stream->SetBufferCollection2(std::move(token));
});
fidl::InterfaceHandle<fuchsia::sysmem2::BufferCollectionToken> client_token;
bool token_received = false;
stream->WatchBufferCollection(
[&](fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken> token_v1) {
auto token_v2 = fuchsia::sysmem2::BufferCollectionTokenHandle(token_v1.TakeChannel());
client_token = std::move(token_v2);
token_received = true;
});
RunLoopUntilFailureOr(token_received);
fuchsia::sysmem2::BufferCollectionPtr collection;
SetFailOnError(collection, "BufferCollection");
fuchsia::sysmem2::AllocatorBindSharedCollectionRequest bind_shared_request;
bind_shared_request.set_token(std::move(client_token));
bind_shared_request.set_buffer_collection_request(collection.NewRequest());
allocator_->BindSharedCollection(std::move(bind_shared_request));
fuchsia::sysmem2::BufferCollectionSetConstraintsRequest set_constraints_request;
auto& constraints = *set_constraints_request.mutable_constraints();
constraints.mutable_usage()->set_cpu(fuchsia::sysmem2::CPU_USAGE_READ);
constraints.set_min_buffer_count_for_camping(2);
collection->SetConstraints(std::move(set_constraints_request));
fuchsia::sysmem2::BufferCollectionInfo buffers_received;
bool buffers_allocated = false;
collection->WaitForAllBuffersAllocated(
[&](fuchsia::sysmem2::BufferCollection_WaitForAllBuffersAllocated_Result result) {
ASSERT_TRUE(result.is_response());
buffers_received = std::move(*result.response().mutable_buffer_collection_info());
buffers_allocated = true;
});
RunLoopUntilFailureOr(buffers_allocated);
ASSERT_FALSE(HasFailure());
collection->Release();
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, static_cast<char>(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());
}