blob: e14308701444f02c738aef62a80fb484e75dcb62 [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 <lib/fpromise/single_threaded_executor.h>
#include <lib/inspect/cpp/hierarchy.h>
#include <lib/inspect/cpp/inspect.h>
#include <lib/inspect/cpp/inspector.h>
#include <lib/inspect/cpp/reader.h>
#include <lib/inspect/cpp/vmo/limits.h>
#include <lib/inspect/cpp/vmo/types.h>
#include <algorithm>
#include <memory>
#include <type_traits>
#include <vector>
#include <zxtest/zxtest.h>
using inspect::Inspector;
using inspect::Node;
namespace {
TEST(Inspect, CreateDeleteActive) {
Node node;
{
auto inspector = std::make_unique<Inspector>();
EXPECT_TRUE(inspector->DuplicateVmo().get() != ZX_HANDLE_INVALID);
EXPECT_TRUE(bool(*inspector));
node = inspector->GetRoot().CreateChild("node");
Node child = node.CreateChild("child");
EXPECT_TRUE(bool(child));
}
EXPECT_TRUE(bool(node));
Node child = node.CreateChild("child");
EXPECT_TRUE(bool(child));
}
TEST(Inspect, VmoName) {
char name[ZX_MAX_NAME_LEN];
auto inspector = std::make_unique<Inspector>();
auto state = inspect::internal::GetState(inspector.get());
EXPECT_OK(state->GetVmo().get_property(ZX_PROP_NAME, name, sizeof(name)));
EXPECT_EQ(std::string(name), "InspectHeap");
}
TEST(Inspect, RecordAndDropInspector) {
Inspector insp;
insp.GetRoot().CreateChild("name", &insp);
auto result = inspect::ReadFromVmo(insp.DuplicateVmo());
ASSERT_TRUE(result.is_ok());
auto hierarchy = result.take_value();
ASSERT_EQ(1u, hierarchy.children().size());
insp.ClearRecorded();
result = inspect::ReadFromVmo(insp.DuplicateVmo());
ASSERT_TRUE(result.is_ok());
hierarchy = result.take_value();
ASSERT_EQ(0u, hierarchy.children().size());
}
TEST(Inspect, CreateNodeWithLongStringReferences) {
auto inspector = std::make_unique<Inspector>();
const std::string long_with_extent_data(3000, '.');
const inspect::StringReference long_with_extent(long_with_extent_data.c_str());
const auto initial = inspector->GetStats().allocated_blocks;
constexpr auto number_nodes_created = 1000u;
std::vector<inspect::Node> nodes(number_nodes_created);
for (size_t i = 0; i < number_nodes_created; i++) {
nodes.push_back(inspector->GetRoot().CreateChild(long_with_extent));
}
EXPECT_EQ(initial + number_nodes_created + 2, inspector->GetStats().allocated_blocks);
auto result = inspect::ReadFromVmo(inspector->DuplicateVmo());
ASSERT_TRUE(result.is_ok());
auto hierarchy = result.take_value();
ASSERT_EQ(number_nodes_created, hierarchy.children().size());
std::for_each(std::cbegin(hierarchy.children()), std::cend(hierarchy.children()),
[&](const auto& child) { EXPECT_EQ(long_with_extent_data, child.name()); });
}
TEST(Inspect, CreateNodeWithLongNames) {
auto inspector = std::make_unique<Inspector>();
const std::string long_one_block("This will make an order 1 block");
const std::string long_with_extent(3000, '.');
const auto initial = inspector->GetStats().allocated_blocks;
auto child_one = inspector->GetRoot().CreateChild(long_one_block);
EXPECT_EQ(initial + 2, inspector->GetStats().allocated_blocks);
auto child_two = inspector->GetRoot().CreateChild(long_with_extent);
EXPECT_EQ(initial + 2 + 3, inspector->GetStats().allocated_blocks);
auto result = inspect::ReadFromVmo(inspector->DuplicateVmo());
ASSERT_TRUE(result.is_ok());
auto hierarchy = result.take_value();
ASSERT_EQ(2u, hierarchy.children().size());
EXPECT_EQ(long_one_block, hierarchy.children()[0].name());
EXPECT_EQ(long_with_extent, hierarchy.children()[1].name());
}
TEST(Inspect, MixStringReferencesWithRegularStrings) {
auto inspector = std::make_unique<Inspector>();
auto regular = inspector->GetRoot().CreateChild("regular");
auto as_ref = inspector->GetRoot().CreateChild(inspect::StringReference("reference"));
auto result = inspect::ReadFromVmo(inspector->DuplicateVmo());
ASSERT_TRUE(result.is_ok());
auto hierarchy = result.take_value();
ASSERT_EQ(2u, hierarchy.children().size());
EXPECT_EQ("regular", hierarchy.children()[0].name());
EXPECT_EQ("reference", hierarchy.children()[1].name());
}
TEST(Inspect, DeallocateStringReferencesThenAddMore) {
auto inspector = std::make_unique<Inspector>();
{
const inspect::StringReference sr1("first");
const inspect::StringReference sr2("second");
auto _ = inspector->GetRoot().CreateChild(sr1);
auto _i = inspector->GetRoot().CreateChild(sr2);
auto result = inspect::ReadFromVmo(inspector->DuplicateVmo());
ASSERT_TRUE(result.is_ok());
auto hierarchy = result.take_value();
ASSERT_EQ(2u, hierarchy.children().size());
EXPECT_EQ("first", hierarchy.children()[0].name());
EXPECT_EQ("second", hierarchy.children()[1].name());
}
const inspect::StringReference outer("outer");
auto _ = inspector->GetRoot().CreateChild(outer);
auto result = inspect::ReadFromVmo(inspector->DuplicateVmo());
ASSERT_TRUE(result.is_ok());
auto hierarchy = result.take_value();
ASSERT_EQ(1u, hierarchy.children().size());
EXPECT_EQ("outer", hierarchy.children()[0].name());
}
cpp17::optional<uint64_t> GetGenerationCount(const zx::vmo& vmo) {
uint8_t bytes[16];
vmo.read(bytes, 0, sizeof bytes);
const auto header = reinterpret_cast<inspect::internal::Block*>(bytes);
return header->payload.u64;
}
cpp17::optional<uint64_t> GetGenerationCount(Inspector* insp) {
const auto state = inspect::internal::GetState(insp);
if (state == nullptr) {
return {};
}
return GetGenerationCount(state->GetVmo());
}
TEST(Inspect, DoingFrozenVmoCopy) {
Inspector inspector;
inspector.GetRoot().CreateChild("child", &inspector);
inspector.GetRoot().CreateChild("child2", &inspector);
const auto result_one = GetGenerationCount(&inspector);
ASSERT_TRUE(result_one.has_value());
ASSERT_EQ(4, result_one.value());
auto frozen_vmo = inspector.FrozenVmoCopy();
ASSERT_TRUE(frozen_vmo.has_value());
const auto result = GetGenerationCount(frozen_vmo.value());
ASSERT_TRUE(result.has_value());
ASSERT_EQ(inspect::internal::kVmoFrozen, result.value());
auto original_vmo = inspector.DuplicateVmo();
for (int i = 0; i < 1000; i++) {
inspector.GetRoot().CreateInt(std::to_string(i), i, &inspector);
}
auto original = inspect::ReadFromVmo(original_vmo);
ASSERT_TRUE(original.is_ok());
auto frozen = inspect::ReadFromVmo(frozen_vmo.value());
ASSERT_TRUE(frozen.is_ok());
auto original_hierarchy = original.take_value();
auto frozen_hierarchy = frozen.take_value();
ASSERT_EQ(1000u, original_hierarchy.node().properties().size());
ASSERT_EQ(2u, frozen_hierarchy.children().size());
}
TEST(Inspect, TransactionsBlockCopy) {
Inspector inspector;
inspector.AtomicUpdate([&](Node& n) { ASSERT_FALSE(inspector.FrozenVmoCopy().has_value()); });
inspector.AtomicUpdate([&](Node& n) { ASSERT_FALSE(inspector.CopyVmo().has_value()); });
inspector.AtomicUpdate([&](Node& n) { ASSERT_FALSE(inspector.CopyBytes().has_value()); });
}
TEST(Inspect, UsingStringReferencesAsNames) {
auto inspector = std::make_unique<Inspector>();
const inspect::StringReference one("one");
const inspect::StringReference two("two");
auto child_one = inspector->GetRoot().CreateChild(one);
auto child_two = inspector->GetRoot().CreateChild(two);
const auto after_children = inspector->GetStats().allocated_blocks;
auto child_one_child_two = child_one.CreateChild(two);
auto child_two_child_one = child_two.CreateChild(one);
const auto after_more_children = inspector->GetStats().allocated_blocks;
// the +2 are the child blocks, note that no name/string_reference is allocated
EXPECT_EQ(after_children + 2, after_more_children);
{ auto c = child_one.CreateChild(one); }
// The 1 is the child created in the above block. Note that
// a new NAME or STRING_REFERENCE is *not* allocated and therefore
// not deallocated.
EXPECT_EQ(inspector->GetStats().deallocated_blocks, 1);
auto c = child_one.CreateChild(inspect::StringReference("a new string reference"));
auto result = inspect::ReadFromVmo(inspector->DuplicateVmo());
ASSERT_TRUE(result.is_ok());
auto hierarchy = result.take_value();
// children of root
ASSERT_EQ(2u, hierarchy.children().size());
EXPECT_EQ("one", hierarchy.children()[1].name());
EXPECT_EQ("two", hierarchy.children()[0].name());
// children of child_one
ASSERT_EQ(2u, hierarchy.children()[1].children().size());
EXPECT_EQ("two", hierarchy.children()[1].children()[0].name());
EXPECT_EQ("a new string reference", hierarchy.children()[1].children()[1].name());
// children of child_two
ASSERT_EQ(1u, hierarchy.children()[0].children().size());
EXPECT_EQ("one", hierarchy.children()[0].children()[0].name());
// Inspector::State::~Heap will ensure that release is done properly,
// so this unit test ensures that StringReferences are correctly refcounted
// and released/destroyed/deallocated.
}
TEST(Inspect, CreateLazyNodeWithStringReferences) {
const inspect::StringReference lazy("lazy");
Inspector inspector;
inspector.GetRoot().CreateLazyNode(
lazy,
[] {
Inspector insp;
insp.GetRoot().CreateInt("val", 10, &insp);
return fpromise::make_ok_promise(insp);
},
&inspector);
auto children = inspector.GetChildNames();
ASSERT_EQ(1u, children.size());
EXPECT_EQ("lazy-0", children[0]);
auto stats = inspector.GetStats();
EXPECT_EQ(1u, stats.dynamic_child_count);
fpromise::result<Inspector> result;
fpromise::single_threaded_executor exec;
exec.schedule_task(inspector.OpenChild("lazy-0").then(
[&](fpromise::result<Inspector>& res) { result = std::move(res); }));
exec.run();
EXPECT_TRUE(result.is_ok());
}
TEST(Inspect, CreateChildren) {
auto inspector = std::make_unique<Inspector>();
Node child = inspector->GetRoot().CreateChild("child");
EXPECT_TRUE(bool(child));
auto result = inspect::ReadFromVmo(inspector->DuplicateVmo());
ASSERT_TRUE(result.is_ok());
auto hierarchy = result.take_value();
ASSERT_EQ(1u, hierarchy.children().size());
EXPECT_EQ("child", hierarchy.children()[0].name());
}
TEST(Inspect, CreateCopyVmo) {
auto inspector = std::make_unique<Inspector>();
// Store a string.
std::string s = "abcd";
auto property = inspector->GetRoot().CreateString("string", s);
auto result = inspect::ReadFromVmo(inspector->CopyVmo().value());
ASSERT_TRUE(result.is_ok());
auto hierarchy = result.take_value();
auto* string_value = hierarchy.node().get_property<inspect::StringPropertyValue>("string");
ASSERT_TRUE(string_value != nullptr);
EXPECT_EQ(s, string_value->value());
}
constexpr size_t kPageSize = 4096;
TEST(Inspect, CreateGetStats) {
// Limit to 2 pages.
Inspector inspector(inspect::InspectSettings{.maximum_size = 2 * kPageSize});
auto stats = inspector.GetStats();
EXPECT_EQ(1 * kPageSize, stats.size);
EXPECT_EQ(2 * kPageSize, stats.maximum_size);
EXPECT_EQ(0, stats.dynamic_child_count);
// Fill up the buffer
for (int i = 0; i < 1000; i++) {
inspector.GetRoot().CreateString(std::to_string(i), "This is a test", &inspector);
}
stats = inspector.GetStats();
EXPECT_EQ(2 * kPageSize, stats.size);
EXPECT_EQ(2 * kPageSize, stats.maximum_size);
EXPECT_EQ(0, stats.dynamic_child_count);
}
TEST(Inspect, StringArrays) {
Inspector inspector;
const inspect::StringReference array_name("array_name");
const inspect::StringReference shared_ref("shared_ref");
const std::string big(inspect::internal::kMaxOrderSize + 1, '.');
auto property = inspector.GetRoot().CreateInt(shared_ref, 5);
{
auto array = inspector.GetRoot().CreateStringArray(array_name, 5);
array.Set(0, "zero");
array.Set(1, shared_ref);
array.Set(3, big);
auto bytes = inspector.CopyBytes().value();
auto result = inspect::ReadFromBuffer(std::move(bytes));
ASSERT_TRUE(result.is_ok());
auto hierarchy = result.take_value();
hierarchy.Sort();
EXPECT_EQ(0u, hierarchy.children().size());
EXPECT_EQ(2u, hierarchy.node().properties().size());
const auto& int_prop = hierarchy.node().properties().at(1);
const auto& array_prop = hierarchy.node().properties().at(0);
EXPECT_EQ(shared_ref.Data(), int_prop.name());
EXPECT_EQ(array_name.Data(), array_prop.name());
const auto& array_data = array_prop.Get<inspect::StringArrayValue>().value();
EXPECT_EQ("zero", array_data.at(0));
EXPECT_EQ(shared_ref.Data(), array_data.at(1));
EXPECT_TRUE(array_data.at(2).empty());
EXPECT_EQ(big, array_data.at(3));
EXPECT_TRUE(array_data.at(4).empty());
}
auto bytes = inspector.CopyBytes().value();
auto result = inspect::ReadFromBuffer(std::move(bytes));
ASSERT_TRUE(result.is_ok());
auto hierarchy = result.take_value();
hierarchy.Sort();
EXPECT_EQ(0u, hierarchy.children().size());
EXPECT_EQ(1u, hierarchy.node().properties().size());
const auto& int_prop = hierarchy.node().properties().at(0);
EXPECT_EQ(shared_ref.Data(), int_prop.name());
}
namespace {
const char* FUCHSIA_INSPECT_STATS = "fuchsia.inspect.Stats-0";
}
void ReadChildHierarchy(Inspector* inspector, inspect::Hierarchy* hierarchy, std::string name) {
fpromise::result<Inspector> res1;
fpromise::single_threaded_executor exec;
exec.schedule_task(inspector->OpenChild(name).then(
[&](fpromise::result<Inspector>& res) { res1 = std::move(res); }));
exec.run();
ASSERT_TRUE(res1.is_ok());
auto res2 = inspect::ReadFromVmo(res1.value().DuplicateVmo());
ASSERT_TRUE(res2.is_ok());
*hierarchy = res2.take_value();
}
void CheckStats(const inspect::Hierarchy* hierarchy, const inspect::InspectStats* expected) {
auto current_size_value =
hierarchy->node().get_property<inspect::UintPropertyValue>("current_size");
ASSERT_TRUE(current_size_value != nullptr);
ASSERT_EQ(expected->size, current_size_value->value());
auto maximum_size_value =
hierarchy->node().get_property<inspect::UintPropertyValue>("maximum_size");
ASSERT_TRUE(maximum_size_value != nullptr);
ASSERT_EQ(expected->maximum_size, maximum_size_value->value());
auto total_dynamic_children_value =
hierarchy->node().get_property<inspect::UintPropertyValue>("total_dynamic_children");
ASSERT_TRUE(total_dynamic_children_value != nullptr);
ASSERT_EQ(expected->dynamic_child_count, total_dynamic_children_value->value());
auto allocated_blocks_value =
hierarchy->node().get_property<inspect::UintPropertyValue>("allocated_blocks");
ASSERT_TRUE(allocated_blocks_value != nullptr);
ASSERT_EQ(expected->allocated_blocks, allocated_blocks_value->value());
auto deallocated_blocks_value =
hierarchy->node().get_property<inspect::UintPropertyValue>("deallocated_blocks");
ASSERT_TRUE(deallocated_blocks_value != nullptr);
ASSERT_EQ(expected->deallocated_blocks, deallocated_blocks_value->value());
auto failed_allocations_value =
hierarchy->node().get_property<inspect::UintPropertyValue>("failed_allocations");
ASSERT_TRUE(failed_allocations_value != nullptr);
ASSERT_EQ(expected->failed_allocations, failed_allocations_value->value());
}
TEST(Inspect, CreateStatsNode) {
// Limit to 2 pages.
Inspector inspector(inspect::InspectSettings{.maximum_size = 2 * kPageSize});
inspector.CreateStatsNode();
auto children = inspector.GetChildNames();
ASSERT_EQ(1u, children.size());
EXPECT_EQ(FUCHSIA_INSPECT_STATS, children[0]);
inspect::Hierarchy hierarchy;
ReadChildHierarchy(&inspector, &hierarchy, FUCHSIA_INSPECT_STATS);
inspect::InspectStats expected = {};
expected.size = 1 * kPageSize;
expected.maximum_size = 2 * kPageSize;
expected.dynamic_child_count = 1u;
expected.allocated_blocks = 4u;
expected.deallocated_blocks = 0u;
expected.failed_allocations = 0u;
CheckStats(&hierarchy, &expected);
for (int i = 0; i < 100; i++) {
inspector.GetRoot().CreateString(std::to_string(i), "This is a test", &inspector);
}
ReadChildHierarchy(&inspector, &hierarchy, FUCHSIA_INSPECT_STATS);
expected.size = 2 * kPageSize;
expected.maximum_size = 2 * kPageSize;
expected.dynamic_child_count = 1u;
expected.allocated_blocks = 304u;
expected.deallocated_blocks = 0u;
expected.failed_allocations = 0u;
CheckStats(&hierarchy, &expected);
for (int i = 101; i < 128; i++) {
inspector.GetRoot().CreateString(std::to_string(i), "This is a test", &inspector);
}
ReadChildHierarchy(&inspector, &hierarchy, FUCHSIA_INSPECT_STATS);
expected.size = 2 * kPageSize;
expected.maximum_size = 2 * kPageSize;
expected.dynamic_child_count = 1u;
expected.allocated_blocks = 381u;
expected.deallocated_blocks = 2u;
expected.failed_allocations = 2u;
CheckStats(&hierarchy, &expected);
}
TEST(Inspect, GetLinks) {
Inspector inspector;
inspector.GetRoot().CreateLazyNode(
"lazy",
[] {
Inspector insp;
insp.GetRoot().CreateInt("val", 10, &insp);
return fpromise::make_ok_promise(insp);
},
&inspector);
auto children = inspector.GetChildNames();
ASSERT_EQ(1u, children.size());
EXPECT_EQ("lazy-0", children[0]);
auto stats = inspector.GetStats();
EXPECT_EQ(1u, stats.dynamic_child_count);
fpromise::result<Inspector> result;
fpromise::single_threaded_executor exec;
exec.schedule_task(inspector.OpenChild("lazy-0").then(
[&](fpromise::result<Inspector>& res) { result = std::move(res); }));
exec.run();
EXPECT_TRUE(result.is_ok());
}
TEST(Inspect, LinksAreDestructed) {
Inspector inspector;
auto ln = inspector.GetRoot().CreateLazyNode("lazy", [] {
Inspector insp;
insp.GetRoot().CreateInt("val", 10, &insp);
return fpromise::make_ok_promise(insp);
});
auto children = inspector.GetChildNames();
ASSERT_EQ(1u, children.size());
EXPECT_EQ("lazy-0", children[0]);
auto stats = inspector.GetStats();
EXPECT_EQ(1u, stats.dynamic_child_count);
fpromise::result<Inspector> result;
fpromise::single_threaded_executor exec;
exec.schedule_task(inspector.OpenChild("lazy-0").then(
[&](fpromise::result<Inspector>& res) { result = std::move(res); }));
exec.run();
EXPECT_TRUE(result.is_ok());
// clear node, shouldn't be in Inspector
ln = inspect::LazyNode();
children = inspector.GetChildNames();
EXPECT_EQ(0u, children.size());
stats = inspector.GetStats();
EXPECT_EQ(0u, stats.dynamic_child_count);
}
TEST(Inspect, LazyNodesAreDestructed) {
Inspector inspector;
auto ln = inspector.GetRoot().CreateLazyNode("lazy", [] {
Inspector insp;
insp.GetRoot().CreateInt("val", 10, &insp);
return fpromise::make_ok_promise(std::move(insp));
});
fpromise::result<inspect::Hierarchy> result;
fpromise::single_threaded_executor exec;
exec.schedule_task(inspect::ReadFromInspector(inspector).then(
[&](fpromise::result<inspect::Hierarchy>& res) { result = std::move(res); }));
exec.run();
ASSERT_TRUE(result.is_ok());
auto hierarchy = result.take_value();
EXPECT_EQ(1, hierarchy.children().size());
EXPECT_EQ("lazy", hierarchy.children()[0].name());
EXPECT_EQ(
10, hierarchy.children()[0].node().properties()[0].Get<inspect::IntPropertyValue>().value());
// this should clear the node
ln = inspect::LazyNode();
// inspector is empty
exec.schedule_task(inspect::ReadFromInspector(inspector).then(
[&](fpromise::result<inspect::Hierarchy>& res) { result = std::move(res); }));
exec.run();
ASSERT_TRUE(result.is_ok());
hierarchy = result.take_value();
EXPECT_EQ(0, hierarchy.children().size());
}
TEST(Inspect, NamedLazyNodesAreDestructed) {
Inspector inspector;
auto ln = inspector.GetRoot().CreateLazyNode("lazy", [] {
Inspector insp;
insp.GetRoot().CreateInt("val", 10, &insp);
return fpromise::make_ok_promise(std::move(insp));
});
auto second = inspector.GetRoot().CreateLazyNode("lazy-2", [] {
Inspector insp;
insp.GetRoot().CreateInt("val-2", 10, &insp);
return fpromise::make_ok_promise(std::move(insp));
});
fpromise::result<inspect::Hierarchy> result;
fpromise::single_threaded_executor exec;
exec.schedule_task(inspect::ReadFromInspector(inspector).then(
[&](fpromise::result<inspect::Hierarchy>& res) { result = std::move(res); }));
exec.run();
ASSERT_TRUE(result.is_ok());
auto hierarchy = result.take_value();
EXPECT_EQ(2, hierarchy.children().size());
EXPECT_EQ("lazy", hierarchy.children()[0].name());
EXPECT_EQ("lazy-2", hierarchy.children()[1].name());
EXPECT_EQ(
10, hierarchy.children()[0].node().properties()[0].Get<inspect::IntPropertyValue>().value());
EXPECT_EQ(
10, hierarchy.children()[1].node().properties()[0].Get<inspect::IntPropertyValue>().value());
// remove second from VMO, replacing it with contents of ln
// result is as if we had removed second
second = std::move(ln);
exec.schedule_task(inspect::ReadFromInspector(inspector).then(
[&](fpromise::result<inspect::Hierarchy>& res) { result = std::move(res); }));
exec.run();
ASSERT_TRUE(result.is_ok());
hierarchy = result.take_value();
EXPECT_EQ(1, hierarchy.children().size());
EXPECT_EQ("lazy", hierarchy.children()[0].name());
EXPECT_EQ(
10, hierarchy.children()[0].node().properties()[0].Get<inspect::IntPropertyValue>().value());
}
TEST(Inspect, CreateCopyBytes) {
// Make a 16MB heap.
auto inspector = std::make_unique<Inspector>();
// Store a string.
std::string s = "abcd";
auto property = inspector->GetRoot().CreateString("string", s);
auto bytes = inspector->CopyBytes().value();
auto result = inspect::ReadFromBuffer(std::move(bytes));
ASSERT_TRUE(result.is_ok());
auto hierarchy = result.take_value();
EXPECT_EQ(s, hierarchy.node().properties()[0].Get<inspect::StringPropertyValue>().value());
}
TEST(Inspect, CreateLargeHeap) {
// Make a 16MB heap.
auto inspector =
std::make_unique<Inspector>(inspect::InspectSettings{.maximum_size = 16 * 1024 * 1024});
// Store a 4MB string.
std::string s(4 * 1024 * 1024, 'a');
auto property = inspector->GetRoot().CreateString("big_string", s);
auto result = inspect::ReadFromVmo(inspector->DuplicateVmo());
ASSERT_TRUE(result.is_ok());
auto hierarchy = result.take_value();
EXPECT_EQ(s, hierarchy.node().properties()[0].Get<inspect::StringPropertyValue>().value());
}
TEST(Inspect, CreateInvalidSize) {
auto inspector = std::make_unique<Inspector>(inspect::InspectSettings{.maximum_size = 0});
EXPECT_TRUE(inspector->DuplicateVmo().get() == ZX_HANDLE_INVALID);
EXPECT_FALSE(bool(inspector->GetRoot()));
EXPECT_FALSE(bool(*inspector));
}
TEST(Inspect, CreateWithVmoInvalidSize) {
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(0 /* size */, 0, &vmo));
Inspector inspector(std::move(vmo));
auto stats = inspector.GetStats();
EXPECT_EQ(0, stats.size);
EXPECT_EQ(0, stats.maximum_size);
EXPECT_EQ(0, stats.dynamic_child_count);
EXPECT_FALSE(bool(inspector));
}
TEST(Inspect, CreateWithVmoReadOnly) {
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(4096 /* size */, 0, &vmo));
zx::vmo duplicate;
ASSERT_OK(vmo.duplicate(ZX_RIGHTS_BASIC | ZX_RIGHT_READ, &duplicate));
Inspector inspector(std::move(duplicate));
auto stats = inspector.GetStats();
EXPECT_EQ(0, stats.size);
EXPECT_EQ(0, stats.maximum_size);
EXPECT_EQ(0, stats.dynamic_child_count);
EXPECT_FALSE(bool(inspector));
}
TEST(Inspect, CreateWithVmoDuplicateVmo) {
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(4096 /* size */, 0, &vmo));
zx::vmo duplicate;
ASSERT_OK(
vmo.duplicate(ZX_RIGHTS_BASIC | ZX_RIGHT_READ | ZX_RIGHT_WRITE | ZX_RIGHT_MAP, &duplicate));
Inspector inspector(std::move(duplicate));
auto stats = inspector.GetStats();
EXPECT_EQ(4096, stats.size);
EXPECT_EQ(4096, stats.maximum_size);
EXPECT_TRUE(bool(inspector));
}
TEST(Inspect, CreateWithDirtyVmo) {
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(4096 /* size */, 0, &vmo));
// Write data into the VMO before using it, internally we will decommit
// the pages to zero them.
std::vector<uint8_t> bytes(4096, 'a');
ASSERT_OK(vmo.write(bytes.data(), 0, bytes.size()));
Inspector inspector(std::move(vmo));
ASSERT_TRUE(bool(inspector));
auto val = inspector.GetRoot().CreateUint("test", 100);
auto result = inspect::ReadFromVmo(inspector.DuplicateVmo());
ASSERT_TRUE(result.is_ok());
auto hierarchy = result.take_value();
EXPECT_EQ(1, hierarchy.node().properties().size());
EXPECT_EQ("test", hierarchy.node().properties()[0].name());
EXPECT_EQ(100, hierarchy.node().properties()[0].Get<inspect::UintPropertyValue>().value());
auto stats = inspector.GetStats();
EXPECT_EQ(4096, stats.size);
EXPECT_EQ(4096, stats.maximum_size);
}
TEST(Inspect, UniqueName) {
inspect::Inspector inspector1, inspector2;
EXPECT_EQ("root0x0", inspector1.GetRoot().UniqueName("root"));
EXPECT_EQ("root0x1", inspector1.GetRoot().UniqueName("root"));
EXPECT_EQ("root0x2", inspector1.GetRoot().UniqueName("root"));
EXPECT_EQ("test0x3", inspector1.GetRoot().UniqueName("test"));
EXPECT_EQ("test0x4", inspector1.GetRoot().UniqueName("test"));
EXPECT_EQ("test0x5", inspector1.GetRoot().UniqueName("test"));
EXPECT_EQ("test0x6", inspector1.GetRoot().UniqueName("test"));
EXPECT_EQ("test0x7", inspector1.GetRoot().UniqueName("test"));
EXPECT_EQ("test0x8", inspector1.GetRoot().UniqueName("test"));
EXPECT_EQ("test0x9", inspector1.GetRoot().UniqueName("test"));
EXPECT_EQ("test0xa", inspector1.GetRoot().UniqueName("test"));
EXPECT_EQ("test0xb", inspector1.GetRoot().UniqueName("test"));
EXPECT_EQ("test0xc", inspector1.GetRoot().UniqueName("test"));
EXPECT_EQ("test0xd", inspector1.GetRoot().UniqueName("test"));
EXPECT_EQ("test0xe", inspector1.GetRoot().UniqueName("test"));
EXPECT_EQ("test0xf", inspector1.GetRoot().UniqueName("test"));
EXPECT_EQ("test0x10", inspector1.GetRoot().UniqueName("test"));
EXPECT_EQ("root0x0", inspector2.GetRoot().UniqueName("root"));
}
TEST(Inspect, UniqueNameNoop) {
inspect::Node noop;
EXPECT_EQ("", noop.UniqueName("root"));
EXPECT_EQ("", noop.UniqueName("test"));
}
} // namespace