blob: a5d0faf0831eaf2450cd2984a23466ca9d9b3a44 [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 <lib/async/cpp/executor.h>
#include <lib/gtest/test_loop_fixture.h>
#include <lib/sys/cpp/testing/component_context_provider.h>
#include <lib/virtualization/testing/fake_manager.h>
#include "src/virtualization/lib/grpc/grpc_vsock_server.h"
#include "src/virtualization/lib/grpc/grpc_vsock_stub.h"
#include "src/virtualization/lib/grpc/test_server.grpc.pb.h"
namespace {
constexpr uint32_t kTestServicePort = 1234;
constexpr const char* kTestMessage = "This is only a test";
class GrpcVsockTest : public gtest::TestLoopFixture {
public:
void SetUp() override {
TestLoopFixture::SetUp();
// Setup a fake guest realm that we can use to connect to a
// HostVsockEndpoint.
provider_.service_directory_provider()->AddService(fake_guest_manager_.GetHandler());
fuchsia::virtualization::ManagerPtr manager;
provider_.context()->svc()->Connect(manager.NewRequest());
manager->Create("test realm", realm_.NewRequest());
RunLoopUntilIdle();
}
protected:
// Connects to the |HostVsockEnpoint| for the realm.
fuchsia::virtualization::HostVsockEndpointPtr GetHostVsockEndpoint() {
fuchsia::virtualization::HostVsockEndpointPtr result;
realm_->GetHostVsockEndpoint(result.NewRequest());
RunLoopUntilIdle();
return result;
}
// Access the |FakeGuestVsock|. This can be used to simulate guest
// interactions over virtio-vsock.
guest::testing::FakeGuestVsock* guest_vsock() { return fake_guest_manager_.GuestVsock(); }
async::Executor* executor() { return &executor_; }
private:
async::Executor executor_{dispatcher()};
fuchsia::virtualization::RealmPtr realm_;
guest::testing::FakeManager fake_guest_manager_;
sys::testing::ComponentContextProvider provider_;
fuchsia::virtualization::HostVsockEndpointPtr socket_endpoint_;
};
// Simple gRPC service that echos messages back to the client.
class TestEchoServer : public vsock_test::Echo::Service {
private:
grpc::Status Echo(grpc::ServerContext* context, const vsock_test::EchoMessage* request,
vsock_test::EchoMessage* response) override {
response->mutable_echo_message()->assign(request->echo_message());
return grpc::Status::OK;
}
};
// Simulate a gRPC echo server running over virtio-vsock.
TEST_F(GrpcVsockTest, Echo) {
// Setup gRPC vsock server.
GrpcVsockServerBuilder server_builder(GetHostVsockEndpoint());
TestEchoServer server_impl;
server_builder.AddListenPort(kTestServicePort);
server_builder.RegisterService(&server_impl);
fit::result<std::unique_ptr<GrpcVsockServer>, zx_status_t> result;
auto p = server_builder.Build().then(
[&result](fit::result<std::unique_ptr<GrpcVsockServer>, zx_status_t>& r) mutable {
result = std::move(r);
});
executor()->schedule_task(std::move(p));
RunLoopUntilIdle();
// Verify the server was started.
ASSERT_FALSE(result.is_pending());
ASSERT_FALSE(result.is_error());
ASSERT_TRUE(result.is_ok());
auto server = result.take_value();
ASSERT_TRUE(server != nullptr);
// Connect to the service using the guest vsock endpoint.
zx::handle guest_handle;
guest_vsock()->ConnectToHost(kTestServicePort,
[&](auto handle) { guest_handle = std::move(handle); });
RunLoopUntilIdle();
ASSERT_TRUE(guest_handle);
// The gRPC server will always use socket as a transport.
zx_info_handle_basic_t info;
ASSERT_EQ(ZX_OK,
guest_handle.get_info(ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr));
ASSERT_EQ(zx::socket::TYPE, info.type);
zx::socket guest_socket(std::move(guest_handle));
// Convert zx::socket into a gRPC stub that can be used to send RPCs.
auto stub_result = NewGrpcStub<vsock_test::Echo>(std::move(guest_socket));
ASSERT_TRUE(stub_result.is_ok());
auto stub = stub_result.take_value();
// Test echo
grpc::ClientContext context;
vsock_test::EchoMessage request;
vsock_test::EchoMessage response;
request.mutable_echo_message()->assign(kTestMessage);
auto grpc_status = stub->Echo(&context, request, &response);
ASSERT_TRUE(grpc_status.ok());
ASSERT_EQ(kTestMessage, response.echo_message());
}
} // namespace