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