blob: 8b55c6b2e30bde1612ebc3b2df991cecb307a989 [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/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, 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());
}