blob: a8002f74bed249423b8624ad980274aeab129541 [file] [log] [blame]
// Copyright 2018 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 "peridot/bin/ledger/app/fidl/serialization_size.h"
#include <memory>
#include <lib/fidl/cpp/binding.h>
#include <lib/fidl/cpp/clone.h>
#include <lib/fidl/cpp/optional.h>
#include <lib/fsl/vmo/strings.h>
#include <lib/fxl/strings/string_printf.h>
#include <lib/gtest/test_loop_fixture.h>
#include "gtest/gtest.h"
#include "peridot/lib/convert/convert.h"
namespace ledger {
namespace fidl_serialization {
namespace {
std::vector<uint8_t> GetKey(size_t index, size_t min_key_size = 0u) {
std::string result = fxl::StringPrintf("key %04" PRIuMAX, index);
result.resize(std::max(result.size(), min_key_size));
return convert::ToArray(result);
}
std::string GetValue(size_t index, size_t min_value_size = 0u) {
std::string result = fxl::StringPrintf("val %zu", index);
result.resize(std::max(result.size(), min_value_size));
return result;
}
::testing::AssertionResult CheckMessageSize(zx::channel channel,
size_t expected_bytes,
size_t expected_handles) {
uint32_t actual_bytes, actual_handles;
zx_status_t status =
channel.read(0, nullptr, 0, &actual_bytes, nullptr, 0, &actual_handles);
if (status != ZX_ERR_BUFFER_TOO_SMALL) {
return ::testing::AssertionFailure()
<< "Channel read status = " << status
<< ", expected ZX_ERR_BUFFER_TOO_SMALL (" << ZX_ERR_BUFFER_TOO_SMALL
<< ").";
}
EXPECT_EQ(expected_bytes, actual_bytes);
EXPECT_EQ(expected_handles, actual_handles);
if ((expected_bytes != actual_bytes) ||
(expected_handles != actual_handles)) {
return ::testing::AssertionFailure() << "Unexpected message size.";
}
return ::testing::AssertionSuccess();
}
using SerializationSizeTest = gtest::TestLoopFixture;
class FakeSnapshotImpl : public PageSnapshot {
public:
FakeSnapshotImpl() {}
~FakeSnapshotImpl() override {}
GetEntriesInlineCallback get_entries_inline_callback;
GetEntriesCallback get_entries_callback;
GetCallback get_callback;
GetInlineCallback get_inline_callback;
// PageSnapshot:
void GetEntriesInline(std::vector<uint8_t> /*key_start*/,
std::unique_ptr<Token> /*token*/,
GetEntriesInlineCallback callback) override {
get_entries_inline_callback = std::move(callback);
}
void GetEntries(std::vector<uint8_t> /*key_start*/,
std::unique_ptr<Token> /*token*/,
GetEntriesCallback callback) override {
get_entries_callback = std::move(callback);
}
void GetKeys(std::vector<uint8_t> /*key_start*/,
std::unique_ptr<Token> /*token*/,
GetKeysCallback /*callback*/) override {}
void Get(std::vector<uint8_t> /*key*/, GetCallback callback) override {
get_callback = std::move(callback);
}
void GetInline(std::vector<uint8_t> /*key*/,
GetInlineCallback callback) override {
get_inline_callback = std::move(callback);
}
void Fetch(std::vector<uint8_t> /*key*/,
FetchCallback /*callback*/) override {
FXL_NOTIMPLEMENTED();
}
void FetchPartial(std::vector<uint8_t> /*key*/, int64_t /*offset*/,
int64_t /*max_size*/,
FetchPartialCallback /*callback*/) override {
FXL_NOTIMPLEMENTED();
}
};
TEST_F(SerializationSizeTest, GetInline) {
PageSnapshotPtr snapshot_proxy;
FakeSnapshotImpl snapshot_impl;
zx::channel writer, reader;
ASSERT_EQ(ZX_OK, zx::channel::create(0, &writer, &reader));
snapshot_proxy.Bind(std::move(reader));
fidl::Binding<PageSnapshot> binding(&snapshot_impl);
binding.Bind(std::move(writer));
const size_t key_size = 125;
const size_t value_size = 125;
std::vector<uint8_t> key = GetKey(0, key_size);
std::vector<uint8_t> value = convert::ToArray(GetValue(0, value_size));
auto client_callback = [](Status /*status*/,
std::unique_ptr<InlinedValue> /*value*/) {};
// FakeSnapshot saves the callback instead of running it.
snapshot_proxy->GetInline(std::move(key), std::move(client_callback));
RunLoopUntilIdle();
fidl::InterfaceHandle<PageSnapshot> handle = snapshot_proxy.Unbind();
reader = handle.TakeChannel();
// Run the callback directly.
auto inlined_value = std::make_unique<InlinedValue>();
inlined_value->value = std::move(value);
snapshot_impl.get_inline_callback(Status::OK, std::move(inlined_value));
const size_t expected_bytes =
Align(kMessageHeaderSize + kPointerSize + GetByteVectorSize(value_size) +
kStatusEnumSize);
const size_t expected_handles = 0;
EXPECT_TRUE(
CheckMessageSize(std::move(reader), expected_bytes, expected_handles));
}
TEST_F(SerializationSizeTest, Get) {
PageSnapshotPtr snapshot_proxy;
FakeSnapshotImpl snapshot_impl;
zx::channel writer, reader;
ASSERT_EQ(ZX_OK, zx::channel::create(0, &writer, &reader));
snapshot_proxy.Bind(std::move(reader));
fidl::Binding<PageSnapshot> binding(&snapshot_impl);
binding.Bind(std::move(writer));
const size_t key_size = 8;
const size_t value_size = 125;
std::vector<uint8_t> key = GetKey(0, key_size);
std::string object_data = GetValue(0, value_size);
fsl::SizedVmo vmo;
ASSERT_TRUE(fsl::VmoFromString(object_data, &vmo));
fuchsia::mem::BufferPtr value =
fidl::MakeOptional(std::move(vmo).ToTransport());
auto client_callback = [](Status /*status*/,
fuchsia::mem::BufferPtr /*value*/) {};
// FakeSnapshot saves the callback instead of running it.
snapshot_proxy->Get(std::move(key), std::move(client_callback));
RunLoopUntilIdle();
fidl::InterfaceHandle<PageSnapshot> handle = snapshot_proxy.Unbind();
reader = handle.TakeChannel();
// Run the callback directly.
snapshot_impl.get_callback(Status::OK, std::move(value));
const size_t expected_bytes =
Align(kMessageHeaderSize + // Header.
kPointerSize + // BufferPtr.
kPointerSize + // Size.
Align(kHandleSize) + // FIDL_HANDLE_PRESENT.
kStatusEnumSize // Status.
);
const size_t expected_handles = 1;
EXPECT_TRUE(
CheckMessageSize(std::move(reader), expected_bytes, expected_handles));
}
TEST_F(SerializationSizeTest, GetEntriesInline) {
PageSnapshotPtr snapshot_proxy;
FakeSnapshotImpl snapshot_impl;
zx::channel writer, reader;
ASSERT_EQ(ZX_OK, zx::channel::create(0, &writer, &reader));
snapshot_proxy.Bind(std::move(reader));
fidl::Binding<PageSnapshot> binding(&snapshot_impl);
binding.Bind(std::move(writer));
auto client_callback = [](Status /*status*/,
std::vector<InlinedEntry> /*entries*/,
std::unique_ptr<Token> /*next_token*/) {};
// FakeSnapshot saves the callback instead of running it.
snapshot_proxy->GetEntriesInline(fidl::VectorPtr<uint8_t>::New(0), nullptr,
std::move(client_callback));
RunLoopUntilIdle();
fidl::InterfaceHandle<PageSnapshot> handle = snapshot_proxy.Unbind();
reader = handle.TakeChannel();
std::vector<InlinedEntry> entries_to_send;
const size_t key_size = 125;
const size_t value_size = 125;
const size_t n_entries = 7;
const size_t n_empty_entries = 7;
auto token = std::make_unique<Token>();
token->opaque_id = GetKey(0, key_size);
InlinedEntry entry;
entry.key = GetKey(0, key_size);
entry.inlined_value = std::make_unique<InlinedValue>();
entry.inlined_value->value = convert::ToArray(GetValue(0, value_size));
size_t kExpectedEntrySize = GetInlinedEntrySize(entry);
for (size_t i = 0; i < n_entries; i++) {
entries_to_send.push_back(fidl::Clone(entry));
}
InlinedEntry empty_entry;
empty_entry.key = GetKey(0, key_size);
for (size_t i = 0; i < n_empty_entries; i++) {
entries_to_send.push_back(fidl::Clone(empty_entry));
}
size_t kExpectedEmptyEntrySize = GetInlinedEntrySize(empty_entry);
// Run the callback directly.
snapshot_impl.get_entries_inline_callback(
Status::OK, std::move(entries_to_send), std::move(token));
const size_t expected_bytes =
Align(kMessageHeaderSize + // Header.
kVectorHeaderSize + // VectorPtr.
n_entries * kExpectedEntrySize + // Vector of entries.
n_empty_entries * kExpectedEmptyEntrySize + // Vector of entries.
kPointerSize + // Pointer to next_token.
GetByteVectorSize(key_size) + // next_token.
kStatusEnumSize // Status.
);
const size_t expected_handles = 0;
EXPECT_TRUE(
CheckMessageSize(std::move(reader), expected_bytes, expected_handles));
}
TEST_F(SerializationSizeTest, GetEntries) {
PageSnapshotPtr snapshot_proxy;
FakeSnapshotImpl snapshot_impl;
zx::channel writer, reader;
ASSERT_EQ(ZX_OK, zx::channel::create(0, &writer, &reader));
snapshot_proxy.Bind(std::move(reader));
fidl::Binding<PageSnapshot> binding(&snapshot_impl);
binding.Bind(std::move(writer));
auto client_callback = [](Status /*status*/,
std::vector<Entry> /*entries*/,
std::unique_ptr<Token> /*next_token*/) {};
// FakeSnapshot saves the callback instead of running it.
snapshot_proxy->GetEntries(fidl::VectorPtr<uint8_t>::New(0), nullptr,
std::move(client_callback));
RunLoopUntilIdle();
fidl::InterfaceHandle<PageSnapshot> handle = snapshot_proxy.Unbind();
reader = handle.TakeChannel();
std::vector<Entry> entries_to_send;
const size_t key_size = 125;
const size_t value_size = 125;
const size_t n_entries = 10;
auto token = std::make_unique<Token>();
token->opaque_id = GetKey(0, key_size);
for (size_t i = 0; i < n_entries; i++) {
Entry entry;
std::string object_data = GetValue(0, value_size);
fsl::SizedVmo vmo;
ASSERT_TRUE(fsl::VmoFromString(object_data, &vmo));
entry.value = fidl::MakeOptional(std::move(vmo).ToTransport());
entry.key = GetKey(0, key_size);
entry.priority = Priority::EAGER;
entries_to_send.push_back(std::move(entry));
}
// Run the callback directly.
snapshot_impl.get_entries_callback(Status::OK, std::move(entries_to_send),
std::move(token));
const size_t expected_bytes =
Align(kMessageHeaderSize + // Header.
kVectorHeaderSize + // VectorPtr.
n_entries * GetEntrySize(key_size) + // Vector of entries.
kPointerSize + // Pointer to next_token.
GetByteVectorSize(key_size) + // next_token.
kStatusEnumSize // Status.
);
const size_t expected_handles = n_entries;
EXPECT_TRUE(
CheckMessageSize(std::move(reader), expected_bytes, expected_handles));
}
} // namespace
} // namespace fidl_serialization
} // namespace ledger