blob: 9983c21d2027a75ddec519a317b7a0b2da4223cb [file] [log] [blame]
// Copyright 2019 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 <fs/synchronous-vfs.h>
#include <fuchsia/inspect/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async_promise/executor.h>
#include <lib/fit/bridge.h>
#include <lib/gtest/real_loop_fixture.h>
#include <lib/inspect/inspect.h>
#include <lib/inspect/reader.h>
#include <lib/inspect/testing/inspect.h>
#include <thread>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace {
using inspect::Object;
using testing::ElementsAre;
using testing::IsEmpty;
using testing::UnorderedElementsAre;
using namespace inspect::testing;
const char kObjectsName[] = "objects";
class TestReader : public gtest::RealLoopFixture {
public:
TestReader()
: object_(component::Object::Make(kObjectsName)),
root_object_(component::ObjectDir(object_)),
executor_(dispatcher()),
server_loop_(&kAsyncLoopConfigNoAttachToThread) {
fuchsia::inspect::InspectSyncPtr ptr;
zx::channel server_channel = ptr.NewRequest().TakeChannel();
server_thread_ = std::thread([this, server_channel = std::move(
server_channel)]() mutable {
async_set_default_dispatcher(server_loop_.dispatcher());
fidl::Binding<fuchsia::inspect::Inspect> binding(
object_.get(), std::move(server_channel), server_loop_.dispatcher());
server_loop_.Run();
});
client_ = ptr.Unbind();
}
~TestReader() override {
server_loop_.Quit();
server_thread_.join();
}
void SchedulePromise(fit::pending_task promise) {
executor_.schedule_task(std::move(promise));
}
protected:
std::shared_ptr<component::Object> object_;
inspect::Object root_object_;
fidl::InterfaceHandle<fuchsia::inspect::Inspect> client_;
private:
async::Executor executor_;
std::thread server_thread_;
async::Loop server_loop_;
};
TEST_F(TestReader, Empty) {
inspect::ObjectReader reader(std::move(client_));
fit::result<fuchsia::inspect::Object> result;
SchedulePromise(
reader.Read().then([&](fit::result<fuchsia::inspect::Object>& res) {
result = std::move(res);
}));
ASSERT_TRUE(RunLoopUntil([&] { return !!result; }));
EXPECT_THAT(
inspect::ReadFromFidlObject(result.take_value()),
NodeMatches(AllOf(NameMatches(kObjectsName), MetricList(IsEmpty()),
PropertyList(IsEmpty()))));
}
TEST_F(TestReader, Values) {
auto metric_int = root_object_.CreateIntMetric("int", -10);
auto metric_uint = root_object_.CreateUIntMetric("uint", 10);
auto metric_double = root_object_.CreateDoubleMetric("double", 1.25);
auto prop_string = root_object_.CreateStringProperty("string", "value");
auto prop_bytes = root_object_.CreateByteVectorProperty(
"bytes", inspect::VectorValue(3, 'a'));
inspect::ObjectReader reader(std::move(client_));
fit::result<fuchsia::inspect::Object> result;
SchedulePromise(
reader.Read().then([&](fit::result<fuchsia::inspect::Object>& res) {
result = std::move(res);
}));
ASSERT_TRUE(RunLoopUntil([&] { return !!result; }));
EXPECT_THAT(
inspect::ReadFromFidlObject(result.take_value()),
NodeMatches(AllOf(
NameMatches(kObjectsName),
PropertyList(UnorderedElementsAre(
StringPropertyIs("string", "value"),
ByteVectorPropertyIs("bytes", inspect::VectorValue(3, 'a')))),
MetricList(UnorderedElementsAre(IntMetricIs("int", -10),
UIntMetricIs("uint", 10),
DoubleMetricIs("double", 1.25))))));
}
TEST_F(TestReader, ListChildren) {
auto child_a = root_object_.CreateChild("child a");
auto child_b = root_object_.CreateChild("child b");
inspect::ObjectReader reader(std::move(client_));
fit::result<inspect::ChildNameVector> result;
SchedulePromise(reader.ListChildren().then(
[&](fit::result<inspect::ChildNameVector>& res) {
result = std::move(res);
}));
ASSERT_TRUE(RunLoopUntil([&] { return !!result; }));
auto children = result.take_value();
EXPECT_THAT(*children, UnorderedElementsAre("child a", "child b"));
}
TEST_F(TestReader, OpenChild) {
auto child_a = root_object_.CreateChild("child a");
auto metric_a = child_a.CreateIntMetric("value", 1);
auto child_b = root_object_.CreateChild("child b");
inspect::ObjectReader reader(std::move(client_));
fit::result<fuchsia::inspect::Object> result;
SchedulePromise(reader.OpenChild("child a")
.and_then([](inspect::ObjectReader& child_reader) {
return child_reader.Read();
})
.then([&](fit::result<fuchsia::inspect::Object>& res) {
result = std::move(res);
}));
ASSERT_TRUE(RunLoopUntil([&] { return !!result; }));
EXPECT_THAT(inspect::ReadFromFidlObject(result.take_value()),
NodeMatches(AllOf(
NameMatches("child a"),
MetricList(UnorderedElementsAre(IntMetricIs("value", 1))))));
}
TEST_F(TestReader, OpenChildren) {
auto child_a = root_object_.CreateChild("child a");
auto metric_a = child_a.CreateIntMetric("value", 1);
auto child_b = root_object_.CreateChild("child b");
auto metric_b = child_b.CreateIntMetric("value", 1);
inspect::ObjectReader reader(std::move(client_));
std::vector<fit::result<fuchsia::inspect::Object>> result;
SchedulePromise(
reader.OpenChildren()
.and_then([](std::vector<inspect::ObjectReader>& child_reader) {
std::vector<fit::promise<fuchsia::inspect::Object>> promises;
for (auto& child : child_reader) {
promises.emplace_back(child.Read());
}
return fit::join_promise_vector(std::move(promises));
})
.and_then(
[&](std::vector<fit::result<fuchsia::inspect::Object>>& res) {
for (auto& r : res) {
result.emplace_back(std::move(r));
}
}));
ASSERT_TRUE(RunLoopUntil([&] { return result.size() == 2; }));
std::vector<std::string> names;
for (size_t i = 0; i < result.size(); i++) {
ASSERT_TRUE(result[i].is_ok());
auto obj = inspect::ReadFromFidlObject(result[i].take_value());
EXPECT_THAT(
obj,
NodeMatches(MetricList(UnorderedElementsAre(IntMetricIs("value", 1)))));
names.push_back(obj.node().name());
}
EXPECT_THAT(names, UnorderedElementsAre("child a", "child b"));
}
// Construct and expect this hierarchy for the following tests:
//
// objects:
// child a:
// value = 1
// child b:
// value = 2u
// child c:
// value = 3f
class TestHierarchy : public TestReader {
public:
TestHierarchy() {
child_a_ = root_object_.CreateChild("child a");
metric_a_ = child_a_.CreateIntMetric("value", 1);
child_b_ = root_object_.CreateChild("child b");
metric_b_ = child_b_.CreateUIntMetric("value", 2);
child_b_c_ = child_b_.CreateChild("child c");
metric_c_ = child_b_c_.CreateDoubleMetric("value", 3);
}
void ExpectHierarchy(const inspect::ObjectHierarchy& hierarchy) {
EXPECT_THAT(hierarchy.node(), AllOf(NameMatches(kObjectsName)));
EXPECT_THAT(
hierarchy.children(),
UnorderedElementsAre(
AllOf(NodeMatches(AllOf(NameMatches("child a"),
MetricList(UnorderedElementsAre(
IntMetricIs("value", 1))))),
ChildrenMatch(IsEmpty())),
AllOf(NodeMatches(AllOf(NameMatches("child b"),
MetricList(UnorderedElementsAre(
UIntMetricIs("value", 2))))),
ChildrenMatch(UnorderedElementsAre(AllOf(
NodeMatches(AllOf(NameMatches("child c"),
MetricList(UnorderedElementsAre(
DoubleMetricIs("value", 3))))),
ChildrenMatch(IsEmpty())))))));
auto* hierarchy_c = hierarchy.GetByPath({"child b", "child c"});
ASSERT_THAT(hierarchy_c, ::testing::NotNull());
};
private:
inspect::Object child_a_, child_b_, child_b_c_;
inspect::IntMetric metric_a_;
inspect::UIntMetric metric_b_;
inspect::DoubleMetric metric_c_;
};
TEST_F(TestHierarchy, ObjectHierarchy) {
fit::result<inspect::ObjectHierarchy> result;
SchedulePromise(
inspect::ReadFromFidl(inspect::ObjectReader(std::move(client_)))
.then([&](fit::result<inspect::ObjectHierarchy>& res) {
result = std::move(res);
}));
ASSERT_TRUE(RunLoopUntil([&] { return !!result; }));
auto hierarchy = result.take_value();
ExpectHierarchy(hierarchy);
}
TEST_F(TestHierarchy, ObjectHierarchyLimitDepth) {
fit::result<inspect::ObjectHierarchy> result;
SchedulePromise(inspect::ReadFromFidl(
inspect::ObjectReader(std::move(client_)), /*depth=*/1)
.then([&](fit::result<inspect::ObjectHierarchy>& res) {
result = std::move(res);
}));
ASSERT_TRUE(RunLoopUntil([&] { return !!result; }));
auto hierarchy = result.take_value();
EXPECT_THAT(hierarchy, ChildrenMatch(UnorderedElementsAre(
NodeMatches(AllOf(NameMatches("child a"))),
NodeMatches(AllOf(NameMatches("child b"))))));
auto* hierarchy_b = hierarchy.GetByPath({"child b"});
ASSERT_THAT(hierarchy_b, ::testing::NotNull());
EXPECT_THAT(*hierarchy_b, ChildrenMatch(IsEmpty()));
}
TEST_F(TestHierarchy, ObjectHierarchyDirect) {
auto hierarchy = inspect::ReadFromObject(root_object_);
ExpectHierarchy(hierarchy);
}
TEST_F(TestHierarchy, ObjectHierarchyDirectLimitDepth) {
auto hierarchy = inspect::ReadFromObject(root_object_, /*depth=*/1);
EXPECT_THAT(hierarchy, ChildrenMatch(UnorderedElementsAre(
NodeMatches(AllOf(NameMatches("child a"))),
NodeMatches(AllOf(NameMatches("child b"))))));
auto* hierarchy_b = hierarchy.GetByPath({"child b"});
ASSERT_THAT(hierarchy_b, ::testing::NotNull());
EXPECT_THAT(*hierarchy_b, ChildrenMatch(IsEmpty()));
}
} // namespace