| // 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 |