blob: fcc4470525178fd125867afa349c57fdb5cd4b9c [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 "src/lib/inspect_deprecated/query/source.h"
#include <lib/fdio/directory.h>
#include <lib/vfs/cpp/vmo_file.h>
#include <src/lib/files/file.h>
#include "fixture.h"
#include "fuchsia/io/cpp/fidl.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "lib/fidl/cpp/binding.h"
#include "lib/fidl/cpp/binding_set.h"
#include "lib/fidl/cpp/interface_request.h"
#include "src/lib/fxl/strings/concatenate.h"
#include "src/lib/fxl/strings/join_strings.h"
#include "src/lib/fxl/strings/substitute.h"
#include "src/lib/inspect_deprecated/hierarchy.h"
#include "src/lib/inspect_deprecated/inspect.h"
#include "src/lib/inspect_deprecated/query/location.h"
#include "src/lib/inspect_deprecated/reader.h"
#include "src/lib/inspect_deprecated/testing/inspect.h"
using namespace inspect_deprecated::testing;
namespace {
class TestDataWrapper {
public:
explicit TestDataWrapper(inspect_deprecated::Node object) : object_(std::move(object)) {
version_ = object_.CreateStringProperty("version", "1.0");
child_test_ = object_.CreateChild("test");
count_ = child_test_.CreateIntMetric("count", 2);
nested_child_ = child_test_.CreateChild("nested");
}
private:
inspect_deprecated::Node object_;
inspect_deprecated::Node child_test_;
inspect_deprecated::Node nested_child_;
inspect_deprecated::StringProperty version_;
inspect_deprecated::IntMetric count_;
};
void CheckHierarchyMatches(const inspect_deprecated::ObjectHierarchy& hierarchy) {
EXPECT_THAT(hierarchy,
::testing::AllOf(
NodeMatches(::testing::AllOf(
NameMatches("root"),
PropertyList(::testing::ElementsAre(StringPropertyIs("version", "1.0"))))),
ChildrenMatch(::testing::ElementsAre(::testing::AllOf(
ChildrenMatch(::testing::ElementsAre(NodeMatches(NameMatches("nested")))),
NodeMatches(::testing::AllOf(
NameMatches("test"),
MetricList(::testing::ElementsAre(IntMetricIs("count", 2))))))))));
}
class SourceTestFidl : public TestFixture {
public:
SourceTestFidl()
: fidl_dir_(component::ObjectDir::Make("root")),
test_data_(inspect_deprecated::Node(fidl_dir_)),
binding_(fidl_dir_.object().get()) {
binding_.Bind(ptr_.NewRequest().TakeChannel());
}
fit::result<inspect_deprecated::Source, std::string> MakeFromPath(std::string path,
int depth = -1) {
fit::result<inspect_deprecated::Source, std::string> result;
SchedulePromise(inspect_deprecated::Source::MakeFromFidl(
inspect_deprecated::Location::Parse(path).take_value(),
inspect_deprecated::ObjectReader(std::move(ptr_)), depth)
.then([&result](fit::result<inspect_deprecated::Source, std::string>& res) {
result = std::move(res);
}));
RunLoopUntil([&result] { return !!result; });
return result;
}
const std::string RootPath = "/test";
protected:
component::ObjectDir fidl_dir_;
TestDataWrapper test_data_;
fidl::Binding<fuchsia::inspect::deprecated::Inspect> binding_;
fuchsia::inspect::deprecated::InspectPtr ptr_;
};
class SourceTestVmo : public TestFixture {
public:
SourceTestVmo()
: inspector_(),
tree_(inspector_.CreateTree()),
vmo_file_(tree_.DuplicateVmo(), 0, 4096),
test_data_(std::move(tree_.GetRoot())) {
ZX_ASSERT(vmo_file_.Serve(fuchsia::io::OPEN_RIGHT_READABLE,
file_ptr_.NewRequest().TakeChannel()) == ZX_OK);
}
fit::result<inspect_deprecated::Source, std::string> MakeFromPath(std::string path,
int depth = -1) {
fit::result<inspect_deprecated::Source, std::string> result;
SchedulePromise(
inspect_deprecated::Source::MakeFromVmo(
inspect_deprecated::Location::Parse(path).take_value(), std::move(file_ptr_), depth)
.then([&result](fit::result<inspect_deprecated::Source, std::string>& res) {
result = std::move(res);
}));
RunLoopUntil([&result] { return !!result; });
return result;
}
const std::string RootPath = "/test/root.inspect";
protected:
inspect_deprecated::Inspector inspector_;
inspect_deprecated::Tree tree_;
vfs::VmoFile vmo_file_;
TestDataWrapper test_data_;
fuchsia::io::FilePtr file_ptr_;
};
class SourceTestFile : public SourceTestVmo {
public:
fit::result<inspect_deprecated::Source, std::string> MakeFromPath(std::string path,
int depth = -1) {
fit::result<std::string, std::string> path_or = WriteFromVmo(path);
if (path_or.is_error()) {
return fit::error(path_or.error());
}
fuchsia::io::FilePtr file_backed_ptr;
zx_status_t status = fdio_open(path_or.value().c_str(), fuchsia::io::OPEN_RIGHT_READABLE,
file_backed_ptr.NewRequest().TakeChannel().release());
ZX_ASSERT(status == ZX_OK && file_backed_ptr.is_bound());
fit::result<inspect_deprecated::Source, std::string> result;
SchedulePromise(inspect_deprecated::Source::MakeFromVmo(
inspect_deprecated::Location::Parse(path).take_value(),
std::move(file_backed_ptr), depth)
.then([&result](fit::result<inspect_deprecated::Source, std::string>& res) {
result = std::move(res);
}));
RunLoopUntil([&result] { return !!result; });
return result;
}
protected:
// Writes the contents of the VMO backed by test data into a file at |path|.
// Returns the resulting file name, or a string error.
fit::result<std::string, std::string> WriteFromVmo(const std::string& path) {
zx::vmo vmo = tree_.DuplicateVmo();
uint64_t vmo_size;
zx_status_t status = vmo.get_size(&vmo_size);
if (status != ZX_OK) {
return fit::error("could not get VMO size");
}
std::vector<uint8_t> buf(vmo_size);
status = vmo.read(buf.data(), 0, vmo_size);
if (status != ZX_OK) {
return fit::error("could not read from VMO");
}
const char* f = reinterpret_cast<const char*>(buf.data()); // Known safe.
if (!files::WriteFile(path, f, buf.size())) {
return fit::error(fxl::Substitute("Could not write: $0", path));
}
return fit::ok(path);
}
const std::string RootPath = "/tmp/file.inspect";
};
template <typename T>
class SourceTest : public T {};
using SourceTestTypes = ::testing::Types<SourceTestFidl, SourceTestVmo, SourceTestFile>;
TYPED_TEST_SUITE(SourceTest, SourceTestTypes);
TYPED_TEST(SourceTest, MakeDefault) {
// TODO(FLK-186): Reenable this test.
GTEST_SKIP();
auto result = this->MakeFromPath(this->RootPath);
ASSERT_TRUE(result.is_ok());
auto source = result.take_value();
CheckHierarchyMatches(source.GetHierarchy());
}
TYPED_TEST(SourceTest, MakeDepth0) {
// TODO(FLK-186): Reenable this test.
GTEST_SKIP();
auto result = this->MakeFromPath(this->RootPath, 0);
ASSERT_TRUE(result.is_ok());
EXPECT_THAT(result.take_value().GetHierarchy(),
::testing::AllOf(NodeMatches(PropertyList(::testing::SizeIs(1))),
ChildrenMatch(::testing::SizeIs(0))));
}
TYPED_TEST(SourceTest, MakeDepth1) {
// TODO(FLK-186): Reenable this test.
GTEST_SKIP();
auto result = this->MakeFromPath(this->RootPath, 1);
ASSERT_TRUE(result.is_ok());
EXPECT_THAT(result.take_value().GetHierarchy(),
::testing::AllOf(ChildrenMatch(::testing::ElementsAre(::testing::AllOf(
NodeMatches(NameMatches("test")), ::ChildrenMatch(::testing::SizeIs(0)))))));
}
TYPED_TEST(SourceTest, MakeWithPath) {
// TODO(FLK-186): Reenable this test.
GTEST_SKIP();
auto result = this->MakeFromPath(fxl::Concatenate({this->RootPath, "#test"}));
ASSERT_TRUE(result.is_ok());
EXPECT_THAT(
result.take_value().GetHierarchy(),
::testing::AllOf(NodeMatches(MetricList(::testing::ElementsAre(IntMetricIs("count", 2)))),
ChildrenMatch(::testing::SizeIs(1))));
}
// These tests only apply to the test prototypes in SourceTestErrorTypes.
template <typename T>
class SourceTestError : public T {};
using SourceTestErrorTypes = ::testing::Types<SourceTestFidl, SourceTestVmo>;
TYPED_TEST_SUITE(SourceTestError, SourceTestErrorTypes);
TYPED_TEST(SourceTestError, MakeError) {
// TODO(FLK-186): Reenable this test.
GTEST_SKIP();
auto result = this->MakeFromPath(this->RootPath);
ASSERT_TRUE(result.is_ok());
result = this->MakeFromPath(this->RootPath); // reusing the connection should fail.
ASSERT_TRUE(result.is_error());
}
inspect_deprecated::ObjectHierarchy MakeNode(std::string name) {
return inspect_deprecated::ObjectHierarchy(inspect_deprecated::hierarchy::Node(std::move(name)),
{});
}
TEST(Source, VisitObjectsInHierarchy) {
inspect_deprecated::ObjectHierarchy root = MakeNode("root");
{
auto child = MakeNode("child");
child.children().emplace_back(MakeNode("nested"));
root.children().emplace_back(std::move(child));
}
root.children().emplace_back(MakeNode("a_child"));
auto source = inspect_deprecated::Source({}, std::move(root));
std::vector<std::string> paths_visited;
source.VisitObjectsInHierarchy([&](const std::vector<std::string>& path,
const inspect_deprecated::ObjectHierarchy& hierarchy) {
paths_visited.push_back(fxl::JoinStrings(path, "/"));
});
EXPECT_THAT(paths_visited, ::testing::ElementsAre("", "child", "child/nested", "a_child"));
paths_visited.clear();
source.SortHierarchy();
source.VisitObjectsInHierarchy([&](const std::vector<std::string>& path,
const inspect_deprecated::ObjectHierarchy& hierarchy) {
paths_visited.push_back(fxl::JoinStrings(path, "/"));
});
EXPECT_THAT(paths_visited, ::testing::ElementsAre("", "a_child", "child", "child/nested"));
}
} // namespace