| // 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 <fuchsia/io/cpp/fidl.h> |
| #include <lib/async/default.h> |
| #include <lib/fdio/directory.h> |
| #include <lib/fdio/fd.h> |
| #include <lib/fdio/fdio.h> |
| #include <lib/inspect/cpp/reader.h> |
| #include <lib/inspect/testing/cpp/inspect.h> |
| |
| #include "gmock/gmock.h" |
| #include "lib/sys/cpp/testing/test_with_environment_fixture.h" |
| #include "src/lib/files/glob.h" |
| #include "src/lib/fxl/strings/substitute.h" |
| |
| namespace { |
| |
| using ::fxl::Substitute; |
| using sys::testing::EnclosingEnvironment; |
| using ::testing::UnorderedElementsAre; |
| using namespace inspect::testing; |
| |
| constexpr char kTestComponent[] = |
| "fuchsia-pkg://fuchsia.com/dart-inspect-vmo-test-writer#meta/" |
| "dart-inspect-vmo-test-writer.cmx"; |
| constexpr char kTestProcessName[] = "dart-inspect-vmo-test-writer.cmx"; |
| |
| class InspectTest : public gtest::TestWithEnvironmentFixture { |
| protected: |
| InspectTest() { |
| fuchsia::sys::LaunchInfo launch_info; |
| launch_info.url = kTestComponent; |
| |
| environment_ = CreateNewEnclosingEnvironment("test", CreateServices()); |
| environment_->CreateComponent(std::move(launch_info), controller_.NewRequest()); |
| bool ready = false; |
| controller_.events().OnDirectoryReady = [&ready] { ready = true; }; |
| RunLoopWithTimeoutOrUntil([&ready] { return ready; }, zx::sec(100)); |
| if (!ready) { |
| printf("The output directory is not ready\n"); |
| } |
| } |
| ~InspectTest() { CheckShutdown(); } |
| |
| void CheckShutdown() { |
| controller_->Kill(); |
| bool done = false; |
| controller_.events().OnTerminated = [&done](int64_t code, |
| fuchsia::sys::TerminationReason reason) { |
| ASSERT_EQ(fuchsia::sys::TerminationReason::EXITED, reason); |
| done = true; |
| }; |
| ASSERT_TRUE(RunLoopWithTimeoutOrUntil([&done] { return done; }, zx::sec(100))); |
| } |
| |
| // Open the root object connection on the given sync pointer. |
| // Returns ZX_OK on success. |
| fpromise::result<fuchsia::io::FileSyncPtr, zx_status_t> OpenInspectVmoFile( |
| const std::string& file_name) { |
| files::Glob glob(Substitute("/hub/r/test/*/c/*/*/c/$0/*/out/diagnostics/$1.inspect", |
| kTestProcessName, file_name)); |
| if (glob.size() == 0) { |
| printf("Size == 0\n"); |
| return fpromise::error(ZX_ERR_NOT_FOUND); |
| } |
| |
| fuchsia::io::FileSyncPtr file; |
| auto status = fdio_open(std::string(*glob.begin()).c_str(), |
| static_cast<uint32_t>(fuchsia::io::OpenFlags::RIGHT_READABLE), |
| file.NewRequest().TakeChannel().release()); |
| if (status != ZX_OK) { |
| printf("Status bad %d\n", status); |
| return fpromise::error(status); |
| } |
| |
| EXPECT_TRUE(file.is_bound()); |
| |
| return fpromise::ok(std::move(file)); |
| } |
| |
| fpromise::result<zx::vmo, zx_status_t> DescribeInspectVmoFile( |
| const fuchsia::io::FileSyncPtr& file) { |
| fuchsia::io::NodeInfo info; |
| auto status = file->Describe(&info); |
| if (status != ZX_OK) { |
| printf("get failed\n"); |
| return fpromise::error(status); |
| } |
| |
| if (!info.is_vmofile()) { |
| printf("not a vmofile"); |
| return fpromise::error(ZX_ERR_NOT_FOUND); |
| } |
| |
| return fpromise::ok(std::move(info.vmofile().vmo)); |
| } |
| |
| private: |
| std::unique_ptr<EnclosingEnvironment> environment_; |
| fuchsia::sys::ComponentControllerPtr controller_; |
| }; |
| |
| TEST_F(InspectTest, ReadHierarchy) { |
| auto open_file_result(InspectTest::OpenInspectVmoFile("root")); |
| ASSERT_TRUE(open_file_result.is_ok()); |
| fuchsia::io::FileSyncPtr file(open_file_result.take_value()); |
| auto describe_file_result = InspectTest::DescribeInspectVmoFile(file); |
| ASSERT_TRUE(describe_file_result.is_ok()); |
| zx::vmo vmo(describe_file_result.take_value()); |
| auto read_file_result = inspect::ReadFromVmo(std::move(vmo)); |
| ASSERT_TRUE(read_file_result.is_ok()); |
| inspect::Hierarchy hierarchy = read_file_result.take_value(); |
| |
| // TODO(36155): Remove this once root migration is complete. |
| auto* real_hierarchy = hierarchy.GetByPath({"root"}); |
| if (real_hierarchy == nullptr) { |
| real_hierarchy = &hierarchy; |
| } |
| |
| EXPECT_THAT( |
| *real_hierarchy, |
| AllOf( |
| NodeMatches(NameMatches("root")), |
| ChildrenMatch(UnorderedElementsAre( |
| AllOf(NodeMatches( |
| AllOf(NameMatches("runner"), |
| PropertyList(UnorderedElementsAre(StringIs("vm_service_port", "")))))), |
| AllOf(NodeMatches(AllOf(NameMatches("t1"), |
| PropertyList(UnorderedElementsAre( |
| StringIs("version", "1.0"), |
| ByteVectorIs("frame", std::vector<uint8_t>({0, 0, 0})), |
| IntIs("value", -10), BoolIs("active", true))))), |
| ChildrenMatch(UnorderedElementsAre( |
| NodeMatches(AllOf(NameMatches("item-0x0"), |
| PropertyList(UnorderedElementsAre(IntIs("value", 10))))), |
| NodeMatches(AllOf(NameMatches("item-0x1"), |
| PropertyList(UnorderedElementsAre(IntIs("value", 100))))) |
| |
| ))), |
| AllOf(NodeMatches(AllOf(NameMatches("t2"), |
| PropertyList(UnorderedElementsAre( |
| StringIs("version", "1.0"), |
| ByteVectorIs("frame", std::vector<uint8_t>({0, 0, 0})), |
| IntIs("value", -10), BoolIs("active", true))))), |
| ChildrenMatch(UnorderedElementsAre( |
| NodeMatches(AllOf(NameMatches("item-0x2"), |
| PropertyList(UnorderedElementsAre(IntIs("value", 4))))))) |
| |
| ))))); |
| } |
| |
| TEST_F(InspectTest, DynamicGeneratesNewHierarchy) { |
| auto open_file_result(OpenInspectVmoFile("digits_of_numbers")); |
| ASSERT_TRUE(open_file_result.is_ok()); |
| fuchsia::io::FileSyncPtr file(open_file_result.take_value()); |
| |
| std::vector<std::string> increments_value; |
| std::vector<std::string> doubles_value; |
| auto expectInspectOnDemandVmoFile = [&]() { |
| auto describe_file_result(DescribeInspectVmoFile(file)); |
| ASSERT_TRUE(describe_file_result.is_ok()); |
| zx::vmo vmo(describe_file_result.take_value()); |
| auto read_file_result = inspect::ReadFromVmo(std::move(vmo)); |
| ASSERT_TRUE(read_file_result.is_ok()); |
| inspect::Hierarchy hierarchy = read_file_result.take_value(); |
| |
| // TODO(36155): Remove this once root migration is complete. |
| auto* real_hierarchy = hierarchy.GetByPath({"root"}); |
| if (real_hierarchy == nullptr) { |
| real_hierarchy = &hierarchy; |
| } |
| |
| EXPECT_THAT(*real_hierarchy, |
| AllOf(NodeMatches(NameMatches("root")), |
| ChildrenMatch(UnorderedElementsAre( |
| NodeMatches(AllOf( // child one |
| NameMatches("increments"), |
| PropertyList(UnorderedElementsAre( |
| StringIs("value", ::testing::Truly([&](const std::string& val) { |
| increments_value.push_back(val); |
| return true; |
| })))))), |
| NodeMatches(AllOf( // child two |
| NameMatches("doubles"), |
| PropertyList(UnorderedElementsAre( |
| StringIs("value", ::testing::Truly([&](const std::string& val) { |
| doubles_value.push_back(val); |
| return true; |
| })))))))))); |
| }; |
| |
| expectInspectOnDemandVmoFile(); |
| expectInspectOnDemandVmoFile(); |
| |
| ASSERT_EQ(2u, increments_value.size()); |
| ASSERT_EQ(2u, doubles_value.size()); |
| |
| EXPECT_NE(increments_value[0], increments_value[1]); |
| EXPECT_NE(doubles_value[0], doubles_value[1]); |
| } |
| } // namespace |