| // 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. |
| |
| #ifndef SRC_VIRTUALIZATION_TESTS_ENCLOSED_GUEST_H_ |
| #define SRC_VIRTUALIZATION_TESTS_ENCLOSED_GUEST_H_ |
| |
| #include <fuchsia/virtualization/cpp/fidl.h> |
| #include <lib/async-loop/cpp/loop.h> |
| #include <lib/async-loop/default.h> |
| #include <lib/async/cpp/executor.h> |
| #include <lib/sys/cpp/service_directory.h> |
| #include <lib/sys/cpp/testing/test_with_environment.h> |
| |
| #include "src/virtualization/lib/grpc/grpc_vsock_server.h" |
| #include "src/virtualization/lib/vsh/command_runner.h" |
| #include "src/virtualization/packages/biscotti_guest/third_party/protos/vm_guest.grpc.pb.h" |
| #include "src/virtualization/packages/biscotti_guest/third_party/protos/vm_host.grpc.pb.h" |
| #include "src/virtualization/tests/fake_netstack.h" |
| #include "src/virtualization/tests/fake_scenic.h" |
| #include "src/virtualization/tests/guest_console.h" |
| |
| static constexpr char kZirconGuestUrl[] = |
| "fuchsia-pkg://fuchsia.com/zircon_guest#meta/zircon_guest.cmx"; |
| static constexpr char kDebianGuestUrl[] = |
| "fuchsia-pkg://fuchsia.com/debian_guest#meta/debian_guest.cmx"; |
| static constexpr char kTerminaGuestUrl[] = |
| "fuchsia-pkg://fuchsia.com/termina_guest#meta/termina_guest.cmx"; |
| |
| enum class GuestKernel { |
| ZIRCON, |
| LINUX, |
| }; |
| |
| // EnclosedGuest is a base class that defines an guest environment and instance |
| // encapsulated in an EnclosingEnvironment. A derived class must define the |
| // |LaunchInfo| to send to the guest environment controller, as well as methods |
| // for waiting for the guest to be ready and running test utilities. Most tests |
| // will derive from either ZirconEnclosedGuest or DebianEnclosedGuest below and |
| // override LaunchInfo only. EnclosedGuest is designed to be used with |
| // GuestTest. |
| class EnclosedGuest { |
| public: |
| EnclosedGuest() |
| : loop_(&kAsyncLoopConfigAttachToCurrentThread), |
| real_services_(sys::ServiceDirectory::CreateFromNamespace()) {} |
| virtual ~EnclosedGuest() {} |
| |
| zx_status_t Start(); |
| zx_status_t Stop(); |
| |
| bool Ready() const { return ready_; } |
| |
| // Execute |command| on the guest serial and wait for the |result|. |
| virtual zx_status_t Execute(const std::vector<std::string>& argv, |
| const std::unordered_map<std::string, std::string>& env = {}, |
| std::string* result = nullptr, int32_t* return_code = nullptr); |
| |
| // Run a test util named |util| with |argv| in the guest and wait for the |
| // |result|. |
| zx_status_t RunUtil(const std::string& util, const std::vector<std::string>& argv, |
| std::string* result = nullptr); |
| |
| // Return a shell command for a test utility named |util| with the given |
| // |argv| in the guest. The result may be passed directly to |Execute| |
| // to actually run the command. |
| virtual std::vector<std::string> GetTestUtilCommand(const std::string& util, |
| const std::vector<std::string>& argv) = 0; |
| |
| virtual GuestKernel GetGuestKernel() = 0; |
| |
| void GetHostVsockEndpoint( |
| fidl::InterfaceRequest<fuchsia::virtualization::HostVsockEndpoint> endpoint) { |
| realm_->GetHostVsockEndpoint(std::move(endpoint)); |
| } |
| |
| void ConnectToBalloon( |
| fidl::InterfaceRequest<fuchsia::virtualization::BalloonController> balloon_controller) { |
| realm_->ConnectToBalloon(guest_cid_, std::move(balloon_controller)); |
| } |
| |
| uint32_t GetGuestCid() const { return guest_cid_; } |
| |
| FakeNetstack* GetNetstack() { return &fake_netstack_; } |
| |
| FakeScenic* GetScenic() { return &fake_scenic_; } |
| |
| GuestConsole* GetConsole() { return console_.get(); } |
| |
| protected: |
| // Provides guest specific |url| and |cfg|, called by Start. |
| virtual zx_status_t LaunchInfo(std::string* url, fuchsia::virtualization::GuestConfig* cfg) = 0; |
| |
| // Waits until the guest is ready to run test utilities, called by Start. |
| virtual zx_status_t WaitForSystemReady() = 0; |
| |
| // Waits for the guest to perform a graceful shutdown. |
| virtual zx_status_t ShutdownAndWait() = 0; |
| |
| virtual std::string ShellPrompt() = 0; |
| |
| // Invoked after the guest |Realm| has been created but before the guest |
| // has been launched. |
| // |
| // Any vsock ports that are listened on here are guaranteed to be ready to |
| // accept connections before the guest attempts to connect to them. |
| virtual zx_status_t SetupVsockServices() { return ZX_OK; } |
| |
| async::Loop* GetLoop() { return &loop_; } |
| |
| private: |
| async::Loop loop_; |
| std::shared_ptr<sys::ServiceDirectory> real_services_; |
| fuchsia::sys::EnvironmentPtr real_env_; |
| std::unique_ptr<sys::testing::EnclosingEnvironment> enclosing_environment_; |
| fuchsia::virtualization::ManagerPtr manager_; |
| fuchsia::virtualization::RealmPtr realm_; |
| fuchsia::virtualization::GuestPtr guest_; |
| FakeScenic fake_scenic_; |
| FakeNetstack fake_netstack_; |
| std::unique_ptr<GuestConsole> console_; |
| uint32_t guest_cid_; |
| bool ready_ = false; |
| }; |
| |
| template <typename EnclosedGuestImpl> |
| class SingleCpuEnclosedGuest : public EnclosedGuestImpl { |
| zx_status_t LaunchInfo(std::string* url, fuchsia::virtualization::GuestConfig* cfg) override { |
| zx_status_t status = EnclosedGuestImpl::LaunchInfo(url, cfg); |
| if (status != ZX_OK) { |
| return status; |
| } |
| cfg->set_virtio_gpu(false); |
| cfg->set_cpus(1); |
| return ZX_OK; |
| } |
| }; |
| |
| class ZirconEnclosedGuest : public EnclosedGuest { |
| public: |
| std::vector<std::string> GetTestUtilCommand(const std::string& util, |
| const std::vector<std::string>& argv) override; |
| |
| GuestKernel GetGuestKernel() override { return GuestKernel::ZIRCON; } |
| |
| protected: |
| zx_status_t LaunchInfo(std::string* url, fuchsia::virtualization::GuestConfig* cfg) override; |
| zx_status_t WaitForSystemReady() override; |
| zx_status_t ShutdownAndWait() override; |
| std::string ShellPrompt() override { return "$ "; } |
| }; |
| |
| class DebianEnclosedGuest : public EnclosedGuest { |
| public: |
| std::vector<std::string> GetTestUtilCommand(const std::string& util, |
| const std::vector<std::string>& argv) override; |
| |
| GuestKernel GetGuestKernel() override { return GuestKernel::LINUX; } |
| |
| protected: |
| zx_status_t LaunchInfo(std::string* url, fuchsia::virtualization::GuestConfig* cfg) override; |
| zx_status_t WaitForSystemReady() override; |
| zx_status_t ShutdownAndWait() override; |
| std::string ShellPrompt() override { return "$ "; } |
| }; |
| |
| class TerminaEnclosedGuest : public EnclosedGuest, public vm_tools::StartupListener::Service { |
| public: |
| GuestKernel GetGuestKernel() override { return GuestKernel::LINUX; } |
| |
| std::vector<std::string> GetTestUtilCommand(const std::string& util, |
| const std::vector<std::string>& argv) override; |
| zx_status_t Execute(const std::vector<std::string>& argv, |
| const std::unordered_map<std::string, std::string>& env, std::string* result, |
| int32_t* return_code) override; |
| |
| protected: |
| zx_status_t LaunchInfo(std::string* url, fuchsia::virtualization::GuestConfig* cfg) override; |
| zx_status_t WaitForSystemReady() override; |
| zx_status_t ShutdownAndWait() override; |
| std::string ShellPrompt() override { return "$ "; } |
| |
| private: |
| zx_status_t SetupVsockServices() override; |
| |
| // |vm_tools::StartupListener::Service| |
| grpc::Status VmReady(grpc::ServerContext* context, const vm_tools::EmptyMessage* request, |
| vm_tools::EmptyMessage* response) override; |
| |
| std::unique_ptr<vsh::BlockingCommandRunner> command_runner_; |
| async::Executor executor_{async_get_default_dispatcher()}; |
| std::unique_ptr<GrpcVsockServer> server_; |
| std::unique_ptr<vm_tools::Maitred::Stub> maitred_; |
| fuchsia::virtualization::HostVsockEndpointPtr vsock_; |
| }; |
| |
| // Termina guests should be added here once they're stable in CQ. |
| using AllGuestTypes = |
| ::testing::Types<ZirconEnclosedGuest, SingleCpuEnclosedGuest<ZirconEnclosedGuest> |
| #if __x86_64__ |
| , |
| DebianEnclosedGuest, SingleCpuEnclosedGuest<DebianEnclosedGuest> |
| #endif // __x86_64__ |
| >; |
| |
| #endif // SRC_VIRTUALIZATION_TESTS_ENCLOSED_GUEST_H_ |