blob: 7768741eb8654506b8fb307917cbbb3fe46dfcf3 [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 "adb-shell.h"
#include <fidl/fuchsia.dash/cpp/wire_test_base.h>
#include <fidl/fuchsia.hardware.adb/cpp/wire_test_base.h>
#include <fidl/fuchsia.sys2/cpp/wire_test_base.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/async-loop/testing/cpp/real_loop.h>
#include <lib/component/incoming/cpp/protocol.h>
#include <lib/component/outgoing/cpp/outgoing_directory.h>
#include <lib/syslog/cpp/macros.h>
#include <cstring>
#include <memory>
#include <optional>
#include <zxtest/zxtest.h>
#include "src/developer/adb/bin/adb-shell/adb_shell_config.h"
namespace adb_shell {
class FakeDashLauncher : public fidl::testing::WireTestBase<fuchsia_dash::Launcher> {
public:
void NotImplemented_(const std::string& name, ::fidl::CompleterBase& completer) override {
FX_LOGS(ERROR) << "Not implemented " << name;
completer.Close(ZX_ERR_NOT_SUPPORTED);
}
void ExploreComponentOverSocket(
::fuchsia_dash::wire::LauncherExploreComponentOverSocketRequest* request,
ExploreComponentOverSocketCompleter::Sync& completer) override {
command_ = request->command.get();
socket_ = std::move(request->socket);
completer.ReplySuccess();
}
// index is the dash launcher instance to send OnTerminate for. Useful when launching multiple
// shells.
zx_status_t SendOnTerminateEvent(uint32_t index = 0) {
if (index < binding_refs_.size()) {
auto result = fidl::WireSendEvent(binding_refs_[index])->OnTerminated(ZX_OK);
return result.status();
}
return ZX_ERR_INVALID_ARGS;
}
void BindServer(async_dispatcher_t* dispatcher,
fidl::ServerEnd<fuchsia_dash::Launcher> server_end) {
binding_refs_.emplace_back(fidl::BindServer(dispatcher, std::move(server_end), this));
}
zx::unowned_socket socket() { return socket_.borrow(); }
std::string command() { return command_; }
private:
// Dash launcher server bindings. Expect multiple when launching multiple shells.
std::vector<fidl::ServerBindingRef<fuchsia_dash::Launcher>> binding_refs_;
// Command string passed in the latest request if any.
std::string command_;
// Server end dash socket passed in the latest request.
zx::socket socket_;
};
class FakeLifecycleController
: public fidl::testing::WireTestBase<fuchsia_sys2::LifecycleController> {
public:
void NotImplemented_(const std::string& name, ::fidl::CompleterBase& completer) override {
FX_LOGS(ERROR) << "Not implemented " << name;
completer.Close(ZX_ERR_NOT_SUPPORTED);
}
void ResolveInstance(::fuchsia_sys2::wire::LifecycleControllerResolveInstanceRequest* request,
ResolveInstanceCompleter::Sync& completer) override {
moniker_ = request->moniker.get();
completer.ReplySuccess();
}
void BindServer(async_dispatcher_t* dispatcher,
fidl::ServerEnd<fuchsia_sys2::LifecycleController> server_end) {
binding_refs_.emplace_back(fidl::BindServer(dispatcher, std::move(server_end), this));
}
std::string moniker() { return moniker_; }
private:
// Lifecycle controller server bindings.
std::vector<fidl::ServerBindingRef<fuchsia_sys2::LifecycleController>> binding_refs_;
// Moniker string passed in the latest request if any.
std::string moniker_;
};
class AdbShellTest : public zxtest::Test, public loop_fixture::RealLoop {
public:
AdbShellTest() : shell_loop_(&kAsyncLoopConfigNeverAttachToThread) {}
void SetUp() override {
shell_loop_.StartThread("adb-shell-test-shell");
incoming_ = std::make_unique<component::OutgoingDirectory>(dispatcher());
auto svc_endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>();
ASSERT_TRUE(svc_endpoints.is_ok());
SetupIncomingServices(std::move(svc_endpoints->server));
adb_ = std::make_unique<adb_shell::AdbShell>(
std::move(svc_endpoints->client), shell_loop_.dispatcher(), adb_shell_config::Config());
ASSERT_NO_FAILURES();
}
void SetupIncomingServices(fidl::ServerEnd<fuchsia_io::Directory> svc) {
zx::result endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>();
ASSERT_OK(endpoints);
auto& [client_end, server_end] = endpoints.value();
ASSERT_OK(incoming_->AddUnmanagedProtocol<fuchsia_dash::Launcher>(
[this](fidl::ServerEnd<fuchsia_dash::Launcher> server_end) {
fake_dash_launcher_.BindServer(dispatcher(), std::move(server_end));
}));
ASSERT_OK(incoming_->AddUnmanagedProtocol<fuchsia_sys2::LifecycleController>(
[this](fidl::ServerEnd<fuchsia_sys2::LifecycleController> server_end) {
fake_lifecycle_controller_.BindServer(dispatcher(), std::move(server_end));
},
"fuchsia.sys2.LifecycleController.root"));
ASSERT_OK(incoming_->Serve(std::move(server_end)));
ASSERT_OK(component::ConnectAt<fuchsia_io::Directory>(
client_end, std::move(svc), component::OutgoingDirectory::kServiceDirectory));
}
protected:
std::unique_ptr<AdbShell> adb_;
FakeDashLauncher fake_dash_launcher_;
FakeLifecycleController fake_lifecycle_controller_;
std::unique_ptr<component::OutgoingDirectory> incoming_;
async::Loop shell_loop_;
};
TEST_F(AdbShellTest, LaunchShell) {
PerformBlockingWork([&] {
zx::socket endpoint0, endpoint1;
ASSERT_OK(zx::socket::create(0, &endpoint0, &endpoint1));
ASSERT_OK(adb_->AddShell({}, std::move(endpoint0)));
EXPECT_TRUE(fake_dash_launcher_.socket()->is_valid());
EXPECT_EQ(fake_dash_launcher_.command(), "");
EXPECT_EQ(fake_lifecycle_controller_.moniker(), "");
EXPECT_EQ(1, adb_->ActiveShellInstances());
EXPECT_OK(fake_dash_launcher_.SendOnTerminateEvent());
// Wait for the OnTerminate event to take effect.
while (adb_->ActiveShellInstances() > 0) {
shell_loop_.RunUntilIdle();
}
});
}
TEST_F(AdbShellTest, LaunchShellWithCommands) {
PerformBlockingWork([&] {
zx::socket endpoint0, endpoint1;
ASSERT_OK(zx::socket::create(0, &endpoint1, &endpoint0));
ASSERT_OK(adb_->AddShell("ls", std::move(endpoint0)));
EXPECT_TRUE(fake_dash_launcher_.socket()->is_valid());
EXPECT_EQ(fake_dash_launcher_.command(), "ls");
EXPECT_EQ(1, adb_->ActiveShellInstances());
EXPECT_OK(fake_dash_launcher_.SendOnTerminateEvent());
// Wait for the OnTerminate event to take effect.
while (adb_->ActiveShellInstances() > 0) {
shell_loop_.RunUntilIdle();
}
});
}
TEST_F(AdbShellTest, MultipleShells) {
PerformBlockingWork([&] {
zx::socket endpoint0, endpoint1;
ASSERT_OK(zx::socket::create(0, &endpoint0, &endpoint1));
ASSERT_OK(adb_->AddShell("echo 'hi'", std::move(endpoint0)));
EXPECT_TRUE(fake_dash_launcher_.socket()->is_valid());
EXPECT_EQ(fake_dash_launcher_.command(), "echo 'hi'");
EXPECT_EQ(1, adb_->ActiveShellInstances());
ASSERT_OK(adb_->AddShell("ls", std::move(endpoint1)));
EXPECT_TRUE(fake_dash_launcher_.socket()->is_valid());
EXPECT_EQ(fake_dash_launcher_.command(), "ls");
EXPECT_EQ(2, adb_->ActiveShellInstances());
EXPECT_OK(fake_dash_launcher_.SendOnTerminateEvent(0));
EXPECT_OK(fake_dash_launcher_.SendOnTerminateEvent(1));
// Wait for the OnTerminate event to take effect.
while (adb_->ActiveShellInstances() > 0) {
shell_loop_.RunUntilIdle();
}
});
}
} // namespace adb_shell