blob: e2c33160443164cfe5ed738a552f2e2391d55108 [file] [log] [blame]
// Copyright 2022 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 <fidl/fuchsia.inspect/cpp/markers.h>
#include <lib/fpromise/bridge.h>
#include <lib/inspect/component/cpp/testing.h>
#include <lib/inspect/cpp/reader.h>
#include <utility>
namespace inspect {
namespace testing {
namespace {
using inspect::internal::SnapshotTree;
fpromise::promise<SnapshotTree> SnapshotTreeFromTree(TreeClient&& tree,
async_dispatcher_t* dispatcher,
SnapshotTree&& root = {}) {
fpromise::bridge<SnapshotTree> content_bridge;
auto callback = [completer = std::move(content_bridge.completer), root = std::move(root)](
fidl::WireUnownedResult<fuchsia_inspect::Tree::GetContent>& result) mutable {
Snapshot::Create(result.Unwrap()->content.buffer().vmo, &root.snapshot);
completer.complete_ok(std::move(root));
};
tree->GetContent().ThenExactlyOnce(std::move(callback));
return content_bridge.consumer.promise().and_then(
[dispatcher = dispatcher, tree = std::move(tree)](
SnapshotTree& current_level) mutable -> fpromise::promise<SnapshotTree> {
auto child_name_endpoints = fidl::CreateEndpoints<fuchsia_inspect::TreeNameIterator>();
return fpromise::make_promise([tree = std::move(tree),
server_ends = std::move(child_name_endpoints),
dispatcher = dispatcher]() mutable {
auto status = tree->ListChildNames(std::move(server_ends->server));
ZX_ASSERT_MSG(status.ok(), "tree->ListChildNames failed with bad status");
return fpromise::make_ok_promise(std::make_pair(
TreeNameIteratorClient(std::move(server_ends->client), dispatcher),
std::move(tree)));
})
.and_then([](std::pair<TreeNameIteratorClient, TreeClient>& args) {
return fpromise::join_promises(ReadAllChildNames(args.first),
fpromise::make_ok_promise(std::move(args.second)));
})
.and_then([](std::tuple<fpromise::result<std::vector<std::string>>,
fpromise::result<TreeClient>>& args) {
auto vec = std::get<0>(args).take_value();
auto client = std::get<1>(args).take_value();
return fpromise::make_ok_promise(std::make_pair(std::move(vec), std::move(client)));
})
.and_then([current_level = std::move(current_level), dispatcher = dispatcher](
std::pair<std::vector<std::string>, TreeClient>& args) mutable {
std::vector<fpromise::promise<SnapshotTree>> child_promises;
std::vector<std::string> child_names;
for (const auto& n : args.first) {
auto endpoints = fidl::CreateEndpoints<fuchsia_inspect::Tree>();
auto status = args.second->OpenChild(fidl::StringView::FromExternal(n),
std::move(endpoints->server));
ZX_ASSERT_MSG(status.ok(), "crashing at OpenChild status check");
auto child_client = TreeClient(std::move(endpoints->client), dispatcher);
child_promises.push_back(SnapshotTreeFromTree(std::move(child_client), dispatcher));
child_names.push_back(n);
}
return fpromise::join_promises(
fpromise::join_promise_vector(std::move(child_promises)),
fpromise::make_ok_promise(std::move(current_level)),
fpromise::make_ok_promise(std::move(child_names)));
})
.and_then([](std::tuple<fpromise::result<std::vector<fpromise::result<SnapshotTree>>>,
fpromise::result<SnapshotTree>,
fpromise::result<std::vector<std::string>>>& results) {
auto child_promises = std::get<0>(results).take_value();
auto current = std::get<1>(results).take_value();
auto child_names = std::get<2>(results).take_value();
for (uint64_t i = 0; i < child_promises.size(); i++) {
current.children[std::move(child_names.at(i))] = child_promises.at(i).take_value();
}
return fpromise::make_ok_promise(std::move(current));
});
});
}
} // namespace
fpromise::promise<std::vector<std::string>> ReadAllChildNames(TreeNameIteratorClient& iter) {
fpromise::bridge<std::vector<std::string>> bridge;
auto callback =
[completer = std::move(bridge.completer)](
fidl::WireUnownedResult<fuchsia_inspect::TreeNameIterator::GetNext>& result) mutable {
std::vector<std::string> resolved_names;
ZX_ASSERT_MSG(result.ok(), "TreeNameIterator::GetNext failed: %s",
result.error().FormatDescription().c_str());
for (const auto& name : result.Unwrap()->name) {
resolved_names.push_back({std::cbegin(name), std::cend(name)});
}
completer.complete_ok(std::move(resolved_names));
};
using result_t = fpromise::result<std::vector<std::string>>;
using promise_t = fpromise::promise<std::vector<std::string>>;
iter->GetNext().ThenExactlyOnce(std::move(callback));
return bridge.consumer.promise().then([&](result_t& result) mutable -> promise_t {
if (!result.is_ok() || result.value().empty()) {
return fpromise::make_ok_promise(std::vector<std::string>());
}
return ReadAllChildNames(iter).then([ret = result.take_value()](result_t& result) mutable {
if (result.is_ok()) {
for (auto& v : result.take_value()) {
ret.emplace_back(std::move(v));
}
}
return fpromise::make_ok_promise(std::move(ret));
});
});
}
fpromise::promise<Hierarchy> ReadFromTree(TreeClient& tree, async_dispatcher_t* dispatcher) {
return SnapshotTreeFromTree(std::move(tree), dispatcher).and_then(internal::ReadFromSnapshotTree);
}
} // namespace testing
} // namespace inspect